Tizen 2.0 Release
[external/lcms.git] / utils / tificc / tificc.c
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:
12 //
13 // The above copyright notice and this permission notice shall be included in 
14 // all copies or substantial portions of the Software.
15 //
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.
23 //
24 //---------------------------------------------------------------------------------
25
26 // This program does apply profiles to (some) TIFF files
27
28 #include "lcms2_plugin.h"
29 #include "tiffio.h"
30 #include "utils.h"
31
32
33 // Flags
34
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;
42 static cmsBool InputLabUsingICC       = FALSE;
43
44 static int Intent                  = INTENT_PERCEPTUAL;
45 static int ProofingIntent          = INTENT_PERCEPTUAL;
46 static int PrecalcMode             = 1;
47 static cmsFloat64Number InkLimit   = 400;
48
49 static cmsFloat64Number ObserverAdaptationState  = 1.0;  // According ICC 4.3 this is the default
50
51 static const char *cInpProf  = NULL;
52 static const char *cOutProf  = NULL;
53 static const char *cProofing = NULL;
54
55 static const char* SaveEmbedded = NULL;
56
57 // Console error & warning
58 static
59 void ConsoleWarningHandler(const char* module, const char* fmt, va_list ap)
60 {
61     char e[512] = { '\0' };
62     if (module != NULL)
63         strcat(strcpy(e, module), ": ");
64
65     vsprintf(e+strlen(e), fmt, ap);
66     strcat(e, ".");
67     if (Verbose) {
68
69         fprintf(stderr, "\nWarning");
70         fprintf(stderr, " %s\n", e);
71         fflush(stderr);
72     }
73 }
74
75 static
76 void ConsoleErrorHandler(const char* module, const char* fmt, va_list ap)
77 {
78     char e[512] = { '\0' };
79
80     if (module != NULL) {
81         if (strlen(module) < 500)
82                strcat(strcpy(e, module), ": ");
83     }
84
85     vsprintf(e+strlen(e), fmt, ap);
86     strcat(e, ".");
87     fprintf(stderr, "\nError");
88     fprintf(stderr, " %s\n", e);
89     fflush(stderr);
90 }
91
92
93 // Issue a warning
94 static
95 void Warning(const char *frm, ...)
96 {
97     va_list args;
98
99     va_start(args, frm);
100     ConsoleWarningHandler("[tificc]", frm, args);
101     va_end(args);
102 }
103
104
105
106 // Out of mememory is a fatal error
107 static
108 void OutOfMem(cmsUInt32Number size)
109 {
110     FatalError("Out of memory on allocating %d bytes.", size);  
111 }
112
113
114 // -----------------------------------------------------------------------------------------------
115
116 // In TIFF, Lab is encoded in a different way, so let's use the plug-in 
117 // capabilities of lcms2 to change the meaning of TYPE_Lab_8.  
118
119 // * 0xffff / 0xff00 = (255 * 257) / (255 * 256) = 257 / 256
120 static int FromLabV2ToLabV4(int x) 
121 {
122     int a;
123
124     a = ((x << 8) | x) >> 8;  // * 257 / 256
125     if ( a > 0xffff) return 0xffff;
126     return a;
127 }
128
129 // * 0xf00 / 0xffff = * 256 / 257
130 static int FromLabV4ToLabV2(int x) 
131 {
132     return ((x << 8) + 0x80) / 257;
133 }
134
135
136 // Formatter for 8bit Lab TIFF (photometric 8)
137 static
138 unsigned char* UnrollTIFFLab8(struct _cmstransform_struct* CMMcargo,
139                               register cmsUInt16Number wIn[], 
140                               register cmsUInt8Number* accum, 
141                               register cmsUInt32Number Stride)
142 {
143     wIn[0] = (cmsUInt16Number) FromLabV2ToLabV4((accum[0]) << 8);
144     wIn[1] = (cmsUInt16Number) FromLabV2ToLabV4(((accum[1] > 127) ? (accum[1] - 128) : (accum[1] + 128)) << 8);
145     wIn[2] = (cmsUInt16Number) FromLabV2ToLabV4(((accum[2] > 127) ? (accum[2] - 128) : (accum[2] + 128)) << 8);
146
147     return accum + 3;
148
149     UTILS_UNUSED_PARAMETER(Stride);
150     UTILS_UNUSED_PARAMETER(CMMcargo);
151 }
152
153 static
154 unsigned char* PackTIFFLab8(struct _cmstransform_struct* CMMcargo, 
155                             register cmsUInt16Number wOut[], 
156                             register cmsUInt8Number* output, 
157                             register cmsUInt32Number Stride)
158 {
159     int a, b;
160
161     *output++ = (cmsUInt8Number) (FromLabV4ToLabV2(wOut[0] + 0x0080) >> 8);
162
163     a = (FromLabV4ToLabV2(wOut[1]) + 0x0080) >> 8;
164     b = (FromLabV4ToLabV2(wOut[2]) + 0x0080) >> 8;
165
166     *output++ = (cmsUInt8Number) ((a < 128) ? (a + 128) : (a - 128));
167     *output++ = (cmsUInt8Number) ((b < 128) ? (b + 128) : (b - 128));
168
169     return output;
170
171     UTILS_UNUSED_PARAMETER(Stride);
172     UTILS_UNUSED_PARAMETER(CMMcargo);
173 }
174
175
176 static
177 cmsFormatter TiffFormatterFactory(cmsUInt32Number Type, 
178                                   cmsFormatterDirection Dir, 
179                                   cmsUInt32Number dwFlags)
180 {
181     cmsFormatter Result = { NULL };
182
183     if (Type == TYPE_Lab_8 && !(dwFlags & CMS_PACK_FLAGS_FLOAT)) {
184
185         if (Dir == cmsFormatterInput)
186             Result.Fmt16 = UnrollTIFFLab8;
187         else
188             Result.Fmt16 = PackTIFFLab8;
189     }
190
191     return Result;
192 }
193
194 static cmsPluginFormatters TiffLabPlugin = { {cmsPluginMagicNumber, 2000, cmsPluginFormattersSig, NULL}, TiffFormatterFactory };
195
196
197 // Build up the pixeltype descriptor
198 static
199 cmsUInt32Number GetInputPixelType(TIFF *Bank)
200 {
201     uint16 Photometric, bps, spp, extra, PlanarConfig, *info;
202     uint16 Compression, reverse = 0;
203     int ColorChannels, IsPlanar = 0, pt = 0, IsFlt;
204
205     TIFFGetField(Bank,           TIFFTAG_PHOTOMETRIC,   &Photometric);
206     TIFFGetFieldDefaulted(Bank,  TIFFTAG_BITSPERSAMPLE, &bps);
207
208     if (bps == 1)
209         FatalError("Sorry, bilevel TIFFs has nothig to do with ICC profiles");
210
211     if (bps != 8 && bps != 16 && bps != 32)
212         FatalError("Sorry, 8, 16 or 32 bits per sample only");
213
214     TIFFGetFieldDefaulted(Bank, TIFFTAG_SAMPLESPERPIXEL, &spp);
215     TIFFGetFieldDefaulted(Bank, TIFFTAG_PLANARCONFIG, &PlanarConfig);
216
217     switch (PlanarConfig) {
218
219      case PLANARCONFIG_CONTIG: IsPlanar = 0; break;
220      case PLANARCONFIG_SEPARATE: IsPlanar = 1; break;
221      default:
222
223          FatalError("Unsupported planar configuration (=%d) ", (int) PlanarConfig);
224     }
225
226     // If Samples per pixel == 1, PlanarConfiguration is irrelevant and need
227     // not to be included.
228
229     if (spp == 1) IsPlanar = 0;
230
231     // Any alpha?
232
233     TIFFGetFieldDefaulted(Bank, TIFFTAG_EXTRASAMPLES, &extra, &info);
234
235     // Read alpha channels as colorant
236
237     if (StoreAsAlpha) {
238
239         ColorChannels = spp;
240         extra = 0;
241     }
242     else
243         ColorChannels = spp - extra;
244
245     switch (Photometric) {
246
247      case PHOTOMETRIC_MINISWHITE:
248
249          reverse = 1;
250
251          // ... fall through ...
252
253      case PHOTOMETRIC_MINISBLACK:                                   
254          pt = PT_GRAY;                                
255          break;
256
257      case PHOTOMETRIC_RGB:                                   
258          pt = PT_RGB;
259          break;
260
261
262      case PHOTOMETRIC_PALETTE:                                             
263          FatalError("Sorry, palette images not supported"); 
264          break;
265
266      case PHOTOMETRIC_SEPARATED: 
267
268          pt = PixelTypeFromChanCount(ColorChannels);
269          break;
270
271      case PHOTOMETRIC_YCBCR:
272          TIFFGetField(Bank, TIFFTAG_COMPRESSION, &Compression);
273          {
274              uint16 subx, suby;
275
276              pt = PT_YCbCr;
277              TIFFGetFieldDefaulted(Bank, TIFFTAG_YCBCRSUBSAMPLING, &subx, &suby);
278              if (subx != 1 || suby != 1)
279                  FatalError("Sorry, subsampled images not supported");
280
281          }
282          break;
283
284      case PHOTOMETRIC_ICCLAB:
285          pt = PT_Lab;
286          InputLabUsingICC = TRUE;
287          break;
288
289      case PHOTOMETRIC_CIELAB:
290          pt = PT_Lab;
291          InputLabUsingICC = FALSE;
292          break;
293
294
295      case PHOTOMETRIC_LOGLUV:      // CIE Log2(L) (u',v') 
296
297          TIFFSetField(Bank, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_16BIT);
298          pt = PT_YUV;             // *ICCSpace = icSigLuvData;
299          bps = 16;                // 16 bits forced by LibTiff
300          break;
301
302      default:
303          FatalError("Unsupported TIFF color space (Photometric %d)", Photometric);
304     }
305
306     // Convert bits per sample to bytes per sample
307
308     bps >>= 3; 
309     IsFlt = (bps == 0) || (bps == 4);
310
311     return (FLOAT_SH(IsFlt)|COLORSPACE_SH(pt)|PLANAR_SH(IsPlanar)|EXTRA_SH(extra)|CHANNELS_SH(ColorChannels)|BYTES_SH(bps)|FLAVOR_SH(reverse));
312 }
313
314
315
316 // Rearrange pixel type to build output descriptor
317 static
318 cmsUInt32Number ComputeOutputFormatDescriptor(cmsUInt32Number dwInput, int OutColorSpace, int bps)
319 {
320     int IsPlanar  = T_PLANAR(dwInput);
321     int Channels  = ChanCountFromPixelType(OutColorSpace);
322     int IsFlt = (bps == 0) || (bps == 4);
323
324     return (FLOAT_SH(IsFlt)|COLORSPACE_SH(OutColorSpace)|PLANAR_SH(IsPlanar)|CHANNELS_SH(Channels)|BYTES_SH(bps));
325 }
326
327
328
329 // Tile based transforms
330 static
331 int TileBasedXform(cmsHTRANSFORM hXForm, TIFF* in, TIFF* out, int nPlanes)
332 {
333     tsize_t BufSizeIn  = TIFFTileSize(in);
334     tsize_t BufSizeOut = TIFFTileSize(out);
335     unsigned char *BufferIn, *BufferOut;
336     ttile_t i, TileCount = TIFFNumberOfTiles(in) / nPlanes;
337     uint32 tw, tl;
338     int PixelCount, j;
339
340
341     TIFFGetFieldDefaulted(in, TIFFTAG_TILEWIDTH,  &tw);
342     TIFFGetFieldDefaulted(in, TIFFTAG_TILELENGTH, &tl);
343
344     PixelCount = (int) tw * tl;
345
346     BufferIn = (unsigned char *) _TIFFmalloc(BufSizeIn * nPlanes);
347     if (!BufferIn) OutOfMem(BufSizeIn * nPlanes);
348
349     BufferOut = (unsigned char *) _TIFFmalloc(BufSizeOut * nPlanes);
350     if (!BufferOut) OutOfMem(BufSizeOut * nPlanes);
351
352
353     for (i = 0; i < TileCount; i++) {
354
355         for (j=0; j < nPlanes; j++) {
356
357             if (TIFFReadEncodedTile(in, i + (j* TileCount), 
358                 BufferIn + (j*BufSizeIn), BufSizeIn) < 0)   goto cleanup;
359         }
360
361         cmsDoTransform(hXForm, BufferIn, BufferOut, PixelCount);
362
363         for (j=0; j < nPlanes; j++) {
364
365             if (TIFFWriteEncodedTile(out, i + (j*TileCount),
366                 BufferOut + (j*BufSizeOut), BufSizeOut) < 0) goto cleanup;
367         }
368
369     }
370
371     _TIFFfree(BufferIn);
372     _TIFFfree(BufferOut);
373     return 1;
374
375
376 cleanup:
377
378     _TIFFfree(BufferIn);
379     _TIFFfree(BufferOut);
380     return 0;
381 }
382
383
384 // Strip based transforms
385
386 static
387 int StripBasedXform(cmsHTRANSFORM hXForm, TIFF* in, TIFF* out, int nPlanes)
388 {
389     tsize_t BufSizeIn  = TIFFStripSize(in);
390     tsize_t BufSizeOut = TIFFStripSize(out);
391     unsigned char *BufferIn, *BufferOut;
392     ttile_t i, StripCount = TIFFNumberOfStrips(in) / nPlanes;
393     uint32 sw;
394     uint32 sl;
395     uint32 iml;
396     int j;
397     int PixelCount;
398
399     TIFFGetFieldDefaulted(in, TIFFTAG_IMAGEWIDTH,  &sw);
400     TIFFGetFieldDefaulted(in, TIFFTAG_ROWSPERSTRIP, &sl);
401     TIFFGetFieldDefaulted(in, TIFFTAG_IMAGELENGTH, &iml);
402
403     // It is possible to get infinite rows per strip
404     if (sl == 0 || sl > iml)
405         sl = iml;   // One strip for whole image
406
407     BufferIn = (unsigned char *) _TIFFmalloc(BufSizeIn * nPlanes);
408     if (!BufferIn) OutOfMem(BufSizeIn * nPlanes);
409
410     BufferOut = (unsigned char *) _TIFFmalloc(BufSizeOut * nPlanes);
411     if (!BufferOut) OutOfMem(BufSizeOut * nPlanes);
412
413
414     for (i = 0; i < StripCount; i++) {
415
416         for (j=0; j < nPlanes; j++) {
417
418             if (TIFFReadEncodedStrip(in, i + (j * StripCount), 
419                 BufferIn + (j * BufSizeIn), BufSizeIn) < 0)   goto cleanup;
420         }
421
422         PixelCount = (int) sw * (iml < sl ? iml : sl);
423         iml -= sl;
424
425         cmsDoTransform(hXForm, BufferIn, BufferOut, PixelCount);
426
427         for (j=0; j < nPlanes; j++) {
428             if (TIFFWriteEncodedStrip(out, i + (j * StripCount), 
429                 BufferOut + j * BufSizeOut, BufSizeOut) < 0) goto cleanup;
430         }
431
432     }
433
434     _TIFFfree(BufferIn);
435     _TIFFfree(BufferOut);
436     return 1;
437
438 cleanup:
439
440     _TIFFfree(BufferIn);
441     _TIFFfree(BufferOut);
442     return 0;
443 }
444
445
446 // Creates minimum required tags
447 static
448 void WriteOutputTags(TIFF *out, int Colorspace, int BytesPerSample)
449 {
450     int BitsPerSample = (8 * BytesPerSample);
451     int nChannels     = ChanCountFromPixelType(Colorspace);
452
453     uint16 Extra[] = { EXTRASAMPLE_UNASSALPHA, 
454                        EXTRASAMPLE_UNASSALPHA, 
455                        EXTRASAMPLE_UNASSALPHA,
456                        EXTRASAMPLE_UNASSALPHA,
457                        EXTRASAMPLE_UNASSALPHA, 
458                        EXTRASAMPLE_UNASSALPHA, 
459                        EXTRASAMPLE_UNASSALPHA,
460                        EXTRASAMPLE_UNASSALPHA,
461                        EXTRASAMPLE_UNASSALPHA,
462                        EXTRASAMPLE_UNASSALPHA,
463                        EXTRASAMPLE_UNASSALPHA
464     };
465
466
467   switch (Colorspace) {
468
469   case PT_GRAY:
470       TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
471       TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
472       TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
473       break;
474
475   case PT_RGB:
476       TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
477       TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
478       TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
479       break;
480
481   case PT_CMY:
482       TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
483       TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
484       TIFFSetField(out, TIFFTAG_INKSET, 2);
485       TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
486       break;
487
488   case PT_CMYK:
489       TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
490       TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 4);
491       TIFFSetField(out, TIFFTAG_INKSET, INKSET_CMYK);
492       TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
493       break;
494
495   case PT_Lab:
496       if (BitsPerSample == 16) 
497           TIFFSetField(out, TIFFTAG_PHOTOMETRIC, 9);
498       else
499           TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
500       TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
501       TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);    // Needed by TIFF Spec
502       break;
503
504
505       // Multi-ink separations
506   case PT_MCH2:
507   case PT_MCH3:
508   case PT_MCH4:
509   case PT_MCH5:
510   case PT_MCH6:
511   case PT_MCH7:
512   case PT_MCH8:
513   case PT_MCH9:
514   case PT_MCH10:
515   case PT_MCH11:
516   case PT_MCH12:
517   case PT_MCH13:
518   case PT_MCH14:
519   case PT_MCH15:
520
521       TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
522       TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, nChannels);
523
524       if (StoreAsAlpha && nChannels >= 4) {                                     
525           // CMYK plus extra alpha
526           TIFFSetField(out, TIFFTAG_EXTRASAMPLES, nChannels - 4, Extra);            
527           TIFFSetField(out, TIFFTAG_INKSET, 1);
528           TIFFSetField(out, TIFFTAG_NUMBEROFINKS, 4);
529       }
530       else {            
531           TIFFSetField(out, TIFFTAG_INKSET, 2);
532           TIFFSetField(out, TIFFTAG_NUMBEROFINKS, nChannels);
533       }
534
535       TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
536       break;
537
538
539   default:
540       FatalError("Unsupported output colorspace");
541     }
542
543   if (Width == 32) 
544       TIFFSetField(out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
545 }
546
547
548 // Copies a bunch of tages
549
550 static
551 void CopyOtherTags(TIFF* in, TIFF* out)
552 {
553 #define CopyField(tag, v) \
554     if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
555
556
557     short shortv;
558     uint32 ow, ol;
559     cmsFloat32Number floatv;
560     char *stringv;
561     uint32 longv;
562
563     CopyField(TIFFTAG_SUBFILETYPE, longv);
564
565     TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &ow);
566     TIFFGetField(in, TIFFTAG_IMAGELENGTH, &ol);
567
568     TIFFSetField(out, TIFFTAG_IMAGEWIDTH, ow);
569     TIFFSetField(out, TIFFTAG_IMAGELENGTH, ol);
570
571     CopyField(TIFFTAG_PLANARCONFIG, shortv);
572     CopyField(TIFFTAG_COMPRESSION, shortv);
573
574     if (Width != 32) 
575         CopyField(TIFFTAG_PREDICTOR, shortv);
576
577     CopyField(TIFFTAG_THRESHHOLDING, shortv);
578     CopyField(TIFFTAG_FILLORDER, shortv);
579     CopyField(TIFFTAG_ORIENTATION, shortv);
580     CopyField(TIFFTAG_MINSAMPLEVALUE, shortv);
581     CopyField(TIFFTAG_MAXSAMPLEVALUE, shortv);
582     CopyField(TIFFTAG_XRESOLUTION, floatv);
583     CopyField(TIFFTAG_YRESOLUTION, floatv);
584     CopyField(TIFFTAG_RESOLUTIONUNIT, shortv);
585     CopyField(TIFFTAG_ROWSPERSTRIP, longv);
586     CopyField(TIFFTAG_XPOSITION, floatv);
587     CopyField(TIFFTAG_YPOSITION, floatv);
588     CopyField(TIFFTAG_IMAGEDEPTH, longv);
589     CopyField(TIFFTAG_TILEDEPTH, longv);
590
591     CopyField(TIFFTAG_TILEWIDTH,  longv);
592     CopyField(TIFFTAG_TILELENGTH, longv);
593
594     CopyField(TIFFTAG_ARTIST, stringv);
595     CopyField(TIFFTAG_IMAGEDESCRIPTION, stringv);
596     CopyField(TIFFTAG_MAKE, stringv);
597     CopyField(TIFFTAG_MODEL, stringv);
598
599     CopyField(TIFFTAG_DATETIME, stringv);
600     CopyField(TIFFTAG_HOSTCOMPUTER, stringv);
601     CopyField(TIFFTAG_PAGENAME, stringv);
602     CopyField(TIFFTAG_DOCUMENTNAME, stringv);
603
604 }
605
606 // A replacement for (the nonstandard) filelenght
607
608
609 static
610 void DoEmbedProfile(TIFF* Out, const char* ProfileFile)
611 {
612     FILE* f;
613     cmsUInt32Number size, EmbedLen;
614     cmsUInt8Number* EmbedBuffer;
615
616     f = fopen(ProfileFile, "rb");
617     if (f == NULL) return;
618
619     size = cmsfilelength(f);
620     EmbedBuffer = (cmsUInt8Number*) malloc(size + 1);
621     if (EmbedBuffer == NULL) { 
622         OutOfMem(size+1);
623         return;
624     }
625
626     EmbedLen = fread(EmbedBuffer, 1, size, f);
627
628     if (EmbedLen != size) 
629         FatalError("Cannot read %ld bytes to %s", size, ProfileFile);
630
631     fclose(f);
632     EmbedBuffer[EmbedLen] = 0;
633
634     TIFFSetField(Out, TIFFTAG_ICCPROFILE, EmbedLen, EmbedBuffer);
635     free(EmbedBuffer);
636 }
637
638
639
640 static
641 cmsHPROFILE GetTIFFProfile(TIFF* in)
642 {    
643     cmsCIExyYTRIPLE Primaries;
644     cmsFloat32Number* chr;
645     cmsCIExyY WhitePoint;
646     cmsFloat32Number* wp;
647     int i;       
648     cmsToneCurve* Curve[3]; 
649     cmsUInt16Number *gmr, *gmg, *gmb;
650     cmsHPROFILE hProfile;
651     cmsUInt32Number EmbedLen;
652     cmsUInt8Number* EmbedBuffer;
653
654     if (IgnoreEmbedded) return NULL;
655
656     if (TIFFGetField(in, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer)) {
657
658         hProfile = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
659
660         // Print description found in the profile
661         if (Verbose) {
662
663             fprintf(stdout, "\n[Embedded profile]\n");
664             PrintProfileInformation(hProfile);                       
665             fflush(stdout);
666         }
667
668         if (hProfile != NULL && SaveEmbedded != NULL)
669             SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded);
670
671         if (hProfile) return hProfile;
672     }
673
674     // Try to see if "colorimetric" tiff
675
676     if (TIFFGetField(in, TIFFTAG_PRIMARYCHROMATICITIES, &chr)) {
677
678         Primaries.Red.x   =  chr[0];
679         Primaries.Red.y   =  chr[1];
680         Primaries.Green.x =  chr[2];
681         Primaries.Green.y =  chr[3];
682         Primaries.Blue.x  =  chr[4];
683         Primaries.Blue.y  =  chr[5];
684
685         Primaries.Red.Y = Primaries.Green.Y = Primaries.Blue.Y = 1.0;
686
687         if (TIFFGetField(in, TIFFTAG_WHITEPOINT, &wp)) {
688
689             WhitePoint.x = wp[0];
690             WhitePoint.y = wp[1];
691             WhitePoint.Y = 1.0;
692
693             // Transferfunction is a bit harder....
694
695             TIFFGetFieldDefaulted(in, TIFFTAG_TRANSFERFUNCTION,
696                 &gmr, 
697                 &gmg,
698                 &gmb);
699
700             Curve[0] = cmsBuildTabulatedToneCurve16(NULL, 256, gmr);
701             Curve[1] = cmsBuildTabulatedToneCurve16(NULL, 256, gmg);
702             Curve[2] = cmsBuildTabulatedToneCurve16(NULL, 256, gmb);
703
704             hProfile = cmsCreateRGBProfileTHR(NULL, &WhitePoint, &Primaries, Curve);
705
706             for (i=0; i < 3; i++)
707                 cmsFreeToneCurve(Curve[i]);
708
709             if (Verbose) {
710                 fprintf(stdout, "\n[Colorimetric TIFF]\n");
711             }
712
713
714             return hProfile;
715         }
716     }
717
718     return NULL;
719 }
720
721
722 // Transform one image
723 static
724 int TransformImage(TIFF* in, TIFF* out, const char *cDefInpProf)
725 {
726     cmsHPROFILE hIn, hOut, hProof, hInkLimit = NULL;
727     cmsHTRANSFORM xform;
728     cmsUInt32Number wInput, wOutput;
729     int OutputColorSpace;
730     int bps = Width / 8;
731     cmsUInt32Number dwFlags = 0;        
732     int nPlanes;
733
734     // Observer adaptation state (only meaningful on absolute colorimetric intent)
735
736     cmsSetAdaptationState(ObserverAdaptationState);
737
738     if (EmbedProfile && cOutProf) 
739         DoEmbedProfile(out, cOutProf);
740
741     if (BlackWhiteCompensation) 
742         dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;           
743
744
745     switch (PrecalcMode) {
746
747        case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break;
748        case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
749        case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
750        case 1: break;
751
752        default: FatalError("Unknown precalculation mode '%d'", PrecalcMode);
753     }
754
755
756     if (GamutCheck)
757         dwFlags |= cmsFLAGS_GAMUTCHECK;
758
759     hProof = NULL;
760     hOut = NULL;
761
762     if (lIsDeviceLink) {
763
764         hIn = cmsOpenProfileFromFile(cDefInpProf, "r");                  
765     }
766     else {
767
768         hIn =  GetTIFFProfile(in);
769
770         if (hIn == NULL)                    
771             hIn = OpenStockProfile(NULL, cDefInpProf);               
772
773         hOut = OpenStockProfile(NULL, cOutProf);
774
775         if (cProofing != NULL) {
776
777             hProof = OpenStockProfile(NULL, cProofing);
778             dwFlags |= cmsFLAGS_SOFTPROOFING;
779         }
780     }
781
782     // Take input color space
783
784     wInput = GetInputPixelType(in);
785
786     // Assure both, input profile and input TIFF are on same colorspace
787
788     if (_cmsLCMScolorSpace(cmsGetColorSpace(hIn)) != (int) T_COLORSPACE(wInput))
789         FatalError("Input profile is not operating in proper color space");
790
791
792     if (!lIsDeviceLink) 
793         OutputColorSpace = _cmsLCMScolorSpace(cmsGetColorSpace(hOut));
794     else 
795         OutputColorSpace = _cmsLCMScolorSpace(cmsGetPCS(hIn));
796
797     wOutput  = ComputeOutputFormatDescriptor(wInput, OutputColorSpace, bps);
798
799     WriteOutputTags(out, OutputColorSpace, bps);
800     CopyOtherTags(in, out);
801
802     // Ink limit
803     if (InkLimit != 400.0 && 
804         (OutputColorSpace == PT_CMYK || OutputColorSpace == PT_CMY)) {
805
806             cmsHPROFILE hProfiles[10];
807             int nProfiles = 0;
808
809
810             hInkLimit = cmsCreateInkLimitingDeviceLink(cmsGetColorSpace(hOut), InkLimit);
811
812             hProfiles[nProfiles++] = hIn;
813             if (hProof) {
814                 hProfiles[nProfiles++] = hProof;
815                 hProfiles[nProfiles++] = hProof;
816             }
817
818             hProfiles[nProfiles++] = hOut;
819             hProfiles[nProfiles++] = hInkLimit;
820
821             xform = cmsCreateMultiprofileTransform(hProfiles, nProfiles, 
822                                                    wInput, wOutput, Intent, dwFlags);
823
824     }
825     else {
826
827         xform = cmsCreateProofingTransform(hIn, wInput, 
828                                            hOut, wOutput, 
829                                            hProof, Intent, 
830                                            ProofingIntent, 
831                                            dwFlags);
832     }
833
834     cmsCloseProfile(hIn);
835     cmsCloseProfile(hOut);
836
837     if (hInkLimit) 
838         cmsCloseProfile(hInkLimit);
839     if (hProof) 
840         cmsCloseProfile(hProof);
841
842     if (xform == NULL) return 0;
843
844     // Planar stuff
845     if (T_PLANAR(wInput)) 
846         nPlanes = T_CHANNELS(wInput) + T_EXTRA(wInput);
847     else
848         nPlanes = 1;
849
850
851     // Handle tile by tile or strip by strip
852     if (TIFFIsTiled(in)) {
853
854         TileBasedXform(xform, in, out, nPlanes);
855     }
856     else {
857         StripBasedXform(xform, in, out, nPlanes);
858     }
859
860
861     cmsDeleteTransform(xform);
862
863     TIFFWriteDirectory(out);
864
865     return 1;
866 }
867
868
869 // Print help
870 static
871 void Help(int level)
872 {
873     fprintf(stderr, "little cms ICC profile applier for TIFF - v6.1 [LittleCMS %2.2f]\n\n", LCMS_VERSION / 1000.0);
874     fflush(stderr);
875
876     switch(level) {
877
878      default:
879      case 0:
880
881          fprintf(stderr, "usage: tifficc [flags] input.tif output.tif\n");
882
883          fprintf(stderr, "\nflags:\n\n");
884          fprintf(stderr, "%cv - Verbose\n", SW);
885          fprintf(stderr, "%ci<profile> - Input profile (defaults to sRGB)\n", SW);
886          fprintf(stderr, "%co<profile> - Output profile (defaults to sRGB)\n", SW);   
887          fprintf(stderr, "%cl<profile> - Transform by device-link profile\n", SW); 
888
889          PrintRenderingIntents();
890
891          fprintf(stderr, "%cb - Black point compensation\n", SW);
892          fprintf(stderr, "%cd<0..1> - Observer adaptation state (abs.col. only)\n", SW);
893
894          fprintf(stderr, "%cc<0,1,2,3> - Precalculates transform (0=Off, 1=Normal, 2=Hi-res, 3=LoRes)\n", SW);     
895          fprintf(stderr, "\n");
896
897          fprintf(stderr, "%cw<8,16,32> - Output depth. Use 32 for floating-point\n\n", SW);
898          fprintf(stderr, "%ca - Handle channels > 4 as alpha\n", SW);
899
900          fprintf(stderr, "%cn - Ignore embedded profile on input\n", SW);
901          fprintf(stderr, "%ce - Embed destination profile\n", SW);
902          fprintf(stderr, "%cs<new profile> - Save embedded profile as <new profile>\n", SW);
903          fprintf(stderr, "\n");
904
905
906          fprintf(stderr, "%cp<profile> - Soft proof profile\n", SW);
907          fprintf(stderr, "%cm<n> - Soft proof intent\n", SW);
908          fprintf(stderr, "%cg - Marks out-of-gamut colors on softproof\n", SW);
909
910          fprintf(stderr, "\n"); 
911    
912          fprintf(stderr, "%ck<0..400> - Ink-limiting in %% (CMYK only)\n", SW);       
913          fprintf(stderr, "\n");
914          fprintf(stderr, "%ch<0,1,2,3> - More help\n", SW);
915          break;
916
917      case 1:
918
919          fprintf(stderr, "Examples:\n\n"
920              "To color correct from scanner to sRGB:\n"
921              "\ttifficc %ciscanner.icm in.tif out.tif\n"
922              "To convert from monitor1 to monitor2:\n"
923              "\ttifficc %cimon1.icm %comon2.icm in.tif out.tif\n"
924              "To make a CMYK separation:\n"
925              "\ttifficc %coprinter.icm inrgb.tif outcmyk.tif\n"
926              "To recover sRGB from a CMYK separation:\n"
927              "\ttifficc %ciprinter.icm incmyk.tif outrgb.tif\n"
928              "To convert from CIELab TIFF to sRGB\n"
929              "\ttifficc %ci*Lab in.tif out.tif\n\n", 
930              SW, SW, SW, SW, SW, SW);
931          break;
932
933      case 2:
934          PrintBuiltins();
935          break;
936
937      case 3:
938
939          fprintf(stderr, "This program is intended to be a demo of the little cms\n"
940              "engine. Both lcms and this program are freeware. You can\n"
941              "obtain both in source code at http://www.littlecms.com\n"
942              "For suggestions, comments, bug reports etc. send mail to\n"
943              "info@littlecms.com\n\n");
944
945          break;
946     }
947
948     fflush(stderr);
949     exit(0);
950 }
951
952
953 // The toggles stuff
954
955 static
956 void HandleSwitches(int argc, char *argv[])
957 {
958     int s;
959
960     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) {
961
962         switch (s) {
963
964         case 'a':
965         case 'A':
966             StoreAsAlpha = TRUE;
967             break;
968         case 'b':
969         case 'B':
970             BlackWhiteCompensation = TRUE;
971             break;
972
973         case 'c':
974         case 'C':
975             PrecalcMode = atoi(xoptarg);
976             if (PrecalcMode < 0 || PrecalcMode > 3)
977                 FatalError("Unknown precalc mode '%d'", PrecalcMode);
978             break;
979
980         case 'd':
981         case 'D': ObserverAdaptationState = atof(xoptarg);
982             if (ObserverAdaptationState < 0 || 
983                 ObserverAdaptationState > 1.0)
984                 Warning("Adaptation state should be 0..1");
985             break;
986
987         case 'e':
988         case 'E':
989             EmbedProfile = TRUE;
990             break;
991
992         case 'g':
993         case 'G':
994             GamutCheck = TRUE;
995             break;
996
997         case 'v':
998         case 'V':
999             Verbose = TRUE;
1000             break;
1001
1002         case 'i':
1003         case 'I':
1004             if (lIsDeviceLink)
1005                 FatalError("Device-link already specified"); 
1006
1007             cInpProf = xoptarg;
1008             break;
1009
1010         case 'o':
1011         case 'O':
1012             if (lIsDeviceLink)
1013                 FatalError("Device-link already specified"); 
1014
1015             cOutProf = xoptarg;
1016             break;
1017
1018         case 'l':
1019         case 'L': 
1020             if (cInpProf != NULL || cOutProf != NULL) 
1021                 FatalError("input/output profiles already specified");
1022
1023             cInpProf = xoptarg;
1024             lIsDeviceLink = TRUE;
1025             break;
1026
1027         case 'p':
1028         case 'P':
1029             cProofing = xoptarg;
1030             break;
1031
1032         case 't':
1033         case 'T':
1034             Intent = atoi(xoptarg);
1035             break;
1036
1037         case 'm':
1038         case 'M':
1039             ProofingIntent = atoi(xoptarg);
1040             break;
1041
1042         case 'N':
1043         case 'n':
1044             IgnoreEmbedded = TRUE;
1045             break;
1046
1047         case 'W':
1048         case 'w':
1049             Width = atoi(xoptarg);
1050             if (Width != 8 && Width != 16 && Width != 32)
1051                 FatalError("Only 8, 16 and 32 bps are supported");
1052             break;
1053
1054         case 'k':
1055         case 'K':
1056             InkLimit = atof(xoptarg);
1057             if (InkLimit < 0.0 || InkLimit > 400.0)
1058                 FatalError("Ink limit must be 0%%..400%%");
1059             break;
1060
1061
1062         case 's':
1063         case 'S': SaveEmbedded = xoptarg;
1064             break;
1065
1066         case 'H':
1067         case 'h':  {
1068
1069             int a =  atoi(xoptarg);
1070             Help(a); 
1071             }
1072             break;
1073
1074         default:
1075
1076             FatalError("Unknown option - run without args to see valid ones");
1077         }
1078
1079     }
1080 }
1081
1082
1083 // The main sink
1084
1085 int main(int argc, char* argv[])
1086 {
1087     TIFF *in, *out;
1088    
1089     cmsPlugin(&TiffLabPlugin);
1090
1091     InitUtils("tifficc");
1092
1093     HandleSwitches(argc, argv);
1094
1095     if ((argc - xoptind) != 2) {
1096
1097         Help(0);              
1098     }
1099    
1100
1101     TIFFSetErrorHandler(ConsoleErrorHandler);
1102     TIFFSetWarningHandler(ConsoleWarningHandler);
1103
1104     in = TIFFOpen(argv[xoptind], "r");
1105     if (in == NULL) FatalError("Unable to open '%s'", argv[xoptind]);
1106
1107     out = TIFFOpen(argv[xoptind+1], "w");
1108
1109     if (out == NULL) {
1110
1111         TIFFClose(in);
1112         FatalError("Unable to write '%s'", argv[xoptind+1]);
1113     }
1114
1115     do {
1116
1117         TransformImage(in, out, cInpProf);
1118
1119
1120     } while (TIFFReadDirectory(in));
1121
1122
1123     if (Verbose) { fprintf(stdout, "\n"); fflush(stdout); }
1124
1125     TIFFClose(in);
1126     TIFFClose(out);
1127
1128     return 0;
1129 }
1130