1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 // This file is available under and governed by the GNU General Public 26 // License version 2 only, as published by the Free Software Foundation. 27 // However, the following notice accompanied the original version of this 28 // file: 29 // 30 31 //--------------------------------------------------------------------------------- 32 // 33 // Little Color Management System 34 // Copyright (c) 1998-2011 Marti Maria Saguer 35 // 36 // Permission is hereby granted, free of charge, to any person obtaining 37 // a copy of this software and associated documentation files (the "Software"), 38 // to deal in the Software without restriction, including without limitation 39 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 40 // and/or sell copies of the Software, and to permit persons to whom the Software 41 // is furnished to do so, subject to the following conditions: 42 // 43 // The above copyright notice and this permission notice shall be included in 44 // all copies or substantial portions of the Software. 45 // 46 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 47 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 48 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 49 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 50 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 51 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 52 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 53 // 54 //--------------------------------------------------------------------------------- 55 // 56 57 #include "lcms2_internal.h" 58 59 60 //---------------------------------------------------------------------------------- 61 62 // Optimization for 8 bits, Shaper-CLUT (3 inputs only) 63 typedef struct { 64 65 cmsContext ContextID; 66 67 const cmsInterpParams* p; // Tetrahedrical interpolation parameters. This is a not-owned pointer. 68 69 cmsUInt16Number rx[256], ry[256], rz[256]; 70 cmsUInt32Number X0[256], Y0[256], Z0[256]; // Precomputed nodes and offsets for 8-bit input data 71 72 73 } Prelin8Data; 74 75 76 // Generic optimization for 16 bits Shaper-CLUT-Shaper (any inputs) 77 typedef struct { 78 79 cmsContext ContextID; 80 81 // Number of channels 82 int nInputs; 83 int nOutputs; 84 85 _cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS]; // The maximum number of input channels is known in advance 86 cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS]; 87 88 _cmsInterpFn16 EvalCLUT; // The evaluator for 3D grid 89 const cmsInterpParams* CLUTparams; // (not-owned pointer) 90 91 92 _cmsInterpFn16* EvalCurveOut16; // Points to an array of curve evaluators in 16 bits (not-owned pointer) 93 cmsInterpParams** ParamsCurveOut16; // Points to an array of references to interpolation params (not-owned pointer) 94 95 96 } Prelin16Data; 97 98 99 // Optimization for matrix-shaper in 8 bits. Numbers are operated in n.14 signed, tables are stored in 1.14 fixed 100 101 typedef cmsInt32Number cmsS1Fixed14Number; // Note that this may hold more than 16 bits! 102 103 #define DOUBLE_TO_1FIXED14(x) ((cmsS1Fixed14Number) floor((x) * 16384.0 + 0.5)) 104 105 typedef struct { 106 107 cmsContext ContextID; 108 109 cmsS1Fixed14Number Shaper1R[256]; // from 0..255 to 1.14 (0.0...1.0) 110 cmsS1Fixed14Number Shaper1G[256]; 111 cmsS1Fixed14Number Shaper1B[256]; 112 113 cmsS1Fixed14Number Mat[3][3]; // n.14 to n.14 (needs a saturation after that) 114 cmsS1Fixed14Number Off[3]; 115 116 cmsUInt16Number Shaper2R[16385]; // 1.14 to 0..255 117 cmsUInt16Number Shaper2G[16385]; 118 cmsUInt16Number Shaper2B[16385]; 119 120 } MatShaper8Data; 121 122 // Curves, optimization is shared between 8 and 16 bits 123 typedef struct { 124 125 cmsContext ContextID; 126 127 int nCurves; // Number of curves 128 int nElements; // Elements in curves 129 cmsUInt16Number** Curves; // Points to a dynamically allocated array 130 131 } Curves16Data; 132 133 134 // Simple optimizations ---------------------------------------------------------------------------------------------------------- 135 136 137 // Remove an element in linked chain 138 static 139 void _RemoveElement(cmsStage** head) 140 { 141 cmsStage* mpe = *head; 142 cmsStage* next = mpe ->Next; 143 *head = next; 144 cmsStageFree(mpe); 145 } 146 147 // Remove all identities in chain. Note that pt actually is a double pointer to the element that holds the pointer. 148 static 149 cmsBool _Remove1Op(cmsPipeline* Lut, cmsStageSignature UnaryOp) 150 { 151 cmsStage** pt = &Lut ->Elements; 152 cmsBool AnyOpt = FALSE; 153 154 while (*pt != NULL) { 155 156 if ((*pt) ->Implements == UnaryOp) { 157 _RemoveElement(pt); 158 AnyOpt = TRUE; 159 } 160 else 161 pt = &((*pt) -> Next); 162 } 163 164 return AnyOpt; 165 } 166 167 // Same, but only if two adjacent elements are found 168 static 169 cmsBool _Remove2Op(cmsPipeline* Lut, cmsStageSignature Op1, cmsStageSignature Op2) 170 { 171 cmsStage** pt1; 172 cmsStage** pt2; 173 cmsBool AnyOpt = FALSE; 174 175 pt1 = &Lut ->Elements; 176 if (*pt1 == NULL) return AnyOpt; 177 178 while (*pt1 != NULL) { 179 180 pt2 = &((*pt1) -> Next); 181 if (*pt2 == NULL) return AnyOpt; 182 183 if ((*pt1) ->Implements == Op1 && (*pt2) ->Implements == Op2) { 184 _RemoveElement(pt2); 185 _RemoveElement(pt1); 186 AnyOpt = TRUE; 187 } 188 else 189 pt1 = &((*pt1) -> Next); 190 } 191 192 return AnyOpt; 193 } 194 195 // Preoptimize just gets rif of no-ops coming paired. Conversion from v2 to v4 followed 196 // by a v4 to v2 and vice-versa. The elements are then discarded. 197 static 198 cmsBool PreOptimize(cmsPipeline* Lut) 199 { 200 cmsBool AnyOpt = FALSE, Opt; 201 202 do { 203 204 Opt = FALSE; 205 206 // Remove all identities 207 Opt |= _Remove1Op(Lut, cmsSigIdentityElemType); 208 209 // Remove XYZ2Lab followed by Lab2XYZ 210 Opt |= _Remove2Op(Lut, cmsSigXYZ2LabElemType, cmsSigLab2XYZElemType); 211 212 // Remove Lab2XYZ followed by XYZ2Lab 213 Opt |= _Remove2Op(Lut, cmsSigLab2XYZElemType, cmsSigXYZ2LabElemType); 214 215 // Remove V4 to V2 followed by V2 to V4 216 Opt |= _Remove2Op(Lut, cmsSigLabV4toV2, cmsSigLabV2toV4); 217 218 // Remove V2 to V4 followed by V4 to V2 219 Opt |= _Remove2Op(Lut, cmsSigLabV2toV4, cmsSigLabV4toV2); 220 221 // Remove float pcs Lab conversions 222 Opt |= _Remove2Op(Lut, cmsSigLab2FloatPCS, cmsSigFloatPCS2Lab); 223 224 // Remove float pcs Lab conversions 225 Opt |= _Remove2Op(Lut, cmsSigXYZ2FloatPCS, cmsSigFloatPCS2XYZ); 226 227 if (Opt) AnyOpt = TRUE; 228 229 } while (Opt); 230 231 return AnyOpt; 232 } 233 234 static 235 void Eval16nop1D(register const cmsUInt16Number Input[], 236 register cmsUInt16Number Output[], 237 register const struct _cms_interp_struc* p) 238 { 239 Output[0] = Input[0]; 240 241 cmsUNUSED_PARAMETER(p); 242 } 243 244 static 245 void PrelinEval16(register const cmsUInt16Number Input[], 246 register cmsUInt16Number Output[], 247 register const void* D) 248 { 249 Prelin16Data* p16 = (Prelin16Data*) D; 250 cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS]; 251 cmsUInt16Number StageDEF[cmsMAXCHANNELS]; 252 int i; 253 254 for (i=0; i < p16 ->nInputs; i++) { 255 256 p16 ->EvalCurveIn16[i](&Input[i], &StageABC[i], p16 ->ParamsCurveIn16[i]); 257 } 258 259 p16 ->EvalCLUT(StageABC, StageDEF, p16 ->CLUTparams); 260 261 for (i=0; i < p16 ->nOutputs; i++) { 262 263 p16 ->EvalCurveOut16[i](&StageDEF[i], &Output[i], p16 ->ParamsCurveOut16[i]); 264 } 265 } 266 267 268 static 269 void PrelinOpt16free(cmsContext ContextID, void* ptr) 270 { 271 Prelin16Data* p16 = (Prelin16Data*) ptr; 272 273 _cmsFree(ContextID, p16 ->EvalCurveOut16); 274 _cmsFree(ContextID, p16 ->ParamsCurveOut16); 275 276 _cmsFree(ContextID, p16); 277 } 278 279 static 280 void* Prelin16dup(cmsContext ContextID, const void* ptr) 281 { 282 Prelin16Data* p16 = (Prelin16Data*) ptr; 283 Prelin16Data* Duped = _cmsDupMem(ContextID, p16, sizeof(Prelin16Data)); 284 285 if (Duped == NULL) return NULL; 286 287 Duped ->EvalCurveOut16 = _cmsDupMem(ContextID, p16 ->EvalCurveOut16, p16 ->nOutputs * sizeof(_cmsInterpFn16)); 288 Duped ->ParamsCurveOut16 = _cmsDupMem(ContextID, p16 ->ParamsCurveOut16, p16 ->nOutputs * sizeof(cmsInterpParams* )); 289 290 return Duped; 291 } 292 293 294 static 295 Prelin16Data* PrelinOpt16alloc(cmsContext ContextID, 296 const cmsInterpParams* ColorMap, 297 int nInputs, cmsToneCurve** In, 298 int nOutputs, cmsToneCurve** Out ) 299 { 300 int i; 301 Prelin16Data* p16 = _cmsMallocZero(ContextID, sizeof(Prelin16Data)); 302 if (p16 == NULL) return NULL; 303 304 p16 ->nInputs = nInputs; 305 p16 -> nOutputs = nOutputs; 306 307 308 for (i=0; i < nInputs; i++) { 309 310 if (In == NULL) { 311 p16 -> ParamsCurveIn16[i] = NULL; 312 p16 -> EvalCurveIn16[i] = Eval16nop1D; 313 314 } 315 else { 316 p16 -> ParamsCurveIn16[i] = In[i] ->InterpParams; 317 p16 -> EvalCurveIn16[i] = p16 ->ParamsCurveIn16[i]->Interpolation.Lerp16; 318 } 319 } 320 321 p16 ->CLUTparams = ColorMap; 322 p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16; 323 324 325 p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16)); 326 p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* )); 327 328 for (i=0; i < nOutputs; i++) { 329 330 if (Out == NULL) { 331 p16 ->ParamsCurveOut16[i] = NULL; 332 p16 -> EvalCurveOut16[i] = Eval16nop1D; 333 } 334 else { 335 336 p16 ->ParamsCurveOut16[i] = Out[i] ->InterpParams; 337 p16 -> EvalCurveOut16[i] = p16 ->ParamsCurveOut16[i]->Interpolation.Lerp16; 338 } 339 } 340 341 return p16; 342 } 343 344 345 346 // Resampling --------------------------------------------------------------------------------- 347 348 #define PRELINEARIZATION_POINTS 4096 349 350 // Sampler implemented by another LUT. This is a clean way to precalculate the devicelink 3D CLUT for 351 // almost any transform. We use floating point precision and then convert from floating point to 16 bits. 352 static 353 int XFormSampler16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) 354 { 355 cmsPipeline* Lut = (cmsPipeline*) Cargo; 356 cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; 357 cmsUInt32Number i; 358 359 _cmsAssert(Lut -> InputChannels < cmsMAXCHANNELS); 360 _cmsAssert(Lut -> OutputChannels < cmsMAXCHANNELS); 361 362 // From 16 bit to floating point 363 for (i=0; i < Lut ->InputChannels; i++) 364 InFloat[i] = (cmsFloat32Number) (In[i] / 65535.0); 365 366 // Evaluate in floating point 367 cmsPipelineEvalFloat(InFloat, OutFloat, Lut); 368 369 // Back to 16 bits representation 370 for (i=0; i < Lut ->OutputChannels; i++) 371 Out[i] = _cmsQuickSaturateWord(OutFloat[i] * 65535.0); 372 373 // Always succeed 374 return TRUE; 375 } 376 377 // Try to see if the curves of a given MPE are linear 378 static 379 cmsBool AllCurvesAreLinear(cmsStage* mpe) 380 { 381 cmsToneCurve** Curves; 382 cmsUInt32Number i, n; 383 384 Curves = _cmsStageGetPtrToCurveSet(mpe); 385 if (Curves == NULL) return FALSE; 386 387 n = cmsStageOutputChannels(mpe); 388 389 for (i=0; i < n; i++) { 390 if (!cmsIsToneCurveLinear(Curves[i])) return FALSE; 391 } 392 393 return TRUE; 394 } 395 396 // This function replaces a specific node placed in "At" by the "Value" numbers. Its purpose 397 // is to fix scum dot on broken profiles/transforms. Works on 1, 3 and 4 channels 398 static 399 cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[], 400 int nChannelsOut, int nChannelsIn) 401 { 402 _cmsStageCLutData* Grid = (_cmsStageCLutData*) CLUT ->Data; 403 cmsInterpParams* p16 = Grid ->Params; 404 cmsFloat64Number px, py, pz, pw; 405 int x0, y0, z0, w0; 406 int i, index; 407 408 if (CLUT -> Type != cmsSigCLutElemType) { 409 cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) Attempt to PatchLUT on non-lut stage"); 410 return FALSE; 411 } 412 413 if (nChannelsIn == 4) { 414 415 px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; 416 py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; 417 pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; 418 pw = ((cmsFloat64Number) At[3] * (p16->Domain[3])) / 65535.0; 419 420 x0 = (int) floor(px); 421 y0 = (int) floor(py); 422 z0 = (int) floor(pz); 423 w0 = (int) floor(pw); 424 425 if (((px - x0) != 0) || 426 ((py - y0) != 0) || 427 ((pz - z0) != 0) || 428 ((pw - w0) != 0)) return FALSE; // Not on exact node 429 430 index = p16 -> opta[3] * x0 + 431 p16 -> opta[2] * y0 + 432 p16 -> opta[1] * z0 + 433 p16 -> opta[0] * w0; 434 } 435 else 436 if (nChannelsIn == 3) { 437 438 px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; 439 py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; 440 pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; 441 442 x0 = (int) floor(px); 443 y0 = (int) floor(py); 444 z0 = (int) floor(pz); 445 446 if (((px - x0) != 0) || 447 ((py - y0) != 0) || 448 ((pz - z0) != 0)) return FALSE; // Not on exact node 449 450 index = p16 -> opta[2] * x0 + 451 p16 -> opta[1] * y0 + 452 p16 -> opta[0] * z0; 453 } 454 else 455 if (nChannelsIn == 1) { 456 457 px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; 458 459 x0 = (int) floor(px); 460 461 if (((px - x0) != 0)) return FALSE; // Not on exact node 462 463 index = p16 -> opta[0] * x0; 464 } 465 else { 466 cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); 467 return FALSE; 468 } 469 470 for (i=0; i < nChannelsOut; i++) 471 Grid -> Tab.T[index + i] = Value[i]; 472 473 return TRUE; 474 } 475 476 // Auxiliar, to see if two values are equal or very different 477 static 478 cmsBool WhitesAreEqual(int n, cmsUInt16Number White1[], cmsUInt16Number White2[] ) 479 { 480 int i; 481 482 for (i=0; i < n; i++) { 483 484 if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremly different that the fixup should be avoided 485 if (White1[i] != White2[i]) return FALSE; 486 } 487 return TRUE; 488 } 489 490 491 // Locate the node for the white point and fix it to pure white in order to avoid scum dot. 492 static 493 cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColorSpace, cmsColorSpaceSignature ExitColorSpace) 494 { 495 cmsUInt16Number *WhitePointIn, *WhitePointOut; 496 cmsUInt16Number WhiteIn[cmsMAXCHANNELS], WhiteOut[cmsMAXCHANNELS], ObtainedOut[cmsMAXCHANNELS]; 497 cmsUInt32Number i, nOuts, nIns; 498 cmsStage *PreLin = NULL, *CLUT = NULL, *PostLin = NULL; 499 500 if (!_cmsEndPointsBySpace(EntryColorSpace, 501 &WhitePointIn, NULL, &nIns)) return FALSE; 502 503 if (!_cmsEndPointsBySpace(ExitColorSpace, 504 &WhitePointOut, NULL, &nOuts)) return FALSE; 505 506 // It needs to be fixed? 507 if (Lut ->InputChannels != nIns) return FALSE; 508 if (Lut ->OutputChannels != nOuts) return FALSE; 509 510 cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut); 511 512 if (WhitesAreEqual(nOuts, WhitePointOut, ObtainedOut)) return TRUE; // whites already match 513 514 // Check if the LUT comes as Prelin, CLUT or Postlin. We allow all combinations 515 if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &PreLin, &CLUT, &PostLin)) 516 if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCurveSetElemType, cmsSigCLutElemType, &PreLin, &CLUT)) 517 if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCLutElemType, cmsSigCurveSetElemType, &CLUT, &PostLin)) 518 if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCLutElemType, &CLUT)) 519 return FALSE; 520 521 // We need to interpolate white points of both, pre and post curves 522 if (PreLin) { 523 524 cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PreLin); 525 526 for (i=0; i < nIns; i++) { 527 WhiteIn[i] = cmsEvalToneCurve16(Curves[i], WhitePointIn[i]); 528 } 529 } 530 else { 531 for (i=0; i < nIns; i++) 532 WhiteIn[i] = WhitePointIn[i]; 533 } 534 535 // If any post-linearization, we need to find how is represented white before the curve, do 536 // a reverse interpolation in this case. 537 if (PostLin) { 538 539 cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PostLin); 540 541 for (i=0; i < nOuts; i++) { 542 543 cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]); 544 if (InversePostLin == NULL) { 545 WhiteOut[i] = WhitePointOut[i]; 546 547 } else { 548 549 WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]); 550 cmsFreeToneCurve(InversePostLin); 551 } 552 } 553 } 554 else { 555 for (i=0; i < nOuts; i++) 556 WhiteOut[i] = WhitePointOut[i]; 557 } 558 559 // Ok, proceed with patching. May fail and we don't care if it fails 560 PatchLUT(CLUT, WhiteIn, WhiteOut, nOuts, nIns); 561 562 return TRUE; 563 } 564 565 // ----------------------------------------------------------------------------------------------------------------------------------------------- 566 // This function creates simple LUT from complex ones. The generated LUT has an optional set of 567 // prelinearization curves, a CLUT of nGridPoints and optional postlinearization tables. 568 // These curves have to exist in the original LUT in order to be used in the simplified output. 569 // Caller may also use the flags to allow this feature. 570 // LUTS with all curves will be simplified to a single curve. Parametric curves are lost. 571 // This function should be used on 16-bits LUTS only, as floating point losses precision when simplified 572 // ----------------------------------------------------------------------------------------------------------------------------------------------- 573 574 static 575 cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) 576 { 577 cmsPipeline* Src = NULL; 578 cmsPipeline* Dest = NULL; 579 cmsStage* mpe; 580 cmsStage* CLUT; 581 cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL; 582 int nGridPoints; 583 cmsColorSpaceSignature ColorSpace, OutputColorSpace; 584 cmsStage *NewPreLin = NULL; 585 cmsStage *NewPostLin = NULL; 586 _cmsStageCLutData* DataCLUT; 587 cmsToneCurve** DataSetIn; 588 cmsToneCurve** DataSetOut; 589 Prelin16Data* p16; 590 591 // This is a loosy optimization! does not apply in floating-point cases 592 if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; 593 594 ColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat)); 595 OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat)); 596 nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); 597 598 // For empty LUTs, 2 points are enough 599 if (cmsPipelineStageCount(*Lut) == 0) 600 nGridPoints = 2; 601 602 Src = *Lut; 603 604 // Named color pipelines cannot be optimized either 605 for (mpe = cmsPipelineGetPtrToFirstStage(Src); 606 mpe != NULL; 607 mpe = cmsStageNext(mpe)) { 608 if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; 609 } 610 611 // Allocate an empty LUT 612 Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); 613 if (!Dest) return FALSE; 614 615 // Prelinearization tables are kept unless indicated by flags 616 if (*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION) { 617 618 // Get a pointer to the prelinearization element 619 cmsStage* PreLin = cmsPipelineGetPtrToFirstStage(Src); 620 621 // Check if suitable 622 if (PreLin ->Type == cmsSigCurveSetElemType) { 623 624 // Maybe this is a linear tram, so we can avoid the whole stuff 625 if (!AllCurvesAreLinear(PreLin)) { 626 627 // All seems ok, proceed. 628 NewPreLin = cmsStageDup(PreLin); 629 if(!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin)) 630 goto Error; 631 632 // Remove prelinearization. Since we have duplicated the curve 633 // in destination LUT, the sampling shoud be applied after this stage. 634 cmsPipelineUnlinkStage(Src, cmsAT_BEGIN, &KeepPreLin); 635 } 636 } 637 } 638 639 // Allocate the CLUT 640 CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL); 641 if (CLUT == NULL) return FALSE; 642 643 // Add the CLUT to the destination LUT 644 if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) { 645 goto Error; 646 } 647 648 // Postlinearization tables are kept unless indicated by flags 649 if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) { 650 651 // Get a pointer to the postlinearization if present 652 cmsStage* PostLin = cmsPipelineGetPtrToLastStage(Src); 653 654 // Check if suitable 655 if (cmsStageType(PostLin) == cmsSigCurveSetElemType) { 656 657 // Maybe this is a linear tram, so we can avoid the whole stuff 658 if (!AllCurvesAreLinear(PostLin)) { 659 660 // All seems ok, proceed. 661 NewPostLin = cmsStageDup(PostLin); 662 if (!cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin)) 663 goto Error; 664 665 // In destination LUT, the sampling shoud be applied after this stage. 666 cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin); 667 } 668 } 669 } 670 671 // Now its time to do the sampling. We have to ignore pre/post linearization 672 // The source LUT whithout pre/post curves is passed as parameter. 673 if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) { 674 Error: 675 // Ops, something went wrong, Restore stages 676 if (KeepPreLin != NULL) { 677 if (!cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin)) { 678 _cmsAssert(0); // This never happens 679 } 680 } 681 if (KeepPostLin != NULL) { 682 if (!cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin)) { 683 _cmsAssert(0); // This never happens 684 } 685 } 686 cmsPipelineFree(Dest); 687 return FALSE; 688 } 689 690 // Done. 691 692 if (KeepPreLin != NULL) cmsStageFree(KeepPreLin); 693 if (KeepPostLin != NULL) cmsStageFree(KeepPostLin); 694 cmsPipelineFree(Src); 695 696 DataCLUT = (_cmsStageCLutData*) CLUT ->Data; 697 698 if (NewPreLin == NULL) DataSetIn = NULL; 699 else DataSetIn = ((_cmsStageToneCurvesData*) NewPreLin ->Data) ->TheCurves; 700 701 if (NewPostLin == NULL) DataSetOut = NULL; 702 else DataSetOut = ((_cmsStageToneCurvesData*) NewPostLin ->Data) ->TheCurves; 703 704 705 if (DataSetIn == NULL && DataSetOut == NULL) { 706 707 _cmsPipelineSetOptimizationParameters(Dest, (_cmsOPTeval16Fn) DataCLUT->Params->Interpolation.Lerp16, DataCLUT->Params, NULL, NULL); 708 } 709 else { 710 711 p16 = PrelinOpt16alloc(Dest ->ContextID, 712 DataCLUT ->Params, 713 Dest ->InputChannels, 714 DataSetIn, 715 Dest ->OutputChannels, 716 DataSetOut); 717 718 _cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); 719 } 720 721 722 // Don't fix white on absolute colorimetric 723 if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) 724 *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; 725 726 if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { 727 728 FixWhiteMisalignment(Dest, ColorSpace, OutputColorSpace); 729 } 730 731 *Lut = Dest; 732 return TRUE; 733 734 cmsUNUSED_PARAMETER(Intent); 735 } 736 737 738 // ----------------------------------------------------------------------------------------------------------------------------------------------- 739 // Fixes the gamma balancing of transform. This is described in my paper "Prelinearization Stages on 740 // Color-Management Application-Specific Integrated Circuits (ASICs)" presented at NIP24. It only works 741 // for RGB transforms. See the paper for more details 742 // ----------------------------------------------------------------------------------------------------------------------------------------------- 743 744 745 // Normalize endpoints by slope limiting max and min. This assures endpoints as well. 746 // Descending curves are handled as well. 747 static 748 void SlopeLimiting(cmsToneCurve* g) 749 { 750 int BeginVal, EndVal; 751 int AtBegin = (int) floor((cmsFloat64Number) g ->nEntries * 0.02 + 0.5); // Cutoff at 2% 752 int AtEnd = g ->nEntries - AtBegin - 1; // And 98% 753 cmsFloat64Number Val, Slope, beta; 754 int i; 755 756 if (cmsIsToneCurveDescending(g)) { 757 BeginVal = 0xffff; EndVal = 0; 758 } 759 else { 760 BeginVal = 0; EndVal = 0xffff; 761 } 762 763 // Compute slope and offset for begin of curve 764 Val = g ->Table16[AtBegin]; 765 Slope = (Val - BeginVal) / AtBegin; 766 beta = Val - Slope * AtBegin; 767 768 for (i=0; i < AtBegin; i++) 769 g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); 770 771 // Compute slope and offset for the end 772 Val = g ->Table16[AtEnd]; 773 Slope = (EndVal - Val) / AtBegin; // AtBegin holds the X interval, which is same in both cases 774 beta = Val - Slope * AtEnd; 775 776 for (i = AtEnd; i < (int) g ->nEntries; i++) 777 g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); 778 } 779 780 781 // Precomputes tables for 8-bit on input devicelink. 782 static 783 Prelin8Data* PrelinOpt8alloc(cmsContext ContextID, const cmsInterpParams* p, cmsToneCurve* G[3]) 784 { 785 int i; 786 cmsUInt16Number Input[3]; 787 cmsS15Fixed16Number v1, v2, v3; 788 Prelin8Data* p8; 789 790 p8 = _cmsMallocZero(ContextID, sizeof(Prelin8Data)); 791 if (p8 == NULL) return NULL; 792 793 // Since this only works for 8 bit input, values comes always as x * 257, 794 // we can safely take msb byte (x << 8 + x) 795 796 for (i=0; i < 256; i++) { 797 798 if (G != NULL) { 799 800 // Get 16-bit representation 801 Input[0] = cmsEvalToneCurve16(G[0], FROM_8_TO_16(i)); 802 Input[1] = cmsEvalToneCurve16(G[1], FROM_8_TO_16(i)); 803 Input[2] = cmsEvalToneCurve16(G[2], FROM_8_TO_16(i)); 804 } 805 else { 806 Input[0] = FROM_8_TO_16(i); 807 Input[1] = FROM_8_TO_16(i); 808 Input[2] = FROM_8_TO_16(i); 809 } 810 811 812 // Move to 0..1.0 in fixed domain 813 v1 = _cmsToFixedDomain(Input[0] * p -> Domain[0]); 814 v2 = _cmsToFixedDomain(Input[1] * p -> Domain[1]); 815 v3 = _cmsToFixedDomain(Input[2] * p -> Domain[2]); 816 817 // Store the precalculated table of nodes 818 p8 ->X0[i] = (p->opta[2] * FIXED_TO_INT(v1)); 819 p8 ->Y0[i] = (p->opta[1] * FIXED_TO_INT(v2)); 820 p8 ->Z0[i] = (p->opta[0] * FIXED_TO_INT(v3)); 821 822 // Store the precalculated table of offsets 823 p8 ->rx[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v1); 824 p8 ->ry[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v2); 825 p8 ->rz[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v3); 826 } 827 828 p8 ->ContextID = ContextID; 829 p8 ->p = p; 830 831 return p8; 832 } 833 834 static 835 void Prelin8free(cmsContext ContextID, void* ptr) 836 { 837 _cmsFree(ContextID, ptr); 838 } 839 840 static 841 void* Prelin8dup(cmsContext ContextID, const void* ptr) 842 { 843 return _cmsDupMem(ContextID, ptr, sizeof(Prelin8Data)); 844 } 845 846 847 848 // A optimized interpolation for 8-bit input. 849 #define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) 850 static 851 void PrelinEval8(register const cmsUInt16Number Input[], 852 register cmsUInt16Number Output[], 853 register const void* D) 854 { 855 856 cmsUInt8Number r, g, b; 857 cmsS15Fixed16Number rx, ry, rz; 858 cmsS15Fixed16Number c0, c1, c2, c3, Rest; 859 int OutChan; 860 register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; 861 Prelin8Data* p8 = (Prelin8Data*) D; 862 register const cmsInterpParams* p = p8 ->p; 863 int TotalOut = p -> nOutputs; 864 const cmsUInt16Number* LutTable = p -> Table; 865 866 r = Input[0] >> 8; 867 g = Input[1] >> 8; 868 b = Input[2] >> 8; 869 870 X0 = X1 = p8->X0[r]; 871 Y0 = Y1 = p8->Y0[g]; 872 Z0 = Z1 = p8->Z0[b]; 873 874 rx = p8 ->rx[r]; 875 ry = p8 ->ry[g]; 876 rz = p8 ->rz[b]; 877 878 X1 = X0 + ((rx == 0) ? 0 : p ->opta[2]); 879 Y1 = Y0 + ((ry == 0) ? 0 : p ->opta[1]); 880 Z1 = Z0 + ((rz == 0) ? 0 : p ->opta[0]); 881 882 883 // These are the 6 Tetrahedral 884 for (OutChan=0; OutChan < TotalOut; OutChan++) { 885 886 c0 = DENS(X0, Y0, Z0); 887 888 if (rx >= ry && ry >= rz) 889 { 890 c1 = DENS(X1, Y0, Z0) - c0; 891 c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); 892 c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); 893 } 894 else 895 if (rx >= rz && rz >= ry) 896 { 897 c1 = DENS(X1, Y0, Z0) - c0; 898 c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); 899 c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); 900 } 901 else 902 if (rz >= rx && rx >= ry) 903 { 904 c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); 905 c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); 906 c3 = DENS(X0, Y0, Z1) - c0; 907 } 908 else 909 if (ry >= rx && rx >= rz) 910 { 911 c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); 912 c2 = DENS(X0, Y1, Z0) - c0; 913 c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); 914 } 915 else 916 if (ry >= rz && rz >= rx) 917 { 918 c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); 919 c2 = DENS(X0, Y1, Z0) - c0; 920 c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); 921 } 922 else 923 if (rz >= ry && ry >= rx) 924 { 925 c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); 926 c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); 927 c3 = DENS(X0, Y0, Z1) - c0; 928 } 929 else { 930 c1 = c2 = c3 = 0; 931 } 932 933 934 Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; 935 Output[OutChan] = (cmsUInt16Number)c0 + ((Rest + (Rest>>16))>>16); 936 937 } 938 } 939 940 #undef DENS 941 942 943 // Curves that contain wide empty areas are not optimizeable 944 static 945 cmsBool IsDegenerated(const cmsToneCurve* g) 946 { 947 int i, Zeros = 0, Poles = 0; 948 int nEntries = g ->nEntries; 949 950 for (i=0; i < nEntries; i++) { 951 952 if (g ->Table16[i] == 0x0000) Zeros++; 953 if (g ->Table16[i] == 0xffff) Poles++; 954 } 955 956 if (Zeros == 1 && Poles == 1) return FALSE; // For linear tables 957 if (Zeros > (nEntries / 4)) return TRUE; // Degenerated, mostly zeros 958 if (Poles > (nEntries / 4)) return TRUE; // Degenerated, mostly poles 959 960 return FALSE; 961 } 962 963 // -------------------------------------------------------------------------------------------------------------- 964 // We need xput over here 965 966 static 967 cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) 968 { 969 cmsPipeline* OriginalLut; 970 int nGridPoints; 971 cmsToneCurve *Trans[cmsMAXCHANNELS], *TransReverse[cmsMAXCHANNELS]; 972 cmsUInt32Number t, i; 973 cmsFloat32Number v, In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS]; 974 cmsBool lIsSuitable, lIsLinear; 975 cmsPipeline* OptimizedLUT = NULL, *LutPlusCurves = NULL; 976 cmsStage* OptimizedCLUTmpe; 977 cmsColorSpaceSignature ColorSpace, OutputColorSpace; 978 cmsStage* OptimizedPrelinMpe; 979 cmsStage* mpe; 980 cmsToneCurve** OptimizedPrelinCurves; 981 _cmsStageCLutData* OptimizedPrelinCLUT; 982 983 984 // This is a loosy optimization! does not apply in floating-point cases 985 if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; 986 987 // Only on RGB 988 if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE; 989 if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE; 990 991 992 // On 16 bits, user has to specify the feature 993 if (!_cmsFormatterIs8bit(*InputFormat)) { 994 if (!(*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION)) return FALSE; 995 } 996 997 OriginalLut = *Lut; 998 999 // Named color pipelines cannot be optimized either 1000 for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); 1001 mpe != NULL; 1002 mpe = cmsStageNext(mpe)) { 1003 if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; 1004 } 1005 1006 ColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat)); 1007 OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat)); 1008 nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); 1009 1010 // Empty gamma containers 1011 memset(Trans, 0, sizeof(Trans)); 1012 memset(TransReverse, 0, sizeof(TransReverse)); 1013 1014 for (t = 0; t < OriginalLut ->InputChannels; t++) { 1015 Trans[t] = cmsBuildTabulatedToneCurve16(OriginalLut ->ContextID, PRELINEARIZATION_POINTS, NULL); 1016 if (Trans[t] == NULL) goto Error; 1017 } 1018 1019 // Populate the curves 1020 for (i=0; i < PRELINEARIZATION_POINTS; i++) { 1021 1022 v = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); 1023 1024 // Feed input with a gray ramp 1025 for (t=0; t < OriginalLut ->InputChannels; t++) 1026 In[t] = v; 1027 1028 // Evaluate the gray value 1029 cmsPipelineEvalFloat(In, Out, OriginalLut); 1030 1031 // Store result in curve 1032 for (t=0; t < OriginalLut ->InputChannels; t++) 1033 Trans[t] ->Table16[i] = _cmsQuickSaturateWord(Out[t] * 65535.0); 1034 } 1035 1036 // Slope-limit the obtained curves 1037 for (t = 0; t < OriginalLut ->InputChannels; t++) 1038 SlopeLimiting(Trans[t]); 1039 1040 // Check for validity 1041 lIsSuitable = TRUE; 1042 lIsLinear = TRUE; 1043 for (t=0; (lIsSuitable && (t < OriginalLut ->InputChannels)); t++) { 1044 1045 // Exclude if already linear 1046 if (!cmsIsToneCurveLinear(Trans[t])) 1047 lIsLinear = FALSE; 1048 1049 // Exclude if non-monotonic 1050 if (!cmsIsToneCurveMonotonic(Trans[t])) 1051 lIsSuitable = FALSE; 1052 1053 if (IsDegenerated(Trans[t])) 1054 lIsSuitable = FALSE; 1055 } 1056 1057 // If it is not suitable, just quit 1058 if (!lIsSuitable) goto Error; 1059 1060 // Invert curves if possible 1061 for (t = 0; t < OriginalLut ->InputChannels; t++) { 1062 TransReverse[t] = cmsReverseToneCurveEx(PRELINEARIZATION_POINTS, Trans[t]); 1063 if (TransReverse[t] == NULL) goto Error; 1064 } 1065 1066 // Now inset the reversed curves at the begin of transform 1067 LutPlusCurves = cmsPipelineDup(OriginalLut); 1068 if (LutPlusCurves == NULL) goto Error; 1069 1070 if (!cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, TransReverse))) 1071 goto Error; 1072 1073 // Create the result LUT 1074 OptimizedLUT = cmsPipelineAlloc(OriginalLut ->ContextID, OriginalLut ->InputChannels, OriginalLut ->OutputChannels); 1075 if (OptimizedLUT == NULL) goto Error; 1076 1077 OptimizedPrelinMpe = cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, Trans); 1078 1079 // Create and insert the curves at the beginning 1080 if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe)) 1081 goto Error; 1082 1083 // Allocate the CLUT for result 1084 OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL); 1085 1086 // Add the CLUT to the destination LUT 1087 if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe)) 1088 goto Error; 1089 1090 // Resample the LUT 1091 if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error; 1092 1093 // Free resources 1094 for (t = 0; t < OriginalLut ->InputChannels; t++) { 1095 1096 if (Trans[t]) cmsFreeToneCurve(Trans[t]); 1097 if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); 1098 } 1099 1100 cmsPipelineFree(LutPlusCurves); 1101 1102 1103 OptimizedPrelinCurves = _cmsStageGetPtrToCurveSet(OptimizedPrelinMpe); 1104 OptimizedPrelinCLUT = (_cmsStageCLutData*) OptimizedCLUTmpe ->Data; 1105 1106 // Set the evaluator if 8-bit 1107 if (_cmsFormatterIs8bit(*InputFormat)) { 1108 1109 Prelin8Data* p8 = PrelinOpt8alloc(OptimizedLUT ->ContextID, 1110 OptimizedPrelinCLUT ->Params, 1111 OptimizedPrelinCurves); 1112 if (p8 == NULL) return FALSE; 1113 1114 _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval8, (void*) p8, Prelin8free, Prelin8dup); 1115 1116 } 1117 else 1118 { 1119 Prelin16Data* p16 = PrelinOpt16alloc(OptimizedLUT ->ContextID, 1120 OptimizedPrelinCLUT ->Params, 1121 3, OptimizedPrelinCurves, 3, NULL); 1122 if (p16 == NULL) return FALSE; 1123 1124 _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); 1125 1126 } 1127 1128 // Don't fix white on absolute colorimetric 1129 if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) 1130 *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; 1131 1132 if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { 1133 1134 if (!FixWhiteMisalignment(OptimizedLUT, ColorSpace, OutputColorSpace)) { 1135 1136 return FALSE; 1137 } 1138 } 1139 1140 // And return the obtained LUT 1141 1142 cmsPipelineFree(OriginalLut); 1143 *Lut = OptimizedLUT; 1144 return TRUE; 1145 1146 Error: 1147 1148 for (t = 0; t < OriginalLut ->InputChannels; t++) { 1149 1150 if (Trans[t]) cmsFreeToneCurve(Trans[t]); 1151 if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); 1152 } 1153 1154 if (LutPlusCurves != NULL) cmsPipelineFree(LutPlusCurves); 1155 if (OptimizedLUT != NULL) cmsPipelineFree(OptimizedLUT); 1156 1157 return FALSE; 1158 1159 cmsUNUSED_PARAMETER(Intent); 1160 } 1161 1162 1163 // Curves optimizer ------------------------------------------------------------------------------------------------------------------ 1164 1165 static 1166 void CurvesFree(cmsContext ContextID, void* ptr) 1167 { 1168 Curves16Data* Data = (Curves16Data*) ptr; 1169 int i; 1170 1171 for (i=0; i < Data -> nCurves; i++) { 1172 1173 _cmsFree(ContextID, Data ->Curves[i]); 1174 } 1175 1176 _cmsFree(ContextID, Data ->Curves); 1177 _cmsFree(ContextID, ptr); 1178 } 1179 1180 static 1181 void* CurvesDup(cmsContext ContextID, const void* ptr) 1182 { 1183 Curves16Data* Data = _cmsDupMem(ContextID, ptr, sizeof(Curves16Data)); 1184 int i; 1185 1186 if (Data == NULL) return NULL; 1187 1188 Data ->Curves = _cmsDupMem(ContextID, Data ->Curves, Data ->nCurves * sizeof(cmsUInt16Number*)); 1189 1190 for (i=0; i < Data -> nCurves; i++) { 1191 Data ->Curves[i] = _cmsDupMem(ContextID, Data ->Curves[i], Data -> nElements * sizeof(cmsUInt16Number)); 1192 } 1193 1194 return (void*) Data; 1195 } 1196 1197 // Precomputes tables for 8-bit on input devicelink. 1198 static 1199 Curves16Data* CurvesAlloc(cmsContext ContextID, int nCurves, int nElements, cmsToneCurve** G) 1200 { 1201 int i, j; 1202 Curves16Data* c16; 1203 1204 c16 = _cmsMallocZero(ContextID, sizeof(Curves16Data)); 1205 if (c16 == NULL) return NULL; 1206 1207 c16 ->nCurves = nCurves; 1208 c16 ->nElements = nElements; 1209 1210 c16 ->Curves = _cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*)); 1211 if (c16 ->Curves == NULL) return NULL; 1212 1213 for (i=0; i < nCurves; i++) { 1214 1215 c16->Curves[i] = _cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number)); 1216 1217 if (c16->Curves[i] == NULL) { 1218 1219 for (j=0; j < i; j++) { 1220 _cmsFree(ContextID, c16->Curves[j]); 1221 } 1222 _cmsFree(ContextID, c16->Curves); 1223 _cmsFree(ContextID, c16); 1224 return NULL; 1225 } 1226 1227 if (nElements == 256) { 1228 1229 for (j=0; j < nElements; j++) { 1230 1231 c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], FROM_8_TO_16(j)); 1232 } 1233 } 1234 else { 1235 1236 for (j=0; j < nElements; j++) { 1237 c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], (cmsUInt16Number) j); 1238 } 1239 } 1240 } 1241 1242 return c16; 1243 } 1244 1245 static 1246 void FastEvaluateCurves8(register const cmsUInt16Number In[], 1247 register cmsUInt16Number Out[], 1248 register const void* D) 1249 { 1250 Curves16Data* Data = (Curves16Data*) D; 1251 cmsUInt8Number x; 1252 int i; 1253 1254 for (i=0; i < Data ->nCurves; i++) { 1255 1256 x = (In[i] >> 8); 1257 Out[i] = Data -> Curves[i][x]; 1258 } 1259 } 1260 1261 1262 static 1263 void FastEvaluateCurves16(register const cmsUInt16Number In[], 1264 register cmsUInt16Number Out[], 1265 register const void* D) 1266 { 1267 Curves16Data* Data = (Curves16Data*) D; 1268 int i; 1269 1270 for (i=0; i < Data ->nCurves; i++) { 1271 Out[i] = Data -> Curves[i][In[i]]; 1272 } 1273 } 1274 1275 1276 static 1277 void FastIdentity16(register const cmsUInt16Number In[], 1278 register cmsUInt16Number Out[], 1279 register const void* D) 1280 { 1281 cmsPipeline* Lut = (cmsPipeline*) D; 1282 cmsUInt32Number i; 1283 1284 for (i=0; i < Lut ->InputChannels; i++) { 1285 Out[i] = In[i]; 1286 } 1287 } 1288 1289 1290 // If the target LUT holds only curves, the optimization procedure is to join all those 1291 // curves together. That only works on curves and does not work on matrices. 1292 static 1293 cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) 1294 { 1295 cmsToneCurve** GammaTables = NULL; 1296 cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; 1297 cmsUInt32Number i, j; 1298 cmsPipeline* Src = *Lut; 1299 cmsPipeline* Dest = NULL; 1300 cmsStage* mpe; 1301 cmsStage* ObtainedCurves = NULL; 1302 1303 1304 // This is a loosy optimization! does not apply in floating-point cases 1305 if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; 1306 1307 // Only curves in this LUT? 1308 for (mpe = cmsPipelineGetPtrToFirstStage(Src); 1309 mpe != NULL; 1310 mpe = cmsStageNext(mpe)) { 1311 if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE; 1312 } 1313 1314 // Allocate an empty LUT 1315 Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); 1316 if (Dest == NULL) return FALSE; 1317 1318 // Create target curves 1319 GammaTables = (cmsToneCurve**) _cmsCalloc(Src ->ContextID, Src ->InputChannels, sizeof(cmsToneCurve*)); 1320 if (GammaTables == NULL) goto Error; 1321 1322 for (i=0; i < Src ->InputChannels; i++) { 1323 GammaTables[i] = cmsBuildTabulatedToneCurve16(Src ->ContextID, PRELINEARIZATION_POINTS, NULL); 1324 if (GammaTables[i] == NULL) goto Error; 1325 } 1326 1327 // Compute 16 bit result by using floating point 1328 for (i=0; i < PRELINEARIZATION_POINTS; i++) { 1329 1330 for (j=0; j < Src ->InputChannels; j++) 1331 InFloat[j] = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); 1332 1333 cmsPipelineEvalFloat(InFloat, OutFloat, Src); 1334 1335 for (j=0; j < Src ->InputChannels; j++) 1336 GammaTables[j] -> Table16[i] = _cmsQuickSaturateWord(OutFloat[j] * 65535.0); 1337 } 1338 1339 ObtainedCurves = cmsStageAllocToneCurves(Src ->ContextID, Src ->InputChannels, GammaTables); 1340 if (ObtainedCurves == NULL) goto Error; 1341 1342 for (i=0; i < Src ->InputChannels; i++) { 1343 cmsFreeToneCurve(GammaTables[i]); 1344 GammaTables[i] = NULL; 1345 } 1346 1347 if (GammaTables != NULL) _cmsFree(Src ->ContextID, GammaTables); 1348 1349 // Maybe the curves are linear at the end 1350 if (!AllCurvesAreLinear(ObtainedCurves)) { 1351 1352 if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves)) 1353 goto Error; 1354 1355 // If the curves are to be applied in 8 bits, we can save memory 1356 if (_cmsFormatterIs8bit(*InputFormat)) { 1357 1358 _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) ObtainedCurves ->Data; 1359 Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves); 1360 1361 if (c16 == NULL) goto Error; 1362 *dwFlags |= cmsFLAGS_NOCACHE; 1363 _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup); 1364 1365 } 1366 else { 1367 1368 _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves); 1369 Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves); 1370 1371 if (c16 == NULL) goto Error; 1372 *dwFlags |= cmsFLAGS_NOCACHE; 1373 _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup); 1374 } 1375 } 1376 else { 1377 1378 // LUT optimizes to nothing. Set the identity LUT 1379 cmsStageFree(ObtainedCurves); 1380 1381 if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels))) 1382 goto Error; 1383 1384 *dwFlags |= cmsFLAGS_NOCACHE; 1385 _cmsPipelineSetOptimizationParameters(Dest, FastIdentity16, (void*) Dest, NULL, NULL); 1386 } 1387 1388 // We are done. 1389 cmsPipelineFree(Src); 1390 *Lut = Dest; 1391 return TRUE; 1392 1393 Error: 1394 1395 if (ObtainedCurves != NULL) cmsStageFree(ObtainedCurves); 1396 if (GammaTables != NULL) { 1397 for (i=0; i < Src ->InputChannels; i++) { 1398 if (GammaTables[i] != NULL) cmsFreeToneCurve(GammaTables[i]); 1399 } 1400 1401 _cmsFree(Src ->ContextID, GammaTables); 1402 } 1403 1404 if (Dest != NULL) cmsPipelineFree(Dest); 1405 return FALSE; 1406 1407 cmsUNUSED_PARAMETER(Intent); 1408 cmsUNUSED_PARAMETER(InputFormat); 1409 cmsUNUSED_PARAMETER(OutputFormat); 1410 cmsUNUSED_PARAMETER(dwFlags); 1411 } 1412 1413 // ------------------------------------------------------------------------------------------------------------------------------------- 1414 // LUT is Shaper - Matrix - Matrix - Shaper, which is very frequent when combining two matrix-shaper profiles 1415 1416 1417 static 1418 void FreeMatShaper(cmsContext ContextID, void* Data) 1419 { 1420 if (Data != NULL) _cmsFree(ContextID, Data); 1421 } 1422 1423 static 1424 void* DupMatShaper(cmsContext ContextID, const void* Data) 1425 { 1426 return _cmsDupMem(ContextID, Data, sizeof(MatShaper8Data)); 1427 } 1428 1429 1430 // A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point 1431 // to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits, 1432 // in total about 50K, and the performance boost is huge! 1433 static 1434 void MatShaperEval16(register const cmsUInt16Number In[], 1435 register cmsUInt16Number Out[], 1436 register const void* D) 1437 { 1438 MatShaper8Data* p = (MatShaper8Data*) D; 1439 cmsS1Fixed14Number l1, l2, l3, r, g, b; 1440 cmsUInt32Number ri, gi, bi; 1441 1442 // In this case (and only in this case!) we can use this simplification since 1443 // In[] is assured to come from a 8 bit number. (a << 8 | a) 1444 ri = In[0] & 0xFF; 1445 gi = In[1] & 0xFF; 1446 bi = In[2] & 0xFF; 1447 1448 // Across first shaper, which also converts to 1.14 fixed point 1449 r = p->Shaper1R[ri]; 1450 g = p->Shaper1G[gi]; 1451 b = p->Shaper1B[bi]; 1452 1453 // Evaluate the matrix in 1.14 fixed point 1454 l1 = (p->Mat[0][0] * r + p->Mat[0][1] * g + p->Mat[0][2] * b + p->Off[0] + 0x2000) >> 14; 1455 l2 = (p->Mat[1][0] * r + p->Mat[1][1] * g + p->Mat[1][2] * b + p->Off[1] + 0x2000) >> 14; 1456 l3 = (p->Mat[2][0] * r + p->Mat[2][1] * g + p->Mat[2][2] * b + p->Off[2] + 0x2000) >> 14; 1457 1458 // Now we have to clip to 0..1.0 range 1459 ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384 : l1); 1460 gi = (l2 < 0) ? 0 : ((l2 > 16384) ? 16384 : l2); 1461 bi = (l3 < 0) ? 0 : ((l3 > 16384) ? 16384 : l3); 1462 1463 // And across second shaper, 1464 Out[0] = p->Shaper2R[ri]; 1465 Out[1] = p->Shaper2G[gi]; 1466 Out[2] = p->Shaper2B[bi]; 1467 1468 } 1469 1470 // This table converts from 8 bits to 1.14 after applying the curve 1471 static 1472 void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) 1473 { 1474 int i; 1475 cmsFloat32Number R, y; 1476 1477 for (i=0; i < 256; i++) { 1478 1479 R = (cmsFloat32Number) (i / 255.0); 1480 y = cmsEvalToneCurveFloat(Curve, R); 1481 1482 Table[i] = DOUBLE_TO_1FIXED14(y); 1483 } 1484 } 1485 1486 // This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve 1487 static 1488 void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) 1489 { 1490 int i; 1491 cmsFloat32Number R, Val; 1492 1493 for (i=0; i < 16385; i++) { 1494 1495 R = (cmsFloat32Number) (i / 16384.0); 1496 Val = cmsEvalToneCurveFloat(Curve, R); // Val comes 0..1.0 1497 1498 if (Is8BitsOutput) { 1499 1500 // If 8 bits output, we can optimize further by computing the / 257 part. 1501 // first we compute the resulting byte and then we store the byte times 1502 // 257. This quantization allows to round very quick by doing a >> 8, but 1503 // since the low byte is always equal to msb, we can do a & 0xff and this works! 1504 cmsUInt16Number w = _cmsQuickSaturateWord(Val * 65535.0); 1505 cmsUInt8Number b = FROM_16_TO_8(w); 1506 1507 Table[i] = FROM_8_TO_16(b); 1508 } 1509 else Table[i] = _cmsQuickSaturateWord(Val * 65535.0); 1510 } 1511 } 1512 1513 // Compute the matrix-shaper structure 1514 static 1515 cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, cmsVEC3* Off, cmsToneCurve* Curve2[3], cmsUInt32Number* OutputFormat) 1516 { 1517 MatShaper8Data* p; 1518 int i, j; 1519 cmsBool Is8Bits = _cmsFormatterIs8bit(*OutputFormat); 1520 1521 // Allocate a big chuck of memory to store precomputed tables 1522 p = (MatShaper8Data*) _cmsMalloc(Dest ->ContextID, sizeof(MatShaper8Data)); 1523 if (p == NULL) return FALSE; 1524 1525 p -> ContextID = Dest -> ContextID; 1526 1527 // Precompute tables 1528 FillFirstShaper(p ->Shaper1R, Curve1[0]); 1529 FillFirstShaper(p ->Shaper1G, Curve1[1]); 1530 FillFirstShaper(p ->Shaper1B, Curve1[2]); 1531 1532 FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits); 1533 FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits); 1534 FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits); 1535 1536 // Convert matrix to nFixed14. Note that those values may take more than 16 bits as 1537 for (i=0; i < 3; i++) { 1538 for (j=0; j < 3; j++) { 1539 p ->Mat[i][j] = DOUBLE_TO_1FIXED14(Mat->v[i].n[j]); 1540 } 1541 } 1542 1543 for (i=0; i < 3; i++) { 1544 1545 if (Off == NULL) { 1546 p ->Off[i] = 0; 1547 } 1548 else { 1549 p ->Off[i] = DOUBLE_TO_1FIXED14(Off->n[i]); 1550 } 1551 } 1552 1553 // Mark as optimized for faster formatter 1554 if (Is8Bits) 1555 *OutputFormat |= OPTIMIZED_SH(1); 1556 1557 // Fill function pointers 1558 _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper); 1559 return TRUE; 1560 } 1561 1562 // 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast! 1563 // TODO: Allow a third matrix for abs. colorimetric 1564 static 1565 cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) 1566 { 1567 cmsStage* Curve1, *Curve2; 1568 cmsStage* Matrix1, *Matrix2; 1569 _cmsStageMatrixData* Data1; 1570 _cmsStageMatrixData* Data2; 1571 cmsMAT3 res; 1572 cmsBool IdentityMat; 1573 cmsPipeline* Dest, *Src; 1574 1575 // Only works on RGB to RGB 1576 if (T_CHANNELS(*InputFormat) != 3 || T_CHANNELS(*OutputFormat) != 3) return FALSE; 1577 1578 // Only works on 8 bit input 1579 if (!_cmsFormatterIs8bit(*InputFormat)) return FALSE; 1580 1581 // Seems suitable, proceed 1582 Src = *Lut; 1583 1584 // Check for shaper-matrix-matrix-shaper structure, that is what this optimizer stands for 1585 if (!cmsPipelineCheckAndRetreiveStages(Src, 4, 1586 cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, 1587 &Curve1, &Matrix1, &Matrix2, &Curve2)) return FALSE; 1588 1589 // Get both matrices 1590 Data1 = (_cmsStageMatrixData*) cmsStageData(Matrix1); 1591 Data2 = (_cmsStageMatrixData*) cmsStageData(Matrix2); 1592 1593 // Input offset should be zero 1594 if (Data1 ->Offset != NULL) return FALSE; 1595 1596 // Multiply both matrices to get the result 1597 _cmsMAT3per(&res, (cmsMAT3*) Data2 ->Double, (cmsMAT3*) Data1 ->Double); 1598 1599 // Now the result is in res + Data2 -> Offset. Maybe is a plain identity? 1600 IdentityMat = FALSE; 1601 if (_cmsMAT3isIdentity(&res) && Data2 ->Offset == NULL) { 1602 1603 // We can get rid of full matrix 1604 IdentityMat = TRUE; 1605 } 1606 1607 // Allocate an empty LUT 1608 Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); 1609 if (!Dest) return FALSE; 1610 1611 // Assamble the new LUT 1612 if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1))) 1613 goto Error; 1614 1615 if (!IdentityMat) 1616 if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest ->ContextID, 3, 3, (const cmsFloat64Number*) &res, Data2 ->Offset))) 1617 goto Error; 1618 if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2))) 1619 goto Error; 1620 1621 // If identity on matrix, we can further optimize the curves, so call the join curves routine 1622 if (IdentityMat) { 1623 1624 OptimizeByJoiningCurves(&Dest, Intent, InputFormat, OutputFormat, dwFlags); 1625 } 1626 else { 1627 _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1); 1628 _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2); 1629 1630 // In this particular optimization, caché does not help as it takes more time to deal with 1631 // the caché that with the pixel handling 1632 *dwFlags |= cmsFLAGS_NOCACHE; 1633 1634 // Setup the optimizarion routines 1635 SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Data2 ->Offset, mpeC2->TheCurves, OutputFormat); 1636 } 1637 1638 cmsPipelineFree(Src); 1639 *Lut = Dest; 1640 return TRUE; 1641 Error: 1642 // Leave Src unchanged 1643 cmsPipelineFree(Dest); 1644 return FALSE; 1645 } 1646 1647 1648 // ------------------------------------------------------------------------------------------------------------------------------------- 1649 // Optimization plug-ins 1650 1651 // List of optimizations 1652 typedef struct _cmsOptimizationCollection_st { 1653 1654 _cmsOPToptimizeFn OptimizePtr; 1655 1656 struct _cmsOptimizationCollection_st *Next; 1657 1658 } _cmsOptimizationCollection; 1659 1660 1661 // The built-in list. We currently implement 4 types of optimizations. Joining of curves, matrix-shaper, linearization and resampling 1662 static _cmsOptimizationCollection DefaultOptimization[] = { 1663 1664 { OptimizeByJoiningCurves, &DefaultOptimization[1] }, 1665 { OptimizeMatrixShaper, &DefaultOptimization[2] }, 1666 { OptimizeByComputingLinearization, &DefaultOptimization[3] }, 1667 { OptimizeByResampling, NULL } 1668 }; 1669 1670 // The linked list head 1671 _cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk = { NULL }; 1672 1673 1674 // Duplicates the zone of memory used by the plug-in in the new context 1675 static 1676 void DupPluginOptimizationList(struct _cmsContext_struct* ctx, 1677 const struct _cmsContext_struct* src) 1678 { 1679 _cmsOptimizationPluginChunkType newHead = { NULL }; 1680 _cmsOptimizationCollection* entry; 1681 _cmsOptimizationCollection* Anterior = NULL; 1682 _cmsOptimizationPluginChunkType* head = (_cmsOptimizationPluginChunkType*) src->chunks[OptimizationPlugin]; 1683 1684 _cmsAssert(ctx != NULL); 1685 _cmsAssert(head != NULL); 1686 1687 // Walk the list copying all nodes 1688 for (entry = head->OptimizationCollection; 1689 entry != NULL; 1690 entry = entry ->Next) { 1691 1692 _cmsOptimizationCollection *newEntry = ( _cmsOptimizationCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsOptimizationCollection)); 1693 1694 if (newEntry == NULL) 1695 return; 1696 1697 // We want to keep the linked list order, so this is a little bit tricky 1698 newEntry -> Next = NULL; 1699 if (Anterior) 1700 Anterior -> Next = newEntry; 1701 1702 Anterior = newEntry; 1703 1704 if (newHead.OptimizationCollection == NULL) 1705 newHead.OptimizationCollection = newEntry; 1706 } 1707 1708 ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsOptimizationPluginChunkType)); 1709 } 1710 1711 void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx, 1712 const struct _cmsContext_struct* src) 1713 { 1714 if (src != NULL) { 1715 1716 // Copy all linked list 1717 DupPluginOptimizationList(ctx, src); 1718 } 1719 else { 1720 static _cmsOptimizationPluginChunkType OptimizationPluginChunkType = { NULL }; 1721 ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx ->MemPool, &OptimizationPluginChunkType, sizeof(_cmsOptimizationPluginChunkType)); 1722 } 1723 } 1724 1725 1726 // Register new ways to optimize 1727 cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Data) 1728 { 1729 cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data; 1730 _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); 1731 _cmsOptimizationCollection* fl; 1732 1733 if (Data == NULL) { 1734 1735 ctx->OptimizationCollection = NULL; 1736 return TRUE; 1737 } 1738 1739 // Optimizer callback is required 1740 if (Plugin ->OptimizePtr == NULL) return FALSE; 1741 1742 fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsOptimizationCollection)); 1743 if (fl == NULL) return FALSE; 1744 1745 // Copy the parameters 1746 fl ->OptimizePtr = Plugin ->OptimizePtr; 1747 1748 // Keep linked list 1749 fl ->Next = ctx->OptimizationCollection; 1750 1751 // Set the head 1752 ctx ->OptimizationCollection = fl; 1753 1754 // All is ok 1755 return TRUE; 1756 } 1757 1758 // The entry point for LUT optimization 1759 cmsBool _cmsOptimizePipeline(cmsContext ContextID, 1760 cmsPipeline** PtrLut, 1761 int Intent, 1762 cmsUInt32Number* InputFormat, 1763 cmsUInt32Number* OutputFormat, 1764 cmsUInt32Number* dwFlags) 1765 { 1766 _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); 1767 _cmsOptimizationCollection* Opts; 1768 cmsBool AnySuccess = FALSE; 1769 1770 // A CLUT is being asked, so force this specific optimization 1771 if (*dwFlags & cmsFLAGS_FORCE_CLUT) { 1772 1773 PreOptimize(*PtrLut); 1774 return OptimizeByResampling(PtrLut, Intent, InputFormat, OutputFormat, dwFlags); 1775 } 1776 1777 // Anything to optimize? 1778 if ((*PtrLut) ->Elements == NULL) { 1779 _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); 1780 return TRUE; 1781 } 1782 1783 // Try to get rid of identities and trivial conversions. 1784 AnySuccess = PreOptimize(*PtrLut); 1785 1786 // After removal do we end with an identity? 1787 if ((*PtrLut) ->Elements == NULL) { 1788 _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); 1789 return TRUE; 1790 } 1791 1792 // Do not optimize, keep all precision 1793 if (*dwFlags & cmsFLAGS_NOOPTIMIZE) 1794 return FALSE; 1795 1796 // Try plug-in optimizations 1797 for (Opts = ctx->OptimizationCollection; 1798 Opts != NULL; 1799 Opts = Opts ->Next) { 1800 1801 // If one schema succeeded, we are done 1802 if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) { 1803 1804 return TRUE; // Optimized! 1805 } 1806 } 1807 1808 // Try built-in optimizations 1809 for (Opts = DefaultOptimization; 1810 Opts != NULL; 1811 Opts = Opts ->Next) { 1812 1813 if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) { 1814 1815 return TRUE; 1816 } 1817 } 1818 1819 // Only simple optimizations succeeded 1820 return AnySuccess; 1821 } 1822 1823 1824