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