static void * print_field_fn( VipsImage *image, const char *field, GValue *value, void *a ) { gboolean *many = (gboolean *) a; const char *extra; char *str_value; /* Look for known enums and decode them. */ extra = NULL; if( strcmp( field, "coding" ) == 0 ) extra = vips_enum_nick( VIPS_TYPE_CODING, g_value_get_int( value ) ); else if( strcmp( field, "format" ) == 0 ) extra = vips_enum_nick( VIPS_TYPE_BAND_FORMAT, g_value_get_int( value ) ); else if( strcmp( field, "interpretation" ) == 0 ) extra = vips_enum_nick( VIPS_TYPE_INTERPRETATION, g_value_get_int( value ) ); if( *many ) printf( "%s: ", image->filename ); str_value = g_strdup_value_contents( value ); printf( "%s: %s", field, str_value ); g_free( str_value ); if( extra ) printf( " - %s", extra ); printf( "\n" ); return( NULL ); }
/* We expect a certain token. */ const char * vips__token_need( const char *p, VipsToken need_token, char *string, int size ) { VipsToken token; if( !(p = vips__token_must( p, &token, string, size )) ) return( NULL ); if( token != need_token ) { vips_error( "get_token", _( "expected %s, saw %s" ), vips_enum_nick( VIPS_TYPE_TOKEN, need_token ), vips_enum_nick( VIPS_TYPE_TOKEN, token ) ); return( NULL ); } return( p ); }
/** * vips_check_coding: * @domain: the originating domain for the error message * @im: image to check * @coding: required coding * * Check that the image has the required @coding. * If not, set an error message * and return non-zero. * * See also: vips_error(). * * Returns: 0 on OK, or -1 on error. */ int vips_check_coding( const char *domain, VipsImage *im, VipsCoding coding ) { if( im->Coding != coding ) { vips_error( domain, _( "coding '%s' only" ), vips_enum_nick( VIPS_TYPE_CODING, coding ) ); return( -1 ); } return( 0 ); }
static void * imageheader_add_item( IMAGE *im, const char *field, GValue *value, Imageheader *imageheader ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); GtkTreeIter iter; char *value_str; const char *extra; value_str = g_strdup_value_contents( value ); vips_buf_appendf( &buf, "%s", value_str ); /* Look for enums and decode them. */ extra = NULL; if( strcmp( field, "coding" ) == 0 ) extra = vips_enum_nick( VIPS_TYPE_CODING, g_value_get_int( value ) ); else if( strcmp( field, "format" ) == 0 ) extra = vips_enum_nick( VIPS_TYPE_BAND_FORMAT, g_value_get_int( value ) ); else if( strcmp( field, "interpretation" ) == 0 ) extra = vips_enum_nick( VIPS_TYPE_INTERPRETATION, g_value_get_int( value ) ); if( extra ) vips_buf_appendf( &buf, " - %s", extra ); gtk_list_store_append( imageheader->store, &iter ); gtk_list_store_set( imageheader->store, &iter, NAME_COLUMN, field, VALUE_COLUMN, vips_buf_all( &buf ), -1 ); g_free( value_str ); return( NULL ); }
/* Auto-rotate, if rotate_image is set. */ static VipsImage * thumbnail_rotate( VipsObject *process, VipsImage *im ) { VipsImage **t = (VipsImage **) vips_object_local_array( process, 2 ); VipsAngle angle = vips_autorot_get_angle( im ); if( rotate_image && angle != VIPS_ANGLE_D0 ) { vips_info( "vipsthumbnail", "rotating by %s", vips_enum_nick( VIPS_TYPE_ANGLE, angle ) ); /* Need to copy to memory, we have to stay seq. */ t[0] = vips_image_new_memory(); if( vips_image_write( im, t[0] ) || vips_rot( t[0], &t[1], angle, NULL ) ) return( NULL ); im = t[1]; vips_autorot_remove_angle( im ); } return( im ); }
static int vips_colourspace_build( VipsObject *object ) { VipsColourspace *colourspace = (VipsColourspace *) object; int i, j; VipsImage *x; VipsImage **t = (VipsImage **) vips_object_local_array( object, 1 ); VipsImage **pipe = (VipsImage **) vips_object_local_array( object, MAX_STEPS ); VipsInterpretation interpretation; /* Verify that all input args have been set. */ if( VIPS_OBJECT_CLASS( vips_colourspace_parent_class )-> build( object ) ) return( -1 ); x = colourspace->in; /* Unpack radiance-coded images. We can't use interpretation for this, * since rad images can be scRGB or XYZ. */ if( x->Coding == VIPS_CODING_RAD ) { if( vips_rad2float( x, &t[0], NULL ) ) return( -1 ); x = t[0]; } if( vips_object_argument_isset( object, "source_space" ) ) interpretation = colourspace->source_space; else interpretation = vips_image_guess_interpretation( x ); /* Treat RGB as sRGB. If you want some other treatment, * you'll need to use the icc funcs. */ if( interpretation == VIPS_INTERPRETATION_RGB ) interpretation = VIPS_INTERPRETATION_sRGB; /* No conversion necessary. */ if( interpretation == colourspace->space ) { g_object_set( colourspace, "out", vips_image_new(), NULL ); return( vips_image_write( colourspace->in, colourspace->out ) ); } for( i = 0; i < VIPS_NUMBER( vips_colour_routes ); i++ ) if( vips_colour_routes[i].from == interpretation && vips_colour_routes[i].to == colourspace->space ) break; if( i == VIPS_NUMBER( vips_colour_routes ) ) { vips_error( "vips_colourspace", _( "no known route between '%s' and '%s'" ), vips_enum_nick( VIPS_TYPE_INTERPRETATION, interpretation ), vips_enum_nick( VIPS_TYPE_INTERPRETATION, colourspace->space ) ); return( -1 ); } for( j = 0; vips_colour_routes[i].route[j]; j++ ) { if( vips_colour_routes[i].route[j]( x, &pipe[j], NULL ) ) return( -1 ); x = pipe[j]; } g_object_set( colourspace, "out", vips_image_new(), NULL ); if( vips_image_write( x, colourspace->out ) ) return( -1 ); return( 0 ); }
static VipsImage * thumbnail_shrink( VipsObject *process, VipsImage *in, VipsInterpolate *interp, VipsImage *sharpen ) { VipsImage **t = (VipsImage **) vips_object_local_array( process, 10 ); VipsInterpretation interpretation = linear_processing ? VIPS_INTERPRETATION_XYZ : VIPS_INTERPRETATION_sRGB; int shrink; double residual; int tile_width; int tile_height; int nlines; /* 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]; } /* In linear mode, we import right at the start. * * 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. */ if( linear_processing && 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]; } /* 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]; shrink = calculate_shrink( in, &residual ); vips_info( "vipsthumbnail", "integer shrink by %d", shrink ); if( vips_shrink( in, &t[3], shrink, shrink, NULL ) ) return( NULL ); in = t[3]; /* We want to make sure we read the image sequentially. * However, the convolution we may be doing later will force us * into SMALLTILE or maybe FATSTRIP mode and that will break * sequentiality. * * So ... read into a cache where tiles are scanlines, and make sure * we keep enough scanlines to be able to serve a line of tiles. * * We use a threaded tilecache to avoid a deadlock: suppose thread1, * evaluating the top block of the output, is delayed, and thread2, * evaluating the second block, gets here first (this can happen on * a heavily-loaded system). * * With an unthreaded tilecache (as we had before), thread2 will get * the cache lock and start evaling the second block of the shrink. * When it reaches the png reader it will stall until the first block * has been used ... but it never will, since thread1 will block on * this cache lock. */ vips_get_tile_size( in, &tile_width, &tile_height, &nlines ); if( vips_tilecache( in, &t[4], "tile_width", in->Xsize, "tile_height", 10, "max_tiles", (nlines * 2) / 10, "access", VIPS_ACCESS_SEQUENTIAL, "threaded", TRUE, NULL ) || vips_affine( t[4], &t[5], residual, 0, 0, residual, "interpolate", interp, NULL ) ) return( NULL ); in = t[5]; vips_info( "vipsthumbnail", "residual scale by %g", residual ); vips_info( "vipsthumbnail", "%s interpolation", VIPS_OBJECT_GET_CLASS( interp )->nickname ); /* Colour management. * * In linear mode, just export. In device space mode, do a combined * import/export to transform to the target space. */ if( linear_processing ) { 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[6], VIPS_INTERPRETATION_sRGB, NULL ) ) return( NULL ); in = t[6]; } } else if( export_profile && (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 ); vips_info( "vipsthumbnail", "exporting with profile %s", export_profile ); if( vips_icc_transform( in, &t[6], export_profile, "input_profile", import_profile, "embedded", TRUE, NULL ) ) return( NULL ); in = t[6]; } /* If we are upsampling, don't sharpen, since nearest looks dumb * sharpened. */ if( shrink >= 1 && residual <= 1.0 && sharpen ) { vips_info( "vipsthumbnail", "sharpening thumbnail" ); if( vips_conv( in, &t[8], sharpen, NULL ) ) return( NULL ); in = t[8]; } 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 ); }
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 ); }