From 28999aa5b64a6e46ea5d12f6ad728daa033f9554 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 10 Jan 2019 22:48:21 +0000 Subject: [PATCH] add vips_profile_load() and use it in icc_transform --- ChangeLog | 1 + libvips/colour/CMYK2XYZ.c | 95 +------------ libvips/colour/Makefile.am | 9 +- libvips/colour/XYZ2CMYK.c | 2 +- libvips/colour/colour.c | 2 + libvips/colour/icc_transform.c | 52 +++---- libvips/colour/pcolour.h | 3 - libvips/colour/profile_load.c | 245 ++++++++++++++++++++++++++++++++ libvips/include/vips/colour.h | 2 + libvips/include/vips/internal.h | 2 + libvips/iofuncs/type.c | 3 + 11 files changed, 277 insertions(+), 139 deletions(-) create mode 100644 libvips/colour/profile_load.c diff --git a/ChangeLog b/ChangeLog index 31c82fe1..54c8d71c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,7 @@ - dzsave has a new skip_blanks option - add vips_CMYK2XYZ() and vips_XYZ2CMYK(), plus associated routes - include cmyk and srgb fallback profiles +- add vips_profile_load() 4/1/19 started 8.7.4 - fix memory leak in magickload [kleisauke] diff --git a/libvips/colour/CMYK2XYZ.c b/libvips/colour/CMYK2XYZ.c index 5febc065..20d6c9ed 100644 --- a/libvips/colour/CMYK2XYZ.c +++ b/libvips/colour/CMYK2XYZ.c @@ -60,99 +60,6 @@ typedef VipsColourCodeClass VipsCMYK2XYZClass; G_DEFINE_TYPE( VipsCMYK2XYZ, vips_CMYK2XYZ, VIPS_TYPE_OPERATION ); -/* Created on first use from a base64 string in profiles.c. - */ -typedef struct _VipsFallbackProfile { - const char *name; - void *data; - size_t data_length; -} VipsFallbackProfile; - -static GSList *vips_fallback_profile_list = NULL; - -static void * -vips__fallback_profile_get_init( void ) -{ - int i; - - for( i = 0; vips__coded_profiles[i].name; i++ ) { - size_t data_length; - unsigned char *data; - VipsFallbackProfile *fallback; - - if( !(data = vips__b64_decode( - vips__coded_profiles[i].data, &data_length )) ) - return( NULL ); - fallback = g_new( VipsFallbackProfile,1 ); - fallback->name = vips__coded_profiles[i].name; - fallback->data = data; - fallback->data_length = data_length; - vips_fallback_profile_list = g_slist_prepend( - vips_fallback_profile_list, fallback ); - } - - return( NULL ); -} - -/* Shared with icc_transform.c - */ -void * -vips__fallback_profile_get( const char *name, size_t *length ) -{ - GOnce once = G_ONCE_INIT; - - GSList *p; - - VIPS_ONCE( &once, (GThreadFunc) vips__fallback_profile_get_init, NULL ); - - for( p = vips_fallback_profile_list; p; p = p->next ) { - VipsFallbackProfile *fallback = (VipsFallbackProfile *) p->data; - - if( strcasecmp( fallback->name, name ) == 0 ) { - *length = fallback->data_length; - - return( fallback->data ); - } - } - - return( NULL ); -} - -/* Shared with XYZ2CMYK.c. - */ -int -vips__fallback_profile_set( const char *name, VipsImage *image ) -{ - size_t data_length; - unsigned char *data; - - /* Things like jpegsave let you save with no profile by setting - * "none" as a profile name. - */ - if( strcmp( name, "none" ) == 0 ) { - vips_image_remove( image, VIPS_META_ICC_NAME ); - - return( 0 ); - } - - /* Already a profile? Do nothing. We could remove and replace non-CMYK - * profiles I guess. - */ - if( vips_image_get_typeof( image, VIPS_META_ICC_NAME ) ) - return( 0 ); - - if( !(data = vips__fallback_profile_get( name, &data_length )) ) { - vips_error( "fallback", - _( "unknown fallback profile \"%s\"" ), name ); - return( -1 ); - } - - vips_image_set_blob( image, VIPS_META_ICC_NAME, - NULL, data, data_length ); - - return( 0 ); -} - /* Our actual processing, as a VipsColourTransformFn. */ static int @@ -179,7 +86,7 @@ vips_CMYK2XYZ_build( VipsObject *object ) g_object_set( object, "out", out, NULL ); if( vips_copy( CMYK2XYZ->in, &t[0], NULL ) || - vips__fallback_profile_set( "cmyk", t[0] ) || + vips__profile_set( t[0], "cmyk" ) || vips__colourspace_process_n( "CMYK2XYZ", t[0], &t[1], 4, vips_CMYK2XYZ_process ) || vips_image_write( t[1], out ) ) diff --git a/libvips/colour/Makefile.am b/libvips/colour/Makefile.am index b606e932..0df8f7fc 100644 --- a/libvips/colour/Makefile.am +++ b/libvips/colour/Makefile.am @@ -1,14 +1,9 @@ noinst_LTLIBRARIES = libcolour.la -if ENABLE_LCMS -PROFILES = profiles.c -else -PROFILES = -endif - libcolour_la_SOURCES = \ - $(PROFILES) \ + profiles.c \ profiles.h \ + profile_load.c \ colour.c \ pcolour.h \ CMYK2XYZ.c \ diff --git a/libvips/colour/XYZ2CMYK.c b/libvips/colour/XYZ2CMYK.c index 21b7c968..e551d65d 100644 --- a/libvips/colour/XYZ2CMYK.c +++ b/libvips/colour/XYZ2CMYK.c @@ -85,7 +85,7 @@ vips_XYZ2CMYK_build( VipsObject *object ) g_object_set( object, "out", out, NULL ); if( vips_copy( XYZ2CMYK->in, &t[0], NULL ) || - vips__fallback_profile_set( "cmyk", t[0] ) || + vips__profile_set( t[0], "cmyk" ) || vips__colourspace_process_n( "XYZ2CMYK", t[0], &t[1], 3, vips_XYZ2CMYK_process ) || vips_image_write( t[1], out ) ) diff --git a/libvips/colour/colour.c b/libvips/colour/colour.c index d8f7ac1c..8bcd816b 100644 --- a/libvips/colour/colour.c +++ b/libvips/colour/colour.c @@ -752,6 +752,7 @@ vips_colour_operation_init( void ) extern GType vips_scRGB2BW_get_type( void ); extern GType vips_XYZ2scRGB_get_type( void ); extern GType vips_scRGB2sRGB_get_type( void ); + extern GType vips_profile_load_get_type( void ); #ifdef HAVE_LCMS2 extern GType vips_icc_import_get_type( void ); extern GType vips_icc_export_get_type( void ); @@ -788,6 +789,7 @@ vips_colour_operation_init( void ) vips_HSV2sRGB_get_type(); vips_XYZ2scRGB_get_type(); vips_scRGB2sRGB_get_type(); + vips_profile_load_get_type(); #ifdef HAVE_LCMS2 vips_icc_import_get_type(); vips_icc_export_get_type(); diff --git a/libvips/colour/icc_transform.c b/libvips/colour/icc_transform.c index 7263c091..8b219cba 100644 --- a/libvips/colour/icc_transform.c +++ b/libvips/colour/icc_transform.c @@ -593,28 +593,6 @@ vips_image_expected_sig( VipsImage *image ) return( expected_sig ); } -/* Get from a filename, including loading a fallback. - */ -static VipsBlob * -vips_icc_get_profile_file( const char *filename ) -{ - void *data; - size_t size; - - if( (data = vips__fallback_profile_get( filename, &size )) ) - /* We have a fallback profile of this name. - */ - return( vips_blob_new( NULL, data, size ) ); - else if( (data = vips__file_read_name( filename, - vips__icc_dir(), &size )) ) - /* Load from the named file. - */ - return( vips_blob_new( - (VipsCallbackFn) vips_free, data, size ) ); - else - return( NULL ); -} - /* Get from an image. */ static VipsBlob * @@ -690,8 +668,9 @@ vips_icc_import_build( VipsObject *object ) if( !icc->in_blob && import->input_profile_filename ) { - icc->in_blob = vips_icc_get_profile_file( - import->input_profile_filename ); + if( vips_profile_load( import->input_profile_filename, + &icc->in_blob, NULL ) ) + return( -1 ); import->used_fallback = TRUE; } @@ -886,19 +865,22 @@ vips_icc_export_build( VipsObject *object ) if( !icc->out_blob && export->output_profile_filename ) { - icc->out_blob = vips_icc_get_profile_file( - export->output_profile_filename ); + if( vips_profile_load( export->output_profile_filename, + &icc->out_blob, NULL ) ) + return( -1 ); colour->profile_filename = export->output_profile_filename; } - if( !(icc->out_profile = - vips_icc_load_profile_blob( icc->out_blob, NULL )) ) { + if( icc->out_blob && + !(icc->out_profile = + vips_icc_load_profile_blob( icc->out_blob, NULL )) ) { vips_error( class->nickname, "%s", _( "no output profile" ) ); return( -1 ); } - vips_check_intent( class->nickname, - icc->out_profile, icc->intent, LCMS_USED_AS_OUTPUT ); + if( icc->out_profile ) + vips_check_intent( class->nickname, + icc->out_profile, icc->intent, LCMS_USED_AS_OUTPUT ); if( VIPS_OBJECT_CLASS( vips_icc_export_parent_class )->build( object ) ) return( -1 ); @@ -1089,8 +1071,9 @@ vips_icc_transform_build( VipsObject *object ) if( !icc->in_blob && transform->input_profile_filename ) - icc->in_blob = vips_icc_get_profile_file( - transform->input_profile_filename ); + if( vips_profile_load( transform->input_profile_filename, + &icc->in_blob, NULL ) ) + return( -1 ); if( icc->in_blob && code->in ) @@ -1103,8 +1086,9 @@ vips_icc_transform_build( VipsObject *object ) } if( transform->output_profile_filename ) { - icc->out_blob = vips_icc_get_profile_file( - transform->output_profile_filename ); + if( vips_profile_load( transform->output_profile_filename, + &icc->out_blob, NULL ) ) + return( -1 ); colour->profile_filename = transform->output_profile_filename; } diff --git a/libvips/colour/pcolour.h b/libvips/colour/pcolour.h index 475c3bb4..e9a028aa 100644 --- a/libvips/colour/pcolour.h +++ b/libvips/colour/pcolour.h @@ -216,9 +216,6 @@ extern float vips_v2Y_16[65536]; void vips_col_make_tables_RGB_8( void ); void vips_col_make_tables_RGB_16( void ); -void *vips__fallback_profile_get( const char *name, size_t *length ); -int vips__fallback_profile_set( const char *name, VipsImage *image ); - /* A colour-transforming function. */ typedef int (*VipsColourTransformFn)( VipsImage *in, VipsImage **out, ... ); diff --git a/libvips/colour/profile_load.c b/libvips/colour/profile_load.c new file mode 100644 index 00000000..80c5c193 --- /dev/null +++ b/libvips/colour/profile_load.c @@ -0,0 +1,245 @@ +/* Load profiles as blobs. + * + * 10/1/19 + * - from CMYK2XYZ.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#include + +#include "profiles.h" +#include "pcolour.h" + +typedef struct _VipsProfileLoad { + VipsOperation parent_instance; + + const char *name; + VipsBlob *profile; + +} VipsProfileLoad; + +typedef VipsOperationClass VipsProfileLoadClass; + +G_DEFINE_TYPE( VipsProfileLoad, vips_profile_load, VIPS_TYPE_OPERATION ); + +/* Created on first use from a base64 string in profiles.c. + */ +typedef struct _VipsFallbackProfile { + const char *name; + void *data; + size_t data_length; +} VipsFallbackProfile; + +static GSList *vips_fallback_profile_list = NULL; + +static void * +vips_fallback_profile_get_init( void ) +{ + int i; + + for( i = 0; vips__coded_profiles[i].name; i++ ) { + size_t data_length; + unsigned char *data; + VipsFallbackProfile *fallback; + + if( !(data = vips__b64_decode( + vips__coded_profiles[i].data, &data_length )) ) + return( NULL ); + fallback = g_new( VipsFallbackProfile,1 ); + fallback->name = vips__coded_profiles[i].name; + fallback->data = data; + fallback->data_length = data_length; + vips_fallback_profile_list = g_slist_prepend( + vips_fallback_profile_list, fallback ); + } + + return( NULL ); +} + +static void * +vips_fallback_profile_get( const char *name, size_t *length ) +{ + GOnce once = G_ONCE_INIT; + + GSList *p; + + VIPS_ONCE( &once, (GThreadFunc) vips_fallback_profile_get_init, NULL ); + + for( p = vips_fallback_profile_list; p; p = p->next ) { + VipsFallbackProfile *fallback = (VipsFallbackProfile *) p->data; + + if( strcasecmp( fallback->name, name ) == 0 ) { + *length = fallback->data_length; + + return( fallback->data ); + } + } + + return( NULL ); +} + +static int +vips_profile_load_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsProfileLoad *load = (VipsProfileLoad *) object; + + size_t length; + const void *data; + VipsBlob *profile; + + if( VIPS_OBJECT_CLASS( vips_profile_load_parent_class )-> + build( object ) ) + return( -1 ); + + if( strcasecmp( load->name, "none" ) == 0 ) { + profile = NULL; + } + else if( (data = vips_fallback_profile_get( load->name, &length )) ) { + profile = vips_blob_new( NULL, data, length ); + } + else if( (data = vips__file_read_name( load->name, + vips__icc_dir(), &length )) ) { + profile = vips_blob_new( NULL, data, length ); + } + else { + vips_error( class->nickname, + _( "unable to load profile \"%s\"" ), load->name ); + return( -1 ); + } + + g_object_set( object, "profile", profile, NULL ); + + if( profile ) { + vips_area_unref( (VipsArea *) profile ); + profile = NULL; + } + + return( 0 ); +} + +static void +vips_profile_load_class_init( VipsProfileLoadClass *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 = "profile_load"; + object_class->description = _( "load named ICC profile" ); + object_class->build = vips_profile_load_build; + + VIPS_ARG_STRING( class, "name", 1, + _( "Name" ), + _( "Profile name" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsProfileLoad, name ), + NULL ); + + VIPS_ARG_BOXED( class, "profile", 2, + _( "Profile" ), + _( "Loaded profile" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsProfileLoad, profile ), + VIPS_TYPE_BLOB ); + +} + +static void +vips_profile_load_init( VipsProfileLoad *load ) +{ +} + +/** + * vips_profile_load: + * @name: name of profile to load + * @profile: (out): loaded profile + * @...: %NULL-terminated list of optional named arguments + * + * Load a named profile. If the name is one of the built in ICC profiles, then + * that is returmed, otherwise a profile is loaded from the system profile + * area. + * + * The special name "none" will make this operation return NULL for @profile. + * + * Returns: 0 on success, -1 on error + */ +int +vips_profile_load( const char *name, VipsBlob **profile, ... ) +{ + va_list ap; + int result; + + va_start( ap, profile ); + result = vips_call_split( "profile_load", ap, name, profile ); + va_end( ap ); + + return( result ); +} + +/* Set (or remove) a named profile on an image. + */ +int +vips__profile_set( VipsImage *image, const char *name ) +{ + VipsBlob *profile; + void *data; + size_t length; + + if( vips_profile_load( name, &profile, NULL ) ) + return( -1 ); + + if( profile ) { + data = ((VipsArea *) profile)->data; + length = ((VipsArea *) profile)->length; + vips_image_set_blob( image, VIPS_META_ICC_NAME, + (VipsCallbackFn) NULL, data, length ); + } + else + vips_image_remove( image, VIPS_META_ICC_NAME ); + + if( profile ) { + vips_area_unref( (VipsArea *) profile ); + profile = NULL; + } + + return( 0 ); +} diff --git a/libvips/include/vips/colour.h b/libvips/include/vips/colour.h index 7f4041e1..894f08d5 100644 --- a/libvips/include/vips/colour.h +++ b/libvips/include/vips/colour.h @@ -172,6 +172,8 @@ int vips_CMYK2XYZ( VipsImage *in, VipsImage **out, ... ) int vips_XYZ2CMYK( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_profile_load( const char *name, VipsBlob **profile, ... ) + __attribute__((sentinel)); int vips_icc_present( void ); int vips_icc_transform( VipsImage *in, VipsImage **out, const char *output_profile, ... ) diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index 9365efbd..62ec1319 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -269,6 +269,8 @@ void vips__reorder_clear( VipsImage *image ); VipsWindow *vips_window_take( VipsWindow *window, VipsImage *im, int top, int height ); +int vips__profile_set( VipsImage *image, const char *name ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/type.c b/libvips/iofuncs/type.c index f1542bb0..d59f4b56 100644 --- a/libvips/iofuncs/type.c +++ b/libvips/iofuncs/type.c @@ -376,6 +376,9 @@ void * vips_area_get_data( VipsArea *area, size_t *length, int *n, GType *type, size_t *sizeof_type ) { + if( !area ) + return( NULL ); + if( length ) *length = area->length; if( n )