comparison mupdf-source/thirdparty/lcms2/utils/tificc/tificc.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2023 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 "lcms2mt_plugin.h"
29 #include "tiffio.h"
30 #include "utils.h"
31
32 // Fix broken libtiff 4.3.0, thanks to Bob Friesenhahn for uncovering this
33
34 #if defined(HAVE_STDINT_H) && (TIFFLIB_VERSION >= 20201219)
35 #include <stdint.h>
36 # undef cmsUInt16Number
37 # define cmsUInt16Number uint16_t
38 # undef cmsUInt32Number
39 # define cmsUInt32Number uint32_t
40 #endif /* TIFFLIB_VERSION */
41
42 // Flags
43
44 static cmsBool BlackWhiteCompensation = FALSE;
45 static cmsBool IgnoreEmbedded = FALSE;
46 static cmsBool EmbedProfile = FALSE;
47 static int PixelDepth = 8;
48 static cmsBool GamutCheck = FALSE;
49 static cmsBool lIsDeviceLink = FALSE;
50 static cmsBool lIsCUBE = FALSE;
51 static cmsBool StoreAsAlpha = FALSE;
52
53 static int Intent = INTENT_PERCEPTUAL;
54 static int ProofingIntent = INTENT_PERCEPTUAL;
55 static int PrecalcMode = 1;
56 static cmsFloat64Number InkLimit = 400;
57
58 static cmsFloat64Number ObserverAdaptationState = 1.0; // According ICC 4.3 this is the default
59
60 static const char *cInpProf = NULL;
61 static const char *cOutProf = NULL;
62 static const char *cProofing = NULL;
63
64 static const char* SaveEmbedded = NULL;
65
66 // Console error & warning
67 static
68 void ConsoleWarningHandler(const char* module, const char* fmt, va_list ap)
69 {
70 if (Verbose) {
71
72 fprintf(stderr, "Warning: ");
73
74 if (module != NULL)
75 fprintf(stderr, "[%s] ", module);
76
77 vfprintf(stderr, fmt, ap);
78 fprintf(stderr, "\n");
79 fflush(stderr);
80 }
81 }
82
83 static
84 void ConsoleErrorHandler(const char* module, const char* fmt, va_list ap)
85 {
86 if (Verbose) {
87
88 fprintf(stderr, "Error: ");
89
90 if (module != NULL)
91 fprintf(stderr, "[%s] ", module);
92
93 vfprintf(stderr, fmt, ap);
94 fprintf(stderr, "\n");
95 fflush(stderr);
96 }
97
98 }
99
100
101 // Issue a warning
102 static
103 void Warning(const char *frm, ...)
104 {
105 va_list args;
106
107 va_start(args, frm);
108 ConsoleWarningHandler("tificc", frm, args);
109 va_end(args);
110 }
111
112
113
114 // Out of memory is a fatal error
115 static
116 void OutOfMem(cmsUInt32Number size)
117 {
118 FatalError("Out of memory on allocating %d bytes.", size);
119 }
120
121
122 // -----------------------------------------------------------------------------------------------
123 // Lab plug-in
124 // In TIFF, Lab is encoded in a different way, so let's use the plug-in
125 // capabilities of lcms2 to change the meaning of TYPE_Lab_8.
126
127 #define LABTIFF_SH(m) ((m) << 30)
128 #define T_LABTIFF(m) (((m)>>30)&1)
129
130 // * 0xffff / 0xff00 = (255 * 257) / (255 * 256) = 257 / 256
131 static
132 int FromLabV2ToLabV4(int x)
133 {
134 int a;
135
136 a = ((x << 8) | x) >> 8; // * 257 / 256
137 if ( a > 0xffff) return 0xffff;
138 return a;
139 }
140
141 // * 0xf00 / 0xffff = * 256 / 257
142 static
143 int FromLabV4ToLabV2(int x)
144 {
145 return ((x << 8) + 0x80) / 257;
146 }
147
148
149 // Formatter for 8bit Lab TIFF (photometric 8)
150 static
151 unsigned char* UnrollTIFFLab8(cmsContext ContextID, struct _cmstransform_struct* CMMcargo,
152 CMSREGISTER cmsUInt16Number wIn[],
153 CMSREGISTER cmsUInt8Number* accum,
154 CMSREGISTER cmsUInt32Number Stride)
155 {
156 wIn[0] = (cmsUInt16Number) FromLabV2ToLabV4((accum[0]) << 8);
157 wIn[1] = (cmsUInt16Number) FromLabV2ToLabV4(((accum[1] > 127) ? (accum[1] - 128) : (accum[1] + 128)) << 8);
158 wIn[2] = (cmsUInt16Number) FromLabV2ToLabV4(((accum[2] > 127) ? (accum[2] - 128) : (accum[2] + 128)) << 8);
159
160 return accum + 3;
161
162 UTILS_UNUSED_PARAMETER(Stride);
163 UTILS_UNUSED_PARAMETER(CMMcargo);
164 }
165
166 // Formatter for 16bit Lab TIFF (photometric 8)
167 static
168 unsigned char* UnrollTIFFLab16(cmsContext ContextID, struct _cmstransform_struct* CMMcargo,
169 CMSREGISTER cmsUInt16Number wIn[],
170 CMSREGISTER cmsUInt8Number* accum,
171 CMSREGISTER cmsUInt32Number Stride )
172 {
173 cmsUInt16Number* accum16 = (cmsUInt16Number*) accum;
174
175 wIn[0] = (cmsUInt16Number) FromLabV2ToLabV4(accum16[0]);
176 wIn[1] = (cmsUInt16Number) FromLabV2ToLabV4(((accum16[1] > 0x7f00) ? (accum16[1] - 0x8000) : (accum16[1] + 0x8000)) );
177 wIn[2] = (cmsUInt16Number) FromLabV2ToLabV4(((accum16[2] > 0x7f00) ? (accum16[2] - 0x8000) : (accum16[2] + 0x8000)) );
178
179 return accum + 3 * sizeof(cmsUInt16Number);
180
181 UTILS_UNUSED_PARAMETER(Stride);
182 UTILS_UNUSED_PARAMETER(CMMcargo);
183 }
184
185
186 static
187 unsigned char* PackTIFFLab8(cmsContext ContextID, struct _cmstransform_struct* CMMcargo,
188 CMSREGISTER cmsUInt16Number wOut[],
189 CMSREGISTER cmsUInt8Number* output,
190 CMSREGISTER cmsUInt32Number Stride)
191 {
192 int a, b;
193
194 *output++ = (cmsUInt8Number) (FromLabV4ToLabV2(wOut[0] + 0x0080) >> 8);
195
196 a = (FromLabV4ToLabV2(wOut[1]) + 0x0080) >> 8;
197 b = (FromLabV4ToLabV2(wOut[2]) + 0x0080) >> 8;
198
199 *output++ = (cmsUInt8Number) ((a < 128) ? (a + 128) : (a - 128));
200 *output++ = (cmsUInt8Number) ((b < 128) ? (b + 128) : (b - 128));
201
202 return output;
203
204 UTILS_UNUSED_PARAMETER(Stride);
205 UTILS_UNUSED_PARAMETER(CMMcargo);
206 }
207
208
209 static
210 unsigned char* PackTIFFLabA8(cmsContext ContextID, struct _cmstransform_struct* CMMcargo,
211 CMSREGISTER cmsUInt16Number wOut[],
212 CMSREGISTER cmsUInt8Number* output,
213 CMSREGISTER cmsUInt32Number Stride)
214 {
215 int a, b;
216
217 *output++ = (cmsUInt8Number) (FromLabV4ToLabV2(wOut[0] + 0x0080) >> 8);
218
219 a = (FromLabV4ToLabV2(wOut[1]) + 0x0080) >> 8;
220 b = (FromLabV4ToLabV2(wOut[2]) + 0x0080) >> 8;
221
222 *output++ = (cmsUInt8Number) ((a < 128) ? (a + 128) : (a - 128));
223 *output++ = (cmsUInt8Number) ((b < 128) ? (b + 128) : (b - 128));
224
225 output++; // Alpha
226
227 return output;
228
229 UTILS_UNUSED_PARAMETER(Stride);
230 UTILS_UNUSED_PARAMETER(CMMcargo);
231 }
232
233
234 static
235 unsigned char* PackTIFFLab16(cmsContext ContextID, struct _cmstransform_struct* CMMcargo,
236 CMSREGISTER cmsUInt16Number wOut[],
237 CMSREGISTER cmsUInt8Number* output,
238 CMSREGISTER cmsUInt32Number Stride)
239 {
240 int a, b;
241 cmsUInt16Number* output16 = (cmsUInt16Number*) output;
242
243 *output16++ = (cmsUInt16Number) FromLabV4ToLabV2(wOut[0]);
244
245 a = FromLabV4ToLabV2(wOut[1]);
246 b = FromLabV4ToLabV2(wOut[2]);
247
248 *output16++ = (cmsUInt16Number) ((a < 0x7f00) ? (a + 0x8000) : (a - 0x8000));
249 *output16++ = (cmsUInt16Number) ((b < 0x7f00) ? (b + 0x8000) : (b - 0x8000));
250
251 return (cmsUInt8Number*) output16;
252
253 UTILS_UNUSED_PARAMETER(Stride);
254 UTILS_UNUSED_PARAMETER(CMMcargo);
255 }
256
257 static
258 unsigned char* PackTIFFLabA16(struct _cmstransform_struct* CMMcargo,
259 CMSREGISTER cmsUInt16Number wOut[],
260 CMSREGISTER cmsUInt8Number* output,
261 CMSREGISTER cmsUInt32Number Stride)
262 {
263 int a, b;
264 cmsUInt16Number* output16 = (cmsUInt16Number*) output;
265
266 *output16++ = (cmsUInt16Number) FromLabV4ToLabV2(wOut[0]);
267
268 a = FromLabV4ToLabV2(wOut[1]);
269 b = FromLabV4ToLabV2(wOut[2]);
270
271 *output16++ = (cmsUInt16Number) ((a < 0x7f00) ? (a + 0x8000) : (a - 0x8000));
272 *output16++ = (cmsUInt16Number) ((b < 0x7f00) ? (b + 0x8000) : (b - 0x8000));
273
274 output16++; // Alpha
275
276 return (cmsUInt8Number*) output16;
277
278 UTILS_UNUSED_PARAMETER(Stride);
279 UTILS_UNUSED_PARAMETER(CMMcargo);
280 }
281
282
283 static
284 cmsFormatter TiffFormatterFactory(cmsContext ContextID, cmsUInt32Number Type,
285 cmsFormatterDirection Dir,
286 cmsUInt32Number dwFlags)
287 {
288 cmsFormatter Result = { NULL };
289 int bps = T_BYTES(Type);
290 int IsTiffSpecial = T_LABTIFF(Type);
291
292 if (IsTiffSpecial && !(dwFlags & CMS_PACK_FLAGS_FLOAT))
293 {
294 if (Dir == cmsFormatterInput)
295 {
296 Result.Fmt16 = (bps == 1) ? UnrollTIFFLab8 : UnrollTIFFLab16;
297 }
298 else
299 {
300 if (T_EXTRA(Type) == 1)
301 Result.Fmt16 = (bps == 1) ? PackTIFFLabA8 : PackTIFFLabA16;
302 else
303 if (T_EXTRA(Type) == 0)
304 Result.Fmt16 = (bps == 1) ? PackTIFFLab8 : PackTIFFLab16;
305 }
306 }
307
308 return Result;
309 }
310
311 static cmsPluginFormatters TiffLabPlugin = { {cmsPluginMagicNumber, 2000-2000, cmsPluginFormattersSig, NULL}, TiffFormatterFactory };
312
313 // -----------------------------------------------------------------------------------------------
314
315 // Build up the pixeltype descriptor
316 static
317 cmsUInt32Number GetInputPixelType(TIFF *Bank)
318 {
319 cmsUInt16Number Photometric, bps, spp, extra, PlanarConfig, *info;
320 cmsUInt16Number Compression;
321 int ColorChannels;
322 int IsPremul = FALSE, IsPlanar = FALSE, IsFlt = FALSE, IsReverse = FALSE;
323 int labTiffSpecial = FALSE;
324 int pt = PT_ANY;
325
326 TIFFGetFieldDefaulted(Bank, TIFFTAG_BITSPERSAMPLE, &bps);
327
328 if (bps == 1)
329 FatalError("Sorry, bilevel TIFFs has nothing to do with ICC profiles");
330
331 if (bps != 8 && bps != 16 && bps != 32)
332 FatalError("Sorry, 8, 16 or 32 bits per sample only");
333
334
335 TIFFGetFieldDefaulted(Bank, TIFFTAG_PLANARCONFIG, &PlanarConfig);
336
337 switch (PlanarConfig) {
338
339 case PLANARCONFIG_CONTIG: IsPlanar = 0; break;
340 case PLANARCONFIG_SEPARATE: IsPlanar = 1; break;
341 default:
342
343 FatalError("Unsupported planar configuration (=%d) ", (int) PlanarConfig);
344 }
345
346 TIFFGetFieldDefaulted(Bank, TIFFTAG_SAMPLESPERPIXEL, &spp);
347
348 // If Samples per pixel == 1, PlanarConfiguration is irrelevant and need not to be included.
349 if (spp == 1) IsPlanar = 0;
350
351 // Any alpha?
352 TIFFGetFieldDefaulted(Bank, TIFFTAG_EXTRASAMPLES, &extra, &info);
353
354 // Read alpha channels as colorant?
355 if (StoreAsAlpha) {
356
357 ColorChannels = spp;
358 extra = 0;
359 }
360 else
361 ColorChannels = spp - extra;
362
363 // Is alpha premultiplied ?
364 IsPremul = ((extra == 1) && (info[0] == EXTRASAMPLE_ASSOCALPHA));
365
366
367 // Get photometric interpretation and proceed accordly
368 TIFFGetField(Bank, TIFFTAG_PHOTOMETRIC, &Photometric);
369
370 switch (Photometric) {
371
372 case PHOTOMETRIC_MINISWHITE:
373
374 IsReverse = 1;
375
376 // ... fall through ...
377
378 case PHOTOMETRIC_MINISBLACK:
379 pt = PT_GRAY;
380 break;
381
382 case PHOTOMETRIC_RGB:
383 pt = PT_RGB;
384 if (ColorChannels < 3)
385 FatalError("Sorry, RGB needs at least 3 samples per pixel");
386 break;
387
388 case PHOTOMETRIC_PALETTE:
389 FatalError("Sorry, palette images not supported");
390 break;
391
392 case PHOTOMETRIC_SEPARATED:
393 pt = PixelTypeFromChanCount(ColorChannels);
394 break;
395
396 case PHOTOMETRIC_YCBCR:
397 TIFFGetField(Bank, TIFFTAG_COMPRESSION, &Compression);
398 {
399 cmsUInt16Number subx, suby;
400
401 pt = PT_YCbCr;
402 TIFFGetFieldDefaulted(Bank, TIFFTAG_YCBCRSUBSAMPLING, &subx, &suby);
403 if (subx != 1 || suby != 1)
404 FatalError("Sorry, subsampled images are not supported");
405 }
406 break;
407
408 // Two Lab flavours
409 case PHOTOMETRIC_ICCLAB:
410 pt = PT_Lab;
411 break;
412
413 case PHOTOMETRIC_CIELAB:
414 pt = PT_Lab;
415 labTiffSpecial = TRUE;
416 break;
417
418 // CIE Log2(L) (u',v')
419 case PHOTOMETRIC_LOGLUV:
420
421 TIFFSetField(Bank, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_16BIT);
422 pt = PT_YUV; // *ICCSpace = icSigLuvData;
423 bps = 16; // 16 bits forced by LibTiff
424 break;
425
426 default:
427 FatalError("Unsupported TIFF color space (Photometric %d)", Photometric);
428 }
429
430 // Convert bits per sample to bytes per sample
431
432 bps >>= 3;
433 IsFlt = (bps == 0) || (bps == 4);
434
435 return (FLOAT_SH(IsFlt) | COLORSPACE_SH(pt) | PLANAR_SH(IsPlanar) | EXTRA_SH(extra) | PREMUL_SH(IsPremul) |
436 CHANNELS_SH(ColorChannels) | BYTES_SH(bps) | FLAVOR_SH(IsReverse) | LABTIFF_SH(labTiffSpecial));
437 }
438
439
440 // Rearrange pixel type to build output descriptor
441
442 static
443 cmsUInt32Number ComputeOutputFormatDescriptor(cmsUInt32Number dwInput, int OutColorSpace, int bps)
444 {
445 int IsPlanar = T_PLANAR(dwInput);
446 int Channels = ChanCountFromPixelType(OutColorSpace);
447 int IsFlt = (bps == 0) || (bps == 4);
448 int labTiffSpecial = FALSE;
449 int Extra = T_EXTRA(dwInput);
450 int IsPremul = T_PREMUL(dwInput);
451
452 if (OutColorSpace == PT_Lab)
453 labTiffSpecial = TRUE;
454
455 return (FLOAT_SH(IsFlt) | COLORSPACE_SH(OutColorSpace) | PLANAR_SH(IsPlanar) |
456 CHANNELS_SH(Channels) | BYTES_SH(bps) | EXTRA_SH(Extra) | PREMUL_SH(IsPremul) |
457 LABTIFF_SH(labTiffSpecial));
458 }
459
460
461
462 // Tile based transforms
463 static
464 int TileBasedXform(cmsContext ContextID, cmsHTRANSFORM hXForm, TIFF* in, TIFF* out, int nPlanes)
465 {
466 tsize_t BufSizeIn = TIFFTileSize(in);
467 tsize_t BufSizeOut = TIFFTileSize(out);
468 unsigned char *BufferIn, *BufferOut;
469 ttile_t i, TileCount = TIFFNumberOfTiles(in) / nPlanes;
470 cmsUInt32Number tw, tl;
471 int PixelCount, j;
472
473 // Check for bad tiffs
474 if (BufSizeIn > INT_MAX || BufSizeOut > INT_MAX)
475 FatalError("Probably corrupted TIFF, tile too big.");
476
477 TIFFGetFieldDefaulted(in, TIFFTAG_TILEWIDTH, &tw);
478 TIFFGetFieldDefaulted(in, TIFFTAG_TILELENGTH, &tl);
479
480 PixelCount = (int) tw * tl;
481
482 BufferIn = (unsigned char *) _TIFFmalloc(BufSizeIn * nPlanes);
483 if (!BufferIn) OutOfMem((cmsUInt32Number) BufSizeIn * nPlanes);
484
485 BufferOut = (unsigned char *) _TIFFmalloc(BufSizeOut * nPlanes);
486 if (!BufferOut) OutOfMem((cmsUInt32Number) BufSizeOut * nPlanes);
487
488
489 for (i = 0; i < TileCount; i++) {
490
491 for (j=0; j < nPlanes; j++) {
492
493 if (TIFFReadEncodedTile(in, i + (j* TileCount),
494 BufferIn + (j*BufSizeIn), BufSizeIn) < 0) goto cleanup;
495 }
496
497 if (PixelCount < 0)
498 FatalError("TIFF is corrupted");
499
500 cmsDoTransform(ContextID, hXForm, BufferIn, BufferOut, PixelCount);
501
502 for (j=0; j < nPlanes; j++) {
503
504 if (TIFFWriteEncodedTile(out, i + (j*TileCount),
505 BufferOut + (j*BufSizeOut), BufSizeOut) < 0) goto cleanup;
506 }
507 }
508
509 _TIFFfree(BufferIn);
510 _TIFFfree(BufferOut);
511 return 1;
512
513
514 cleanup:
515
516 _TIFFfree(BufferIn);
517 _TIFFfree(BufferOut);
518 return 0;
519 }
520
521
522 // Strip based transforms
523 static
524 int StripBasedXform(cmsContext ContextID, cmsHTRANSFORM hXForm, TIFF* in, TIFF* out, int nPlanes)
525 {
526 tsize_t BufSizeIn = TIFFStripSize(in);
527 tsize_t BufSizeOut = TIFFStripSize(out);
528 unsigned char *BufferIn, *BufferOut;
529 ttile_t i, StripCount = TIFFNumberOfStrips(in) / nPlanes;
530 cmsUInt32Number sw;
531 cmsUInt32Number sl;
532 cmsUInt32Number iml;
533 int j;
534 int PixelCount;
535
536 // Check for bad tiffs
537 if (BufSizeIn > INT_MAX || BufSizeOut > INT_MAX)
538 FatalError("Probably corrupted TIFF, strip too big.");
539
540 TIFFGetFieldDefaulted(in, TIFFTAG_IMAGEWIDTH, &sw);
541 TIFFGetFieldDefaulted(in, TIFFTAG_ROWSPERSTRIP, &sl);
542 TIFFGetFieldDefaulted(in, TIFFTAG_IMAGELENGTH, &iml);
543
544 // It is possible to get infinite rows per strip
545 if (sl == 0 || sl > iml)
546 sl = iml; // One strip for whole image
547
548 BufferIn = (unsigned char *) _TIFFmalloc(BufSizeIn * nPlanes);
549 if (!BufferIn) OutOfMem((cmsUInt32Number) BufSizeIn * nPlanes);
550
551 BufferOut = (unsigned char *) _TIFFmalloc(BufSizeOut * nPlanes);
552 if (!BufferOut) OutOfMem((cmsUInt32Number) BufSizeOut * nPlanes);
553
554
555 for (i = 0; i < StripCount; i++) {
556
557 for (j=0; j < nPlanes; j++) {
558
559 if (TIFFReadEncodedStrip(in, i + (j * StripCount),
560 BufferIn + (j * BufSizeIn), BufSizeIn) < 0) goto cleanup;
561 }
562
563 PixelCount = (int) sw * (iml < sl ? iml : sl);
564 iml -= sl;
565
566 if (PixelCount < 0)
567 FatalError("TIFF is corrupted");
568
569 cmsDoTransform(ContextID, hXForm, BufferIn, BufferOut, PixelCount);
570
571 for (j=0; j < nPlanes; j++) {
572 if (TIFFWriteEncodedStrip(out, i + (j * StripCount),
573 BufferOut + j * BufSizeOut, BufSizeOut) < 0) goto cleanup;
574 }
575
576 }
577
578 _TIFFfree(BufferIn);
579 _TIFFfree(BufferOut);
580 return 1;
581
582 cleanup:
583
584 _TIFFfree(BufferIn);
585 _TIFFfree(BufferOut);
586 return 0;
587 }
588
589
590 // Fill the array with a short value
591 static
592 void fillArray(cmsInt16Number array[], cmsInt16Number val, cmsUInt32Number size)
593 {
594 cmsUInt32Number i;
595
596 size /= sizeof(cmsInt16Number);
597 for (i = 0; i < size; i++)
598 array[i] = val;
599
600 }
601
602 // Creates minimum required tags
603 static
604 void WriteOutputTags(TIFF* out, int Colorspace, int BytesPerSample, int AlphaChannels, int IsPremul)
605 {
606 int BitsPerSample = (8 * BytesPerSample);
607 int nChannels = ChanCountFromPixelType(Colorspace);
608
609 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
610 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, nChannels + AlphaChannels);
611
612 if (AlphaChannels > 0)
613 {
614 cmsInt16Number Extra[20];
615
616 if (IsPremul)
617 fillArray(Extra, EXTRASAMPLE_ASSOCALPHA, sizeof(Extra));
618 else
619 fillArray(Extra, EXTRASAMPLE_UNASSALPHA, sizeof(Extra));
620
621 TIFFSetField(out, TIFFTAG_EXTRASAMPLES, AlphaChannels, Extra);
622 }
623
624 switch (Colorspace) {
625
626 case PT_GRAY:
627 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
628
629 break;
630
631 case PT_RGB:
632 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
633 break;
634
635 case PT_CMY:
636 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
637 TIFFSetField(out, TIFFTAG_INKSET, INKSET_MULTIINK);
638 break;
639
640 case PT_CMYK:
641 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
642 TIFFSetField(out, TIFFTAG_INKSET, INKSET_CMYK);
643 break;
644
645 case PT_Lab:
646 if (BitsPerSample == 16)
647 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
648 else
649 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
650 break;
651
652
653 // Multi-ink separations
654 case PT_MCH2:
655 case PT_MCH3:
656 case PT_MCH4:
657 case PT_MCH5:
658 case PT_MCH6:
659 case PT_MCH7:
660 case PT_MCH8:
661 case PT_MCH9:
662 case PT_MCH10:
663 case PT_MCH11:
664 case PT_MCH12:
665 case PT_MCH13:
666 case PT_MCH14:
667 case PT_MCH15:
668
669 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
670
671 if (StoreAsAlpha && nChannels >= 4) {
672
673 cmsInt16Number Extra[20];
674
675 fillArray(Extra, EXTRASAMPLE_UNASSALPHA, sizeof(Extra));
676
677 // CMYK plus extra alpha
678 TIFFSetField(out, TIFFTAG_EXTRASAMPLES, nChannels - 4, Extra);
679 TIFFSetField(out, TIFFTAG_INKSET, INKSET_CMYK);
680 TIFFSetField(out, TIFFTAG_NUMBEROFINKS, 4);
681 }
682 else {
683 TIFFSetField(out, TIFFTAG_INKSET, INKSET_MULTIINK);
684 TIFFSetField(out, TIFFTAG_NUMBEROFINKS, nChannels);
685 }
686 break;
687
688
689 default:
690 FatalError("Unsupported output colorspace");
691 }
692
693 if (PixelDepth == 32)
694 TIFFSetField(out, TIFFTAG_SAMPLEFORMAT,
695 SAMPLEFORMAT_IEEEFP,
696 SAMPLEFORMAT_IEEEFP,
697 SAMPLEFORMAT_IEEEFP,
698 SAMPLEFORMAT_IEEEFP);
699 }
700
701
702 // Copies a bunch of tags
703
704 static
705 void CopyOtherTags(TIFF* in, TIFF* out)
706 {
707 #define CopyField(tag, v) \
708 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
709
710
711 short shortv;
712 unsigned short compression;
713 cmsUInt32Number ow, ol;
714 cmsFloat32Number floatv;
715 char *stringv;
716 cmsUInt32Number longv;
717
718 CopyField(TIFFTAG_SUBFILETYPE, longv);
719
720 TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &ow);
721 TIFFGetField(in, TIFFTAG_IMAGELENGTH, &ol);
722
723 TIFFSetField(out, TIFFTAG_IMAGEWIDTH, ow);
724 TIFFSetField(out, TIFFTAG_IMAGELENGTH, ol);
725
726 CopyField(TIFFTAG_PLANARCONFIG, shortv);
727 CopyField(TIFFTAG_COMPRESSION, compression);
728
729 // This is tricky, libtiff would access predictor in a wrong way
730 // if the codec is none of those
731 if (compression == COMPRESSION_LZW ||
732 compression == 34925 /*COMPRESSION_LZMA*/ ||
733 compression == COMPRESSION_PIXARLOG ||
734 compression == COMPRESSION_DEFLATE ||
735 compression == COMPRESSION_ADOBE_DEFLATE ||
736 compression == 50000 /*COMPRESSION_ZSTD*/)
737 {
738 if (PixelDepth != 32)
739 CopyField(TIFFTAG_PREDICTOR, shortv);
740 }
741
742 CopyField(TIFFTAG_THRESHHOLDING, shortv);
743 CopyField(TIFFTAG_FILLORDER, shortv);
744 CopyField(TIFFTAG_ORIENTATION, shortv);
745 CopyField(TIFFTAG_MINSAMPLEVALUE, shortv);
746 CopyField(TIFFTAG_MAXSAMPLEVALUE, shortv);
747 CopyField(TIFFTAG_XRESOLUTION, floatv);
748 CopyField(TIFFTAG_YRESOLUTION, floatv);
749 CopyField(TIFFTAG_RESOLUTIONUNIT, shortv);
750 CopyField(TIFFTAG_ROWSPERSTRIP, longv);
751 CopyField(TIFFTAG_XPOSITION, floatv);
752 CopyField(TIFFTAG_YPOSITION, floatv);
753 CopyField(TIFFTAG_IMAGEDEPTH, longv);
754 CopyField(TIFFTAG_TILEDEPTH, longv);
755
756 CopyField(TIFFTAG_TILEWIDTH, longv);
757 CopyField(TIFFTAG_TILELENGTH, longv);
758
759 CopyField(TIFFTAG_ARTIST, stringv);
760 CopyField(TIFFTAG_IMAGEDESCRIPTION, stringv);
761 CopyField(TIFFTAG_MAKE, stringv);
762 CopyField(TIFFTAG_MODEL, stringv);
763
764 CopyField(TIFFTAG_DATETIME, stringv);
765 CopyField(TIFFTAG_HOSTCOMPUTER, stringv);
766 CopyField(TIFFTAG_PAGENAME, stringv);
767 CopyField(TIFFTAG_DOCUMENTNAME, stringv);
768
769 }
770
771
772 static
773 void DoEmbedProfile(TIFF* Out, const char* ProfileFile)
774 {
775 FILE* f;
776 cmsInt32Number size;
777 cmsUInt32Number EmbedLen;
778 cmsUInt8Number* EmbedBuffer;
779
780 f = fopen(ProfileFile, "rb");
781 if (f == NULL) return;
782
783 size = cmsfilelength(f);
784 if (size < 0) return;
785
786 EmbedBuffer = (cmsUInt8Number*) malloc((size_t) size + 1);
787 if (EmbedBuffer == NULL) {
788 OutOfMem(size+1);
789 return;
790 }
791
792 EmbedLen = (cmsUInt32Number) fread(EmbedBuffer, 1, (size_t) size, f);
793
794 if (EmbedLen != (cmsUInt32Number) size)
795 FatalError("Cannot read %ld bytes to %s", (long) size, ProfileFile);
796
797 fclose(f);
798 EmbedBuffer[EmbedLen] = 0;
799
800 TIFFSetField(Out, TIFFTAG_ICCPROFILE, EmbedLen, EmbedBuffer);
801 free(EmbedBuffer);
802 }
803
804
805 // Read or create a ICC profile from the TIFF data
806 static
807 cmsHPROFILE GetTIFFProfile(cmsContext ContextID, TIFF* in)
808 {
809 cmsCIExyYTRIPLE Primaries;
810 cmsFloat32Number* chr;
811 cmsCIExyY WhitePoint;
812 cmsFloat32Number* wp;
813 int i;
814 cmsToneCurve* Curve[3];
815 cmsUInt16Number *gmr, *gmg, *gmb;
816 cmsHPROFILE hProfile;
817 cmsUInt32Number EmbedLen;
818 cmsUInt8Number* EmbedBuffer;
819
820 if (IgnoreEmbedded) return NULL;
821
822 if (TIFFGetField(in, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer)) {
823
824 hProfile = cmsOpenProfileFromMem(ContextID, EmbedBuffer, EmbedLen);
825
826 // Print description found in the profile
827 if (Verbose && (hProfile != NULL)) {
828
829 fprintf(stdout, "\n[Embedded profile]\n");
830 PrintProfileInformation(ContextID, hProfile);
831 fflush(stdout);
832 }
833
834 if (hProfile != NULL && SaveEmbedded != NULL)
835 SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded);
836
837 if (hProfile) return hProfile;
838 }
839
840 // Try to see if "colorimetric" tiff
841 if (TIFFGetField(in, TIFFTAG_PRIMARYCHROMATICITIES, &chr)) {
842
843 Primaries.Red.x = chr[0];
844 Primaries.Red.y = chr[1];
845 Primaries.Green.x = chr[2];
846 Primaries.Green.y = chr[3];
847 Primaries.Blue.x = chr[4];
848 Primaries.Blue.y = chr[5];
849
850 Primaries.Red.Y = Primaries.Green.Y = Primaries.Blue.Y = 1.0;
851
852 if (TIFFGetField(in, TIFFTAG_WHITEPOINT, &wp)) {
853
854 WhitePoint.x = wp[0];
855 WhitePoint.y = wp[1];
856 WhitePoint.Y = 1.0;
857
858 // Transfer function is a bit harder....
859 TIFFGetFieldDefaulted(in, TIFFTAG_TRANSFERFUNCTION,
860 &gmr,
861 &gmg,
862 &gmb);
863
864 Curve[0] = cmsBuildTabulatedToneCurve16(ContextID, 256, gmr);
865 Curve[1] = cmsBuildTabulatedToneCurve16(ContextID, 256, gmg);
866 Curve[2] = cmsBuildTabulatedToneCurve16(ContextID, 256, gmb);
867
868 hProfile = cmsCreateRGBProfile(ContextID, &WhitePoint, &Primaries, Curve);
869
870 for (i=0; i < 3; i++)
871 cmsFreeToneCurve(ContextID, Curve[i]);
872
873 if (Verbose) {
874 fprintf(stdout, "\n[Colorimetric TIFF]\n");
875 }
876
877
878 return hProfile;
879 }
880 }
881
882 return NULL;
883 }
884
885
886 // Transform one image
887 static
888 int TransformImage(cmsContext ContextID, TIFF* in, TIFF* out, const char *cDefInpProf)
889 {
890 cmsHPROFILE hIn, hOut, hProof, hInkLimit = NULL;
891 cmsHTRANSFORM xform;
892 cmsUInt32Number wInput, wOutput;
893 int OutputColorSpace;
894 int BytesPerSample = PixelDepth / 8;
895 cmsUInt32Number dwFlags;
896 int nPlanes;
897
898 // Default options
899 dwFlags = cmsFLAGS_COPY_ALPHA;
900
901 // Observer adaptation state (only meaningful on absolute colorimetric intent)
902 cmsSetAdaptationState(ContextID, ObserverAdaptationState);
903
904 if (EmbedProfile && cOutProf)
905 DoEmbedProfile(out, cOutProf);
906
907 if (BlackWhiteCompensation)
908 dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
909
910 switch (PrecalcMode) {
911
912 case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break;
913 case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
914 case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
915 case 1: break;
916
917 default: FatalError("Unknown precalculation mode '%d'", PrecalcMode);
918 }
919
920 if (GamutCheck)
921 dwFlags |= cmsFLAGS_GAMUTCHECK;
922
923 hProof = NULL;
924 hOut = NULL;
925
926 if (lIsDeviceLink) {
927
928 if (lIsCUBE)
929 hIn = cmsCreateDeviceLinkFromCubeFile(ContextID, cDefInpProf);
930 else
931 hIn = cmsOpenProfileFromFile(ContextID, cDefInpProf, "r");
932 }
933 else {
934
935 hIn = GetTIFFProfile(ContextID, in);
936
937 if (hIn == NULL)
938 hIn = OpenStockProfile(ContextID, cDefInpProf);
939
940 hOut = OpenStockProfile(ContextID, cOutProf);
941
942 if (cProofing != NULL) {
943
944 hProof = OpenStockProfile(ContextID, cProofing);
945 dwFlags |= cmsFLAGS_SOFTPROOFING;
946 }
947 }
948
949 // Take input color space
950 wInput = GetInputPixelType(in);
951
952 // Assure both, input profile and input TIFF are on same colorspace
953 if (_cmsLCMScolorSpace(ContextID, cmsGetColorSpace(ContextID, hIn)) != (int) T_COLORSPACE(wInput))
954 FatalError("Input profile is not operating in proper color space (%d)", T_COLORSPACE(wInput));
955
956
957 if (!lIsDeviceLink)
958 OutputColorSpace = _cmsLCMScolorSpace(ContextID, cmsGetColorSpace(ContextID, hOut));
959 else
960 OutputColorSpace = _cmsLCMScolorSpace(ContextID, cmsGetPCS(ContextID, hIn));
961
962 wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace, BytesPerSample);
963
964 WriteOutputTags(out, OutputColorSpace, BytesPerSample, T_EXTRA(wOutput), T_PREMUL(wOutput));
965 CopyOtherTags(in, out);
966
967 // Ink limit
968 if (InkLimit != 400.0 &&
969 (OutputColorSpace == PT_CMYK || OutputColorSpace == PT_CMY)) {
970
971 cmsHPROFILE hProfiles[10];
972 int nProfiles = 0;
973
974 hInkLimit = cmsCreateInkLimitingDeviceLink(ContextID, cmsGetColorSpace(ContextID, hOut), InkLimit);
975
976 hProfiles[nProfiles++] = hIn;
977 if (hProof) {
978 hProfiles[nProfiles++] = hProof;
979 hProfiles[nProfiles++] = hProof;
980 }
981
982 hProfiles[nProfiles++] = hOut;
983 hProfiles[nProfiles++] = hInkLimit;
984
985 xform = cmsCreateMultiprofileTransform(ContextID, hProfiles, nProfiles,
986 wInput, wOutput, Intent, dwFlags);
987
988 }
989 else {
990
991 xform = cmsCreateProofingTransform(ContextID, hIn, wInput,
992 hOut, wOutput,
993 hProof, Intent,
994 ProofingIntent,
995 dwFlags);
996 }
997
998 cmsCloseProfile(ContextID, hIn);
999 cmsCloseProfile(ContextID, hOut);
1000
1001 if (hInkLimit)
1002 cmsCloseProfile(ContextID, hInkLimit);
1003 if (hProof)
1004 cmsCloseProfile(ContextID, hProof);
1005
1006 if (xform == NULL) return 0;
1007
1008 // Planar stuff
1009 if (T_PLANAR(wInput))
1010 nPlanes = T_CHANNELS(wInput) + T_EXTRA(wInput);
1011 else
1012 nPlanes = 1;
1013
1014
1015 // Handle tile by tile or strip by strip
1016 if (TIFFIsTiled(in)) {
1017
1018 TileBasedXform(ContextID, xform, in, out, nPlanes);
1019 }
1020 else {
1021 StripBasedXform(ContextID, xform, in, out, nPlanes);
1022 }
1023
1024
1025 cmsDeleteTransform(ContextID, xform);
1026
1027 TIFFWriteDirectory(out);
1028
1029 return 1;
1030 }
1031
1032
1033 // Print help
1034 static
1035 void Help(cmsContext ContextID, int level)
1036 {
1037 UTILS_UNUSED_PARAMETER(level);
1038
1039 fprintf(stderr, "Little CMS ICC profile applier for TIFF - v8.0 [LittleCMS %2.2f]\n", cmsGetEncodedCMMversion() / 1000.0);
1040 fprintf(stderr, "Copyright (c) 1998-2023 Marti Maria Saguer. See COPYING file for details.\n");
1041 fflush(stderr);
1042
1043 fprintf(stderr, "usage: tificc [flags] input.tif output.tif\n");
1044
1045 fprintf(stderr, "\nflags:\n\n");
1046 fprintf(stderr, "-v - Verbose\n");
1047 fprintf(stderr, "-i<profile> - Input profile (defaults to sRGB)\n");
1048 fprintf(stderr, "-o<profile> - Output profile (defaults to sRGB)\n");
1049 fprintf(stderr, "-l<profile> - Transform by device-link profile\n");
1050 fprintf(stderr, "-u<profile> - Transform by CUBE colormap\n");
1051
1052 PrintBuiltins();
1053
1054 PrintRenderingIntents(ContextID);
1055
1056 fprintf(stderr, "-b - Black point compensation\n");
1057 fprintf(stderr, "-d<0..1> - Observer adaptation state (abs.col. only)\n");
1058
1059 fprintf(stderr, "-c<0,1,2,3> - Precalculates transform (0=Off, 1=Normal, 2=Hi-res, 3=LoRes)\n");
1060 fprintf(stderr, "\n");
1061
1062 fprintf(stderr, "-w<8,16,32> - Output depth. Use 32 for floating-point\n\n");
1063 fprintf(stderr, "-a - Handle channels > 4 as alpha\n");
1064
1065 fprintf(stderr, "-n - Ignore embedded profile on input\n");
1066 fprintf(stderr, "-e - Embed destination profile\n");
1067 fprintf(stderr, "-s<new profile> - Save embedded profile as <new profile>\n");
1068 fprintf(stderr, "\n");
1069
1070
1071 fprintf(stderr, "-p<profile> - Soft proof profile\n");
1072 fprintf(stderr, "-m<n> - Soft proof intent\n");
1073 fprintf(stderr, "\tThat is the intent used to translate the simulation to the output device.\n\tNote that the simulated intent is set by using -t not by -m\n");
1074 fprintf(stderr, "-g - Marks out-of-gamut colors on softproof\n");
1075
1076 fprintf(stderr, "\n");
1077
1078 fprintf(stderr, "-k<0..400> - Ink-limiting in %% (CMYK only)\n");
1079 fprintf(stderr, "\n");
1080
1081
1082 fprintf(stderr, "Examples:\n\n"
1083 "To color correct from scanner to sRGB:\n"
1084 "\ttificc -iscanner.icm in.tif out.tif\n"
1085 "To convert from monitor1 to monitor2:\n"
1086 "\ttificc -imon1.icm -omon2.icm in.tif out.tif\n"
1087 "To make a CMYK separation:\n"
1088 "\ttificc -oprinter.icm inrgb.tif outcmyk.tif\n"
1089 "To recover sRGB from a CMYK separation:\n"
1090 "\ttificc -iprinter.icm incmyk.tif outrgb.tif\n"
1091 "To soft-proof how behaves Probev1_ICCv4.icc on perceptual:\n"
1092 "\ttifficc -t0 -p Probev1_ICCv4.icc -m1 infile.tif out.tif\n"
1093 "To convert from CIELab TIFF to sRGB\n"
1094 "\ttificc -i*Lab in.tif out.tif\n\n");
1095
1096
1097 fprintf(stderr, "This program is intended to be a demo of the Little CMS\n"
1098 "color engine. Both lcms and this program are open source.\n"
1099 "You can obtain both in source code at https://www.littlecms.com\n"
1100 "For suggestions, comments, bug reports etc. send mail to\n"
1101 "info@littlecms.com\n\n");
1102
1103 exit(0);
1104 }
1105
1106
1107 // The toggles stuff
1108
1109 static
1110 void HandleSwitches(cmsContext ContextID, int argc, char *argv[])
1111 {
1112 int s;
1113
1114 while ((s=xgetopt(argc,argv,"aAeEbBw:W:nNvVGgh:H:i:I:o:O:P:p:t:T:c:C:l:L:u:U:M:m:K:k:S:s:D:d:-:")) != EOF) {
1115
1116 switch (s) {
1117
1118
1119 case '-':
1120 if (strcmp(xoptarg, "help") == 0)
1121 {
1122 Help(ContextID, 0);
1123 }
1124 else
1125 {
1126 FatalError("Unknown option - run without args to see valid ones.\n");
1127 }
1128 break;
1129
1130 case 'a':
1131 case 'A':
1132 StoreAsAlpha = TRUE;
1133 break;
1134 case 'b':
1135 case 'B':
1136 BlackWhiteCompensation = TRUE;
1137 break;
1138
1139 case 'c':
1140 case 'C':
1141 PrecalcMode = atoi(xoptarg);
1142 if (PrecalcMode < 0 || PrecalcMode > 3)
1143 FatalError("Unknown precalc mode '%d'", PrecalcMode);
1144 break;
1145
1146 case 'd':
1147 case 'D': ObserverAdaptationState = atof(xoptarg);
1148 if (ObserverAdaptationState < 0 ||
1149 ObserverAdaptationState > 1.0)
1150 Warning("Adaptation state should be 0..1");
1151 break;
1152
1153 case 'e':
1154 case 'E':
1155 EmbedProfile = TRUE;
1156 break;
1157
1158 case 'g':
1159 case 'G':
1160 GamutCheck = TRUE;
1161 break;
1162
1163 case 'v':
1164 case 'V':
1165 Verbose = TRUE;
1166 break;
1167
1168 case 'i':
1169 case 'I':
1170 if (lIsDeviceLink)
1171 FatalError("Device-link already specified");
1172
1173 cInpProf = xoptarg;
1174 break;
1175
1176 case 'o':
1177 case 'O':
1178 if (lIsDeviceLink)
1179 FatalError("Device-link already specified");
1180
1181 cOutProf = xoptarg;
1182 break;
1183
1184 case 'l':
1185 case 'L':
1186 if (cInpProf != NULL || cOutProf != NULL)
1187 FatalError("input/output profiles already specified");
1188
1189 cInpProf = xoptarg;
1190 lIsDeviceLink = TRUE;
1191 lIsCUBE = FALSE;
1192 break;
1193
1194 case 'u':
1195 case 'U':
1196 if (cInpProf != NULL || cOutProf != NULL)
1197 FatalError("input/output profiles already specified");
1198
1199 cInpProf = xoptarg;
1200 lIsDeviceLink = TRUE;
1201 lIsCUBE = TRUE;
1202 break;
1203
1204 case 'p':
1205 case 'P':
1206 cProofing = xoptarg;
1207 break;
1208
1209 case 't':
1210 case 'T':
1211 Intent = atoi(xoptarg);
1212 break;
1213
1214 case 'm':
1215 case 'M':
1216 ProofingIntent = atoi(xoptarg);
1217 break;
1218
1219 case 'N':
1220 case 'n':
1221 IgnoreEmbedded = TRUE;
1222 break;
1223
1224 case 'W':
1225 case 'w':
1226 PixelDepth = atoi(xoptarg);
1227 if (PixelDepth != 8 && PixelDepth != 16 && PixelDepth != 32)
1228 FatalError("Only 8, 16 and 32 bps are supported");
1229 break;
1230
1231 case 'k':
1232 case 'K':
1233 InkLimit = atof(xoptarg);
1234 if (InkLimit < 0.0 || InkLimit > 400.0)
1235 FatalError("Ink limit must be 0%%..400%%");
1236 break;
1237
1238
1239 case 's':
1240 case 'S': SaveEmbedded = xoptarg;
1241 break;
1242
1243 case 'H':
1244 case 'h': {
1245
1246 int a = atoi(xoptarg);
1247 Help(ContextID, a);
1248 }
1249 break;
1250
1251 default:
1252
1253 FatalError("Unknown option - run without args to see valid ones");
1254 }
1255
1256 }
1257 }
1258
1259
1260 // The main sink
1261
1262 int main(int argc, char* argv[])
1263 {
1264 cmsContext ContextID;
1265 TIFF *in, *out;
1266
1267 ContextID = cmsCreateContext(NULL, NULL);
1268
1269 cmsPlugin(ContextID, &TiffLabPlugin);
1270
1271 InitUtils(ContextID, "tificc");
1272
1273 HandleSwitches(ContextID, argc, argv);
1274
1275 if ((argc - xoptind) != 2) {
1276
1277 Help(ContextID, 0);
1278 }
1279
1280
1281 TIFFSetErrorHandler(ConsoleErrorHandler);
1282 TIFFSetWarningHandler(ConsoleWarningHandler);
1283
1284 in = TIFFOpen(argv[xoptind], "r");
1285 if (in == NULL) FatalError("Unable to open '%s'", argv[xoptind]);
1286
1287 out = TIFFOpen(argv[xoptind+1], "w");
1288
1289 if (out == NULL) {
1290
1291 TIFFClose(in);
1292 FatalError("Unable to write '%s'", argv[xoptind+1]);
1293 }
1294
1295 do {
1296
1297 TransformImage(ContextID, in, out, cInpProf);
1298
1299
1300 } while (TIFFReadDirectory(in));
1301
1302
1303 if (Verbose) { fprintf(stdout, "\n"); fflush(stdout); }
1304
1305 TIFFClose(in);
1306 TIFFClose(out);
1307
1308 cmsDeleteContext(ContextID);
1309
1310 return 0;
1311 }