Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
