/** * vips_tracked_free: * @s: memory to free * * Only use it to free * memory that was previously allocated with vips_tracked_malloc() with a * %NULL first argument. * * See also: vips_tracked_malloc(). */ void vips_tracked_free( void *s ) { size_t size; /* Keep the size of the alloc in the previous 16 bytes. Ensures * alignment rules are kept. */ s = (void *) ((char*)s - 16); size = *((size_t*)s); g_mutex_lock( vips_tracked_mutex ); if( vips_tracked_allocs <= 0 ) vips_warn( "vips_tracked", "%s", _( "vips_free: too many frees" ) ); if( vips_tracked_mem < size ) vips_warn( "vips_tracked", "%s", _( "vips_free: too much free" ) ); vips_tracked_mem -= size; vips_tracked_allocs -= 1; g_mutex_unlock( vips_tracked_mutex ); g_free( s ); VIPS_GATE_FREE( size ); }
static void res_from_exif( VipsImage *im, ExifData *ed ) { double xres, yres; int unit; /* The main image xres/yres are in ifd0. ifd1 has xres/yres of the * image thumbnail, if any. */ if( get_entry_double( ed, 0, EXIF_TAG_X_RESOLUTION, &xres ) || get_entry_double( ed, 0, EXIF_TAG_Y_RESOLUTION, &yres ) || get_entry_int( ed, 0, EXIF_TAG_RESOLUTION_UNIT, &unit ) ) { vips_warn( "VipsJpeg", "%s", _( "error reading resolution" ) ); return; } #ifdef DEBUG printf( "res_from_exif: seen exif tags " "xres = %g, yres = %g, unit = %d\n", xres, yres, unit ); #endif /*DEBUG*/ switch( unit ) { case 2: /* In inches. */ xres /= 25.4; yres /= 25.4; vips_image_set_string( im, VIPS_META_RESOLUTION_UNIT, "in" ); break; case 3: /* In cm. */ xres /= 10.0; yres /= 10.0; vips_image_set_string( im, VIPS_META_RESOLUTION_UNIT, "cm" ); break; default: vips_warn( "VipsJpeg", "%s", _( "unknown EXIF resolution unit" ) ); return; } #ifdef DEBUG printf( "res_from_exif: seen exif resolution %g, %g p/mm\n", xres, yres ); #endif /*DEBUG*/ im->Xres = xres; im->Yres = yres; }
static int write_blob( Write *write, const char *field, int app ) { unsigned char *data; size_t data_length; if( vips_image_get_typeof( write->in, field ) ) { if( vips_image_get_blob( write->in, field, (void *) &data, &data_length ) ) return( -1 ); /* Single jpeg markers can only hold 64kb, large objects must * be split into multiple markers. * * Unfortunately, how this splitting is done depends on the * data type. For example, ICC and XMP have completely * different ways of doing this. * * For now, just ignore oversize objects and warn. */ if( data_length > 65530 ) vips_warn( "VipsJpeg", _( "field \"%s\" is too large " "for a single JPEG marker, ignoring" ), field ); else { #ifdef DEBUG printf( "write_blob: attaching %zd bytes of %s\n", data_length, field ); #endif /*DEBUG*/ jpeg_write_marker( &write->cinfo, app, data, data_length ); } } return( 0 ); }
/* Call a stop function if a sequence is running in this VipsRegion. */ void vips__region_stop( VipsRegion *region ) { VipsImage *image = region->im; if( region->seq && image->stop_fn ) { int result; VIPS_GATE_START( "vips__region_stop: wait" ); g_mutex_lock( image->sslock ); VIPS_GATE_STOP( "vips__region_stop: wait" ); result = image->stop_fn( region->seq, image->client1, image->client2 ); g_mutex_unlock( image->sslock ); /* stop function can return an error, but we have nothing we * can really do with it, sadly. */ if( result ) vips_warn( "VipsRegion", "stop callback failed for image %s", image->filename ); region->seq = NULL; } }
/* Embed any IPCT metadata. */ static int write_embed_ipct( Write *write, TIFF *tif ) { void *data; size_t data_length; if( !vips_image_get_typeof( write->im, VIPS_META_IPCT_NAME ) ) return( 0 ); if( vips_image_get_blob( write->im, VIPS_META_IPCT_NAME, &data, &data_length ) ) return( -1 ); /* For no very good reason, libtiff stores IPCT as an array of * long, not byte. */ if( data_length & 3 ) { vips_warn( "vips2tiff", "%s", _( "rounding up IPCT data length" ) ); data_length /= 4; data_length += 1; } else data_length /= 4; TIFFSetField( tif, TIFFTAG_RICHTIFFIPTC, data_length, data ); #ifdef DEBUG printf( "vips2tiff: attached IPCT from meta\n" ); #endif /*DEBUG*/ return( 0 ); }
static void vips_thread_profile_save( VipsThreadProfile *profile ) { g_mutex_lock( vips__global_lock ); VIPS_DEBUG_MSG( "vips_thread_profile_save: %s\n", profile->name ); if( !vips__thread_fp ) { vips__thread_fp = vips__file_open_write( "vips-profile.txt", TRUE ); if( !vips__thread_fp ) { g_mutex_unlock( vips__global_lock ); vips_warn( "VipsGate", "%s", "unable to create profile log" ); return; } printf( "recording profile in vips-profile.txt\n" ); } fprintf( vips__thread_fp, "thread: %s (%p)\n", profile->name, profile ); g_hash_table_foreach( profile->gates, vips_thread_profile_save_cb, vips__thread_fp ); vips_thread_profile_save_gate( profile->memory, vips__thread_fp ); g_mutex_unlock( vips__global_lock ); }
static int icc_error( int code, const char *text ) { if( code == LCMS_ERRC_WARNING ) vips_warn( "VipsIcc", "%s", text ); else vips_error( "VipsIcc", "%s", text ); return( 0 ); }
/* This is different, we set the xres/yres from the vips header rather than * from the exif tags on the image metadata. * * This is also called from the jpg reader to fix up bad exif resoltion. */ int vips__set_exif_resolution( ExifData *ed, VipsImage *im ) { double xres, yres; const char *p; int unit; VIPS_DEBUG_MSG( "vips__set_exif_resolution: vips res of %g, %g\n", im->Xres, im->Yres ); /* Default to inches, more progs support it. */ unit = 2; if( vips_image_get_typeof( im, VIPS_META_RESOLUTION_UNIT ) && !vips_image_get_string( im, VIPS_META_RESOLUTION_UNIT, &p ) ) { if( vips_isprefix( "cm", p ) ) unit = 3; else if( vips_isprefix( "none", p ) ) unit = 1; } switch( unit ) { case 1: xres = im->Xres; yres = im->Yres; break; case 2: xres = im->Xres * 25.4; yres = im->Yres * 25.4; break; case 3: xres = im->Xres * 10.0; yres = im->Yres * 10.0; break; default: vips_warn( "VipsJpeg", "%s", _( "unknown EXIF resolution unit" ) ); return( 0 ); } /* Main image xres/yres/unit are in ifd0. ifd1 has the thumbnail * xres/yres/unit. */ write_tag( ed, 0, EXIF_TAG_X_RESOLUTION, vips_exif_set_double, (void *) &xres ); write_tag( ed, 0, EXIF_TAG_Y_RESOLUTION, vips_exif_set_double, (void *) &yres ); write_tag( ed, 0, EXIF_TAG_RESOLUTION_UNIT, vips_exif_set_int, (void *) &unit ); return( 0 ); }
/* Load all plugins in a directory ... look for '.plg' suffix. Error if we had * any probs. */ static int vips_load_plugins( const char *fmt, ... ) { va_list ap; char dir_name[VIPS_PATH_MAX]; GDir *dir; const char *name; int result; /* Silently succeed if we can't do modules. */ if( !g_module_supported() ) return( 0 ); va_start( ap, fmt ); (void) vips_vsnprintf( dir_name, VIPS_PATH_MAX - 1, fmt, ap ); va_end( ap ); #ifdef DEBUG printf( "vips_load_plugins: searching \"%s\"\n", dir_name ); #endif /*DEBUG*/ if( !(dir = g_dir_open( dir_name, 0, NULL )) ) /* Silent success for dir not there. */ return( 0 ); result = 0; while( (name = g_dir_read_name( dir )) ) if( vips_ispostfix( name, ".plg" ) ) { char path[VIPS_PATH_MAX]; GModule *module; vips_snprintf( path, VIPS_PATH_MAX - 1, "%s" G_DIR_SEPARATOR_S "%s", dir_name, name ); #ifdef DEBUG printf( "vips_load_plugins: loading \"%s\"\n", path ); #endif /*DEBUG*/ module = g_module_open( path, G_MODULE_BIND_LAZY ); if( !module ) { vips_warn( "vips_init", _( "unable to load \"%s\" -- %s" ), path, g_module_error() ); result = -1; } } g_dir_close( dir ); return( result ); }
static void * vips_exif_image_field( VipsImage *image, const char *field, GValue *value, void *data ) { ExifData *ed = (ExifData *) data; const char *string; int ifd; const char *p; ExifTag tag; if( !vips_isprefix( "exif-ifd", field ) ) return( NULL ); /* value must be a string. */ if( vips_image_get_string( image, field, &string ) ) { vips_warn( "VipsJpeg", _( "bad exif meta \"%s\"" ), field ); return( NULL ); } p = field + strlen( "exif-ifd" ); ifd = atoi( p ); for( ; isdigit( *p ); p++ ) ; if( *p != '-' ) { vips_warn( "VipsJpeg", _( "bad exif meta \"%s\"" ), field ); return( NULL ); } if( !(tag = exif_tag_from_name( p + 1 )) ) { vips_warn( "VipsJpeg", _( "bad exif meta \"%s\"" ), field ); return( NULL ); } write_tag( ed, ifd, tag, vips_exif_set_entry, (void *) string ); return( NULL ); }
static int readjpeg_free( ReadJpeg *jpeg ) { int result; result = 0; if( setjmp( jpeg->eman.jmp ) ) return( -1 ); if( jpeg->eman.pub.num_warnings != 0 ) { if( jpeg->fail ) { vips_error( "VipsJpeg", "%s", vips_error_buffer() ); result = -1; } else { vips_warn( "VipsJpeg", _( "read gave %ld warnings" ), jpeg->eman.pub.num_warnings ); vips_warn( NULL, "%s", vips_error_buffer() ); } /* Make the message only appear once. */ jpeg->eman.pub.num_warnings = 0; } if( jpeg->decompressing ) { jpeg_finish_decompress( &jpeg->cinfo ); jpeg->decompressing = FALSE; } VIPS_FREEF( fclose, jpeg->eman.fp ); VIPS_FREE( jpeg->filename ); jpeg->eman.fp = NULL; jpeg_destroy_decompress( &jpeg->cinfo ); return( result ); }
// just g_object_set_property(), except we allow set enum from string static void set_property( VipsObject *object, const char *name, const GValue *value ) { VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); GType type = G_VALUE_TYPE( value ); GParamSpec *pspec; VipsArgumentClass *argument_class; VipsArgumentInstance *argument_instance; if( vips_object_get_argument( object, name, &pspec, &argument_class, &argument_instance ) ) { vips_warn( NULL, "%s", vips_error_buffer() ); vips_error_clear(); return; } if( G_IS_PARAM_SPEC_ENUM( pspec ) && type == G_TYPE_STRING ) { GType pspec_type = G_PARAM_SPEC_VALUE_TYPE( pspec ); int enum_value; GValue value2 = { 0 }; if( (enum_value = vips_enum_from_nick( object_class->nickname, pspec_type, g_value_get_string( value ) )) < 0 ) { vips_warn( NULL, "%s", vips_error_buffer() ); vips_error_clear(); return; } g_value_init( &value2, pspec_type ); g_value_set_enum( &value2, enum_value ); g_object_set_property( G_OBJECT( object ), name, &value2 ); g_value_unset( &value2 ); } else g_object_set_property( G_OBJECT( object ), name, value ); }
/* This can be called many times. */ static int readjpeg_free( ReadJpeg *jpeg ) { int result; result = 0; if( jpeg->eman.pub.num_warnings != 0 ) { if( jpeg->fail ) { vips_error( "VipsJpeg", "%s", vips_error_buffer() ); result = -1; } else { vips_warn( "VipsJpeg", _( "read gave %ld warnings" ), jpeg->eman.pub.num_warnings ); vips_warn( NULL, "%s", vips_error_buffer() ); } /* Make the message only appear once. */ jpeg->eman.pub.num_warnings = 0; } /* Don't call jpeg_finish_decompress(). It just checks the tail of the * file and who cares about that. All mem is freed in * jpeg_destroy_decompress(). */ VIPS_FREEF( fclose, jpeg->eman.fp ); VIPS_FREE( jpeg->filename ); jpeg->eman.fp = NULL; /* I don't think this can fail. It's harmless to call many times. */ jpeg_destroy_decompress( &jpeg->cinfo ); return( result ); }
/* Read a single item. Syntax is: * * element : * whitespace* item whitespace* [EOF|EOL|separator] * * item : * double | * "anything" | * empty * * the anything in quotes can contain " escaped with \ * * 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( ch == '"' ) { (void) fgetc( fp ); (void) skip_to_quote( fp ); ch = fgetc( fp ); } else if( !sepmap[ch] && fscanf( fp, "%lf", out ) != 1 ) { /* Only a warning, since (for example) exported spreadsheets * will often have text or date fields. */ vips_warn( "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 ); }
/** * vips_tracked_malloc: * @size: number of bytes to allocate * * Allocate an area of memory that will be tracked by vips_tracked_get_mem() * and friends. * * If allocation fails, vips_malloc() returns %NULL and * sets an error message. * * You must only free the memory returned with vips_tracked_free(). * * See also: vips_tracked_free(), vips_malloc(). * * Returns: a pointer to the allocated memory, or %NULL on error. */ void * vips_tracked_malloc( size_t size ) { void *buf; vips_tracked_init(); /* Need an extra sizeof(size_t) bytes to track * size of this block. Ask for an extra 16 to make sure we don't break * alignment rules. */ size += 16; if( !(buf = g_try_malloc( size )) ) { #ifdef DEBUG g_assert( 0 ); #endif /*DEBUG*/ vips_error( "vips_tracked", _( "out of memory --- size == %dMB" ), (int) (size / (1024.0*1024.0)) ); vips_warn( "vips_tracked", _( "out of memory --- size == %dMB" ), (int) (size / (1024.0*1024.0)) ); return( NULL ); } g_mutex_lock( vips_tracked_mutex ); *((size_t *)buf) = size; buf = (void *) ((char *)buf + 16); vips_tracked_mem += size; if( vips_tracked_mem > vips_tracked_mem_highwater ) vips_tracked_mem_highwater = vips_tracked_mem; vips_tracked_allocs += 1; g_mutex_unlock( vips_tracked_mutex ); VIPS_GATE_MALLOC( size ); return( buf ); }
static void vips__thread_profile_init_cb( VipsThreadProfile *profile ) { /* We only come here if vips_thread_shutdown() was not called for this * thread. Do our best to clean up. * * GPrivate has stopped working, be careful not to touch that. * * Don't try to save: we must free all mem before saving and we * probably haven't done that because vips_thread_shutdown() has not * been called. */ if( vips__thread_profile ) vips_warn( "VipsGate", "discarding unsaved state for thread %p --- " "call vips_thread_shutdown() for this thread", profile->thread ); vips_thread_profile_free( profile ); }
/** * vips_init: * @argv0: name of application * * vips_init() starts up the world of VIPS. You should call this on * program startup before using any other VIPS operations. If you do not call * vips_init(), VIPS will call it for you when you use your first VIPS * operation, but * it may not be able to get hold of @argv0 and VIPS may therefore be unable * to find its data files. It is much better to call this function yourself. * * vips_init() does approximately the following: * * <itemizedlist> * <listitem> * <para>initialises any libraries that VIPS is using, including GObject * and the threading system, if neccessary</para> * </listitem> * <listitem> * <para>guesses where the VIPS data files are and sets up * internationalisation --- see vips_guess_prefix() * </para> * </listitem> * <listitem> * <para>creates the main vips types, including VipsImage and friends * </para> * </listitem> * <listitem> * <para>loads any plugins from $libdir/vips-x.y, where x and y are the * major and minor version numbers for this VIPS. * </para> * </listitem> * </itemizedlist> * * Example: * * |[ * int main( int argc, char **argv ) * { * if( vips_init( argv[0] ) ) * vips_error_exit( "unable to start VIPS" ); * * vips_shutdown(); * * return( 0 ); * } * ]| * * See also: vips_shutdown(), vips_get_option_group(), vips_version(), * vips_guess_prefix(), vips_guess_libdir(). * * Returns: 0 on success, -1 otherwise */ int vips_init( const char *argv0 ) { extern GType vips_system_get_type( void ); static gboolean started = FALSE; static gboolean done = FALSE; char *prgname; const char *prefix; const char *libdir; char name[256]; /* Two stage done handling: 'done' means we've completed, 'started' * means we're currently initialising. Use this to prevent recursive * invocation. */ if( done ) /* Called more than once, we succeeded, just return OK. */ return( 0 ); if( started ) /* Recursive invocation, something has broken horribly. * Hopefully the first init will handle it. */ return( 0 ); started = TRUE; #ifdef NEED_TYPE_INIT /* Before glib 2.36 you have to call this on startup. */ g_type_init(); #endif /*NEED_TYPE_INIT*/ /* Older glibs need this. */ #ifndef HAVE_THREAD_NEW if( !g_thread_supported() ) g_thread_init( NULL ); #endif if( !vips__global_lock ) vips__global_lock = vips_g_mutex_new(); VIPS_SETSTR( vips__argv0, argv0 ); prgname = g_path_get_basename( argv0 ); g_set_prgname( prgname ); g_free( prgname ); /* Try to discover our prefix. */ if( !(prefix = vips_guess_prefix( argv0, "VIPSHOME" )) || !(libdir = vips_guess_libdir( argv0, "VIPSHOME" )) ) return( -1 ); /* Get i18n .mo files from $VIPSHOME/share/locale/. */ vips_snprintf( name, 256, "%s" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "locale", prefix ); bindtextdomain( GETTEXT_PACKAGE, name ); bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" ); /* Default info setting from env. */ if( g_getenv( "IM_INFO" ) ) vips__info = 1; /* Register base vips types. */ (void) vips_image_get_type(); (void) vips_region_get_type(); vips__meta_init_types(); vips__interpolate_init(); im__format_init(); /* Start up operator cache. */ vips__cache_init(); /* Start up packages. */ (void) vips_system_get_type(); vips_arithmetic_operation_init(); vips_conversion_operation_init(); vips_create_operation_init(); vips_foreign_operation_init(); vips_resample_operation_init(); vips_colour_operation_init(); vips_histogram_operation_init(); vips_convolution_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 * plugin. */ if( im_load_plugins( "%s/vips-%d.%d", libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ) ) { vips_warn( "vips_init", "%s", vips_error_buffer() ); vips_error_clear(); } /* Also load from libdir. This is old and slightly broken behaviour * :-( kept for back compat convenience. */ if( im_load_plugins( "%s", libdir ) ) { vips_warn( "vips_init", "%s", vips_error_buffer() ); vips_error_clear(); } /* Build classes which wrap old vips7 operations. */ vips__init_wrap7_classes(); /* Start up the buffer cache. */ vips__buffer_init(); /* Get the run-time compiler going. */ vips_vector_init(); /* Register vips_shutdown(). This may well not get called and many * platforms don't support it anyway. */ #ifdef HAVE_ATEXIT atexit( vips_shutdown ); #endif /*HAVE_ATEXIT*/ done = TRUE; return( 0 ); }
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to * do 255-pel. */ static int read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) { struct jpeg_decompress_struct *cinfo = &jpeg->cinfo; jpeg_saved_marker_ptr p; VipsInterpretation interpretation; double xres, yres; /* Capture app2 sections here for assembly. */ void *app2_data[MAX_APP2_SECTIONS] = { 0 }; size_t app2_data_length[MAX_APP2_SECTIONS] = { 0 }; size_t 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 = jpeg->shrink; cinfo->scale_num = 1; jpeg_calc_output_dimensions( cinfo ); jpeg->invert_pels = FALSE; switch( cinfo->out_color_space ) { case JCS_GRAYSCALE: interpretation = VIPS_INTERPRETATION_B_W; break; case JCS_CMYK: interpretation = VIPS_INTERPRETATION_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: interpretation = VIPS_INTERPRETATION_sRGB; break; } /* Get the jfif resolution. exif may overwrite this later. */ xres = 1.0; yres = 1.0; if( cinfo->saw_JFIF_marker && cinfo->X_density != 1U && cinfo->Y_density != 1U ) { #ifdef DEBUG printf( "read_jpeg_header: seen jfif _density %d, %d\n", cinfo->X_density, cinfo->Y_density ); #endif /*DEBUG*/ switch( cinfo->density_unit ) { case 1: /* Pixels per inch. */ xres = cinfo->X_density / 25.4; yres = cinfo->Y_density / 25.4; break; case 2: /* Pixels per cm. */ xres = cinfo->X_density / 10.0; yres = cinfo->Y_density / 10.0; break; default: vips_warn( "VipsJpeg", "%s", _( "unknown JFIF resolution unit" ) ); break; } #ifdef DEBUG printf( "read_jpeg_header: seen jfif resolution %g, %g p/mm\n", xres, yres ); #endif /*DEBUG*/ } /* Set VIPS header. */ vips_image_init_fields( out, cinfo->output_width, cinfo->output_height, cinfo->output_components, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, interpretation, xres, yres ); vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); /* Interlaced jpegs need lots of memory to read, so our caller needs * to know. */ (void) 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( "read_jpeg_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 && vips_isprefix( "Exif", (char *) p->data ) ) { if( parse_exif( out, p->data, p->data_length ) || attach_blob( out, VIPS_META_EXIF_NAME, p->data, p->data_length ) ) return( -1 ); } if( p->data_length > 4 && vips_isprefix( "http", (char *) p->data ) && attach_blob( out, VIPS_META_XMP_NAME, p->data, p->data_length ) ) return( -1 ); break; case JPEG_APP0 + 2: /* Possible ICC profile. */ if( p->data_length > 14 && vips_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; case JPEG_APP0 + 13: /* Possible IPCT data block. */ if( p->data_length > 5 && vips_isprefix( "Photo", (char *) p->data ) && attach_blob( out, VIPS_META_IPCT_NAME, p->data, p->data_length ) ) return( -1 ); 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 p; #ifdef DEBUG printf( "read_jpeg_header: assembled %zd byte ICC profile\n", data_length ); #endif /*DEBUG*/ if( !(data = vips_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]; } vips_image_set_blob( out, VIPS_META_ICC_NAME, (VipsCallbackFn) vips_free, data, data_length ); } return( 0 ); }
int main( int argc, char **argv ) { GOptionContext *context; GOptionGroup *main_group; GError *error = NULL; int i; int result; if( VIPS_INIT( argv[0] ) ) vips_error_exit( "unable to start VIPS" ); textdomain( GETTEXT_PACKAGE ); setlocale( LC_ALL, "" ); context = g_option_context_new( _( "- thumbnail generator" ) ); main_group = g_option_group_new( NULL, NULL, NULL, NULL, NULL ); g_option_group_add_entries( main_group, options ); vips_add_option_entries( main_group ); g_option_group_set_translation_domain( main_group, GETTEXT_PACKAGE ); g_option_context_set_main_group( context, main_group ); if( !g_option_context_parse( context, &argc, &argv, &error ) ) { if( error ) { fprintf( stderr, "%s\n", error->message ); g_error_free( error ); } vips_error_exit( "try \"%s --help\"", g_get_prgname() ); } g_option_context_free( context ); if( sscanf( thumbnail_size, "%d x %d", &thumbnail_width, &thumbnail_height ) != 2 ) { if( sscanf( thumbnail_size, "%d", &thumbnail_width ) != 1 ) vips_error_exit( "unable to parse size \"%s\" -- " "use eg. 128 or 200x300", thumbnail_size ); thumbnail_height = thumbnail_width; } if( rotate_image ) { #ifndef HAVE_EXIF vips_warn( "vipsthumbnail", "%s", _( "auto-rotate disabled: " "libvips built without exif support" ) ); #endif /*!HAVE_EXIF*/ } result = 0; for( i = 1; i < argc; i++ ) { /* Hang resources for processing this thumbnail off @process. */ VipsObject *process = VIPS_OBJECT( vips_image_new() ); if( thumbnail_process( process, argv[i] ) ) { fprintf( stderr, "%s: unable to thumbnail %s\n", argv[0], argv[i] ); fprintf( stderr, "%s", vips_error_buffer() ); vips_error_clear(); /* We had a conversion failure: return an error code * when we finally exit. */ result = -1; } g_object_unref( process ); } vips_shutdown(); return( result ); }
static VipsImage * thumbnail_shrink( VipsObject *process, VipsImage *in ) { VipsImage **t = (VipsImage **) vips_object_local_array( process, 10 ); VipsInterpretation interpretation = linear_processing ? VIPS_INTERPRETATION_XYZ : VIPS_INTERPRETATION_sRGB; /* TRUE if we've done the import of an ICC transform and still need to * export. */ gboolean have_imported; /* TRUE if we've premultiplied and need to unpremultiply. */ gboolean have_premultiplied; VipsBandFormat unpremultiplied_format; /* Sniff the incoming image and try to guess what the alpha max is. */ double max_alpha; double shrink; /* RAD needs special unpacking. */ if( in->Coding == VIPS_CODING_RAD ) { vips_info( "vipsthumbnail", "unpacking Rad to float" ); /* rad is scrgb. */ if( vips_rad2float( in, &t[0], NULL ) ) return( NULL ); in = t[0]; } /* Try to guess what the maximum alpha might be. */ max_alpha = 255; if( in->BandFmt == VIPS_FORMAT_USHORT ) max_alpha = 65535; /* In linear mode, we import right at the start. * * We also have to import the whole image if it's CMYK, since * vips_colourspace() (see below) doesn't know about CMYK. * * This is only going to work for images in device space. If you have * an image in PCS which also has an attached profile, strange things * will happen. */ have_imported = FALSE; if( (linear_processing || in->Type == VIPS_INTERPRETATION_CMYK) && in->Coding == VIPS_CODING_NONE && (in->BandFmt == VIPS_FORMAT_UCHAR || in->BandFmt == VIPS_FORMAT_USHORT) && (vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || import_profile) ) { if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) vips_info( "vipsthumbnail", "importing with embedded profile" ); else vips_info( "vipsthumbnail", "importing with profile %s", import_profile ); if( vips_icc_import( in, &t[1], "input_profile", import_profile, "embedded", TRUE, "pcs", VIPS_PCS_XYZ, NULL ) ) return( NULL ); in = t[1]; have_imported = TRUE; } /* To the processing colourspace. This will unpack LABQ as well. */ vips_info( "vipsthumbnail", "converting to processing space %s", vips_enum_nick( VIPS_TYPE_INTERPRETATION, interpretation ) ); if( vips_colourspace( in, &t[2], interpretation, NULL ) ) return( NULL ); in = t[2]; /* If there's an alpha, we have to premultiply before shrinking. See * https://github.com/jcupitt/libvips/issues/291 */ have_premultiplied = FALSE; if( in->Bands == 2 || (in->Bands == 4 && in->Type != VIPS_INTERPRETATION_CMYK) || in->Bands == 5 ) { vips_info( "vipsthumbnail", "premultiplying alpha" ); if( vips_premultiply( in, &t[3], "max_alpha", max_alpha, NULL ) ) return( NULL ); have_premultiplied = TRUE; /* vips_premultiply() makes a float image. When we * vips_unpremultiply() below, we need to cast back to the * pre-premultiply format. */ unpremultiplied_format = in->BandFmt; in = t[3]; } shrink = calculate_shrink( in ); if( vips_resize( in, &t[4], 1.0 / shrink, NULL ) ) return( NULL ); in = t[4]; if( have_premultiplied ) { vips_info( "vipsthumbnail", "unpremultiplying alpha" ); if( vips_unpremultiply( in, &t[5], "max_alpha", max_alpha, NULL ) || vips_cast( t[5], &t[6], unpremultiplied_format, NULL ) ) return( NULL ); in = t[6]; } /* Colour management. * * If we've already imported, just export. Otherwise, we're in * device space and we need a combined import/export to transform to * the target space. */ if( have_imported ) { if( export_profile || vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { vips_info( "vipsthumbnail", "exporting to device space with a profile" ); if( vips_icc_export( in, &t[7], "output_profile", export_profile, NULL ) ) return( NULL ); in = t[7]; } else { vips_info( "vipsthumbnail", "converting to sRGB" ); if( vips_colourspace( in, &t[7], VIPS_INTERPRETATION_sRGB, NULL ) ) return( NULL ); in = t[7]; } } else if( export_profile && (vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || import_profile) ) { VipsImage *out; vips_info( "vipsthumbnail", "exporting with profile %s", export_profile ); /* We first try with the embedded profile, if any, then if * that fails try again with the supplied fallback profile. */ out = NULL; if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { vips_info( "vipsthumbnail", "importing with embedded profile" ); if( vips_icc_transform( in, &t[7], export_profile, "embedded", TRUE, NULL ) ) { vips_warn( "vipsthumbnail", _( "unable to import with " "embedded profile: %s" ), vips_error_buffer() ); vips_error_clear(); } else out = t[7]; } if( !out && import_profile ) { vips_info( "vipsthumbnail", "importing with fallback profile" ); if( vips_icc_transform( in, &t[7], export_profile, "input_profile", import_profile, "embedded", FALSE, NULL ) ) return( NULL ); out = t[7]; } /* If the embedded profile failed and there's no fallback or * the fallback failed, out will still be NULL. */ if( out ) in = out; } if( delete_profile && vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { vips_info( "vipsthumbnail", "deleting profile from output image" ); if( !vips_image_remove( in, VIPS_META_ICC_NAME ) ) return( NULL ); } return( in ); }
/* Make and init a Write. */ static Write * write_new( VipsImage *im, const char *filename, VipsForeignTiffCompression compression, int Q, VipsForeignTiffPredictor predictor, char *profile, gboolean tile, int tile_width, int tile_height, gboolean pyramid, gboolean squash, gboolean miniswhite, VipsForeignTiffResunit resunit, double xres, double yres, gboolean bigtiff, gboolean rgbjpeg, gboolean properties, gboolean strip ) { Write *write; if( !(write = VIPS_NEW( im, Write )) ) return( NULL ); write->im = im; write->filename = vips_strdup( VIPS_OBJECT( im ), filename ); write->layer = NULL; write->tbuf = NULL; write->compression = get_compression( compression ); write->jpqual = Q; write->predictor = predictor; write->tile = tile; write->tilew = tile_width; write->tileh = tile_height; write->pyramid = pyramid; write->onebit = squash; write->miniswhite = miniswhite; write->icc_profile = vips_strdup( NULL, profile ); write->bigtiff = bigtiff; write->rgbjpeg = rgbjpeg; write->properties = properties; write->strip = strip; write->resunit = get_resunit( resunit ); write->xres = xres; write->yres = yres; /* In strip mode we use tileh to set rowsperstrip, and that does not * have the multiple-of-16 restriction. */ if( tile ) { if( (write->tilew & 0xf) != 0 || (write->tileh & 0xf) != 0 ) { vips_error( "vips2tiff", "%s", _( "tile size not a multiple of 16" ) ); return( NULL ); } } /* We can only pyramid LABQ and non-complex images. */ if( write->pyramid ) { if( im->Coding == VIPS_CODING_NONE && vips_band_format_iscomplex( im->BandFmt ) ) { vips_error( "vips2tiff", "%s", _( "can only pyramid LABQ and " "non-complex images" ) ); return( NULL ); } } /* Only 1-bit-ize 8 bit mono images. */ if( write->onebit && (im->Coding != VIPS_CODING_NONE || im->BandFmt != VIPS_FORMAT_UCHAR || im->Bands != 1) ) { vips_warn( "vips2tiff", "%s", _( "can only squash 1 band uchar images -- " "disabling squash" ) ); write->onebit = 0; } if( write->onebit && write->compression == COMPRESSION_JPEG ) { vips_warn( "vips2tiff", "%s", _( "can't have 1-bit JPEG -- disabling JPEG" ) ); write->compression = COMPRESSION_NONE; } /* We can only MINISWHITE non-complex images of 1 or 2 bands. */ if( write->miniswhite && (im->Coding != VIPS_CODING_NONE || vips_band_format_iscomplex( im->BandFmt ) || im->Bands > 2) ) { vips_warn( "vips2tiff", "%s", _( "can only save non-complex greyscale images " "as miniswhite -- disabling miniswhite" ) ); write->miniswhite = FALSE; } /* Sizeof a line of bytes in the TIFF tile. */ if( im->Coding == VIPS_CODING_LABQ ) write->tls = write->tilew * 3; else if( write->onebit ) write->tls = VIPS_ROUND_UP( write->tilew, 8 ) / 8; else write->tls = VIPS_IMAGE_SIZEOF_PEL( im ) * write->tilew; /* Build the pyramid framework. */ write->layer = pyramid_new( write, NULL, im->Xsize, im->Ysize ); /* Fill all the layers. */ if( pyramid_fill( write ) ) { write_free( write ); return( NULL ); } if( tile ) write->tbuf = vips_malloc( NULL, TIFFTileSize( write->layer->tif ) ); else write->tbuf = vips_malloc( NULL, TIFFScanlineSize( write->layer->tif ) ); if( !write->tbuf ) { write_free( write ); return( NULL ); } return( write ); }
int vips_init( const char *argv0 ) { extern GType vips_system_get_type( void ); static gboolean started = FALSE; static gboolean done = FALSE; char *prgname; const char *prefix; const char *libdir; char name[256]; /* Two stage done handling: 'done' means we've completed, 'started' * means we're currently initialising. Use this to prevent recursive * invocation. */ if( done ) /* Called more than once, we succeeded, just return OK. */ return( 0 ); if( started ) /* Recursive invocation, something has broken horribly. * Hopefully the first init will handle it. */ return( 0 ); started = TRUE; #ifdef HAVE_TYPE_INIT /* Before glib 2.36 you have to call this on startup. */ g_type_init(); #endif /*HAVE_TYPE_INIT*/ /* Older glibs need this. */ #ifndef HAVE_THREAD_NEW if( !g_thread_supported() ) g_thread_init( NULL ); #endif /* This does an unsynchronised static hash table init on first call -- * we have to make sure we do this single-threaded. See: * https://github.com/openslide/openslide/issues/161 */ (void) g_get_language_names(); if( !vips__global_lock ) vips__global_lock = vips_g_mutex_new(); VIPS_SETSTR( vips__argv0, argv0 ); prgname = g_path_get_basename( argv0 ); g_set_prgname( prgname ); g_free( prgname ); vips__thread_profile_attach( "main" ); /* We can't do VIPS_GATE_START() until command-line processing * happens, since vips__thread_profile may not be set yet. Call * directly. */ vips__thread_gate_start( "init: main" ); vips__thread_gate_start( "init: startup" ); /* Try to discover our prefix. */ if( !(prefix = vips_guess_prefix( argv0, "VIPSHOME" )) || !(libdir = vips_guess_libdir( argv0, "VIPSHOME" )) ) return( -1 ); /* Get i18n .mo files from $VIPSHOME/share/locale/. */ vips_snprintf( name, 256, "%s" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "locale", prefix ); bindtextdomain( GETTEXT_PACKAGE, name ); bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" ); /* Default various settings from env. */ if( g_getenv( "VIPS_INFO" ) || g_getenv( "IM_INFO" ) ) vips_info_set( TRUE ); if( g_getenv( "VIPS_TRACE" ) ) vips_cache_set_trace( TRUE ); /* Register base vips types. */ (void) vips_image_get_type(); (void) vips_region_get_type(); vips__meta_init_types(); vips__interpolate_init(); im__format_init(); /* Start up operator cache. */ vips__cache_init(); /* Start up packages. */ (void) vips_system_get_type(); vips_arithmetic_operation_init(); vips_conversion_operation_init(); vips_create_operation_init(); vips_foreign_operation_init(); vips_resample_operation_init(); vips_colour_operation_init(); vips_histogram_operation_init(); vips_convolution_operation_init(); vips_freqfilt_operation_init(); vips_morphology_operation_init(); vips_draw_operation_init(); vips_mosaicing_operation_init(); /* Load any vips8 plugins from the vips libdir. Keep going, even if * some plugins fail to load. */ (void) vips_load_plugins( "%s/vips-plugins-%d.%d", libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ); /* Load up any vips7 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 plugin. */ if( im_load_plugins( "%s/vips-%d.%d", libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ) ) { vips_warn( "vips_init", "%s", vips_error_buffer() ); vips_error_clear(); } /* Also load from libdir. This is old and slightly broken behaviour * :-( kept for back compat convenience. */ if( im_load_plugins( "%s", libdir ) ) { vips_warn( "vips_init", "%s", vips_error_buffer() ); vips_error_clear(); } /* Start up the buffer cache. */ vips__buffer_init(); /* Get the run-time compiler going. */ vips_vector_init(); #ifdef HAVE_GSF /* Use this for structured file write. */ gsf_init(); #endif /*HAVE_GSF*/ /* Register vips_shutdown(). This may well not get called and many * platforms don't support it anyway. */ #ifdef HAVE_ATEXIT atexit( vips_shutdown ); #endif /*HAVE_ATEXIT*/ #ifdef DEBUG_LEAK vips__image_pixels_quark = g_quark_from_static_string( "vips-image-pixels" ); #endif /*DEBUG_LEAK*/ done = TRUE; vips__thread_gate_stop( "init: startup" ); return( 0 ); }
void * vips__mmap( int fd, int writeable, size_t length, gint64 offset ) { void *baseaddr; #ifdef DEBUG printf( "vips__mmap: length = 0x%zx, offset = 0x%lx\n", length, offset ); #endif /*DEBUG*/ #ifdef OS_WIN32 { HANDLE hFile = (HANDLE) _get_osfhandle( fd ); DWORD flProtect; DWORD dwDesiredAccess; HANDLE hMMFile; ULARGE_INTEGER quad; DWORD dwFileOffsetHigh; DWORD dwFileOffsetLow; if( writeable ) { flProtect = PAGE_READWRITE; dwDesiredAccess = FILE_MAP_WRITE; } else { flProtect = PAGE_READONLY; dwDesiredAccess = FILE_MAP_READ; } quad.QuadPart = offset; dwFileOffsetLow = quad.LowPart; dwFileOffsetHigh = quad.HighPart; if( !(hMMFile = CreateFileMapping( hFile, NULL, flProtect, 0, 0, NULL )) ) { vips_error_system( GetLastError(), "vips_mapfile", "%s", _( "unable to CreateFileMapping" ) ); printf( "CreateFileMapping failed: %s\n", vips_error_buffer() ); return( NULL ); } if( !(baseaddr = (char *)MapViewOfFile( hMMFile, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, length )) ) { vips_error_system( GetLastError(), "vips_mapfile", "%s", _( "unable to MapViewOfFile" ) ); printf( "MapViewOfFile failed: %s\n", vips_error_buffer() ); CloseHandle( hMMFile ); return( NULL ); } /* Can close mapping now ... view stays until UnmapViewOfFile(). FIXME ... is this a performance problem? */ CloseHandle( hMMFile ); } #else /*!OS_WIN32*/ { int prot; int flags; if( writeable ) prot = PROT_WRITE; else prot = PROT_READ; flags = MAP_SHARED; /* OS X caches mmapped files very aggressively if this flags is not * set. Scanning a large file without this flag will cause every other * process to get swapped out and kill performance. */ #ifdef MAP_NOCACHE flags |= MAP_NOCACHE; #endif /*MAP_NOCACHE*/ /* Casting gint64 to off_t should be safe, even on *nixes without * LARGEFILE. */ baseaddr = mmap( 0, length, prot, MAP_SHARED, fd, (off_t) offset ); if( baseaddr == MAP_FAILED ) { vips_error_system( errno, "vips_mapfile", "%s", _( "unable to mmap" ) ); vips_warn( "vips_mapfile", _( "map failed (%s), " "running very low on system resources, " "expect a crash soon" ), strerror( errno ) ); return( NULL ); } } #endif /*OS_WIN32*/ return( baseaddr ); }
static int read_header( Read *read, VipsImage *out ) { vips_image_init_fields( out, read->width, read->height, read->config.input.has_alpha ? 4 : 3, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); #ifdef HAVE_LIBWEBPMUX { WebPData bitstream; WebPMux *mux; int i; /* We have to parse the whole file again to get the metadata out. * * Don't make parse failure an error. We don't want to refuse to read * any pixels because of some malformed metadata. */ bitstream.bytes = read->data; bitstream.size = read->length; if( !(mux = WebPMuxCreate( &bitstream, 0 )) ) { vips_warn( "webp", "%s", _( "unable to read image metadata" ) ); return( 0 ); } for( i = 0; i < vips__n_webp_names; i++ ) { const char *vips = vips__webp_names[i].vips; const char *webp = vips__webp_names[i].webp; WebPData data; if( WebPMuxGetChunk( mux, webp, &data ) == WEBP_MUX_OK ) { void *blob; if( !(blob = vips_malloc( NULL, data.size )) ) { WebPMuxDelete( mux ); return( -1 ); } memcpy( blob, data.bytes, data.size ); vips_image_set_blob( out, vips, (VipsCallbackFn) vips_free, blob, data.size ); } } WebPMuxDelete( mux ); /* We may have read some exif ... parse into the header. */ if( vips__exif_parse( out ) ) return( -1 ); } #endif /*HAVE_LIBWEBPMUX*/ return( 0 ); }
/* Write a VIPS image to a JPEG compress struct. */ static int write_vips( Write *write, int qfac, const char *profile, gboolean optimize_coding, gboolean progressive, gboolean strip, gboolean no_subsample, gboolean trellis_quant, gboolean overshoot_deringing, gboolean optimize_scans, int quant_table ) { VipsImage *in; J_COLOR_SPACE space; /* The image we'll be writing ... can change, see CMYK. */ in = write->in; /* Should have been converted for save. */ g_assert( in->BandFmt == VIPS_FORMAT_UCHAR ); g_assert( in->Coding == VIPS_CODING_NONE ); g_assert( in->Bands == 1 || in->Bands == 3 || in->Bands == 4 ); /* Check input image. */ if( vips_image_pio_input( in ) ) 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 == VIPS_INTERPRETATION_CMYK ) { space = JCS_CMYK; /* IJG always sets an Adobe marker, so we should invert CMYK. */ if( vips_invert( in, &write->inverted, NULL ) ) 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. */ if( !(write->row_pointer = VIPS_ARRAY( NULL, in->Ysize, JSAMPROW )) ) return( -1 ); #ifdef HAVE_JPEG_EXT_PARAMS /* Reset compression profile to libjpeg defaults */ if( jpeg_c_int_param_supported( &write->cinfo, JINT_COMPRESS_PROFILE ) ) jpeg_c_set_int_param( &write->cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST ); #endif /* Reset to default. */ jpeg_set_defaults( &write->cinfo ); /* Compute optimal Huffman coding tables. */ write->cinfo.optimize_coding = optimize_coding; #ifdef HAVE_JPEG_EXT_PARAMS /* Apply trellis quantisation to each 8x8 block. Implies * "optimize_coding". */ if( trellis_quant ) { if( jpeg_c_bool_param_supported( &write->cinfo, JBOOLEAN_TRELLIS_QUANT ) ) { jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_TRELLIS_QUANT, TRUE ); write->cinfo.optimize_coding = TRUE; } else vips_warn( "vips2jpeg", "%s", _( "trellis_quant unsupported" ) ); } /* Apply overshooting to samples with extreme values e.g. 0 & 255 * for 8-bit. */ if( overshoot_deringing ) { if( jpeg_c_bool_param_supported( &write->cinfo, JBOOLEAN_OVERSHOOT_DERINGING ) ) jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_OVERSHOOT_DERINGING, TRUE ); else vips_warn( "vips2jpeg", "%s", _( "overshoot_deringing unsupported" ) ); } /* Split the spectrum of DCT coefficients into separate scans. * Requires progressive output. Must be set before * jpeg_simple_progression. */ if( optimize_scans ) { if( progressive ) { if( jpeg_c_bool_param_supported( &write->cinfo, JBOOLEAN_OPTIMIZE_SCANS ) ) jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_OPTIMIZE_SCANS, TRUE ); else vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) ); } else vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans for baseline" ) ); } /* Use predefined quantization table. */ if( quant_table > 0 ) { if( jpeg_c_int_param_supported( &write->cinfo, JINT_BASE_QUANT_TBL_IDX ) ) jpeg_c_set_int_param( &write->cinfo, JINT_BASE_QUANT_TBL_IDX, quant_table ); else vips_warn( "vips2jpeg", "%s", _( "Setting quant_table unsupported" ) ); } #else /* Using jpeglib.h without extension parameters, warn of ignored * options. */ if( trellis_quant ) vips_warn( "vips2jpeg", "%s", _( "Ignoring trellis_quant" ) ); if( overshoot_deringing ) vips_warn( "vips2jpeg", "%s", _( "Ignoring overshoot_deringing" ) ); if( optimize_scans ) vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) ); if( quant_table > 0 ) vips_warn( "vips2jpeg", "%s", _( "Ignoring quant_table" ) ); #endif /* Set compression quality. Must be called after setting params above. */ jpeg_set_quality( &write->cinfo, qfac, TRUE ); /* Enable progressive write. */ if( progressive ) jpeg_simple_progression( &write->cinfo ); /* Turn off chroma subsampling. Follow IM and do it automatically for * high Q. */ if( no_subsample || qfac > 90 ) { int i; for( i = 0; i < in->Bands; i++ ) { write->cinfo.comp_info[i].h_samp_factor = 1; write->cinfo.comp_info[i].v_samp_factor = 1; } } /* Don't write the APP0 JFIF headers if we are stripping. */ if( strip ) write->cinfo.write_JFIF_header = FALSE; /* Build compress tables. */ jpeg_start_compress( &write->cinfo, TRUE ); /* Write any APP markers we need. */ if( !strip ) { if( write_exif( write ) || write_blob( write, VIPS_META_XMP_NAME, JPEG_APP0 + 1 ) || write_blob( write, VIPS_META_IPCT_NAME, JPEG_APP0 + 13 ) ) return( -1 ); /* A profile supplied as an argument overrides an embedded * profile. "none" means don't attach a profile. */ if( profile && strcmp( profile, "none" ) != 0 && write_profile_file( write, profile ) ) return( -1 ); if( !profile && vips_image_get_typeof( in, VIPS_META_ICC_NAME ) && write_profile_meta( write ) ) return( -1 ); } /* Write data. Note that the write function grabs the longjmp()! */ if( vips_sink_disc( in, write_jpeg_block, write ) ) 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 ); }