new jpeg loader works
This commit is contained in:
parent
2c641c6644
commit
ab0f72db62
13
TODO
13
TODO
@ -1,6 +1,15 @@
|
||||
- file.c has lazy open stuff in, needs work though
|
||||
- get image.c using the new system
|
||||
|
||||
how do we handle loaders that don't need to be lazy (eg. tiled tiff load)
|
||||
move format/* to deprecated
|
||||
|
||||
try jpeg save
|
||||
|
||||
try tiff load
|
||||
|
||||
|
||||
|
||||
|
||||
- return missing just after read_xmp() in master?
|
||||
|
||||
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
#include <vips/debug.h>
|
||||
|
||||
/**
|
||||
* SECTION: file
|
||||
@ -245,20 +246,24 @@ vips_file_print_class( VipsObjectClass *object_class, VipsBuf *buf )
|
||||
vips_buf_appends( buf, ") " );
|
||||
}
|
||||
|
||||
vips_buf_appendf( buf, "priority=%d ", class->priority );
|
||||
vips_buf_appendf( buf, "priority=%d", class->priority );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_file_class_init( VipsFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "file";
|
||||
object_class->description = _( "load and save image files" );
|
||||
object_class->print_class = vips_file_print_class;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 12,
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "File filename" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
@ -331,6 +336,16 @@ vips_file_map( const char *base, VipsSListMap2Fn fn, void *a, void *b )
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsFileLoad, vips_file_load, VIPS_TYPE_FILE );
|
||||
|
||||
static void
|
||||
vips_file_load_dispose( GObject *gobject )
|
||||
{
|
||||
VipsFileLoad *load = VIPS_FILE_LOAD( gobject );
|
||||
|
||||
VIPS_UNREF( load->real );
|
||||
|
||||
G_OBJECT_CLASS( vips_file_load_parent_class )->dispose( gobject );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_file_load_print_class( VipsObjectClass *object_class, VipsBuf *buf )
|
||||
{
|
||||
@ -338,24 +353,19 @@ vips_file_load_print_class( VipsObjectClass *object_class, VipsBuf *buf )
|
||||
|
||||
VIPS_OBJECT_CLASS( vips_file_load_parent_class )->
|
||||
print_class( object_class, buf );
|
||||
vips_buf_appends( buf, ", " );
|
||||
|
||||
if( class->is_a )
|
||||
vips_buf_appends( buf, "is_a " );
|
||||
|
||||
/*
|
||||
if( class->header )
|
||||
vips_buf_appends( buf, "header " );
|
||||
if( class->load )
|
||||
vips_buf_appends( buf, "load " );
|
||||
*/
|
||||
|
||||
vips_buf_appends( buf, ", is_a" );
|
||||
if( class->get_flags )
|
||||
vips_buf_appends( buf, "get_flags " );
|
||||
vips_buf_appends( buf, ", get_flags" );
|
||||
if( class->header )
|
||||
vips_buf_appends( buf, ", header" );
|
||||
if( class->load )
|
||||
vips_buf_appends( buf, ", load" );
|
||||
}
|
||||
|
||||
static size_t
|
||||
disc_threshold( void )
|
||||
vips_get_disc_threshold( void )
|
||||
{
|
||||
static gboolean done = FALSE;
|
||||
static size_t threshold;
|
||||
@ -375,42 +385,13 @@ disc_threshold( void )
|
||||
if( vips__disc_threshold )
|
||||
threshold = vips__parse_size( vips__disc_threshold );
|
||||
|
||||
VIPS_DEBUG_MSG( "disc_threshold: %zd bytes\n", threshold );
|
||||
VIPS_DEBUG_MSG( "vips_get_disc_threshold: "
|
||||
"%zd bytes\n", threshold );
|
||||
}
|
||||
|
||||
return( threshold );
|
||||
}
|
||||
|
||||
/* Make the real underlying image: either a direct disc file, or a temp file
|
||||
* somewhere.
|
||||
*/
|
||||
static int
|
||||
vips_file_load_new_real( VipsFileLoad *load )
|
||||
{
|
||||
/* We open via disc if:
|
||||
* - 'disc' is set
|
||||
* - disc_threshold() has not been set to zero
|
||||
* - the format does not support lazy read
|
||||
* - the uncompressed image will be larger than disc_threshold()
|
||||
*/
|
||||
load->real = NULL;
|
||||
if( lazy->disc &&
|
||||
disc_threshold() &&
|
||||
!(vips_format_get_flags( lazy->format, lazy->filename ) &
|
||||
VIPS_FORMAT_PARTIAL) &&
|
||||
VIPS_IMAGE_SIZEOF_IMAGE( lazy->image ) > disc_threshold() )
|
||||
if( !(real = vips_image_new_disc_temp( "%s.v" )) )
|
||||
return( NULL );
|
||||
|
||||
/* Otherwise, fall back to a "p".
|
||||
*/
|
||||
if( !real &&
|
||||
!(real = vips_image_new()) )
|
||||
return( NULL );
|
||||
|
||||
return( real );
|
||||
}
|
||||
|
||||
/* Our start function ... do the lazy open, if necessary, and return a region
|
||||
* on the new image.
|
||||
*/
|
||||
@ -421,8 +402,32 @@ vips_file_load_start_cb( VipsImage *out, void *a, void *dummy )
|
||||
VipsFileLoadClass *class = VIPS_FILE_LOAD_GET_CLASS( a );
|
||||
|
||||
if( !load->real ) {
|
||||
if( vips_file_load_new_real( load ) ||
|
||||
class->load( load ) ||
|
||||
const size_t disc_threshold = vips_get_disc_threshold();
|
||||
const size_t image_size = VIPS_IMAGE_SIZEOF_IMAGE( load->out );
|
||||
|
||||
/* We open via disc if:
|
||||
* - 'disc' is set
|
||||
* - disc-threshold has not been set to zero
|
||||
* - the format does not support lazy read
|
||||
* - the uncompressed image will be larger than
|
||||
* vips_get_disc_threshold()
|
||||
*/
|
||||
if( load->disc &&
|
||||
disc_threshold &&
|
||||
(load->flags & VIPS_FORMAT_PARTIAL) &&
|
||||
image_size > disc_threshold )
|
||||
if( !(load->real = vips_image_new_disc_temp( "%s.v" )) )
|
||||
return( NULL );
|
||||
|
||||
/* Otherwise, fall back to a "p".
|
||||
*/
|
||||
if( !load->real &&
|
||||
!(load->real = vips_image_new()) )
|
||||
return( NULL );
|
||||
|
||||
/* Read the image in.
|
||||
*/
|
||||
if( class->load( load ) ||
|
||||
vips_image_pio_input( load->real ) ) {
|
||||
VIPS_UNREF( load->real );
|
||||
return( NULL );
|
||||
@ -463,10 +468,9 @@ vips_file_load_build( VipsObject *object )
|
||||
|
||||
g_object_set( object, "out", vips_image_new(), NULL );
|
||||
|
||||
if( class->get_flags )
|
||||
g_object_set( load,
|
||||
"flags", class->get_flags( load ),
|
||||
NULL );
|
||||
if( class->get_flags &&
|
||||
class->get_flags( load ) )
|
||||
return( -1 );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_file_load_parent_class )->
|
||||
build( object ) )
|
||||
@ -481,14 +485,14 @@ vips_file_load_build( VipsObject *object )
|
||||
return( -1 );
|
||||
vips_demand_hint( load->out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
|
||||
|
||||
/* Then 'start' creates the real image and 'gen' paints 'out' with
|
||||
* pixels from the real image on demand.
|
||||
/* Then 'start' creates the real image and 'gen' fetches pixels for
|
||||
* 'out' from real on demand.
|
||||
*/
|
||||
if( vips_image_generate( load->out,
|
||||
vips_file_load_start_cb,
|
||||
vips_file_load_generate_cb,
|
||||
vips_stop_one,
|
||||
lazy, NULL ) )
|
||||
load, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
@ -497,13 +501,24 @@ vips_file_load_build( VipsObject *object )
|
||||
static void
|
||||
vips_file_load_class_init( VipsFileLoadClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->dispose = vips_file_load_dispose;
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "fileload";
|
||||
object_class->description = _( "file loaders" );
|
||||
object_class->print_class = vips_file_load_print_class;
|
||||
object_class->build = vips_file_load_build;
|
||||
|
||||
VIPS_ARG_IMAGE( class, "out", 2,
|
||||
_( "Output" ),
|
||||
_( "Output image" ),
|
||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||
G_STRUCT_OFFSET( VipsFileLoad, out ) );
|
||||
|
||||
VIPS_ARG_ENUM( class, "flags", 6,
|
||||
_( "Flags" ),
|
||||
_( "Flags for this file" ),
|
||||
@ -511,24 +526,19 @@ vips_file_load_class_init( VipsFileLoadClass *class )
|
||||
G_STRUCT_OFFSET( VipsFileLoad, flags ),
|
||||
VIPS_TYPE_FILE_FLAGS, VIPS_FILE_NONE );
|
||||
|
||||
VIPS_ARG_IMAGE( class, "out", 1,
|
||||
_( "Output" ),
|
||||
_( "Output image" ),
|
||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||
G_STRUCT_OFFSET( VipsFileLoad, out ) );
|
||||
|
||||
VIPS_ARG_BOOL( class, "memory", 6,
|
||||
_( "Memory" ),
|
||||
_( "Open to memory" ),
|
||||
VIPS_ARG_BOOL( class, "disc", 7,
|
||||
_( "Disc" ),
|
||||
_( "Open to disc" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsFileLoad, memory ),
|
||||
FALSE );
|
||||
G_STRUCT_OFFSET( VipsFileLoad, disc ),
|
||||
TRUE );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_file_load_init( VipsFileLoad *object )
|
||||
vips_file_load_init( VipsFileLoad *load )
|
||||
{
|
||||
load->disc = TRUE;
|
||||
}
|
||||
|
||||
/* Can this file open this file?
|
||||
@ -619,14 +629,18 @@ vips_file_save_build( VipsObject *object )
|
||||
static void
|
||||
vips_file_save_class_init( VipsFileSaveClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "filesave";
|
||||
object_class->description = _( "file savers" );
|
||||
object_class->print_class = vips_file_save_print_class;
|
||||
object_class->build = vips_file_save_build;
|
||||
|
||||
VIPS_ARG_IMAGE( class, "in", 1,
|
||||
VIPS_ARG_IMAGE( class, "in", 0,
|
||||
_( "Input" ),
|
||||
_( "Image to save" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
@ -740,3 +754,16 @@ vips_file_write( VipsImage *in, const char *filename, ... )
|
||||
return( result );
|
||||
}
|
||||
|
||||
/* Called from iofuncs to init all operations in this dir. Use a plugin system
|
||||
* instead?
|
||||
*/
|
||||
void
|
||||
vips_file_operation_init( void )
|
||||
{
|
||||
extern GType vips_file_load_jpeg_get_type( void );
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
vips_file_load_jpeg_get_type();
|
||||
#endif /*HAVE_JPEG*/
|
||||
}
|
||||
|
||||
|
@ -123,12 +123,49 @@ typedef struct _VipsFileLoadJpeg {
|
||||
*/
|
||||
gboolean fail;
|
||||
|
||||
/* For some jpeg CMYK formats we have to invert pels on read.
|
||||
*/
|
||||
gboolean invert_pels;
|
||||
|
||||
} VipsFileLoadJpeg;
|
||||
|
||||
typedef VipsFileLoadJpegClass VipsFileLoadJpeg;
|
||||
typedef VipsFileLoadClass VipsFileLoadJpegClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsFileLoadJpeg, vips_file_load_jpeg, VIPS_TYPE_FILE_LOAD );
|
||||
|
||||
static int
|
||||
vips_file_load_jpeg_build( VipsObject *object )
|
||||
{
|
||||
VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) object;
|
||||
|
||||
if( jpeg->shrink != 1 &&
|
||||
jpeg->shrink != 2 &&
|
||||
jpeg->shrink != 4 &&
|
||||
jpeg->shrink != 8 ) {
|
||||
vips_error( "VipsFormatLoadJpeg",
|
||||
_( "bad shrink factor %d" ), jpeg->shrink );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_file_load_jpeg_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
/* Define a new error handler for when we bomb out.
|
||||
*/
|
||||
typedef struct {
|
||||
@ -184,6 +221,54 @@ new_error_exit( j_common_ptr cinfo )
|
||||
longjmp( eman->jmp, 1 );
|
||||
}
|
||||
|
||||
/* 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
|
||||
@ -485,47 +570,416 @@ attach_thumbnail( IMAGE *im, ExifData *ed )
|
||||
}
|
||||
#endif /*HAVE_EXIF*/
|
||||
|
||||
|
||||
static int
|
||||
vips_file_load_jpeg_build( VipsObject *object )
|
||||
read_exif( IMAGE *im, void *data, int data_length )
|
||||
{
|
||||
VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) object;
|
||||
char *data_copy;
|
||||
|
||||
if( jpeg->shrink != 1 &&
|
||||
jpeg->shrink != 2 &&
|
||||
jpeg->shrink != 4 &&
|
||||
jpeg->shrink != 8 ) {
|
||||
vips_error( "VipsFormatLoadJpeg",
|
||||
_( "bad shrink factor %d" ), jpeg->shrink );
|
||||
/* 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 );
|
||||
}
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_file_load_jpeg_parent_class )->
|
||||
build( object ) )
|
||||
#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
|
||||
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 = 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.
|
||||
*/
|
||||
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 );
|
||||
}
|
||||
|
||||
static int
|
||||
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 = 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.
|
||||
*/
|
||||
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 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsFileLoadClass *load_class = (VipsFileLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "jpegload";
|
||||
object_class->description = _( "load jpeg from file" );
|
||||
object_class->build = vips_file_load_jpeg_build;
|
||||
|
||||
VIPS_ARG_INT( class, "shrink", 5,
|
||||
load_class->is_a = vips_file_load_jpeg_is_a;
|
||||
load_class->header = vips_file_load_jpeg_header;
|
||||
load_class->load = vips_file_load_jpeg_load;
|
||||
|
||||
VIPS_ARG_INT( class, "shrink", 10,
|
||||
_( "Shrink" ),
|
||||
_( "Shrink factor on load" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsFileLoadJpeg, shrink ),
|
||||
1, 16, 1 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "fail", 6,
|
||||
VIPS_ARG_BOOL( class, "fail", 11,
|
||||
_( "Fail" ),
|
||||
_( "Fail on first warning" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
@ -535,10 +989,8 @@ vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class )
|
||||
}
|
||||
|
||||
static void
|
||||
vips_file_load_jpeg_init( VipsFileLoadJpeg *object )
|
||||
vips_file_load_jpeg_init( VipsFileLoadJpeg *jpeg )
|
||||
{
|
||||
jpeg->shrink = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -73,7 +73,7 @@ typedef struct _VipsFileClass {
|
||||
int priority;
|
||||
|
||||
/* Null-terminated list of recommended suffixes, eg. ".tif", ".tiff".
|
||||
* This is used by both load and save, so it's in the base class.
|
||||
* This can be used by both load and save, so it's in the base class.
|
||||
*/
|
||||
const char **suffs;
|
||||
|
||||
@ -113,9 +113,9 @@ typedef struct _VipsFileLoad {
|
||||
VipsFile parent_object;
|
||||
/*< public >*/
|
||||
|
||||
/* Open to memory (default is to open via disc).
|
||||
/* Open to disc (default is to open to memory).
|
||||
*/
|
||||
gboolean memory;
|
||||
gboolean disc;
|
||||
|
||||
/* Flags read from the file.
|
||||
*/
|
||||
@ -125,7 +125,8 @@ typedef struct _VipsFileLoad {
|
||||
*/
|
||||
VipsImage *out;
|
||||
|
||||
/* The behind-the-scenes real image we decompress to.
|
||||
/* The behind-the-scenes real image we decompress to. This can be a
|
||||
* disc file or a memory buffer.
|
||||
*/
|
||||
VipsImage *real;
|
||||
|
||||
@ -140,15 +141,15 @@ typedef struct _VipsFileLoadClass {
|
||||
*/
|
||||
gboolean (*is_a)( const char * );
|
||||
|
||||
/* Get the flags for this file in this file.
|
||||
/* Get the flags for this file.
|
||||
*/
|
||||
VipsFileFlags (*get_flags)( VipsFileLoad * );
|
||||
int (*get_flags)( VipsFileLoad * );
|
||||
|
||||
/* Read the header into @out.
|
||||
*/
|
||||
int (*header)( VipsFileLoad * );
|
||||
|
||||
/* Read the whole image into @out.
|
||||
/* Read the whole image into real. It gets copied to out later.
|
||||
*/
|
||||
int (*load)( VipsFileLoad * );
|
||||
|
||||
@ -199,6 +200,8 @@ const char *vips_file_find_save( const char *filename );
|
||||
int vips_file_read( const char *filename, VipsImage **out, ... );
|
||||
int vips_file_write( VipsImage *in, const char *filename, ... );
|
||||
|
||||
void vips_file_operation_init( void );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
@ -231,6 +231,7 @@ vips_init( const char *argv0 )
|
||||
*/
|
||||
vips_arithmetic_operation_init();
|
||||
vips_conversion_operation_init();
|
||||
vips_file_operation_init();
|
||||
|
||||
/* Load up any plugins in the vips libdir. We don't error on failure,
|
||||
* it's too annoying to have VIPS refuse to start because of a broken
|
||||
|
Loading…
Reference in New Issue
Block a user