revised jpeg binding works
This commit is contained in:
parent
ee17946bf9
commit
f7c34a369e
6
TODO
6
TODO
@ -1,8 +1,4 @@
|
||||
- fix up jpeg2vips.c
|
||||
|
||||
fix up jpegload.c
|
||||
|
||||
added classes for read and write jpeg buffers
|
||||
- add classes to read and write jpeg buffers
|
||||
|
||||
make compat wrappers for old im_jpeg2vips() and im_vips2jpeg()
|
||||
|
||||
|
@ -913,9 +913,6 @@ static int
|
||||
vips_file_save_build( VipsObject *object )
|
||||
{
|
||||
VipsFileSave *save = VIPS_FILE_SAVE( object );
|
||||
/*
|
||||
VipsFile *file = VIPS_FILE( object );
|
||||
*/
|
||||
|
||||
if( vips_file_convert_saveable( save ) )
|
||||
return( -1 );
|
||||
|
@ -47,13 +47,15 @@ typedef struct {
|
||||
FILE *fp; /* fclose() if non-NULL */
|
||||
} ErrorManager;
|
||||
|
||||
extern const char *vips__jpeg_suffs[];
|
||||
|
||||
void vips__new_output_message( j_common_ptr cinfo );
|
||||
void vips__new_error_exit( j_common_ptr cinfo );
|
||||
|
||||
int vips__jpeg_write_file( VipsImage *in,
|
||||
const char *filename, int Q, const char *profile );
|
||||
int vips__jpeg_write_buffer( VipsImage *in,
|
||||
void **obuf, int *olen, int Q, const char *profile );
|
||||
void **obuf, size_t *olen, int Q, const char *profile );
|
||||
|
||||
int vips__isjpeg( const char *filename );
|
||||
int vips__jpeg_read_file( const char *name, VipsImage *out,
|
||||
|
@ -728,12 +728,9 @@ read_jpeg_image( struct jpeg_decompress_struct *cinfo, IMAGE *out,
|
||||
/* Read a JPEG file into a VIPS image.
|
||||
*/
|
||||
int
|
||||
vips__jpeg2vips( const char *name, VipsImage *out, gboolean header_only,
|
||||
vips__jpeg_read_file( const char *name, VipsImage *out, gboolean header_only,
|
||||
int shrink, gboolean fail )
|
||||
{
|
||||
char filename[FILENAME_MAX];
|
||||
char mode[FILENAME_MAX];
|
||||
char *p, *q;
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
ErrorManager eman;
|
||||
FILE *fp;
|
||||
@ -743,8 +740,8 @@ vips__jpeg2vips( const char *name, VipsImage *out, gboolean header_only,
|
||||
/* Make jpeg dcompression object.
|
||||
*/
|
||||
cinfo.err = jpeg_std_error( &eman.pub );
|
||||
eman.pub.error_exit = new_error_exit;
|
||||
eman.pub.output_message = new_output_message;
|
||||
eman.pub.error_exit = vips__new_error_exit;
|
||||
eman.pub.output_message = vips__new_output_message;
|
||||
eman.fp = NULL;
|
||||
if( setjmp( eman.jmp ) ) {
|
||||
/* Here for longjmp() from new_error_exit().
|
||||
@ -757,7 +754,7 @@ vips__jpeg2vips( const char *name, VipsImage *out, gboolean header_only,
|
||||
|
||||
/* Make input.
|
||||
*/
|
||||
if( !(fp = im__file_open_read( filename, NULL, FALSE )) )
|
||||
if( !(fp = im__file_open_read( name, NULL, FALSE )) )
|
||||
return( -1 );
|
||||
eman.fp = fp;
|
||||
jpeg_stdio_src( &cinfo, fp );
|
||||
@ -967,7 +964,7 @@ buf_source (j_decompress_ptr cinfo, void *buf, size_t len)
|
||||
}
|
||||
|
||||
int
|
||||
vips__bufjpeg2vips( void *buf, size_t len, VipsImage *out,
|
||||
vips__jpeg_read_buffer( void *buf, size_t len, VipsImage *out,
|
||||
gboolean header_only,
|
||||
int shrink, int fail )
|
||||
{
|
||||
@ -979,8 +976,8 @@ vips__bufjpeg2vips( void *buf, size_t len, VipsImage *out,
|
||||
/* Make jpeg dcompression object.
|
||||
*/
|
||||
cinfo.err = jpeg_std_error( &eman.pub );
|
||||
eman.pub.error_exit = new_error_exit;
|
||||
eman.pub.output_message = new_output_message;
|
||||
eman.pub.error_exit = vips__new_error_exit;
|
||||
eman.pub.output_message = vips__new_output_message;
|
||||
eman.fp = NULL;
|
||||
if( setjmp( eman.jmp ) ) {
|
||||
/* Here for longjmp() from new_error_exit().
|
||||
|
@ -1,46 +1,7 @@
|
||||
/* load jpeg from a file
|
||||
/* save to 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
|
||||
* 16/6/09
|
||||
* - added "fail" option ... fail on any warnings
|
||||
* 12/10/09
|
||||
* - also set scale_num on shrink (thanks Guido)
|
||||
* 4/2/10
|
||||
* - gtkdoc
|
||||
* 4/12/10
|
||||
* - attach the jpeg thumbnail and multiscan fields (thanks Mike)
|
||||
* 21/2/10
|
||||
* - only accept the first APP1 block which starts "Exif..." as exif
|
||||
* data, some jpegs seem to have several APP1s, argh
|
||||
* 20/4/2011
|
||||
* - added im_bufjpeg2vips()
|
||||
* 12/10/2011
|
||||
* - read XMP data
|
||||
* 3/11/11
|
||||
* - attach exif tags as coded values
|
||||
* 24/11/11
|
||||
* - redo as a class
|
||||
* - wrap a class around the jpeg reader
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -125,10 +86,6 @@ typedef struct _VipsFileLoadJpeg {
|
||||
*/
|
||||
gboolean fail;
|
||||
|
||||
/* For some jpeg CMYK formats we have to invert pels on read.
|
||||
*/
|
||||
gboolean invert_pels;
|
||||
|
||||
} VipsFileLoadJpeg;
|
||||
|
||||
typedef VipsFileLoadClass VipsFileLoadJpegClass;
|
||||
@ -156,582 +113,6 @@ vips_file_load_jpeg_build( VipsObject *object )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vips_file_load_jpeg_is_a( const char *filename )
|
||||
{
|
||||
unsigned char buf[2];
|
||||
|
||||
if( vips__get_bytes( filename, buf, 2 ) )
|
||||
if( (int) buf[0] == 0xff && (int) buf[1] == 0xd8 )
|
||||
return( TRUE );
|
||||
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to
|
||||
* do 255-pel.
|
||||
*/
|
||||
static int
|
||||
vips_file_load_jpeg_read_header( VipsFileLoadJpeg *jpeg,
|
||||
struct jpeg_decompress_struct *cinfo, VipsImage *out )
|
||||
{
|
||||
int type;
|
||||
|
||||
/* Read JPEG header. libjpeg will set out_color_space sanely for us
|
||||
* for YUV YCCK etc.
|
||||
*/
|
||||
jpeg_read_header( cinfo, TRUE );
|
||||
cinfo->scale_denom = jpeg->shrink;
|
||||
cinfo->scale_num = 1;
|
||||
jpeg_calc_output_dimensions( cinfo );
|
||||
|
||||
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 )
|
||||
jpeg->invert_pels = TRUE;
|
||||
break;
|
||||
|
||||
case JCS_RGB:
|
||||
default:
|
||||
type = IM_TYPE_sRGB;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set VIPS header.
|
||||
*/
|
||||
vips_image_init_fields( out,
|
||||
cinfo->output_width, cinfo->output_height,
|
||||
cinfo->output_components,
|
||||
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, type,
|
||||
1.0, 1.0 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#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 int
|
||||
vips_exif_get_int( ExifData *ed,
|
||||
ExifEntry *entry, unsigned long component, int *out )
|
||||
{
|
||||
ExifByteOrder bo = exif_data_get_byte_order( ed );
|
||||
size_t sizeof_component = entry->size / entry->components;
|
||||
size_t offset = component * sizeof_component;
|
||||
|
||||
if( entry->format == EXIF_FORMAT_SHORT )
|
||||
*out = exif_get_short( entry->data + offset, bo );
|
||||
else if( entry->format == EXIF_FORMAT_SSHORT )
|
||||
*out = exif_get_sshort( entry->data + offset, bo );
|
||||
else if( entry->format == EXIF_FORMAT_LONG )
|
||||
/* This won't work for huge values, but who cares.
|
||||
*/
|
||||
*out = (int) exif_get_long( entry->data + offset, bo );
|
||||
else if( entry->format == EXIF_FORMAT_SLONG )
|
||||
*out = exif_get_slong( entry->data + offset, bo );
|
||||
else
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_exif_get_double( ExifData *ed,
|
||||
ExifEntry *entry, unsigned long component, double *out )
|
||||
{
|
||||
ExifByteOrder bo = exif_data_get_byte_order( ed );
|
||||
size_t sizeof_component = entry->size / entry->components;
|
||||
size_t offset = component * sizeof_component;
|
||||
|
||||
if( entry->format == EXIF_FORMAT_RATIONAL ) {
|
||||
ExifRational value;
|
||||
|
||||
value = exif_get_rational( entry->data + offset, bo );
|
||||
*out = (double) value.numerator / value.denominator;
|
||||
}
|
||||
else if( entry->format == EXIF_FORMAT_SRATIONAL ) {
|
||||
ExifSRational value;
|
||||
|
||||
value = exif_get_srational( entry->data + offset, bo );
|
||||
*out = (double) value.numerator / value.denominator;
|
||||
}
|
||||
else
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Save an exif value to a string in a way that we can restore. We only bother
|
||||
* for the simple formats (that a client might try to change) though.
|
||||
*
|
||||
* Keep in sync with vips_exif_from_s() in vips2jpeg.
|
||||
*/
|
||||
static void
|
||||
vips_exif_to_s( ExifData *ed, ExifEntry *entry, VipsBuf *buf )
|
||||
{
|
||||
unsigned long i;
|
||||
int iv;
|
||||
double dv;
|
||||
char txt[256];
|
||||
|
||||
if( entry->format == EXIF_FORMAT_ASCII )
|
||||
vips_buf_appendf( buf, "%s ", entry->data );
|
||||
|
||||
else if( entry->components < 10 &&
|
||||
!vips_exif_get_int( ed, entry, 0, &iv ) ) {
|
||||
for( i = 0; i < entry->components; i++ ) {
|
||||
vips_exif_get_int( ed, entry, i, &iv );
|
||||
vips_buf_appendf( buf, "%d ", iv );
|
||||
}
|
||||
}
|
||||
else if( entry->components < 10 &&
|
||||
!vips_exif_get_double( ed, entry, 0, &dv ) ) {
|
||||
for( i = 0; i < entry->components; i++ ) {
|
||||
vips_exif_get_double( ed, entry, i, &dv );
|
||||
/* Need to be locale independent.
|
||||
*/
|
||||
g_ascii_dtostr( txt, 256, dv );
|
||||
vips_buf_appendf( buf, "%s ", txt );
|
||||
}
|
||||
}
|
||||
else
|
||||
vips_buf_appendf( buf, "%s ",
|
||||
exif_entry_get_value( entry, txt, 256 ) );
|
||||
|
||||
vips_buf_appendf( buf, "(%s, %s, %lu components, %d bytes)",
|
||||
exif_entry_get_value( entry, txt, 256 ),
|
||||
exif_format_get_name( entry->format ),
|
||||
entry->components,
|
||||
entry->size );
|
||||
}
|
||||
|
||||
typedef struct _VipsExif {
|
||||
VipsImage *image;
|
||||
ExifData *ed;
|
||||
} VipsExif;
|
||||
|
||||
static void
|
||||
attach_exif_entry( ExifEntry *entry, VipsExif *ve )
|
||||
{
|
||||
char name_txt[256];
|
||||
VipsBuf name = VIPS_BUF_STATIC( name_txt );
|
||||
char value_txt[256];
|
||||
VipsBuf value = VIPS_BUF_STATIC( value_txt );
|
||||
|
||||
vips_buf_appendf( &name, "exif-%s", exif_tag_get_title( entry->tag ) );
|
||||
vips_exif_to_s( ve->ed, entry, &value );
|
||||
|
||||
/* Can't do anything sensible with the error return.
|
||||
*/
|
||||
(void) im_meta_set_string( ve->image,
|
||||
vips_buf_all( &name ), vips_buf_all( &value ) );
|
||||
}
|
||||
|
||||
static void
|
||||
attach_exif_content( ExifContent *content, VipsExif *ve )
|
||||
{
|
||||
exif_content_foreach_entry( content,
|
||||
(ExifContentForeachEntryFunc) attach_exif_entry, ve );
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if( !(entry = find_entry( ed, tag )) ||
|
||||
entry->format != EXIF_FORMAT_RATIONAL ||
|
||||
entry->components != 1 )
|
||||
return( -1 );
|
||||
|
||||
return( vips_exif_get_double( ed, entry, 0, out ) );
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
return( vips_exif_get_int( ed, entry, 0, out ) );
|
||||
}
|
||||
|
||||
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",
|
||||
"%s", _( "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", "%s", _( "bad resolution unit" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
im->Xres = xres;
|
||||
im->Yres = yres;
|
||||
}
|
||||
|
||||
static int
|
||||
attach_thumbnail( IMAGE *im, ExifData *ed )
|
||||
{
|
||||
if( ed->size > 0 ) {
|
||||
char *thumb_copy;
|
||||
|
||||
thumb_copy = im_malloc( NULL, ed->size );
|
||||
memcpy( thumb_copy, ed->data, ed->size );
|
||||
|
||||
if( im_meta_set_blob( im, "jpeg-thumbnail-data",
|
||||
(im_callback_fn) im_free, thumb_copy, ed->size ) ) {
|
||||
im_free( thumb_copy );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
static int
|
||||
read_exif( IMAGE *im, void *data, int data_length )
|
||||
{
|
||||
char *data_copy;
|
||||
|
||||
/* Only use the first one.
|
||||
*/
|
||||
if( im_header_get_typeof( im, IM_META_EXIF_NAME ) ) {
|
||||
#ifdef DEBUG
|
||||
printf( "read_exif: second EXIF block, ignoring\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "read_exif: attaching %d bytes of exif\n", data_length );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* 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 ) {
|
||||
VipsExif ve;
|
||||
|
||||
#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?
|
||||
|
||||
*/
|
||||
ve.image = im;
|
||||
ve.ed = ed;
|
||||
exif_data_foreach_content( ed,
|
||||
(ExifDataForeachContentFunc) attach_exif_content, &ve );
|
||||
|
||||
/* Look for resolution fields and use them to set the VIPS
|
||||
* xres/yres fields.
|
||||
*/
|
||||
set_vips_resolution( im, ed );
|
||||
|
||||
attach_thumbnail( im, ed );
|
||||
}
|
||||
|
||||
exif_data_free( ed );
|
||||
}
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
read_xmp( IMAGE *im, void *data, int data_length )
|
||||
{
|
||||
char *data_copy;
|
||||
|
||||
/* XMP sections start "http". Only use the first one.
|
||||
*/
|
||||
if( im_header_get_typeof( im, VIPS_META_XMP_NAME ) ) {
|
||||
#ifdef DEBUG
|
||||
printf( "read_xmp: second XMP block, ignoring\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "read_xmp: attaching %d bytes of XMP\n", data_length );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* 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, VIPS_META_XMP_NAME,
|
||||
(im_callback_fn) im_free, data_copy, data_length ) ) {
|
||||
im_free( data_copy );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Number of app2 sections we can capture. Each one can be 64k, so 6400k should
|
||||
* be enough for anyone (haha).
|
||||
*/
|
||||
#define MAX_APP2_SECTIONS (100)
|
||||
|
||||
static int
|
||||
vips_file_load_jpeg_meta( VipsFileLoadJpeg *jpeg,
|
||||
struct jpeg_decompress_struct *cinfo, VipsImage *out )
|
||||
{
|
||||
/* Capture app2 sections here for assembly.
|
||||
*/
|
||||
void *app2_data[MAX_APP2_SECTIONS] = { 0 };
|
||||
int app2_data_length[MAX_APP2_SECTIONS] = { 0 };
|
||||
int data_length;
|
||||
jpeg_saved_marker_ptr p;
|
||||
int i;
|
||||
|
||||
/* Interlaced jpegs need lots of memory to read, so our caller needs
|
||||
* to know.
|
||||
*/
|
||||
vips_image_set_int( out, "jpeg-multiscan",
|
||||
jpeg_has_multiple_scans( cinfo ) );
|
||||
|
||||
/* Look for EXIF and ICC profile.
|
||||
*/
|
||||
for( p = cinfo->marker_list; p; p = p->next ) {
|
||||
#ifdef DEBUG
|
||||
{
|
||||
printf( "vips_file_load_jpeg_read_header: "
|
||||
"seen %d bytes of APP%d\n",
|
||||
p->data_length,
|
||||
p->marker - JPEG_APP0 );
|
||||
|
||||
for( i = 0; i < 10; i++ )
|
||||
printf( "\t%d) '%c' (%d)\n",
|
||||
i, p->data[i], p->data[i] );
|
||||
}
|
||||
#endif /*DEBUG*/
|
||||
|
||||
switch( p->marker ) {
|
||||
case JPEG_APP0 + 1:
|
||||
/* Possible EXIF or XMP data.
|
||||
*/
|
||||
if( p->data_length > 4 &&
|
||||
im_isprefix( "Exif", (char *) p->data ) &&
|
||||
read_exif( out, p->data, p->data_length ) )
|
||||
return( -1 );
|
||||
|
||||
if( p->data_length > 4 &&
|
||||
im_isprefix( "http", (char *) p->data ) &&
|
||||
read_xmp( out, p->data, p->data_length ) )
|
||||
return( -1 );
|
||||
|
||||
break;
|
||||
|
||||
case JPEG_APP0 + 2:
|
||||
/* ICC profile.
|
||||
*/
|
||||
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:
|
||||
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 x;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_file_load_jpeg_read_header: "
|
||||
"assembled %d byte ICC profile\n",
|
||||
data_length );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
data = g_malloc( data_length );
|
||||
x = 0;
|
||||
for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) {
|
||||
memcpy( data + x, app2_data[i], app2_data_length[i] );
|
||||
x += app2_data_length[i];
|
||||
}
|
||||
vips_image_set_blob( out, VIPS_META_ICC_NAME,
|
||||
(VipsCallbackFn) g_free, data, data_length );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read just the image header into ->out.
|
||||
*/
|
||||
static int
|
||||
@ -740,99 +121,9 @@ vips_file_load_jpeg_header( VipsFileLoad *load )
|
||||
VipsFile *file = VIPS_FILE( load );
|
||||
VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) load;
|
||||
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
ErrorManager eman;
|
||||
FILE *fp;
|
||||
int result;
|
||||
|
||||
/* Make jpeg dcompression object.
|
||||
*/
|
||||
cinfo.err = jpeg_std_error( &eman.pub );
|
||||
eman.pub.error_exit = vips__new_error_exit;
|
||||
eman.pub.output_message = vips__new_output_message;
|
||||
eman.fp = NULL;
|
||||
if( setjmp( eman.jmp ) ) {
|
||||
/* Here for longjmp() from vips__new_error_exit().
|
||||
*/
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
|
||||
if( vips__jpeg_read_file( file->filename, load->out,
|
||||
TRUE, jpeg->shrink, jpeg->fail ) )
|
||||
return( -1 );
|
||||
}
|
||||
jpeg_create_decompress( &cinfo );
|
||||
|
||||
/* Make input.
|
||||
*/
|
||||
if( !(fp = vips__file_open_read( file->filename, NULL, FALSE )) )
|
||||
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 = vips_file_load_jpeg_read_header( jpeg, &cinfo, load->out );
|
||||
|
||||
/* Get extra metadata too.
|
||||
*/
|
||||
if( !result )
|
||||
result = vips_file_load_jpeg_meta( jpeg, &cinfo, load->out );
|
||||
|
||||
/* Close and tidy.
|
||||
*/
|
||||
fclose( fp );
|
||||
eman.fp = NULL;
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/* Read a cinfo to a VIPS image.
|
||||
*/
|
||||
static int
|
||||
vips_file_load_jpeg_read_image( VipsFileLoadJpeg *jpeg,
|
||||
struct jpeg_decompress_struct *cinfo, VipsImage *out )
|
||||
{
|
||||
int x, y, sz;
|
||||
JSAMPROW row_pointer[1];
|
||||
|
||||
/* Check VIPS.
|
||||
*/
|
||||
if( vips_image_wio_output( 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++ ) {
|
||||
/* We set an error handler that longjmps() out, so I don't
|
||||
* think this can fail.
|
||||
*/
|
||||
jpeg_read_scanlines( cinfo, &row_pointer[0], 1 );
|
||||
|
||||
if( jpeg->invert_pels ) {
|
||||
for( x = 0; x < sz; x++ )
|
||||
row_pointer[0][x] = 255 - row_pointer[0][x];
|
||||
}
|
||||
if( vips_image_write_line( out, y, row_pointer[0] ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Stop decompressor.
|
||||
*/
|
||||
jpeg_finish_decompress( cinfo );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -843,66 +134,13 @@ vips_file_load_jpeg_load( VipsFileLoad *load )
|
||||
VipsFile *file = VIPS_FILE( load );
|
||||
VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) load;
|
||||
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
ErrorManager eman;
|
||||
FILE *fp;
|
||||
int result;
|
||||
|
||||
/* Make jpeg dcompression object.
|
||||
*/
|
||||
cinfo.err = jpeg_std_error( &eman.pub );
|
||||
eman.pub.error_exit = vips__new_error_exit;
|
||||
eman.pub.output_message = vips__new_output_message;
|
||||
eman.fp = NULL;
|
||||
if( setjmp( eman.jmp ) ) {
|
||||
/* Here for longjmp() from vips__new_error_exit().
|
||||
*/
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
|
||||
if( vips__jpeg_read_file( file->filename, load->real,
|
||||
FALSE, jpeg->shrink, jpeg->fail ) )
|
||||
return( -1 );
|
||||
}
|
||||
jpeg_create_decompress( &cinfo );
|
||||
|
||||
/* Make input.
|
||||
*/
|
||||
if( !(fp = vips__file_open_read( file->filename, NULL, FALSE )) )
|
||||
return( -1 );
|
||||
eman.fp = fp;
|
||||
jpeg_stdio_src( &cinfo, fp );
|
||||
|
||||
/* Convert!
|
||||
*/
|
||||
result = vips_file_load_jpeg_read_header( jpeg, &cinfo, load->real );
|
||||
if( !result )
|
||||
result = vips_file_load_jpeg_read_image( jpeg,
|
||||
&cinfo, load->real );
|
||||
|
||||
/* Close and tidy.
|
||||
*/
|
||||
fclose( fp );
|
||||
eman.fp = NULL;
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
|
||||
if( eman.pub.num_warnings != 0 ) {
|
||||
if( jpeg->fail ) {
|
||||
vips_error( "VipsFileLoadJpeg",
|
||||
"%s", vips_error_buffer() );
|
||||
result = -1;
|
||||
}
|
||||
else {
|
||||
vips_warn( "VipsFileLoadJpeg",
|
||||
_( "read gave %ld warnings" ),
|
||||
eman.pub.num_warnings );
|
||||
vips_warn( "VipsFileLoadJpeg",
|
||||
"%s", vips_error_buffer() );
|
||||
}
|
||||
}
|
||||
|
||||
return( result );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static const char *jpeg_suffs[] = { ".jpg", ".jpeg", ".jpe", NULL };
|
||||
|
||||
static void
|
||||
vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class )
|
||||
{
|
||||
@ -918,9 +156,9 @@ vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class )
|
||||
object_class->description = _( "load jpeg from file" );
|
||||
object_class->build = vips_file_load_jpeg_build;
|
||||
|
||||
file_class->suffs = jpeg_suffs;
|
||||
file_class->suffs = vips__jpeg_suffs;
|
||||
|
||||
load_class->is_a = vips_file_load_jpeg_is_a;
|
||||
load_class->is_a = vips__isjpeg;
|
||||
load_class->header = vips_file_load_jpeg_header;
|
||||
load_class->load = vips_file_load_jpeg_load;
|
||||
|
||||
|
@ -103,7 +103,8 @@ vips_file_save_jpeg_build( VipsObject *object )
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips__jpeg_write_file( save->in, file->filename,
|
||||
// ->ready loses a ref suring the write??
|
||||
if( vips__jpeg_write_file( save->ready, file->filename,
|
||||
jpeg->Q, jpeg->profile ) )
|
||||
return( -1 );
|
||||
|
||||
@ -119,8 +120,6 @@ static int bandfmt_jpeg[10] = {
|
||||
UC, UC, UC, UC, UC, UC, UC, UC, UC, UC
|
||||
};
|
||||
|
||||
static const char *jpeg_suffs[] = { ".jpg", ".jpeg", ".jpe", NULL };
|
||||
|
||||
static void
|
||||
vips_file_save_jpeg_class_init( VipsFileSaveJpegClass *class )
|
||||
{
|
||||
@ -136,7 +135,7 @@ vips_file_save_jpeg_class_init( VipsFileSaveJpegClass *class )
|
||||
object_class->description = _( "save image to jpeg file" );
|
||||
object_class->build = vips_file_save_jpeg_build;
|
||||
|
||||
file_class->suffs = jpeg_suffs;
|
||||
file_class->suffs = vips__jpeg_suffs;
|
||||
|
||||
save_class->saveable = VIPS_SAVEABLE_RGB_CMYK;
|
||||
save_class->format_table = bandfmt_jpeg;
|
||||
|
@ -177,7 +177,6 @@ static void
|
||||
write_destroy( Write *write )
|
||||
{
|
||||
jpeg_destroy_compress( &write->cinfo );
|
||||
VIPS_UNREF( write->in );
|
||||
VIPS_FREEF( fclose, write->eman.fp );
|
||||
VIPS_FREE( write->row_pointer );
|
||||
VIPS_FREE( write->profile_bytes );
|
||||
@ -960,8 +959,8 @@ typedef struct {
|
||||
|
||||
/* Copy the compressed area here.
|
||||
*/
|
||||
char **obuf; /* Allocated buffer, and size */
|
||||
int *olen;
|
||||
void **obuf; /* Allocated buffer, and size */
|
||||
size_t *olen;
|
||||
} OutputBuffer;
|
||||
|
||||
/* Init dest method.
|
||||
@ -1012,7 +1011,7 @@ term_destination( j_compress_ptr cinfo )
|
||||
{
|
||||
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
|
||||
|
||||
int len;
|
||||
size_t len;
|
||||
void *obuf;
|
||||
|
||||
/* Record the number of bytes we wrote in the final buffer.
|
||||
@ -1041,7 +1040,7 @@ term_destination( j_compress_ptr cinfo )
|
||||
/* Set dest to one of our objects.
|
||||
*/
|
||||
static void
|
||||
buf_dest( j_compress_ptr cinfo, char **obuf, int *olen )
|
||||
buf_dest( j_compress_ptr cinfo, void **obuf, size_t *olen )
|
||||
{
|
||||
OutputBuffer *buf;
|
||||
|
||||
@ -1073,7 +1072,7 @@ buf_dest( j_compress_ptr cinfo, char **obuf, int *olen )
|
||||
|
||||
int
|
||||
vips__jpeg_write_buffer( VipsImage *in,
|
||||
char **obuf, int *olen, int Q, const char *profile )
|
||||
void **obuf, size_t *olen, int Q, const char *profile )
|
||||
{
|
||||
Write *write;
|
||||
|
||||
@ -1111,3 +1110,5 @@ vips__jpeg_write_buffer( VipsImage *in,
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
const char *vips__jpeg_suffs[] = { ".jpg", ".jpeg", ".jpe", NULL };
|
||||
|
@ -141,7 +141,7 @@ typedef struct _VipsFileLoadClass {
|
||||
*/
|
||||
gboolean (*is_a)( const char * );
|
||||
|
||||
/* Get the flags for this file.
|
||||
/* Get the flags for this file. NULL means 0, ie. no flags.
|
||||
*/
|
||||
int (*get_flags)( VipsFileLoad * );
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user