1 /* Transform images with little cms
2  *
3  * 26/4/02 JC
4  * 26/8/05
5  * 	- attach profiles and intents to output images
6  * 	- added im_icc_import_embedded() to import with an embedded profile
7  * 12/5/06
8  * 	- lock around cmsDoTransform
9  * 23/1/07
10  * 	- set RGB16 on 16-bit RGB export
11  * 6/4/09
12  * 	- catch lcms error messages
13  * 2/11/09
14  * 	- gtkdoc
15  * 	- small cleanups
16  * 	- call attach_profile() before im_wrapone() so the profile will get
17  * 	  written if we are wrinting to a file
18  * 2/8/10
19  * 	- add lcms2
20  * 12/7/11
21  * 	- import and export cast @in to an appropriate format for you
22  * 25/9/12
23  * 	- redo as a class
24  * 14/5/13
25  * 	- import and export would segv on very wide images
26  * 12/11/13
27  * 	- support XYZ as an alternative PCS
28  * 10/9/14
29  * 	- support GRAY as an input and output space
30  * 29/9/14
31  * 	- check input profiles for compatibility with the input image, thanks
32  * 	  James
33  * 26/6/15
34  *	- better profile sanity checking for icc import
35  * 2/8/17
36  * 	- remove lcms1 support, it was untested
37  * 10/10/17
38  * 	- more input profile sanity tests
39  * 8/3/18
40  * 	- attach fallback profile on import if we used it
41  * 28/12/18
42  * 	- remove warning messages from vips_icc_is_compatible_profile() since
43  * 	  they can be triggered under normal circumstances
44  * 17/4/19 kleisauke
45  * 	- better rejection of broken embedded profiles
46  * 29/3/21 [hanssonrickard]
47  * 	- add black_point_compensation
48  */
49 
50 /*
51 
52     This file is part of VIPS.
53 
54     VIPS is free software; you can redistribute it and/or modify
55     it under the terms of the GNU Lesser General Public License as published by
56     the Free Software Foundation; either version 2 of the License, or
57     (at your option) any later version.
58 
59     This program is distributed in the hope that it will be useful,
60     but WITHOUT ANY WARRANTY; without even the implied warranty of
61     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
62     GNU Lesser General Public License for more details.
63 
64     You should have received a copy of the GNU Lesser General Public License
65     along with this program; if not, write to the Free Software
66     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
67     02110-1301  USA
68 
69  */
70 
71 /*
72 
73     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
74 
75  */
76 
77 /*
78 #define DEBUG
79  */
80 
81 #ifdef HAVE_CONFIG_H
82 #include <config.h>
83 #endif /*HAVE_CONFIG_H*/
84 #include <vips/intl.h>
85 
86 #ifdef HAVE_LCMS2
87 
88 #include <stdio.h>
89 #include <math.h>
90 
91 /* Has to be before VIPS to avoid nameclashes.
92  */
93 #include <lcms2.h>
94 
95 #include <vips/vips.h>
96 
97 #include "pcolour.h"
98 
99 /* Call lcms with up to this many pixels at once.
100  */
101 #define PIXEL_BUFFER_SIZE (10000)
102 
103 /**
104  * VipsIntent:
105  * @VIPS_INTENT_PERCEPTUAL: perceptual rendering intent
106  * @VIPS_INTENT_RELATIVE: relative colorimetric rendering intent
107  * @VIPS_INTENT_SATURATION: saturation rendering intent
108  * @VIPS_INTENT_ABSOLUTE: absolute colorimetric rendering intent
109  *
110  * The rendering intent. #VIPS_INTENT_ABSOLUTE is best for
111  * scientific work, #VIPS_INTENT_RELATIVE is usually best for
112  * accurate communication with other imaging libraries.
113  */
114 
115 /**
116  * VipsPCS:
117  * @VIPS_PCS_LAB: use CIELAB D65 as the Profile Connection Space
118  * @VIPS_PCS_XYZ: use XYZ as the Profile Connection Space
119  *
120  * Pick a Profile Connection Space for vips_icc_import() and
121  * vips_icc_export(). LAB is usually best, XYZ can be more convenient in some
122  * cases.
123  */
124 
125 /**
126  * vips_icc_present:
127  *
128  * VIPS can optionally be built without the ICC library. Use this function to
129  * test for its availability.
130  *
131  * Returns: non-zero if the ICC library is present.
132  */
133 int
vips_icc_present(void)134 vips_icc_present( void )
135 {
136 	return( 1 );
137 }
138 
139 #define VIPS_TYPE_ICC (vips_icc_get_type())
140 #define VIPS_ICC( obj ) \
141 	(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
142 		VIPS_TYPE_ICC, VipsIcc ))
143 #define VIPS_ICC_CLASS( klass ) \
144 	(G_TYPE_CHECK_CLASS_CAST( (klass), \
145 		VIPS_TYPE_ICC, VipsIccClass))
146 #define VIPS_IS_ICC( obj ) \
147 	(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_ICC ))
148 #define VIPS_IS_ICC_CLASS( klass ) \
149 	(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_ICC ))
150 #define VIPS_ICC_GET_CLASS( obj ) \
151 	(G_TYPE_INSTANCE_GET_CLASS( (obj), \
152 		VIPS_TYPE_ICC, VipsIccClass ))
153 
154 typedef struct _VipsIcc {
155 	VipsColourCode parent_instance;
156 
157 	VipsIntent intent;
158 	VipsPCS pcs;
159 	int depth;
160 	gboolean black_point_compensation;
161 
162 	VipsBlob *in_blob;
163 	cmsHPROFILE in_profile;
164 	VipsBlob *out_blob;
165 	cmsHPROFILE out_profile;
166 	cmsUInt32Number in_icc_format;
167 	cmsUInt32Number out_icc_format;
168 	cmsHTRANSFORM trans;
169 	gboolean non_standard_input_profile;
170 } VipsIcc;
171 
172 typedef VipsColourCodeClass VipsIccClass;
173 
174 G_DEFINE_ABSTRACT_TYPE( VipsIcc, vips_icc, VIPS_TYPE_COLOUR_CODE );
175 
176 /* Error from lcms.
177  */
178 
179 static void
icc_error(cmsContext context,cmsUInt32Number code,const char * text)180 icc_error( cmsContext context, cmsUInt32Number code, const char *text )
181 {
182 	vips_error( "VipsIcc", "%s", text );
183 }
184 
185 static void
vips_icc_dispose(GObject * gobject)186 vips_icc_dispose( GObject *gobject )
187 {
188 	VipsIcc *icc = (VipsIcc *) gobject;
189 
190 	VIPS_FREEF( cmsDeleteTransform, icc->trans );
191 	VIPS_FREEF( cmsCloseProfile, icc->in_profile );
192 	VIPS_FREEF( cmsCloseProfile, icc->out_profile );
193 
194 	if( icc->in_blob ) {
195 		vips_area_unref( (VipsArea *) icc->in_blob );
196 		icc->in_blob = NULL;
197 	}
198 
199 	if( icc->out_blob ) {
200 		vips_area_unref( (VipsArea *) icc->out_blob );
201 		icc->out_blob = NULL;
202 	}
203 
204 	G_OBJECT_CLASS( vips_icc_parent_class )->dispose( gobject );
205 }
206 
207 /* Is a profile just a pcs stub.
208  */
209 static gboolean
is_pcs(cmsHPROFILE profile)210 is_pcs( cmsHPROFILE profile )
211 {
212 	return( cmsGetColorSpace( profile ) == cmsSigLabData ||
213 		cmsGetColorSpace( profile ) == cmsSigXYZData );
214 }
215 
216 static int
vips_icc_build(VipsObject * object)217 vips_icc_build( VipsObject *object )
218 {
219 	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
220 	VipsColour *colour = (VipsColour *) object;
221 	VipsColourCode *code = (VipsColourCode *) object;
222 	VipsIcc *icc = (VipsIcc *) object;
223 
224 	cmsUInt32Number flags;
225 
226 	if( icc->depth != 8 &&
227 		icc->depth != 16 ) {
228 		vips_error( class->nickname,
229 			"%s", _( "depth must be 8 or 16" ) );
230 		return( -1 );
231 	}
232 
233 	if( icc->in_profile &&
234 		code->in ) {
235 		switch( cmsGetColorSpace( icc->in_profile ) ) {
236 		case cmsSigRgbData:
237 			colour->input_bands = 3;
238 			code->input_format =
239 				code->in->BandFmt == VIPS_FORMAT_USHORT ?
240 				VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR;
241 			icc->in_icc_format =
242 				code->in->BandFmt == VIPS_FORMAT_USHORT ?
243 				TYPE_RGB_16 : TYPE_RGB_8;
244 			break;
245 
246 		case cmsSigGrayData:
247 			colour->input_bands = 1;
248 			code->input_format =
249 				code->in->BandFmt == VIPS_FORMAT_USHORT ?
250 				VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR;
251 			icc->in_icc_format =
252 				code->in->BandFmt == VIPS_FORMAT_USHORT ?
253 				TYPE_GRAY_16 : TYPE_GRAY_8;
254 			break;
255 
256 		case cmsSigCmykData:
257 			colour->input_bands = 4;
258 			code->input_format =
259 				code->in->BandFmt == VIPS_FORMAT_USHORT ?
260 				VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR;
261 			icc->in_icc_format =
262 				code->in->BandFmt == VIPS_FORMAT_USHORT ?
263 				TYPE_CMYK_16 : TYPE_CMYK_8;
264 			break;
265 
266 		case cmsSigLabData:
267 			colour->input_bands = 3;
268 			code->input_format = VIPS_FORMAT_FLOAT;
269 			code->input_interpretation =
270 				VIPS_INTERPRETATION_LAB;
271 			icc->in_icc_format = TYPE_Lab_16;
272 			break;
273 
274 		case cmsSigXYZData:
275 			colour->input_bands = 3;
276 			code->input_format = VIPS_FORMAT_FLOAT;
277 			icc->in_icc_format = TYPE_XYZ_16;
278 			break;
279 
280 		default:
281 			vips_error( class->nickname,
282 				_( "unimplemented input color space 0x%x" ),
283 				cmsGetColorSpace( icc->in_profile ) );
284 			return( -1 );
285 		}
286 	}
287 
288 	if( icc->out_profile )
289 		switch( cmsGetColorSpace( icc->out_profile ) ) {
290 		case cmsSigRgbData:
291 			colour->interpretation =
292 				icc->depth == 8 ?
293 				VIPS_INTERPRETATION_sRGB :
294 					VIPS_INTERPRETATION_RGB16;
295 			colour->format =
296 				icc->depth == 8 ?
297 				VIPS_FORMAT_UCHAR : VIPS_FORMAT_USHORT;
298 			colour->bands = 3;
299 			icc->out_icc_format =
300 				icc->depth == 16 ?
301 				TYPE_RGB_16 : TYPE_RGB_8;
302 			break;
303 
304 		case cmsSigGrayData:
305 			colour->interpretation =
306 				icc->depth == 8 ?
307 				VIPS_INTERPRETATION_B_W :
308 					VIPS_INTERPRETATION_GREY16;
309 			colour->format =
310 				icc->depth == 8 ?
311 				VIPS_FORMAT_UCHAR : VIPS_FORMAT_USHORT;
312 			colour->bands = 1;
313 			icc->out_icc_format =
314 				icc->depth == 16 ?
315 				TYPE_GRAY_16 : TYPE_GRAY_8;
316 			break;
317 
318 		case cmsSigCmykData:
319 			colour->interpretation = VIPS_INTERPRETATION_CMYK;
320 			colour->format =
321 				icc->depth == 8 ?
322 				VIPS_FORMAT_UCHAR : VIPS_FORMAT_USHORT;
323 			colour->bands = 4;
324 			icc->out_icc_format =
325 				icc->depth == 16 ?
326 				TYPE_CMYK_16 : TYPE_CMYK_8;
327 			break;
328 
329 		case cmsSigLabData:
330 			colour->interpretation = VIPS_INTERPRETATION_LAB;
331 			colour->format = VIPS_FORMAT_FLOAT;
332 			colour->bands = 3;
333 			icc->out_icc_format = TYPE_Lab_16;
334 			break;
335 
336 		case cmsSigXYZData:
337 			colour->interpretation = VIPS_INTERPRETATION_XYZ;
338 			colour->format = VIPS_FORMAT_FLOAT;
339 			colour->bands = 3;
340 			icc->out_icc_format = TYPE_XYZ_16;
341 			break;
342 
343 		default:
344 			vips_error( class->nickname,
345 				_( "unimplemented output color space 0x%x" ),
346 				cmsGetColorSpace( icc->out_profile ) );
347 			return( -1 );
348 		}
349 
350 	/* At least one must be a device profile.
351 	 */
352 	if( icc->in_profile &&
353 		icc->out_profile &&
354 		is_pcs( icc->in_profile ) &&
355 		is_pcs( icc->out_profile ) ) {
356 		vips_error( class->nickname,
357 			"%s", _( "no device profile" ) );
358 		return( -1 );
359 	}
360 
361 	/* Use cmsFLAGS_NOCACHE to disable the 1-pixel cache and make
362 	 * calling cmsDoTransform() from multiple threads safe.
363 	 */
364 	flags = cmsFLAGS_NOCACHE;
365 
366 	if( icc->black_point_compensation )
367 		flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
368 
369 	if( !(icc->trans = cmsCreateTransform(
370 		icc->in_profile, icc->in_icc_format,
371 		icc->out_profile, icc->out_icc_format,
372 		icc->intent, flags )) )
373 		return( -1 );
374 
375 	if( VIPS_OBJECT_CLASS( vips_icc_parent_class )->
376 		build( object ) )
377 		return( -1 );
378 
379 	return( 0 );
380 }
381 
382 /* Get from an image.
383  */
384 static VipsBlob *
vips_icc_get_profile_image(VipsImage * image)385 vips_icc_get_profile_image( VipsImage *image )
386 {
387 	const void *data;
388 	size_t size;
389 
390 	if( !vips_image_get_typeof( image, VIPS_META_ICC_NAME ) )
391 		return( NULL );
392 	if( vips_image_get_blob( image, VIPS_META_ICC_NAME, &data, &size ) )
393 		return( NULL );
394 
395 	return( vips_blob_new( NULL, data, size ) );
396 }
397 
398 #ifdef DEBUG
399 static void
vips_icc_print_profile(const char * name,cmsHPROFILE profile)400 vips_icc_print_profile( const char *name, cmsHPROFILE profile )
401 {
402 	static const cmsInfoType info_types[] = {
403 	     cmsInfoDescription,
404              cmsInfoManufacturer,
405              cmsInfoModel,
406              cmsInfoCopyright
407 	};
408 	static const char *info_names[] = {
409 	     "description",
410              "manufacturer",
411              "model",
412              "copyright"
413 	};
414 
415 	int i;
416 	cmsUInt32Number n_bytes;
417 	cmsUInt32Number n_intents;
418 	cmsUInt32Number *intent_codes;
419 	char **intent_descriptions;
420 
421 	printf( "icc profile %s: %p\n", name, profile );
422 	for( i = 0; i < VIPS_NUMBER( info_types ); i++ ) {
423 		if( (n_bytes = cmsGetProfileInfoASCII( profile,
424 			info_types[i], "en", "US",
425 			NULL, 0 )) ) {
426 			char *buffer;
427 
428 			buffer = VIPS_ARRAY( NULL, n_bytes, char );
429 			(void) cmsGetProfileInfoASCII( profile,
430 				info_types[i], "en", "US",
431 				buffer, n_bytes );
432 			printf( "%s: %s\n", info_names[i], buffer );
433 			g_free( buffer );
434 		}
435 	}
436 
437 	printf( "profile class: %#x\n", cmsGetDeviceClass( profile ) );
438 {
439 	cmsColorSpaceSignature pcs = cmsGetPCS( profile );
440 	guint pcs_as_be = GUINT_TO_BE( pcs );
441 
442 	char name[5];
443 
444 	vips_strncpy( name, (const char *) &pcs_as_be, 5 );
445 	printf( "PCS: %#x (%s)\n", pcs, name );
446 }
447 
448 	printf( "matrix shaper: %d\n", cmsIsMatrixShaper( profile ) );
449 	printf( "version: %g\n", cmsGetProfileVersion( profile ) );
450 
451 	n_intents = cmsGetSupportedIntents( 0, NULL, NULL );
452 	printf( "n_intents = %u\n", n_intents );
453 	intent_codes = VIPS_ARRAY( NULL, n_intents, cmsUInt32Number );
454 	intent_descriptions = VIPS_ARRAY( NULL, n_intents, char * );
455 	(void) cmsGetSupportedIntents( n_intents,
456 		intent_codes, intent_descriptions );
457 	for( i = 0; i < n_intents; i++ ) {
458 		printf( "  %#x: %s, in CLUT = %d, out CLUT = %d\n",
459 			intent_codes[i], intent_descriptions[i],
460 			cmsIsCLUT( profile,
461 				intent_codes[i], LCMS_USED_AS_INPUT ),
462 			cmsIsCLUT( profile,
463 				intent_codes[i], LCMS_USED_AS_OUTPUT ) );
464 	}
465 	g_free( intent_codes );
466 	g_free( intent_descriptions );
467 }
468 #endif /*DEBUG*/
469 
470 /* How many bands we expect to see from an image after preprocessing by our
471  * parent classes. This is a bit fragile :-(
472  *
473  * FIXME ... split the _build() for colour into separate preprocess / process
474  * / postprocess phases so we can load profiles after preprocess but before
475  * actual processing takes place.
476  */
477 static int
vips_image_expected_bands(VipsImage * image)478 vips_image_expected_bands( VipsImage *image )
479 {
480 	int expected_bands;
481 
482 	switch( image->Type ) {
483 	case VIPS_INTERPRETATION_B_W:
484 	case VIPS_INTERPRETATION_GREY16:
485 		expected_bands = 1;
486 		break;
487 
488 	case VIPS_INTERPRETATION_XYZ:
489 	case VIPS_INTERPRETATION_LAB:
490 	case VIPS_INTERPRETATION_LABQ:
491 	case VIPS_INTERPRETATION_RGB:
492 	case VIPS_INTERPRETATION_CMC:
493 	case VIPS_INTERPRETATION_LCH:
494 	case VIPS_INTERPRETATION_LABS:
495 	case VIPS_INTERPRETATION_sRGB:
496 	case VIPS_INTERPRETATION_YXY:
497 	case VIPS_INTERPRETATION_RGB16:
498 	case VIPS_INTERPRETATION_scRGB:
499 	case VIPS_INTERPRETATION_HSV:
500 		expected_bands = 3;
501 		break;
502 
503 	case VIPS_INTERPRETATION_CMYK:
504 		expected_bands = 4;
505 		break;
506 
507 	case VIPS_INTERPRETATION_MULTIBAND:
508 	case VIPS_INTERPRETATION_HISTOGRAM:
509 	case VIPS_INTERPRETATION_MATRIX:
510 	case VIPS_INTERPRETATION_FOURIER:
511 	default:
512 		expected_bands = image->Bands;
513 		break;
514 	}
515 
516 	expected_bands = VIPS_MIN( expected_bands, image->Bands );
517 
518 	return( expected_bands );
519 }
520 
521 static int
vips_icc_profile_needs_bands(cmsHPROFILE profile)522 vips_icc_profile_needs_bands( cmsHPROFILE profile )
523 {
524 	int needs_bands;
525 
526 	switch( cmsGetColorSpace( profile ) ) {
527 	case cmsSigGrayData:
528 		needs_bands = 1;
529 		break;
530 
531 	case cmsSigRgbData:
532 	case cmsSigLabData:
533 	case cmsSigXYZData:
534 		needs_bands = 3;
535 		break;
536 
537 	case cmsSigCmykData:
538 		needs_bands = 4;
539 		break;
540 
541 	default:
542 		needs_bands = -1;
543 		break;
544 	}
545 
546 	return( needs_bands );
547 }
548 
549 /* What cmsColorSpaceSignature do we expect this image to be (roughly) after
550  * preprocessing. Again, fragile :( see the FIXME above.
551  */
552 static cmsColorSpaceSignature
vips_image_expected_sig(VipsImage * image)553 vips_image_expected_sig( VipsImage *image )
554 {
555 	cmsColorSpaceSignature expected_sig;
556 
557 	switch( image->Type ) {
558 	case VIPS_INTERPRETATION_B_W:
559 	case VIPS_INTERPRETATION_GREY16:
560 		expected_sig = cmsSigGrayData;
561 		break;
562 
563 	case VIPS_INTERPRETATION_LAB:
564 	case VIPS_INTERPRETATION_LABQ:
565 	case VIPS_INTERPRETATION_LABS:
566 		expected_sig = cmsSigLabData;
567 		break;
568 
569 	case VIPS_INTERPRETATION_sRGB:
570 	case VIPS_INTERPRETATION_RGB:
571 	case VIPS_INTERPRETATION_RGB16:
572 	case VIPS_INTERPRETATION_scRGB:
573 		expected_sig = cmsSigRgbData;
574 		break;
575 
576 	case VIPS_INTERPRETATION_XYZ:
577 		expected_sig = cmsSigXYZData;
578 		break;
579 
580 	case VIPS_INTERPRETATION_CMYK:
581 		expected_sig = cmsSigCmykData;
582 		break;
583 
584 	case VIPS_INTERPRETATION_HSV:
585 		expected_sig = cmsSigHsvData;
586 		break;
587 
588 	case VIPS_INTERPRETATION_YXY:
589 		expected_sig = cmsSigYxyData;
590 		break;
591 
592 	case VIPS_INTERPRETATION_MULTIBAND:
593 		/* A generic many-band image. Try to guess from the number of
594 		 * image bands instead.
595 		 */
596 		switch( image->Bands ) {
597 		case 1:
598 		case 2:
599 			expected_sig = cmsSigGrayData;
600 			break;
601 
602 		case 3:
603 			expected_sig = cmsSigRgbData;
604 			break;
605 
606 		case 4:
607 		case 5:
608 			expected_sig = cmsSigCmykData;
609 			break;
610 
611 		default:
612 			expected_sig = -1;
613 			break;
614 		}
615 		break;
616 
617 	case VIPS_INTERPRETATION_LCH:
618 	case VIPS_INTERPRETATION_CMC:
619 	case VIPS_INTERPRETATION_HISTOGRAM:
620 	case VIPS_INTERPRETATION_MATRIX:
621 	case VIPS_INTERPRETATION_FOURIER:
622 	default:
623 		expected_sig = -1;
624 		break;
625 	}
626 
627 	return( expected_sig );
628 }
629 
630 /* Load a profile from a blob and check compatibility with image, intent and
631  * direction.
632  *
633  * Don't set any errors since this is used to test compatibility.
634  */
635 static cmsHPROFILE
vips_icc_load_profile_blob(VipsBlob * blob,VipsImage * image,VipsIntent intent,int direction)636 vips_icc_load_profile_blob( VipsBlob *blob,
637 	VipsImage *image, VipsIntent intent, int direction )
638 {
639 	const void *data;
640 	size_t size;
641 	cmsHPROFILE profile;
642 
643 #ifdef DEBUG
644 	printf( "loading %s profile, intent %s, from blob %p\n",
645 		direction == LCMS_USED_AS_INPUT ?  _( "input" ) : _( "output" ),
646 		vips_enum_nick( VIPS_TYPE_INTENT, intent ),
647 		blob );
648 #endif /*DEBUG*/
649 
650 	data = vips_blob_get( blob, &size );
651 	if( !(profile = cmsOpenProfileFromMem( data, size )) ) {
652 		g_warning( "%s", _( "corrupt profile" ) );
653 		return( NULL );
654 	}
655 
656 #ifdef DEBUG
657 	vips_icc_print_profile( "loaded from blob to make", profile );
658 #endif /*DEBUG*/
659 
660 	if( image &&
661 		vips_image_expected_bands( image ) !=
662 			vips_icc_profile_needs_bands( profile ) ) {
663 		VIPS_FREEF( cmsCloseProfile, profile );
664 		g_warning( "%s", _( "profile incompatible with image" ) );
665 		return( NULL );
666 	}
667 
668 	if( image &&
669 		vips_image_expected_sig( image ) !=
670 			cmsGetColorSpace( profile ) ) {
671 		VIPS_FREEF( cmsCloseProfile, profile );
672 		g_warning( "%s",
673 			_( "profile colourspace differs from image" ) );
674 		return( NULL );
675 	}
676 
677 	if( !cmsIsIntentSupported( profile, intent, direction ) ) {
678 		VIPS_FREEF( cmsCloseProfile, profile );
679 		g_warning( _( "profile does not support %s %s intent" ),
680 			vips_enum_nick( VIPS_TYPE_INTENT, intent ),
681 			direction == LCMS_USED_AS_INPUT ?
682 				_( "input" ) : _( "output" ) );
683 		return( NULL );
684 	}
685 
686 	return( profile );
687 }
688 
689 /* Verify that a blob is not corrupt and is compatible with this image.
690  *
691  * unref the blob if it's useless.
692  */
693 static cmsHPROFILE
vips_icc_verify_blob(VipsBlob ** blob,VipsImage * image,VipsIntent intent,int direction)694 vips_icc_verify_blob( VipsBlob **blob,
695 	VipsImage *image, VipsIntent intent, int direction )
696 {
697 	if( *blob ) {
698 		cmsHPROFILE profile;
699 
700 		if( !(profile = vips_icc_load_profile_blob( *blob,
701 			image, intent, direction )) ) {
702 			vips_area_unref( (VipsArea *) *blob );
703 			*blob = NULL;
704 		}
705 
706 		return( profile );
707 	}
708 
709 	return( NULL );
710 }
711 
712 /* Try to set the inport profile. We read the input profile like this:
713  *
714  *	embedded	filename	action
715  *	0		0 		image
716  *	1		0		image
717  *	0		1		file
718  *	1		1		image, then fall back to file
719  *
720  * If averything fails, we fall back to our built-in profiles, either
721  * srgb or cmyk, depending on the input image.
722  *
723  * We set attach_input_profile if we used a non-emdedded profile. The profile
724  * in in_blob will need to be attached to the output image in some way.
725  */
726 static int
vips_icc_set_import(VipsIcc * icc,gboolean embedded,const char * input_profile_filename)727 vips_icc_set_import( VipsIcc *icc,
728 	gboolean embedded, const char *input_profile_filename )
729 {
730 	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( icc );
731 	VipsColourCode *code = (VipsColourCode *) icc;
732 
733 	icc->non_standard_input_profile = FALSE;
734 
735 	/* Try embedded profile.
736 	 */
737 	if( code->in &&
738 		(embedded || !input_profile_filename) ) {
739 		icc->in_blob = vips_icc_get_profile_image( code->in );
740 		icc->in_profile = vips_icc_verify_blob( &icc->in_blob,
741 			code->in, icc->intent, LCMS_USED_AS_INPUT );
742 	}
743 
744 	/* Try profile from filename.
745 	 */
746 	if( code->in &&
747 		!icc->in_blob &&
748 		input_profile_filename ) {
749 		if(
750 			!vips_profile_load( input_profile_filename,
751 				&icc->in_blob, NULL ) &&
752 			(icc->in_profile = vips_icc_verify_blob( &icc->in_blob,
753 				code->in, icc->intent, LCMS_USED_AS_INPUT )) )
754 			icc->non_standard_input_profile = TRUE;
755 	}
756 
757 	/* Try built-in profile.
758 	 */
759 	if( code->in &&
760 		!icc->in_profile ) {
761 		const char *name = code->in->Type == VIPS_INTERPRETATION_CMYK ?
762 			"cmyk" : "srgb";
763 
764 		if(
765 			!vips_profile_load( name, &icc->in_blob, NULL ) &&
766 			(icc->in_profile = vips_icc_verify_blob( &icc->in_blob,
767 				code->in, icc->intent, LCMS_USED_AS_INPUT )) )
768 			icc->non_standard_input_profile = TRUE;
769 	}
770 
771 	if( !icc->in_profile ) {
772 		vips_error( class->nickname, "%s", _( "unable to load or "
773 			"find any compatible input profile" ) );
774 		return( -1 );
775 	}
776 
777 	return( 0 );
778 }
779 
780 static void
vips_icc_class_init(VipsIccClass * class)781 vips_icc_class_init( VipsIccClass *class )
782 {
783 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
784 	VipsObjectClass *object_class = (VipsObjectClass *) class;
785 
786 	gobject_class->dispose = vips_icc_dispose;
787 	gobject_class->set_property = vips_object_set_property;
788 	gobject_class->get_property = vips_object_get_property;
789 
790 	object_class->nickname = "icc";
791 	object_class->description = _( "transform using ICC profiles" );
792 	object_class->build = vips_icc_build;
793 
794 	VIPS_ARG_ENUM( class, "intent", 6,
795 		_( "Intent" ),
796 		_( "Rendering intent" ),
797 		VIPS_ARGUMENT_OPTIONAL_INPUT,
798 		G_STRUCT_OFFSET( VipsIcc, intent ),
799 		VIPS_TYPE_INTENT, VIPS_INTENT_RELATIVE );
800 
801 	VIPS_ARG_ENUM( class, "pcs", 6,
802 		_( "PCS" ),
803 		_( "Set Profile Connection Space" ),
804 		VIPS_ARGUMENT_OPTIONAL_INPUT,
805 		G_STRUCT_OFFSET( VipsIcc, pcs ),
806 		VIPS_TYPE_PCS, VIPS_PCS_LAB );
807 
808 	VIPS_ARG_BOOL( class, "black_point_compensation", 7,
809 		_( "Black point compensation" ),
810 		_( "Enable black point compensation" ),
811 		VIPS_ARGUMENT_OPTIONAL_INPUT,
812 		G_STRUCT_OFFSET( VipsIcc, black_point_compensation ),
813 		FALSE );
814 
815 	cmsSetLogErrorHandler( icc_error );
816 }
817 
818 static void
vips_icc_init(VipsIcc * icc)819 vips_icc_init( VipsIcc *icc )
820 {
821 	icc->intent = VIPS_INTENT_RELATIVE;
822 	icc->pcs = VIPS_PCS_LAB;
823 	icc->depth = 8;
824 }
825 
826 typedef struct _VipsIccImport {
827 	VipsIcc parent_instance;
828 
829 	gboolean embedded;
830 	char *input_profile_filename;
831 
832 } VipsIccImport;
833 
834 typedef VipsIccClass VipsIccImportClass;
835 
836 G_DEFINE_TYPE( VipsIccImport, vips_icc_import, VIPS_TYPE_ICC );
837 
838 static int
vips_icc_import_build(VipsObject * object)839 vips_icc_import_build( VipsObject *object )
840 {
841 	VipsColour *colour = (VipsColour *) object;
842 	VipsIcc *icc = (VipsIcc *) object;
843 	VipsIccImport *import = (VipsIccImport *) object;
844 
845 	if( vips_icc_set_import( icc,
846 		import->embedded, import->input_profile_filename ) )
847 		return( -1 );
848 
849 	if( icc->pcs == VIPS_PCS_LAB ) {
850 		cmsCIExyY white;
851 		cmsWhitePointFromTemp( &white, 6500 );
852 
853 		icc->out_profile = cmsCreateLab4Profile( &white );
854 	}
855 	else
856 		icc->out_profile = cmsCreateXYZProfile();
857 
858 	if( VIPS_OBJECT_CLASS( vips_icc_import_parent_class )->build( object ) )
859 		return( -1 );
860 
861 	/* If we used the fallback profile, we need to attach it to the PCS
862 	 * image, since the PCS image needs a route back to device space.
863 	 *
864 	 * In the same way, we don't remove the embedded input profile on
865 	 * import.
866 	 */
867 	if( icc->non_standard_input_profile &&
868 		icc->in_blob ) {
869 		const void *data;
870 		size_t size;
871 
872 		data = vips_blob_get( icc->in_blob, &size );
873 		vips_image_set_blob( colour->out, VIPS_META_ICC_NAME,
874 			NULL, data, size );
875 	}
876 
877 	return( 0 );
878 }
879 
880 static void
decode_lab(guint16 * fixed,float * lab,int n)881 decode_lab( guint16 *fixed, float *lab, int n )
882 {
883 	int i;
884 
885         for( i = 0; i < n; i++ ) {
886                 lab[0] = (double) fixed[0] / 652.800;
887                 lab[1] = ((double) fixed[1] / 256.0) - 128.0;
888                 lab[2] = ((double) fixed[2] / 256.0) - 128.0;
889 
890                 lab += 3;
891                 fixed += 3;
892         }
893 }
894 
895 #define X_FAC (VIPS_D50_X0 * 32768 / (VIPS_D65_X0 * 100))
896 #define Y_FAC (VIPS_D50_Y0 * 32768 / (VIPS_D65_Y0 * 100))
897 #define Z_FAC (VIPS_D50_Z0 * 32768 / (VIPS_D65_Z0 * 100))
898 
899 static void
decode_xyz(guint16 * fixed,float * xyz,int n)900 decode_xyz( guint16 *fixed, float *xyz, int n )
901 {
902 	int i;
903 
904         for( i = 0; i < n; i++ ) {
905                 xyz[0] = (double) fixed[0] / X_FAC;
906                 xyz[1] = (double) fixed[1] / Y_FAC;
907                 xyz[2] = (double) fixed[2] / Z_FAC;
908 
909                 xyz += 3;
910                 fixed += 3;
911         }
912 }
913 
914 /* Process a buffer of data.
915  */
916 static void
vips_icc_import_line(VipsColour * colour,VipsPel * out,VipsPel ** in,int width)917 vips_icc_import_line( VipsColour *colour,
918 	VipsPel *out, VipsPel **in, int width )
919 {
920 	VipsIcc *icc = (VipsIcc *) colour;
921 
922 	VipsPel *p;
923 	float *q;
924 	int i;
925 
926 	/* Buffer of encoded 16-bit pixels we transform.
927 	 */
928 	guint16 encoded[3 * PIXEL_BUFFER_SIZE];
929 
930 	p = (VipsPel *) in[0];
931 	q = (float *) out;
932 	for( i = 0; i < width; i += PIXEL_BUFFER_SIZE ) {
933 		const int chunk = VIPS_MIN( width - i, PIXEL_BUFFER_SIZE );
934 
935 		cmsDoTransform( icc->trans, p, encoded, chunk );
936 
937 		if( icc->pcs == VIPS_PCS_LAB )
938 			decode_lab( encoded, q, chunk );
939 		else
940 			decode_xyz( encoded, q, chunk );
941 
942 		p += PIXEL_BUFFER_SIZE * VIPS_IMAGE_SIZEOF_PEL( colour->in[0] );
943 		q += PIXEL_BUFFER_SIZE * 3;
944 	}
945 }
946 
947 static void
vips_icc_import_class_init(VipsIccImportClass * class)948 vips_icc_import_class_init( VipsIccImportClass *class )
949 {
950 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
951 	VipsObjectClass *object_class = (VipsObjectClass *) class;
952 	VipsColourClass *colour_class = VIPS_COLOUR_CLASS( class );
953 
954 	gobject_class->set_property = vips_object_set_property;
955 	gobject_class->get_property = vips_object_get_property;
956 
957 	object_class->nickname = "icc_import";
958 	object_class->description = _( "import from device with ICC profile" );
959 	object_class->build = vips_icc_import_build;
960 
961 	colour_class->process_line = vips_icc_import_line;
962 
963 	VIPS_ARG_BOOL( class, "embedded", 110,
964 		_( "Embedded" ),
965 		_( "Use embedded input profile, if available" ),
966 		VIPS_ARGUMENT_OPTIONAL_INPUT,
967 		G_STRUCT_OFFSET( VipsIccImport, embedded ),
968 		FALSE );
969 
970 	VIPS_ARG_STRING( class, "input_profile", 120,
971 		_( "Input profile" ),
972 		_( "Filename to load input profile from" ),
973 		VIPS_ARGUMENT_OPTIONAL_INPUT,
974 		G_STRUCT_OFFSET( VipsIccImport, input_profile_filename ),
975 		NULL );
976 
977 }
978 
979 static void
vips_icc_import_init(VipsIccImport * import)980 vips_icc_import_init( VipsIccImport *import )
981 {
982 }
983 
984 typedef struct _VipsIccExport {
985 	VipsIcc parent_instance;
986 
987 	char *output_profile_filename;
988 
989 } VipsIccExport;
990 
991 typedef VipsIccClass VipsIccExportClass;
992 
993 G_DEFINE_TYPE( VipsIccExport, vips_icc_export, VIPS_TYPE_ICC );
994 
995 static int
vips_icc_export_build(VipsObject * object)996 vips_icc_export_build( VipsObject *object )
997 {
998 	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
999 	VipsColour *colour = (VipsColour *) object;
1000 	VipsColourCode *code = (VipsColourCode *) object;
1001 	VipsIcc *icc = (VipsIcc *) object;
1002 	VipsIccExport *export = (VipsIccExport *) object;
1003 
1004 	/* If icc->pcs hasn't been set and this image is tagged as XYZ, swap
1005 	 * to XYZ pcs. This will save a XYZ->LAB conversion when we chain up.
1006 	 */
1007 	if( !vips_object_argument_isset( object, "pcs" ) &&
1008 		code->in &&
1009 		code->in->Type == VIPS_INTERPRETATION_XYZ )
1010 		icc->pcs = VIPS_PCS_XYZ;
1011 
1012 	if( icc->pcs == VIPS_PCS_LAB ) {
1013 		cmsCIExyY white;
1014 		cmsWhitePointFromTemp( &white, 6500 );
1015 
1016 		icc->in_profile = cmsCreateLab4Profile( &white );
1017 	}
1018 	else
1019 		icc->in_profile = cmsCreateXYZProfile();
1020 
1021 	if( code->in &&
1022 		!export->output_profile_filename )
1023 		icc->out_blob = vips_icc_get_profile_image( code->in );
1024 
1025 	if( !icc->out_blob &&
1026 		export->output_profile_filename ) {
1027 		if( vips_profile_load( export->output_profile_filename,
1028 			&icc->out_blob, NULL ) )
1029 			return( -1 );
1030 		colour->profile_filename = export->output_profile_filename;
1031 	}
1032 
1033 	if( icc->out_blob &&
1034 		!(icc->out_profile = vips_icc_load_profile_blob( icc->out_blob,
1035 			NULL, icc->intent, LCMS_USED_AS_OUTPUT )) ) {
1036 		vips_error( class->nickname, "%s", _( "no output profile" ) );
1037 		return( -1 );
1038 	}
1039 
1040 	if( VIPS_OBJECT_CLASS( vips_icc_export_parent_class )->build( object ) )
1041 		return( -1 );
1042 
1043 	return( 0 );
1044 }
1045 
1046 /* Pack a buffer of floats into lcms's fixed-point formats. Cut from
1047  * lcms-1.0.8.
1048  */
1049 static void
encode_lab(float * lab,guint16 * fixed,int n)1050 encode_lab( float *lab, guint16 *fixed, int n )
1051 {
1052 	int i;
1053 
1054 	for( i = 0; i < n; i++ ) {
1055 		float L = lab[0];
1056 		float a = lab[1];
1057 		float b = lab[2];
1058 
1059 		if( L < 0 )
1060 			L = 0;
1061 		if( L > 100. )
1062 			L = 100.;
1063 
1064 		if( a < -128. )
1065 			a = -128;
1066 		if( a > 127.9961 )
1067 			a = 127.9961;
1068 		if( b < -128. )
1069 			b = -128;
1070 		if( b > 127.9961 )
1071 			b = 127.9961;
1072 
1073 		fixed[0] = L *  652.800 + 0.5;
1074 		fixed[1] = (a + 128.0) * 256.0 + 0.5;
1075 		fixed[2] = (b + 128.0) * 256.0 + 0.5;
1076 
1077 		lab += 3;
1078 		fixed += 3;
1079 	}
1080 }
1081 
1082 #define MAX_ENCODEABLE_XYZ  (100 * (1.0 + 32767.0 / 32768.0))
1083 
1084 // 1.15 fixed point for XYZ
1085 
1086 static void
encode_xyz(float * xyz,guint16 * fixed,int n)1087 encode_xyz( float *xyz, guint16 *fixed, int n )
1088 {
1089 	int i;
1090 
1091 	for( i = 0; i < n; i++ ) {
1092 		float X = xyz[0];
1093 		float Y = xyz[1];
1094 		float Z = xyz[2];
1095 
1096 		if( X < 0 )
1097 			X = 0;
1098 		if( X > MAX_ENCODEABLE_XYZ )
1099 			X = MAX_ENCODEABLE_XYZ;
1100 
1101 		if( Y < 0 )
1102 			Y = 0;
1103 		if( Y > MAX_ENCODEABLE_XYZ )
1104 			Y = MAX_ENCODEABLE_XYZ;
1105 
1106 		if( Z < 0 )
1107 			Z = 0;
1108 		if( Z > MAX_ENCODEABLE_XYZ )
1109 			Z = MAX_ENCODEABLE_XYZ;
1110 
1111 		fixed[0] = X * X_FAC + 0.5;
1112 		fixed[1] = Y * Y_FAC + 0.5;
1113 		fixed[2] = Z * Z_FAC + 0.5;
1114 
1115 		xyz += 3;
1116 		fixed += 3;
1117 	}
1118 }
1119 
1120 /* Process a buffer of data.
1121  */
1122 static void
vips_icc_export_line(VipsColour * colour,VipsPel * out,VipsPel ** in,int width)1123 vips_icc_export_line( VipsColour *colour,
1124 	VipsPel *out, VipsPel **in, int width )
1125 {
1126 	VipsIcc *icc = (VipsIcc *) colour;
1127 
1128 	float *p;
1129 	VipsPel *q;
1130 	int x;
1131 
1132 	/* Buffer of encoded 16-bit pixels we transform.
1133 	 */
1134 	guint16 encoded[3 * PIXEL_BUFFER_SIZE];
1135 
1136 	p = (float *) in[0];
1137 	q = (VipsPel *) out;
1138 	for( x = 0; x < width; x += PIXEL_BUFFER_SIZE ) {
1139 		const int chunk = VIPS_MIN( width - x, PIXEL_BUFFER_SIZE );
1140 
1141 		if( icc->pcs == VIPS_PCS_LAB )
1142 			encode_lab( p, encoded, chunk );
1143 		else
1144 			encode_xyz( p, encoded, chunk );
1145 
1146 		cmsDoTransform( icc->trans, encoded, q, chunk );
1147 
1148 		p += PIXEL_BUFFER_SIZE * 3;
1149 		q += PIXEL_BUFFER_SIZE * VIPS_IMAGE_SIZEOF_PEL( colour->out );
1150 	}
1151 }
1152 
1153 static void
vips_icc_export_class_init(VipsIccExportClass * class)1154 vips_icc_export_class_init( VipsIccExportClass *class )
1155 {
1156 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
1157 	VipsObjectClass *object_class = (VipsObjectClass *) class;
1158 	VipsColourClass *colour_class = VIPS_COLOUR_CLASS( class );
1159 
1160 	gobject_class->set_property = vips_object_set_property;
1161 	gobject_class->get_property = vips_object_get_property;
1162 
1163 	object_class->nickname = "icc_export";
1164 	object_class->description = _( "output to device with ICC profile" );
1165 	object_class->build = vips_icc_export_build;
1166 
1167 	colour_class->process_line = vips_icc_export_line;
1168 
1169 	VIPS_ARG_STRING( class, "output_profile", 110,
1170 		_( "Output profile" ),
1171 		_( "Filename to load output profile from" ),
1172 		VIPS_ARGUMENT_OPTIONAL_INPUT,
1173 		G_STRUCT_OFFSET( VipsIccExport, output_profile_filename ),
1174 		NULL );
1175 
1176 	VIPS_ARG_INT( class, "depth", 130,
1177 		_( "Depth" ),
1178 		_( "Output device space depth in bits" ),
1179 		VIPS_ARGUMENT_OPTIONAL_INPUT,
1180 		G_STRUCT_OFFSET( VipsIcc, depth ),
1181 		8, 16, 8 );
1182 }
1183 
1184 static void
vips_icc_export_init(VipsIccExport * export)1185 vips_icc_export_init( VipsIccExport *export )
1186 {
1187 }
1188 
1189 typedef struct _VipsIccTransform {
1190 	VipsIcc parent_instance;
1191 
1192 	gboolean embedded;
1193 	char *input_profile_filename;
1194 	char *output_profile_filename;
1195 
1196 } VipsIccTransform;
1197 
1198 typedef VipsIccClass VipsIccTransformClass;
1199 
1200 G_DEFINE_TYPE( VipsIccTransform, vips_icc_transform, VIPS_TYPE_ICC );
1201 
1202 static int
vips_icc_transform_build(VipsObject * object)1203 vips_icc_transform_build( VipsObject *object )
1204 {
1205 	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
1206 	VipsColour *colour = (VipsColour *) object;
1207 	VipsIcc *icc = (VipsIcc *) object;
1208 	VipsIccTransform *transform = (VipsIccTransform *) object;
1209 
1210 	if( vips_icc_set_import( icc,
1211 		transform->embedded, transform->input_profile_filename ) )
1212 		return( -1 );
1213 
1214 	if( transform->output_profile_filename ) {
1215 		if( vips_profile_load( transform->output_profile_filename,
1216 			&icc->out_blob, NULL ) )
1217 			return( -1 );
1218 		colour->profile_filename = transform->output_profile_filename;
1219 	}
1220 
1221 	if( icc->out_blob )
1222 		icc->out_profile = vips_icc_load_profile_blob( icc->out_blob,
1223 			NULL, icc->intent, LCMS_USED_AS_OUTPUT );
1224 
1225 	if( !icc->out_profile ) {
1226 		vips_error( class->nickname, "%s", _( "no output profile" ) );
1227 		return( -1 );
1228 	}
1229 
1230 	if( VIPS_OBJECT_CLASS( vips_icc_transform_parent_class )->
1231 		build( object ) )
1232 		return( -1 );
1233 
1234 	return( 0 );
1235 }
1236 
1237 /* Process a buffer of data.
1238  */
1239 static void
vips_icc_transform_line(VipsColour * colour,VipsPel * out,VipsPel ** in,int width)1240 vips_icc_transform_line( VipsColour *colour,
1241 	VipsPel *out, VipsPel **in, int width )
1242 {
1243 	VipsIcc *icc = (VipsIcc *) colour;
1244 
1245 	cmsDoTransform( icc->trans, in[0], out, width );
1246 }
1247 
1248 static void
vips_icc_transform_class_init(VipsIccImportClass * class)1249 vips_icc_transform_class_init( VipsIccImportClass *class )
1250 {
1251 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
1252 	VipsObjectClass *object_class = (VipsObjectClass *) class;
1253 	VipsColourClass *colour_class = VIPS_COLOUR_CLASS( class );
1254 
1255 	gobject_class->set_property = vips_object_set_property;
1256 	gobject_class->get_property = vips_object_get_property;
1257 
1258 	object_class->nickname = "icc_transform";
1259 	object_class->description =
1260 		_( "transform between devices with ICC profiles" );
1261 	object_class->build = vips_icc_transform_build;
1262 
1263 	colour_class->process_line = vips_icc_transform_line;
1264 
1265 	VIPS_ARG_STRING( class, "output_profile", 110,
1266 		_( "Output profile" ),
1267 		_( "Filename to load output profile from" ),
1268 		VIPS_ARGUMENT_REQUIRED_INPUT,
1269 		G_STRUCT_OFFSET( VipsIccTransform, output_profile_filename ),
1270 		NULL );
1271 
1272 	VIPS_ARG_BOOL( class, "embedded", 120,
1273 		_( "Embedded" ),
1274 		_( "Use embedded input profile, if available" ),
1275 		VIPS_ARGUMENT_OPTIONAL_INPUT,
1276 		G_STRUCT_OFFSET( VipsIccTransform, embedded ),
1277 		FALSE );
1278 
1279 	VIPS_ARG_STRING( class, "input_profile", 130,
1280 		_( "Input profile" ),
1281 		_( "Filename to load input profile from" ),
1282 		VIPS_ARGUMENT_OPTIONAL_INPUT,
1283 		G_STRUCT_OFFSET( VipsIccTransform, input_profile_filename ),
1284 		NULL );
1285 
1286 	VIPS_ARG_INT( class, "depth", 140,
1287 		_( "Depth" ),
1288 		_( "Output device space depth in bits" ),
1289 		VIPS_ARGUMENT_OPTIONAL_INPUT,
1290 		G_STRUCT_OFFSET( VipsIcc, depth ),
1291 		8, 16, 8 );
1292 
1293 }
1294 
1295 static void
vips_icc_transform_init(VipsIccTransform * transform)1296 vips_icc_transform_init( VipsIccTransform *transform )
1297 {
1298 }
1299 
1300 /**
1301  * vips_icc_ac2rc: (method)
1302  * @in: input image
1303  * @out: (out): output image
1304  * @profile_filename: use this profile
1305  *
1306  * Transform an image from absolute to relative colorimetry using the
1307  * MediaWhitePoint stored in the ICC profile.
1308  *
1309  * See also: vips_icc_transform(), vips_icc_import().
1310  *
1311  * Returns: 0 on success, -1 on error.
1312  */
1313 int
vips_icc_ac2rc(VipsImage * in,VipsImage ** out,const char * profile_filename)1314 vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
1315 {
1316 	VipsImage *t;
1317 	cmsHPROFILE profile;
1318 	cmsCIEXYZ *media;
1319 	double X, Y, Z;
1320 	double *add;
1321 	double *mul;
1322 	int i;
1323 
1324 	if( !(profile = cmsOpenProfileFromFile( profile_filename, "r" )) )
1325 		return( -1 );
1326 
1327 #ifdef DEBUG
1328 	vips_icc_print_profile( profile_filename, profile );
1329 #endif /*DEBUG*/
1330 
1331 	if( !(media = cmsReadTag( profile, cmsSigMediaWhitePointTag )) ) {
1332 		vips_error( "vips_icc_ac2rc",
1333 			"%s", _( "unable to get media white point" ) );
1334 		return( -1 );
1335 	}
1336 
1337 	X = media->X;
1338 	Y = media->Y;
1339 	Z = media->Z;
1340 
1341 	cmsCloseProfile( profile );
1342 
1343 	/* We need XYZ so we can adjust the white balance.
1344 	 */
1345 	if( vips_colourspace( in, &t, VIPS_INTERPRETATION_XYZ, NULL ) )
1346 		return( -1 );
1347 	in = t;
1348 
1349 	if( !(add = VIPS_ARRAY( in, in->Bands, double )) ||
1350 		!(mul = VIPS_ARRAY( in, in->Bands, double )) )
1351 		return( -1 );
1352 
1353 	/* There might be extra bands off to the right somewhere.
1354 	 */
1355 	for( i = 0; i < in->Bands; i++ )
1356 		add[i] = 0.0;
1357 
1358 	mul[0] = VIPS_D50_X0 / (X * 100.0);
1359 	mul[1] = VIPS_D50_Y0 / (Y * 100.0);
1360 	mul[2] = VIPS_D50_Z0 / (Z * 100.0);
1361 
1362 	for( i = 3; i < in->Bands; i++ )
1363 		mul[i] = 1.0;
1364 
1365 	if( vips_linear( in, &t, add, mul, in->Bands, NULL ) ) {
1366 		g_object_unref( in );
1367 		return( -1 );
1368 	}
1369 	g_object_unref( in );
1370 	in = t;
1371 
1372 	*out = in;
1373 
1374 	return( 0 );
1375 }
1376 
1377 /* TRUE if a profile is sane and is compatible with an image.
1378  */
1379 gboolean
vips_icc_is_compatible_profile(VipsImage * image,const void * data,size_t data_length)1380 vips_icc_is_compatible_profile( VipsImage *image,
1381 	const void *data, size_t data_length )
1382 {
1383 	cmsHPROFILE profile;
1384 
1385 	if( !(profile = cmsOpenProfileFromMem( data, data_length )) )
1386 		/* Corrupt profile.
1387 		 */
1388 		return( FALSE );
1389 
1390 #ifdef DEBUG
1391 	vips_icc_print_profile( "from memory", profile );
1392 #endif /*DEBUG*/
1393 
1394 	if( vips_image_expected_bands( image ) !=
1395 		vips_icc_profile_needs_bands( profile ) ) {
1396 		VIPS_FREEF( cmsCloseProfile, profile );
1397 		return( FALSE );
1398 	}
1399 
1400 	if( vips_image_expected_sig( image ) != cmsGetColorSpace( profile ) ) {
1401 		VIPS_FREEF( cmsCloseProfile, profile );
1402 		return( FALSE );
1403 	}
1404 
1405 	VIPS_FREEF( cmsCloseProfile, profile );
1406 
1407 	return( TRUE );
1408 }
1409 
1410 #else /*!HAVE_LCMS2*/
1411 
1412 #include <vips/vips.h>
1413 
1414 int
vips_icc_present(void)1415 vips_icc_present( void )
1416 {
1417 	return( 0 );
1418 }
1419 
1420 int
vips_icc_ac2rc(VipsImage * in,VipsImage ** out,const char * profile_filename)1421 vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
1422 {
1423 	vips_error( "VipsIcc", "%s",
1424 		_( "libvips configured without lcms support" ) );
1425 
1426 	return( -1 );
1427 }
1428 
1429 gboolean
vips_icc_is_compatible_profile(VipsImage * image,const void * data,size_t data_length)1430 vips_icc_is_compatible_profile( VipsImage *image,
1431 	const void *data, size_t data_length )
1432 {
1433 	return( TRUE );
1434 }
1435 
1436 #endif /*HAVE_LCMS*/
1437 
1438 /**
1439  * vips_icc_import: (method)
1440  * @in: input image
1441  * @out: (out): output image
1442  * @...: %NULL-terminated list of optional named arguments
1443  *
1444  * Optional arguments:
1445  *
1446  * * @pcs: #VipsPCS,  use XYZ or LAB PCS
1447  * * @intent: #VipsIntent, transform with this intent
1448  * * @black_point_compensation: %gboolean, enable black point compensation
1449  * * @embedded: %gboolean, use profile embedded in input image
1450  * * @input_profile: %gchararray, get the input profile from here
1451  *
1452  * Import an image from device space to D65 LAB with an ICC profile. If @pcs is
1453  * set to #VIPS_PCS_XYZ, use CIE XYZ PCS instead.
1454  *
1455  * If @embedded is set, the input profile is taken from the input image
1456  * metadata. If there is no embedded profile,
1457  * @input_profile_filename is used as a fall-back.
1458  * You can test for the
1459  * presence of an embedded profile with
1460  * vips_image_get_typeof() with #VIPS_META_ICC_NAME as an argument. This will
1461  * return %GType 0 if there is no profile.
1462  *
1463  * If @embedded is not set, the input profile is taken from
1464  * @input_profile. If @input_profile is not supplied, the
1465  * metadata profile, if any, is used as a fall-back.
1466  *
1467  * If @black_point_compensation is set, LCMS black point compensation is
1468  * enabled.
1469  *
1470  * Returns: 0 on success, -1 on error.
1471  */
1472 int
vips_icc_import(VipsImage * in,VipsImage ** out,...)1473 vips_icc_import( VipsImage *in, VipsImage **out, ... )
1474 {
1475 	va_list ap;
1476 	int result;
1477 
1478 	va_start( ap, out );
1479 	result = vips_call_split( "icc_import", ap, in, out );
1480 	va_end( ap );
1481 
1482 	return( result );
1483 }
1484 
1485 /**
1486  * vips_icc_export: (method)
1487  * @in: input image
1488  * @out: (out): output image
1489  * @...: %NULL-terminated list of optional named arguments
1490  *
1491  * Optional arguments:
1492  *
1493  * * @pcs: #VipsPCS,  use XYZ or LAB PCS
1494  * * @intent: #VipsIntent, transform with this intent
1495  * * @black_point_compensation: %gboolean, enable black point compensation
1496  * * @output_profile: %gchararray, get the output profile from here
1497  * * @depth: %gint, depth of output image in bits
1498  *
1499  * Export an image from D65 LAB to device space with an ICC profile.
1500  * If @pcs is
1501  * set to #VIPS_PCS_XYZ, use CIE XYZ PCS instead.
1502  * If @output_profile is not set, use the embedded profile, if any.
1503  * If @output_profile is set, export with that and attach it to the output
1504  * image.
1505  *
1506  * If @black_point_compensation is set, LCMS black point compensation is
1507  * enabled.
1508  *
1509  * Returns: 0 on success, -1 on error.
1510  */
1511 int
vips_icc_export(VipsImage * in,VipsImage ** out,...)1512 vips_icc_export( VipsImage *in, VipsImage **out, ... )
1513 {
1514 	va_list ap;
1515 	int result;
1516 
1517 	va_start( ap, out );
1518 	result = vips_call_split( "icc_export", ap, in, out );
1519 	va_end( ap );
1520 
1521 	return( result );
1522 }
1523 
1524 /**
1525  * vips_icc_transform: (method)
1526  * @in: input image
1527  * @out: (out): output image
1528  * @output_profile: get the output profile from here
1529  * @...: %NULL-terminated list of optional named arguments
1530  *
1531  * Optional arguments:
1532  *
1533  * * @pcs: #VipsPCS,  use XYZ or LAB PCS
1534  * * @intent: #VipsIntent, transform with this intent
1535  * * @black_point_compensation: %gboolean, enable black point compensation
1536  * * @embedded: %gboolean, use profile embedded in input image
1537  * * @input_profile: %gchararray, get the input profile from here
1538  * * @depth: %gint, depth of output image in bits
1539  *
1540  * Transform an image with a pair of ICC profiles. The input image is moved to
1541  * profile-connection space with the input profile and then to the output
1542  * space with the output profile.
1543  *
1544  * If @embedded is set, the input profile is taken from the input image
1545  * metadata, if present. If there is no embedded profile,
1546  * @input_profile is used as a fall-back.
1547  * You can test for the
1548  * presence of an embedded profile with
1549  * vips_image_get_typeof() with #VIPS_META_ICC_NAME as an argument. This will
1550  * return %GType 0 if there is no profile.
1551  *
1552  * If @embedded is not set, the input profile is taken from
1553  * @input_profile. If @input_profile is not supplied, the
1554  * metadata profile, if any, is used as a fall-back.
1555  *
1556  * If @black_point_compensation is set, LCMS black point compensation is
1557  * enabled.
1558  *
1559  * The output image has the output profile attached to the #VIPS_META_ICC_NAME
1560  * field.
1561  *
1562  * Use vips_icc_import() and vips_icc_export() to do either the first or
1563  * second half of this operation in isolation.
1564  *
1565  * Returns: 0 on success, -1 on error.
1566  */
1567 int
vips_icc_transform(VipsImage * in,VipsImage ** out,const char * output_profile,...)1568 vips_icc_transform( VipsImage *in, VipsImage **out,
1569 	const char *output_profile, ... )
1570 {
1571 	va_list ap;
1572 	int result;
1573 
1574 	va_start( ap, output_profile );
1575 	result = vips_call_split( "icc_transform", ap,
1576 		in, out, output_profile );
1577 	va_end( ap );
1578 
1579 	return( result );
1580 }
1581