Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/lcms2/src/cmslut.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 | |
| 27 #include "lcms2_internal.h" | |
| 28 | |
| 29 | |
| 30 // Allocates an empty multi profile element | |
| 31 cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, | |
| 32 cmsStageSignature Type, | |
| 33 cmsUInt32Number InputChannels, | |
| 34 cmsUInt32Number OutputChannels, | |
| 35 _cmsStageEvalFn EvalPtr, | |
| 36 _cmsStageDupElemFn DupElemPtr, | |
| 37 _cmsStageFreeElemFn FreePtr, | |
| 38 void* Data) | |
| 39 { | |
| 40 cmsStage* ph = (cmsStage*) _cmsMallocZero(ContextID, sizeof(cmsStage)); | |
| 41 | |
| 42 if (ph == NULL) return NULL; | |
| 43 | |
| 44 ph ->Type = Type; | |
| 45 ph ->Implements = Type; // By default, no clue on what is implementing | |
| 46 | |
| 47 ph ->InputChannels = InputChannels; | |
| 48 ph ->OutputChannels = OutputChannels; | |
| 49 ph ->EvalPtr = EvalPtr; | |
| 50 ph ->DupElemPtr = DupElemPtr; | |
| 51 ph ->FreePtr = FreePtr; | |
| 52 ph ->Data = Data; | |
| 53 | |
| 54 return ph; | |
| 55 } | |
| 56 | |
| 57 | |
| 58 static | |
| 59 void EvaluateIdentity(cmsContext ContextID, const cmsFloat32Number In[], | |
| 60 cmsFloat32Number Out[], | |
| 61 const cmsStage *mpe) | |
| 62 { | |
| 63 cmsUNUSED_PARAMETER(ContextID); | |
| 64 memmove(Out, In, mpe ->InputChannels * sizeof(cmsFloat32Number)); | |
| 65 } | |
| 66 | |
| 67 | |
| 68 cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels) | |
| 69 { | |
| 70 return _cmsStageAllocPlaceholder(ContextID, | |
| 71 cmsSigIdentityElemType, | |
| 72 nChannels, nChannels, | |
| 73 EvaluateIdentity, | |
| 74 NULL, | |
| 75 NULL, | |
| 76 NULL); | |
| 77 } | |
| 78 | |
| 79 // Conversion functions. From floating point to 16 bits | |
| 80 static | |
| 81 void FromFloatTo16(const cmsFloat32Number In[], cmsUInt16Number Out[], cmsUInt32Number n) | |
| 82 { | |
| 83 cmsUInt32Number i; | |
| 84 | |
| 85 for (i=0; i < n; i++) { | |
| 86 Out[i] = _cmsQuickSaturateWord(In[i] * 65535.0); | |
| 87 } | |
| 88 } | |
| 89 | |
| 90 // From 16 bits to floating point | |
| 91 static | |
| 92 void From16ToFloat(const cmsUInt16Number In[], cmsFloat32Number Out[], cmsUInt32Number n) | |
| 93 { | |
| 94 cmsUInt32Number i; | |
| 95 | |
| 96 for (i=0; i < n; i++) { | |
| 97 Out[i] = (cmsFloat32Number) In[i] / 65535.0F; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 | |
| 102 // This function is quite useful to analyze the structure of a LUT and retrieve the MPE elements | |
| 103 // that conform the LUT. It should be called with the LUT, the number of expected elements and | |
| 104 // then a list of expected types followed with a list of cmsFloat64Number pointers to MPE elements. If | |
| 105 // the function founds a match with current pipeline, it fills the pointers and returns TRUE | |
| 106 // if not, returns FALSE without touching anything. Setting pointers to NULL does bypass | |
| 107 // the storage process. | |
| 108 cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(cmsContext ContextID, const cmsPipeline* Lut, cmsUInt32Number n, ...) | |
| 109 { | |
| 110 va_list args; | |
| 111 cmsUInt32Number i; | |
| 112 cmsStage* mpe; | |
| 113 cmsStageSignature Type; | |
| 114 void** ElemPtr; | |
| 115 | |
| 116 // Make sure same number of elements | |
| 117 if (cmsPipelineStageCount(ContextID, Lut) != n) return FALSE; | |
| 118 | |
| 119 va_start(args, n); | |
| 120 | |
| 121 // Iterate across asked types | |
| 122 mpe = Lut ->Elements; | |
| 123 for (i=0; i < n; i++) { | |
| 124 | |
| 125 // Get asked type. cmsStageSignature is promoted to int by compiler | |
| 126 Type = (cmsStageSignature)va_arg(args, int); | |
| 127 if (mpe ->Type != Type) { | |
| 128 | |
| 129 va_end(args); // Mismatch. We are done. | |
| 130 return FALSE; | |
| 131 } | |
| 132 mpe = mpe ->Next; | |
| 133 } | |
| 134 | |
| 135 // Found a combination, fill pointers if not NULL | |
| 136 mpe = Lut ->Elements; | |
| 137 for (i=0; i < n; i++) { | |
| 138 | |
| 139 ElemPtr = va_arg(args, void**); | |
| 140 if (ElemPtr != NULL) | |
| 141 *ElemPtr = mpe; | |
| 142 | |
| 143 mpe = mpe ->Next; | |
| 144 } | |
| 145 | |
| 146 va_end(args); | |
| 147 return TRUE; | |
| 148 } | |
| 149 | |
| 150 // Below there are implementations for several types of elements. Each type may be implemented by a | |
| 151 // evaluation function, a duplication function, a function to free resources and a constructor. | |
| 152 | |
| 153 // ************************************************************************************************* | |
| 154 // Type cmsSigCurveSetElemType (curves) | |
| 155 // ************************************************************************************************* | |
| 156 | |
| 157 cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe) | |
| 158 { | |
| 159 _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; | |
| 160 | |
| 161 return Data ->TheCurves; | |
| 162 } | |
| 163 | |
| 164 static | |
| 165 void EvaluateCurves(cmsContext ContextID, const cmsFloat32Number In[], | |
| 166 cmsFloat32Number Out[], | |
| 167 const cmsStage *mpe) | |
| 168 { | |
| 169 _cmsStageToneCurvesData* Data; | |
| 170 cmsUInt32Number i; | |
| 171 | |
| 172 _cmsAssert(mpe != NULL); | |
| 173 | |
| 174 Data = (_cmsStageToneCurvesData*) mpe ->Data; | |
| 175 if (Data == NULL) return; | |
| 176 | |
| 177 if (Data ->TheCurves == NULL) return; | |
| 178 | |
| 179 for (i=0; i < Data ->nCurves; i++) { | |
| 180 Out[i] = cmsEvalToneCurveFloat(ContextID, Data ->TheCurves[i], In[i]); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 static | |
| 185 void CurveSetElemTypeFree(cmsContext ContextID, cmsStage* mpe) | |
| 186 { | |
| 187 _cmsStageToneCurvesData* Data; | |
| 188 cmsUInt32Number i; | |
| 189 | |
| 190 _cmsAssert(mpe != NULL); | |
| 191 | |
| 192 Data = (_cmsStageToneCurvesData*) mpe ->Data; | |
| 193 if (Data == NULL) return; | |
| 194 | |
| 195 if (Data ->TheCurves != NULL) { | |
| 196 for (i=0; i < Data ->nCurves; i++) { | |
| 197 if (Data ->TheCurves[i] != NULL) | |
| 198 cmsFreeToneCurve(ContextID, Data ->TheCurves[i]); | |
| 199 } | |
| 200 } | |
| 201 _cmsFree(ContextID, Data ->TheCurves); | |
| 202 _cmsFree(ContextID, Data); | |
| 203 } | |
| 204 | |
| 205 | |
| 206 static | |
| 207 void* CurveSetDup(cmsContext ContextID, cmsStage* mpe) | |
| 208 { | |
| 209 _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; | |
| 210 _cmsStageToneCurvesData* NewElem; | |
| 211 cmsUInt32Number i; | |
| 212 | |
| 213 NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData)); | |
| 214 if (NewElem == NULL) return NULL; | |
| 215 | |
| 216 NewElem ->nCurves = Data ->nCurves; | |
| 217 NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, NewElem ->nCurves, sizeof(cmsToneCurve*)); | |
| 218 | |
| 219 if (NewElem ->TheCurves == NULL) goto Error; | |
| 220 | |
| 221 for (i=0; i < NewElem ->nCurves; i++) { | |
| 222 | |
| 223 // Duplicate each curve. It may fail. | |
| 224 NewElem ->TheCurves[i] = cmsDupToneCurve(ContextID, Data ->TheCurves[i]); | |
| 225 if (NewElem ->TheCurves[i] == NULL) goto Error; | |
| 226 | |
| 227 | |
| 228 } | |
| 229 return (void*) NewElem; | |
| 230 | |
| 231 Error: | |
| 232 | |
| 233 if (NewElem ->TheCurves != NULL) { | |
| 234 for (i=0; i < NewElem ->nCurves; i++) { | |
| 235 if (NewElem ->TheCurves[i]) | |
| 236 cmsFreeToneCurve(ContextID, NewElem ->TheCurves[i]); | |
| 237 } | |
| 238 } | |
| 239 _cmsFree(ContextID, NewElem ->TheCurves); | |
| 240 _cmsFree(ContextID, NewElem); | |
| 241 return NULL; | |
| 242 } | |
| 243 | |
| 244 | |
| 245 // Curves == NULL forces identity curves | |
| 246 cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]) | |
| 247 { | |
| 248 cmsUInt32Number i; | |
| 249 _cmsStageToneCurvesData* NewElem; | |
| 250 cmsStage* NewMPE; | |
| 251 | |
| 252 | |
| 253 NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCurveSetElemType, nChannels, nChannels, | |
| 254 EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL ); | |
| 255 if (NewMPE == NULL) return NULL; | |
| 256 | |
| 257 NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData)); | |
| 258 if (NewElem == NULL) { | |
| 259 cmsStageFree(ContextID, NewMPE); | |
| 260 return NULL; | |
| 261 } | |
| 262 | |
| 263 NewMPE ->Data = (void*) NewElem; | |
| 264 | |
| 265 NewElem ->nCurves = nChannels; | |
| 266 NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, nChannels, sizeof(cmsToneCurve*)); | |
| 267 if (NewElem ->TheCurves == NULL) { | |
| 268 cmsStageFree(ContextID, NewMPE); | |
| 269 return NULL; | |
| 270 } | |
| 271 | |
| 272 for (i=0; i < nChannels; i++) { | |
| 273 | |
| 274 if (Curves == NULL) { | |
| 275 NewElem ->TheCurves[i] = cmsBuildGamma(ContextID, 1.0); | |
| 276 } | |
| 277 else { | |
| 278 NewElem ->TheCurves[i] = cmsDupToneCurve(ContextID, Curves[i]); | |
| 279 } | |
| 280 | |
| 281 if (NewElem ->TheCurves[i] == NULL) { | |
| 282 cmsStageFree(ContextID, NewMPE); | |
| 283 return NULL; | |
| 284 } | |
| 285 | |
| 286 } | |
| 287 | |
| 288 return NewMPE; | |
| 289 } | |
| 290 | |
| 291 | |
| 292 // Create a bunch of identity curves | |
| 293 cmsStage* CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels) | |
| 294 { | |
| 295 cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL); | |
| 296 | |
| 297 if (mpe == NULL) return NULL; | |
| 298 mpe ->Implements = cmsSigIdentityElemType; | |
| 299 return mpe; | |
| 300 } | |
| 301 | |
| 302 | |
| 303 // ************************************************************************************************* | |
| 304 // Type cmsSigMatrixElemType (Matrices) | |
| 305 // ************************************************************************************************* | |
| 306 | |
| 307 | |
| 308 // Special care should be taken here because precision loss. A temporary cmsFloat64Number buffer is being used | |
| 309 static | |
| 310 void EvaluateMatrix(cmsContext ContextID, const cmsFloat32Number In[], | |
| 311 cmsFloat32Number Out[], | |
| 312 const cmsStage *mpe) | |
| 313 { | |
| 314 cmsUInt32Number i, j; | |
| 315 _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; | |
| 316 cmsFloat64Number Tmp; | |
| 317 cmsUNUSED_PARAMETER(ContextID); | |
| 318 | |
| 319 // Input is already in 0..1.0 notation | |
| 320 for (i=0; i < mpe ->OutputChannels; i++) { | |
| 321 | |
| 322 Tmp = 0; | |
| 323 for (j=0; j < mpe->InputChannels; j++) { | |
| 324 Tmp += In[j] * Data->Double[i*mpe->InputChannels + j]; | |
| 325 } | |
| 326 | |
| 327 if (Data ->Offset != NULL) | |
| 328 Tmp += Data->Offset[i]; | |
| 329 | |
| 330 Out[i] = (cmsFloat32Number) Tmp; | |
| 331 } | |
| 332 | |
| 333 | |
| 334 // Output in 0..1.0 domain | |
| 335 } | |
| 336 | |
| 337 | |
| 338 // Duplicate a yet-existing matrix element | |
| 339 static | |
| 340 void* MatrixElemDup(cmsContext ContextID, cmsStage* mpe) | |
| 341 { | |
| 342 _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; | |
| 343 _cmsStageMatrixData* NewElem; | |
| 344 cmsUInt32Number sz; | |
| 345 | |
| 346 NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData)); | |
| 347 if (NewElem == NULL) return NULL; | |
| 348 | |
| 349 sz = mpe ->InputChannels * mpe ->OutputChannels; | |
| 350 | |
| 351 NewElem ->Double = (cmsFloat64Number*) _cmsDupMem(ContextID, Data ->Double, sz * sizeof(cmsFloat64Number)) ; | |
| 352 | |
| 353 if (Data ->Offset) | |
| 354 NewElem ->Offset = (cmsFloat64Number*) _cmsDupMem(ContextID, | |
| 355 Data ->Offset, mpe -> OutputChannels * sizeof(cmsFloat64Number)) ; | |
| 356 | |
| 357 return (void*) NewElem; | |
| 358 } | |
| 359 | |
| 360 | |
| 361 static | |
| 362 void MatrixElemTypeFree(cmsContext ContextID, cmsStage* mpe) | |
| 363 { | |
| 364 _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; | |
| 365 if (Data == NULL) | |
| 366 return; | |
| 367 if (Data ->Double) | |
| 368 _cmsFree(ContextID, Data ->Double); | |
| 369 | |
| 370 if (Data ->Offset) | |
| 371 _cmsFree(ContextID, Data ->Offset); | |
| 372 | |
| 373 _cmsFree(ContextID, mpe ->Data); | |
| 374 } | |
| 375 | |
| 376 | |
| 377 | |
| 378 cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, | |
| 379 const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset) | |
| 380 { | |
| 381 cmsUInt32Number i, n; | |
| 382 _cmsStageMatrixData* NewElem; | |
| 383 cmsStage* NewMPE; | |
| 384 | |
| 385 n = Rows * Cols; | |
| 386 | |
| 387 // Check for overflow | |
| 388 if (n == 0) return NULL; | |
| 389 if (n >= UINT_MAX / Cols) return NULL; | |
| 390 if (n >= UINT_MAX / Rows) return NULL; | |
| 391 if (n < Rows || n < Cols) return NULL; | |
| 392 | |
| 393 NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigMatrixElemType, Cols, Rows, | |
| 394 EvaluateMatrix, MatrixElemDup, MatrixElemTypeFree, NULL ); | |
| 395 if (NewMPE == NULL) return NULL; | |
| 396 | |
| 397 | |
| 398 NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData)); | |
| 399 if (NewElem == NULL) goto Error; | |
| 400 NewMPE->Data = (void*)NewElem; | |
| 401 | |
| 402 NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number)); | |
| 403 if (NewElem->Double == NULL) goto Error; | |
| 404 | |
| 405 for (i=0; i < n; i++) { | |
| 406 NewElem ->Double[i] = Matrix[i]; | |
| 407 } | |
| 408 | |
| 409 if (Offset != NULL) { | |
| 410 | |
| 411 NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number)); | |
| 412 if (NewElem->Offset == NULL) goto Error; | |
| 413 | |
| 414 for (i=0; i < Rows; i++) { | |
| 415 NewElem ->Offset[i] = Offset[i]; | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 return NewMPE; | |
| 420 | |
| 421 Error: | |
| 422 cmsStageFree(ContextID, NewMPE); | |
| 423 return NULL; | |
| 424 } | |
| 425 | |
| 426 | |
| 427 // ************************************************************************************************* | |
| 428 // Type cmsSigCLutElemType | |
| 429 // ************************************************************************************************* | |
| 430 | |
| 431 | |
| 432 // Evaluate in true floating point | |
| 433 static | |
| 434 void EvaluateCLUTfloat(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) | |
| 435 { | |
| 436 _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; | |
| 437 | |
| 438 Data -> Params ->Interpolation.LerpFloat(ContextID, In, Out, Data->Params); | |
| 439 } | |
| 440 | |
| 441 | |
| 442 // Convert to 16 bits, evaluate, and back to floating point | |
| 443 static | |
| 444 void EvaluateCLUTfloatIn16(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) | |
| 445 { | |
| 446 _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; | |
| 447 cmsUInt16Number In16[MAX_STAGE_CHANNELS], Out16[MAX_STAGE_CHANNELS]; | |
| 448 | |
| 449 _cmsAssert(mpe ->InputChannels <= MAX_STAGE_CHANNELS); | |
| 450 _cmsAssert(mpe ->OutputChannels <= MAX_STAGE_CHANNELS); | |
| 451 | |
| 452 FromFloatTo16(In, In16, mpe ->InputChannels); | |
| 453 Data -> Params ->Interpolation.Lerp16(ContextID, In16, Out16, Data->Params); | |
| 454 From16ToFloat(Out16, Out, mpe ->OutputChannels); | |
| 455 } | |
| 456 | |
| 457 | |
| 458 // Given an hypercube of b dimensions, with Dims[] number of nodes by dimension, calculate the total amount of nodes | |
| 459 static | |
| 460 cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b) | |
| 461 { | |
| 462 cmsUInt32Number rv, dim; | |
| 463 | |
| 464 _cmsAssert(Dims != NULL); | |
| 465 | |
| 466 for (rv = 1; b > 0; b--) { | |
| 467 | |
| 468 dim = Dims[b-1]; | |
| 469 if (dim <= 1) return 0; // Error | |
| 470 | |
| 471 rv *= dim; | |
| 472 | |
| 473 // Check for overflow | |
| 474 if (rv > UINT_MAX / dim) return 0; | |
| 475 } | |
| 476 | |
| 477 // Again, prevent overflow | |
| 478 if (rv > UINT_MAX / 15) return 0; | |
| 479 | |
| 480 return rv; | |
| 481 } | |
| 482 | |
| 483 static | |
| 484 void* CLUTElemDup(cmsContext ContextID, cmsStage* mpe) | |
| 485 { | |
| 486 _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; | |
| 487 _cmsStageCLutData* NewElem; | |
| 488 | |
| 489 | |
| 490 NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); | |
| 491 if (NewElem == NULL) return NULL; | |
| 492 | |
| 493 NewElem ->nEntries = Data ->nEntries; | |
| 494 NewElem ->HasFloatValues = Data ->HasFloatValues; | |
| 495 | |
| 496 if (Data ->Tab.T) { | |
| 497 | |
| 498 if (Data ->HasFloatValues) { | |
| 499 NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number)); | |
| 500 if (NewElem ->Tab.TFloat == NULL) | |
| 501 goto Error; | |
| 502 } else { | |
| 503 NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number)); | |
| 504 if (NewElem ->Tab.T == NULL) | |
| 505 goto Error; | |
| 506 } | |
| 507 } | |
| 508 | |
| 509 NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, | |
| 510 Data ->Params ->nSamples, | |
| 511 Data ->Params ->nInputs, | |
| 512 Data ->Params ->nOutputs, | |
| 513 NewElem ->Tab.T, | |
| 514 Data ->Params ->dwFlags); | |
| 515 if (NewElem->Params != NULL) | |
| 516 return (void*) NewElem; | |
| 517 Error: | |
| 518 if (NewElem->Tab.T) | |
| 519 // This works for both types | |
| 520 _cmsFree(ContextID, NewElem -> Tab.T); | |
| 521 _cmsFree(ContextID, NewElem); | |
| 522 return NULL; | |
| 523 } | |
| 524 | |
| 525 | |
| 526 static | |
| 527 void CLutElemTypeFree(cmsContext ContextID, cmsStage* mpe) | |
| 528 { | |
| 529 | |
| 530 _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; | |
| 531 | |
| 532 // Already empty | |
| 533 if (Data == NULL) return; | |
| 534 | |
| 535 // This works for both types | |
| 536 if (Data -> Tab.T) | |
| 537 _cmsFree(ContextID, Data -> Tab.T); | |
| 538 | |
| 539 _cmsFreeInterpParams(ContextID, Data ->Params); | |
| 540 _cmsFree(ContextID, mpe ->Data); | |
| 541 } | |
| 542 | |
| 543 | |
| 544 // Allocates a 16-bit multidimensional CLUT. This is evaluated at 16-bit precision. Table may have different | |
| 545 // granularity on each dimension. | |
| 546 cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, | |
| 547 const cmsUInt32Number clutPoints[], | |
| 548 cmsUInt32Number inputChan, | |
| 549 cmsUInt32Number outputChan, | |
| 550 const cmsUInt16Number* Table) | |
| 551 { | |
| 552 cmsUInt32Number i, n; | |
| 553 _cmsStageCLutData* NewElem; | |
| 554 cmsStage* NewMPE; | |
| 555 | |
| 556 _cmsAssert(clutPoints != NULL); | |
| 557 | |
| 558 if (inputChan > MAX_INPUT_DIMENSIONS) { | |
| 559 cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); | |
| 560 return NULL; | |
| 561 } | |
| 562 | |
| 563 NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, | |
| 564 EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL ); | |
| 565 | |
| 566 if (NewMPE == NULL) return NULL; | |
| 567 | |
| 568 NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); | |
| 569 if (NewElem == NULL) { | |
| 570 cmsStageFree(ContextID, NewMPE); | |
| 571 return NULL; | |
| 572 } | |
| 573 | |
| 574 NewMPE ->Data = (void*) NewElem; | |
| 575 | |
| 576 NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); | |
| 577 NewElem -> HasFloatValues = FALSE; | |
| 578 | |
| 579 if (n == 0) { | |
| 580 cmsStageFree(ContextID, NewMPE); | |
| 581 return NULL; | |
| 582 } | |
| 583 | |
| 584 | |
| 585 NewElem ->Tab.T = (cmsUInt16Number*) _cmsCalloc(ContextID, n, sizeof(cmsUInt16Number)); | |
| 586 if (NewElem ->Tab.T == NULL) { | |
| 587 cmsStageFree(ContextID, NewMPE); | |
| 588 return NULL; | |
| 589 } | |
| 590 | |
| 591 if (Table != NULL) { | |
| 592 for (i=0; i < n; i++) { | |
| 593 NewElem ->Tab.T[i] = Table[i]; | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.T, CMS_LERP_FLAGS_16BITS); | |
| 598 if (NewElem ->Params == NULL) { | |
| 599 cmsStageFree(ContextID, NewMPE); | |
| 600 return NULL; | |
| 601 } | |
| 602 | |
| 603 return NewMPE; | |
| 604 } | |
| 605 | |
| 606 cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, | |
| 607 cmsUInt32Number nGridPoints, | |
| 608 cmsUInt32Number inputChan, | |
| 609 cmsUInt32Number outputChan, | |
| 610 const cmsUInt16Number* Table) | |
| 611 { | |
| 612 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; | |
| 613 int i; | |
| 614 | |
| 615 // Our resulting LUT would be same gridpoints on all dimensions | |
| 616 for (i=0; i < MAX_INPUT_DIMENSIONS; i++) | |
| 617 Dimensions[i] = nGridPoints; | |
| 618 | |
| 619 return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table); | |
| 620 } | |
| 621 | |
| 622 | |
| 623 cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, | |
| 624 cmsUInt32Number nGridPoints, | |
| 625 cmsUInt32Number inputChan, | |
| 626 cmsUInt32Number outputChan, | |
| 627 const cmsFloat32Number* Table) | |
| 628 { | |
| 629 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; | |
| 630 int i; | |
| 631 | |
| 632 // Our resulting LUT would be same gridpoints on all dimensions | |
| 633 for (i=0; i < MAX_INPUT_DIMENSIONS; i++) | |
| 634 Dimensions[i] = nGridPoints; | |
| 635 | |
| 636 return cmsStageAllocCLutFloatGranular(ContextID, Dimensions, inputChan, outputChan, Table); | |
| 637 } | |
| 638 | |
| 639 | |
| 640 | |
| 641 cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table) | |
| 642 { | |
| 643 cmsUInt32Number i, n; | |
| 644 _cmsStageCLutData* NewElem; | |
| 645 cmsStage* NewMPE; | |
| 646 | |
| 647 _cmsAssert(clutPoints != NULL); | |
| 648 | |
| 649 if (inputChan > MAX_INPUT_DIMENSIONS) { | |
| 650 cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); | |
| 651 return NULL; | |
| 652 } | |
| 653 | |
| 654 NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, | |
| 655 EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL); | |
| 656 if (NewMPE == NULL) return NULL; | |
| 657 | |
| 658 | |
| 659 NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); | |
| 660 if (NewElem == NULL) { | |
| 661 cmsStageFree(ContextID, NewMPE); | |
| 662 return NULL; | |
| 663 } | |
| 664 | |
| 665 NewMPE ->Data = (void*) NewElem; | |
| 666 | |
| 667 // There is a potential integer overflow on conputing n and nEntries. | |
| 668 NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); | |
| 669 NewElem -> HasFloatValues = TRUE; | |
| 670 | |
| 671 if (n == 0) { | |
| 672 cmsStageFree(ContextID, NewMPE); | |
| 673 return NULL; | |
| 674 } | |
| 675 | |
| 676 NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat32Number)); | |
| 677 if (NewElem ->Tab.TFloat == NULL) { | |
| 678 cmsStageFree(ContextID, NewMPE); | |
| 679 return NULL; | |
| 680 } | |
| 681 | |
| 682 if (Table != NULL) { | |
| 683 for (i=0; i < n; i++) { | |
| 684 NewElem ->Tab.TFloat[i] = Table[i]; | |
| 685 } | |
| 686 } | |
| 687 | |
| 688 NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT); | |
| 689 if (NewElem ->Params == NULL) { | |
| 690 cmsStageFree(ContextID, NewMPE); | |
| 691 return NULL; | |
| 692 } | |
| 693 | |
| 694 return NewMPE; | |
| 695 } | |
| 696 | |
| 697 | |
| 698 static | |
| 699 int IdentitySampler(cmsContext ContextID, CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void * Cargo) | |
| 700 { | |
| 701 int nChan = *(int*) Cargo; | |
| 702 int i; | |
| 703 cmsUNUSED_PARAMETER(ContextID); | |
| 704 | |
| 705 for (i=0; i < nChan; i++) | |
| 706 Out[i] = In[i]; | |
| 707 | |
| 708 return 1; | |
| 709 } | |
| 710 | |
| 711 // Creates an MPE that just copies input to output | |
| 712 cmsStage* CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan) | |
| 713 { | |
| 714 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; | |
| 715 cmsStage* mpe ; | |
| 716 int i; | |
| 717 | |
| 718 for (i=0; i < MAX_INPUT_DIMENSIONS; i++) | |
| 719 Dimensions[i] = 2; | |
| 720 | |
| 721 mpe = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, nChan, nChan, NULL); | |
| 722 if (mpe == NULL) return NULL; | |
| 723 | |
| 724 if (!cmsStageSampleCLut16bit(ContextID, mpe, IdentitySampler, &nChan, 0)) { | |
| 725 cmsStageFree(ContextID, mpe); | |
| 726 return NULL; | |
| 727 } | |
| 728 | |
| 729 mpe ->Implements = cmsSigIdentityElemType; | |
| 730 return mpe; | |
| 731 } | |
| 732 | |
| 733 | |
| 734 | |
| 735 // Quantize a value 0 <= i < MaxSamples to 0..0xffff | |
| 736 cmsUInt16Number CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples) | |
| 737 { | |
| 738 cmsFloat64Number x; | |
| 739 | |
| 740 x = ((cmsFloat64Number) i * 65535.) / (cmsFloat64Number) (MaxSamples - 1); | |
| 741 return _cmsQuickSaturateWord(x); | |
| 742 } | |
| 743 | |
| 744 | |
| 745 // This routine does a sweep on whole input space, and calls its callback | |
| 746 // function on knots. returns TRUE if all ok, FALSE otherwise. | |
| 747 cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsContext ContextID, cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags) | |
| 748 { | |
| 749 int i, t, index, rest; | |
| 750 cmsUInt32Number nTotalPoints; | |
| 751 cmsUInt32Number nInputs, nOutputs; | |
| 752 cmsUInt32Number* nSamples; | |
| 753 cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; | |
| 754 _cmsStageCLutData* clut; | |
| 755 | |
| 756 if (mpe == NULL) return FALSE; | |
| 757 | |
| 758 clut = (_cmsStageCLutData*) mpe->Data; | |
| 759 | |
| 760 if (clut == NULL) return FALSE; | |
| 761 | |
| 762 nSamples = clut->Params ->nSamples; | |
| 763 nInputs = clut->Params ->nInputs; | |
| 764 nOutputs = clut->Params ->nOutputs; | |
| 765 | |
| 766 if (nInputs <= 0) return FALSE; | |
| 767 if (nOutputs <= 0) return FALSE; | |
| 768 if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; | |
| 769 if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; | |
| 770 | |
| 771 memset(In, 0, sizeof(In)); | |
| 772 memset(Out, 0, sizeof(Out)); | |
| 773 | |
| 774 nTotalPoints = CubeSize(nSamples, nInputs); | |
| 775 if (nTotalPoints == 0) return FALSE; | |
| 776 | |
| 777 index = 0; | |
| 778 for (i = 0; i < (int) nTotalPoints; i++) { | |
| 779 | |
| 780 rest = i; | |
| 781 for (t = (int)nInputs - 1; t >= 0; --t) { | |
| 782 | |
| 783 cmsUInt32Number Colorant = rest % nSamples[t]; | |
| 784 | |
| 785 rest /= nSamples[t]; | |
| 786 | |
| 787 In[t] = _cmsQuantizeVal(Colorant, nSamples[t]); | |
| 788 } | |
| 789 | |
| 790 if (clut ->Tab.T != NULL) { | |
| 791 for (t = 0; t < (int)nOutputs; t++) | |
| 792 Out[t] = clut->Tab.T[index + t]; | |
| 793 } | |
| 794 | |
| 795 if (!Sampler(ContextID, In, Out, Cargo)) | |
| 796 return FALSE; | |
| 797 | |
| 798 if (!(dwFlags & SAMPLER_INSPECT)) { | |
| 799 | |
| 800 if (clut ->Tab.T != NULL) { | |
| 801 for (t=0; t < (int) nOutputs; t++) | |
| 802 clut->Tab.T[index + t] = Out[t]; | |
| 803 } | |
| 804 } | |
| 805 | |
| 806 index += nOutputs; | |
| 807 } | |
| 808 | |
| 809 return TRUE; | |
| 810 } | |
| 811 | |
| 812 // Same as anterior, but for floating point | |
| 813 cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsContext ContextID, cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags) | |
| 814 { | |
| 815 int i, t, index, rest; | |
| 816 cmsUInt32Number nTotalPoints; | |
| 817 cmsUInt32Number nInputs, nOutputs; | |
| 818 cmsUInt32Number* nSamples; | |
| 819 cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; | |
| 820 _cmsStageCLutData* clut; | |
| 821 | |
| 822 if (mpe == NULL) return FALSE; | |
| 823 | |
| 824 clut = (_cmsStageCLutData*)mpe->Data; | |
| 825 | |
| 826 if (clut == NULL) return FALSE; | |
| 827 | |
| 828 nSamples = clut->Params ->nSamples; | |
| 829 nInputs = clut->Params ->nInputs; | |
| 830 nOutputs = clut->Params ->nOutputs; | |
| 831 | |
| 832 if (nInputs <= 0) return FALSE; | |
| 833 if (nOutputs <= 0) return FALSE; | |
| 834 if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; | |
| 835 if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; | |
| 836 | |
| 837 nTotalPoints = CubeSize(nSamples, nInputs); | |
| 838 if (nTotalPoints == 0) return FALSE; | |
| 839 | |
| 840 index = 0; | |
| 841 for (i = 0; i < (int)nTotalPoints; i++) { | |
| 842 | |
| 843 rest = i; | |
| 844 for (t = (int) nInputs-1; t >=0; --t) { | |
| 845 | |
| 846 cmsUInt32Number Colorant = rest % nSamples[t]; | |
| 847 | |
| 848 rest /= nSamples[t]; | |
| 849 | |
| 850 In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, nSamples[t]) / 65535.0); | |
| 851 } | |
| 852 | |
| 853 if (clut ->Tab.TFloat != NULL) { | |
| 854 for (t=0; t < (int) nOutputs; t++) | |
| 855 Out[t] = clut->Tab.TFloat[index + t]; | |
| 856 } | |
| 857 | |
| 858 if (!Sampler(ContextID, In, Out, Cargo)) | |
| 859 return FALSE; | |
| 860 | |
| 861 if (!(dwFlags & SAMPLER_INSPECT)) { | |
| 862 | |
| 863 if (clut ->Tab.TFloat != NULL) { | |
| 864 for (t=0; t < (int) nOutputs; t++) | |
| 865 clut->Tab.TFloat[index + t] = Out[t]; | |
| 866 } | |
| 867 } | |
| 868 | |
| 869 index += nOutputs; | |
| 870 } | |
| 871 | |
| 872 return TRUE; | |
| 873 } | |
| 874 | |
| 875 | |
| 876 | |
| 877 // This routine does a sweep on whole input space, and calls its callback | |
| 878 // function on knots. returns TRUE if all ok, FALSE otherwise. | |
| 879 cmsBool CMSEXPORT cmsSliceSpace16(cmsContext ContextID, cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], | |
| 880 cmsSAMPLER16 Sampler, void * Cargo) | |
| 881 { | |
| 882 int i, t, rest; | |
| 883 cmsUInt32Number nTotalPoints; | |
| 884 cmsUInt16Number In[cmsMAXCHANNELS]; | |
| 885 | |
| 886 if (nInputs >= cmsMAXCHANNELS) return FALSE; | |
| 887 | |
| 888 nTotalPoints = CubeSize(clutPoints, nInputs); | |
| 889 if (nTotalPoints == 0) return FALSE; | |
| 890 | |
| 891 for (i = 0; i < (int) nTotalPoints; i++) { | |
| 892 | |
| 893 rest = i; | |
| 894 for (t = (int) nInputs-1; t >=0; --t) { | |
| 895 | |
| 896 cmsUInt32Number Colorant = rest % clutPoints[t]; | |
| 897 | |
| 898 rest /= clutPoints[t]; | |
| 899 In[t] = _cmsQuantizeVal(Colorant, clutPoints[t]); | |
| 900 | |
| 901 } | |
| 902 | |
| 903 if (!Sampler(ContextID, In, NULL, Cargo)) | |
| 904 return FALSE; | |
| 905 } | |
| 906 | |
| 907 return TRUE; | |
| 908 } | |
| 909 | |
| 910 cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsContext ContextID, cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], | |
| 911 cmsSAMPLERFLOAT Sampler, void * Cargo) | |
| 912 { | |
| 913 int i, t, rest; | |
| 914 cmsUInt32Number nTotalPoints; | |
| 915 cmsFloat32Number In[cmsMAXCHANNELS]; | |
| 916 | |
| 917 if (nInputs >= cmsMAXCHANNELS) return FALSE; | |
| 918 | |
| 919 nTotalPoints = CubeSize(clutPoints, nInputs); | |
| 920 if (nTotalPoints == 0) return FALSE; | |
| 921 | |
| 922 for (i = 0; i < (int) nTotalPoints; i++) { | |
| 923 | |
| 924 rest = i; | |
| 925 for (t = (int) nInputs-1; t >=0; --t) { | |
| 926 | |
| 927 cmsUInt32Number Colorant = rest % clutPoints[t]; | |
| 928 | |
| 929 rest /= clutPoints[t]; | |
| 930 In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, clutPoints[t]) / 65535.0); | |
| 931 | |
| 932 } | |
| 933 | |
| 934 if (!Sampler(ContextID, In, NULL, Cargo)) | |
| 935 return FALSE; | |
| 936 } | |
| 937 | |
| 938 return TRUE; | |
| 939 } | |
| 940 | |
| 941 // ******************************************************************************** | |
| 942 // Type cmsSigLab2XYZElemType | |
| 943 // ******************************************************************************** | |
| 944 | |
| 945 | |
| 946 static | |
| 947 void EvaluateLab2XYZ(cmsContext ContextID, const cmsFloat32Number In[], | |
| 948 cmsFloat32Number Out[], | |
| 949 const cmsStage *mpe) | |
| 950 { | |
| 951 cmsCIELab Lab; | |
| 952 cmsCIEXYZ XYZ; | |
| 953 const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; | |
| 954 | |
| 955 // V4 rules | |
| 956 Lab.L = In[0] * 100.0; | |
| 957 Lab.a = In[1] * 255.0 - 128.0; | |
| 958 Lab.b = In[2] * 255.0 - 128.0; | |
| 959 | |
| 960 cmsLab2XYZ(ContextID, NULL, &XYZ, &Lab); | |
| 961 | |
| 962 // From XYZ, range 0..19997 to 0..1.0, note that 1.99997 comes from 0xffff | |
| 963 // encoded as 1.15 fixed point, so 1 + (32767.0 / 32768.0) | |
| 964 | |
| 965 Out[0] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.X / XYZadj); | |
| 966 Out[1] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Y / XYZadj); | |
| 967 Out[2] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Z / XYZadj); | |
| 968 return; | |
| 969 | |
| 970 cmsUNUSED_PARAMETER(mpe); | |
| 971 } | |
| 972 | |
| 973 | |
| 974 // No dup or free routines needed, as the structure has no pointers in it. | |
| 975 cmsStage* CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID) | |
| 976 { | |
| 977 return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL); | |
| 978 } | |
| 979 | |
| 980 // ******************************************************************************** | |
| 981 | |
| 982 // v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable | |
| 983 // number of gridpoints that would make exact match. However, a prelinearization | |
| 984 // of 258 entries, would map 0xFF00 exactly on entry 257, and this is good to avoid scum dot. | |
| 985 // Almost all what we need but unfortunately, the rest of entries should be scaled by | |
| 986 // (255*257/256) and this is not exact. | |
| 987 | |
| 988 cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID) | |
| 989 { | |
| 990 cmsStage* mpe; | |
| 991 cmsToneCurve* LabTable[3]; | |
| 992 int i, j; | |
| 993 | |
| 994 LabTable[0] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); | |
| 995 LabTable[1] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); | |
| 996 LabTable[2] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); | |
| 997 | |
| 998 for (j=0; j < 3; j++) { | |
| 999 | |
| 1000 if (LabTable[j] == NULL) { | |
| 1001 cmsFreeToneCurveTriple(ContextID, LabTable); | |
| 1002 return NULL; | |
| 1003 } | |
| 1004 | |
| 1005 // We need to map * (0xffff / 0xff00), that's same as (257 / 256) | |
| 1006 // So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256); | |
| 1007 for (i=0; i < 257; i++) { | |
| 1008 | |
| 1009 LabTable[j]->Table16[i] = (cmsUInt16Number) ((i * 0xffff + 0x80) >> 8); | |
| 1010 } | |
| 1011 | |
| 1012 LabTable[j] ->Table16[257] = 0xffff; | |
| 1013 } | |
| 1014 | |
| 1015 mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable); | |
| 1016 cmsFreeToneCurveTriple(ContextID, LabTable); | |
| 1017 | |
| 1018 if (mpe == NULL) return NULL; | |
| 1019 mpe ->Implements = cmsSigLabV2toV4; | |
| 1020 return mpe; | |
| 1021 } | |
| 1022 | |
| 1023 // ******************************************************************************** | |
| 1024 | |
| 1025 // Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles | |
| 1026 cmsStage* CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID) | |
| 1027 { | |
| 1028 static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0, | |
| 1029 0, 65535.0/65280.0, 0, | |
| 1030 0, 0, 65535.0/65280.0 | |
| 1031 }; | |
| 1032 | |
| 1033 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V2ToV4, NULL); | |
| 1034 | |
| 1035 if (mpe == NULL) return mpe; | |
| 1036 mpe ->Implements = cmsSigLabV2toV4; | |
| 1037 return mpe; | |
| 1038 } | |
| 1039 | |
| 1040 | |
| 1041 // Reverse direction | |
| 1042 cmsStage* CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID) | |
| 1043 { | |
| 1044 static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0, | |
| 1045 0, 65280.0/65535.0, 0, | |
| 1046 0, 0, 65280.0/65535.0 | |
| 1047 }; | |
| 1048 | |
| 1049 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V4ToV2, NULL); | |
| 1050 | |
| 1051 if (mpe == NULL) return mpe; | |
| 1052 mpe ->Implements = cmsSigLabV4toV2; | |
| 1053 return mpe; | |
| 1054 } | |
| 1055 | |
| 1056 | |
| 1057 // To Lab to float. Note that the MPE gives numbers in normal Lab range | |
| 1058 // and we need 0..1.0 range for the formatters | |
| 1059 // L* : 0...100 => 0...1.0 (L* / 100) | |
| 1060 // ab* : -128..+127 to 0..1 ((ab* + 128) / 255) | |
| 1061 | |
| 1062 cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID) | |
| 1063 { | |
| 1064 static const cmsFloat64Number a1[] = { | |
| 1065 1.0/100.0, 0, 0, | |
| 1066 0, 1.0/255.0, 0, | |
| 1067 0, 0, 1.0/255.0 | |
| 1068 }; | |
| 1069 | |
| 1070 static const cmsFloat64Number o1[] = { | |
| 1071 0, | |
| 1072 128.0/255.0, | |
| 1073 128.0/255.0 | |
| 1074 }; | |
| 1075 | |
| 1076 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); | |
| 1077 | |
| 1078 if (mpe == NULL) return mpe; | |
| 1079 mpe ->Implements = cmsSigLab2FloatPCS; | |
| 1080 return mpe; | |
| 1081 } | |
| 1082 | |
| 1083 // Fom XYZ to floating point PCS | |
| 1084 cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID) | |
| 1085 { | |
| 1086 #define n (32768.0/65535.0) | |
| 1087 static const cmsFloat64Number a1[] = { | |
| 1088 n, 0, 0, | |
| 1089 0, n, 0, | |
| 1090 0, 0, n | |
| 1091 }; | |
| 1092 #undef n | |
| 1093 | |
| 1094 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); | |
| 1095 | |
| 1096 if (mpe == NULL) return mpe; | |
| 1097 mpe ->Implements = cmsSigXYZ2FloatPCS; | |
| 1098 return mpe; | |
| 1099 } | |
| 1100 | |
| 1101 cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID) | |
| 1102 { | |
| 1103 static const cmsFloat64Number a1[] = { | |
| 1104 100.0, 0, 0, | |
| 1105 0, 255.0, 0, | |
| 1106 0, 0, 255.0 | |
| 1107 }; | |
| 1108 | |
| 1109 static const cmsFloat64Number o1[] = { | |
| 1110 0, | |
| 1111 -128.0, | |
| 1112 -128.0 | |
| 1113 }; | |
| 1114 | |
| 1115 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); | |
| 1116 if (mpe == NULL) return mpe; | |
| 1117 mpe ->Implements = cmsSigFloatPCS2Lab; | |
| 1118 return mpe; | |
| 1119 } | |
| 1120 | |
| 1121 cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID) | |
| 1122 { | |
| 1123 #define n (65535.0/32768.0) | |
| 1124 | |
| 1125 static const cmsFloat64Number a1[] = { | |
| 1126 n, 0, 0, | |
| 1127 0, n, 0, | |
| 1128 0, 0, n | |
| 1129 }; | |
| 1130 #undef n | |
| 1131 | |
| 1132 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); | |
| 1133 if (mpe == NULL) return mpe; | |
| 1134 mpe ->Implements = cmsSigFloatPCS2XYZ; | |
| 1135 return mpe; | |
| 1136 } | |
| 1137 | |
| 1138 // Clips values smaller than zero | |
| 1139 static | |
| 1140 void Clipper(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) | |
| 1141 { | |
| 1142 cmsUInt32Number i; | |
| 1143 cmsUNUSED_PARAMETER(ContextID); | |
| 1144 for (i = 0; i < mpe->InputChannels; i++) { | |
| 1145 | |
| 1146 cmsFloat32Number n = In[i]; | |
| 1147 Out[i] = n < 0 ? 0 : n; | |
| 1148 } | |
| 1149 } | |
| 1150 | |
| 1151 cmsStage* _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels) | |
| 1152 { | |
| 1153 return _cmsStageAllocPlaceholder(ContextID, cmsSigClipNegativesElemType, | |
| 1154 nChannels, nChannels, Clipper, NULL, NULL, NULL); | |
| 1155 } | |
| 1156 | |
| 1157 // ******************************************************************************** | |
| 1158 // Type cmsSigXYZ2LabElemType | |
| 1159 // ******************************************************************************** | |
| 1160 | |
| 1161 static | |
| 1162 void EvaluateXYZ2Lab(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) | |
| 1163 { | |
| 1164 cmsCIELab Lab; | |
| 1165 cmsCIEXYZ XYZ; | |
| 1166 const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; | |
| 1167 | |
| 1168 // From 0..1.0 to XYZ | |
| 1169 | |
| 1170 XYZ.X = In[0] * XYZadj; | |
| 1171 XYZ.Y = In[1] * XYZadj; | |
| 1172 XYZ.Z = In[2] * XYZadj; | |
| 1173 | |
| 1174 cmsXYZ2Lab(ContextID, NULL, &Lab, &XYZ); | |
| 1175 | |
| 1176 // From V4 Lab to 0..1.0 | |
| 1177 | |
| 1178 Out[0] = (cmsFloat32Number) (Lab.L / 100.0); | |
| 1179 Out[1] = (cmsFloat32Number) ((Lab.a + 128.0) / 255.0); | |
| 1180 Out[2] = (cmsFloat32Number) ((Lab.b + 128.0) / 255.0); | |
| 1181 return; | |
| 1182 | |
| 1183 cmsUNUSED_PARAMETER(mpe); | |
| 1184 } | |
| 1185 | |
| 1186 cmsStage* CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID) | |
| 1187 { | |
| 1188 return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL); | |
| 1189 | |
| 1190 } | |
| 1191 | |
| 1192 // ******************************************************************************** | |
| 1193 | |
| 1194 // For v4, S-Shaped curves are placed in a/b axis to increase resolution near gray | |
| 1195 | |
| 1196 cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID) | |
| 1197 { | |
| 1198 cmsToneCurve* LabTable[3]; | |
| 1199 cmsFloat64Number Params[1] = {2.4} ; | |
| 1200 | |
| 1201 LabTable[0] = cmsBuildGamma(ContextID, 1.0); | |
| 1202 LabTable[1] = cmsBuildParametricToneCurve(ContextID, 108, Params); | |
| 1203 LabTable[2] = cmsBuildParametricToneCurve(ContextID, 108, Params); | |
| 1204 | |
| 1205 return cmsStageAllocToneCurves(ContextID, 3, LabTable); | |
| 1206 } | |
| 1207 | |
| 1208 | |
| 1209 // Free a single MPE | |
| 1210 void CMSEXPORT cmsStageFree(cmsContext ContextID, cmsStage* mpe) | |
| 1211 { | |
| 1212 if (mpe ->FreePtr) | |
| 1213 mpe ->FreePtr(ContextID, mpe); | |
| 1214 | |
| 1215 _cmsFree(ContextID, mpe); | |
| 1216 } | |
| 1217 | |
| 1218 | |
| 1219 cmsUInt32Number CMSEXPORT cmsStageInputChannels(cmsContext ContextID, const cmsStage* mpe) | |
| 1220 { | |
| 1221 cmsUNUSED_PARAMETER(ContextID); | |
| 1222 return mpe ->InputChannels; | |
| 1223 } | |
| 1224 | |
| 1225 cmsUInt32Number CMSEXPORT cmsStageOutputChannels(cmsContext ContextID, const cmsStage* mpe) | |
| 1226 { | |
| 1227 cmsUNUSED_PARAMETER(ContextID); | |
| 1228 return mpe ->OutputChannels; | |
| 1229 } | |
| 1230 | |
| 1231 cmsStageSignature CMSEXPORT cmsStageType(cmsContext ContextID, const cmsStage* mpe) | |
| 1232 { | |
| 1233 cmsUNUSED_PARAMETER(ContextID); | |
| 1234 return mpe -> Type; | |
| 1235 } | |
| 1236 | |
| 1237 void* CMSEXPORT cmsStageData(cmsContext ContextID, const cmsStage* mpe) | |
| 1238 { | |
| 1239 cmsUNUSED_PARAMETER(ContextID); | |
| 1240 return mpe -> Data; | |
| 1241 } | |
| 1242 | |
| 1243 cmsStage* CMSEXPORT cmsStageNext(cmsContext ContextID, const cmsStage* mpe) | |
| 1244 { | |
| 1245 cmsUNUSED_PARAMETER(ContextID); | |
| 1246 return mpe -> Next; | |
| 1247 } | |
| 1248 | |
| 1249 | |
| 1250 // Duplicates an MPE | |
| 1251 cmsStage* CMSEXPORT cmsStageDup(cmsContext ContextID, cmsStage* mpe) | |
| 1252 { | |
| 1253 cmsStage* NewMPE; | |
| 1254 | |
| 1255 if (mpe == NULL) return NULL; | |
| 1256 NewMPE = _cmsStageAllocPlaceholder(ContextID, | |
| 1257 mpe ->Type, | |
| 1258 mpe ->InputChannels, | |
| 1259 mpe ->OutputChannels, | |
| 1260 mpe ->EvalPtr, | |
| 1261 mpe ->DupElemPtr, | |
| 1262 mpe ->FreePtr, | |
| 1263 NULL); | |
| 1264 if (NewMPE == NULL) return NULL; | |
| 1265 | |
| 1266 NewMPE ->Implements = mpe ->Implements; | |
| 1267 | |
| 1268 if (mpe ->DupElemPtr) { | |
| 1269 | |
| 1270 NewMPE ->Data = mpe ->DupElemPtr(ContextID, mpe); | |
| 1271 | |
| 1272 if (NewMPE->Data == NULL) { | |
| 1273 | |
| 1274 cmsStageFree(ContextID, NewMPE); | |
| 1275 return NULL; | |
| 1276 } | |
| 1277 | |
| 1278 } else { | |
| 1279 | |
| 1280 NewMPE ->Data = NULL; | |
| 1281 } | |
| 1282 | |
| 1283 return NewMPE; | |
| 1284 } | |
| 1285 | |
| 1286 | |
| 1287 // *********************************************************************************************************** | |
| 1288 | |
| 1289 // This function sets up the channel count | |
| 1290 static | |
| 1291 cmsBool BlessLUT(cmsContext ContextID, cmsPipeline* lut) | |
| 1292 { | |
| 1293 // We can set the input/output channels only if we have elements. | |
| 1294 if (lut ->Elements != NULL) { | |
| 1295 | |
| 1296 cmsStage* prev; | |
| 1297 cmsStage* next; | |
| 1298 cmsStage* First; | |
| 1299 cmsStage* Last; | |
| 1300 | |
| 1301 First = cmsPipelineGetPtrToFirstStage(ContextID, lut); | |
| 1302 Last = cmsPipelineGetPtrToLastStage(ContextID, lut); | |
| 1303 | |
| 1304 if (First == NULL || Last == NULL) return FALSE; | |
| 1305 | |
| 1306 lut->InputChannels = First->InputChannels; | |
| 1307 lut->OutputChannels = Last->OutputChannels; | |
| 1308 | |
| 1309 // Check chain consistency | |
| 1310 prev = First; | |
| 1311 next = prev->Next; | |
| 1312 | |
| 1313 while (next != NULL) | |
| 1314 { | |
| 1315 if (next->InputChannels != prev->OutputChannels) | |
| 1316 return FALSE; | |
| 1317 | |
| 1318 next = next->Next; | |
| 1319 prev = prev->Next; | |
| 1320 } | |
| 1321 } | |
| 1322 | |
| 1323 return TRUE; | |
| 1324 } | |
| 1325 | |
| 1326 | |
| 1327 // Default to evaluate the LUT on 16 bit-basis. Precision is retained. | |
| 1328 static | |
| 1329 void _LUTeval16(cmsContext ContextID, CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER const void* D) | |
| 1330 { | |
| 1331 cmsPipeline* lut = (cmsPipeline*) D; | |
| 1332 cmsStage *mpe; | |
| 1333 cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS]; | |
| 1334 int Phase = 0, NextPhase; | |
| 1335 | |
| 1336 From16ToFloat(In, &Storage[Phase][0], lut ->InputChannels); | |
| 1337 | |
| 1338 for (mpe = lut ->Elements; | |
| 1339 mpe != NULL; | |
| 1340 mpe = mpe ->Next) { | |
| 1341 | |
| 1342 NextPhase = Phase ^ 1; | |
| 1343 mpe ->EvalPtr(ContextID, &Storage[Phase][0], &Storage[NextPhase][0], mpe); | |
| 1344 Phase = NextPhase; | |
| 1345 } | |
| 1346 | |
| 1347 | |
| 1348 FromFloatTo16(&Storage[Phase][0], Out, lut ->OutputChannels); | |
| 1349 } | |
| 1350 | |
| 1351 | |
| 1352 | |
| 1353 // Does evaluate the LUT on cmsFloat32Number-basis. | |
| 1354 static | |
| 1355 void _LUTevalFloat(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const void* D) | |
| 1356 { | |
| 1357 cmsPipeline* lut = (cmsPipeline*) D; | |
| 1358 cmsStage *mpe; | |
| 1359 cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS]; | |
| 1360 int Phase = 0, NextPhase; | |
| 1361 | |
| 1362 memmove(&Storage[Phase][0], In, lut ->InputChannels * sizeof(cmsFloat32Number)); | |
| 1363 | |
| 1364 for (mpe = lut ->Elements; | |
| 1365 mpe != NULL; | |
| 1366 mpe = mpe ->Next) { | |
| 1367 | |
| 1368 NextPhase = Phase ^ 1; | |
| 1369 mpe ->EvalPtr(ContextID, &Storage[Phase][0], &Storage[NextPhase][0], mpe); | |
| 1370 Phase = NextPhase; | |
| 1371 } | |
| 1372 | |
| 1373 memmove(Out, &Storage[Phase][0], lut ->OutputChannels * sizeof(cmsFloat32Number)); | |
| 1374 } | |
| 1375 | |
| 1376 | |
| 1377 // LUT Creation & Destruction | |
| 1378 cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels) | |
| 1379 { | |
| 1380 cmsPipeline* NewLUT; | |
| 1381 | |
| 1382 // A value of zero in channels is allowed as placeholder | |
| 1383 if (InputChannels >= cmsMAXCHANNELS || | |
| 1384 OutputChannels >= cmsMAXCHANNELS) return NULL; | |
| 1385 | |
| 1386 NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline)); | |
| 1387 if (NewLUT == NULL) return NULL; | |
| 1388 | |
| 1389 NewLUT -> InputChannels = InputChannels; | |
| 1390 NewLUT -> OutputChannels = OutputChannels; | |
| 1391 | |
| 1392 NewLUT ->Eval16Fn = _LUTeval16; | |
| 1393 NewLUT ->EvalFloatFn = _LUTevalFloat; | |
| 1394 NewLUT ->DupDataFn = NULL; | |
| 1395 NewLUT ->FreeDataFn = NULL; | |
| 1396 NewLUT ->Data = NewLUT; | |
| 1397 | |
| 1398 if (!BlessLUT(ContextID, NewLUT)) | |
| 1399 { | |
| 1400 _cmsFree(ContextID, NewLUT); | |
| 1401 return NULL; | |
| 1402 } | |
| 1403 | |
| 1404 return NewLUT; | |
| 1405 } | |
| 1406 | |
| 1407 cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(cmsContext ContextID, const cmsPipeline* lut) | |
| 1408 { | |
| 1409 cmsUNUSED_PARAMETER(ContextID); | |
| 1410 _cmsAssert(lut != NULL); | |
| 1411 return lut ->InputChannels; | |
| 1412 } | |
| 1413 | |
| 1414 cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(cmsContext ContextID, const cmsPipeline* lut) | |
| 1415 { | |
| 1416 cmsUNUSED_PARAMETER(ContextID); | |
| 1417 _cmsAssert(lut != NULL); | |
| 1418 return lut ->OutputChannels; | |
| 1419 } | |
| 1420 | |
| 1421 // Free a profile elements LUT | |
| 1422 void CMSEXPORT cmsPipelineFree(cmsContext ContextID, cmsPipeline* lut) | |
| 1423 { | |
| 1424 cmsStage *mpe, *Next; | |
| 1425 | |
| 1426 if (lut == NULL) return; | |
| 1427 | |
| 1428 for (mpe = lut ->Elements; | |
| 1429 mpe != NULL; | |
| 1430 mpe = Next) { | |
| 1431 | |
| 1432 Next = mpe ->Next; | |
| 1433 cmsStageFree(ContextID, mpe); | |
| 1434 } | |
| 1435 | |
| 1436 if (lut ->FreeDataFn) lut ->FreeDataFn(ContextID, lut ->Data); | |
| 1437 | |
| 1438 _cmsFree(ContextID, lut); | |
| 1439 } | |
| 1440 | |
| 1441 | |
| 1442 // Default to evaluate the LUT on 16 bit-basis. | |
| 1443 void CMSEXPORT cmsPipelineEval16(cmsContext ContextID, const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut) | |
| 1444 { | |
| 1445 _cmsAssert(lut != NULL); | |
| 1446 lut ->Eval16Fn(ContextID, In, Out, lut->Data); | |
| 1447 } | |
| 1448 | |
| 1449 | |
| 1450 // Does evaluate the LUT on cmsFloat32Number-basis. | |
| 1451 void CMSEXPORT cmsPipelineEvalFloat(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut) | |
| 1452 { | |
| 1453 _cmsAssert(lut != NULL); | |
| 1454 lut ->EvalFloatFn(ContextID, In, Out, lut); | |
| 1455 } | |
| 1456 | |
| 1457 // Duplicates a LUT | |
| 1458 cmsPipeline* CMSEXPORT cmsPipelineDup(cmsContext ContextID, const cmsPipeline* lut) | |
| 1459 { | |
| 1460 cmsPipeline* NewLUT; | |
| 1461 cmsStage *NewMPE, *Anterior = NULL, *mpe; | |
| 1462 cmsBool First = TRUE; | |
| 1463 | |
| 1464 if (lut == NULL) return NULL; | |
| 1465 | |
| 1466 NewLUT = cmsPipelineAlloc(ContextID, lut ->InputChannels, lut ->OutputChannels); | |
| 1467 if (NewLUT == NULL) return NULL; | |
| 1468 | |
| 1469 for (mpe = lut ->Elements; | |
| 1470 mpe != NULL; | |
| 1471 mpe = mpe ->Next) { | |
| 1472 | |
| 1473 NewMPE = cmsStageDup(ContextID, mpe); | |
| 1474 | |
| 1475 if (NewMPE == NULL) { | |
| 1476 cmsPipelineFree(ContextID, NewLUT); | |
| 1477 return NULL; | |
| 1478 } | |
| 1479 | |
| 1480 if (First) { | |
| 1481 NewLUT ->Elements = NewMPE; | |
| 1482 First = FALSE; | |
| 1483 } | |
| 1484 else { | |
| 1485 if (Anterior != NULL) | |
| 1486 Anterior ->Next = NewMPE; | |
| 1487 } | |
| 1488 | |
| 1489 Anterior = NewMPE; | |
| 1490 } | |
| 1491 | |
| 1492 NewLUT ->Eval16Fn = lut ->Eval16Fn; | |
| 1493 NewLUT ->EvalFloatFn = lut ->EvalFloatFn; | |
| 1494 NewLUT ->DupDataFn = lut ->DupDataFn; | |
| 1495 NewLUT ->FreeDataFn = lut ->FreeDataFn; | |
| 1496 | |
| 1497 if (NewLUT ->DupDataFn != NULL) | |
| 1498 NewLUT ->Data = NewLUT ->DupDataFn(ContextID, lut->Data); | |
| 1499 | |
| 1500 | |
| 1501 NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits; | |
| 1502 | |
| 1503 if (!BlessLUT(ContextID, NewLUT)) | |
| 1504 { | |
| 1505 _cmsFree(ContextID, NewLUT); | |
| 1506 return NULL; | |
| 1507 } | |
| 1508 | |
| 1509 return NewLUT; | |
| 1510 } | |
| 1511 | |
| 1512 | |
| 1513 int CMSEXPORT cmsPipelineInsertStage(cmsContext ContextID, cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe) | |
| 1514 { | |
| 1515 cmsStage* Anterior = NULL, *pt; | |
| 1516 | |
| 1517 if (lut == NULL || mpe == NULL) | |
| 1518 return FALSE; | |
| 1519 | |
| 1520 switch (loc) { | |
| 1521 | |
| 1522 case cmsAT_BEGIN: | |
| 1523 mpe ->Next = lut ->Elements; | |
| 1524 lut ->Elements = mpe; | |
| 1525 break; | |
| 1526 | |
| 1527 case cmsAT_END: | |
| 1528 | |
| 1529 if (lut ->Elements == NULL) | |
| 1530 lut ->Elements = mpe; | |
| 1531 else { | |
| 1532 | |
| 1533 for (pt = lut ->Elements; | |
| 1534 pt != NULL; | |
| 1535 pt = pt -> Next) Anterior = pt; | |
| 1536 | |
| 1537 Anterior ->Next = mpe; | |
| 1538 mpe ->Next = NULL; | |
| 1539 } | |
| 1540 break; | |
| 1541 default:; | |
| 1542 return FALSE; | |
| 1543 } | |
| 1544 | |
| 1545 return BlessLUT(ContextID, lut); | |
| 1546 } | |
| 1547 | |
| 1548 // Unlink an element and return the pointer to it | |
| 1549 void CMSEXPORT cmsPipelineUnlinkStage(cmsContext ContextID, cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe) | |
| 1550 { | |
| 1551 cmsStage *Anterior, *pt, *Last; | |
| 1552 cmsStage *Unlinked = NULL; | |
| 1553 | |
| 1554 | |
| 1555 // If empty LUT, there is nothing to remove | |
| 1556 if (lut ->Elements == NULL) { | |
| 1557 if (mpe) *mpe = NULL; | |
| 1558 return; | |
| 1559 } | |
| 1560 | |
| 1561 // On depending on the strategy... | |
| 1562 switch (loc) { | |
| 1563 | |
| 1564 case cmsAT_BEGIN: | |
| 1565 { | |
| 1566 cmsStage* elem = lut ->Elements; | |
| 1567 | |
| 1568 lut ->Elements = elem -> Next; | |
| 1569 elem ->Next = NULL; | |
| 1570 Unlinked = elem; | |
| 1571 | |
| 1572 } | |
| 1573 break; | |
| 1574 | |
| 1575 case cmsAT_END: | |
| 1576 Anterior = Last = NULL; | |
| 1577 for (pt = lut ->Elements; | |
| 1578 pt != NULL; | |
| 1579 pt = pt -> Next) { | |
| 1580 Anterior = Last; | |
| 1581 Last = pt; | |
| 1582 } | |
| 1583 | |
| 1584 Unlinked = Last; // Next already points to NULL | |
| 1585 | |
| 1586 // Truncate the chain | |
| 1587 if (Anterior) | |
| 1588 Anterior ->Next = NULL; | |
| 1589 else | |
| 1590 lut ->Elements = NULL; | |
| 1591 break; | |
| 1592 default:; | |
| 1593 } | |
| 1594 | |
| 1595 if (mpe) | |
| 1596 *mpe = Unlinked; | |
| 1597 else | |
| 1598 cmsStageFree(ContextID, Unlinked); | |
| 1599 | |
| 1600 // May fail, but we ignore it | |
| 1601 BlessLUT(ContextID, lut); | |
| 1602 } | |
| 1603 | |
| 1604 | |
| 1605 // Concatenate two LUT into a new single one | |
| 1606 cmsBool CMSEXPORT cmsPipelineCat(cmsContext ContextID, cmsPipeline* l1, const cmsPipeline* l2) | |
| 1607 { | |
| 1608 cmsStage* mpe; | |
| 1609 | |
| 1610 // If both LUTS does not have elements, we need to inherit | |
| 1611 // the number of channels | |
| 1612 if (l1 ->Elements == NULL && l2 ->Elements == NULL) { | |
| 1613 l1 ->InputChannels = l2 ->InputChannels; | |
| 1614 l1 ->OutputChannels = l2 ->OutputChannels; | |
| 1615 } | |
| 1616 | |
| 1617 // Cat second | |
| 1618 for (mpe = l2 ->Elements; | |
| 1619 mpe != NULL; | |
| 1620 mpe = mpe ->Next) { | |
| 1621 | |
| 1622 // We have to dup each element | |
| 1623 if (!cmsPipelineInsertStage(ContextID, l1, cmsAT_END, cmsStageDup(ContextID, mpe))) | |
| 1624 return FALSE; | |
| 1625 } | |
| 1626 | |
| 1627 return BlessLUT(ContextID, l1); | |
| 1628 } | |
| 1629 | |
| 1630 | |
| 1631 cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsContext ContextID, cmsPipeline* lut, cmsBool On) | |
| 1632 { | |
| 1633 cmsBool Anterior = lut ->SaveAs8Bits; | |
| 1634 cmsUNUSED_PARAMETER(ContextID); | |
| 1635 | |
| 1636 lut ->SaveAs8Bits = On; | |
| 1637 return Anterior; | |
| 1638 } | |
| 1639 | |
| 1640 | |
| 1641 cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(cmsContext ContextID, const cmsPipeline* lut) | |
| 1642 { | |
| 1643 cmsUNUSED_PARAMETER(ContextID); | |
| 1644 return lut ->Elements; | |
| 1645 } | |
| 1646 | |
| 1647 cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(cmsContext ContextID, const cmsPipeline* lut) | |
| 1648 { | |
| 1649 cmsStage *mpe, *Anterior = NULL; | |
| 1650 cmsUNUSED_PARAMETER(ContextID); | |
| 1651 | |
| 1652 for (mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) | |
| 1653 Anterior = mpe; | |
| 1654 | |
| 1655 return Anterior; | |
| 1656 } | |
| 1657 | |
| 1658 cmsUInt32Number CMSEXPORT cmsPipelineStageCount(cmsContext ContextID, const cmsPipeline* lut) | |
| 1659 { | |
| 1660 cmsStage *mpe; | |
| 1661 cmsUInt32Number n; | |
| 1662 cmsUNUSED_PARAMETER(ContextID); | |
| 1663 | |
| 1664 for (n=0, mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) | |
| 1665 n++; | |
| 1666 | |
| 1667 return n; | |
| 1668 } | |
| 1669 | |
| 1670 // This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional | |
| 1671 // duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. | |
| 1672 void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsContext ContextID, | |
| 1673 cmsPipeline* Lut, | |
| 1674 _cmsPipelineEval16Fn Eval16, | |
| 1675 void* PrivateData, | |
| 1676 _cmsFreeUserDataFn FreePrivateDataFn, | |
| 1677 _cmsDupUserDataFn DupPrivateDataFn) | |
| 1678 { | |
| 1679 cmsUNUSED_PARAMETER(ContextID); | |
| 1680 | |
| 1681 Lut ->Eval16Fn = Eval16; | |
| 1682 Lut ->DupDataFn = DupPrivateDataFn; | |
| 1683 Lut ->FreeDataFn = FreePrivateDataFn; | |
| 1684 Lut ->Data = PrivateData; | |
| 1685 } | |
| 1686 | |
| 1687 | |
| 1688 // ----------------------------------------------------------- Reverse interpolation | |
| 1689 // Here's how it goes. The derivative Df(x) of the function f is the linear | |
| 1690 // transformation that best approximates f near the point x. It can be represented | |
| 1691 // by a matrix A whose entries are the partial derivatives of the components of f | |
| 1692 // with respect to all the coordinates. This is know as the Jacobian | |
| 1693 // | |
| 1694 // The best linear approximation to f is given by the matrix equation: | |
| 1695 // | |
| 1696 // y-y0 = A (x-x0) | |
| 1697 // | |
| 1698 // So, if x0 is a good "guess" for the zero of f, then solving for the zero of this | |
| 1699 // linear approximation will give a "better guess" for the zero of f. Thus let y=0, | |
| 1700 // and since y0=f(x0) one can solve the above equation for x. This leads to the | |
| 1701 // Newton's method formula: | |
| 1702 // | |
| 1703 // xn+1 = xn - A-1 f(xn) | |
| 1704 // | |
| 1705 // where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the | |
| 1706 // fashion described above. Iterating this will give better and better approximations | |
| 1707 // if you have a "good enough" initial guess. | |
| 1708 | |
| 1709 | |
| 1710 #define JACOBIAN_EPSILON 0.001f | |
| 1711 #define INVERSION_MAX_ITERATIONS 30 | |
| 1712 | |
| 1713 // Increment with reflexion on boundary | |
| 1714 static | |
| 1715 void IncDelta(cmsFloat32Number *Val) | |
| 1716 { | |
| 1717 if (*Val < (1.0 - JACOBIAN_EPSILON)) | |
| 1718 | |
| 1719 *Val += JACOBIAN_EPSILON; | |
| 1720 | |
| 1721 else | |
| 1722 *Val -= JACOBIAN_EPSILON; | |
| 1723 | |
| 1724 } | |
| 1725 | |
| 1726 | |
| 1727 | |
| 1728 // Euclidean distance between two vectors of n elements each one | |
| 1729 static | |
| 1730 cmsFloat32Number EuclideanDistance(cmsFloat32Number a[], cmsFloat32Number b[], int n) | |
| 1731 { | |
| 1732 cmsFloat32Number sum = 0; | |
| 1733 int i; | |
| 1734 | |
| 1735 for (i=0; i < n; i++) { | |
| 1736 cmsFloat32Number dif = b[i] - a[i]; | |
| 1737 sum += dif * dif; | |
| 1738 } | |
| 1739 | |
| 1740 return sqrtf(sum); | |
| 1741 } | |
| 1742 | |
| 1743 | |
| 1744 // Evaluate a LUT in reverse direction. It only searches on 3->3 LUT. Uses Newton method | |
| 1745 // | |
| 1746 // x1 <- x - [J(x)]^-1 * f(x) | |
| 1747 // | |
| 1748 // lut: The LUT on where to do the search | |
| 1749 // Target: LabK, 3 values of Lab plus destination K which is fixed | |
| 1750 // Result: The obtained CMYK | |
| 1751 // Hint: Location where begin the search | |
| 1752 | |
| 1753 cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsContext ContextID, | |
| 1754 cmsFloat32Number Target[], | |
| 1755 cmsFloat32Number Result[], | |
| 1756 cmsFloat32Number Hint[], | |
| 1757 const cmsPipeline* lut) | |
| 1758 { | |
| 1759 cmsUInt32Number i, j; | |
| 1760 cmsFloat64Number error, LastError = 1E20; | |
| 1761 cmsFloat32Number fx[4], x[4], xd[4], fxd[4]; | |
| 1762 cmsVEC3 tmp, tmp2; | |
| 1763 cmsMAT3 Jacobian; | |
| 1764 | |
| 1765 // Only 3->3 and 4->3 are supported | |
| 1766 if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE; | |
| 1767 if (lut ->OutputChannels != 3) return FALSE; | |
| 1768 | |
| 1769 // Take the hint as starting point if specified | |
| 1770 if (Hint == NULL) { | |
| 1771 | |
| 1772 // Begin at any point, we choose 1/3 of CMY axis | |
| 1773 x[0] = x[1] = x[2] = 0.3f; | |
| 1774 } | |
| 1775 else { | |
| 1776 | |
| 1777 // Only copy 3 channels from hint... | |
| 1778 for (j=0; j < 3; j++) | |
| 1779 x[j] = Hint[j]; | |
| 1780 } | |
| 1781 | |
| 1782 // If Lut is 4-dimensions, then grab target[3], which is fixed | |
| 1783 if (lut ->InputChannels == 4) { | |
| 1784 x[3] = Target[3]; | |
| 1785 } | |
| 1786 else x[3] = 0; // To keep lint happy | |
| 1787 | |
| 1788 | |
| 1789 // Iterate | |
| 1790 for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) { | |
| 1791 | |
| 1792 // Get beginning fx | |
| 1793 cmsPipelineEvalFloat(ContextID, x, fx, lut); | |
| 1794 | |
| 1795 // Compute error | |
| 1796 error = EuclideanDistance(fx, Target, 3); | |
| 1797 | |
| 1798 // If not convergent, return last safe value | |
| 1799 if (error >= LastError) | |
| 1800 break; | |
| 1801 | |
| 1802 // Keep latest values | |
| 1803 LastError = error; | |
| 1804 for (j=0; j < lut ->InputChannels; j++) | |
| 1805 Result[j] = x[j]; | |
| 1806 | |
| 1807 // Found an exact match? | |
| 1808 if (error <= 0) | |
| 1809 break; | |
| 1810 | |
| 1811 // Obtain slope (the Jacobian) | |
| 1812 for (j = 0; j < 3; j++) { | |
| 1813 | |
| 1814 xd[0] = x[0]; | |
| 1815 xd[1] = x[1]; | |
| 1816 xd[2] = x[2]; | |
| 1817 xd[3] = x[3]; // Keep fixed channel | |
| 1818 | |
| 1819 IncDelta(&xd[j]); | |
| 1820 | |
| 1821 cmsPipelineEvalFloat(ContextID, xd, fxd, lut); | |
| 1822 | |
| 1823 Jacobian.v[0].n[j] = ((fxd[0] - fx[0]) / JACOBIAN_EPSILON); | |
| 1824 Jacobian.v[1].n[j] = ((fxd[1] - fx[1]) / JACOBIAN_EPSILON); | |
| 1825 Jacobian.v[2].n[j] = ((fxd[2] - fx[2]) / JACOBIAN_EPSILON); | |
| 1826 } | |
| 1827 | |
| 1828 // Solve system | |
| 1829 tmp2.n[0] = fx[0] - Target[0]; | |
| 1830 tmp2.n[1] = fx[1] - Target[1]; | |
| 1831 tmp2.n[2] = fx[2] - Target[2]; | |
| 1832 | |
| 1833 if (!_cmsMAT3solve(ContextID, &tmp, &Jacobian, &tmp2)) | |
| 1834 return FALSE; | |
| 1835 | |
| 1836 // Move our guess | |
| 1837 x[0] -= (cmsFloat32Number) tmp.n[0]; | |
| 1838 x[1] -= (cmsFloat32Number) tmp.n[1]; | |
| 1839 x[2] -= (cmsFloat32Number) tmp.n[2]; | |
| 1840 | |
| 1841 // Some clipping.... | |
| 1842 for (j=0; j < 3; j++) { | |
| 1843 if (x[j] < 0) x[j] = 0; | |
| 1844 else | |
| 1845 if (x[j] > 1.0) x[j] = 1.0; | |
| 1846 } | |
| 1847 } | |
| 1848 | |
| 1849 return TRUE; | |
| 1850 } |
