Merged-in libcms2 v2.5
[AROS.git] / workbench / libs / lcms2 / utils / tificc / tificc.c
blob76fb15a0259dac47b3027723c9fa9e5a2937893f
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2010 Marti Maria Saguer
5 //
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"
29 #include "tiffio.h"
30 #include "utils.h"
33 // Flags
35 static cmsBool BlackWhiteCompensation = FALSE;
36 static cmsBool IgnoreEmbedded = FALSE;
37 static cmsBool EmbedProfile = FALSE;
38 static int Width = 8;
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
57 static
58 void ConsoleWarningHandler(const char* module, const char* fmt, va_list ap)
60 char e[512] = { '\0' };
61 if (module != NULL)
62 strcat(strcpy(e, module), ": ");
64 vsprintf(e+strlen(e), fmt, ap);
65 strcat(e, ".");
66 if (Verbose) {
68 fprintf(stderr, "\nWarning");
69 fprintf(stderr, " %s\n", e);
70 fflush(stderr);
74 static
75 void ConsoleErrorHandler(const char* module, const char* fmt, va_list ap)
77 char e[512] = { '\0' };
79 if (module != NULL) {
80 if (strlen(module) < 500)
81 strcat(strcpy(e, module), ": ");
84 vsprintf(e+strlen(e), fmt, ap);
85 strcat(e, ".");
86 fprintf(stderr, "\nError");
87 fprintf(stderr, " %s\n", e);
88 fflush(stderr);
92 // Issue a warning
93 static
94 void Warning(const char *frm, ...)
96 va_list args;
98 va_start(args, frm);
99 ConsoleWarningHandler("[tificc]", frm, args);
100 va_end(args);
105 // Out of mememory is a fatal error
106 static
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)
121 int a;
123 a = ((x << 8) | x) >> 8; // * 257 / 256
124 if ( a > 0xffff) return 0xffff;
125 return a;
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)
136 static
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);
146 return accum + 3;
148 UTILS_UNUSED_PARAMETER(Stride);
149 UTILS_UNUSED_PARAMETER(CMMcargo);
152 // Formatter for 16bit Lab TIFF (photometric 8)
153 static
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);
172 static
173 unsigned char* PackTIFFLab8(struct _cmstransform_struct* CMMcargo,
174 register cmsUInt16Number wOut[],
175 register cmsUInt8Number* output,
176 register cmsUInt32Number Stride)
178 int a, b;
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));
188 return output;
190 UTILS_UNUSED_PARAMETER(Stride);
191 UTILS_UNUSED_PARAMETER(CMMcargo);
194 static
195 unsigned char* PackTIFFLab16(struct _cmstransform_struct* CMMcargo,
196 register cmsUInt16Number wOut[],
197 register cmsUInt8Number* output,
198 register cmsUInt32Number Stride)
200 int a, b;
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);
218 static
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;
233 else
234 Result.Fmt16 = (bps == 1) ? PackTIFFLab8 : PackTIFFLab16;
237 return Result;
240 static cmsPluginFormatters TiffLabPlugin = { {cmsPluginMagicNumber, 2000, cmsPluginFormattersSig, NULL}, TiffFormatterFactory };
244 // Build up the pixeltype descriptor
245 static
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);
256 if (bps == 1)
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;
269 default:
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;
279 // Any alpha?
281 TIFFGetFieldDefaulted(Bank, TIFFTAG_EXTRASAMPLES, &extra, &info);
283 // Read alpha channels as colorant
285 if (StoreAsAlpha) {
287 ColorChannels = spp;
288 extra = 0;
290 else
291 ColorChannels = spp - extra;
293 switch (Photometric) {
295 case PHOTOMETRIC_MINISWHITE:
297 reverse = 1;
299 // ... fall through ...
301 case PHOTOMETRIC_MINISBLACK:
302 pt = PT_GRAY;
303 break;
305 case PHOTOMETRIC_RGB:
306 pt = PT_RGB;
307 break;
310 case PHOTOMETRIC_PALETTE:
311 FatalError("Sorry, palette images not supported");
312 break;
314 case PHOTOMETRIC_SEPARATED:
316 pt = PixelTypeFromChanCount(ColorChannels);
317 break;
319 case PHOTOMETRIC_YCBCR:
320 TIFFGetField(Bank, TIFFTAG_COMPRESSION, &Compression);
322 uint16 subx, suby;
324 pt = PT_YCbCr;
325 TIFFGetFieldDefaulted(Bank, TIFFTAG_YCBCRSUBSAMPLING, &subx, &suby);
326 if (subx != 1 || suby != 1)
327 FatalError("Sorry, subsampled images not supported");
330 break;
332 case PHOTOMETRIC_ICCLAB:
333 pt = PT_LabV2;
334 break;
336 case PHOTOMETRIC_CIELAB:
337 pt = PT_Lab;
338 labTiffSpecial = TRUE;
339 break;
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
347 break;
349 default:
350 FatalError("Unsupported TIFF color space (Photometric %d)", Photometric);
353 // Convert bits per sample to bytes per sample
355 bps >>= 3;
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
364 static
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
377 static
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;
384 uint32 tw, tl;
385 int PixelCount, j;
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;
418 _TIFFfree(BufferIn);
419 _TIFFfree(BufferOut);
420 return 1;
423 cleanup:
425 _TIFFfree(BufferIn);
426 _TIFFfree(BufferOut);
427 return 0;
431 // Strip based transforms
433 static
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;
440 uint32 sw;
441 uint32 sl;
442 uint32 iml;
443 int j;
444 int PixelCount;
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);
470 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;
481 _TIFFfree(BufferIn);
482 _TIFFfree(BufferOut);
483 return 1;
485 cleanup:
487 _TIFFfree(BufferIn);
488 _TIFFfree(BufferOut);
489 return 0;
493 // Creates minimum required tags
494 static
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) {
516 case PT_GRAY:
517 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
518 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
519 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
520 break;
522 case PT_RGB:
523 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
524 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
525 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
526 break;
528 case PT_CMY:
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);
533 break;
535 case PT_CMYK:
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);
540 break;
542 case PT_Lab:
543 if (BitsPerSample == 16)
544 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, 9);
545 else
546 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
547 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
548 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample); // Needed by TIFF Spec
549 break;
552 // Multi-ink separations
553 case PT_MCH2:
554 case PT_MCH3:
555 case PT_MCH4:
556 case PT_MCH5:
557 case PT_MCH6:
558 case PT_MCH7:
559 case PT_MCH8:
560 case PT_MCH9:
561 case PT_MCH10:
562 case PT_MCH11:
563 case PT_MCH12:
564 case PT_MCH13:
565 case PT_MCH14:
566 case PT_MCH15:
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);
577 else {
578 TIFFSetField(out, TIFFTAG_INKSET, 2);
579 TIFFSetField(out, TIFFTAG_NUMBEROFINKS, nChannels);
582 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
583 break;
586 default:
587 FatalError("Unsupported output colorspace");
590 if (Width == 32)
591 TIFFSetField(out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
595 // Copies a bunch of tages
597 static
598 void CopyOtherTags(TIFF* in, TIFF* out)
600 #define CopyField(tag, v) \
601 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
604 short shortv;
605 uint32 ow, ol;
606 cmsFloat32Number floatv;
607 char *stringv;
608 uint32 longv;
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);
621 if (Width != 32)
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
656 static
657 void DoEmbedProfile(TIFF* Out, const char* ProfileFile)
659 FILE* f;
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) {
669 OutOfMem(size+1);
670 return;
673 EmbedLen = fread(EmbedBuffer, 1, size, f);
675 if (EmbedLen != size)
676 FatalError("Cannot read %ld bytes to %s", size, ProfileFile);
678 fclose(f);
679 EmbedBuffer[EmbedLen] = 0;
681 TIFFSetField(Out, TIFFTAG_ICCPROFILE, EmbedLen, EmbedBuffer);
682 free(EmbedBuffer);
687 static
688 cmsHPROFILE GetTIFFProfile(TIFF* in)
690 cmsCIExyYTRIPLE Primaries;
691 cmsFloat32Number* chr;
692 cmsCIExyY WhitePoint;
693 cmsFloat32Number* wp;
694 int i;
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
708 if (Verbose) {
710 fprintf(stdout, "\n[Embedded profile]\n");
711 PrintProfileInformation(hProfile);
712 fflush(stdout);
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];
738 WhitePoint.Y = 1.0;
740 // Transferfunction is a bit harder....
742 TIFFGetFieldDefaulted(in, TIFFTAG_TRANSFERFUNCTION,
743 &gmr,
744 &gmg,
745 &gmb);
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]);
756 if (Verbose) {
757 fprintf(stdout, "\n[Colorimetric TIFF]\n");
761 return hProfile;
765 return NULL;
769 // Transform one image
770 static
771 int TransformImage(TIFF* in, TIFF* out, const char *cDefInpProf)
773 cmsHPROFILE hIn, hOut, hProof, hInkLimit = NULL;
774 cmsHTRANSFORM xform;
775 cmsUInt32Number wInput, wOutput;
776 int OutputColorSpace;
777 int bps = Width / 8;
778 cmsUInt32Number dwFlags = 0;
779 int nPlanes;
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;
797 case 1: break;
799 default: FatalError("Unknown precalculation mode '%d'", PrecalcMode);
803 if (GamutCheck)
804 dwFlags |= cmsFLAGS_GAMUTCHECK;
806 hProof = NULL;
807 hOut = NULL;
809 if (lIsDeviceLink) {
811 hIn = cmsOpenProfileFromFile(cDefInpProf, "r");
813 else {
815 hIn = GetTIFFProfile(in);
817 if (hIn == NULL)
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");
839 if (!lIsDeviceLink)
840 OutputColorSpace = _cmsLCMScolorSpace(cmsGetColorSpace(hOut));
841 else
842 OutputColorSpace = _cmsLCMScolorSpace(cmsGetPCS(hIn));
844 wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace, bps);
846 WriteOutputTags(out, OutputColorSpace, bps);
847 CopyOtherTags(in, out);
849 // Ink limit
850 if (InkLimit != 400.0 &&
851 (OutputColorSpace == PT_CMYK || OutputColorSpace == PT_CMY)) {
853 cmsHPROFILE hProfiles[10];
854 int nProfiles = 0;
857 hInkLimit = cmsCreateInkLimitingDeviceLink(cmsGetColorSpace(hOut), InkLimit);
859 hProfiles[nProfiles++] = hIn;
860 if (hProof) {
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);
872 else {
874 xform = cmsCreateProofingTransform(hIn, wInput,
875 hOut, wOutput,
876 hProof, Intent,
877 ProofingIntent,
878 dwFlags);
881 cmsCloseProfile(hIn);
882 cmsCloseProfile(hOut);
884 if (hInkLimit)
885 cmsCloseProfile(hInkLimit);
886 if (hProof)
887 cmsCloseProfile(hProof);
889 if (xform == NULL) return 0;
891 // Planar stuff
892 if (T_PLANAR(wInput))
893 nPlanes = T_CHANNELS(wInput) + T_EXTRA(wInput);
894 else
895 nPlanes = 1;
898 // Handle tile by tile or strip by strip
899 if (TIFFIsTiled(in)) {
901 TileBasedXform(xform, in, out, nPlanes);
903 else {
904 StripBasedXform(xform, in, out, nPlanes);
908 cmsDeleteTransform(xform);
910 TIFFWriteDirectory(out);
912 return 1;
916 // Print help
917 static
918 void Help(int level)
920 fprintf(stderr, "little cms ICC profile applier for TIFF - v6.2 [LittleCMS %2.2f]\n\n", LCMS_VERSION / 1000.0);
921 fflush(stderr);
923 switch(level) {
925 default:
926 case 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);
962 break;
964 case 1:
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);
978 break;
980 case 2:
981 PrintBuiltins();
982 break;
984 case 3:
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");
992 break;
995 fflush(stderr);
996 exit(0);
1000 // The toggles stuff
1002 static
1003 void HandleSwitches(int argc, char *argv[])
1005 int s;
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) {
1009 switch (s) {
1011 case 'a':
1012 case 'A':
1013 StoreAsAlpha = TRUE;
1014 break;
1015 case 'b':
1016 case 'B':
1017 BlackWhiteCompensation = TRUE;
1018 break;
1020 case 'c':
1021 case 'C':
1022 PrecalcMode = atoi(xoptarg);
1023 if (PrecalcMode < 0 || PrecalcMode > 3)
1024 FatalError("Unknown precalc mode '%d'", PrecalcMode);
1025 break;
1027 case 'd':
1028 case 'D': ObserverAdaptationState = atof(xoptarg);
1029 if (ObserverAdaptationState < 0 ||
1030 ObserverAdaptationState > 1.0)
1031 Warning("Adaptation state should be 0..1");
1032 break;
1034 case 'e':
1035 case 'E':
1036 EmbedProfile = TRUE;
1037 break;
1039 case 'g':
1040 case 'G':
1041 GamutCheck = TRUE;
1042 break;
1044 case 'v':
1045 case 'V':
1046 Verbose = TRUE;
1047 break;
1049 case 'i':
1050 case 'I':
1051 if (lIsDeviceLink)
1052 FatalError("Device-link already specified");
1054 cInpProf = xoptarg;
1055 break;
1057 case 'o':
1058 case 'O':
1059 if (lIsDeviceLink)
1060 FatalError("Device-link already specified");
1062 cOutProf = xoptarg;
1063 break;
1065 case 'l':
1066 case 'L':
1067 if (cInpProf != NULL || cOutProf != NULL)
1068 FatalError("input/output profiles already specified");
1070 cInpProf = xoptarg;
1071 lIsDeviceLink = TRUE;
1072 break;
1074 case 'p':
1075 case 'P':
1076 cProofing = xoptarg;
1077 break;
1079 case 't':
1080 case 'T':
1081 Intent = atoi(xoptarg);
1082 break;
1084 case 'm':
1085 case 'M':
1086 ProofingIntent = atoi(xoptarg);
1087 break;
1089 case 'N':
1090 case 'n':
1091 IgnoreEmbedded = TRUE;
1092 break;
1094 case 'W':
1095 case 'w':
1096 Width = atoi(xoptarg);
1097 if (Width != 8 && Width != 16 && Width != 32)
1098 FatalError("Only 8, 16 and 32 bps are supported");
1099 break;
1101 case 'k':
1102 case 'K':
1103 InkLimit = atof(xoptarg);
1104 if (InkLimit < 0.0 || InkLimit > 400.0)
1105 FatalError("Ink limit must be 0%%..400%%");
1106 break;
1109 case 's':
1110 case 'S': SaveEmbedded = xoptarg;
1111 break;
1113 case 'H':
1114 case 'h': {
1116 int a = atoi(xoptarg);
1117 Help(a);
1119 break;
1121 default:
1123 FatalError("Unknown option - run without args to see valid ones");
1130 // The main sink
1132 int main(int argc, char* argv[])
1134 TIFF *in, *out;
1136 cmsPlugin(&TiffLabPlugin);
1138 InitUtils("tificc");
1140 HandleSwitches(argc, argv);
1142 if ((argc - xoptind) != 2) {
1144 Help(0);
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");
1156 if (out == NULL) {
1158 TIFFClose(in);
1159 FatalError("Unable to write '%s'", argv[xoptind+1]);
1162 do {
1164 TransformImage(in, out, cInpProf);
1167 } while (TIFFReadDirectory(in));
1170 if (Verbose) { fprintf(stdout, "\n"); fflush(stdout); }
1172 TIFFClose(in);
1173 TIFFClose(out);
1175 return 0;