move format to a separate branch, revert trunk
This commit is contained in:
parent
e910c95e8a
commit
51b5259a6a
10
TODO
10
TODO
@ -1,13 +1,3 @@
|
||||
- when we open with a mmap window and later do im_incheck(), do we remap the
|
||||
whole file?
|
||||
|
||||
- remove a lot of stuff
|
||||
|
||||
- read_vips() in im_open.c:179 needs moving to format and breaking into is_a,
|
||||
header, load and save, as tiff etc.
|
||||
|
||||
make im_file2vips, im_vips2file?
|
||||
|
||||
- pluggable formats
|
||||
|
||||
for each format, store:
|
||||
|
@ -375,7 +375,6 @@ AC_OUTPUT([
|
||||
libsrc/colour/Makefile
|
||||
libsrc/conversion/Makefile
|
||||
libsrc/convolution/Makefile
|
||||
libsrc/format/Makefile
|
||||
libsrc/freq_filt/Makefile
|
||||
libsrc/histograms_lut/Makefile
|
||||
libsrc/inplace/Makefile
|
||||
|
@ -145,10 +145,10 @@ typedef struct {
|
||||
|
||||
/* Function protos for formats.
|
||||
*/
|
||||
typedef gboolean (*im_format_is_a_fn)( const char * );
|
||||
typedef int (*im_format_header_fn)( const char *, IMAGE * );
|
||||
typedef int (*im_format_load_fn)( const char *, IMAGE * );
|
||||
typedef int (*im_format_save_fn)( IMAGE *, const char * );
|
||||
typedef gboolean (*im_format_is_a)( const char * );
|
||||
typedef int (*im_format_header)( const char *, IMAGE * );
|
||||
typedef int (*im_format_load)( const char *, IMAGE * );
|
||||
typedef int (*im_forrmat_save)( IMAGE *, const char * );
|
||||
|
||||
/* A VIPS image format.
|
||||
*/
|
||||
@ -156,11 +156,11 @@ typedef struct {
|
||||
const char *name; /* Format name, same as mime */
|
||||
const char *name_user; /* I18n'd name for users */
|
||||
int priority; /* Keep formats sorted by this, default 0 */
|
||||
const char **suffs; /* Allowed suffixes */
|
||||
im_format_is_a_fn is_a; /* Filename is in format */
|
||||
im_format_header_fn header;/* Load header only from filename */
|
||||
im_format_load_fn load; /* Load image from filename */
|
||||
im_format_save_fn save; /* Save image to filename */
|
||||
const char *suffs[]; /* Allowed suffixes */
|
||||
im_format_is_a is_a; /* Filename is in format */
|
||||
im_format_header header;/* Load header only from filename */
|
||||
im_format_load load; /* Load image from filename */
|
||||
im_format_save save; /* Save image to filename */
|
||||
} im_format;
|
||||
|
||||
/* A set of VIPS formats forming a format package.
|
||||
@ -300,12 +300,6 @@ im_function *im_find_function( const char *name );
|
||||
im_package *im_find_package( const char *name );
|
||||
im_package *im_package_of_function( const char *name );
|
||||
|
||||
/* Map over and find formats.
|
||||
*/
|
||||
void *im_map_formats( VSListMap2Fn fn, void *a, void *b );
|
||||
im_format *im_format_for_file( const char *filename );
|
||||
im_format *im_format_for_name( const char *filename );
|
||||
|
||||
/* Allocate space for, and free im_object argument lists.
|
||||
*/
|
||||
int im_free_vargv( im_function *fn, im_object *vargv );
|
||||
|
@ -17,7 +17,6 @@ SUBDIRS = \
|
||||
conversion \
|
||||
convolution \
|
||||
$(C_COMPILE_DIR) \
|
||||
format \
|
||||
freq_filt \
|
||||
histograms_lut \
|
||||
inplace \
|
||||
@ -43,7 +42,6 @@ libvips_la_LIBADD = \
|
||||
conversion/libconversion.la \
|
||||
convolution/libconvolution.la \
|
||||
$(C_LIB) \
|
||||
format/libformat.la \
|
||||
freq_filt/libfreq_filt.la \
|
||||
histograms_lut/libhistograms_lut.la \
|
||||
inplace/libinplace.la \
|
||||
|
@ -2,9 +2,12 @@ noinst_LTLIBRARIES = libconversion.la
|
||||
|
||||
libconversion_la_SOURCES = \
|
||||
im_bernd.c \
|
||||
im_vips2tiff.c \
|
||||
im_tiff2vips.c \
|
||||
conver_dispatch.c \
|
||||
dbh.h \
|
||||
im_bandjoin.c \
|
||||
im_analyze2vips.c \
|
||||
im_black.c \
|
||||
im_c2amph.c \
|
||||
im_c2rect.c \
|
||||
@ -13,18 +16,26 @@ libconversion_la_SOURCES = \
|
||||
im_c2real.c \
|
||||
im_clip.c \
|
||||
im_copy.c \
|
||||
im_csv2vips.c \
|
||||
im_vips2csv.c \
|
||||
im_extract.c \
|
||||
im_exr2vips.c \
|
||||
im_falsecolour.c \
|
||||
im_fliphor.c \
|
||||
im_flipver.c \
|
||||
im_gbandjoin.c \
|
||||
im_insert.c \
|
||||
im_jpeg2vips.c \
|
||||
im_lrjoin.c \
|
||||
im_magick2vips.c \
|
||||
im_mask2vips.c \
|
||||
im_msb.c \
|
||||
im_png2vips.c \
|
||||
im_ppm2vips.c \
|
||||
im_recomb.c \
|
||||
im_replicate.c \
|
||||
im_grid.c \
|
||||
im_raw2vips.c \
|
||||
im_ri2c.c \
|
||||
im_rightshift_size.c \
|
||||
im_rot180.c \
|
||||
@ -37,9 +48,13 @@ libconversion_la_SOURCES = \
|
||||
im_system.c \
|
||||
im_print.c \
|
||||
im_tbjoin.c \
|
||||
im_tile_cache.c \
|
||||
im_text.c \
|
||||
im_thresh.c \
|
||||
im_vips2mask.c \
|
||||
im_vips2jpeg.c \
|
||||
im_vips2ppm.c \
|
||||
im_vips2png.c \
|
||||
im_wrap.c \
|
||||
im_zoom.c
|
||||
|
||||
|
@ -1444,6 +1444,355 @@ static im_function zoom_desc = {
|
||||
zoom_args /* Arg list */
|
||||
};
|
||||
|
||||
static int
|
||||
jpeg2vips_vec( im_object *argv )
|
||||
{
|
||||
char *in = argv[0];
|
||||
IMAGE *out = argv[1];
|
||||
|
||||
if( im_jpeg2vips( in, out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static im_arg_desc jpeg2vips_args[] = {
|
||||
IM_INPUT_STRING( "in" ),
|
||||
IM_OUTPUT_IMAGE( "out" )
|
||||
};
|
||||
|
||||
static im_function jpeg2vips_desc = {
|
||||
"im_jpeg2vips", /* Name */
|
||||
"convert from jpeg", /* Description */
|
||||
0, /* Flags */
|
||||
jpeg2vips_vec, /* Dispatch function */
|
||||
IM_NUMBER( jpeg2vips_args ), /* Size of arg list */
|
||||
jpeg2vips_args /* Arg list */
|
||||
};
|
||||
|
||||
static int
|
||||
vips2jpeg_vec( im_object *argv )
|
||||
{
|
||||
IMAGE *in = argv[0];
|
||||
char *out = argv[1];
|
||||
|
||||
if( im_vips2jpeg( in, out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static im_arg_desc vips2jpeg_args[] = {
|
||||
IM_INPUT_IMAGE( "in" ),
|
||||
IM_INPUT_STRING( "out" )
|
||||
};
|
||||
|
||||
static im_function vips2jpeg_desc = {
|
||||
"im_vips2jpeg", /* Name */
|
||||
"convert to jpeg", /* Description */
|
||||
0, /* Flags */
|
||||
vips2jpeg_vec, /* Dispatch function */
|
||||
IM_NUMBER( vips2jpeg_args ), /* Size of arg list */
|
||||
vips2jpeg_args /* Arg list */
|
||||
};
|
||||
|
||||
static int
|
||||
vips2mimejpeg_vec( im_object *argv )
|
||||
{
|
||||
IMAGE *in = argv[0];
|
||||
int qfac = *((int *) argv[1]);
|
||||
|
||||
if( im_vips2mimejpeg( in, qfac ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static im_arg_desc vips2mimejpeg_args[] = {
|
||||
IM_INPUT_IMAGE( "in" ),
|
||||
IM_INPUT_INT( "qfac" )
|
||||
};
|
||||
|
||||
static im_function vips2mimejpeg_desc = {
|
||||
"im_vips2mimejpeg", /* Name */
|
||||
"convert to jpeg as mime type on stdout", /* Description */
|
||||
0, /* Flags */
|
||||
vips2mimejpeg_vec, /* Dispatch function */
|
||||
IM_NUMBER( vips2mimejpeg_args ), /* Size of arg list */
|
||||
vips2mimejpeg_args /* Arg list */
|
||||
};
|
||||
|
||||
/* Args for vips2png.
|
||||
*/
|
||||
static im_arg_desc vips2png_args[] = {
|
||||
IM_INPUT_IMAGE( "in" ),
|
||||
IM_INPUT_STRING( "out" )
|
||||
};
|
||||
|
||||
/* Call im_vips2png via arg vector.
|
||||
*/
|
||||
static int
|
||||
vips2png_vec( im_object *argv )
|
||||
{
|
||||
return( im_vips2png( argv[0], argv[1] ) );
|
||||
}
|
||||
|
||||
/* Description of im_vips2png.
|
||||
*/
|
||||
static im_function vips2png_desc = {
|
||||
"im_vips2png", /* Name */
|
||||
"convert VIPS image to PNG file", /* Description */
|
||||
0,
|
||||
vips2png_vec, /* Dispatch function */
|
||||
IM_NUMBER( vips2png_args ), /* Size of arg list */
|
||||
vips2png_args /* Arg list */
|
||||
};
|
||||
|
||||
/* Args for png2vips.
|
||||
*/
|
||||
static im_arg_desc png2vips_args[] = {
|
||||
IM_INPUT_STRING( "in" ),
|
||||
IM_OUTPUT_IMAGE( "out" )
|
||||
};
|
||||
|
||||
/* Call im_png2vips via arg vector.
|
||||
*/
|
||||
static int
|
||||
png2vips_vec( im_object *argv )
|
||||
{
|
||||
return( im_png2vips( argv[0], argv[1] ) );
|
||||
}
|
||||
|
||||
/* Description of im_png2vips.
|
||||
*/
|
||||
static im_function png2vips_desc = {
|
||||
"im_png2vips", /* Name */
|
||||
"convert PNG file to VIPS image", /* Description */
|
||||
0,
|
||||
png2vips_vec, /* Dispatch function */
|
||||
IM_NUMBER( png2vips_args ), /* Size of arg list */
|
||||
png2vips_args /* Arg list */
|
||||
};
|
||||
|
||||
/* Args for exr2vips.
|
||||
*/
|
||||
static im_arg_desc exr2vips_args[] = {
|
||||
IM_INPUT_STRING( "in" ),
|
||||
IM_OUTPUT_IMAGE( "out" )
|
||||
};
|
||||
|
||||
/* Call im_exr2vips via arg vector.
|
||||
*/
|
||||
static int
|
||||
exr2vips_vec( im_object *argv )
|
||||
{
|
||||
return( im_exr2vips( argv[0], argv[1] ) );
|
||||
}
|
||||
|
||||
/* Description of im_exr2vips.
|
||||
*/
|
||||
static im_function exr2vips_desc = {
|
||||
"im_exr2vips", /* Name */
|
||||
"convert an OpenEXR file to VIPS", /* Description */
|
||||
0,
|
||||
exr2vips_vec, /* Dispatch function */
|
||||
IM_NUMBER( exr2vips_args ), /* Size of arg list */
|
||||
exr2vips_args /* Arg list */
|
||||
};
|
||||
|
||||
/* Args for vips2tiff.
|
||||
*/
|
||||
static im_arg_desc vips2tiff_args[] = {
|
||||
IM_INPUT_IMAGE( "in" ),
|
||||
IM_INPUT_STRING( "out" )
|
||||
};
|
||||
|
||||
/* Call im_vips2tiff via arg vector.
|
||||
*/
|
||||
static int
|
||||
vips2tiff_vec( im_object *argv )
|
||||
{
|
||||
return( im_vips2tiff( argv[0], argv[1] ) );
|
||||
}
|
||||
|
||||
/* Description of im_vips2tiff.
|
||||
*/
|
||||
static im_function vips2tiff_desc = {
|
||||
"im_vips2tiff", /* Name */
|
||||
"convert VIPS image to TIFF file", /* Description */
|
||||
0,
|
||||
vips2tiff_vec, /* Dispatch function */
|
||||
IM_NUMBER( vips2tiff_args ), /* Size of arg list */
|
||||
vips2tiff_args /* Arg list */
|
||||
};
|
||||
|
||||
/* Args for magick2vips.
|
||||
*/
|
||||
static im_arg_desc magick2vips_args[] = {
|
||||
IM_INPUT_STRING( "in" ),
|
||||
IM_OUTPUT_IMAGE( "out" )
|
||||
};
|
||||
|
||||
/* Call im_magick2vips via arg vector.
|
||||
*/
|
||||
static int
|
||||
magick2vips_vec( im_object *argv )
|
||||
{
|
||||
return( im_magick2vips( argv[0], argv[1] ) );
|
||||
}
|
||||
|
||||
/* Description of im_magick2vips.
|
||||
*/
|
||||
static im_function magick2vips_desc = {
|
||||
"im_magick2vips", /* Name */
|
||||
"load file with libMagick", /* Description */
|
||||
0,
|
||||
magick2vips_vec, /* Dispatch function */
|
||||
IM_NUMBER( magick2vips_args ), /* Size of arg list */
|
||||
magick2vips_args /* Arg list */
|
||||
};
|
||||
|
||||
/* Args for tiff2vips.
|
||||
*/
|
||||
static im_arg_desc tiff2vips_args[] = {
|
||||
IM_INPUT_STRING( "in" ),
|
||||
IM_OUTPUT_IMAGE( "out" )
|
||||
};
|
||||
|
||||
/* Call im_tiff2vips via arg vector.
|
||||
*/
|
||||
static int
|
||||
tiff2vips_vec( im_object *argv )
|
||||
{
|
||||
return( im_tiff2vips( argv[0], argv[1] ) );
|
||||
}
|
||||
|
||||
/* Description of im_tiff2vips.
|
||||
*/
|
||||
static im_function tiff2vips_desc = {
|
||||
"im_tiff2vips", /* Name */
|
||||
"convert TIFF file to VIPS image", /* Description */
|
||||
0,
|
||||
tiff2vips_vec, /* Dispatch function */
|
||||
IM_NUMBER( tiff2vips_args ), /* Size of arg list */
|
||||
tiff2vips_args /* Arg list */
|
||||
};
|
||||
|
||||
static int
|
||||
analyze2vips_vec( im_object *argv )
|
||||
{
|
||||
const char *in = argv[0];
|
||||
IMAGE *out = argv[1];
|
||||
|
||||
return( im_analyze2vips( in, out ) );
|
||||
}
|
||||
|
||||
static im_arg_desc analyze2vips_arg_types[] = {
|
||||
IM_INPUT_STRING( "filename" ),
|
||||
IM_OUTPUT_IMAGE( "im" )
|
||||
};
|
||||
|
||||
static im_function analyze2vips_desc = {
|
||||
"im_analyze2vips", /* Name */
|
||||
"read a file in analyze format",/* Description */
|
||||
0, /* Flags */
|
||||
analyze2vips_vec, /* Dispatch function */
|
||||
IM_NUMBER( analyze2vips_arg_types ),/* Size of arg list */
|
||||
analyze2vips_arg_types /* Arg list */
|
||||
};
|
||||
|
||||
static int
|
||||
csv2vips_vec( im_object *argv )
|
||||
{
|
||||
const char *in = argv[0];
|
||||
IMAGE *out = argv[1];
|
||||
|
||||
return( im_csv2vips( in, out ) );
|
||||
}
|
||||
|
||||
static im_arg_desc csv2vips_arg_types[] = {
|
||||
IM_INPUT_STRING( "filename" ),
|
||||
IM_OUTPUT_IMAGE( "im" )
|
||||
};
|
||||
|
||||
static im_function csv2vips_desc = {
|
||||
"im_csv2vips", /* Name */
|
||||
"read a file in csv format",/* Description */
|
||||
0, /* Flags */
|
||||
csv2vips_vec, /* Dispatch function */
|
||||
IM_NUMBER( csv2vips_arg_types ),/* Size of arg list */
|
||||
csv2vips_arg_types /* Arg list */
|
||||
};
|
||||
|
||||
static int
|
||||
vips2csv_vec( im_object *argv )
|
||||
{
|
||||
IMAGE *in = argv[0];
|
||||
const char *filename = argv[1];
|
||||
|
||||
return( im_vips2csv( in, filename ) );
|
||||
}
|
||||
|
||||
static im_arg_desc vips2csv_arg_types[] = {
|
||||
IM_INPUT_IMAGE( "in" ),
|
||||
IM_INPUT_STRING( "filename" )
|
||||
};
|
||||
|
||||
static im_function vips2csv_desc = {
|
||||
"im_vips2csv", /* Name */
|
||||
"write an image in csv format", /* Description */
|
||||
0, /* Flags */
|
||||
vips2csv_vec, /* Dispatch function */
|
||||
IM_NUMBER( vips2csv_arg_types ),/* Size of arg list */
|
||||
vips2csv_arg_types /* Arg list */
|
||||
};
|
||||
|
||||
static int
|
||||
ppm2vips_vec( im_object *argv )
|
||||
{
|
||||
const char *in = argv[0];
|
||||
IMAGE *out = argv[1];
|
||||
|
||||
return( im_ppm2vips( in, out ) );
|
||||
}
|
||||
|
||||
static im_arg_desc ppm2vips_arg_types[] = {
|
||||
IM_INPUT_STRING( "filename" ),
|
||||
IM_OUTPUT_IMAGE( "im" )
|
||||
};
|
||||
|
||||
static im_function ppm2vips_desc = {
|
||||
"im_ppm2vips", /* Name */
|
||||
"read a file in pbm/pgm/ppm format", /* Description */
|
||||
0, /* Flags */
|
||||
ppm2vips_vec, /* Dispatch function */
|
||||
IM_NUMBER( ppm2vips_arg_types ),/* Size of arg list */
|
||||
ppm2vips_arg_types /* Arg list */
|
||||
};
|
||||
|
||||
static int
|
||||
vips2ppm_vec( im_object *argv )
|
||||
{
|
||||
IMAGE *im = argv[0];
|
||||
const char *filename = argv[1];
|
||||
|
||||
return( im_vips2ppm( im, filename ) );
|
||||
}
|
||||
|
||||
static im_arg_desc vips2ppm_arg_types[] = {
|
||||
IM_INPUT_IMAGE( "im" ),
|
||||
IM_INPUT_STRING( "filename" )
|
||||
};
|
||||
|
||||
static im_function vips2ppm_desc = {
|
||||
"im_vips2ppm", /* Name */
|
||||
"write a file in pbm/pgm/ppm format", /* Description */
|
||||
0, /* Flags */
|
||||
vips2ppm_vec, /* Dispatch function */
|
||||
IM_NUMBER( vips2ppm_arg_types ),/* Size of arg list */
|
||||
vips2ppm_arg_types /* Arg list */
|
||||
};
|
||||
|
||||
/* Call im_msb via arg vector.
|
||||
*/
|
||||
static int
|
||||
@ -1585,6 +1934,7 @@ static im_function *conv_list[] = {
|
||||
©_swap_desc,
|
||||
©_set_desc,
|
||||
©_set_meta_desc,
|
||||
&csv2vips_desc,
|
||||
&extract_area_desc,
|
||||
&extract_areabands_desc,
|
||||
&extract_band_desc,
|
||||
@ -1597,10 +1947,16 @@ static im_function *conv_list[] = {
|
||||
&grid_desc,
|
||||
&insert_desc,
|
||||
&insert_noexpand_desc,
|
||||
&jpeg2vips_desc,
|
||||
&lrjoin_desc,
|
||||
&magick2vips_desc,
|
||||
&mask2vips_desc,
|
||||
&msb_desc,
|
||||
&msb_band_desc,
|
||||
&png2vips_desc,
|
||||
&exr2vips_desc,
|
||||
&ppm2vips_desc,
|
||||
&analyze2vips_desc,
|
||||
&print_desc,
|
||||
&recomb_desc,
|
||||
&replicate_desc,
|
||||
@ -1617,7 +1973,14 @@ static im_function *conv_list[] = {
|
||||
&tbjoin_desc,
|
||||
&text_desc,
|
||||
&thresh_desc,
|
||||
&tiff2vips_desc,
|
||||
&vips2csv_desc,
|
||||
&vips2jpeg_desc,
|
||||
&vips2mask_desc,
|
||||
&vips2mimejpeg_desc,
|
||||
&vips2png_desc,
|
||||
&vips2ppm_desc,
|
||||
&vips2tiff_desc,
|
||||
&wrap_desc,
|
||||
&zoom_desc
|
||||
};
|
||||
|
98
libsrc/conversion/dbh.h
Normal file
98
libsrc/conversion/dbh.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
*
|
||||
* (c) Copyright, 1986-1991
|
||||
* Biodynamics Research Unit
|
||||
* Mayo Foundation
|
||||
*
|
||||
* dbh.h
|
||||
*
|
||||
*
|
||||
* database sub-definitions
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* The previous-generation header for Analyze images. Although (c) Mayo
|
||||
* Foundation, it has been in the public domain for many years.
|
||||
*
|
||||
* Analyze images have a 348 byte header stored in a file with a .hdr suffix,
|
||||
* and a corresponding .img file containing just the pixel data.
|
||||
*
|
||||
*/
|
||||
|
||||
struct header_key /* header_key */
|
||||
{ /* off + size*/
|
||||
int sizeof_hdr; /* 0 + 4 */
|
||||
char data_type[10]; /* 4 + 10 */
|
||||
char db_name[18]; /* 14 + 18 */
|
||||
int extents; /* 32 + 4 */
|
||||
short int session_error; /* 36 + 2 */
|
||||
char regular; /* 38 + 1 */
|
||||
char hkey_un0; /* 39 + 1 */
|
||||
}; /* total=40 */
|
||||
|
||||
struct image_dimension /* image_dimension */
|
||||
{ /* off + size*/
|
||||
short int dim[8]; /* 0 + 16 */
|
||||
char vox_units[4]; /* 16 + 4 */
|
||||
char cal_units[8]; /* 20 + 4 */
|
||||
short int unused1; /* 24 + 2 */
|
||||
short int datatype; /* 30 + 2 */
|
||||
short int bitpix; /* 32 + 2 */
|
||||
short int dim_un0; /* 34 + 2 */
|
||||
float pixdim[8]; /* 36 + 32 */
|
||||
|
||||
/* pixdim[] specifies the voxel dimensions:
|
||||
pixdim[1] - voxel width
|
||||
pixdim[2] - voxel height
|
||||
pixdim[3] - interslice distance
|
||||
..etc
|
||||
*/
|
||||
float vox_offset; /* 68 + 4 */
|
||||
float funused1; /* 72 + 4 */
|
||||
float funused2; /* 76 + 4 */
|
||||
float funused3; /* 80 + 4 */
|
||||
float cal_max; /* 84 + 4 */
|
||||
float cal_min; /* 88 + 4 */
|
||||
int compressed; /* 92 + 4 */
|
||||
int verified; /* 96 + 4 */
|
||||
int glmax, glmin; /* 100 + 8 */
|
||||
};
|
||||
|
||||
struct data_history /* data_history */
|
||||
{ /* off + size*/
|
||||
char descrip[80]; /* 0 + 80 */
|
||||
char aux_file[24]; /* 80 + 24 */
|
||||
char orient; /* 104 + 1 */
|
||||
char originator[10]; /* 105 + 10 */
|
||||
char generated[10]; /* 115 + 10 */
|
||||
char scannum[10]; /* 125 + 10 */
|
||||
char patient_id[10]; /* 135 + 10 */
|
||||
char exp_date[10]; /* 145 + 10 */
|
||||
char exp_time[10]; /* 155 + 10 */
|
||||
char hist_un0[3]; /* 165 + 3 */
|
||||
int views; /* 168 + 4 */
|
||||
int vols_added; /* 172 + 4 */
|
||||
int start_field; /* 176 + 4 */
|
||||
int field_skip; /* 180 + 4 */
|
||||
int omax,omin; /* 184 + 8 */
|
||||
int smax,smin; /* 192 + 8 */
|
||||
}; /* total=200 */
|
||||
|
||||
struct dsr /* dsr */
|
||||
{ /* off + size*/
|
||||
struct header_key hk; /* 0 + 40 */
|
||||
struct image_dimension dime; /* 40 + 108 */
|
||||
struct data_history hist; /* 148 + 200 */
|
||||
}; /* total=348 */
|
||||
|
||||
/* Acceptable values for hdr.dime.datatype */
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_BINARY 1
|
||||
#define DT_UNSIGNED_CHAR 2
|
||||
#define DT_SIGNED_SHORT 4
|
||||
#define DT_SIGNED_INT 8
|
||||
#define DT_FLOAT 16
|
||||
#define DT_COMPLEX 32
|
||||
#define DT_DOUBLE 64
|
||||
#define DT_RGB 128
|
583
libsrc/conversion/im_analyze2vips.c
Normal file
583
libsrc/conversion/im_analyze2vips.c
Normal file
@ -0,0 +1,583 @@
|
||||
/* Read a Analyze file. Old-style header (so called 7.5 format).
|
||||
*
|
||||
* 3/8/05
|
||||
* - dbh.h header from Ralph Myers
|
||||
* 22/8/05
|
||||
* - better byteswapper
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "dbh.h"
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* The things we can have in header fields. Can't use GType, since we want a
|
||||
* static value we can use in a declaration.
|
||||
*/
|
||||
typedef enum {
|
||||
BYTE,
|
||||
SHORT,
|
||||
INT,
|
||||
FLOAT,
|
||||
STRING
|
||||
} Type;
|
||||
|
||||
/* A field in the dsr header.
|
||||
*/
|
||||
typedef struct {
|
||||
const char *name; /* Eg. "header_key.sizeof_hdr" */
|
||||
Type type;
|
||||
glong offset; /* Offset in struct */
|
||||
int len; /* Sizeof ... useful for string types */
|
||||
} Field;
|
||||
|
||||
static Field dsr_header[] = {
|
||||
{ "dsr-header_key.sizeof_hdr", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hk.sizeof_hdr ), 4 },
|
||||
{ "dsr-header_key.data_type", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hk.data_type ), 10 },
|
||||
{ "dsr-header_key.db_name", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hk.db_name ), 18 },
|
||||
{ "dsr-header_key.extents", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hk.extents ), 4 },
|
||||
{ "dsr-header_key.session_error", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, hk.session_error ), 2 },
|
||||
{ "dsr-header_key.regular", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, hk.regular ), 1 },
|
||||
{ "dsr-header_key.hkey_un0", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, hk.hkey_un0 ), 1 },
|
||||
|
||||
{ "dsr-image_dimension.dim[0]", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.dim[0] ), 2 },
|
||||
{ "dsr-image_dimension.dim[1]", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.dim[1] ), 2 },
|
||||
{ "dsr-image_dimension.dim[2]", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.dim[2] ), 2 },
|
||||
{ "dsr-image_dimension.dim[3]", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.dim[3] ), 2 },
|
||||
{ "dsr-image_dimension.dim[4]", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.dim[4] ), 2 },
|
||||
{ "dsr-image_dimension.dim[5]", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.dim[5] ), 2 },
|
||||
{ "dsr-image_dimension.dim[6]", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.dim[6] ), 2 },
|
||||
{ "dsr-image_dimension.dim[7]", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.dim[7] ), 2 },
|
||||
{ "dsr-image_dimension.vox_units[0]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.vox_units[0] ), 1 },
|
||||
{ "dsr-image_dimension.vox_units[1]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.vox_units[1] ), 1 },
|
||||
{ "dsr-image_dimension.vox_units[2]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.vox_units[2] ), 1 },
|
||||
{ "dsr-image_dimension.vox_units[3]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.vox_units[3] ), 1 },
|
||||
{ "dsr-image_dimension.cal_units[0]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_units[0] ), 1 },
|
||||
{ "dsr-image_dimension.cal_units[1]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_units[1] ), 1 },
|
||||
{ "dsr-image_dimension.cal_units[2]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_units[2] ), 1 },
|
||||
{ "dsr-image_dimension.cal_units[3]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_units[3] ), 1 },
|
||||
{ "dsr-image_dimension.cal_units[4]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_units[4] ), 1 },
|
||||
{ "dsr-image_dimension.cal_units[5]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_units[5] ), 1 },
|
||||
{ "dsr-image_dimension.cal_units[6]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_units[6] ), 1 },
|
||||
{ "dsr-image_dimension.cal_units[7]", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_units[7] ), 1 },
|
||||
{ "dsr-image_dimension.data_type", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.datatype ), 2 },
|
||||
{ "dsr-image_dimension.bitpix", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.bitpix ), 2 },
|
||||
{ "dsr-image_dimension.dim_un0", SHORT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.dim_un0 ), 2 },
|
||||
{ "dsr-image_dimension.pixdim[0]", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.pixdim[0] ), 4 },
|
||||
{ "dsr-image_dimension.pixdim[1]", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.pixdim[1] ), 4 },
|
||||
{ "dsr-image_dimension.pixdim[2]", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.pixdim[2] ), 4 },
|
||||
{ "dsr-image_dimension.pixdim[3]", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.pixdim[3] ), 4 },
|
||||
{ "dsr-image_dimension.pixdim[4]", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.pixdim[4] ), 4 },
|
||||
{ "dsr-image_dimension.pixdim[5]", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.pixdim[5] ), 4 },
|
||||
{ "dsr-image_dimension.pixdim[6]", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.pixdim[6] ), 4 },
|
||||
{ "dsr-image_dimension.pixdim[7]", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.pixdim[7] ), 4 },
|
||||
{ "dsr-image_dimension.vox_offset", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.vox_offset ), 4 },
|
||||
{ "dsr-image_dimension.cal_max", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_max ), 4 },
|
||||
{ "dsr-image_dimension.cal_min", FLOAT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.cal_min ), 4 },
|
||||
{ "dsr-image_dimension.compressed", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.compressed ), 4 },
|
||||
{ "dsr-image_dimension.verified", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.verified ), 4 },
|
||||
{ "dsr-image_dimension.glmax", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.glmax ), 4 },
|
||||
{ "dsr-image_dimension.glmin", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, dime.glmin ), 4 },
|
||||
|
||||
{ "dsr-data_history.descrip", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.descrip ), 80 },
|
||||
{ "dsr-data_history.aux_file", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.aux_file ), 24 },
|
||||
{ "dsr-data_history.orient", BYTE,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.orient ), 1 },
|
||||
{ "dsr-data_history.originator", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.originator ), 10 },
|
||||
{ "dsr-data_history.generated", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.generated ), 10 },
|
||||
{ "dsr-data_history.scannum", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.scannum ), 10 },
|
||||
{ "dsr-data_history.patient_id", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.patient_id ), 10 },
|
||||
{ "dsr-data_history.exp_date", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.exp_date ), 10 },
|
||||
{ "dsr-data_history.exp_time", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.exp_time ), 10 },
|
||||
{ "dsr-data_history.hist_un0", STRING,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.hist_un0 ), 3 },
|
||||
{ "dsr-data_history.views", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.views ), 4 },
|
||||
{ "dsr-data_history.vols_added", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.vols_added ), 4 },
|
||||
{ "dsr-data_history.start_field", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.start_field ), 4 },
|
||||
{ "dsr-data_history.field_skip", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.field_skip ), 4 },
|
||||
{ "dsr-data_history.omax", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.omax ), 4 },
|
||||
{ "dsr-data_history.omin", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.omin ), 4 },
|
||||
{ "dsr-data_history.smax", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.smax ), 4 },
|
||||
{ "dsr-data_history.smin", INT,
|
||||
G_STRUCT_OFFSET( struct dsr, hist.smin ), 4 }
|
||||
};
|
||||
|
||||
/* Given a filename, generate the names for the header and the image data.
|
||||
*
|
||||
* Eg.
|
||||
* "fred" -> "fred.hdr", "fred.img"
|
||||
* "fred.img" -> "fred.hdr", "fred.img"
|
||||
*/
|
||||
static void
|
||||
generate_filenames( const char *path, char *header, char *image )
|
||||
{
|
||||
char name[FILENAME_MAX];
|
||||
char mode[FILENAME_MAX];
|
||||
|
||||
const char *olds[] = { ".img", ".hdr" };
|
||||
|
||||
/* Take off any modifiers.
|
||||
*/
|
||||
im_filename_split( path, name, mode );
|
||||
|
||||
im__change_suffix( name, header, FILENAME_MAX, ".hdr", olds, 2 );
|
||||
im__change_suffix( name, image, FILENAME_MAX, ".img", olds, 2 );
|
||||
}
|
||||
|
||||
/* str is a str which may not be NULL-terminated. Return a pointer to a static
|
||||
* buffer with a NULL-terminated version so we can safely printf() the string.
|
||||
* Also, make sure the string is plain ascii.
|
||||
*/
|
||||
static char *
|
||||
getstr( int mx, const char *str )
|
||||
{
|
||||
static char buf[256];
|
||||
int i;
|
||||
|
||||
assert( mx < 256 );
|
||||
|
||||
strncpy( buf, str, mx );
|
||||
buf[mx]= '\0';
|
||||
|
||||
/* How annoying, patient_id has some funny ctrlchars in that mess up
|
||||
* xml encode later.
|
||||
*/
|
||||
for( i = 0; i < mx && buf[i]; i++ )
|
||||
if( !isascii( buf[i] ) || buf[i] < 32 )
|
||||
buf[i] = '@';
|
||||
|
||||
return( buf );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
print_dsr( struct dsr *d )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < IM_NUMBER( dsr_header ); i++ ) {
|
||||
printf( "%s = ", dsr_header[i].name );
|
||||
|
||||
switch( dsr_header[i].type ) {
|
||||
case BYTE:
|
||||
printf( "%d\n", G_STRUCT_MEMBER( char, d,
|
||||
dsr_header[i].offset ) );
|
||||
break;
|
||||
|
||||
case SHORT:
|
||||
printf( "%d\n", G_STRUCT_MEMBER( short, d,
|
||||
dsr_header[i].offset ) );
|
||||
break;
|
||||
|
||||
case INT:
|
||||
printf( "%d\n", G_STRUCT_MEMBER( int, d,
|
||||
dsr_header[i].offset ) );
|
||||
break;
|
||||
|
||||
case FLOAT:
|
||||
printf( "%g\n", G_STRUCT_MEMBER( float, d,
|
||||
dsr_header[i].offset ) );
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
printf( "\"%s\"\n", getstr( dsr_header[i].len,
|
||||
&G_STRUCT_MEMBER( char, d,
|
||||
dsr_header[i].offset ) ) );
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /*DEBUG*/
|
||||
|
||||
static struct dsr *
|
||||
read_header( const char *header )
|
||||
{
|
||||
struct dsr *d;
|
||||
unsigned int len;
|
||||
|
||||
if( !(d = (struct dsr *) im__file_read_name( header, &len )) )
|
||||
return( NULL );
|
||||
if( len != sizeof( struct dsr ) ) {
|
||||
im_error( "im_analyze2vips",
|
||||
_( "header file size incorrect" ) );
|
||||
im_free( d );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Ouch! Should check at configure time I guess.
|
||||
*/
|
||||
assert( sizeof( struct dsr ) == 348 );
|
||||
|
||||
/* dsr headers are always SPARC byte order (MSB first). Do we need to
|
||||
* swap?
|
||||
*/
|
||||
if( !im_amiMSBfirst() ) {
|
||||
int i;
|
||||
|
||||
for( i = 0; i < IM_NUMBER( dsr_header ); i++ ) {
|
||||
unsigned char *p;
|
||||
|
||||
|
||||
switch( dsr_header[i].type ) {
|
||||
case SHORT:
|
||||
p = &G_STRUCT_MEMBER( unsigned char, d,
|
||||
dsr_header[i].offset );
|
||||
im__read_2byte( 1, p, &p );
|
||||
break;
|
||||
|
||||
case INT:
|
||||
case FLOAT:
|
||||
p = &G_STRUCT_MEMBER( unsigned char, d,
|
||||
dsr_header[i].offset );
|
||||
im__read_4byte( 1, p, &p );
|
||||
break;
|
||||
|
||||
case BYTE:
|
||||
case STRING:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( len != d->hk.sizeof_hdr ) {
|
||||
im_error( "im_analyze2vips",
|
||||
_( "header file size incorrect" ) );
|
||||
im_free( d );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( d );
|
||||
}
|
||||
|
||||
/* Try to get VIPS header properties from a dsr.
|
||||
*/
|
||||
static int
|
||||
get_vips_properties( struct dsr *d,
|
||||
int *width, int *height, int *bands, int *fmt )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( d->dime.dim[0] < 2 || d->dime.dim[0] > 7 ) {
|
||||
im_error( "im_analyze2vips",
|
||||
_( "%d-dimensional images not supported" ),
|
||||
d->dime.dim[0] );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Size of base 2d images.
|
||||
*/
|
||||
*width = d->dime.dim[1];
|
||||
*height = d->dime.dim[2];
|
||||
|
||||
for( i = 3; i <= d->dime.dim[0]; i++ )
|
||||
*height *= d->dime.dim[i];
|
||||
|
||||
/* Check it's a datatype we can handle.
|
||||
*/
|
||||
switch( d->dime.datatype ) {
|
||||
case DT_UNSIGNED_CHAR:
|
||||
*bands = 1;
|
||||
*fmt = IM_BANDFMT_UCHAR;
|
||||
break;
|
||||
|
||||
case DT_SIGNED_SHORT:
|
||||
*bands = 1;
|
||||
*fmt = IM_BANDFMT_SHORT;
|
||||
break;
|
||||
|
||||
case DT_SIGNED_INT:
|
||||
*bands = 1;
|
||||
*fmt = IM_BANDFMT_INT;
|
||||
break;
|
||||
|
||||
case DT_FLOAT:
|
||||
*bands = 1;
|
||||
*fmt = IM_BANDFMT_FLOAT;
|
||||
break;
|
||||
|
||||
case DT_COMPLEX:
|
||||
*bands = 1;
|
||||
*fmt = IM_BANDFMT_COMPLEX;
|
||||
break;
|
||||
|
||||
case DT_DOUBLE:
|
||||
*bands = 1;
|
||||
*fmt = IM_BANDFMT_DOUBLE;
|
||||
break;
|
||||
|
||||
case DT_RGB:
|
||||
*bands = 3;
|
||||
*fmt = IM_BANDFMT_UCHAR;
|
||||
break;
|
||||
|
||||
default:
|
||||
im_error( "im_analyze2vips",
|
||||
_( "datatype %d not supported" ), d->dime.datatype );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "get_vips_properties: width = %d\n", *width );
|
||||
printf( "get_vips_properties: height = %d\n", *height );
|
||||
printf( "get_vips_properties: bands = %d\n", *bands );
|
||||
printf( "get_vips_properties: fmt = %d\n", *fmt );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
attach_meta( IMAGE *out, struct dsr *d )
|
||||
{
|
||||
int i;
|
||||
|
||||
im_meta_set_blob( out, "dsr",
|
||||
(im_callback_fn) im_free, d, d->hk.sizeof_hdr );
|
||||
|
||||
for( i = 0; i < IM_NUMBER( dsr_header ); i++ ) {
|
||||
switch( dsr_header[i].type ) {
|
||||
case BYTE:
|
||||
im_meta_set_int( out, dsr_header[i].name,
|
||||
G_STRUCT_MEMBER( char, d,
|
||||
dsr_header[i].offset ) );
|
||||
break;
|
||||
|
||||
case SHORT:
|
||||
im_meta_set_int( out, dsr_header[i].name,
|
||||
G_STRUCT_MEMBER( short, d,
|
||||
dsr_header[i].offset ) );
|
||||
break;
|
||||
|
||||
case INT:
|
||||
im_meta_set_int( out, dsr_header[i].name,
|
||||
G_STRUCT_MEMBER( int, d,
|
||||
dsr_header[i].offset ) );
|
||||
break;
|
||||
|
||||
case FLOAT:
|
||||
im_meta_set_double( out, dsr_header[i].name,
|
||||
G_STRUCT_MEMBER( float, d,
|
||||
dsr_header[i].offset ) );
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
im_meta_set_string( out, dsr_header[i].name,
|
||||
getstr( dsr_header[i].len,
|
||||
&G_STRUCT_MEMBER( char, d,
|
||||
dsr_header[i].offset ) ) );
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
im_isanalyze( const char *filename )
|
||||
{
|
||||
char header[FILENAME_MAX];
|
||||
char image[FILENAME_MAX];
|
||||
struct dsr *d;
|
||||
int width, height;
|
||||
int bands;
|
||||
int fmt;
|
||||
|
||||
generate_filenames( filename, header, image );
|
||||
if( !(d = read_header( header )) )
|
||||
return( 0 );
|
||||
|
||||
#ifdef DEBUG
|
||||
print_dsr( d );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( get_vips_properties( d, &width, &height, &bands, &fmt ) ) {
|
||||
im_free( d );
|
||||
return( 0 );
|
||||
}
|
||||
im_free( d );
|
||||
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_analyze2vips_header( const char *filename, IMAGE *out )
|
||||
{
|
||||
char header[FILENAME_MAX];
|
||||
char image[FILENAME_MAX];
|
||||
struct dsr *d;
|
||||
int width, height;
|
||||
int bands;
|
||||
int fmt;
|
||||
|
||||
generate_filenames( filename, header, image );
|
||||
if( !(d = read_header( header )) )
|
||||
return( -1 );
|
||||
|
||||
#ifdef DEBUG
|
||||
print_dsr( d );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( get_vips_properties( d, &width, &height, &bands, &fmt ) ) {
|
||||
im_free( d );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
im_initdesc( out, width, height, bands, im_bits_of_fmt( fmt ), fmt,
|
||||
IM_CODING_NONE,
|
||||
bands == 1 ? IM_TYPE_B_W : IM_TYPE_sRGB,
|
||||
1.0, 1.0,
|
||||
0, 0 );
|
||||
|
||||
attach_meta( out, d );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_analyze2vips( const char *filename, IMAGE *out )
|
||||
{
|
||||
char header[FILENAME_MAX];
|
||||
char image[FILENAME_MAX];
|
||||
struct dsr *d;
|
||||
IMAGE *t[2];
|
||||
int width, height;
|
||||
int bands;
|
||||
int fmt;
|
||||
im_arch_type arch = im_amiMSBfirst() ?
|
||||
IM_ARCH_NATIVE : IM_ARCH_BYTE_SWAPPED;
|
||||
|
||||
generate_filenames( filename, header, image );
|
||||
if( !(d = read_header( header )) )
|
||||
return( -1 );
|
||||
|
||||
#ifdef DEBUG
|
||||
print_dsr( d );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( get_vips_properties( d, &width, &height, &bands, &fmt ) ||
|
||||
im_open_local_array( out, t, 2, "im_analyze2vips", "p" ) ||
|
||||
im_raw2vips( image, t[0], width, height,
|
||||
bands * im_bits_of_fmt( fmt ) / 8, 0 ) ||
|
||||
im_copy_morph( t[0], t[1], bands, fmt, IM_CODING_NONE ) ||
|
||||
im_copy_from( t[1], out, arch ) ) {
|
||||
im_free( d );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
attach_meta( out, d );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
316
libsrc/conversion/im_csv2vips.c
Normal file
316
libsrc/conversion/im_csv2vips.c
Normal file
@ -0,0 +1,316 @@
|
||||
/* Read a csv file.
|
||||
*
|
||||
* 19/12/05 JC
|
||||
* - hacked from ppm reader
|
||||
* 11/9/06
|
||||
* - now distingushes whitespace and separators, so we can have blank
|
||||
* fields
|
||||
* 20/9/06
|
||||
* - oop, unquoted trailing columns could get missed
|
||||
* 17/5/07
|
||||
* - added im_csv2vips_header()
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
static int
|
||||
skip_line( FILE *fp )
|
||||
{
|
||||
int ch;
|
||||
|
||||
while( (ch = fgetc( fp )) != '\n' && ch != EOF )
|
||||
;
|
||||
|
||||
return( ch );
|
||||
}
|
||||
|
||||
static int
|
||||
skip_white( FILE *fp, const char whitemap[256] )
|
||||
{
|
||||
int ch;
|
||||
|
||||
do {
|
||||
ch = fgetc( fp );
|
||||
} while (ch != EOF && ch != '\n' && whitemap[ch] );
|
||||
|
||||
ungetc( ch, fp );
|
||||
|
||||
return( ch );
|
||||
}
|
||||
|
||||
static int
|
||||
skip_to_sep( FILE *fp, const char sepmap[256] )
|
||||
{
|
||||
int ch;
|
||||
|
||||
do {
|
||||
ch = fgetc( fp );
|
||||
} while (ch != EOF && ch != '\n' && !sepmap[ch] );
|
||||
|
||||
ungetc( ch, fp );
|
||||
|
||||
return( ch );
|
||||
}
|
||||
|
||||
/* Read a single item. Syntax is:
|
||||
*
|
||||
* item : whitespace* double? whitespace* [EOF|EOL|separator]
|
||||
*
|
||||
* Return the char that caused failure on fail (EOF or \n).
|
||||
*/
|
||||
static int
|
||||
read_double( FILE *fp, const char whitemap[256], const char sepmap[256],
|
||||
int lineno, int colno, double *out )
|
||||
{
|
||||
int ch;
|
||||
|
||||
/* The fscanf() may change this ... but all other cases need a zero.
|
||||
*/
|
||||
*out = 0;
|
||||
|
||||
ch = skip_white( fp, whitemap );
|
||||
if( ch == EOF || ch == '\n' )
|
||||
return( ch );
|
||||
|
||||
if( !sepmap[ch] && fscanf( fp, "%lf", out ) != 1 ) {
|
||||
/* Only a warning, since (for example) exported spreadsheets
|
||||
* will often have text or date fields.
|
||||
*/
|
||||
im_warn( "im_csv2vips",
|
||||
_( "error parsing number, line %d, column %d" ),
|
||||
lineno, colno );
|
||||
|
||||
/* Step over the bad data to the next separator.
|
||||
*/
|
||||
ch = skip_to_sep( fp, sepmap );
|
||||
}
|
||||
|
||||
/* Don't need to check result, we have read a field successfully.
|
||||
*/
|
||||
ch = skip_white( fp, whitemap );
|
||||
|
||||
/* If it's a separator, we have to step over it.
|
||||
*/
|
||||
if( ch != EOF && sepmap[ch] )
|
||||
(void) fgetc( fp );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
read_csv( FILE *fp, IMAGE *out,
|
||||
int start_skip,
|
||||
const char *whitespace, const char *separator,
|
||||
int lines )
|
||||
{
|
||||
int i;
|
||||
char whitemap[256];
|
||||
char sepmap[256];
|
||||
const char *p;
|
||||
fpos_t pos;
|
||||
int columns;
|
||||
int ch;
|
||||
double d;
|
||||
double *buf;
|
||||
int y;
|
||||
|
||||
/* Make our char maps.
|
||||
*/
|
||||
for( i = 0; i < 256; i++ ) {
|
||||
whitemap[i] = 0;
|
||||
sepmap[i] = 0;
|
||||
}
|
||||
for( p = whitespace; *p; p++ )
|
||||
whitemap[(int) *p] = 1;
|
||||
for( p = separator; *p; p++ )
|
||||
sepmap[(int) *p] = 1;
|
||||
|
||||
/* Skip first few lines.
|
||||
*/
|
||||
for( i = 0; i < start_skip; i++ )
|
||||
if( skip_line( fp ) == EOF ) {
|
||||
im_error( "im_csv2vips",
|
||||
_( "end of file while skipping start" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Parse the first line to get number of columns. Only bother checking
|
||||
* fgetpos() the first time we use it: assume it's working after this.
|
||||
*/
|
||||
if( fgetpos( fp, &pos ) ) {
|
||||
im_error_system( errno, "im_csv2vips", _( "unable to seek" ) );
|
||||
return( -1 );
|
||||
}
|
||||
for( columns = 0; (ch = read_double( fp, whitemap, sepmap,
|
||||
start_skip + 1, columns + 1, &d )) == 0; columns++ )
|
||||
;
|
||||
fsetpos( fp, &pos );
|
||||
|
||||
if( columns == 0 ) {
|
||||
im_error( "im_csv2vips", _( "empty line" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( ch == -2 )
|
||||
/* Failed to parse a number.
|
||||
*/
|
||||
return( -1 );
|
||||
|
||||
/* If lines is -1, we have to parse the whole file to get the
|
||||
* number of lines out.
|
||||
*/
|
||||
if( lines == -1 ) {
|
||||
fgetpos( fp, &pos );
|
||||
for( lines = 0; skip_line( fp ) != EOF; lines++ )
|
||||
;
|
||||
fsetpos( fp, &pos );
|
||||
}
|
||||
|
||||
im_initdesc( out, columns, lines, 1,
|
||||
IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE,
|
||||
IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 );
|
||||
|
||||
if( im_outcheck( out ) || im_setupout( out ) ||
|
||||
!(buf = IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( out ), double )) )
|
||||
return( -1 );
|
||||
|
||||
for( y = 0; y < lines; y++ ) {
|
||||
int x;
|
||||
|
||||
for( x = 0; x < columns; x++ ) {
|
||||
ch = read_double( fp, whitemap, sepmap,
|
||||
y + start_skip + 1, x + 1, &d );
|
||||
if( ch == EOF ) {
|
||||
im_error( "im_csv2vips",
|
||||
_( "unexpected end of file" ) );
|
||||
return( -1 );
|
||||
}
|
||||
else if( ch == '\n' ) {
|
||||
im_error( "im_csv2vips",
|
||||
_( "unexpected end of line" ) );
|
||||
return( -1 );
|
||||
}
|
||||
else if( ch )
|
||||
/* Parse error.
|
||||
*/
|
||||
return( -1 );
|
||||
|
||||
buf[x] = d;
|
||||
}
|
||||
|
||||
if( im_writeline( y, out, (PEL *) buf ) )
|
||||
return( -1 );
|
||||
|
||||
/* Skip over the '\n' to the next line.
|
||||
*/
|
||||
skip_line( fp );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_csv2vips( const char *filename, IMAGE *out )
|
||||
{
|
||||
/* Read options.
|
||||
*/
|
||||
int start_skip = 0;
|
||||
char *whitespace = " \"";
|
||||
char *separator = ";,\t";
|
||||
int lines = -1;
|
||||
|
||||
char name[FILENAME_MAX];
|
||||
char mode[FILENAME_MAX];
|
||||
char *p, *q, *r;
|
||||
FILE *fp;
|
||||
|
||||
/* Parse mode string.
|
||||
*/
|
||||
im_filename_split( filename, name, mode );
|
||||
p = &mode[0];
|
||||
while( (q = im_getnextoption( &p )) ) {
|
||||
if( im_isprefix( "ski", q ) && (r = im_getsuboption( q )) )
|
||||
start_skip = atoi( r );
|
||||
else if( im_isprefix( "whi", q ) && (r = im_getsuboption( q )) )
|
||||
whitespace = r;
|
||||
else if( im_isprefix( "sep", q ) && (r = im_getsuboption( q )) )
|
||||
separator = r;
|
||||
else if( im_isprefix( "lin", q ) && (r = im_getsuboption( q )) )
|
||||
lines = atoi( r );
|
||||
}
|
||||
|
||||
if( !(fp = fopen( name, "r" )) ) {
|
||||
im_error( "im_csv2vips",
|
||||
_( "unable to open \"%s\"" ), name );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( read_csv( fp, out, start_skip, whitespace, separator, lines ) ) {
|
||||
fclose( fp );
|
||||
return( -1 );
|
||||
}
|
||||
fclose( fp );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We can't just read the header of a CSV. Instead, we read to a temp image,
|
||||
* then copy just the header to the output.
|
||||
*/
|
||||
int
|
||||
im_csv2vips_header( const char *filename, IMAGE *out )
|
||||
{
|
||||
IMAGE *t;
|
||||
|
||||
if( !(t = im_open( "im_csv2vips_header", "p" )) )
|
||||
return( -1 );
|
||||
if( im_csv2vips( filename, t ) ||
|
||||
im_cp_desc( out, t ) ) {
|
||||
im_close( t );
|
||||
return( -1 );
|
||||
}
|
||||
im_close( t );
|
||||
|
||||
return( 0 );
|
||||
}
|
427
libsrc/conversion/im_exr2vips.c
Normal file
427
libsrc/conversion/im_exr2vips.c
Normal file
@ -0,0 +1,427 @@
|
||||
/* Convert OpenEXR to VIPS
|
||||
*
|
||||
* 1/5/06
|
||||
* - from im_png2vips.c
|
||||
* 17/5/06
|
||||
* - oops, buffer calcs were wrong
|
||||
* 19/5/06
|
||||
* - added tiled read, with a separate cache
|
||||
* - removed *255 we had before, better to do something clever with
|
||||
* chromaticities
|
||||
|
||||
- colour management
|
||||
- attributes
|
||||
- more of OpenEXR's pixel formats
|
||||
- more than just RGBA channels
|
||||
|
||||
the openexr C API is very limited ... it seems RGBA half pixels is
|
||||
all you can do
|
||||
|
||||
openexr lets you have different formats in different channels :-(
|
||||
|
||||
there's no API to read the "chromaticities" attribute :-(
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifndef HAVE_OPENEXR
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
int
|
||||
im_exr2vips( const char *name, IMAGE *out )
|
||||
{
|
||||
im_error( "im_exr2vips", _( "OpenEXR support disabled" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_exr2vips_header( const char *name, IMAGE *out )
|
||||
{
|
||||
im_error( "im_exr2vips_header", _( "OpenEXR support disabled" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#else /*HAVE_OPENEXR*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/thread.h>
|
||||
|
||||
#include <ImfCRgbaFile.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* What we track during a OpenEXR read.
|
||||
*/
|
||||
typedef struct {
|
||||
char *name;
|
||||
IMAGE *out;
|
||||
|
||||
ImfTiledInputFile *tiles;
|
||||
ImfInputFile *lines;
|
||||
const ImfHeader *header;
|
||||
Rect window;
|
||||
int tile_width;
|
||||
int tile_height;
|
||||
|
||||
/* Need to single-thread calls to ReadTile.
|
||||
*/
|
||||
GMutex *lock;
|
||||
} Read;
|
||||
|
||||
static void
|
||||
get_imf_error( void )
|
||||
{
|
||||
im_error( "im_exr2vips", _( "EXR error: %s" ), ImfErrorMessage() );
|
||||
}
|
||||
|
||||
static void
|
||||
read_destroy( Read *read )
|
||||
{
|
||||
IM_FREE( read->name );
|
||||
|
||||
IM_FREEF( ImfCloseTiledInputFile, read->tiles );
|
||||
IM_FREEF( ImfCloseInputFile, read->lines );
|
||||
|
||||
IM_FREEF( g_mutex_free, read->lock );
|
||||
|
||||
im_free( read );
|
||||
}
|
||||
|
||||
static Read *
|
||||
read_new( const char *name, IMAGE *out )
|
||||
{
|
||||
Read *read;
|
||||
int xmin, ymin;
|
||||
int xmax, ymax;
|
||||
|
||||
if( !(read = IM_NEW( NULL, Read )) )
|
||||
return( NULL );
|
||||
|
||||
read->name = im_strdup( NULL, name );
|
||||
read->out = out;
|
||||
read->tiles = NULL;
|
||||
read->lines = NULL;
|
||||
read->lock = NULL;
|
||||
|
||||
if( im_add_close_callback( out,
|
||||
(im_callback_fn) read_destroy, read, NULL ) ) {
|
||||
read_destroy( read );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Try to open tiled first ... if that fails, fall back to scanlines.
|
||||
|
||||
FIXME ... seems a bit ugly, but how else can you spot a tiled
|
||||
EXR image?
|
||||
|
||||
*/
|
||||
if( !(read->tiles = ImfOpenTiledInputFile( read->name )) ) {
|
||||
if( !(read->lines = ImfOpenInputFile( read->name )) ) {
|
||||
get_imf_error();
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if( read->tiles )
|
||||
printf( "im_exr2vips: opening in tiles mode\n" );
|
||||
else
|
||||
printf( "im_exr2vips: opening in scanline mode\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( read->tiles ) {
|
||||
read->header = ImfTiledInputHeader( read->tiles );
|
||||
read->lock = g_mutex_new();
|
||||
read->tile_width = ImfTiledInputTileXSize( read->tiles );
|
||||
read->tile_height = ImfTiledInputTileYSize( read->tiles );
|
||||
}
|
||||
else
|
||||
read->header = ImfInputHeader( read->lines );
|
||||
|
||||
ImfHeaderDataWindow( read->header, &xmin, &ymin, &xmax, &ymax );
|
||||
read->window.left = xmin;
|
||||
read->window.top = ymin;
|
||||
read->window.width = xmax - xmin + 1;
|
||||
read->window.height = ymax - ymin + 1;
|
||||
|
||||
return( read );
|
||||
}
|
||||
|
||||
/* Read a OpenEXR file (header) into a VIPS (header).
|
||||
*/
|
||||
static int
|
||||
exr2vips_header( Read *read, IMAGE *out )
|
||||
{
|
||||
/*
|
||||
|
||||
FIXME ... not really sRGB. I think EXR is actually linear (no
|
||||
gamma). We ought to read the chromaticities from the header, put
|
||||
through a 3x3 matrix and output as XYZ
|
||||
|
||||
*/
|
||||
im_initdesc( out,
|
||||
read->window.width, read->window.height, 4,
|
||||
IM_BBITS_FLOAT, IM_BANDFMT_FLOAT,
|
||||
IM_CODING_NONE, IM_TYPE_sRGB, 1.0, 1.0, 0, 0 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a OpenEXR file header into a VIPS header.
|
||||
*/
|
||||
int
|
||||
im_exr2vips_header( const char *name, IMAGE *out )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( name, out )) ||
|
||||
exr2vips_header( read, out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
fill_region( REGION *out, void *seq, void *a, void *b )
|
||||
{
|
||||
ImfRgba *imf_buffer = (ImfRgba *) seq;
|
||||
Read *read = (Read *) a;
|
||||
Rect *r = &out->valid;
|
||||
|
||||
const int tw = read->tile_width;
|
||||
const int th = read->tile_height;
|
||||
|
||||
/* Find top left of tiles we need.
|
||||
*/
|
||||
const int xs = (r->left / tw) * tw;
|
||||
const int ys = (r->top / th) * th;
|
||||
|
||||
int x, y, z;
|
||||
Rect image;
|
||||
|
||||
/* Area of image.
|
||||
*/
|
||||
image.left = 0;
|
||||
image.top = 0;
|
||||
image.width = read->out->Xsize;
|
||||
image.height = read->out->Ysize;
|
||||
|
||||
for( y = ys; y < IM_RECT_BOTTOM( r ); y += th )
|
||||
for( x = xs; x < IM_RECT_RIGHT( r ); x += tw ) {
|
||||
Rect tile;
|
||||
Rect hit;
|
||||
int result;
|
||||
|
||||
if( !ImfTiledInputSetFrameBuffer( read->tiles,
|
||||
imf_buffer -
|
||||
(read->window.left + x) -
|
||||
(read->window.top + y) * tw,
|
||||
1, tw ) ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_exr2vips: requesting tile %d x %d\n",
|
||||
x / tw, y / th );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
g_mutex_lock( read->lock );
|
||||
result = ImfTiledInputReadTile( read->tiles,
|
||||
x / tw, y / th, 0, 0 );
|
||||
g_mutex_unlock( read->lock );
|
||||
|
||||
if( !result ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* The tile in the file, in VIPS coordinates.
|
||||
*/
|
||||
tile.left = x;
|
||||
tile.top = y;
|
||||
tile.width = tw;
|
||||
tile.height = th;
|
||||
im_rect_intersectrect( &tile, &image, &tile );
|
||||
|
||||
/* The part of this tile that hits the region.
|
||||
*/
|
||||
im_rect_intersectrect( &tile, r, &hit );
|
||||
|
||||
/* Convert to float and write to the region.
|
||||
*/
|
||||
for( z = 0; z < hit.height; z++ ) {
|
||||
ImfRgba *p = imf_buffer +
|
||||
(hit.left - tile.left) +
|
||||
(hit.top - tile.top + z) * tw;
|
||||
float *q = (float *) IM_REGION_ADDR( out,
|
||||
hit.left, hit.top + z );
|
||||
|
||||
ImfHalfToFloatArray( 4 * hit.width,
|
||||
(ImfHalf *) p, q );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Allocate a tile buffer.
|
||||
*/
|
||||
static void *
|
||||
seq_start( IMAGE *out, void *a, void *b )
|
||||
{
|
||||
Read *read = (Read *) a;
|
||||
ImfRgba *imf_buffer;
|
||||
|
||||
if( !(imf_buffer = IM_ARRAY( out,
|
||||
read->tile_width * read->tile_height, ImfRgba )) )
|
||||
return( NULL );
|
||||
|
||||
return( imf_buffer );
|
||||
}
|
||||
|
||||
/* Read tilewise.
|
||||
*/
|
||||
static int
|
||||
exr2vips_tiles( Read *read, IMAGE *out )
|
||||
{
|
||||
if( exr2vips_header( read, out ) ||
|
||||
im_poutcheck( out ) ||
|
||||
im_demand_hint( out, IM_SMALLTILE, NULL ) ||
|
||||
im_generate( out, seq_start, fill_region, NULL, read, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read scanlinewise.
|
||||
*/
|
||||
static int
|
||||
exr2vips_lines( Read *read, IMAGE *out )
|
||||
{
|
||||
const int left = read->window.left;
|
||||
const int top = read->window.top;
|
||||
const int width = read->window.width;
|
||||
const int height = read->window.height;
|
||||
|
||||
ImfRgba *imf_buffer;
|
||||
float *vips_buffer;
|
||||
int y;
|
||||
|
||||
if( !(imf_buffer = IM_ARRAY( out, width, ImfRgba )) ||
|
||||
!(vips_buffer = IM_ARRAY( out, 4 * width, float )) ||
|
||||
exr2vips_header( read, out ) ||
|
||||
im_outcheck( out ) ||
|
||||
im_setupout( out ) )
|
||||
return( -1 );
|
||||
|
||||
for( y = 0; y < height; y++ ) {
|
||||
if( !ImfInputSetFrameBuffer( read->lines,
|
||||
imf_buffer - left - (top + y) * width,
|
||||
1, width ) ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
if( !ImfInputReadPixels( read->lines, top + y, top + y ) ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
ImfHalfToFloatArray( 4 * width,
|
||||
(ImfHalf *) imf_buffer, vips_buffer );
|
||||
|
||||
if( im_writeline( y, out, (PEL *) vips_buffer ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
exr2vips( Read *read )
|
||||
{
|
||||
if( read->tiles ) {
|
||||
IMAGE *raw;
|
||||
|
||||
/* Tile cache: keep enough for two complete rows of tiles.
|
||||
* This lets us do (smallish) area ops, like im_conv(), while
|
||||
* still only hitting each OpenEXR tile once.
|
||||
*/
|
||||
if( !(raw = im_open_local( read->out, "cache", "p" )) )
|
||||
return( -1 );
|
||||
if( exr2vips_tiles( read, raw ) )
|
||||
return( -1 );
|
||||
if( im_tile_cache( raw, read->out,
|
||||
read->tile_width, read->tile_height,
|
||||
2 * (1 + raw->Xsize / read->tile_width) ) )
|
||||
return( -1 );
|
||||
}
|
||||
else {
|
||||
if( exr2vips_lines( read, read->out ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a OpenEXR file into a VIPS image.
|
||||
*/
|
||||
int
|
||||
im_exr2vips( const char *name, IMAGE *out )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_exr2vips: reading \"%s\"\n", name );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( !(read = read_new( name, out )) ||
|
||||
exr2vips( read ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif /*HAVE_OPENEXR*/
|
714
libsrc/conversion/im_jpeg2vips.c
Normal file
714
libsrc/conversion/im_jpeg2vips.c
Normal file
@ -0,0 +1,714 @@
|
||||
/* Convert 1 or 3-band 8-bit VIPS images to/from JPEG.
|
||||
*
|
||||
* 28/11/03 JC
|
||||
* - better no-overshoot on tile loop
|
||||
* 12/11/04
|
||||
* - better demand size choice for eval
|
||||
* 30/6/05 JC
|
||||
* - update im_error()/im_warn()
|
||||
* - now loads and saves exif data
|
||||
* 30/7/05
|
||||
* - now loads ICC profiles
|
||||
* - now saves ICC profiles from the VIPS header
|
||||
* 24/8/05
|
||||
* - jpeg load sets vips xres/yres from exif, if possible
|
||||
* - jpeg save sets exif xres/yres from vips, if possible
|
||||
* 29/8/05
|
||||
* - cut from old vips_jpeg.c
|
||||
* 13/10/06
|
||||
* - add </libexif/ prefix if required
|
||||
* 11/2/08
|
||||
* - spot CMYK jpegs and set Type
|
||||
* - spot Adobe CMYK JPEG and invert ink density
|
||||
* 15/2/08
|
||||
* - added "shrink" parameter
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
#define DEBUG_VERBOSE
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifndef HAVE_JPEG
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
int
|
||||
im_jpeg2vips_header( const char *name, IMAGE *out )
|
||||
{
|
||||
im_error( "im_jpeg2vips_header", _( "JPEG support disabled" ) );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_jpeg2vips( const char *name, IMAGE *out )
|
||||
{
|
||||
im_error( "im_jpeg2vips", _( "JPEG support disabled" ) );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#else /*HAVE_JPEG*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
#ifdef UNTAGGED_EXIF
|
||||
#include <exif-data.h>
|
||||
#include <exif-loader.h>
|
||||
#include <exif-ifd.h>
|
||||
#include <exif-utils.h>
|
||||
#else /*!UNTAGGED_EXIF*/
|
||||
#include <libexif/exif-data.h>
|
||||
#include <libexif/exif-loader.h>
|
||||
#include <libexif/exif-ifd.h>
|
||||
#include <libexif/exif-utils.h>
|
||||
#endif /*UNTAGGED_EXIF*/
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/vbuf.h>
|
||||
|
||||
/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we
|
||||
* also define. Make sure it's turned off.
|
||||
*/
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#undef HAVE_STDLIB_H
|
||||
#endif /*HAVE_STDLIB_H*/
|
||||
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* Define a new error handler for when we bomb out.
|
||||
*/
|
||||
typedef struct {
|
||||
/* Public fields.
|
||||
*/
|
||||
struct jpeg_error_mgr pub;
|
||||
|
||||
/* Private stuff for us.
|
||||
*/
|
||||
jmp_buf jmp; /* longjmp() here to get back to VIPS */
|
||||
FILE *fp; /* fclose() if non-NULL */
|
||||
} ErrorManager;
|
||||
|
||||
/* New output message method - send to VIPS.
|
||||
*/
|
||||
METHODDEF(void)
|
||||
new_output_message( j_common_ptr cinfo )
|
||||
{
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
|
||||
(*cinfo->err->format_message)( cinfo, buffer );
|
||||
im_error( "vips_jpeg", _( "%s" ), buffer );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_jpeg.c: new_output_message: \"%s\"\n", buffer );
|
||||
#endif /*DEBUG*/
|
||||
}
|
||||
|
||||
/* New error_exit handler.
|
||||
*/
|
||||
METHODDEF(void)
|
||||
new_error_exit( j_common_ptr cinfo )
|
||||
{
|
||||
ErrorManager *eman = (ErrorManager *) cinfo->err;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_jpeg.c: new_error_exit\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Close the fp if necessary.
|
||||
*/
|
||||
if( eman->fp ) {
|
||||
(void) fclose( eman->fp );
|
||||
eman->fp = NULL;
|
||||
}
|
||||
|
||||
/* Send the error message to VIPS. This method is overridden above.
|
||||
*/
|
||||
(*cinfo->err->output_message)( cinfo );
|
||||
|
||||
/* Jump back.
|
||||
*/
|
||||
longjmp( eman->jmp, 1 );
|
||||
}
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
#ifdef DEBUG_VERBOSE
|
||||
/* Print exif for debugging ... hacked from exif-0.6.9/actions.c
|
||||
*/
|
||||
static void
|
||||
show_tags( ExifData *data )
|
||||
{
|
||||
int i;
|
||||
unsigned int tag;
|
||||
const char *name;
|
||||
|
||||
printf( "show EXIF tags:\n" );
|
||||
|
||||
for( i = 0; i < EXIF_IFD_COUNT; i++ )
|
||||
printf( "%-7.7s", exif_ifd_get_name( i ) );
|
||||
printf( "\n" );
|
||||
|
||||
for( tag = 0; tag < 0xffff; tag++ ) {
|
||||
name = exif_tag_get_title( tag );
|
||||
if( !name )
|
||||
continue;
|
||||
printf( " 0x%04x %-29.29s", tag, name );
|
||||
for( i = 0; i < EXIF_IFD_COUNT; i++ )
|
||||
if( exif_content_get_entry( data->ifd[i], tag ) )
|
||||
printf( " * " );
|
||||
else
|
||||
printf( " - " );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
show_entry( ExifEntry *entry, void *client )
|
||||
{
|
||||
char exif_text[256];
|
||||
|
||||
printf( "%s", exif_tag_get_title( entry->tag ) );
|
||||
printf( "|" );
|
||||
printf( "%s", exif_entry_get_value( entry, exif_text, 256 ) );
|
||||
printf( "|" );
|
||||
printf( "%s", exif_format_get_name( entry->format ) );
|
||||
printf( "|" );
|
||||
printf( "%d bytes", entry->size );
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
static void
|
||||
show_ifd( ExifContent *content, void *client )
|
||||
{
|
||||
exif_content_foreach_entry( content, show_entry, client );
|
||||
printf( "-\n" );
|
||||
}
|
||||
|
||||
void
|
||||
show_values( ExifData *data )
|
||||
{
|
||||
ExifByteOrder order;
|
||||
|
||||
order = exif_data_get_byte_order( data );
|
||||
printf( "EXIF tags in '%s' byte order\n",
|
||||
exif_byte_order_get_name( order ) );
|
||||
|
||||
printf( "%-20.20s", "Tag" );
|
||||
printf( "|" );
|
||||
printf( "%-58.58s", "Value" );
|
||||
printf( "\n" );
|
||||
|
||||
exif_data_foreach_content( data, show_ifd, NULL );
|
||||
|
||||
if( data->size )
|
||||
printf( "contains thumbnail of %d bytes\n", data->size );
|
||||
}
|
||||
#endif /*DEBUG_VERBOSE*/
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
static void
|
||||
attach_exif_entry( ExifEntry *entry, IMAGE *im )
|
||||
{
|
||||
char name_text[256];
|
||||
VBuf name;
|
||||
char value_text[256];
|
||||
VBuf value;
|
||||
char exif_value[256];
|
||||
|
||||
im_buf_init_static( &name, name_text, 256 );
|
||||
im_buf_init_static( &value, value_text, 256 );
|
||||
|
||||
im_buf_appendf( &name, "exif-%s", exif_tag_get_title( entry->tag ) );
|
||||
im_buf_appendf( &value, "%s (%s, %d bytes)",
|
||||
exif_entry_get_value( entry, exif_value, 256 ),
|
||||
exif_format_get_name( entry->format ),
|
||||
entry->size );
|
||||
|
||||
/* Can't do anything sensible with the error return.
|
||||
*/
|
||||
(void) im_meta_set_string( im,
|
||||
im_buf_all( &name ), im_buf_all( &value ) );
|
||||
}
|
||||
|
||||
static void
|
||||
attach_exif_content( ExifContent *content, IMAGE *im )
|
||||
{
|
||||
exif_content_foreach_entry( content,
|
||||
(ExifContentForeachEntryFunc) attach_exif_entry, im );
|
||||
}
|
||||
|
||||
/* Just find the first occurence of the tag (is this correct?)
|
||||
*/
|
||||
static ExifEntry *
|
||||
find_entry( ExifData *ed, ExifTag tag )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < EXIF_IFD_COUNT; i++ ) {
|
||||
ExifEntry *entry;
|
||||
|
||||
if( (entry = exif_content_get_entry( ed->ifd[i], tag )) )
|
||||
return( entry );
|
||||
}
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
static int
|
||||
get_entry_rational( ExifData *ed, ExifTag tag, double *out )
|
||||
{
|
||||
ExifEntry *entry;
|
||||
ExifRational rational;
|
||||
|
||||
if( !(entry = find_entry( ed, tag )) ||
|
||||
entry->format != EXIF_FORMAT_RATIONAL ||
|
||||
entry->components != 1 )
|
||||
return( -1 );
|
||||
|
||||
rational = exif_get_rational( entry->data,
|
||||
exif_data_get_byte_order( ed ) );
|
||||
|
||||
*out = (double) rational.numerator / rational.denominator;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
get_entry_short( ExifData *ed, ExifTag tag, int *out )
|
||||
{
|
||||
ExifEntry *entry;
|
||||
|
||||
if( !(entry = find_entry( ed, tag )) ||
|
||||
entry->format != EXIF_FORMAT_SHORT ||
|
||||
entry->components != 1 )
|
||||
return( -1 );
|
||||
|
||||
*out = exif_get_short( entry->data,
|
||||
exif_data_get_byte_order( ed ) );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
set_vips_resolution( IMAGE *im, ExifData *ed )
|
||||
{
|
||||
double xres, yres;
|
||||
int unit;
|
||||
|
||||
if( get_entry_rational( ed, EXIF_TAG_X_RESOLUTION, &xres ) ||
|
||||
get_entry_rational( ed, EXIF_TAG_Y_RESOLUTION, &yres ) ||
|
||||
get_entry_short( ed, EXIF_TAG_RESOLUTION_UNIT, &unit ) ) {
|
||||
im_warn( "im_jpeg2vips", _( "error reading resolution" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
switch( unit ) {
|
||||
case 2:
|
||||
/* In inches.
|
||||
*/
|
||||
xres /= 25.4;
|
||||
yres /= 25.4;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
/* In cm.
|
||||
*/
|
||||
xres /= 10.0;
|
||||
yres /= 10.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
im_warn( "im_jpeg2vips", _( "bad resolution unit" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
im->Xres = xres;
|
||||
im->Yres = yres;
|
||||
}
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
static int
|
||||
read_exif( IMAGE *im, void *data, int data_length )
|
||||
{
|
||||
char *data_copy;
|
||||
|
||||
/* Always attach a copy of the unparsed exif data.
|
||||
*/
|
||||
if( !(data_copy = im_malloc( NULL, data_length )) )
|
||||
return( -1 );
|
||||
memcpy( data_copy, data, data_length );
|
||||
if( im_meta_set_blob( im, IM_META_EXIF_NAME,
|
||||
(im_callback_fn) im_free, data_copy, data_length ) ) {
|
||||
im_free( data_copy );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
{
|
||||
ExifData *ed;
|
||||
|
||||
if( !(ed = exif_data_new_from_data( data, data_length )) )
|
||||
return( -1 );
|
||||
|
||||
if( ed->size > 0 ) {
|
||||
#ifdef DEBUG_VERBOSE
|
||||
show_tags( ed );
|
||||
show_values( ed );
|
||||
#endif /*DEBUG_VERBOSE*/
|
||||
|
||||
/* Attach informational fields for what we find.
|
||||
|
||||
FIXME ... better to have this in the UI layer?
|
||||
|
||||
Or we could attach non-human-readable tags here (int, double
|
||||
etc) and then move the human stuff to the UI layer?
|
||||
|
||||
*/
|
||||
exif_data_foreach_content( ed,
|
||||
(ExifDataForeachContentFunc) attach_exif_content, im );
|
||||
|
||||
/* Look for resolution fields and use them to set the VIPS
|
||||
* xres/yres fields.
|
||||
*/
|
||||
set_vips_resolution( im, ed );
|
||||
}
|
||||
|
||||
exif_data_free( ed );
|
||||
}
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Number of app2 sections we can capture. Each one can be 64k, so 640k should
|
||||
* be enough for anyone (haha).
|
||||
*/
|
||||
#define MAX_APP2_SECTIONS (10)
|
||||
|
||||
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to
|
||||
* do 255-pel.
|
||||
*/
|
||||
static int
|
||||
read_jpeg_header( struct jpeg_decompress_struct *cinfo,
|
||||
IMAGE *out, gboolean *invert_pels, int shrink )
|
||||
{
|
||||
jpeg_saved_marker_ptr p;
|
||||
int type;
|
||||
|
||||
/* Capture app2 sections here for assembly.
|
||||
*/
|
||||
void *app2_data[MAX_APP2_SECTIONS] = { 0 };
|
||||
int app2_data_length[MAX_APP2_SECTIONS] = { 0 };
|
||||
int data_length;
|
||||
int i;
|
||||
|
||||
/* Read JPEG header. libjpeg will set out_color_space sanely for us
|
||||
* for YUV YCCK etc.
|
||||
*/
|
||||
jpeg_read_header( cinfo, TRUE );
|
||||
cinfo->scale_denom = shrink;
|
||||
jpeg_calc_output_dimensions( cinfo );
|
||||
*invert_pels = FALSE;
|
||||
switch( cinfo->out_color_space ) {
|
||||
case JCS_GRAYSCALE:
|
||||
type = IM_TYPE_B_W;
|
||||
break;
|
||||
|
||||
case JCS_CMYK:
|
||||
type = IM_TYPE_CMYK;
|
||||
/* Photoshop writes CMYK JPEG inverted :-( Maybe this is a
|
||||
* way to spot photoshop CMYK JPGs.
|
||||
*/
|
||||
if( cinfo->saw_Adobe_marker )
|
||||
*invert_pels = TRUE;
|
||||
break;
|
||||
|
||||
case JCS_RGB:
|
||||
default:
|
||||
type = IM_TYPE_sRGB;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set VIPS header.
|
||||
*/
|
||||
im_initdesc( out,
|
||||
cinfo->output_width, cinfo->output_height,
|
||||
cinfo->output_components,
|
||||
IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, type,
|
||||
1.0, 1.0, 0, 0 );
|
||||
|
||||
/* Look for EXIF and ICC profile.
|
||||
*/
|
||||
for( p = cinfo->marker_list; p; p = p->next ) {
|
||||
switch( p->marker ) {
|
||||
case JPEG_APP0 + 1:
|
||||
/* EXIF data.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
printf( "read_jpeg_header: seen %d bytes of EXIF\n",
|
||||
p->data_length );
|
||||
#endif /*DEBUG*/
|
||||
if( read_exif( out, p->data, p->data_length ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
case JPEG_APP0 + 2:
|
||||
/* ICC profile.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
printf( "read_jpeg_header: seen %d bytes of ICC\n",
|
||||
p->data_length );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( p->data_length > 14 &&
|
||||
im_isprefix( "ICC_PROFILE",
|
||||
(char *) p->data ) ) {
|
||||
/* cur_marker numbers from 1, according to
|
||||
* spec.
|
||||
*/
|
||||
int cur_marker = p->data[12] - 1;
|
||||
|
||||
if( cur_marker >= 0 &&
|
||||
cur_marker < MAX_APP2_SECTIONS ) {
|
||||
app2_data[cur_marker] = p->data + 14;
|
||||
app2_data_length[cur_marker] =
|
||||
p->data_length - 14;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf( "read_jpeg_header: seen %d bytes of data\n",
|
||||
p->data_length );
|
||||
#endif /*DEBUG*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assemble ICC sections.
|
||||
*/
|
||||
data_length = 0;
|
||||
for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ )
|
||||
data_length += app2_data_length[i];
|
||||
if( data_length ) {
|
||||
unsigned char *data;
|
||||
int p;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "read_jpeg_header: assembled %d byte ICC profile\n",
|
||||
data_length );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( !(data = im_malloc( NULL, data_length )) )
|
||||
return( -1 );
|
||||
|
||||
p = 0;
|
||||
for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) {
|
||||
memcpy( data + p, app2_data[i], app2_data_length[i] );
|
||||
p += app2_data_length[i];
|
||||
}
|
||||
|
||||
if( im_meta_set_blob( out, IM_META_ICC_NAME,
|
||||
(im_callback_fn) im_free, data, data_length ) ) {
|
||||
im_free( data );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a cinfo to a VIPS image.
|
||||
*/
|
||||
static int
|
||||
read_jpeg_image( struct jpeg_decompress_struct *cinfo, IMAGE *out,
|
||||
gboolean invert_pels )
|
||||
{
|
||||
int x, y, sz;
|
||||
JSAMPROW row_pointer[1];
|
||||
|
||||
/* Check VIPS.
|
||||
*/
|
||||
if( im_outcheck( out ) || im_setupout( out ) )
|
||||
return( -1 );
|
||||
|
||||
/* Get size of output line and make a buffer.
|
||||
*/
|
||||
sz = cinfo->output_width * cinfo->output_components;
|
||||
row_pointer[0] = (JSAMPLE *) (*cinfo->mem->alloc_large)
|
||||
( (j_common_ptr) cinfo, JPOOL_IMAGE, sz );
|
||||
|
||||
/* Start up decompressor.
|
||||
*/
|
||||
jpeg_start_decompress( cinfo );
|
||||
|
||||
/* Process image.
|
||||
*/
|
||||
for( y = 0; y < out->Ysize; y++ ) {
|
||||
if( jpeg_read_scanlines( cinfo, &row_pointer[0], 1 ) != 1 ) {
|
||||
im_error( "im_jpeg2vips", _( "truncated JPEG file" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( invert_pels ) {
|
||||
for( x = 0; x < sz; x++ )
|
||||
row_pointer[0][x] = 255 - row_pointer[0][x];
|
||||
}
|
||||
if( im_writeline( y, out, row_pointer[0] ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Stop decompressor.
|
||||
*/
|
||||
jpeg_finish_decompress( cinfo );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a JPEG file into a VIPS image.
|
||||
*/
|
||||
static int
|
||||
jpeg2vips( const char *name, IMAGE *out, gboolean header_only )
|
||||
{
|
||||
char filename[FILENAME_MAX];
|
||||
char mode[FILENAME_MAX];
|
||||
char *p, *q;
|
||||
int shrink;
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
ErrorManager eman;
|
||||
FILE *fp;
|
||||
int result;
|
||||
gboolean invert_pels;
|
||||
|
||||
/* Parse the filename.
|
||||
*/
|
||||
im_filename_split( name, filename, mode );
|
||||
p = &mode[0];
|
||||
shrink = 1;
|
||||
if( (q = im_getnextoption( &p )) ) {
|
||||
shrink = atoi( q );
|
||||
|
||||
if( shrink != 1 && shrink != 2 &&
|
||||
shrink != 4 && shrink != 8 ) {
|
||||
im_error( "im_jpeg2vips",
|
||||
_( "bad shrink factor %d" ), shrink );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Make jpeg compression object.
|
||||
*/
|
||||
cinfo.err = jpeg_std_error( &eman.pub );
|
||||
eman.pub.error_exit = new_error_exit;
|
||||
eman.pub.output_message = new_output_message;
|
||||
eman.fp = NULL;
|
||||
if( setjmp( eman.jmp ) ) {
|
||||
/* Here for longjmp() from new_error_exit().
|
||||
*/
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
jpeg_create_decompress( &cinfo );
|
||||
|
||||
/* Make input.
|
||||
*/
|
||||
#ifdef BINARY_OPEN
|
||||
if( !(fp = fopen( filename, "rb" )) ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( !(fp = fopen( filename, "r" )) ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
im_error( "im_jpeg2vips",
|
||||
_( "unable to open \"%s\"" ), filename );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
eman.fp = fp;
|
||||
jpeg_stdio_src( &cinfo, fp );
|
||||
|
||||
/* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile).
|
||||
*/
|
||||
jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff );
|
||||
jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff );
|
||||
|
||||
/* Convert!
|
||||
*/
|
||||
result = read_jpeg_header( &cinfo, out, &invert_pels, shrink );
|
||||
if( !header_only && !result )
|
||||
result = read_jpeg_image( &cinfo, out, invert_pels );
|
||||
|
||||
/* Close and tidy.
|
||||
*/
|
||||
fclose( fp );
|
||||
eman.fp = NULL;
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
|
||||
if( eman.pub.num_warnings != 0 ) {
|
||||
im_warn( "im_jpeg2vips", _( "read gave %ld warnings" ),
|
||||
eman.pub.num_warnings );
|
||||
im_warn( "im_jpeg2vips", "%s", im_error_buffer() );
|
||||
}
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/* Read a JPEG file into a VIPS image.
|
||||
*/
|
||||
int
|
||||
im_jpeg2vips( const char *name, IMAGE *out )
|
||||
{
|
||||
return( jpeg2vips( name, out, FALSE ) );
|
||||
}
|
||||
|
||||
int
|
||||
im_jpeg2vips_header( const char *name, IMAGE *out )
|
||||
{
|
||||
return( jpeg2vips( name, out, TRUE ) );
|
||||
}
|
||||
|
||||
#endif /*HAVE_JPEG*/
|
650
libsrc/conversion/im_magick2vips.c
Normal file
650
libsrc/conversion/im_magick2vips.c
Normal file
@ -0,0 +1,650 @@
|
||||
/* Read a file using libMagick
|
||||
*
|
||||
* 7/1/03 JC
|
||||
* - from im_tiff2vips
|
||||
* 3/2/03 JC
|
||||
* - some InitializeMagick() fail with NULL arg
|
||||
* 2/11/04
|
||||
* - im_magick2vips_header() also checks sensible width/height
|
||||
* 28/10/05
|
||||
* - copy attributes to meta
|
||||
* - write many-frame images as a big column if all frames have identical
|
||||
* width/height/bands/depth
|
||||
* 31/3/06
|
||||
* - test for magick attr support
|
||||
* 8/5/06
|
||||
* - set RGB16/GREY16 if appropriate
|
||||
* 10/8/07
|
||||
* - support 32/64 bit imagemagick too
|
||||
* 21/2/08
|
||||
* - use MaxRGB if QuantumRange is missing (thanks Bob)
|
||||
* - look for MAGICKCORE_HDRI_SUPPORT (thanks Marcel)
|
||||
* - use image->attributes if GetNextImageAttribute() is missing
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/* Turn on debugging output.
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifndef HAVE_MAGICK
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
int
|
||||
im_magick2vips( const char *filename, IMAGE *im )
|
||||
{
|
||||
im_error( "im_magick2vips", _( "libMagick support disabled" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_magick2vips_header( const char *filename, IMAGE *im )
|
||||
{
|
||||
im_error( "im_magick2vips", _( "libMagick support disabled" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#else /*HAVE_MAGICK*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/vbuf.h>
|
||||
#include <vips/thread.h>
|
||||
|
||||
#include <magick/api.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* pre-float Magick used to call this MaxRGB.
|
||||
*/
|
||||
#if !defined(QuantumRange)
|
||||
# define QuantumRange MaxRGB
|
||||
#endif
|
||||
|
||||
/* And this used to be UseHDRI.
|
||||
*/
|
||||
#if MAGICKCORE_HDRI_SUPPORT
|
||||
# define UseHDRI=1
|
||||
#endif
|
||||
|
||||
/* What we track during a read call.
|
||||
*/
|
||||
typedef struct _Read {
|
||||
char *filename;
|
||||
IMAGE *im;
|
||||
|
||||
Image *image;
|
||||
ImageInfo *image_info;
|
||||
ExceptionInfo exception;
|
||||
|
||||
int n_frames;
|
||||
Image **frames;
|
||||
int frame_height;
|
||||
|
||||
/* Mutex to serialise calls to libMagick during threaded read.
|
||||
*/
|
||||
GMutex *lock;
|
||||
} Read;
|
||||
|
||||
static int
|
||||
read_destroy( Read *read )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf( "im_magick2vips: read_destroy: %s\n", read->filename );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
IM_FREEF( DestroyImage, read->image );
|
||||
IM_FREEF( DestroyImageInfo, read->image_info );
|
||||
IM_FREE( read->frames );
|
||||
IM_FREE( read->filename );
|
||||
DestroyExceptionInfo( &read->exception );
|
||||
IM_FREEF( g_mutex_free, read->lock );
|
||||
im_free( read );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static Read *
|
||||
read_new( const char *filename, IMAGE *im )
|
||||
{
|
||||
Read *read;
|
||||
static int inited = 0;
|
||||
|
||||
if( !inited ) {
|
||||
InitializeMagick( "" );
|
||||
inited = 1;
|
||||
}
|
||||
|
||||
if( !(read = IM_NEW( NULL, Read )) )
|
||||
return( NULL );
|
||||
read->filename = im_strdup( NULL, filename );
|
||||
read->im = im;
|
||||
read->image = NULL;
|
||||
read->image_info = CloneImageInfo( NULL );
|
||||
GetExceptionInfo( &read->exception );
|
||||
read->n_frames = 0;
|
||||
read->frames = NULL;
|
||||
read->frame_height = 0;
|
||||
read->lock = g_mutex_new();
|
||||
|
||||
if( im_add_close_callback( im,
|
||||
(im_callback_fn) read_destroy, read, NULL ) ) {
|
||||
read_destroy( read );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( !read->filename || !read->image_info )
|
||||
return( NULL );
|
||||
|
||||
im_strncpy( read->image_info->filename, filename, MaxTextExtent );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_magick2vips: read_new: %s\n", read->filename );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( read );
|
||||
}
|
||||
|
||||
static int
|
||||
get_bands( Image *image )
|
||||
{
|
||||
int bands;
|
||||
|
||||
switch( image->colorspace ) {
|
||||
case GRAYColorspace:
|
||||
bands = 1;
|
||||
break;
|
||||
|
||||
case RGBColorspace:
|
||||
bands = 3;
|
||||
break;
|
||||
|
||||
case sRGBColorspace:
|
||||
bands = 3;
|
||||
break;
|
||||
|
||||
case CMYKColorspace:
|
||||
bands = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
im_error( "im_magick2vips", _( "unsupported colorspace %d" ),
|
||||
(int) image->colorspace );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Alpha as well?
|
||||
*/
|
||||
if( image->matte ) {
|
||||
assert( image->colorspace != CMYKColorspace );
|
||||
bands += 1;
|
||||
}
|
||||
|
||||
return( bands );
|
||||
}
|
||||
|
||||
static int
|
||||
parse_header( Read *read )
|
||||
{
|
||||
IMAGE *im = read->im;
|
||||
Image *image = read->image;
|
||||
|
||||
#ifdef HAVE_MAGICK_ATTR
|
||||
const ImageAttribute *attr;
|
||||
#endif /*HAVE_MAGICK_ATTR*/
|
||||
Image *p;
|
||||
int i;
|
||||
|
||||
im->Xsize = image->columns;
|
||||
im->Ysize = image->rows;
|
||||
read->frame_height = image->rows;
|
||||
if( (im->Bands = get_bands( image )) < 0 )
|
||||
return( -1 );
|
||||
|
||||
switch( image->depth ) {
|
||||
case 8:
|
||||
im->BandFmt = IM_BANDFMT_UCHAR;
|
||||
im->Bbits = IM_BBITS_BYTE;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
im->BandFmt = IM_BANDFMT_USHORT;
|
||||
im->Bbits = IM_BBITS_SHORT;
|
||||
break;
|
||||
|
||||
#ifdef UseHDRI
|
||||
case 32:
|
||||
im->BandFmt = IM_BANDFMT_FLOAT;
|
||||
im->Bbits = IM_BBITS_FLOAT;
|
||||
break;
|
||||
|
||||
case 64:
|
||||
im->BandFmt = IM_BANDFMT_DOUBLE;
|
||||
im->Bbits = IM_BBITS_DOUBLE;
|
||||
break;
|
||||
#else /*!UseHDRI*/
|
||||
case 32:
|
||||
im->BandFmt = IM_BANDFMT_UINT;
|
||||
im->Bbits = IM_BBITS_INT;
|
||||
break;
|
||||
|
||||
/* No 64-bit int format in VIPS.
|
||||
*/
|
||||
#endif /*UseHDRI*/
|
||||
|
||||
default:
|
||||
im_error( "im_magick2vips", _( "unsupported bit depth %d" ),
|
||||
(int) image->depth );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
switch( image->colorspace ) {
|
||||
case GRAYColorspace:
|
||||
if( im->BandFmt == IM_BANDFMT_USHORT )
|
||||
im->Type = IM_TYPE_GREY16;
|
||||
else
|
||||
im->Type = IM_TYPE_B_W;
|
||||
break;
|
||||
|
||||
case RGBColorspace:
|
||||
if( im->BandFmt == IM_BANDFMT_USHORT )
|
||||
im->Type = IM_TYPE_RGB16;
|
||||
else
|
||||
im->Type = IM_TYPE_RGB;
|
||||
break;
|
||||
|
||||
case sRGBColorspace:
|
||||
if( im->BandFmt == IM_BANDFMT_USHORT )
|
||||
im->Type = IM_TYPE_RGB16;
|
||||
else
|
||||
im->Type = IM_TYPE_sRGB;
|
||||
break;
|
||||
|
||||
case CMYKColorspace:
|
||||
im->Type = IM_TYPE_CMYK;
|
||||
break;
|
||||
|
||||
default:
|
||||
im_error( "im_magick2vips", _( "unsupported colorspace %d" ),
|
||||
(int) image->colorspace );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
switch( image->units ) {
|
||||
case PixelsPerInchResolution:
|
||||
im->Xres = image->x_resolution / 25.4;
|
||||
im->Yres = image->y_resolution / 25.4;
|
||||
break;
|
||||
|
||||
case PixelsPerCentimeterResolution:
|
||||
im->Xres = image->x_resolution / 10.0;
|
||||
im->Yres = image->y_resolution / 10.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
im->Xres = 1.0;
|
||||
im->Yres = 1.0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Other fields.
|
||||
*/
|
||||
im->Coding = IM_CODING_NONE;
|
||||
|
||||
#ifdef HAVE_MAGICK_ATTR
|
||||
#ifdef HAVE_GETNEXTIMAGEATTRIBUTE
|
||||
/* Gah, magick6.something and later only. Attach any attributes.
|
||||
*/
|
||||
ResetImageAttributeIterator( image );
|
||||
while( (attr = GetNextImageAttribute( image )) ) {
|
||||
#elif defined(HAVE_GETIMAGEATTRIBUTE)
|
||||
/* GraphicsMagick is missing the iterator: we have to loop ourselves.
|
||||
* ->attributes is marked as private in the header, but there's no
|
||||
* getter so we have to access it directly.
|
||||
*/
|
||||
for( attr = image->attributes; attr; attr = attr->next ) {
|
||||
#else /*stuff*/
|
||||
#error attributes enabled, but no access funcs found
|
||||
#endif
|
||||
char name_text[256];
|
||||
VBuf name;
|
||||
|
||||
im_buf_init_static( &name, name_text, 256 );
|
||||
im_buf_appendf( &name, "magick-%s", attr->key );
|
||||
im_meta_set_string( im, im_buf_all( &name ), attr->value );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "key = \"%s\", value = \"%s\"\n",
|
||||
attr->key, attr->value );
|
||||
#endif /*DEBUG*/
|
||||
}
|
||||
#endif /*HAVE_MAGICK_ATTR*/
|
||||
|
||||
/* Do we have a set of equal-sized frames? Append them.
|
||||
|
||||
FIXME ... there must be an attribute somewhere from dicom read
|
||||
which says this is a volumetric image
|
||||
|
||||
*/
|
||||
read->n_frames = 0;
|
||||
for( p = image; p; (p = GetNextImageInList( p )) ) {
|
||||
if( p->columns != im->Xsize ||
|
||||
p->rows != im->Ysize ||
|
||||
p->depth != im->Bbits ||
|
||||
get_bands( p ) != im->Bands )
|
||||
break;
|
||||
|
||||
read->n_frames += 1;
|
||||
}
|
||||
if( p )
|
||||
/* Nope ... just do the first image in the list.
|
||||
*/
|
||||
read->n_frames = 1;
|
||||
|
||||
/* Record frame pointers.
|
||||
*/
|
||||
im->Ysize *= read->n_frames;
|
||||
if( !(read->frames = IM_ARRAY( NULL, read->n_frames, Image * )) )
|
||||
return( -1 );
|
||||
p = image;
|
||||
for( i = 0; i < read->n_frames; i++ ) {
|
||||
read->frames[i] = p;
|
||||
p = GetNextImageInList( p );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Divide by this to get 0 - MAX from a Quantum. Eg. consider QuantumRange ==
|
||||
* 65535, MAX == 255 (a Q16 ImageMagic representing an 8-bit image). Make sure
|
||||
* this can't be zero (if QuantumRange < MAX) .. can happen if we have a Q8
|
||||
* ImageMagick trying to represent a 16-bit image.
|
||||
*/
|
||||
#define SCALE( MAX ) \
|
||||
(QuantumRange < (MAX) ? \
|
||||
1 : \
|
||||
((QuantumRange + 1) / ((MAX) + 1)))
|
||||
|
||||
#define GRAY_LOOP( TYPE, MAX ) { \
|
||||
TYPE *q = (TYPE *) q8; \
|
||||
\
|
||||
for( x = 0; x < n; x++ ) \
|
||||
q[x] = pixels[x].green / SCALE( MAX ); \
|
||||
}
|
||||
|
||||
#define GRAYA_LOOP( TYPE, MAX ) { \
|
||||
TYPE *q = (TYPE *) q8; \
|
||||
\
|
||||
for( x = 0; x < n; x++ ) { \
|
||||
q[0] = pixels[x].green / SCALE( MAX ); \
|
||||
q[1] = pixels[x].opacity / SCALE( MAX ); \
|
||||
\
|
||||
q += 2; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define RGB_LOOP( TYPE, MAX ) { \
|
||||
TYPE *q = (TYPE *) q8; \
|
||||
\
|
||||
for( x = 0; x < n; x++ ) { \
|
||||
q[0] = pixels[x].red / SCALE( MAX ); \
|
||||
q[1] = pixels[x].green / SCALE( MAX ); \
|
||||
q[2] = pixels[x].blue / SCALE( MAX ); \
|
||||
\
|
||||
q += 3; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define RGBA_LOOP( TYPE, MAX ) { \
|
||||
TYPE *q = (TYPE *) q8; \
|
||||
\
|
||||
for( x = 0; x < n; x++ ) { \
|
||||
q[0] = pixels[x].red / SCALE( MAX ); \
|
||||
q[1] = pixels[x].green / SCALE( MAX ); \
|
||||
q[2] = pixels[x].blue / SCALE( MAX ); \
|
||||
q[3] = pixels[x].opacity / SCALE( MAX ); \
|
||||
\
|
||||
q += 4; \
|
||||
} \
|
||||
}
|
||||
|
||||
static void
|
||||
unpack_pixels( IMAGE *im, PEL *q8, PixelPacket *pixels, int n )
|
||||
{
|
||||
int x;
|
||||
|
||||
switch( im->Bands ) {
|
||||
case 1:
|
||||
/* Gray.
|
||||
*/
|
||||
switch( im->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
GRAY_LOOP( unsigned char, 255 ); break;
|
||||
case IM_BANDFMT_USHORT:
|
||||
GRAY_LOOP( unsigned short, 65535 ); break;
|
||||
case IM_BANDFMT_UINT:
|
||||
GRAY_LOOP( unsigned int, 4294967295UL ); break;
|
||||
case IM_BANDFMT_DOUBLE:
|
||||
GRAY_LOOP( double, QuantumRange ); break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* Gray plus alpha.
|
||||
*/
|
||||
switch( im->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
GRAYA_LOOP( unsigned char, 255 ); break;
|
||||
case IM_BANDFMT_USHORT:
|
||||
GRAYA_LOOP( unsigned short, 65535 ); break;
|
||||
case IM_BANDFMT_UINT:
|
||||
GRAYA_LOOP( unsigned int, 4294967295UL ); break;
|
||||
case IM_BANDFMT_DOUBLE:
|
||||
GRAYA_LOOP( double, QuantumRange ); break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
/* RGB.
|
||||
*/
|
||||
switch( im->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
RGB_LOOP( unsigned char, 255 ); break;
|
||||
case IM_BANDFMT_USHORT:
|
||||
RGB_LOOP( unsigned short, 65535 ); break;
|
||||
case IM_BANDFMT_UINT:
|
||||
RGB_LOOP( unsigned int, 4294967295UL ); break;
|
||||
case IM_BANDFMT_DOUBLE:
|
||||
RGB_LOOP( double, QuantumRange ); break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
/* RGBA or CMYK.
|
||||
*/
|
||||
switch( im->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
RGBA_LOOP( unsigned char, 255 ); break;
|
||||
case IM_BANDFMT_USHORT:
|
||||
RGBA_LOOP( unsigned short, 65535 ); break;
|
||||
case IM_BANDFMT_UINT:
|
||||
RGBA_LOOP( unsigned int, 4294967295UL ); break;
|
||||
case IM_BANDFMT_DOUBLE:
|
||||
RGBA_LOOP( double, QuantumRange ); break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
static PixelPacket *
|
||||
get_pixels( Image *image, int left, int top, int width, int height )
|
||||
{
|
||||
PixelPacket *pixels;
|
||||
|
||||
if( !(pixels = GetImagePixels( image, left, top, width, height )) )
|
||||
return( NULL );
|
||||
|
||||
/* Can't happen if red/green/blue are doubles.
|
||||
*/
|
||||
#ifndef UseHDRI
|
||||
/* Unpack palette.
|
||||
*/
|
||||
if( image->storage_class == PseudoClass ) {
|
||||
IndexPacket *indexes = GetIndexes( image );
|
||||
int i;
|
||||
|
||||
for( i = 0; i < width * height; i++ ) {
|
||||
IndexPacket x = indexes[i];
|
||||
|
||||
if( x < image->colors ) {
|
||||
pixels[i].red = image->colormap[x].red;
|
||||
pixels[i].green = image->colormap[x].green;
|
||||
pixels[i].blue = image->colormap[x].blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /*UseHDRI*/
|
||||
|
||||
return( pixels );
|
||||
}
|
||||
|
||||
static int
|
||||
magick_fill_region( REGION *out, void *seq, void *a, void *b )
|
||||
{
|
||||
Read *read = (Read *) a;
|
||||
Rect *r = &out->valid;
|
||||
int y;
|
||||
|
||||
for( y = 0; y < r->height; y++ ) {
|
||||
int top = r->top + y;
|
||||
int frame = top / read->frame_height;
|
||||
int line = top % read->frame_height;
|
||||
|
||||
PixelPacket *pixels;
|
||||
|
||||
g_mutex_lock( read->lock );
|
||||
pixels = get_pixels( read->frames[frame],
|
||||
r->left, line, r->width, 1 );
|
||||
g_mutex_unlock( read->lock );
|
||||
|
||||
if( !pixels ) {
|
||||
im_error( "im_magick2vips",
|
||||
_( "unable to read pixels" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
unpack_pixels( read->im,
|
||||
(PEL *) IM_REGION_ADDR( out, r->left, top ),
|
||||
pixels, r->width );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_magick2vips( const char *filename, IMAGE *im )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( filename, im )) )
|
||||
return( -1 );
|
||||
|
||||
read->image = ReadImage( read->image_info, &read->exception );
|
||||
if( !read->image ) {
|
||||
im_error( "im_magick2vips", _( "unable to read file \"%s\"\n"
|
||||
"libMagick error: %s %s" ),
|
||||
filename,
|
||||
read->exception.reason, read->exception.description );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( parse_header( read ) ||
|
||||
im_poutcheck( im ) ||
|
||||
im_generate( im, NULL, magick_fill_region, NULL, read, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_magick2vips_header( const char *filename, IMAGE *im )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( filename, im )) )
|
||||
return( -1 );
|
||||
|
||||
read->image = PingImage( read->image_info, &read->exception );
|
||||
if( !read->image ) {
|
||||
im_error( "im_magick2vips", _( "unable to ping file "
|
||||
"\"%s\"\nlibMagick error: %s %s" ),
|
||||
filename,
|
||||
read->exception.reason, read->exception.description );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( parse_header( read ) )
|
||||
return( -1 );
|
||||
|
||||
if( im->Xsize <= 0 || im->Ysize <= 0 ) {
|
||||
im_error( "im_magick2vips", _( "bad image size" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif /*HAVE_MAGICK*/
|
381
libsrc/conversion/im_png2vips.c
Normal file
381
libsrc/conversion/im_png2vips.c
Normal file
@ -0,0 +1,381 @@
|
||||
/* Convert 1 to 4-band 8 or 16-bit VIPS images to/from PNG.
|
||||
*
|
||||
* 28/11/03 JC
|
||||
* - better no-overshoot on tile loop
|
||||
* 22/2/05
|
||||
* - read non-interlaced PNG with a line buffer (thanks Michel Brabants)
|
||||
* 11/1/06
|
||||
* - read RGBA palette-ized images more robustly (thanks Tom)
|
||||
* 20/4/06
|
||||
* - auto convert to sRGB/mono (with optional alpha) for save
|
||||
* 1/5/06
|
||||
* - from vips_png.c
|
||||
* 8/5/06
|
||||
* - set RGB16/GREY16 if appropriate
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifndef HAVE_PNG
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
int
|
||||
im_png2vips( const char *name, IMAGE *out )
|
||||
{
|
||||
im_error( "im_png2vips", _( "PNG support disabled" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_png2vips_header( const char *name, IMAGE *out )
|
||||
{
|
||||
im_error( "im_png2vips_header", _( "PNG support disabled" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#else /*HAVE_PNG*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
#if PNG_LIBPNG_VER < 10003
|
||||
#error "PNG library too old."
|
||||
#endif
|
||||
|
||||
static void
|
||||
user_error_function( png_structp png_ptr, png_const_charp error_msg )
|
||||
{
|
||||
im_error( "im_png2vips", _( "PNG error: \"%s\"" ), error_msg );
|
||||
}
|
||||
|
||||
static void
|
||||
user_warning_function( png_structp png_ptr, png_const_charp warning_msg )
|
||||
{
|
||||
im_error( "im_png2vips", _( "PNG warning: \"%s\"" ), warning_msg );
|
||||
}
|
||||
|
||||
/* What we track during a PNG read.
|
||||
*/
|
||||
typedef struct {
|
||||
char *name;
|
||||
IMAGE *out;
|
||||
|
||||
FILE *fp;
|
||||
png_structp pPng;
|
||||
png_infop pInfo;
|
||||
png_bytep *row_pointer;
|
||||
png_bytep data;
|
||||
} Read;
|
||||
|
||||
static void
|
||||
read_destroy( Read *read )
|
||||
{
|
||||
if( read->name ) {
|
||||
im_free( read->name );
|
||||
read->name = NULL;
|
||||
}
|
||||
if( read->fp ) {
|
||||
fclose( read->fp );
|
||||
read->fp = NULL;
|
||||
}
|
||||
if( read->pPng )
|
||||
png_destroy_read_struct( &read->pPng, &read->pInfo, NULL );
|
||||
if( read->row_pointer ) {
|
||||
im_free( read->row_pointer );
|
||||
read->row_pointer = NULL;
|
||||
}
|
||||
if( read->data ) {
|
||||
im_free( read->data );
|
||||
read->data = NULL;
|
||||
}
|
||||
|
||||
im_free( read );
|
||||
}
|
||||
|
||||
static Read *
|
||||
read_new( const char *name, IMAGE *out )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = IM_NEW( NULL, Read )) )
|
||||
return( NULL );
|
||||
|
||||
read->name = im_strdup( NULL, name );
|
||||
read->out = out;
|
||||
read->fp = NULL;
|
||||
read->pPng = NULL;
|
||||
read->pInfo = NULL;
|
||||
read->row_pointer = NULL;
|
||||
read->data = NULL;
|
||||
|
||||
#ifdef BINARY_OPEN
|
||||
if( !(read->fp = fopen( name, "rb" )) ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( !(read->fp = fopen( name, "r" )) ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
read_destroy( read );
|
||||
im_error( "im_png2vips", _( "unable to open \"%s\"" ), name );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( !(read->pPng = png_create_read_struct(
|
||||
PNG_LIBPNG_VER_STRING, NULL,
|
||||
user_error_function, user_warning_function )) ) {
|
||||
read_destroy( read );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Catch PNG errors from png_create_info_struct().
|
||||
*/
|
||||
if( setjmp( read->pPng->jmpbuf ) ) {
|
||||
read_destroy( read );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( !(read->pInfo = png_create_info_struct( read->pPng )) ) {
|
||||
read_destroy( read );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( read );
|
||||
}
|
||||
|
||||
/* Yuk! Have to malloc enough space for the whole image. Interlaced PNG
|
||||
* is not really suitable for large objects ...
|
||||
*/
|
||||
static int
|
||||
png2vips_interlace( Read *read )
|
||||
{
|
||||
const int rowbytes = IM_IMAGE_SIZEOF_LINE( read->out );
|
||||
int y;
|
||||
|
||||
if( !(read->row_pointer = IM_ARRAY( NULL,
|
||||
read->pInfo->height, png_bytep )) )
|
||||
return( -1 );
|
||||
if( !(read->data = (png_bytep) im_malloc( NULL,
|
||||
read->pInfo->height * rowbytes )) )
|
||||
return( -1 );
|
||||
|
||||
for( y = 0; y < (int) read->pInfo->height; y++ )
|
||||
read->row_pointer[y] = read->data + y * rowbytes;
|
||||
if( im_outcheck( read->out ) ||
|
||||
im_setupout( read->out ) ||
|
||||
setjmp( read->pPng->jmpbuf ) )
|
||||
return( -1 );
|
||||
|
||||
png_read_image( read->pPng, read->row_pointer );
|
||||
|
||||
for( y = 0; y < (int) read->pInfo->height; y++ )
|
||||
if( im_writeline( y, read->out, read->row_pointer[y] ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Noninterlaced images can be read without needing enough RAM for the whole
|
||||
* image.
|
||||
*/
|
||||
static int
|
||||
png2vips_noninterlace( Read *read )
|
||||
{
|
||||
const int rowbytes = IM_IMAGE_SIZEOF_LINE( read->out );
|
||||
int y;
|
||||
|
||||
if( !(read->data = (png_bytep) im_malloc( NULL, rowbytes )) )
|
||||
return( -1 );
|
||||
if( im_outcheck( read->out ) ||
|
||||
im_setupout( read->out ) ||
|
||||
setjmp( read->pPng->jmpbuf ) )
|
||||
return( -1 );
|
||||
|
||||
for( y = 0; y < (int) read->pInfo->height; y++ ) {
|
||||
png_read_row( read->pPng, read->data, NULL );
|
||||
|
||||
if( im_writeline( y, read->out, read->data ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a PNG file (header) into a VIPS (header).
|
||||
*/
|
||||
static int
|
||||
png2vips( Read *read, int header_only )
|
||||
{
|
||||
int bands, bpp, type;
|
||||
|
||||
if( setjmp( read->pPng->jmpbuf ) )
|
||||
return( -1 );
|
||||
|
||||
png_init_io( read->pPng, read->fp );
|
||||
png_read_info( read->pPng, read->pInfo );
|
||||
|
||||
/* png_get_channels() gives us 1 band for palette images ... so look
|
||||
* at colour_type for output bands.
|
||||
*/
|
||||
switch( read->pInfo->color_type ) {
|
||||
case PNG_COLOR_TYPE_PALETTE:
|
||||
bands = 3;
|
||||
|
||||
/* Don't know if this is really correct. If there are
|
||||
* transparent pixels, assume we're going to output RGBA.
|
||||
*/
|
||||
if( read->pInfo->num_trans )
|
||||
bands = 4;
|
||||
|
||||
break;
|
||||
|
||||
case PNG_COLOR_TYPE_GRAY: bands = 1; break;
|
||||
case PNG_COLOR_TYPE_GRAY_ALPHA: bands = 2; break;
|
||||
case PNG_COLOR_TYPE_RGB: bands = 3; break;
|
||||
case PNG_COLOR_TYPE_RGB_ALPHA: bands = 4; break;
|
||||
|
||||
default:
|
||||
im_error( "im_png2vips", _( "unsupported colour type" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* 8 or 16 bit.
|
||||
*/
|
||||
bpp = read->pInfo->bit_depth > 8 ? 2 : 1;
|
||||
|
||||
if( bpp > 1 ) {
|
||||
if( bands < 3 )
|
||||
type = IM_TYPE_GREY16;
|
||||
else
|
||||
type = IM_TYPE_RGB16;
|
||||
}
|
||||
else {
|
||||
if( bands < 3 )
|
||||
type = IM_TYPE_B_W;
|
||||
else
|
||||
type = IM_TYPE_sRGB;
|
||||
}
|
||||
|
||||
/* Expand palette images.
|
||||
*/
|
||||
if( read->pInfo->color_type == PNG_COLOR_TYPE_PALETTE )
|
||||
png_set_expand( read->pPng );
|
||||
|
||||
/* Expand <8 bit images to full bytes.
|
||||
*/
|
||||
if( read->pInfo->bit_depth < 8 )
|
||||
png_set_packing( read->pPng );
|
||||
|
||||
/* If we're an INTEL byte order machine and this is 16bits, we need
|
||||
* to swap bytes.
|
||||
*/
|
||||
if( read->pInfo->bit_depth > 8 && !im_amiMSBfirst() )
|
||||
png_set_swap( read->pPng );
|
||||
|
||||
/* Set VIPS header.
|
||||
*/
|
||||
im_initdesc( read->out,
|
||||
read->pInfo->width, read->pInfo->height, bands,
|
||||
bpp == 1 ? IM_BBITS_BYTE : IM_BBITS_SHORT,
|
||||
bpp == 1 ? IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT,
|
||||
IM_CODING_NONE, type, 1.0, 1.0, 0, 0 );
|
||||
|
||||
if( !header_only ) {
|
||||
if( png_set_interlace_handling( read->pPng ) > 1 ) {
|
||||
if( png2vips_interlace( read ) )
|
||||
return( -1 );
|
||||
}
|
||||
else {
|
||||
if( png2vips_noninterlace( read ) )
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a PNG file header into a VIPS header.
|
||||
*/
|
||||
int
|
||||
im_png2vips_header( const char *name, IMAGE *out )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( name, out )) )
|
||||
return( -1 );
|
||||
|
||||
if( png2vips( read, 1 ) ) {
|
||||
read_destroy( read );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
read_destroy( read );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a PNG file into a VIPS image.
|
||||
*/
|
||||
int
|
||||
im_png2vips( const char *name, IMAGE *out )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_png2vips: reading \"%s\"\n", name );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( !(read = read_new( name, out )) )
|
||||
return( -1 );
|
||||
|
||||
if( png2vips( read, 0 ) ) {
|
||||
read_destroy( read );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
read_destroy( read );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif /*HAVE_PNG*/
|
440
libsrc/conversion/im_ppm2vips.c
Normal file
440
libsrc/conversion/im_ppm2vips.c
Normal file
@ -0,0 +1,440 @@
|
||||
/* Read a ppm file.
|
||||
*
|
||||
* Stephen Chan ... original code
|
||||
*
|
||||
* 21/11/00 JC
|
||||
* - hacked for VIPS
|
||||
* - reads ppm/pgm/pbm
|
||||
* - mmaps binary pgm/ppm
|
||||
* - reads all ascii formats (slowly!)
|
||||
* 22/11/00 JC
|
||||
* - oops, ascii read was broken
|
||||
* - does 16/32 bit ascii now as well
|
||||
* 24/5/01
|
||||
* - im_ppm2vips_header() added
|
||||
* 22/5/04
|
||||
* - does 16/32 bit binary too
|
||||
* - tiny fix for missing file close on read error
|
||||
* 19/8/05
|
||||
* - use im_raw2vips() for binary read
|
||||
* 9/9/05
|
||||
* - tiny cleanups
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* The largest number/field/whatever we can read.
|
||||
*/
|
||||
#define IM_MAX_THING (80)
|
||||
|
||||
static void
|
||||
skip_line( FILE *fp )
|
||||
{
|
||||
int ch;
|
||||
|
||||
while( (ch = fgetc( fp )) != '\n' )
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
skip_white_space( FILE *fp )
|
||||
{
|
||||
int ch;
|
||||
|
||||
while( isspace( ch = fgetc( fp ) ) )
|
||||
;
|
||||
ungetc( ch, fp );
|
||||
|
||||
if( ch == '#' ) {
|
||||
skip_line( fp );
|
||||
skip_white_space( fp );
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
read_uint( FILE *fp )
|
||||
{
|
||||
int i;
|
||||
char buf[IM_MAX_THING];
|
||||
int ch;
|
||||
|
||||
skip_white_space( fp );
|
||||
|
||||
/* Stop complaints about used-before-set on ch.
|
||||
*/
|
||||
ch = -1;
|
||||
|
||||
for( i = 0; i < IM_MAX_THING - 1 && isdigit( ch = fgetc( fp ) ); i++ )
|
||||
buf[i] = ch;
|
||||
buf[i] = '\0';
|
||||
|
||||
if( i == 0 ) {
|
||||
im_error( "im_ppm2vips", _( "bad unsigned int" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
ungetc( ch, fp );
|
||||
|
||||
return( atoi( buf ) );
|
||||
}
|
||||
|
||||
static int
|
||||
read_header( FILE *fp, IMAGE *out, int *bits, int *ascii )
|
||||
{
|
||||
int width, height, bands, fmt, type;
|
||||
int i;
|
||||
char buf[IM_MAX_THING];
|
||||
int max_value;
|
||||
|
||||
/* ppm types.
|
||||
*/
|
||||
static char *magic_names[] = {
|
||||
"P1", /* pbm ... 1 band 1 bit, ascii */
|
||||
"P2", /* pgm ... 1 band many bit, ascii */
|
||||
"P3", /* ppm ... 3 band many bit, ascii */
|
||||
"P4", /* pbm ... 1 band 1 bit, binary */
|
||||
"P5", /* pgm ... 1 band 8 bit, binary */
|
||||
"P6" /* ppm ... 3 band 8 bit, binary */
|
||||
};
|
||||
|
||||
/* Characteristics, indexed by ppm type.
|
||||
*/
|
||||
static int lookup_bits[] = {
|
||||
1, 8, 8, 1, 8, 8
|
||||
};
|
||||
static int lookup_bands[] = {
|
||||
1, 1, 3, 1, 1, 3
|
||||
};
|
||||
static int lookup_ascii[] = {
|
||||
1, 1, 1, 0, 0, 0
|
||||
};
|
||||
|
||||
/* Read in the magic number.
|
||||
*/
|
||||
buf[0] = fgetc( fp );
|
||||
buf[1] = fgetc( fp );
|
||||
buf[2] = '\0';
|
||||
|
||||
for( i = 0; i < IM_NUMBER( magic_names ); i++ )
|
||||
if( strcmp( magic_names[i], buf ) == 0 )
|
||||
break;
|
||||
if( i == IM_NUMBER( magic_names ) ) {
|
||||
im_error( "im_ppm2vips", _( "bad magic number" ) );
|
||||
return( -1 );
|
||||
}
|
||||
*bits = lookup_bits[i];
|
||||
bands = lookup_bands[i];
|
||||
*ascii = lookup_ascii[i];
|
||||
|
||||
/* Read in size.
|
||||
*/
|
||||
if( (width = read_uint( fp )) < 0 ||
|
||||
(height = read_uint( fp )) < 0 )
|
||||
return( -1 );
|
||||
|
||||
/* Read in max value for >1 bit images.
|
||||
*/
|
||||
if( *bits > 1 ) {
|
||||
if( (max_value = read_uint( fp )) < 0 )
|
||||
return( -1 );
|
||||
|
||||
if( max_value > 255 )
|
||||
*bits = 16;
|
||||
if( max_value > 65535 )
|
||||
*bits = 32;
|
||||
}
|
||||
|
||||
/* For binary images, there is always exactly 1 more whitespace
|
||||
* character before the data starts.
|
||||
*/
|
||||
if( !*ascii && !isspace( fgetc( fp ) ) ) {
|
||||
im_error( "im_ppm2vips",
|
||||
_( "not whitespace before start of binary data" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Choose a VIPS bandfmt.
|
||||
*/
|
||||
switch( *bits ) {
|
||||
case 1:
|
||||
case 8:
|
||||
fmt = IM_BANDFMT_UCHAR;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
fmt = IM_BANDFMT_USHORT;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
fmt = IM_BANDFMT_UINT;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
if( bands == 1 ) {
|
||||
if( fmt == IM_BANDFMT_USHORT )
|
||||
type = IM_TYPE_GREY16;
|
||||
else
|
||||
type = IM_TYPE_B_W;
|
||||
}
|
||||
else {
|
||||
if( fmt == IM_BANDFMT_USHORT )
|
||||
type = IM_TYPE_RGB16;
|
||||
else if( fmt == IM_BANDFMT_UINT )
|
||||
type = IM_TYPE_RGB;
|
||||
else
|
||||
type = IM_TYPE_sRGB;
|
||||
}
|
||||
|
||||
im_initdesc( out, width, height, bands, (*bits == 1) ? 8 : *bits, fmt,
|
||||
IM_CODING_NONE,
|
||||
type,
|
||||
1.0, 1.0,
|
||||
0, 0 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a ppm/pgm file using mmap().
|
||||
*/
|
||||
static int
|
||||
read_mmap( FILE *fp, const char *filename, IMAGE *out )
|
||||
{
|
||||
const int header_offset = ftell( fp );
|
||||
IMAGE *t[2];
|
||||
im_arch_type arch = im_amiMSBfirst() ?
|
||||
IM_ARCH_NATIVE : IM_ARCH_BYTE_SWAPPED;
|
||||
|
||||
if( im_open_local_array( out, t, 2, "im_ppm2vips", "p" ) ||
|
||||
im_raw2vips( filename, t[0],
|
||||
out->Xsize, out->Ysize,
|
||||
IM_IMAGE_SIZEOF_PEL( out ), header_offset ) ||
|
||||
im_copy_morph( t[0], t[1],
|
||||
out->Bands, out->BandFmt, out->Coding ) ||
|
||||
im_copy_from( t[1], out, arch ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read an ascii ppm/pgm file.
|
||||
*/
|
||||
static int
|
||||
read_ascii( FILE *fp, IMAGE *out )
|
||||
{
|
||||
int x, y;
|
||||
PEL *buf;
|
||||
|
||||
if( im_outcheck( out ) || im_setupout( out ) ||
|
||||
!(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) )
|
||||
return( -1 );
|
||||
|
||||
for( y = 0; y < out->Ysize; y++ ) {
|
||||
for( x = 0; x < out->Xsize * out->Bands; x++ ) {
|
||||
int val;
|
||||
|
||||
if( (val = read_uint( fp )) < 0 )
|
||||
return( -1 );
|
||||
|
||||
switch( out->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
buf[x] = IM_CLIP( 0, val, 255 );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_USHORT:
|
||||
((unsigned short *) buf)[x] =
|
||||
IM_CLIP( 0, val, 65535 );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_UINT:
|
||||
((unsigned int *) buf)[x] = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
if( im_writeline( y, out, buf ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read an ascii 1 bit file.
|
||||
*/
|
||||
static int
|
||||
read_1bit_ascii( FILE *fp, IMAGE *out )
|
||||
{
|
||||
int x, y;
|
||||
PEL *buf;
|
||||
|
||||
if( im_outcheck( out ) || im_setupout( out ) ||
|
||||
!(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) )
|
||||
return( -1 );
|
||||
|
||||
for( y = 0; y < out->Ysize; y++ ) {
|
||||
for( x = 0; x < out->Xsize * out->Bands; x++ ) {
|
||||
int val;
|
||||
|
||||
if( (val = read_uint( fp )) < 0 )
|
||||
return( -1 );
|
||||
|
||||
if( val == 1 )
|
||||
buf[x] = 0;
|
||||
else
|
||||
buf[x] = 255;
|
||||
}
|
||||
|
||||
if( im_writeline( y, out, buf ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a 1 bit binary file.
|
||||
*/
|
||||
static int
|
||||
read_1bit_binary( FILE *fp, IMAGE *out )
|
||||
{
|
||||
int x, y, i;
|
||||
int bits;
|
||||
PEL *buf;
|
||||
|
||||
if( im_outcheck( out ) || im_setupout( out ) ||
|
||||
!(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) )
|
||||
return( -1 );
|
||||
|
||||
bits = fgetc( fp );
|
||||
for( i = 0, y = 0; y < out->Ysize; y++ ) {
|
||||
for( x = 0; x < out->Xsize * out->Bands; x++, i++ ) {
|
||||
buf[x] = (bits & 128) ? 255 : 0;
|
||||
bits <<= 1;
|
||||
if( (i & 7) == 7 )
|
||||
bits = fgetc( fp );
|
||||
}
|
||||
|
||||
if( im_writeline( y, out, buf ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
parse_ppm( FILE *fp, const char *filename, IMAGE *out )
|
||||
{
|
||||
int bits;
|
||||
int ascii;
|
||||
|
||||
if( read_header( fp, out, &bits, &ascii ) )
|
||||
return( -1 );
|
||||
|
||||
/* What sort of read are we doing?
|
||||
*/
|
||||
if( !ascii && bits >= 8 )
|
||||
return( read_mmap( fp, filename, out ) );
|
||||
else if( !ascii && bits == 1 )
|
||||
return( read_1bit_binary( fp, out ) );
|
||||
else if( ascii && bits == 1 )
|
||||
return( read_1bit_ascii( fp, out ) );
|
||||
else
|
||||
return( read_ascii( fp, out ) );
|
||||
}
|
||||
|
||||
int
|
||||
im_ppm2vips_header( const char *filename, IMAGE *out )
|
||||
{
|
||||
FILE *fp;
|
||||
int bits;
|
||||
int ascii;
|
||||
|
||||
#ifdef BINARY_OPEN
|
||||
if( !(fp = fopen( filename, "rb" )) ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( !(fp = fopen( filename, "r" )) ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
im_error( "im_ppm2vips_header",
|
||||
_( "unable to open \"%s\"" ), filename );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( read_header( fp, out, &bits, &ascii ) ) {
|
||||
fclose( fp );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
fclose( fp );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_ppm2vips( const char *filename, IMAGE *out )
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
#ifdef BINARY_OPEN
|
||||
if( !(fp = fopen( filename, "rb" )) ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( !(fp = fopen( filename, "r" )) ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
im_error( "im_ppm2vips",
|
||||
_( "unable to open \"%s\"" ), filename );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( parse_ppm( fp, filename, out ) ) {
|
||||
fclose( fp );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
fclose( fp );
|
||||
|
||||
return( 0 );
|
||||
}
|
64
libsrc/conversion/im_raw2vips.c
Normal file
64
libsrc/conversion/im_raw2vips.c
Normal file
@ -0,0 +1,64 @@
|
||||
/* Read raw image. Just a wrapper over im_binfile().
|
||||
*
|
||||
* 3/8/05
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
int
|
||||
im_raw2vips( const char *filename, IMAGE *out,
|
||||
int width, int height, int bpp, int offset )
|
||||
{
|
||||
IMAGE *t;
|
||||
|
||||
if( !(t = im_binfile( filename, width, height, bpp, offset )) )
|
||||
return( -1 );
|
||||
if( im_add_close_callback( out,
|
||||
(im_callback_fn) im_close, t, NULL ) ) {
|
||||
im_close( t );
|
||||
return( -1 );
|
||||
}
|
||||
if( im_copy( t, out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
1502
libsrc/conversion/im_tiff2vips.c
Normal file
1502
libsrc/conversion/im_tiff2vips.c
Normal file
File diff suppressed because it is too large
Load Diff
391
libsrc/conversion/im_tile_cache.c
Normal file
391
libsrc/conversion/im_tile_cache.c
Normal file
@ -0,0 +1,391 @@
|
||||
/* Tile tile cache from tiff2vips ... broken out so it can be shared with
|
||||
* openexr read.
|
||||
*
|
||||
* This isn't the same as im_cache(): we don't sub-divide, and we
|
||||
* single-thread our callee.
|
||||
*
|
||||
* 23/8/06
|
||||
* - take ownership of reused tiles in case the cache is being shared
|
||||
* 13/2/07
|
||||
* - relase ownership after fillng with pixels in case we read across
|
||||
* threads
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/* Turn on debugging output.
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/thread.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* Lower and upper bounds for tile cache size. Choose an exact number based on
|
||||
* tile size.
|
||||
*/
|
||||
#define IM_MAX_TILE_CACHE (250)
|
||||
#define IM_MIN_TILE_CACHE (5)
|
||||
|
||||
/* A tile in our cache.
|
||||
*/
|
||||
typedef struct {
|
||||
struct _Read *read;
|
||||
|
||||
REGION *region; /* REGION with private mem for data */
|
||||
int time; /* Time of last use for flush */
|
||||
int x; /* xy pos in VIPS image cods */
|
||||
int y;
|
||||
} Tile;
|
||||
|
||||
/* Stuff we track during a read.
|
||||
*/
|
||||
typedef struct _Read {
|
||||
/* Parameters.
|
||||
*/
|
||||
IMAGE *in;
|
||||
IMAGE *out;
|
||||
int tile_width;
|
||||
int tile_height;
|
||||
int max_tiles;
|
||||
|
||||
/* Cache.
|
||||
*/
|
||||
int time; /* Update ticks for LRU here */
|
||||
int ntiles; /* Current cache size */
|
||||
GMutex *lock; /* Lock everything here */
|
||||
GSList *cache; /* List of tiles */
|
||||
} Read;
|
||||
|
||||
static void
|
||||
tile_destroy( Tile *tile )
|
||||
{
|
||||
Read *read = tile->read;
|
||||
|
||||
read->cache = g_slist_remove( read->cache, tile );
|
||||
read->ntiles -= 1;
|
||||
assert( read->ntiles >= 0 );
|
||||
tile->read = NULL;
|
||||
|
||||
IM_FREEF( im_region_free, tile->region );
|
||||
|
||||
im_free( tile );
|
||||
}
|
||||
|
||||
static void
|
||||
read_destroy( Read *read )
|
||||
{
|
||||
IM_FREEF( g_mutex_free, read->lock );
|
||||
|
||||
while( read->cache ) {
|
||||
Tile *tile = (Tile *) read->cache->data;
|
||||
|
||||
tile_destroy( tile );
|
||||
}
|
||||
|
||||
im_free( read );
|
||||
}
|
||||
|
||||
static Read *
|
||||
read_new( IMAGE *in, IMAGE *out,
|
||||
int tile_width, int tile_height, int max_tiles )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = IM_NEW( NULL, Read )) )
|
||||
return( NULL );
|
||||
read->in = in;
|
||||
read->out = out;
|
||||
read->tile_width = tile_width;
|
||||
read->tile_height = tile_height;
|
||||
read->max_tiles = max_tiles;
|
||||
read->time = 0;
|
||||
read->ntiles = 0;
|
||||
read->lock = g_mutex_new();
|
||||
read->cache = NULL;
|
||||
|
||||
if( im_add_close_callback( out,
|
||||
(im_callback_fn) read_destroy, read, NULL ) ) {
|
||||
read_destroy( read );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( read );
|
||||
}
|
||||
|
||||
static Tile *
|
||||
tile_new( Read *read )
|
||||
{
|
||||
Tile *tile;
|
||||
|
||||
if( !(tile = IM_NEW( NULL, Tile )) )
|
||||
return( NULL );
|
||||
|
||||
tile->read = read;
|
||||
tile->region = NULL;
|
||||
tile->time = read->time;
|
||||
tile->x = -1;
|
||||
tile->y = -1;
|
||||
read->cache = g_slist_prepend( read->cache, tile );
|
||||
assert( read->ntiles >= 0 );
|
||||
read->ntiles += 1;
|
||||
|
||||
if( !(tile->region = im_region_create( read->in )) ) {
|
||||
tile_destroy( tile );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( tile );
|
||||
}
|
||||
|
||||
/* Do we have a tile in the cache?
|
||||
*/
|
||||
static Tile *
|
||||
tile_search( Read *read, int x, int y )
|
||||
{
|
||||
GSList *p;
|
||||
|
||||
for( p = read->cache; p; p = p->next ) {
|
||||
Tile *tile = (Tile *) p->data;
|
||||
|
||||
if( tile->x == x && tile->y == y )
|
||||
return( tile );
|
||||
}
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
tile_touch( Tile *tile )
|
||||
{
|
||||
assert( tile->read->ntiles >= 0 );
|
||||
|
||||
tile->time = tile->read->time++;
|
||||
}
|
||||
|
||||
/* Fill a tile with pixels.
|
||||
*/
|
||||
static int
|
||||
tile_fill( Tile *tile, int x, int y )
|
||||
{
|
||||
Rect area;
|
||||
|
||||
tile->x = x;
|
||||
tile->y = y;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_tile_cache: filling tile %d x %d\n", tile->x, tile->y );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
area.left = x;
|
||||
area.top = y;
|
||||
area.width = tile->read->tile_width;
|
||||
area.height = tile->read->tile_height;
|
||||
if( im_prepare( tile->region, &area ) )
|
||||
return( -1 );
|
||||
|
||||
/* Make sure these pixels aren't part of this thread's buffer cache
|
||||
* ... they may be read out by another thread.
|
||||
*/
|
||||
im__region_no_ownership( tile->region );
|
||||
|
||||
tile_touch( tile );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Find existing tile, make a new tile, or if we have a full set of tiles,
|
||||
* reuse LRU.
|
||||
*/
|
||||
static Tile *
|
||||
tile_find( Read *read, int x, int y )
|
||||
{
|
||||
Tile *tile;
|
||||
int oldest;
|
||||
GSList *p;
|
||||
|
||||
/* In cache already?
|
||||
*/
|
||||
if( (tile = tile_search( read, x, y )) ) {
|
||||
tile_touch( tile );
|
||||
|
||||
return( tile );
|
||||
}
|
||||
|
||||
/* Cache not full?
|
||||
*/
|
||||
if( read->max_tiles == -1 ||
|
||||
read->ntiles < read->max_tiles ) {
|
||||
if( !(tile = tile_new( read )) ||
|
||||
tile_fill( tile, x, y ) )
|
||||
return( NULL );
|
||||
|
||||
return( tile );
|
||||
}
|
||||
|
||||
/* Reuse an old one.
|
||||
*/
|
||||
oldest = read->time;
|
||||
tile = NULL;
|
||||
for( p = read->cache; p; p = p->next ) {
|
||||
Tile *t = (Tile *) p->data;
|
||||
|
||||
if( t->time < oldest ) {
|
||||
oldest = t->time;
|
||||
tile = t;
|
||||
}
|
||||
}
|
||||
|
||||
assert( tile );
|
||||
|
||||
/* The tile may have been created by another thread if we are sharing
|
||||
* the tile cache between several readers. Take ownership of the tile
|
||||
* to stop assert() failures in im_prepare(). This is safe, since we
|
||||
* are in a mutex.
|
||||
*/
|
||||
im__region_take_ownership( tile->region );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_tile_cache: reusing tile %d x %d\n", tile->x, tile->y );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( tile_fill( tile, x, y ) )
|
||||
return( NULL );
|
||||
|
||||
return( tile );
|
||||
}
|
||||
|
||||
/* Copy rect from from to to.
|
||||
*/
|
||||
static void
|
||||
copy_region( REGION *from, REGION *to, Rect *area )
|
||||
{
|
||||
int y;
|
||||
|
||||
/* Area should be inside both from and to.
|
||||
*/
|
||||
assert( im_rect_includesrect( &from->valid, area ) );
|
||||
assert( im_rect_includesrect( &to->valid, area ) );
|
||||
|
||||
/* Loop down common area, copying.
|
||||
*/
|
||||
for( y = area->top; y < IM_RECT_BOTTOM( area ); y++ ) {
|
||||
PEL *p = (PEL *) IM_REGION_ADDR( from, area->left, y );
|
||||
PEL *q = (PEL *) IM_REGION_ADDR( to, area->left, y );
|
||||
|
||||
memcpy( q, p, IM_IMAGE_SIZEOF_PEL( from->im ) * area->width );
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop over the output region, filling with data from cache.
|
||||
*/
|
||||
static int
|
||||
fill_region( REGION *out, void *seq, void *a, void *b )
|
||||
{
|
||||
Read *read = (Read *) a;
|
||||
const int tw = read->tile_width;
|
||||
const int th = read->tile_height;
|
||||
Rect *r = &out->valid;
|
||||
|
||||
/* Find top left of tiles we need.
|
||||
*/
|
||||
int xs = (r->left / tw) * tw;
|
||||
int ys = (r->top / th) * th;
|
||||
|
||||
int x, y;
|
||||
|
||||
g_mutex_lock( read->lock );
|
||||
|
||||
for( y = ys; y < IM_RECT_BOTTOM( r ); y += th )
|
||||
for( x = xs; x < IM_RECT_RIGHT( r ); x += tw ) {
|
||||
Tile *tile;
|
||||
Rect tarea;
|
||||
Rect hit;
|
||||
|
||||
if( !(tile = tile_find( read, x, y )) ) {
|
||||
g_mutex_unlock( read->lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* The area of the tile.
|
||||
*/
|
||||
tarea.left = x;
|
||||
tarea.top = y;
|
||||
tarea.width = tw;
|
||||
tarea.height = th;
|
||||
|
||||
/* The part of the tile that we need.
|
||||
*/
|
||||
im_rect_intersectrect( &tarea, r, &hit );
|
||||
|
||||
copy_region( tile->region, out, &hit );
|
||||
}
|
||||
|
||||
g_mutex_unlock( read->lock );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_tile_cache( IMAGE *in, IMAGE *out,
|
||||
int tile_width, int tile_height, int max_tiles )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( tile_width <= 0 || tile_height <= 0 || max_tiles < -1 ) {
|
||||
im_error( "im_tile_cache", _( "bad parameters" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( im_piocheck( in, out ) )
|
||||
return( -1 );
|
||||
if( im_cp_desc( out, in ) )
|
||||
return( -1 );
|
||||
if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
if( !(read = read_new( in, out,
|
||||
tile_width, tile_height, max_tiles )) )
|
||||
return( -1 );
|
||||
if( im_generate( out,
|
||||
NULL, fill_region, NULL, read, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
146
libsrc/conversion/im_vips2csv.c
Normal file
146
libsrc/conversion/im_vips2csv.c
Normal file
@ -0,0 +1,146 @@
|
||||
/* Write a csv file.
|
||||
*
|
||||
* 9/6/06
|
||||
* - hacked from im_debugim
|
||||
* 23/10/06
|
||||
* - allow separator to be specified (default "\t", <tab>)
|
||||
* 17/11/06
|
||||
* - oops, was broken
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
#define PRINT_INT( TYPE ) fprintf( fp, "%d", *((TYPE*)p) );
|
||||
#define PRINT_FLOAT( TYPE ) fprintf( fp, "%g", *((TYPE*)p) );
|
||||
#define PRINT_COMPLEX( TYPE ) fprintf( fp, "(%g, %g)", \
|
||||
((TYPE*)p)[0], ((TYPE*)p)[1] );
|
||||
|
||||
static int
|
||||
vips2csv( IMAGE *in, FILE *fp, const char *sep )
|
||||
{
|
||||
int w = IM_IMAGE_N_ELEMENTS( in );
|
||||
int es = IM_IMAGE_SIZEOF_ELEMENT( in );
|
||||
|
||||
int x, y;
|
||||
PEL *p;
|
||||
|
||||
p = (PEL *) in->data;
|
||||
for( y = 0; y < in->Ysize; y++ ) {
|
||||
for( x = 0; x < w; x++ ) {
|
||||
if( x > 0 )
|
||||
fprintf( fp, "%s", sep );
|
||||
|
||||
switch( in->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
PRINT_INT( unsigned char ); break;
|
||||
case IM_BANDFMT_CHAR:
|
||||
PRINT_INT( char ); break;
|
||||
case IM_BANDFMT_USHORT:
|
||||
PRINT_INT( unsigned short ); break;
|
||||
case IM_BANDFMT_SHORT:
|
||||
PRINT_INT( short ); break;
|
||||
case IM_BANDFMT_UINT:
|
||||
PRINT_INT( unsigned int ); break;
|
||||
case IM_BANDFMT_INT:
|
||||
PRINT_INT( int ); break;
|
||||
case IM_BANDFMT_FLOAT:
|
||||
PRINT_FLOAT( float ); break;
|
||||
case IM_BANDFMT_DOUBLE:
|
||||
PRINT_FLOAT( double ); break;
|
||||
case IM_BANDFMT_COMPLEX:
|
||||
PRINT_COMPLEX( float ); break;
|
||||
case IM_BANDFMT_DPCOMPLEX:
|
||||
PRINT_COMPLEX( double ); break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
p += es;
|
||||
}
|
||||
|
||||
fprintf( fp, "\n" );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_vips2csv( IMAGE *in, const char *filename )
|
||||
{
|
||||
char *separator = "\t";
|
||||
|
||||
char name[FILENAME_MAX];
|
||||
char mode[FILENAME_MAX];
|
||||
FILE *fp;
|
||||
char *p, *q, *r;
|
||||
|
||||
/* Parse mode string.
|
||||
*/
|
||||
im_filename_split( filename, name, mode );
|
||||
p = &mode[0];
|
||||
while( (q = im_getnextoption( &p )) ) {
|
||||
if( im_isprefix( "sep", q ) && (r = im_getsuboption( q )) )
|
||||
separator = r;
|
||||
}
|
||||
|
||||
if( im_incheck( in ) )
|
||||
return( -1 );
|
||||
if( in->Coding != IM_CODING_NONE ) {
|
||||
im_error( "im_vips2csv", _( "input must be uncoded" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( !(fp = fopen( name, "w" )) ) {
|
||||
im_error( "im_cvips2csv", _( "unable to open \"%s\"" ),
|
||||
name );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( vips2csv( in, fp, separator ) ) {
|
||||
fclose( fp );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
fclose( fp );
|
||||
|
||||
return( 0 );
|
||||
}
|
896
libsrc/conversion/im_vips2jpeg.c
Normal file
896
libsrc/conversion/im_vips2jpeg.c
Normal file
@ -0,0 +1,896 @@
|
||||
/* Convert 8-bit VIPS images to/from JPEG.
|
||||
*
|
||||
* 28/11/03 JC
|
||||
* - better no-overshoot on tile loop
|
||||
* 12/11/04
|
||||
* - better demand size choice for eval
|
||||
* 30/6/05 JC
|
||||
* - update im_error()/im_warn()
|
||||
* - now loads and saves exif data
|
||||
* 30/7/05
|
||||
* - now loads ICC profiles
|
||||
* - now saves ICC profiles from the VIPS header
|
||||
* 24/8/05
|
||||
* - jpeg load sets vips xres/yres from exif, if possible
|
||||
* - jpeg save sets exif xres/yres from vips, if possible
|
||||
* 29/8/05
|
||||
* - cut from old vips_jpeg.c
|
||||
* 20/4/06
|
||||
* - auto convert to sRGB/mono for save
|
||||
* 13/10/06
|
||||
* - add </libexif/ prefix if required
|
||||
* 19/1/07
|
||||
* - oop, libexif confusion
|
||||
* 2/11/07
|
||||
* - use im_wbuffer() API for BG writes
|
||||
* 15/2/08
|
||||
* - write CMYK if Bands == 4 and Type == CMYK
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
#define DEBUG_VERBOSE
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifndef HAVE_JPEG
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
int
|
||||
im_vips2jpeg( IMAGE *in, const char *filename )
|
||||
{
|
||||
im_error( "im_vips2jpeg", _( "JPEG support disabled" ) );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_vips2bufjpeg( IMAGE *in, IMAGE *out, int qfac, char **obuf, int *olen )
|
||||
{
|
||||
im_error( "im_vips2bufjpeg", _( "JPEG support disabled" ) );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_vips2mimejpeg( IMAGE *in, int qfac )
|
||||
{
|
||||
im_error( "im_vips2mimejpeg", _( "JPEG support disabled" ) );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#else /*HAVE_JPEG*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
#ifdef UNTAGGED_EXIF
|
||||
#include <exif-data.h>
|
||||
#include <exif-loader.h>
|
||||
#include <exif-ifd.h>
|
||||
#include <exif-utils.h>
|
||||
#else /*!UNTAGGED_EXIF*/
|
||||
#include <libexif/exif-data.h>
|
||||
#include <libexif/exif-loader.h>
|
||||
#include <libexif/exif-ifd.h>
|
||||
#include <libexif/exif-utils.h>
|
||||
#endif /*UNTAGGED_EXIF*/
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
#include <vips/vbuf.h>
|
||||
|
||||
/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we
|
||||
* also define. Make sure it's turned off.
|
||||
*/
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#undef HAVE_STDLIB_H
|
||||
#endif /*HAVE_STDLIB_H*/
|
||||
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* Define a new error handler for when we bomb out.
|
||||
*/
|
||||
typedef struct {
|
||||
/* Public fields.
|
||||
*/
|
||||
struct jpeg_error_mgr pub;
|
||||
|
||||
/* Private stuff for us.
|
||||
*/
|
||||
jmp_buf jmp; /* longjmp() here to get back to VIPS */
|
||||
FILE *fp; /* fclose() if non-NULL */
|
||||
} ErrorManager;
|
||||
|
||||
/* New output message method - send to VIPS.
|
||||
*/
|
||||
METHODDEF(void)
|
||||
new_output_message( j_common_ptr cinfo )
|
||||
{
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
|
||||
(*cinfo->err->format_message)( cinfo, buffer );
|
||||
im_error( "vips_jpeg", _( "%s" ), buffer );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_jpeg.c: new_output_message: \"%s\"\n", buffer );
|
||||
#endif /*DEBUG*/
|
||||
}
|
||||
|
||||
/* New error_exit handler.
|
||||
*/
|
||||
METHODDEF(void)
|
||||
new_error_exit( j_common_ptr cinfo )
|
||||
{
|
||||
ErrorManager *eman = (ErrorManager *) cinfo->err;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_jpeg.c: new_error_exit\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Close the fp if necessary.
|
||||
*/
|
||||
if( eman->fp ) {
|
||||
(void) fclose( eman->fp );
|
||||
eman->fp = NULL;
|
||||
}
|
||||
|
||||
/* Send the error message to VIPS. This method is overridden above.
|
||||
*/
|
||||
(*cinfo->err->output_message)( cinfo );
|
||||
|
||||
/* Jump back.
|
||||
*/
|
||||
longjmp( eman->jmp, 1 );
|
||||
}
|
||||
|
||||
/* What we track during a JPEG write.
|
||||
*/
|
||||
typedef struct {
|
||||
IMAGE *in;
|
||||
struct jpeg_compress_struct cinfo;
|
||||
ErrorManager eman;
|
||||
im_threadgroup_t *tg;
|
||||
JSAMPROW *row_pointer;
|
||||
char *profile_bytes;
|
||||
unsigned int profile_length;
|
||||
IMAGE *inverted;
|
||||
} Write;
|
||||
|
||||
static void
|
||||
write_destroy( Write *write )
|
||||
{
|
||||
jpeg_destroy_compress( &write->cinfo );
|
||||
IM_FREEF( im_threadgroup_free, write->tg );
|
||||
IM_FREEF( im_close, write->in );
|
||||
IM_FREEF( fclose, write->eman.fp );
|
||||
IM_FREE( write->row_pointer );
|
||||
IM_FREE( write->profile_bytes );
|
||||
IM_FREEF( im_close, write->inverted );
|
||||
im_free( write );
|
||||
}
|
||||
|
||||
static Write *
|
||||
write_new( IMAGE *in )
|
||||
{
|
||||
Write *write;
|
||||
|
||||
if( !(write = IM_NEW( NULL, Write )) )
|
||||
return( NULL );
|
||||
memset( write, 0, sizeof( Write ) );
|
||||
|
||||
if( !(write->in = im__convert_saveable( in, IM__RGB_CMYK )) ) {
|
||||
im_error( "im_vips2jpeg",
|
||||
_( "unable to convert to saveable format" ) );
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
write->tg = NULL;
|
||||
write->row_pointer = NULL;
|
||||
write->cinfo.err = jpeg_std_error( &write->eman.pub );
|
||||
write->eman.pub.error_exit = new_error_exit;
|
||||
write->eman.pub.output_message = new_output_message;
|
||||
write->eman.fp = NULL;
|
||||
write->profile_bytes = NULL;
|
||||
write->profile_length = 0;
|
||||
write->inverted = NULL;
|
||||
|
||||
return( write );
|
||||
}
|
||||
|
||||
#ifdef HAVE_EXIF
|
||||
static void
|
||||
write_rational( ExifEntry *entry, ExifByteOrder bo, void *data )
|
||||
{
|
||||
ExifRational *v = (ExifRational *) data;
|
||||
|
||||
exif_set_rational( entry->data, bo, *v );
|
||||
}
|
||||
|
||||
static void
|
||||
write_short( ExifEntry *entry, ExifByteOrder bo, void *data )
|
||||
{
|
||||
ExifShort *v = (ExifShort *) data;
|
||||
|
||||
exif_set_short( entry->data, bo, *v );
|
||||
}
|
||||
|
||||
typedef void (*write_fn)( ExifEntry *, ExifByteOrder, void * );
|
||||
|
||||
static int
|
||||
write_tag( ExifData *ed, ExifTag tag, ExifFormat f, write_fn fn, void *data )
|
||||
{
|
||||
ExifByteOrder bo;
|
||||
int found;
|
||||
int i;
|
||||
|
||||
bo = exif_data_get_byte_order( ed );
|
||||
|
||||
/* Need to set the tag in all sections which have it :-(
|
||||
*/
|
||||
found = 0;
|
||||
for( i = 0; i < EXIF_IFD_COUNT; i++ ) {
|
||||
ExifEntry *entry;
|
||||
|
||||
if( (entry = exif_content_get_entry( ed->ifd[i], tag )) &&
|
||||
entry->format == f &&
|
||||
entry->components == 1 ) {
|
||||
fn( entry, bo, data );
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( !found ) {
|
||||
/* There was no tag we could update ... make one in ifd[0].
|
||||
*/
|
||||
ExifEntry *entry;
|
||||
|
||||
entry = exif_entry_new();
|
||||
exif_content_add_entry( ed->ifd[0], entry );
|
||||
exif_entry_initialize( entry, tag );
|
||||
fn( entry, bo, data );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
set_exif_resolution( ExifData *ed, IMAGE *im )
|
||||
{
|
||||
double xres, yres;
|
||||
ExifRational xres_rational, yres_rational;
|
||||
ExifShort unit;
|
||||
|
||||
/* Always save as inches - more progs support it for read.
|
||||
*/
|
||||
xres = im->Xres * 25.4;
|
||||
yres = im->Yres * 25.4;
|
||||
unit = 2;
|
||||
|
||||
/* Wow, how dumb, fix this.
|
||||
*/
|
||||
xres_rational.numerator = xres * 100000;
|
||||
xres_rational.denominator = 100000;
|
||||
yres_rational.numerator = yres * 100000;
|
||||
yres_rational.denominator = 100000;
|
||||
|
||||
if( write_tag( ed, EXIF_TAG_X_RESOLUTION, EXIF_FORMAT_RATIONAL,
|
||||
write_rational, &xres_rational ) ||
|
||||
write_tag( ed, EXIF_TAG_Y_RESOLUTION, EXIF_FORMAT_RATIONAL,
|
||||
write_rational, &yres_rational ) ||
|
||||
write_tag( ed, EXIF_TAG_RESOLUTION_UNIT, EXIF_FORMAT_SHORT,
|
||||
write_short, &unit ) ) {
|
||||
im_error( "im_jpeg2vips",
|
||||
_( "error setting JPEG resolution" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
static int
|
||||
write_exif( Write *write )
|
||||
{
|
||||
unsigned char *data;
|
||||
size_t data_length;
|
||||
unsigned int idl;
|
||||
#ifdef HAVE_EXIF
|
||||
ExifData *ed;
|
||||
|
||||
/* Either parse from the embedded EXIF, or if there's none, make
|
||||
* some fresh EXIF we can write the resolution to.
|
||||
*/
|
||||
if( im_header_get_type( write->in, IM_META_EXIF_NAME ) ) {
|
||||
if( im_meta_get_blob( write->in, IM_META_EXIF_NAME,
|
||||
(void *) &data, &data_length ) )
|
||||
return( -1 );
|
||||
|
||||
if( !(ed = exif_data_new_from_data( data, data_length )) )
|
||||
return( -1 );
|
||||
}
|
||||
else
|
||||
ed = exif_data_new();
|
||||
|
||||
/* Set EXIF resolution from VIPS.
|
||||
*/
|
||||
if( set_exif_resolution( ed, write->in ) ) {
|
||||
exif_data_free( ed );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Reserialise and write. exif_data_save_data() returns an int for some
|
||||
* reason.
|
||||
*/
|
||||
exif_data_save_data( ed, &data, &idl );
|
||||
if( !idl ) {
|
||||
im_error( "im_jpeg2vips", _( "error saving EXIF" ) );
|
||||
exif_data_free( ed );
|
||||
return( -1 );
|
||||
}
|
||||
data_length = idl;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_vips2jpeg: attaching %d bytes of EXIF\n", data_length );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
exif_data_free( ed );
|
||||
jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length );
|
||||
free( data );
|
||||
#else /*!HAVE_EXIF*/
|
||||
/* No libexif ... just copy the embedded EXIF over.
|
||||
*/
|
||||
if( im_header_get_type( write->in, IM_META_EXIF_NAME ) ) {
|
||||
if( im_meta_get_blob( write->in, IM_META_EXIF_NAME,
|
||||
(void *) &data, &data_length ) )
|
||||
return( -1 );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_vips2jpeg: attaching %d bytes of EXIF\n",
|
||||
data_length );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1,
|
||||
data, data_length );
|
||||
}
|
||||
#endif /*!HAVE_EXIF*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* ICC writer from lcms, slight tweaks.
|
||||
*/
|
||||
|
||||
#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
|
||||
#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
|
||||
#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
|
||||
#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
|
||||
|
||||
/*
|
||||
* This routine writes the given ICC profile data into a JPEG file.
|
||||
* It *must* be called AFTER calling jpeg_start_compress() and BEFORE
|
||||
* the first call to jpeg_write_scanlines().
|
||||
* (This ordering ensures that the APP2 marker(s) will appear after the
|
||||
* SOI and JFIF or Adobe markers, but before all else.)
|
||||
*/
|
||||
|
||||
static void
|
||||
write_profile_data (j_compress_ptr cinfo,
|
||||
const JOCTET *icc_data_ptr,
|
||||
unsigned int icc_data_len)
|
||||
{
|
||||
unsigned int num_markers; /* total number of markers we'll write */
|
||||
int cur_marker = 1; /* per spec, counting starts at 1 */
|
||||
unsigned int length; /* number of bytes to write in this marker */
|
||||
|
||||
/* rounding up will fail for length == 0 */
|
||||
assert( icc_data_len > 0 );
|
||||
|
||||
/* Calculate the number of markers we'll need, rounding up of course */
|
||||
num_markers = (icc_data_len + MAX_DATA_BYTES_IN_MARKER - 1) /
|
||||
MAX_DATA_BYTES_IN_MARKER;
|
||||
|
||||
while (icc_data_len > 0) {
|
||||
/* length of profile to put in this marker */
|
||||
length = icc_data_len;
|
||||
if (length > MAX_DATA_BYTES_IN_MARKER)
|
||||
length = MAX_DATA_BYTES_IN_MARKER;
|
||||
icc_data_len -= length;
|
||||
|
||||
/* Write the JPEG marker header (APP2 code and marker length) */
|
||||
jpeg_write_m_header(cinfo, ICC_MARKER,
|
||||
(unsigned int) (length + ICC_OVERHEAD_LEN));
|
||||
|
||||
/* Write the marker identifying string "ICC_PROFILE" (null-terminated).
|
||||
* We code it in this less-than-transparent way so that the code works
|
||||
* even if the local character set is not ASCII.
|
||||
*/
|
||||
jpeg_write_m_byte(cinfo, 0x49);
|
||||
jpeg_write_m_byte(cinfo, 0x43);
|
||||
jpeg_write_m_byte(cinfo, 0x43);
|
||||
jpeg_write_m_byte(cinfo, 0x5F);
|
||||
jpeg_write_m_byte(cinfo, 0x50);
|
||||
jpeg_write_m_byte(cinfo, 0x52);
|
||||
jpeg_write_m_byte(cinfo, 0x4F);
|
||||
jpeg_write_m_byte(cinfo, 0x46);
|
||||
jpeg_write_m_byte(cinfo, 0x49);
|
||||
jpeg_write_m_byte(cinfo, 0x4C);
|
||||
jpeg_write_m_byte(cinfo, 0x45);
|
||||
jpeg_write_m_byte(cinfo, 0x0);
|
||||
|
||||
/* Add the sequencing info */
|
||||
jpeg_write_m_byte(cinfo, cur_marker);
|
||||
jpeg_write_m_byte(cinfo, (int) num_markers);
|
||||
|
||||
/* Add the profile data */
|
||||
while (length--) {
|
||||
jpeg_write_m_byte(cinfo, *icc_data_ptr);
|
||||
icc_data_ptr++;
|
||||
}
|
||||
cur_marker++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write an ICC Profile from a file into the JPEG stream.
|
||||
*/
|
||||
static int
|
||||
write_profile_file( Write *write, const char *profile )
|
||||
{
|
||||
if( !(write->profile_bytes =
|
||||
im__file_read_name( profile, &write->profile_length )) )
|
||||
return( -1 );
|
||||
write_profile_data( &write->cinfo,
|
||||
(JOCTET *) write->profile_bytes, write->profile_length );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_vips2jpeg: attached profile \"%s\"\n", profile );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
write_profile_meta( Write *write )
|
||||
{
|
||||
void *data;
|
||||
size_t data_length;
|
||||
|
||||
if( im_meta_get_blob( write->in, IM_META_ICC_NAME,
|
||||
&data, &data_length ) )
|
||||
return( -1 );
|
||||
|
||||
write_profile_data( &write->cinfo, data, data_length );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_vips2jpeg: attached %d byte profile from VIPS header\n",
|
||||
data_length );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
write_jpeg_block( REGION *region, Rect *area, void *a, void *b )
|
||||
{
|
||||
Write *write = (Write *) a;
|
||||
int i;
|
||||
|
||||
/* We are running in a background thread. We need to catch longjmp()s
|
||||
* here instead.
|
||||
*/
|
||||
if( setjmp( write->eman.jmp ) )
|
||||
return( -1 );
|
||||
|
||||
for( i = 0; i < area->height; i++ )
|
||||
write->row_pointer[i] = (JSAMPROW)
|
||||
IM_REGION_ADDR( region, 0, area->top + i );
|
||||
|
||||
jpeg_write_scanlines( &write->cinfo, write->row_pointer, area->height );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Write a VIPS image to a JPEG compress struct.
|
||||
*/
|
||||
static int
|
||||
write_vips( Write *write, int qfac, const char *profile )
|
||||
{
|
||||
IMAGE *in;
|
||||
J_COLOR_SPACE space;
|
||||
|
||||
/* The image we'll be writing ... can change, see CMYK.
|
||||
*/
|
||||
in = write->in;
|
||||
|
||||
/* Should have been converted for save.
|
||||
*/
|
||||
assert( in->BandFmt == IM_BANDFMT_UCHAR );
|
||||
assert( in->Coding == IM_CODING_NONE );
|
||||
assert( in->Bands == 1 || in->Bands == 3 || in->Bands == 4 );
|
||||
|
||||
/* Check input image.
|
||||
*/
|
||||
if( im_pincheck( in ) )
|
||||
return( -1 );
|
||||
if( qfac < 0 || qfac > 100 ) {
|
||||
im_error( "im_vips2jpeg", _( "qfac should be in 0-100" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Set compression parameters.
|
||||
*/
|
||||
write->cinfo.image_width = in->Xsize;
|
||||
write->cinfo.image_height = in->Ysize;
|
||||
write->cinfo.input_components = in->Bands;
|
||||
if( in->Bands == 4 && in->Type == IM_TYPE_CMYK ) {
|
||||
space = JCS_CMYK;
|
||||
/* IJG always sets an Adobe marker, so we should invert CMYK.
|
||||
*/
|
||||
if( !(write->inverted = im_open( "vips2jpeg_invert", "p" )) ||
|
||||
im_invert( in, write->inverted ) )
|
||||
return( -1 );
|
||||
in = write->inverted;
|
||||
}
|
||||
else if( in->Bands == 3 )
|
||||
space = JCS_RGB;
|
||||
else if( in->Bands == 1 )
|
||||
space = JCS_GRAYSCALE;
|
||||
else
|
||||
/* Use luminance compression for all channels.
|
||||
*/
|
||||
space = JCS_UNKNOWN;
|
||||
write->cinfo.in_color_space = space;
|
||||
|
||||
/* Build VIPS output stuff now we know the image we'll be writing.
|
||||
*/
|
||||
write->tg = im_threadgroup_create( in );
|
||||
write->row_pointer = IM_ARRAY( NULL, write->tg->nlines, JSAMPROW );
|
||||
if( !write->tg || !write->row_pointer )
|
||||
return( -1 );
|
||||
|
||||
/* Rest to default.
|
||||
*/
|
||||
jpeg_set_defaults( &write->cinfo );
|
||||
jpeg_set_quality( &write->cinfo, qfac, TRUE );
|
||||
|
||||
/* Build compress tables.
|
||||
*/
|
||||
jpeg_start_compress( &write->cinfo, TRUE );
|
||||
|
||||
/* Write any APP markers we need.
|
||||
*/
|
||||
if( write_exif( write ) )
|
||||
return( -1 );
|
||||
|
||||
/* A profile supplied as an argument overrides an embedded profile.
|
||||
*/
|
||||
if( profile &&
|
||||
write_profile_file( write, profile ) )
|
||||
return( -1 );
|
||||
if( !profile &&
|
||||
im_header_get_type( in, IM_META_ICC_NAME ) &&
|
||||
write_profile_meta( write ) )
|
||||
return( -1 );
|
||||
|
||||
/* Write data. Note that the write function grabs the longjmp()!
|
||||
*/
|
||||
if( im_wbuffer( write->tg, write_jpeg_block, write, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
/* We have to reinstate the setjmp() before we jpeg_finish_compress().
|
||||
*/
|
||||
if( setjmp( write->eman.jmp ) )
|
||||
return( -1 );
|
||||
|
||||
jpeg_finish_compress( &write->cinfo );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Write a VIPS image to a file as JPEG.
|
||||
*/
|
||||
int
|
||||
im_vips2jpeg( IMAGE *in, const char *filename )
|
||||
{
|
||||
Write *write;
|
||||
int qfac = 75;
|
||||
char *profile = NULL;
|
||||
|
||||
char *p, *q;
|
||||
|
||||
char name[FILENAME_MAX];
|
||||
char mode[FILENAME_MAX];
|
||||
char buf[FILENAME_MAX];
|
||||
|
||||
/* Parse mode from filename.
|
||||
*/
|
||||
im_filename_split( filename, name, mode );
|
||||
strcpy( buf, mode );
|
||||
p = &buf[0];
|
||||
if( (q = im_getnextoption( &p )) ) {
|
||||
if( strcmp( q, "" ) != 0 )
|
||||
qfac = atoi( mode );
|
||||
}
|
||||
if( (q = im_getnextoption( &p )) ) {
|
||||
if( strcmp( q, "" ) != 0 )
|
||||
profile = q;
|
||||
}
|
||||
if( (q = im_getnextoption( &p )) ) {
|
||||
im_error( "im_vips2jpeg",
|
||||
_( "unknown extra options \"%s\"" ), q );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( !(write = write_new( in )) )
|
||||
return( -1 );
|
||||
|
||||
if( setjmp( write->eman.jmp ) ) {
|
||||
/* Here for longjmp() from new_error_exit().
|
||||
*/
|
||||
write_destroy( write );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Can't do this in write_new(), has to be after we've made the
|
||||
* setjmp().
|
||||
*/
|
||||
jpeg_create_compress( &write->cinfo );
|
||||
|
||||
/* Make output.
|
||||
*/
|
||||
#ifdef BINARY_OPEN
|
||||
if( !(write->eman.fp = fopen( name, "wb" )) ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( !(write->eman.fp = fopen( name, "w" )) ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
write_destroy( write );
|
||||
im_error( "im_vips2jpeg",
|
||||
_( "unable to open \"%s\"" ), name );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
jpeg_stdio_dest( &write->cinfo, write->eman.fp );
|
||||
|
||||
/* Convert!
|
||||
*/
|
||||
if( write_vips( write, qfac, profile ) ) {
|
||||
write_destroy( write );
|
||||
return( -1 );
|
||||
}
|
||||
write_destroy( write );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Just like the above, but we write to a memory buffer.
|
||||
*
|
||||
* A memory buffer for the compressed image.
|
||||
*/
|
||||
typedef struct {
|
||||
/* Public jpeg fields.
|
||||
*/
|
||||
struct jpeg_destination_mgr pub;
|
||||
|
||||
/* Private stuff during write.
|
||||
*/
|
||||
JOCTET *data; /* Allocated area */
|
||||
int used; /* Number of bytes written so far */
|
||||
int size; /* Max size */
|
||||
|
||||
/* Copy the compressed area here.
|
||||
*/
|
||||
IMAGE *out; /* Allocate relative to this */
|
||||
char **obuf; /* Allocated buffer, and size */
|
||||
int *olen;
|
||||
} OutputBuffer;
|
||||
|
||||
/* Init dest method.
|
||||
*/
|
||||
METHODDEF(void)
|
||||
init_destination( j_compress_ptr cinfo )
|
||||
{
|
||||
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
|
||||
int mx = cinfo->image_width * cinfo->image_height *
|
||||
cinfo->input_components * sizeof( JOCTET );
|
||||
|
||||
/* Allocate relative to the image we are writing .. freed when we junk
|
||||
* this output.
|
||||
*/
|
||||
buf->data = (JOCTET *) (*cinfo->mem->alloc_large)
|
||||
( (j_common_ptr) cinfo, JPOOL_IMAGE, mx );
|
||||
buf->used = 0;
|
||||
buf->size = mx;
|
||||
|
||||
/* Set buf pointers for library.
|
||||
*/
|
||||
buf->pub.next_output_byte = buf->data;
|
||||
buf->pub.free_in_buffer = mx;
|
||||
}
|
||||
|
||||
/* Buffer full method ... should never get this.
|
||||
*/
|
||||
METHODDEF(boolean)
|
||||
empty_output_buffer( j_compress_ptr cinfo )
|
||||
{
|
||||
/* Not really a file write error, but why not. Should never happen.
|
||||
*/
|
||||
ERREXIT( cinfo, JERR_FILE_WRITE );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Cleanup. Write entire buffer as a MIME type.
|
||||
*/
|
||||
METHODDEF(void)
|
||||
term_destination( j_compress_ptr cinfo )
|
||||
{
|
||||
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
|
||||
int len = buf->size - buf->pub.free_in_buffer;
|
||||
void *obuf;
|
||||
|
||||
/* Allocate and copy to the VIPS output area.
|
||||
*/
|
||||
if( !(obuf = im_malloc( buf->out, len )) )
|
||||
ERREXIT( cinfo, JERR_FILE_WRITE );
|
||||
memcpy( obuf, buf->data, len );
|
||||
*(buf->obuf) = obuf;
|
||||
*(buf->olen) = len;
|
||||
}
|
||||
|
||||
/* Set dest to one of our objects.
|
||||
*/
|
||||
static void
|
||||
buf_dest( j_compress_ptr cinfo, IMAGE *out, char **obuf, int *olen )
|
||||
{
|
||||
OutputBuffer *buf;
|
||||
|
||||
/* The destination object is made permanent so that multiple JPEG
|
||||
* images can be written to the same file without re-executing
|
||||
* jpeg_stdio_dest. This makes it dangerous to use this manager and
|
||||
* a different destination manager serially with the same JPEG object,
|
||||
* because their private object sizes may be different.
|
||||
*
|
||||
* Caveat programmer.
|
||||
*/
|
||||
if( !cinfo->dest ) { /* first time for this JPEG object? */
|
||||
cinfo->dest = (struct jpeg_destination_mgr *)
|
||||
(*cinfo->mem->alloc_small)
|
||||
( (j_common_ptr) cinfo, JPOOL_PERMANENT,
|
||||
sizeof( OutputBuffer ) );
|
||||
}
|
||||
|
||||
buf = (OutputBuffer *) cinfo->dest;
|
||||
buf->pub.init_destination = init_destination;
|
||||
buf->pub.empty_output_buffer = empty_output_buffer;
|
||||
buf->pub.term_destination = term_destination;
|
||||
|
||||
/* Save output parameters.
|
||||
*/
|
||||
buf->out = out;
|
||||
buf->obuf = obuf;
|
||||
buf->olen = olen;
|
||||
}
|
||||
|
||||
/* As above, but save to a buffer. The buffer is allocated relative to out.
|
||||
* On success, buf is set to the output buffer and len to the size of the
|
||||
* compressed image.
|
||||
*/
|
||||
int
|
||||
im_vips2bufjpeg( IMAGE *in, IMAGE *out, int qfac, char **obuf, int *olen )
|
||||
{
|
||||
Write *write;
|
||||
|
||||
if( !(write = write_new( in )) )
|
||||
return( -1 );
|
||||
|
||||
/* Clear output parameters.
|
||||
*/
|
||||
*obuf = NULL;
|
||||
*olen = 0;
|
||||
|
||||
/* Make jpeg compression object.
|
||||
*/
|
||||
if( setjmp( write->eman.jmp ) ) {
|
||||
/* Here for longjmp() from new_error_exit().
|
||||
*/
|
||||
write_destroy( write );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
jpeg_create_compress( &write->cinfo );
|
||||
|
||||
/* Attach output.
|
||||
*/
|
||||
buf_dest( &write->cinfo, out, obuf, olen );
|
||||
|
||||
/* Convert!
|
||||
*/
|
||||
if( write_vips( write, qfac, NULL ) ) {
|
||||
write_destroy( write );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
write_destroy( write );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* As above, but save as a mime jpeg on stdout.
|
||||
*/
|
||||
int
|
||||
im_vips2mimejpeg( IMAGE *in, int qfac )
|
||||
{
|
||||
IMAGE *base;
|
||||
int len;
|
||||
char *buf;
|
||||
|
||||
if( !(base = im_open( "im_vips2mimejpeg:1", "p" )) )
|
||||
return( -1 );
|
||||
if( im_vips2bufjpeg( in, base, qfac, &buf, &len ) ) {
|
||||
im_close( base );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Write as a MIME type.
|
||||
*/
|
||||
printf( "Content-length: %d\r\n", len );
|
||||
printf( "Content-type: image/jpeg\r\n" );
|
||||
printf( "\r\n" );
|
||||
fwrite( buf, sizeof( char ), len, stdout );
|
||||
fflush( stdout );
|
||||
|
||||
im_close( base );
|
||||
|
||||
if( ferror( stdout ) ) {
|
||||
im_error( "im_vips2mimejpeg", _( "error writing output" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif /*HAVE_JPEG*/
|
320
libsrc/conversion/im_vips2png.c
Normal file
320
libsrc/conversion/im_vips2png.c
Normal file
@ -0,0 +1,320 @@
|
||||
/* Convert 1 to 4-band 8 or 16-bit VIPS images to/from PNG.
|
||||
*
|
||||
* 28/11/03 JC
|
||||
* - better no-overshoot on tile loop
|
||||
* 22/2/05
|
||||
* - read non-interlaced PNG with a line buffer (thanks Michel Brabants)
|
||||
* 11/1/06
|
||||
* - read RGBA palette-ized images more robustly (thanks Tom)
|
||||
* 20/4/06
|
||||
* - auto convert to sRGB/mono (with optional alpha) for save
|
||||
* 1/5/06
|
||||
* - from vips_png.c
|
||||
* 2/11/07
|
||||
* - use im_wbuffer() API for BG writes
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#ifndef HAVE_PNG
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
int
|
||||
im_vips2png( IMAGE *in, const char *filename )
|
||||
{
|
||||
im_error( "im_vips2png", _( "PNG support disabled" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#else /*HAVE_PNG*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
#if PNG_LIBPNG_VER < 10003
|
||||
#error "PNG library too old."
|
||||
#endif
|
||||
|
||||
static void
|
||||
user_error_function( png_structp png_ptr, png_const_charp error_msg )
|
||||
{
|
||||
im_error( "im_vips2png", _( "PNG error: \"%s\"" ), error_msg );
|
||||
}
|
||||
|
||||
static void
|
||||
user_warning_function( png_structp png_ptr, png_const_charp warning_msg )
|
||||
{
|
||||
im_error( "im_vips2png", _( "PNG warning: \"%s\"" ), warning_msg );
|
||||
}
|
||||
|
||||
/* What we track during a PNG write.
|
||||
*/
|
||||
typedef struct {
|
||||
IMAGE *in;
|
||||
im_threadgroup_t *tg;
|
||||
|
||||
FILE *fp;
|
||||
png_structp pPng;
|
||||
png_infop pInfo;
|
||||
png_bytep *row_pointer;
|
||||
} Write;
|
||||
|
||||
static void
|
||||
write_destroy( Write *write )
|
||||
{
|
||||
IM_FREEF( im_threadgroup_free, write->tg );
|
||||
IM_FREEF( im_close, write->in );
|
||||
IM_FREEF( fclose, write->fp );
|
||||
if( write->pPng )
|
||||
png_destroy_write_struct( &write->pPng, &write->pInfo );
|
||||
IM_FREE( write->row_pointer );
|
||||
|
||||
im_free( write );
|
||||
}
|
||||
|
||||
static Write *
|
||||
write_new( IMAGE *in )
|
||||
{
|
||||
Write *write;
|
||||
|
||||
if( !(write = IM_NEW( NULL, Write )) )
|
||||
return( NULL );
|
||||
memset( write, 0, sizeof( Write ) );
|
||||
|
||||
if( !(write->in = im__convert_saveable( in, IM__RGBA )) ) {
|
||||
im_error( "im_vips2png",
|
||||
_( "unable to convert to RGB for save" ) );
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
write->tg = im_threadgroup_create( write->in );
|
||||
write->row_pointer = IM_ARRAY( NULL, write->tg->nlines, png_bytep );
|
||||
write->fp = NULL;
|
||||
write->pPng = NULL;
|
||||
write->pInfo = NULL;
|
||||
|
||||
if( !write->tg || !write->row_pointer ) {
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( !(write->pPng = png_create_write_struct(
|
||||
PNG_LIBPNG_VER_STRING, NULL,
|
||||
user_error_function, user_warning_function )) ) {
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Catch PNG errors from png_create_info_struct().
|
||||
*/
|
||||
if( setjmp( write->pPng->jmpbuf ) ) {
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( !(write->pInfo = png_create_info_struct( write->pPng )) ) {
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( write );
|
||||
}
|
||||
|
||||
static int
|
||||
write_png_block( REGION *region, Rect *area, void *a, void *b )
|
||||
{
|
||||
Write *write = (Write *) a;
|
||||
int i;
|
||||
|
||||
/* Catch PNG errors. Yuk.
|
||||
*/
|
||||
if( setjmp( write->pPng->jmpbuf ) )
|
||||
return( -1 );
|
||||
|
||||
for( i = 0; i < area->height; i++ )
|
||||
write->row_pointer[i] = (png_bytep)
|
||||
IM_REGION_ADDR( region, 0, area->top + i );
|
||||
|
||||
png_write_rows( write->pPng, write->row_pointer, area->height );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Write a VIPS image to PNG.
|
||||
*/
|
||||
static int
|
||||
write_vips( Write *write, int compress, int interlace )
|
||||
{
|
||||
IMAGE *in = write->in;
|
||||
|
||||
int i, nb_passes;
|
||||
|
||||
assert( in->BandFmt == IM_BANDFMT_UCHAR );
|
||||
assert( in->Coding == IM_CODING_NONE );
|
||||
assert( in->Bands > 0 && in->Bands < 5 );
|
||||
|
||||
/* Catch PNG errors.
|
||||
*/
|
||||
if( setjmp( write->pPng->jmpbuf ) )
|
||||
return( -1 );
|
||||
|
||||
/* Check input image.
|
||||
*/
|
||||
if( im_pincheck( in ) )
|
||||
return( -1 );
|
||||
if( compress < 0 || compress > 9 ) {
|
||||
im_error( "im_vips2png", _( "compress should be in [0,9]" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Set compression parameters.
|
||||
*/
|
||||
png_set_compression_level( write->pPng, compress );
|
||||
|
||||
write->pInfo->width = in->Xsize;
|
||||
write->pInfo->height = in->Ysize;
|
||||
write->pInfo->bit_depth = (in->BandFmt == IM_BANDFMT_UCHAR ? 8 : 16);
|
||||
write->pInfo->gamma = (float) 1.0;
|
||||
|
||||
switch( in->Bands ) {
|
||||
case 1: write->pInfo->color_type = PNG_COLOR_TYPE_GRAY; break;
|
||||
case 2: write->pInfo->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
|
||||
case 3: write->pInfo->color_type = PNG_COLOR_TYPE_RGB; break;
|
||||
case 4: write->pInfo->color_type = PNG_COLOR_TYPE_RGB_ALPHA; break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
png_write_info( write->pPng, write->pInfo );
|
||||
|
||||
/* If we're an intel byte order CPU and this is a 16bit image, we need
|
||||
* to swap bytes.
|
||||
*/
|
||||
if( write->pInfo->bit_depth > 8 && !im_amiMSBfirst() )
|
||||
png_set_swap( write->pPng );
|
||||
|
||||
if( interlace )
|
||||
nb_passes = png_set_interlace_handling( write->pPng );
|
||||
else
|
||||
nb_passes = 1;
|
||||
|
||||
/* Write data.
|
||||
*/
|
||||
for( i = 0; i < nb_passes; i++ )
|
||||
if( im_wbuffer( write->tg, write_png_block, write, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
/* The setjmp() was held by our background writer: reset it.
|
||||
*/
|
||||
if( setjmp( write->pPng->jmpbuf ) )
|
||||
return( -1 );
|
||||
|
||||
png_write_end( write->pPng, write->pInfo );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Write a VIPS image to a file as PNG.
|
||||
*/
|
||||
int
|
||||
im_vips2png( IMAGE *in, const char *filename )
|
||||
{
|
||||
Write *write;
|
||||
int compress;
|
||||
int interlace;
|
||||
|
||||
char *p, *q;
|
||||
|
||||
char name[FILENAME_MAX];
|
||||
char mode[FILENAME_MAX];
|
||||
char buf[FILENAME_MAX];
|
||||
|
||||
if( !(write = write_new( in )) )
|
||||
return( -1 );
|
||||
|
||||
/* Extract write mode from filename and parse.
|
||||
*/
|
||||
im_filename_split( filename, name, mode );
|
||||
strcpy( buf, mode );
|
||||
p = &buf[0];
|
||||
compress = 6;
|
||||
interlace = 0;
|
||||
if( (q = im_getnextoption( &p )) )
|
||||
compress = atoi( q );
|
||||
if( (q = im_getnextoption( &p )) )
|
||||
interlace = atoi( q );
|
||||
|
||||
/* Make output.
|
||||
*/
|
||||
#ifdef BINARY_OPEN
|
||||
if( !(write->fp = fopen( name, "wb" )) ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( !(write->fp = fopen( name, "w" )) ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
write_destroy( write );
|
||||
im_error( "im_vips2png", _( "unable to open \"%s\"" ), name );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
png_init_io( write->pPng, write->fp );
|
||||
|
||||
/* Convert it!
|
||||
*/
|
||||
if( write_vips( write, compress, interlace ) ) {
|
||||
write_destroy( write );
|
||||
im_error( "im_vips2png", _( "unable to write \"%s\"" ), name );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
write_destroy( write );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif /*HAVE_PNG*/
|
293
libsrc/conversion/im_vips2ppm.c
Normal file
293
libsrc/conversion/im_vips2ppm.c
Normal file
@ -0,0 +1,293 @@
|
||||
/* Write a ppm file.
|
||||
*
|
||||
* 28/11/03 JC
|
||||
* - better no-overshoot on tile loop
|
||||
* 9/9/05
|
||||
* - tiny cleanups
|
||||
* 3/11/07
|
||||
* - use im_wbuffer() for bg writes
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* What we track during a PPM write.
|
||||
*/
|
||||
typedef struct {
|
||||
IMAGE *in;
|
||||
im_threadgroup_t *tg;
|
||||
FILE *fp;
|
||||
char *name;
|
||||
} Write;
|
||||
|
||||
static void
|
||||
write_destroy( Write *write )
|
||||
{
|
||||
IM_FREEF( im_threadgroup_free, write->tg );
|
||||
IM_FREEF( fclose, write->fp );
|
||||
IM_FREE( write->name );
|
||||
|
||||
im_free( write );
|
||||
}
|
||||
|
||||
static Write *
|
||||
write_new( IMAGE *in, const char *name )
|
||||
{
|
||||
Write *write;
|
||||
|
||||
if( !(write = IM_NEW( NULL, Write )) )
|
||||
return( NULL );
|
||||
|
||||
write->in = in;
|
||||
write->tg = im_threadgroup_create( write->in );
|
||||
write->name = im_strdup( NULL, name );
|
||||
|
||||
#ifdef BINARY_OPEN
|
||||
if( !(write->fp = fopen( name, "wb" )) ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( !(write->fp = fopen( name, "w" )) ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
im_error( "im_vips2ppm",
|
||||
_( "unable to open \"%s\" for output" ), name );
|
||||
}
|
||||
|
||||
if( !write->tg || !write->name || !write->fp ) {
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( write );
|
||||
}
|
||||
|
||||
typedef int (*write_fn)( IMAGE *in, FILE *fp, PEL *p );
|
||||
|
||||
static int
|
||||
write_ppm_line_ascii( IMAGE *in, FILE *fp, PEL *p )
|
||||
{
|
||||
const int sk = IM_IMAGE_SIZEOF_PEL( in );
|
||||
const int nb = IM_MIN( 3, in->Bands );
|
||||
int x, k;
|
||||
|
||||
/* If IM_CODING_LABQ, write 3 bands.
|
||||
*/
|
||||
|
||||
for( x = 0; x < in->Xsize; x++ ) {
|
||||
for( k = 0; k < nb; k++ ) {
|
||||
switch( in->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
fprintf( fp, "%d ", p[k] );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_USHORT:
|
||||
fprintf( fp, "%d ", ((unsigned short *) p)[k] );
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_UINT:
|
||||
fprintf( fp, "%d ", ((unsigned int *) p)[k] );
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
fprintf( fp, " " );
|
||||
|
||||
p += sk;
|
||||
}
|
||||
|
||||
if( !fprintf( fp, "\n" ) ) {
|
||||
im_error( "im_vips2ppm", _( "write error ... disc full?" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
write_ppm_line_binary( IMAGE *in, FILE *fp, PEL *p )
|
||||
{
|
||||
const int sk = IM_IMAGE_SIZEOF_PEL( in );
|
||||
const int nb = IM_MIN( 3, in->Bands );
|
||||
int x;
|
||||
|
||||
for( x = 0; x < in->Xsize; x++ ) {
|
||||
if( !fwrite( p, 1, nb, fp ) ) {
|
||||
im_error( "im_vips2ppm",
|
||||
_( "write error ... disc full?" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
p += sk;
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
write_ppm_block( REGION *region, Rect *area, void *a, void *b )
|
||||
{
|
||||
Write *write = (Write *) a;
|
||||
write_fn fn = (write_fn) b;
|
||||
int i;
|
||||
|
||||
for( i = 0; i < area->height; i++ ) {
|
||||
PEL *p = (PEL *) IM_REGION_ADDR( region, 0, area->top + i );
|
||||
|
||||
if( fn( write->in, write->fp, p ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
write_ppm( Write *write, int ascii )
|
||||
{
|
||||
IMAGE *in = write->in;
|
||||
write_fn fn = ascii ? write_ppm_line_ascii : write_ppm_line_binary;
|
||||
|
||||
int max_value;
|
||||
char *magic;
|
||||
time_t timebuf;
|
||||
|
||||
switch( in->BandFmt ) {
|
||||
case IM_BANDFMT_UCHAR:
|
||||
max_value = UCHAR_MAX;
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_USHORT:
|
||||
max_value = USHRT_MAX;
|
||||
break;
|
||||
|
||||
case IM_BANDFMT_UINT:
|
||||
max_value = UINT_MAX;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
if( in->Bands == 1 && ascii )
|
||||
magic = "P2";
|
||||
else if( in->Bands == 1 && !ascii )
|
||||
magic = "P5";
|
||||
else if( in->Bands == 3 && ascii )
|
||||
magic = "P3";
|
||||
else if( in->Bands == 3 && !ascii )
|
||||
magic = "P6";
|
||||
else
|
||||
assert( 0 );
|
||||
|
||||
fprintf( write->fp, "%s\n", magic );
|
||||
time( &timebuf );
|
||||
fprintf( write->fp, "#im_vips2ppm - %s\n", ctime( &timebuf ) );
|
||||
fprintf( write->fp, "%d %d\n", in->Xsize, in->Ysize );
|
||||
fprintf( write->fp, "%d\n", max_value );
|
||||
|
||||
if( im_wbuffer( write->tg, write_ppm_block, write, fn ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_vips2ppm( IMAGE *in, const char *filename )
|
||||
{
|
||||
Write *write;
|
||||
int ascii;
|
||||
char name[FILENAME_MAX];
|
||||
char mode[FILENAME_MAX];
|
||||
|
||||
/* Default to binary output ... much smaller.
|
||||
*/
|
||||
ascii = 0;
|
||||
|
||||
/* Extract write mode from filename.
|
||||
*/
|
||||
im_filename_split( filename, name, mode );
|
||||
if( strcmp( mode, "" ) != 0 ) {
|
||||
if( im_isprefix( "binary", mode ) )
|
||||
ascii = 0;
|
||||
else if( im_isprefix( "ascii", mode ) )
|
||||
ascii = 1;
|
||||
else {
|
||||
im_error( "im_vips2ppm",
|
||||
_( "bad mode string, "
|
||||
"should be \"binary\" or \"ascii\"" ) );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( in->Bbits > 8 && !ascii ) {
|
||||
im_error( "im_vips2ppm",
|
||||
_( "can't write binary >8 bit images" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( !im_isuint( in ) ) {
|
||||
im_error( "im_vips2ppm", _( "unsigned int formats only" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) {
|
||||
im_error( "im_vips2ppm",
|
||||
_( "uncoded or IM_CODING_LABQ only" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( in->Bands != 1 && in->Bands != 3 ) {
|
||||
im_error( "im_vips2ppm", _( "1 or 3 band images only" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( im_pincheck( in ) || !(write = write_new( in, name )) )
|
||||
return( -1 );
|
||||
|
||||
if( write_ppm( write, ascii ) ) {
|
||||
write_destroy( write );
|
||||
return( -1 );
|
||||
}
|
||||
write_destroy( write );
|
||||
|
||||
return( 0 );
|
||||
}
|
1653
libsrc/conversion/im_vips2tiff.c
Normal file
1653
libsrc/conversion/im_vips2tiff.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -139,6 +139,33 @@ Modified:
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* Suffix sets.
|
||||
*/
|
||||
static const char *im_suffix_vips[] = {
|
||||
"v", "",
|
||||
NULL
|
||||
};
|
||||
static const char *im_suffix_tiff[] = {
|
||||
"tif", "tiff",
|
||||
NULL
|
||||
};
|
||||
static const char *im_suffix_jpeg[] = {
|
||||
"jpeg", "jpg", "jfif", "jpe",
|
||||
NULL
|
||||
};
|
||||
static const char *im_suffix_ppm[] = {
|
||||
"ppm", "pbm", "pgm",
|
||||
NULL
|
||||
};
|
||||
static const char *im_suffix_png[] = {
|
||||
"png",
|
||||
NULL
|
||||
};
|
||||
static const char *im_suffix_csv[] = {
|
||||
"csv",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Progress feedback. Only really useful for testing, tbh.
|
||||
*/
|
||||
int im__progress = 0;
|
||||
@ -198,30 +225,6 @@ read_vips( const char *filename )
|
||||
return( im2 );
|
||||
}
|
||||
|
||||
/*
|
||||
else if( im_isvips( name ) ) {
|
||||
if( mode[1] == 'w' ) {
|
||||
if( !(im = im_init( filename )) )
|
||||
return( NULL );
|
||||
if( im_openinrw( im ) ) {
|
||||
im_close( im );
|
||||
return( NULL );
|
||||
}
|
||||
if( im->Bbits != IM_BBITS_BYTE &&
|
||||
im_isMSBfirst( im ) !=
|
||||
im_amiMSBfirst() ) {
|
||||
im_close( im );
|
||||
im_error( "im_open", _( "open for read-"
|
||||
"write for native format "
|
||||
"images only" ) );
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
else
|
||||
im = read_vips( filename );
|
||||
}
|
||||
*/
|
||||
|
||||
/* Delayed save: if we write to TIFF or to JPEG format, actually do the write
|
||||
* to a "p" and on preclose do im_vips2tiff() or whatever. Track save
|
||||
* parameters here.
|
||||
@ -413,7 +416,6 @@ IMAGE *
|
||||
im_open( const char *filename, const char *mode )
|
||||
{
|
||||
IMAGE *im;
|
||||
im_format *format;
|
||||
|
||||
/* Pass in a nonsense name for argv0 ... this init world is only here
|
||||
* for old programs which are missing an im_init_world() call. We must
|
||||
@ -429,11 +431,107 @@ im_open( const char *filename, const char *mode )
|
||||
|
||||
switch( mode[0] ) {
|
||||
case 'r':
|
||||
if( !(format = im_format_for_file( filename )) )
|
||||
{
|
||||
char name[FILENAME_MAX];
|
||||
char options[FILENAME_MAX];
|
||||
|
||||
/* Break any options off the name ... eg. "fred.tif:jpeg,tile"
|
||||
* etc.
|
||||
*/
|
||||
im_filename_split( filename, name, options );
|
||||
|
||||
/* Check for other formats.
|
||||
|
||||
FIXME ... should have a table to avoid all this
|
||||
repetition
|
||||
|
||||
*/
|
||||
if( !im_existsf( "%s", name ) ) {
|
||||
im_error( "im_open",
|
||||
_( "\"%s\" is not readable" ), name );
|
||||
return( NULL );
|
||||
if( !(im = open_sub(
|
||||
format->header, format->load, filename )) )
|
||||
}
|
||||
else if( im_istiff( name ) ) {
|
||||
/* If TIFF open fails, try going through libmagick.
|
||||
*/
|
||||
if( !(im = open_sub(
|
||||
im_tiff2vips_header, im_tiff2vips,
|
||||
filename )) &&
|
||||
!(im = open_sub(
|
||||
im_magick2vips_header, im_magick2vips,
|
||||
filename )) )
|
||||
return( NULL );
|
||||
}
|
||||
else if( im_isjpeg( name ) ) {
|
||||
if( !(im = open_sub(
|
||||
im_jpeg2vips_header, im_jpeg2vips, filename )) )
|
||||
return( NULL );
|
||||
}
|
||||
else if( im_isexr( name ) ) {
|
||||
if( !(im = open_sub(
|
||||
im_exr2vips_header, im_exr2vips, filename )) )
|
||||
return( NULL );
|
||||
}
|
||||
else if( im_isppm( name ) ) {
|
||||
if( !(im = open_sub(
|
||||
im_ppm2vips_header, im_ppm2vips, filename )) )
|
||||
return( NULL );
|
||||
}
|
||||
else if( im_ispng( name ) ) {
|
||||
if( !(im = open_sub(
|
||||
im_png2vips_header, im_png2vips, filename )) )
|
||||
return( NULL );
|
||||
}
|
||||
else if( im_filename_suffix_match( name, im_suffix_csv ) ) {
|
||||
if( !(im = open_sub(
|
||||
im_csv2vips_header, im_csv2vips, filename )) )
|
||||
return( NULL );
|
||||
}
|
||||
else if( im_isvips( name ) ) {
|
||||
if( mode[1] == 'w' ) {
|
||||
/* Has to be native format for >8 bits.
|
||||
*/
|
||||
if( !(im = im_init( filename )) )
|
||||
return( NULL );
|
||||
if( im_openinrw( im ) ) {
|
||||
im_close( im );
|
||||
return( NULL );
|
||||
}
|
||||
if( im->Bbits != IM_BBITS_BYTE &&
|
||||
im_isMSBfirst( im ) !=
|
||||
im_amiMSBfirst() ) {
|
||||
im_close( im );
|
||||
im_error( "im_open", _( "open for read-"
|
||||
"write for native format "
|
||||
"images only" ) );
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
else
|
||||
im = read_vips( filename );
|
||||
}
|
||||
else if( im_isanalyze( name ) ) {
|
||||
if( !(im = open_sub(
|
||||
im_analyze2vips_header, im_analyze2vips,
|
||||
filename )) )
|
||||
return( NULL );
|
||||
}
|
||||
else if( im_ismagick( name ) ) {
|
||||
/* Have this last as it can be very slow to detect
|
||||
* failure.
|
||||
*/
|
||||
if( !(im = open_sub(
|
||||
im_magick2vips_header, im_magick2vips,
|
||||
filename )) )
|
||||
return( NULL );
|
||||
}
|
||||
else {
|
||||
im_error( "im_open", _( "\"%s\" is not "
|
||||
"a supported format" ), filename );
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
|
@ -1,819 +0,0 @@
|
||||
/* Read and write a VIPS file into an IMAGE *
|
||||
*
|
||||
* 22/5/08
|
||||
* - from im_open.c, im_openin.c, im_desc_hd.c, im_readhist.c,
|
||||
* im_openout.c
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_FILE_H
|
||||
#include <sys/file.h>
|
||||
#endif /*HAVE_SYS_FILE_H*/
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /*HAVE_UNISTD_H*/
|
||||
#ifdef HAVE_IO_H
|
||||
#include <io.h>
|
||||
#endif /*HAVE_IO_H*/
|
||||
#include <libxml/parser.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef OS_WIN32
|
||||
#include <windows.h>
|
||||
#endif /*OS_WIN32*/
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
#include <vips/debug.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* Try to make an O_BINARY ... sometimes need the leading '_'.
|
||||
*/
|
||||
#ifdef BINARY_OPEN
|
||||
#ifndef O_BINARY
|
||||
#ifdef _O_BINARY
|
||||
#define O_BINARY _O_BINARY
|
||||
#endif /*_O_BINARY*/
|
||||
#endif /*!O_BINARY*/
|
||||
#endif /*BINARY_OPEN*/
|
||||
|
||||
/* Our XML namespace.
|
||||
*/
|
||||
#define NAMESPACE "http://www.vips.ecs.soton.ac.uk/vips"
|
||||
|
||||
/* mmap() whole vs. window threshold ... an int, so we can tune easily from a
|
||||
* debugger.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
int im__mmap_limit = 1;
|
||||
#else
|
||||
int im__mmap_limit = IM__MMAP_LIMIT;
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Sort of open for read for image files.
|
||||
*/
|
||||
static int
|
||||
im__open_image_file( const char *filename )
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* Try to open read-write, so that calls to im_makerw() will work.
|
||||
* When we later mmap this file, we set read-only, so there
|
||||
* is little danger of scrubbing over files we own.
|
||||
*/
|
||||
#ifdef BINARY_OPEN
|
||||
if( (fd = open( filename, O_RDWR | O_BINARY )) == -1 ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( (fd = open( filename, O_RDWR )) == -1 ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
/* Open read-write failed. Fall back to open read-only.
|
||||
*/
|
||||
#ifdef BINARY_OPEN
|
||||
if( (fd = open( filename, O_RDONLY | O_BINARY )) == -1 ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( (fd = open( filename, O_RDONLY )) == -1 ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
im_error( "im__open_image_file",
|
||||
_( "unable to open \"%s\", %s" ),
|
||||
filename, strerror( errno ) );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( fd );
|
||||
}
|
||||
|
||||
/* Predict the size of the header plus pixel data. Don't use off_t,
|
||||
* it's sometimes only 32 bits (eg. on many windows build environments) and we
|
||||
* want to always be 64 bit.
|
||||
*/
|
||||
static gint64
|
||||
im__image_pixel_length( IMAGE *im )
|
||||
{
|
||||
gint64 psize;
|
||||
|
||||
switch( im->Coding ) {
|
||||
case IM_CODING_LABQ:
|
||||
case IM_CODING_NONE:
|
||||
psize = (gint64) IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize;
|
||||
break;
|
||||
|
||||
default:
|
||||
psize = im->Length;
|
||||
break;
|
||||
}
|
||||
|
||||
return( psize + im->sizeof_header );
|
||||
}
|
||||
|
||||
/* Read short/int/float LSB and MSB first.
|
||||
*/
|
||||
void
|
||||
im__read_4byte( int msb_first, unsigned char *to, unsigned char **from )
|
||||
{
|
||||
unsigned char *p = *from;
|
||||
int out;
|
||||
|
||||
if( msb_first )
|
||||
out = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
|
||||
else
|
||||
out = p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
|
||||
|
||||
*from += 4;
|
||||
*((guint32 *) to) = out;
|
||||
}
|
||||
|
||||
void
|
||||
im__read_2byte( int msb_first, unsigned char *to, unsigned char **from )
|
||||
{
|
||||
int out;
|
||||
unsigned char *p = *from;
|
||||
|
||||
if( msb_first )
|
||||
out = p[0] << 8 | p[1];
|
||||
else
|
||||
out = p[1] << 8 | p[0];
|
||||
|
||||
*from += 2;
|
||||
*((guint16 *) to) = out;
|
||||
}
|
||||
|
||||
/* We always write in native byte order.
|
||||
*/
|
||||
void
|
||||
im__write_4byte( unsigned char **to, unsigned char *from )
|
||||
{
|
||||
*((guint32 *) *to) = *((guint32 *) from);
|
||||
*to += 4;
|
||||
}
|
||||
|
||||
void
|
||||
im__write_2byte( unsigned char **to, unsigned char *from )
|
||||
{
|
||||
*((guint16 *) *to) = *((guint16 *) from);
|
||||
*to += 2;
|
||||
}
|
||||
|
||||
/* offset, read, write functions.
|
||||
*/
|
||||
typedef struct _FieldIO {
|
||||
glong offset;
|
||||
void (*read)( int msb_first, unsigned char *to, unsigned char **from );
|
||||
void (*write)( unsigned char **to, unsigned char *from );
|
||||
} FieldIO;
|
||||
|
||||
static FieldIO fields[] = {
|
||||
{ G_STRUCT_OFFSET( IMAGE, Xsize ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Ysize ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Bands ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Bbits ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, BandFmt ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Coding ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Type ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Xres ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Yres ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Length ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Compression ),
|
||||
im__read_2byte, im__write_2byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Level ),
|
||||
im__read_2byte, im__write_2byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Xoffset ),
|
||||
im__read_4byte, im__write_4byte },
|
||||
{ G_STRUCT_OFFSET( IMAGE, Yoffset ),
|
||||
im__read_4byte, im__write_4byte }
|
||||
};
|
||||
|
||||
static int
|
||||
im__read_header_bytes( IMAGE *im, unsigned char *from )
|
||||
{
|
||||
int msb_first;
|
||||
int i;
|
||||
|
||||
im__read_4byte( 1, (unsigned char *) &im->magic, &from );
|
||||
if( im->magic != IM_MAGIC_INTEL && im->magic != IM_MAGIC_SPARC ) {
|
||||
im_error( "im_open", _( "\"%s\" is not a VIPS image" ),
|
||||
im->filename );
|
||||
return( -1 );
|
||||
}
|
||||
msb_first = im->magic == IM_MAGIC_SPARC;
|
||||
|
||||
for( i = 0; i < IM_NUMBER( fields ); i++ )
|
||||
fields[i].read( msb_first,
|
||||
&G_STRUCT_MEMBER( unsigned char, im, fields[i].offset ),
|
||||
&from );
|
||||
|
||||
/* Set this ourselves ... bbits is deprecated in the file format.
|
||||
*/
|
||||
im->Bbits = im_bits_of_fmt( im->BandFmt );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im__write_header_bytes( IMAGE *im, unsigned char *to )
|
||||
{
|
||||
guint32 magic;
|
||||
int i;
|
||||
unsigned char *q;
|
||||
|
||||
/* Always write the magic number MSB first.
|
||||
*/
|
||||
magic = im_amiMSBfirst() ? IM_MAGIC_SPARC : IM_MAGIC_INTEL;
|
||||
to[0] = magic >> 24;
|
||||
to[1] = magic >> 16;
|
||||
to[2] = magic >> 8;
|
||||
to[3] = magic;
|
||||
q = to + 4;
|
||||
|
||||
for( i = 0; i < IM_NUMBER( fields ); i++ )
|
||||
fields[i].write( &q,
|
||||
&G_STRUCT_MEMBER( unsigned char, im,
|
||||
fields[i].offset ) );
|
||||
|
||||
/* Pad spares with zeros.
|
||||
*/
|
||||
while( q - to < im->sizeof_header )
|
||||
*q++ = 0;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a chunk of an fd into memory. Add a '\0' at the end.
|
||||
*/
|
||||
static char *
|
||||
read_chunk( int fd, gint64 offset, size_t length )
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if( im__seek( fd, offset ) )
|
||||
return( NULL );
|
||||
if( !(buf = im_malloc( NULL, length + 1 )) )
|
||||
return( NULL );
|
||||
if( read( fd, buf, length ) != length ) {
|
||||
im_free( buf );
|
||||
im_error( "im_readhist", _( "unable to read history" ) );
|
||||
return( NULL );
|
||||
}
|
||||
buf[length] = '\0';
|
||||
|
||||
return( buf );
|
||||
}
|
||||
|
||||
/* Does it look like an image has an extension block?
|
||||
*/
|
||||
static int
|
||||
im__has_extension_block( IMAGE *im )
|
||||
{
|
||||
gint64 length;
|
||||
gint64 psize;
|
||||
|
||||
psize = im__image_pixel_length( im );
|
||||
if( (length = im_file_length( im->fd )) == -1 )
|
||||
return( 0 );
|
||||
|
||||
return( length - psize > 0 );
|
||||
}
|
||||
|
||||
/* Read everything after the pixels into memory.
|
||||
*/
|
||||
static void *
|
||||
im__read_extension_block( IMAGE *im, int *size )
|
||||
{
|
||||
gint64 length;
|
||||
gint64 psize;
|
||||
void *buf;
|
||||
|
||||
psize = im__image_pixel_length( im );
|
||||
if( (length = im_file_length( im->fd )) == -1 )
|
||||
return( NULL );
|
||||
if( length - psize > 10 * 1024 * 1024 ) {
|
||||
im_error( "im_readhist",
|
||||
_( "more than a 10 megabytes of XML? "
|
||||
"sufferin' succotash!" ) );
|
||||
return( NULL );
|
||||
}
|
||||
if( length - psize == 0 )
|
||||
return( NULL );
|
||||
if( !(buf = read_chunk( im->fd, psize, length - psize )) )
|
||||
return( NULL );
|
||||
if( size )
|
||||
*size = length - psize;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im__read_extension_block: read %d bytes from %s\n",
|
||||
(int) (length - psize), im->filename );
|
||||
printf( "data: \"%s\"\n", (char *) buf );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( buf );
|
||||
}
|
||||
|
||||
/* Read everything after the pixels into memory.
|
||||
|
||||
FIXME ... why can't we use xmlParserInputBufferCreateFd and parse
|
||||
directly from the fd rather than having to read the stupid thing into
|
||||
memory
|
||||
|
||||
the libxml API docs are impossible to decipher
|
||||
|
||||
*/
|
||||
static xmlDoc *
|
||||
read_xml( IMAGE *im )
|
||||
{
|
||||
void *buf;
|
||||
int size;
|
||||
xmlDoc *doc;
|
||||
xmlNode *node;
|
||||
|
||||
if( !(buf = im__read_extension_block( im, &size )) )
|
||||
return( NULL );
|
||||
if( !(doc = xmlParseMemory( buf, size )) ) {
|
||||
im_free( buf );
|
||||
return( NULL );
|
||||
}
|
||||
im_free( buf );
|
||||
if( !(node = xmlDocGetRootElement( doc )) ||
|
||||
!node->nsDef ||
|
||||
!im_isprefix( NAMESPACE, (char *) node->nsDef->href ) ) {
|
||||
im_error( "im__readhist", _( "incorrect namespace in XML" ) );
|
||||
xmlFreeDoc( doc );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "read_xml: namespace == %s\n", node->nsDef->href );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( doc );
|
||||
}
|
||||
|
||||
/* Find the first child node with a name.
|
||||
*/
|
||||
static xmlNode *
|
||||
get_node( xmlNode *base, const char *name )
|
||||
{
|
||||
xmlNode *i;
|
||||
|
||||
for( i = base->children; i; i = i->next )
|
||||
if( strcmp( (char *) i->name, name ) == 0 )
|
||||
return( i );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Read a string property to a buffer. TRUE for success.
|
||||
*/
|
||||
static int
|
||||
get_sprop( xmlNode *xnode, const char *name, char *buf, int sz )
|
||||
{
|
||||
char *value = (char *) xmlGetProp( xnode, (xmlChar *) name );
|
||||
|
||||
if( !value )
|
||||
return( 0 );
|
||||
|
||||
im_strncpy( buf, value, sz );
|
||||
IM_FREEF( xmlFree, value );
|
||||
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
/* Chop history into lines, add each one as a refstring.
|
||||
*/
|
||||
static void
|
||||
set_history( IMAGE *im, char *history )
|
||||
{
|
||||
GSList *history_list;
|
||||
char *p, *q;
|
||||
|
||||
/* There can be history there already if we're rewinding.
|
||||
*/
|
||||
IM_FREEF( im__gslist_gvalue_free, im->history_list );
|
||||
|
||||
history_list = NULL;
|
||||
|
||||
for( p = history; *p; p = q ) {
|
||||
if( (q = strchr( p, '\n' )) )
|
||||
*q = '\0';
|
||||
else
|
||||
q = p + strlen( p );
|
||||
|
||||
history_list = g_slist_prepend( history_list,
|
||||
im__gvalue_ref_string_new( p ) );
|
||||
}
|
||||
|
||||
im->history_list = g_slist_reverse( history_list );
|
||||
}
|
||||
|
||||
/* Load header fields.
|
||||
*/
|
||||
static int
|
||||
rebuild_header_builtin( IMAGE *im, xmlNode *i )
|
||||
{
|
||||
char name[256];
|
||||
|
||||
if( get_sprop( i, "name", name, 256 ) ) {
|
||||
if( strcmp( name, "Hist" ) == 0 ) {
|
||||
char *history;
|
||||
|
||||
/* Have to take (another) copy, since we need to free
|
||||
* with xmlFree().
|
||||
*/
|
||||
history = (char *) xmlNodeGetContent( i );
|
||||
set_history( im, history );
|
||||
xmlFree( history );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Load meta fields.
|
||||
*/
|
||||
static int
|
||||
rebuild_header_meta( IMAGE *im, xmlNode *i )
|
||||
{
|
||||
char name[256];
|
||||
char type[256];
|
||||
|
||||
if( get_sprop( i, "name", name, 256 ) &&
|
||||
get_sprop( i, "type", type, 256 ) ) {
|
||||
GType gtype = g_type_from_name( type );
|
||||
|
||||
/* Can we convert from IM_SAVE_STRING to type?
|
||||
*/
|
||||
if( gtype &&
|
||||
g_value_type_transformable(
|
||||
IM_TYPE_SAVE_STRING, gtype ) ) {
|
||||
char *content;
|
||||
GValue save_value = { 0 };
|
||||
GValue value = { 0 };
|
||||
|
||||
content = (char *) xmlNodeGetContent( i );
|
||||
g_value_init( &save_value, IM_TYPE_SAVE_STRING );
|
||||
im_save_string_set( &save_value, content );
|
||||
xmlFree( content );
|
||||
|
||||
g_value_init( &value, gtype );
|
||||
if( !g_value_transform( &save_value, &value ) ) {
|
||||
g_value_unset( &save_value );
|
||||
im_error( "im__readhist", _( "error "
|
||||
"transforming from save format" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( im_meta_set( im, name, &value ) ) {
|
||||
g_value_unset( &save_value );
|
||||
g_value_unset( &value );
|
||||
return( -1 );
|
||||
}
|
||||
g_value_unset( &save_value );
|
||||
g_value_unset( &value );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static xmlDoc *
|
||||
get_xml( IMAGE *im )
|
||||
{
|
||||
if( im_header_get_type( im, IM_META_XML ) ) {
|
||||
xmlDoc *doc;
|
||||
|
||||
if( im_meta_get_area( im, IM_META_XML, (void *) &doc ) )
|
||||
return( NULL );
|
||||
|
||||
return( doc );
|
||||
}
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Rebuild header fields that depend on stuff saved in xml.
|
||||
*/
|
||||
static int
|
||||
rebuild_header( IMAGE *im )
|
||||
{
|
||||
xmlDoc *doc;
|
||||
|
||||
if( (doc = get_xml( im )) ) {
|
||||
xmlNode *root;
|
||||
xmlNode *block;
|
||||
|
||||
if( !(root = xmlDocGetRootElement( doc )) )
|
||||
return( -1 );
|
||||
if( (block = get_node( root, "header" )) ) {
|
||||
xmlNode *i;
|
||||
|
||||
for( i = block->children; i; i = i->next )
|
||||
if( strcmp( (char *) i->name, "field" ) == 0 )
|
||||
if( rebuild_header_builtin( im, i ) )
|
||||
return( -1 );
|
||||
}
|
||||
if( (block = get_node( root, "meta" )) ) {
|
||||
xmlNode *i;
|
||||
|
||||
for( i = block->children; i; i = i->next )
|
||||
if( strcmp( (char *) i->name, "field" ) == 0 )
|
||||
if( rebuild_header_meta( im, i ) )
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Called at the end of im__read_header ... get any XML after the pixel data
|
||||
* and read it in.
|
||||
*/
|
||||
static int
|
||||
im__readhist( IMAGE *im )
|
||||
{
|
||||
/* Junk any old xml meta.
|
||||
*/
|
||||
if( im_header_get_type( im, IM_META_XML ) )
|
||||
im_meta_set_area( im, IM_META_XML, NULL, NULL );
|
||||
|
||||
if( im__has_extension_block( im ) ) {
|
||||
xmlDoc *doc;
|
||||
|
||||
if( !(doc = read_xml( im )) )
|
||||
return( -1 );
|
||||
if( im_meta_set_area( im, IM_META_XML,
|
||||
(im_callback_fn) xmlFreeDoc, doc ) ) {
|
||||
xmlFreeDoc( doc );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( rebuild_header( im ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Open the filename, read the header, some sanity checking.
|
||||
*/
|
||||
static int
|
||||
im__read_header( IMAGE *image )
|
||||
{
|
||||
/* We don't use im->sizeof_header here, but we know we're reading a
|
||||
* VIPS image anyway.
|
||||
*/
|
||||
unsigned char header[IM_SIZEOF_HEADER];
|
||||
|
||||
gint64 length;
|
||||
gint64 psize;
|
||||
|
||||
image->dtype = IM_OPENIN;
|
||||
if( (image->fd = im__open_image_file( image->filename )) == -1 )
|
||||
return( -1 );
|
||||
if( read( image->fd, header, IM_SIZEOF_HEADER ) != IM_SIZEOF_HEADER ||
|
||||
im__read_header_bytes( image, header ) ) {
|
||||
im_error( "im_openin",
|
||||
_( "unable to read header for \"%s\", %s" ),
|
||||
image->filename, strerror( errno ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Predict and check the file size.
|
||||
*/
|
||||
psize = im__image_pixel_length( image );
|
||||
if( (length = im_file_length( image->fd )) == -1 )
|
||||
return( -1 );
|
||||
if( psize > length ) {
|
||||
im_error( "im_openin", _( "unable to open \"%s\", %s" ),
|
||||
image->filename, _( "file has been truncated" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Set demand style. Allow the most permissive sort.
|
||||
*/
|
||||
image->dhint = IM_THINSTRIP;
|
||||
|
||||
/* Set the history part of im descriptor. Don't return an error if this
|
||||
* fails (due to eg. corrupted XML) because it's probably mostly
|
||||
* harmless.
|
||||
*/
|
||||
if( im__readhist( image ) ) {
|
||||
im_warn( "im_openin", _( "error reading XML: %s" ),
|
||||
im_error_buffer() );
|
||||
im_error_clear();
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Open, then mmap() small images, leave large images to have a rolling mmap()
|
||||
* window for each region.
|
||||
*/
|
||||
static int
|
||||
im_openin( IMAGE *image )
|
||||
{
|
||||
gint64 size;
|
||||
|
||||
#ifdef DEBUG
|
||||
char *str;
|
||||
|
||||
if( (str = g_getenv( "IM_MMAP_LIMIT" )) ) {
|
||||
im__mmap_limit = atoi( str );
|
||||
printf( "im_openin: setting maplimit to %d from environment\n",
|
||||
im__mmap_limit );
|
||||
}
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( im__read_header( image ) )
|
||||
return( -1 );
|
||||
|
||||
size = (gint64) IM_IMAGE_SIZEOF_LINE( image ) * image->Ysize +
|
||||
image->sizeof_header;
|
||||
if( size < im__mmap_limit ) {
|
||||
if( im_mapfile( image ) )
|
||||
return( -1 );
|
||||
image->data = image->baseaddr + image->sizeof_header;
|
||||
image->dtype = IM_MMAPIN;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_openin: completely mmap()ing \"%s\": it's small\n",
|
||||
image->filename );
|
||||
#endif /*DEBUG*/
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
printf( "im_openin: delaying mmap() of \"%s\": it's big!\n",
|
||||
image->filename );
|
||||
#endif /*DEBUG*/
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Open, then mmap() read/write.
|
||||
*/
|
||||
static int
|
||||
im_openinrw( IMAGE *image )
|
||||
{
|
||||
if( im__read_header( image ) )
|
||||
return( -1 );
|
||||
if( im_mapfilerw( image ) )
|
||||
return( -1 );
|
||||
image->data = image->baseaddr + image->sizeof_header;
|
||||
image->dtype = IM_MMAPINRW;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_openin: completely mmap()ing \"%s\" read-write\n",
|
||||
image->filename );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Open a VIPS image for reading and byte-swap the image data if necessary. A
|
||||
* ":w" at the end of the filename means we open read-write.
|
||||
*/
|
||||
IMAGE *
|
||||
im_vips_open( const char *filename )
|
||||
{
|
||||
char name[FILENAME_MAX];
|
||||
char mode[FILENAME_MAX];
|
||||
IMAGE *im;
|
||||
|
||||
im_filename_split( filename, name, mode );
|
||||
|
||||
if( !(im = im_init( name )) )
|
||||
return( NULL );
|
||||
if( mode[0] == 'w' ) {
|
||||
if( im_openinrw( im ) ) {
|
||||
im_close( im );
|
||||
return( NULL );
|
||||
}
|
||||
if( im->Bbits != IM_BBITS_BYTE &&
|
||||
im_isMSBfirst( im ) != im_amiMSBfirst() ) {
|
||||
im_close( im );
|
||||
im_error( "im_open_vips", _( "open for read-write for "
|
||||
"native format images only" ) );
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( im_openin( im ) ) {
|
||||
im_close( im );
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
/* Not in native format?
|
||||
*/
|
||||
if( im_isMSBfirst( im ) != im_amiMSBfirst() ) {
|
||||
/* Does it need swapping?
|
||||
*/
|
||||
switch( im->Coding ) {
|
||||
case IM_CODING_LABQ:
|
||||
break;
|
||||
|
||||
case IM_CODING_NONE:
|
||||
if( im->BandFmt != IM_BANDFMT_CHAR &&
|
||||
im->BandFmt != IM_BANDFMT_UCHAR ) {
|
||||
IMAGE *im2;
|
||||
|
||||
/* Needs swapping :( make a little pipeline up
|
||||
* to do this for us.
|
||||
*/
|
||||
if( !(im2 = im_open( filename, "p" )) ) {
|
||||
im_close( im );
|
||||
return( NULL );
|
||||
}
|
||||
if( im_add_close_callback( im2,
|
||||
(im_callback_fn)im_close, im, NULL ) ) {
|
||||
im_close( im );
|
||||
im_close( im2 );
|
||||
return( NULL );
|
||||
}
|
||||
if( im_copy_swap( im, im2 ) ) {
|
||||
im_close( im2 );
|
||||
return( NULL );
|
||||
}
|
||||
im = im2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
im_close( im );
|
||||
im_error( "im_open", _( "unknown coding type" ) );
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
return( im );
|
||||
}
|
||||
|
||||
IMAGE *
|
||||
im_vips_openout( const char *filename )
|
||||
{
|
||||
IMAGE *image;
|
||||
|
||||
if( !(image = im_init( filename )) )
|
||||
return( NULL );
|
||||
image->dtype = IM_OPENOUT;
|
||||
|
||||
return( image );
|
||||
}
|
||||
|
@ -61,7 +61,6 @@ extern im_package im__boolean;
|
||||
extern im_package im__colour;
|
||||
extern im_package im__conversion;
|
||||
extern im_package im__convolution;
|
||||
extern im_package im__format;
|
||||
extern im_package im__freq_filt;
|
||||
extern im_package im__histograms_lut;
|
||||
extern im_package im__inplace;
|
||||
@ -72,8 +71,6 @@ extern im_package im__other;
|
||||
extern im_package im__relational;
|
||||
extern im_package im__video;
|
||||
|
||||
extern im_format_package im__format_format;
|
||||
|
||||
/* im_guess_prefix() args.
|
||||
*/
|
||||
static im_arg_desc guess_prefix_args[] = {
|
||||
@ -408,7 +405,6 @@ static im_package *built_in[] = {
|
||||
&im__colour,
|
||||
&im__conversion,
|
||||
&im__convolution,
|
||||
&im__format,
|
||||
&im__freq_filt,
|
||||
&im__histograms_lut,
|
||||
&im__inplace,
|
||||
@ -421,56 +417,12 @@ static im_package *built_in[] = {
|
||||
&im__video
|
||||
};
|
||||
|
||||
/* List of loaded formats.
|
||||
*/
|
||||
static GSList *format_list = NULL;
|
||||
|
||||
static gint
|
||||
format_compare( im_format *a, im_format *b )
|
||||
{
|
||||
return( a->priority - b->priority );
|
||||
}
|
||||
|
||||
/* Sort the format list after a change.
|
||||
*/
|
||||
static void
|
||||
format_sort( void )
|
||||
{
|
||||
format_list = g_slist_sort( format_list,
|
||||
(GCompareFunc) format_compare );
|
||||
}
|
||||
|
||||
/* Remove a package of formats.
|
||||
*/
|
||||
static void
|
||||
format_remove( im_format_package *format )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < format->nfuncs; i++ )
|
||||
format_list = g_slist_remove( format_list, format->table[i] );
|
||||
format_sort();
|
||||
}
|
||||
|
||||
/* Add a package of formats.
|
||||
*/
|
||||
static void
|
||||
format_add( im_format_package *format )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < format->nfuncs; i++ )
|
||||
format_list = g_slist_prepend( format_list, format->table[i] );
|
||||
format_sort();
|
||||
}
|
||||
|
||||
/* How we represent a loaded plugin.
|
||||
*/
|
||||
typedef struct _Plugin {
|
||||
GModule *module; /* As loaded by g_module_open() */
|
||||
char *name; /* Name we loaded */
|
||||
im_package *pack; /* Package table */
|
||||
im_format_package *format; /* Package format table */
|
||||
} Plugin;
|
||||
|
||||
/* List of loaded plugins.
|
||||
@ -484,8 +436,6 @@ plugin_free( Plugin *plug )
|
||||
{
|
||||
char *name = plug->name ? plug->name : "<unknown>";
|
||||
|
||||
if( plug->format )
|
||||
format_remove( plug->format );
|
||||
if( plug->module ) {
|
||||
if( !g_module_close( plug->module ) ) {
|
||||
im_error( "plugin",
|
||||
@ -525,7 +475,6 @@ im_load_plugin( const char *name )
|
||||
plug->module = NULL;
|
||||
plug->name = NULL;
|
||||
plug->pack = NULL;
|
||||
plug->format = NULL;
|
||||
plugin_list = g_slist_prepend( plugin_list, plug );
|
||||
|
||||
/* Attach name.
|
||||
@ -560,44 +509,22 @@ im_load_plugin( const char *name )
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* The format table is optional.
|
||||
*/
|
||||
if( !g_module_symbol( plug->module,
|
||||
"format_table", (gpointer *) ((void *) &plug->format) ) )
|
||||
plug->format = NULL;
|
||||
|
||||
/* Sanity check.
|
||||
*/
|
||||
if( !plug->pack->name || plug->pack->nfuncs < 0 ||
|
||||
plug->pack->nfuncs > 10000 ) {
|
||||
im_error( "plugin",
|
||||
_( "corrupted package table in plugin \"%s\"" ), name );
|
||||
_( "corrupted package table in plugin \"%s\"" ),
|
||||
name );
|
||||
plugin_free( plug );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
if( plug->format ) {
|
||||
if( !plug->format->name || plug->format->nfuncs < 0 ||
|
||||
plug->format->nfuncs > 10000 ) {
|
||||
|
||||
im_error( "plugin",
|
||||
_( "corrupted format table in plugin \"%s\"" ),
|
||||
name );
|
||||
plugin_free( plug );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "added package \"%s\" ...\n", plug->pack->name );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Add any formats to our format list and sort again.
|
||||
*/
|
||||
if( plug->format )
|
||||
format_add( plug->format );
|
||||
|
||||
return( plug->pack );
|
||||
}
|
||||
|
||||
@ -771,89 +698,6 @@ im_package_of_function( const char *name )
|
||||
return( pack );
|
||||
}
|
||||
|
||||
/* Map a function over all formats.
|
||||
*/
|
||||
void *
|
||||
im_map_formats( VSListMap2Fn fn, void *a, void *b )
|
||||
{
|
||||
return( im_slist_map2( format_list, fn, a, b ) );
|
||||
}
|
||||
|
||||
/* Can this format open this file?
|
||||
*/
|
||||
static void *
|
||||
format_for_file_sub( im_format *format,
|
||||
const char *filename, const char *name )
|
||||
{
|
||||
if( format->is_a ) {
|
||||
if( format->is_a( name ) )
|
||||
return( format );
|
||||
}
|
||||
else if( im_filename_suffix_match( name, format->suffs ) )
|
||||
return( format );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
im_format *
|
||||
im_format_for_file( const char *filename )
|
||||
{
|
||||
char name[FILENAME_MAX];
|
||||
char options[FILENAME_MAX];
|
||||
im_format *format;
|
||||
|
||||
/* Break any options off the name ... eg. "fred.tif:jpeg,tile"
|
||||
* etc.
|
||||
*/
|
||||
im_filename_split( filename, name, options );
|
||||
|
||||
if( !im_existsf( "%s", name ) ) {
|
||||
im_error( "im_format_for_file",
|
||||
_( "\"%s\" is not readable" ), name );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
format = (im_format *) im_map_formats(
|
||||
(VSListMap2Fn) format_for_file_sub,
|
||||
(void *) filename, (void *) name );
|
||||
|
||||
if( !format ) {
|
||||
im_error( "im_format_for_file",
|
||||
_( "\"%s\" is not in a supported format" ), name );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( format );
|
||||
}
|
||||
|
||||
/* Can we write this filename with this format?
|
||||
*/
|
||||
static void *
|
||||
format_for_name_sub( im_format *format,
|
||||
const char *filename, const char *name )
|
||||
{
|
||||
if( im_filename_suffix_match( name, format->suffs ) )
|
||||
return( format );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
im_format *
|
||||
im_format_for_name( const char *filename )
|
||||
{
|
||||
char name[FILENAME_MAX];
|
||||
char options[FILENAME_MAX];
|
||||
|
||||
/* Break any options off the name ... eg. "fred.tif:jpeg,tile"
|
||||
* etc.
|
||||
*/
|
||||
im_filename_split( filename, name, options );
|
||||
|
||||
return( (im_format *) im_map_formats(
|
||||
(VSListMap2Fn) format_for_name_sub,
|
||||
(void *) filename, (void *) name ) );
|
||||
}
|
||||
|
||||
/* Free any store we allocated for the argument list.
|
||||
*/
|
||||
int
|
||||
|
@ -24,8 +24,6 @@
|
||||
* - added exr
|
||||
* 3/8/07
|
||||
* - cleanups
|
||||
* 22/5/08
|
||||
* - image format stuff broken out
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -76,6 +74,14 @@
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef HAVE_TIFF
|
||||
#include <tiffio.h>
|
||||
#endif /*HAVE_TIFF*/
|
||||
|
||||
#ifdef HAVE_PNG
|
||||
#include <png.h>
|
||||
#endif /*HAVE_PNG*/
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
@ -235,6 +241,195 @@ im_iscomplex( IMAGE *im )
|
||||
}
|
||||
}
|
||||
|
||||
/* Read a few bytes from the start of a file. For sniffing file types.
|
||||
*/
|
||||
static int
|
||||
get_bytes( const char *filename, unsigned char buf[], int len )
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* File may not even exist (for tmp images for example!)
|
||||
* so no hasty messages. And the file might be truncated, so no error
|
||||
* on read either.
|
||||
*/
|
||||
#ifdef BINARY_OPEN
|
||||
if( (fd = open( filename, O_RDONLY | O_BINARY )) == -1 )
|
||||
#else /*BINARY_OPEN*/
|
||||
if( (fd = open( filename, O_RDONLY )) == -1 )
|
||||
#endif /*BINARY_OPEN*/
|
||||
return( 0 );
|
||||
if( read( fd, buf, len ) != len ) {
|
||||
close( fd );
|
||||
return( 0 );
|
||||
}
|
||||
close( fd );
|
||||
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_istiff( const char *filename )
|
||||
{
|
||||
unsigned char buf[2];
|
||||
|
||||
if( get_bytes( filename, buf, 2 ) )
|
||||
if( (buf[0] == 'M' && buf[1] == 'M') ||
|
||||
(buf[0] == 'I' && buf[1] == 'I') )
|
||||
return( 1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#ifdef HAVE_PNG
|
||||
int
|
||||
im_ispng( const char *filename )
|
||||
{
|
||||
unsigned char buf[8];
|
||||
|
||||
return( get_bytes( filename, buf, 8 ) &&
|
||||
!png_sig_cmp( buf, 0, 8 ) );
|
||||
}
|
||||
#else /*HAVE_PNG*/
|
||||
int
|
||||
im_ispng( const char *filename )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
#endif /*HAVE_PNG*/
|
||||
|
||||
#ifdef HAVE_MAGICK
|
||||
int
|
||||
im_ismagick( const char *filename )
|
||||
{
|
||||
IMAGE *im;
|
||||
int result;
|
||||
|
||||
if( !(im = im_open( "dummy", "p" )) )
|
||||
return( -1 );
|
||||
result = im_magick2vips_header( filename, im );
|
||||
im_clear_error_string();
|
||||
im_close( im );
|
||||
|
||||
return( result == 0 );
|
||||
}
|
||||
#else /*HAVE_MAGICK*/
|
||||
int
|
||||
im_ismagick( const char *filename )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
#endif /*HAVE_MAGICK*/
|
||||
|
||||
int
|
||||
im_isppm( const char *filename )
|
||||
{
|
||||
unsigned char buf[2];
|
||||
|
||||
if( get_bytes( filename, buf, 2 ) )
|
||||
if( buf[0] == 'P' && (buf[1] >= '1' || buf[1] <= '6') )
|
||||
return( 1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#ifdef HAVE_TIFF
|
||||
|
||||
/* Handle TIFF errors here.
|
||||
*/
|
||||
static void
|
||||
vhandle( char *module, char *fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
im_errormsg( "TIFF error in \"%s\": ", module );
|
||||
|
||||
va_start( ap, fmt );
|
||||
im_verrormsg( fmt, ap );
|
||||
va_end( ap );
|
||||
}
|
||||
|
||||
int
|
||||
im_istifftiled( const char *filename )
|
||||
{
|
||||
TIFF *tif;
|
||||
int tiled;
|
||||
|
||||
/* Override the default TIFF error handler.
|
||||
*/
|
||||
TIFFSetErrorHandler( (TIFFErrorHandler) vhandle );
|
||||
|
||||
#ifdef BINARY_OPEN
|
||||
if( !(tif = TIFFOpen( filename, "rb" )) ) {
|
||||
#else /*BINARY_OPEN*/
|
||||
if( !(tif = TIFFOpen( filename, "r" )) ) {
|
||||
#endif /*BINARY_OPEN*/
|
||||
/* Not a TIFF file ... return False.
|
||||
*/
|
||||
im_clear_error_string();
|
||||
return( 0 );
|
||||
}
|
||||
tiled = TIFFIsTiled( tif );
|
||||
TIFFClose( tif );
|
||||
|
||||
return( tiled );
|
||||
}
|
||||
|
||||
#else /*HAVE_TIFF*/
|
||||
|
||||
int
|
||||
im_istifftiled( const char *filename )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif /*HAVE_TIFF*/
|
||||
|
||||
int
|
||||
im_isjpeg( const char *filename )
|
||||
{
|
||||
unsigned char buf[2];
|
||||
|
||||
if( get_bytes( filename, buf, 2 ) )
|
||||
if( (int) buf[0] == 0xff && (int) buf[1] == 0xd8 )
|
||||
return( 1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_isvips( const char *filename )
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
if( get_bytes( filename, buf, 4 ) ) {
|
||||
if( buf[0] == 0x08 && buf[1] == 0xf2 &&
|
||||
buf[2] == 0xa6 && buf[3] == 0xb6 )
|
||||
/* SPARC-order VIPS image.
|
||||
*/
|
||||
return( 1 );
|
||||
else if( buf[3] == 0x08 && buf[2] == 0xf2 &&
|
||||
buf[1] == 0xa6 && buf[0] == 0xb6 )
|
||||
/* INTEL-order VIPS image.
|
||||
*/
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_isexr( const char *filename )
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
if( get_bytes( filename, buf, 4 ) )
|
||||
if( buf[0] == 0x76 && buf[1] == 0x2f &&
|
||||
buf[2] == 0x31 && buf[3] == 0x01 )
|
||||
return( 1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Test for file exists.
|
||||
*/
|
||||
int
|
||||
@ -319,7 +514,7 @@ im_ispoweroftwo( int p )
|
||||
/* Count set bits. Could use a LUT, I guess.
|
||||
*/
|
||||
for( i = 0, n = 0; p; i++, p >>= 1 )
|
||||
if( p & 1 )
|
||||
if( (p & 1) == 1 )
|
||||
n++;
|
||||
|
||||
/* Should be just one set bit.
|
||||
|
Loading…
Reference in New Issue
Block a user