1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2010 Marti Maria Saguer
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 //---------------------------------------------------------------------------------
26 // This program does apply profiles to (some) TIFF files
28 #include "lcms2_plugin.h"
35 static cmsBool BlackWhiteCompensation
= FALSE
;
36 static cmsBool IgnoreEmbedded
= FALSE
;
37 static cmsBool EmbedProfile
= FALSE
;
39 static cmsBool GamutCheck
= FALSE
;
40 static cmsBool lIsDeviceLink
= FALSE
;
41 static cmsBool StoreAsAlpha
= FALSE
;
43 static int Intent
= INTENT_PERCEPTUAL
;
44 static int ProofingIntent
= INTENT_PERCEPTUAL
;
45 static int PrecalcMode
= 1;
46 static cmsFloat64Number InkLimit
= 400;
48 static cmsFloat64Number ObserverAdaptationState
= 1.0; // According ICC 4.3 this is the default
50 static const char *cInpProf
= NULL
;
51 static const char *cOutProf
= NULL
;
52 static const char *cProofing
= NULL
;
54 static const char* SaveEmbedded
= NULL
;
56 // Console error & warning
58 void ConsoleWarningHandler(const char* module
, const char* fmt
, va_list ap
)
60 char e
[512] = { '\0' };
62 strcat(strcpy(e
, module
), ": ");
64 vsprintf(e
+strlen(e
), fmt
, ap
);
68 fprintf(stderr
, "\nWarning");
69 fprintf(stderr
, " %s\n", e
);
75 void ConsoleErrorHandler(const char* module
, const char* fmt
, va_list ap
)
77 char e
[512] = { '\0' };
80 if (strlen(module
) < 500)
81 strcat(strcpy(e
, module
), ": ");
84 vsprintf(e
+strlen(e
), fmt
, ap
);
86 fprintf(stderr
, "\nError");
87 fprintf(stderr
, " %s\n", e
);
94 void Warning(const char *frm
, ...)
99 ConsoleWarningHandler("[tificc]", frm
, args
);
105 // Out of mememory is a fatal error
107 void OutOfMem(cmsUInt32Number size
)
109 FatalError("Out of memory on allocating %d bytes.", size
);
113 // -----------------------------------------------------------------------------------------------
115 // In TIFF, Lab is encoded in a different way, so let's use the plug-in
116 // capabilities of lcms2 to change the meaning of TYPE_Lab_8.
118 // * 0xffff / 0xff00 = (255 * 257) / (255 * 256) = 257 / 256
119 static int FromLabV2ToLabV4(int x
)
123 a
= ((x
<< 8) | x
) >> 8; // * 257 / 256
124 if ( a
> 0xffff) return 0xffff;
128 // * 0xf00 / 0xffff = * 256 / 257
129 static int FromLabV4ToLabV2(int x
)
131 return ((x
<< 8) + 0x80) / 257;
135 // Formatter for 8bit Lab TIFF (photometric 8)
137 unsigned char* UnrollTIFFLab8(struct _cmstransform_struct
* CMMcargo
,
138 register cmsUInt16Number wIn
[],
139 register cmsUInt8Number
* accum
,
140 register cmsUInt32Number Stride
)
142 wIn
[0] = (cmsUInt16Number
) FromLabV2ToLabV4((accum
[0]) << 8);
143 wIn
[1] = (cmsUInt16Number
) FromLabV2ToLabV4(((accum
[1] > 127) ? (accum
[1] - 128) : (accum
[1] + 128)) << 8);
144 wIn
[2] = (cmsUInt16Number
) FromLabV2ToLabV4(((accum
[2] > 127) ? (accum
[2] - 128) : (accum
[2] + 128)) << 8);
148 UTILS_UNUSED_PARAMETER(Stride
);
149 UTILS_UNUSED_PARAMETER(CMMcargo
);
152 // Formatter for 16bit Lab TIFF (photometric 8)
154 unsigned char* UnrollTIFFLab16(struct _cmstransform_struct
* CMMcargo
,
155 register cmsUInt16Number wIn
[],
156 register cmsUInt8Number
* accum
,
157 register cmsUInt32Number Stride
)
159 cmsUInt16Number
* accum16
= (cmsUInt16Number
*) accum
;
161 wIn
[0] = (cmsUInt16Number
) FromLabV2ToLabV4(accum16
[0]);
162 wIn
[1] = (cmsUInt16Number
) FromLabV2ToLabV4(((accum16
[1] > 0x7f00) ? (accum16
[1] - 0x8000) : (accum16
[1] + 0x8000)) );
163 wIn
[2] = (cmsUInt16Number
) FromLabV2ToLabV4(((accum16
[2] > 0x7f00) ? (accum16
[2] - 0x8000) : (accum16
[2] + 0x8000)) );
165 return accum
+ 3 * sizeof(cmsUInt16Number
);
167 UTILS_UNUSED_PARAMETER(Stride
);
168 UTILS_UNUSED_PARAMETER(CMMcargo
);
173 unsigned char* PackTIFFLab8(struct _cmstransform_struct
* CMMcargo
,
174 register cmsUInt16Number wOut
[],
175 register cmsUInt8Number
* output
,
176 register cmsUInt32Number Stride
)
180 *output
++ = (cmsUInt8Number
) (FromLabV4ToLabV2(wOut
[0] + 0x0080) >> 8);
182 a
= (FromLabV4ToLabV2(wOut
[1]) + 0x0080) >> 8;
183 b
= (FromLabV4ToLabV2(wOut
[2]) + 0x0080) >> 8;
185 *output
++ = (cmsUInt8Number
) ((a
< 128) ? (a
+ 128) : (a
- 128));
186 *output
++ = (cmsUInt8Number
) ((b
< 128) ? (b
+ 128) : (b
- 128));
190 UTILS_UNUSED_PARAMETER(Stride
);
191 UTILS_UNUSED_PARAMETER(CMMcargo
);
195 unsigned char* PackTIFFLab16(struct _cmstransform_struct
* CMMcargo
,
196 register cmsUInt16Number wOut
[],
197 register cmsUInt8Number
* output
,
198 register cmsUInt32Number Stride
)
201 cmsUInt16Number
* output16
= (cmsUInt16Number
*) output
;
203 *output16
++ = (cmsUInt16Number
) FromLabV4ToLabV2(wOut
[0]);
205 a
= FromLabV4ToLabV2(wOut
[1]);
206 b
= FromLabV4ToLabV2(wOut
[2]);
208 *output16
++ = (cmsUInt16Number
) ((a
< 0x7f00) ? (a
+ 0x8000) : (a
- 0x8000));
209 *output16
++ = (cmsUInt16Number
) ((b
< 0x7f00) ? (b
+ 0x8000) : (b
- 0x8000));
211 return (cmsUInt8Number
*) output16
;
213 UTILS_UNUSED_PARAMETER(Stride
);
214 UTILS_UNUSED_PARAMETER(CMMcargo
);
219 cmsFormatter
TiffFormatterFactory(cmsUInt32Number Type
,
220 cmsFormatterDirection Dir
,
221 cmsUInt32Number dwFlags
)
223 cmsFormatter Result
= { NULL
};
224 int bps
= T_BYTES(Type
);
225 int IsTiffSpecial
= (Type
>> 23) & 1;
227 if (IsTiffSpecial
&& !(dwFlags
& CMS_PACK_FLAGS_FLOAT
))
229 if (Dir
== cmsFormatterInput
)
231 Result
.Fmt16
= (bps
== 1) ? UnrollTIFFLab8
: UnrollTIFFLab16
;
234 Result
.Fmt16
= (bps
== 1) ? PackTIFFLab8
: PackTIFFLab16
;
240 static cmsPluginFormatters TiffLabPlugin
= { {cmsPluginMagicNumber
, 2000, cmsPluginFormattersSig
, NULL
}, TiffFormatterFactory
};
244 // Build up the pixeltype descriptor
246 cmsUInt32Number
GetInputPixelType(TIFF
*Bank
)
248 uint16 Photometric
, bps
, spp
, extra
, PlanarConfig
, *info
;
249 uint16 Compression
, reverse
= 0;
250 int ColorChannels
, IsPlanar
= 0, pt
= 0, IsFlt
;
251 int labTiffSpecial
= FALSE
;
253 TIFFGetField(Bank
, TIFFTAG_PHOTOMETRIC
, &Photometric
);
254 TIFFGetFieldDefaulted(Bank
, TIFFTAG_BITSPERSAMPLE
, &bps
);
257 FatalError("Sorry, bilevel TIFFs has nothing to do with ICC profiles");
259 if (bps
!= 8 && bps
!= 16 && bps
!= 32)
260 FatalError("Sorry, 8, 16 or 32 bits per sample only");
262 TIFFGetFieldDefaulted(Bank
, TIFFTAG_SAMPLESPERPIXEL
, &spp
);
263 TIFFGetFieldDefaulted(Bank
, TIFFTAG_PLANARCONFIG
, &PlanarConfig
);
265 switch (PlanarConfig
) {
267 case PLANARCONFIG_CONTIG
: IsPlanar
= 0; break;
268 case PLANARCONFIG_SEPARATE
: IsPlanar
= 1; break;
271 FatalError("Unsupported planar configuration (=%d) ", (int) PlanarConfig
);
274 // If Samples per pixel == 1, PlanarConfiguration is irrelevant and need
275 // not to be included.
277 if (spp
== 1) IsPlanar
= 0;
281 TIFFGetFieldDefaulted(Bank
, TIFFTAG_EXTRASAMPLES
, &extra
, &info
);
283 // Read alpha channels as colorant
291 ColorChannels
= spp
- extra
;
293 switch (Photometric
) {
295 case PHOTOMETRIC_MINISWHITE
:
299 // ... fall through ...
301 case PHOTOMETRIC_MINISBLACK
:
305 case PHOTOMETRIC_RGB
:
310 case PHOTOMETRIC_PALETTE
:
311 FatalError("Sorry, palette images not supported");
314 case PHOTOMETRIC_SEPARATED
:
316 pt
= PixelTypeFromChanCount(ColorChannels
);
319 case PHOTOMETRIC_YCBCR
:
320 TIFFGetField(Bank
, TIFFTAG_COMPRESSION
, &Compression
);
325 TIFFGetFieldDefaulted(Bank
, TIFFTAG_YCBCRSUBSAMPLING
, &subx
, &suby
);
326 if (subx
!= 1 || suby
!= 1)
327 FatalError("Sorry, subsampled images not supported");
332 case PHOTOMETRIC_ICCLAB
:
336 case PHOTOMETRIC_CIELAB
:
338 labTiffSpecial
= TRUE
;
342 case PHOTOMETRIC_LOGLUV
: // CIE Log2(L) (u',v')
344 TIFFSetField(Bank
, TIFFTAG_SGILOGDATAFMT
, SGILOGDATAFMT_16BIT
);
345 pt
= PT_YUV
; // *ICCSpace = icSigLuvData;
346 bps
= 16; // 16 bits forced by LibTiff
350 FatalError("Unsupported TIFF color space (Photometric %d)", Photometric
);
353 // Convert bits per sample to bytes per sample
356 IsFlt
= (bps
== 0) || (bps
== 4);
358 return (FLOAT_SH(IsFlt
)|COLORSPACE_SH(pt
)|PLANAR_SH(IsPlanar
)|EXTRA_SH(extra
)|CHANNELS_SH(ColorChannels
)|BYTES_SH(bps
)|FLAVOR_SH(reverse
) | (labTiffSpecial
<< 23) );
363 // Rearrange pixel type to build output descriptor
365 cmsUInt32Number
ComputeOutputFormatDescriptor(cmsUInt32Number dwInput
, int OutColorSpace
, int bps
)
367 int IsPlanar
= T_PLANAR(dwInput
);
368 int Channels
= ChanCountFromPixelType(OutColorSpace
);
369 int IsFlt
= (bps
== 0) || (bps
== 4);
371 return (FLOAT_SH(IsFlt
)|COLORSPACE_SH(OutColorSpace
)|PLANAR_SH(IsPlanar
)|CHANNELS_SH(Channels
)|BYTES_SH(bps
));
376 // Tile based transforms
378 int TileBasedXform(cmsHTRANSFORM hXForm
, TIFF
* in
, TIFF
* out
, int nPlanes
)
380 tsize_t BufSizeIn
= TIFFTileSize(in
);
381 tsize_t BufSizeOut
= TIFFTileSize(out
);
382 unsigned char *BufferIn
, *BufferOut
;
383 ttile_t i
, TileCount
= TIFFNumberOfTiles(in
) / nPlanes
;
388 TIFFGetFieldDefaulted(in
, TIFFTAG_TILEWIDTH
, &tw
);
389 TIFFGetFieldDefaulted(in
, TIFFTAG_TILELENGTH
, &tl
);
391 PixelCount
= (int) tw
* tl
;
393 BufferIn
= (unsigned char *) _TIFFmalloc(BufSizeIn
* nPlanes
);
394 if (!BufferIn
) OutOfMem(BufSizeIn
* nPlanes
);
396 BufferOut
= (unsigned char *) _TIFFmalloc(BufSizeOut
* nPlanes
);
397 if (!BufferOut
) OutOfMem(BufSizeOut
* nPlanes
);
400 for (i
= 0; i
< TileCount
; i
++) {
402 for (j
=0; j
< nPlanes
; j
++) {
404 if (TIFFReadEncodedTile(in
, i
+ (j
* TileCount
),
405 BufferIn
+ (j
*BufSizeIn
), BufSizeIn
) < 0) goto cleanup
;
408 cmsDoTransform(hXForm
, BufferIn
, BufferOut
, PixelCount
);
410 for (j
=0; j
< nPlanes
; j
++) {
412 if (TIFFWriteEncodedTile(out
, i
+ (j
*TileCount
),
413 BufferOut
+ (j
*BufSizeOut
), BufSizeOut
) < 0) goto cleanup
;
419 _TIFFfree(BufferOut
);
426 _TIFFfree(BufferOut
);
431 // Strip based transforms
434 int StripBasedXform(cmsHTRANSFORM hXForm
, TIFF
* in
, TIFF
* out
, int nPlanes
)
436 tsize_t BufSizeIn
= TIFFStripSize(in
);
437 tsize_t BufSizeOut
= TIFFStripSize(out
);
438 unsigned char *BufferIn
, *BufferOut
;
439 ttile_t i
, StripCount
= TIFFNumberOfStrips(in
) / nPlanes
;
446 TIFFGetFieldDefaulted(in
, TIFFTAG_IMAGEWIDTH
, &sw
);
447 TIFFGetFieldDefaulted(in
, TIFFTAG_ROWSPERSTRIP
, &sl
);
448 TIFFGetFieldDefaulted(in
, TIFFTAG_IMAGELENGTH
, &iml
);
450 // It is possible to get infinite rows per strip
451 if (sl
== 0 || sl
> iml
)
452 sl
= iml
; // One strip for whole image
454 BufferIn
= (unsigned char *) _TIFFmalloc(BufSizeIn
* nPlanes
);
455 if (!BufferIn
) OutOfMem(BufSizeIn
* nPlanes
);
457 BufferOut
= (unsigned char *) _TIFFmalloc(BufSizeOut
* nPlanes
);
458 if (!BufferOut
) OutOfMem(BufSizeOut
* nPlanes
);
461 for (i
= 0; i
< StripCount
; i
++) {
463 for (j
=0; j
< nPlanes
; j
++) {
465 if (TIFFReadEncodedStrip(in
, i
+ (j
* StripCount
),
466 BufferIn
+ (j
* BufSizeIn
), BufSizeIn
) < 0) goto cleanup
;
469 PixelCount
= (int) sw
* (iml
< sl
? iml
: sl
);
472 cmsDoTransform(hXForm
, BufferIn
, BufferOut
, PixelCount
);
474 for (j
=0; j
< nPlanes
; j
++) {
475 if (TIFFWriteEncodedStrip(out
, i
+ (j
* StripCount
),
476 BufferOut
+ j
* BufSizeOut
, BufSizeOut
) < 0) goto cleanup
;
482 _TIFFfree(BufferOut
);
488 _TIFFfree(BufferOut
);
493 // Creates minimum required tags
495 void WriteOutputTags(TIFF
*out
, int Colorspace
, int BytesPerSample
)
497 int BitsPerSample
= (8 * BytesPerSample
);
498 int nChannels
= ChanCountFromPixelType(Colorspace
);
500 uint16 Extra
[] = { EXTRASAMPLE_UNASSALPHA
,
501 EXTRASAMPLE_UNASSALPHA
,
502 EXTRASAMPLE_UNASSALPHA
,
503 EXTRASAMPLE_UNASSALPHA
,
504 EXTRASAMPLE_UNASSALPHA
,
505 EXTRASAMPLE_UNASSALPHA
,
506 EXTRASAMPLE_UNASSALPHA
,
507 EXTRASAMPLE_UNASSALPHA
,
508 EXTRASAMPLE_UNASSALPHA
,
509 EXTRASAMPLE_UNASSALPHA
,
510 EXTRASAMPLE_UNASSALPHA
514 switch (Colorspace
) {
517 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, PHOTOMETRIC_MINISBLACK
);
518 TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, 1);
519 TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, BitsPerSample
);
523 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, PHOTOMETRIC_RGB
);
524 TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, 3);
525 TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, BitsPerSample
);
529 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, PHOTOMETRIC_SEPARATED
);
530 TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, 3);
531 TIFFSetField(out
, TIFFTAG_INKSET
, 2);
532 TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, BitsPerSample
);
536 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, PHOTOMETRIC_SEPARATED
);
537 TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, 4);
538 TIFFSetField(out
, TIFFTAG_INKSET
, INKSET_CMYK
);
539 TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, BitsPerSample
);
543 if (BitsPerSample
== 16)
544 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, 9);
546 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, PHOTOMETRIC_CIELAB
);
547 TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, 3);
548 TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, BitsPerSample
); // Needed by TIFF Spec
552 // Multi-ink separations
568 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, PHOTOMETRIC_SEPARATED
);
569 TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, nChannels
);
571 if (StoreAsAlpha
&& nChannels
>= 4) {
572 // CMYK plus extra alpha
573 TIFFSetField(out
, TIFFTAG_EXTRASAMPLES
, nChannels
- 4, Extra
);
574 TIFFSetField(out
, TIFFTAG_INKSET
, 1);
575 TIFFSetField(out
, TIFFTAG_NUMBEROFINKS
, 4);
578 TIFFSetField(out
, TIFFTAG_INKSET
, 2);
579 TIFFSetField(out
, TIFFTAG_NUMBEROFINKS
, nChannels
);
582 TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, BitsPerSample
);
587 FatalError("Unsupported output colorspace");
591 TIFFSetField(out
, TIFFTAG_SAMPLEFORMAT
, SAMPLEFORMAT_IEEEFP
);
595 // Copies a bunch of tages
598 void CopyOtherTags(TIFF
* in
, TIFF
* out
)
600 #define CopyField(tag, v) \
601 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
606 cmsFloat32Number floatv
;
610 CopyField(TIFFTAG_SUBFILETYPE
, longv
);
612 TIFFGetField(in
, TIFFTAG_IMAGEWIDTH
, &ow
);
613 TIFFGetField(in
, TIFFTAG_IMAGELENGTH
, &ol
);
615 TIFFSetField(out
, TIFFTAG_IMAGEWIDTH
, ow
);
616 TIFFSetField(out
, TIFFTAG_IMAGELENGTH
, ol
);
618 CopyField(TIFFTAG_PLANARCONFIG
, shortv
);
619 CopyField(TIFFTAG_COMPRESSION
, shortv
);
622 CopyField(TIFFTAG_PREDICTOR
, shortv
);
624 CopyField(TIFFTAG_THRESHHOLDING
, shortv
);
625 CopyField(TIFFTAG_FILLORDER
, shortv
);
626 CopyField(TIFFTAG_ORIENTATION
, shortv
);
627 CopyField(TIFFTAG_MINSAMPLEVALUE
, shortv
);
628 CopyField(TIFFTAG_MAXSAMPLEVALUE
, shortv
);
629 CopyField(TIFFTAG_XRESOLUTION
, floatv
);
630 CopyField(TIFFTAG_YRESOLUTION
, floatv
);
631 CopyField(TIFFTAG_RESOLUTIONUNIT
, shortv
);
632 CopyField(TIFFTAG_ROWSPERSTRIP
, longv
);
633 CopyField(TIFFTAG_XPOSITION
, floatv
);
634 CopyField(TIFFTAG_YPOSITION
, floatv
);
635 CopyField(TIFFTAG_IMAGEDEPTH
, longv
);
636 CopyField(TIFFTAG_TILEDEPTH
, longv
);
638 CopyField(TIFFTAG_TILEWIDTH
, longv
);
639 CopyField(TIFFTAG_TILELENGTH
, longv
);
641 CopyField(TIFFTAG_ARTIST
, stringv
);
642 CopyField(TIFFTAG_IMAGEDESCRIPTION
, stringv
);
643 CopyField(TIFFTAG_MAKE
, stringv
);
644 CopyField(TIFFTAG_MODEL
, stringv
);
646 CopyField(TIFFTAG_DATETIME
, stringv
);
647 CopyField(TIFFTAG_HOSTCOMPUTER
, stringv
);
648 CopyField(TIFFTAG_PAGENAME
, stringv
);
649 CopyField(TIFFTAG_DOCUMENTNAME
, stringv
);
653 // A replacement for (the nonstandard) filelenght
657 void DoEmbedProfile(TIFF
* Out
, const char* ProfileFile
)
660 cmsUInt32Number size
, EmbedLen
;
661 cmsUInt8Number
* EmbedBuffer
;
663 f
= fopen(ProfileFile
, "rb");
664 if (f
== NULL
) return;
666 size
= cmsfilelength(f
);
667 EmbedBuffer
= (cmsUInt8Number
*) malloc(size
+ 1);
668 if (EmbedBuffer
== NULL
) {
673 EmbedLen
= fread(EmbedBuffer
, 1, size
, f
);
675 if (EmbedLen
!= size
)
676 FatalError("Cannot read %ld bytes to %s", size
, ProfileFile
);
679 EmbedBuffer
[EmbedLen
] = 0;
681 TIFFSetField(Out
, TIFFTAG_ICCPROFILE
, EmbedLen
, EmbedBuffer
);
688 cmsHPROFILE
GetTIFFProfile(TIFF
* in
)
690 cmsCIExyYTRIPLE Primaries
;
691 cmsFloat32Number
* chr
;
692 cmsCIExyY WhitePoint
;
693 cmsFloat32Number
* wp
;
695 cmsToneCurve
* Curve
[3];
696 cmsUInt16Number
*gmr
, *gmg
, *gmb
;
697 cmsHPROFILE hProfile
;
698 cmsUInt32Number EmbedLen
;
699 cmsUInt8Number
* EmbedBuffer
;
701 if (IgnoreEmbedded
) return NULL
;
703 if (TIFFGetField(in
, TIFFTAG_ICCPROFILE
, &EmbedLen
, &EmbedBuffer
)) {
705 hProfile
= cmsOpenProfileFromMem(EmbedBuffer
, EmbedLen
);
707 // Print description found in the profile
710 fprintf(stdout
, "\n[Embedded profile]\n");
711 PrintProfileInformation(hProfile
);
715 if (hProfile
!= NULL
&& SaveEmbedded
!= NULL
)
716 SaveMemoryBlock(EmbedBuffer
, EmbedLen
, SaveEmbedded
);
718 if (hProfile
) return hProfile
;
721 // Try to see if "colorimetric" tiff
723 if (TIFFGetField(in
, TIFFTAG_PRIMARYCHROMATICITIES
, &chr
)) {
725 Primaries
.Red
.x
= chr
[0];
726 Primaries
.Red
.y
= chr
[1];
727 Primaries
.Green
.x
= chr
[2];
728 Primaries
.Green
.y
= chr
[3];
729 Primaries
.Blue
.x
= chr
[4];
730 Primaries
.Blue
.y
= chr
[5];
732 Primaries
.Red
.Y
= Primaries
.Green
.Y
= Primaries
.Blue
.Y
= 1.0;
734 if (TIFFGetField(in
, TIFFTAG_WHITEPOINT
, &wp
)) {
736 WhitePoint
.x
= wp
[0];
737 WhitePoint
.y
= wp
[1];
740 // Transferfunction is a bit harder....
742 TIFFGetFieldDefaulted(in
, TIFFTAG_TRANSFERFUNCTION
,
747 Curve
[0] = cmsBuildTabulatedToneCurve16(NULL
, 256, gmr
);
748 Curve
[1] = cmsBuildTabulatedToneCurve16(NULL
, 256, gmg
);
749 Curve
[2] = cmsBuildTabulatedToneCurve16(NULL
, 256, gmb
);
751 hProfile
= cmsCreateRGBProfileTHR(NULL
, &WhitePoint
, &Primaries
, Curve
);
753 for (i
=0; i
< 3; i
++)
754 cmsFreeToneCurve(Curve
[i
]);
757 fprintf(stdout
, "\n[Colorimetric TIFF]\n");
769 // Transform one image
771 int TransformImage(TIFF
* in
, TIFF
* out
, const char *cDefInpProf
)
773 cmsHPROFILE hIn
, hOut
, hProof
, hInkLimit
= NULL
;
775 cmsUInt32Number wInput
, wOutput
;
776 int OutputColorSpace
;
778 cmsUInt32Number dwFlags
= 0;
781 // Observer adaptation state (only meaningful on absolute colorimetric intent)
783 cmsSetAdaptationState(ObserverAdaptationState
);
785 if (EmbedProfile
&& cOutProf
)
786 DoEmbedProfile(out
, cOutProf
);
788 if (BlackWhiteCompensation
)
789 dwFlags
|= cmsFLAGS_BLACKPOINTCOMPENSATION
;
792 switch (PrecalcMode
) {
794 case 0: dwFlags
|= cmsFLAGS_NOOPTIMIZE
; break;
795 case 2: dwFlags
|= cmsFLAGS_HIGHRESPRECALC
; break;
796 case 3: dwFlags
|= cmsFLAGS_LOWRESPRECALC
; break;
799 default: FatalError("Unknown precalculation mode '%d'", PrecalcMode
);
804 dwFlags
|= cmsFLAGS_GAMUTCHECK
;
811 hIn
= cmsOpenProfileFromFile(cDefInpProf
, "r");
815 hIn
= GetTIFFProfile(in
);
818 hIn
= OpenStockProfile(NULL
, cDefInpProf
);
820 hOut
= OpenStockProfile(NULL
, cOutProf
);
822 if (cProofing
!= NULL
) {
824 hProof
= OpenStockProfile(NULL
, cProofing
);
825 dwFlags
|= cmsFLAGS_SOFTPROOFING
;
829 // Take input color space
831 wInput
= GetInputPixelType(in
);
833 // Assure both, input profile and input TIFF are on same colorspace
835 if (_cmsLCMScolorSpace(cmsGetColorSpace(hIn
)) != (int) T_COLORSPACE(wInput
))
836 FatalError("Input profile is not operating in proper color space");
840 OutputColorSpace
= _cmsLCMScolorSpace(cmsGetColorSpace(hOut
));
842 OutputColorSpace
= _cmsLCMScolorSpace(cmsGetPCS(hIn
));
844 wOutput
= ComputeOutputFormatDescriptor(wInput
, OutputColorSpace
, bps
);
846 WriteOutputTags(out
, OutputColorSpace
, bps
);
847 CopyOtherTags(in
, out
);
850 if (InkLimit
!= 400.0 &&
851 (OutputColorSpace
== PT_CMYK
|| OutputColorSpace
== PT_CMY
)) {
853 cmsHPROFILE hProfiles
[10];
857 hInkLimit
= cmsCreateInkLimitingDeviceLink(cmsGetColorSpace(hOut
), InkLimit
);
859 hProfiles
[nProfiles
++] = hIn
;
861 hProfiles
[nProfiles
++] = hProof
;
862 hProfiles
[nProfiles
++] = hProof
;
865 hProfiles
[nProfiles
++] = hOut
;
866 hProfiles
[nProfiles
++] = hInkLimit
;
868 xform
= cmsCreateMultiprofileTransform(hProfiles
, nProfiles
,
869 wInput
, wOutput
, Intent
, dwFlags
);
874 xform
= cmsCreateProofingTransform(hIn
, wInput
,
881 cmsCloseProfile(hIn
);
882 cmsCloseProfile(hOut
);
885 cmsCloseProfile(hInkLimit
);
887 cmsCloseProfile(hProof
);
889 if (xform
== NULL
) return 0;
892 if (T_PLANAR(wInput
))
893 nPlanes
= T_CHANNELS(wInput
) + T_EXTRA(wInput
);
898 // Handle tile by tile or strip by strip
899 if (TIFFIsTiled(in
)) {
901 TileBasedXform(xform
, in
, out
, nPlanes
);
904 StripBasedXform(xform
, in
, out
, nPlanes
);
908 cmsDeleteTransform(xform
);
910 TIFFWriteDirectory(out
);
920 fprintf(stderr
, "little cms ICC profile applier for TIFF - v6.2 [LittleCMS %2.2f]\n\n", LCMS_VERSION
/ 1000.0);
928 fprintf(stderr
, "usage: tificc [flags] input.tif output.tif\n");
930 fprintf(stderr
, "\nflags:\n\n");
931 fprintf(stderr
, "%cv - Verbose\n", SW
);
932 fprintf(stderr
, "%ci<profile> - Input profile (defaults to sRGB)\n", SW
);
933 fprintf(stderr
, "%co<profile> - Output profile (defaults to sRGB)\n", SW
);
934 fprintf(stderr
, "%cl<profile> - Transform by device-link profile\n", SW
);
936 PrintRenderingIntents();
938 fprintf(stderr
, "%cb - Black point compensation\n", SW
);
939 fprintf(stderr
, "%cd<0..1> - Observer adaptation state (abs.col. only)\n", SW
);
941 fprintf(stderr
, "%cc<0,1,2,3> - Precalculates transform (0=Off, 1=Normal, 2=Hi-res, 3=LoRes)\n", SW
);
942 fprintf(stderr
, "\n");
944 fprintf(stderr
, "%cw<8,16,32> - Output depth. Use 32 for floating-point\n\n", SW
);
945 fprintf(stderr
, "%ca - Handle channels > 4 as alpha\n", SW
);
947 fprintf(stderr
, "%cn - Ignore embedded profile on input\n", SW
);
948 fprintf(stderr
, "%ce - Embed destination profile\n", SW
);
949 fprintf(stderr
, "%cs<new profile> - Save embedded profile as <new profile>\n", SW
);
950 fprintf(stderr
, "\n");
953 fprintf(stderr
, "%cp<profile> - Soft proof profile\n", SW
);
954 fprintf(stderr
, "%cm<n> - Soft proof intent\n", SW
);
955 fprintf(stderr
, "%cg - Marks out-of-gamut colors on softproof\n", SW
);
957 fprintf(stderr
, "\n");
959 fprintf(stderr
, "%ck<0..400> - Ink-limiting in %% (CMYK only)\n", SW
);
960 fprintf(stderr
, "\n");
961 fprintf(stderr
, "%ch<0,1,2,3> - More help\n", SW
);
966 fprintf(stderr
, "Examples:\n\n"
967 "To color correct from scanner to sRGB:\n"
968 "\ttificc %ciscanner.icm in.tif out.tif\n"
969 "To convert from monitor1 to monitor2:\n"
970 "\ttificc %cimon1.icm %comon2.icm in.tif out.tif\n"
971 "To make a CMYK separation:\n"
972 "\ttificc %coprinter.icm inrgb.tif outcmyk.tif\n"
973 "To recover sRGB from a CMYK separation:\n"
974 "\ttificc %ciprinter.icm incmyk.tif outrgb.tif\n"
975 "To convert from CIELab TIFF to sRGB\n"
976 "\ttificc %ci*Lab in.tif out.tif\n\n",
977 SW
, SW
, SW
, SW
, SW
, SW
);
986 fprintf(stderr
, "This program is intended to be a demo of the little cms\n"
987 "engine. Both lcms and this program are freeware. You can\n"
988 "obtain both in source code at http://www.littlecms.com\n"
989 "For suggestions, comments, bug reports etc. send mail to\n"
990 "info@littlecms.com\n\n");
1000 // The toggles stuff
1003 void HandleSwitches(int argc
, char *argv
[])
1007 while ((s
=xgetopt(argc
,argv
,"aAeEbBw:W:nNvVGgh:H:i:I:o:O:P:p:t:T:c:C:l:L:M:m:K:k:S:s:D:d:")) != EOF
) {
1013 StoreAsAlpha
= TRUE
;
1017 BlackWhiteCompensation
= TRUE
;
1022 PrecalcMode
= atoi(xoptarg
);
1023 if (PrecalcMode
< 0 || PrecalcMode
> 3)
1024 FatalError("Unknown precalc mode '%d'", PrecalcMode
);
1028 case 'D': ObserverAdaptationState
= atof(xoptarg
);
1029 if (ObserverAdaptationState
< 0 ||
1030 ObserverAdaptationState
> 1.0)
1031 Warning("Adaptation state should be 0..1");
1036 EmbedProfile
= TRUE
;
1052 FatalError("Device-link already specified");
1060 FatalError("Device-link already specified");
1067 if (cInpProf
!= NULL
|| cOutProf
!= NULL
)
1068 FatalError("input/output profiles already specified");
1071 lIsDeviceLink
= TRUE
;
1076 cProofing
= xoptarg
;
1081 Intent
= atoi(xoptarg
);
1086 ProofingIntent
= atoi(xoptarg
);
1091 IgnoreEmbedded
= TRUE
;
1096 Width
= atoi(xoptarg
);
1097 if (Width
!= 8 && Width
!= 16 && Width
!= 32)
1098 FatalError("Only 8, 16 and 32 bps are supported");
1103 InkLimit
= atof(xoptarg
);
1104 if (InkLimit
< 0.0 || InkLimit
> 400.0)
1105 FatalError("Ink limit must be 0%%..400%%");
1110 case 'S': SaveEmbedded
= xoptarg
;
1116 int a
= atoi(xoptarg
);
1123 FatalError("Unknown option - run without args to see valid ones");
1132 int main(int argc
, char* argv
[])
1136 cmsPlugin(&TiffLabPlugin
);
1138 InitUtils("tificc");
1140 HandleSwitches(argc
, argv
);
1142 if ((argc
- xoptind
) != 2) {
1148 TIFFSetErrorHandler(ConsoleErrorHandler
);
1149 TIFFSetWarningHandler(ConsoleWarningHandler
);
1151 in
= TIFFOpen(argv
[xoptind
], "r");
1152 if (in
== NULL
) FatalError("Unable to open '%s'", argv
[xoptind
]);
1154 out
= TIFFOpen(argv
[xoptind
+1], "w");
1159 FatalError("Unable to write '%s'", argv
[xoptind
+1]);
1164 TransformImage(in
, out
, cInpProf
);
1167 } while (TIFFReadDirectory(in
));
1170 if (Verbose
) { fprintf(stdout
, "\n"); fflush(stdout
); }