move format to a separate branch, revert trunk

This commit is contained in:
John Cupitt 2008-05-25 13:50:29 +00:00
parent e910c95e8a
commit 51b5259a6a
26 changed files with 9587 additions and 1036 deletions

10
TODO
View File

@ -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:

View File

@ -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

View File

@ -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 );

View File

@ -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 \

View File

@ -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

View File

@ -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[] = {
&copy_swap_desc,
&copy_set_desc,
&copy_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
View 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

View 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 );
}

View 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 );
}

View 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*/

View 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*/

View 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*/

View 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*/

View 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 );
}

View 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 );
}

File diff suppressed because it is too large Load Diff

View 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 );
}

View 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 );
}

View 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*/

View 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*/

View 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 );
}

File diff suppressed because it is too large Load Diff

View File

@ -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':

View File

@ -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 );
}

View File

@ -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

View File

@ -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.