comparison mupdf-source/thirdparty/lcms2/src/cmsvirt.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 // Virtual (built-in) profiles
30 // -----------------------------------------------------------------------------------
31
32 static
33 cmsBool SetTextTags(cmsContext ContextID, cmsHPROFILE hProfile, const wchar_t* Description)
34 {
35 cmsMLU *DescriptionMLU, *CopyrightMLU;
36 cmsBool rc = FALSE;
37
38 DescriptionMLU = cmsMLUalloc(ContextID, 1);
39 CopyrightMLU = cmsMLUalloc(ContextID, 1);
40
41 if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;
42
43 if (!cmsMLUsetWide(ContextID, DescriptionMLU, "en", "US", Description)) goto Error;
44 if (!cmsMLUsetWide(ContextID, CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error;
45
46 if (!cmsWriteTag(ContextID, hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error;
47 if (!cmsWriteTag(ContextID, hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error;
48
49 rc = TRUE;
50
51 Error:
52
53 if (DescriptionMLU)
54 cmsMLUfree(ContextID, DescriptionMLU);
55 if (CopyrightMLU)
56 cmsMLUfree(ContextID, CopyrightMLU);
57 return rc;
58 }
59
60
61 static
62 cmsBool SetSeqDescTag(cmsContext ContextID, cmsHPROFILE hProfile, const char* Model)
63 {
64 cmsBool rc = FALSE;
65 cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1);
66
67 if (Seq == NULL) return FALSE;
68
69 Seq->seq[0].deviceMfg = (cmsSignature) 0;
70 Seq->seq[0].deviceModel = (cmsSignature) 0;
71
72 #ifdef CMS_DONT_USE_INT64
73 Seq->seq[0].attributes[0] = 0;
74 Seq->seq[0].attributes[1] = 0;
75 #else
76 Seq->seq[0].attributes = 0;
77 #endif
78
79 Seq->seq[0].technology = (cmsTechnologySignature) 0;
80
81 cmsMLUsetASCII(ContextID, Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS");
82 cmsMLUsetASCII(ContextID, Seq->seq[0].Model, cmsNoLanguage, cmsNoCountry, Model);
83
84 if (!_cmsWriteProfileSequence(ContextID, hProfile, Seq)) goto Error;
85
86 rc = TRUE;
87
88 Error:
89 if (Seq)
90 cmsFreeProfileSequenceDescription(ContextID, Seq);
91
92 return rc;
93 }
94
95
96
97 // This function creates a profile based on White point, primaries and
98 // transfer functions.
99 cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(cmsContext ContextID,
100 const cmsCIExyY* WhitePoint,
101 const cmsCIExyYTRIPLE* Primaries,
102 cmsToneCurve* const TransferFunction[3])
103 {
104 cmsHPROFILE hICC;
105 cmsMAT3 MColorants;
106 cmsCIEXYZTRIPLE Colorants;
107 cmsCIExyY MaxWhite;
108 cmsMAT3 CHAD;
109 cmsCIEXYZ WhitePointXYZ;
110
111 hICC = cmsCreateProfilePlaceholder(ContextID);
112 if (!hICC) // can't allocate
113 return NULL;
114
115 cmsSetProfileVersion(ContextID, hICC, 4.4);
116
117 cmsSetDeviceClass(ContextID, hICC, cmsSigDisplayClass);
118 cmsSetColorSpace(ContextID, hICC, cmsSigRgbData);
119 cmsSetPCS(ContextID, hICC, cmsSigXYZData);
120
121 cmsSetHeaderRenderingIntent(ContextID, hICC, INTENT_PERCEPTUAL);
122
123
124 // Implement profile using following tags:
125 //
126 // 1 cmsSigProfileDescriptionTag
127 // 2 cmsSigMediaWhitePointTag
128 // 3 cmsSigRedColorantTag
129 // 4 cmsSigGreenColorantTag
130 // 5 cmsSigBlueColorantTag
131 // 6 cmsSigRedTRCTag
132 // 7 cmsSigGreenTRCTag
133 // 8 cmsSigBlueTRCTag
134 // 9 Chromatic adaptation Tag
135 // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II)
136 // 10 cmsSigChromaticityTag
137
138
139 if (!SetTextTags(ContextID, hICC, L"RGB built-in")) goto Error;
140
141 if (WhitePoint) {
142
143 if (!cmsWriteTag(ContextID, hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ(ContextID))) goto Error;
144
145 cmsxyY2XYZ(ContextID, &WhitePointXYZ, WhitePoint);
146 _cmsAdaptationMatrix(ContextID, &CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ(ContextID));
147
148 // This is a V4 tag, but many CMM does read and understand it no matter which version
149 if (!cmsWriteTag(ContextID, hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error;
150 }
151
152 if (WhitePoint && Primaries) {
153
154 MaxWhite.x = WhitePoint -> x;
155 MaxWhite.y = WhitePoint -> y;
156 MaxWhite.Y = 1.0;
157
158 if (!_cmsBuildRGB2XYZtransferMatrix(ContextID, &MColorants, &MaxWhite, Primaries)) goto Error;
159
160 Colorants.Red.X = MColorants.v[0].n[0];
161 Colorants.Red.Y = MColorants.v[1].n[0];
162 Colorants.Red.Z = MColorants.v[2].n[0];
163
164 Colorants.Green.X = MColorants.v[0].n[1];
165 Colorants.Green.Y = MColorants.v[1].n[1];
166 Colorants.Green.Z = MColorants.v[2].n[1];
167
168 Colorants.Blue.X = MColorants.v[0].n[2];
169 Colorants.Blue.Y = MColorants.v[1].n[2];
170 Colorants.Blue.Z = MColorants.v[2].n[2];
171
172 if (!cmsWriteTag(ContextID, hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error;
173 if (!cmsWriteTag(ContextID, hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error;
174 if (!cmsWriteTag(ContextID, hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error;
175 }
176
177
178 if (TransferFunction) {
179
180 // Tries to minimize space. Thanks to Richard Hughes for this nice idea
181 if (!cmsWriteTag(ContextID, hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error;
182
183 if (TransferFunction[1] == TransferFunction[0]) {
184
185 if (!cmsLinkTag (ContextID, hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;
186
187 } else {
188
189 if (!cmsWriteTag(ContextID, hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
190 }
191
192 if (TransferFunction[2] == TransferFunction[0]) {
193
194 if (!cmsLinkTag (ContextID, hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;
195
196 } else {
197
198 if (!cmsWriteTag(ContextID, hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
199 }
200 }
201
202 if (Primaries) {
203 if (!cmsWriteTag(ContextID, hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error;
204 }
205
206
207 return hICC;
208
209 Error:
210 if (hICC)
211 cmsCloseProfile(ContextID, hICC);
212 return NULL;
213 }
214
215
216
217 // This function creates a profile based on White point and transfer function.
218 cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(cmsContext ContextID,
219 const cmsCIExyY* WhitePoint,
220 const cmsToneCurve* TransferFunction)
221 {
222 cmsHPROFILE hICC;
223 cmsCIEXYZ tmp;
224
225 hICC = cmsCreateProfilePlaceholder(ContextID);
226 if (!hICC) // can't allocate
227 return NULL;
228
229 cmsSetProfileVersion(ContextID, hICC, 4.4);
230
231 cmsSetDeviceClass(ContextID, hICC, cmsSigDisplayClass);
232 cmsSetColorSpace(ContextID, hICC, cmsSigGrayData);
233 cmsSetPCS(ContextID, hICC, cmsSigXYZData);
234 cmsSetHeaderRenderingIntent(ContextID, hICC, INTENT_PERCEPTUAL);
235
236
237 // Implement profile using following tags:
238 //
239 // 1 cmsSigProfileDescriptionTag
240 // 2 cmsSigMediaWhitePointTag
241 // 3 cmsSigGrayTRCTag
242
243 // This conforms a standard Gray DisplayProfile
244
245 // Fill-in the tags
246
247 if (!SetTextTags(ContextID, hICC, L"gray built-in")) goto Error;
248
249
250 if (WhitePoint) {
251
252 cmsxyY2XYZ(ContextID, &tmp, WhitePoint);
253 if (!cmsWriteTag(ContextID, hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error;
254 }
255
256 if (TransferFunction) {
257
258 if (!cmsWriteTag(ContextID, hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error;
259 }
260
261 return hICC;
262
263 Error:
264 if (hICC)
265 cmsCloseProfile(ContextID, hICC);
266 return NULL;
267 }
268
269
270
271
272 // This is a devicelink operating in the target colorspace with as many transfer functions as components
273
274 cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsContext ContextID,
275 cmsColorSpaceSignature ColorSpace,
276 cmsToneCurve* const TransferFunctions[])
277 {
278 cmsHPROFILE hICC;
279 cmsPipeline* Pipeline;
280 cmsInt32Number nChannels;
281
282 hICC = cmsCreateProfilePlaceholder(ContextID);
283 if (!hICC)
284 return NULL;
285
286 cmsSetProfileVersion(ContextID, hICC, 4.4);
287
288 cmsSetDeviceClass(ContextID, hICC, cmsSigLinkClass);
289 cmsSetColorSpace(ContextID, hICC, ColorSpace);
290 cmsSetPCS(ContextID, hICC, ColorSpace);
291
292 cmsSetHeaderRenderingIntent(ContextID, hICC, INTENT_PERCEPTUAL);
293
294 // Set up channels
295 nChannels = cmsChannelsOfColorSpace(ContextID, ColorSpace);
296
297 // Creates a Pipeline with prelinearization step only
298 Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
299 if (Pipeline == NULL) goto Error;
300
301
302 // Copy tables to Pipeline
303 if (!cmsPipelineInsertStage(ContextID, Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
304 goto Error;
305
306 // Create tags
307 if (!SetTextTags(ContextID, hICC, L"Linearization built-in")) goto Error;
308 if (!cmsWriteTag(ContextID, hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error;
309 if (!SetSeqDescTag(ContextID, hICC, "Linearization built-in")) goto Error;
310
311 // Pipeline is already on virtual profile
312 cmsPipelineFree(ContextID, Pipeline);
313
314 // Ok, done
315 return hICC;
316
317 Error:
318 cmsPipelineFree(ContextID, Pipeline);
319 if (hICC)
320 cmsCloseProfile(ContextID, hICC);
321
322
323 return NULL;
324 }
325
326 // Ink-limiting algorithm
327 //
328 // Sum = C + M + Y + K
329 // If Sum > InkLimit
330 // Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
331 // if Ratio <0
332 // Ratio=0
333 // endif
334 // Else
335 // Ratio=1
336 // endif
337 //
338 // C = Ratio * C
339 // M = Ratio * M
340 // Y = Ratio * Y
341 // K: Does not change
342
343 static
344 int InkLimitingSampler(cmsContext ContextID, CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
345 {
346 cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
347 cmsFloat64Number SumCMY, SumCMYK, Ratio;
348 cmsUNUSED_PARAMETER(ContextID);
349
350 InkLimit = (InkLimit * 655.35);
351
352 SumCMY = (cmsFloat64Number) In[0] + In[1] + In[2];
353 SumCMYK = SumCMY + In[3];
354
355 if (SumCMYK > InkLimit) {
356
357 Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
358 if (Ratio < 0)
359 Ratio = 0;
360 }
361 else Ratio = 1;
362
363 Out[0] = _cmsQuickSaturateWord(In[0] * Ratio); // C
364 Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M
365 Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y
366
367 Out[3] = In[3]; // K (untouched)
368
369 return TRUE;
370 }
371
372 // This is a devicelink operating in CMYK for ink-limiting
373
374 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsContext ContextID,
375 cmsColorSpaceSignature ColorSpace,
376 cmsFloat64Number Limit)
377 {
378 cmsHPROFILE hICC;
379 cmsPipeline* LUT;
380 cmsStage* CLUT;
381 cmsInt32Number nChannels;
382
383 if (ColorSpace != cmsSigCmykData) {
384 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
385 return NULL;
386 }
387
388 if (Limit < 0.0 || Limit > 400) {
389
390 cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 1..400");
391 if (Limit < 1) Limit = 1;
392 if (Limit > 400) Limit = 400;
393 }
394
395 hICC = cmsCreateProfilePlaceholder(ContextID);
396 if (!hICC) // can't allocate
397 return NULL;
398
399 cmsSetProfileVersion(ContextID, hICC, 4.4);
400
401 cmsSetDeviceClass(ContextID, hICC, cmsSigLinkClass);
402 cmsSetColorSpace(ContextID, hICC, ColorSpace);
403 cmsSetPCS(ContextID, hICC, ColorSpace);
404
405 cmsSetHeaderRenderingIntent(ContextID, hICC, INTENT_PERCEPTUAL);
406
407
408 // Creates a Pipeline with 3D grid only
409 LUT = cmsPipelineAlloc(ContextID, 4, 4);
410 if (LUT == NULL) goto Error;
411
412
413 nChannels = cmsChannelsOf(ContextID, ColorSpace);
414
415 CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL);
416 if (CLUT == NULL) goto Error;
417
418 if (!cmsStageSampleCLut16bit(ContextID, CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;
419
420 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) ||
421 !cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, CLUT) ||
422 !cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels)))
423 goto Error;
424
425 // Create tags
426 if (!SetTextTags(ContextID, hICC, L"ink-limiting built-in")) goto Error;
427
428 if (!cmsWriteTag(ContextID, hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error;
429 if (!SetSeqDescTag(ContextID, hICC, "ink-limiting built-in")) goto Error;
430
431 // cmsPipeline is already on virtual profile
432 cmsPipelineFree(ContextID, LUT);
433
434 // Ok, done
435 return hICC;
436
437 Error:
438 if (LUT != NULL)
439 cmsPipelineFree(ContextID, LUT);
440
441 if (hICC != NULL)
442 cmsCloseProfile(ContextID, hICC);
443
444 return NULL;
445 }
446
447
448 // Creates a fake Lab identity.
449 cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(cmsContext ContextID, const cmsCIExyY* WhitePoint)
450 {
451 cmsHPROFILE hProfile;
452 cmsPipeline* LUT = NULL;
453
454 hProfile = cmsCreateRGBProfile(ContextID, WhitePoint == NULL ? cmsD50_xyY(ContextID) : WhitePoint, NULL, NULL);
455 if (hProfile == NULL) return NULL;
456
457 cmsSetProfileVersion(ContextID, hProfile, 2.1);
458
459 cmsSetDeviceClass(ContextID, hProfile, cmsSigAbstractClass);
460 cmsSetColorSpace(ContextID, hProfile, cmsSigLabData);
461 cmsSetPCS(ContextID, hProfile, cmsSigLabData);
462
463 if (!SetTextTags(ContextID, hProfile, L"Lab identity built-in")) goto Error;
464
465 // An identity LUT is all we need
466 LUT = cmsPipelineAlloc(ContextID, 3, 3);
467 if (LUT == NULL) goto Error;
468
469 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
470 goto Error;
471
472 if (!cmsWriteTag(ContextID, hProfile, cmsSigAToB0Tag, LUT)) goto Error;
473 cmsPipelineFree(ContextID, LUT);
474
475 return hProfile;
476
477 Error:
478
479 if (LUT != NULL)
480 cmsPipelineFree(ContextID, LUT);
481
482 if (hProfile != NULL)
483 cmsCloseProfile(ContextID, hProfile);
484
485 return NULL;
486 }
487
488
489
490 // Creates a fake Lab V4 identity.
491 cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(cmsContext ContextID, const cmsCIExyY* WhitePoint)
492 {
493 cmsHPROFILE hProfile;
494 cmsPipeline* LUT = NULL;
495
496 hProfile = cmsCreateRGBProfile(ContextID, WhitePoint == NULL ? cmsD50_xyY(ContextID) : WhitePoint, NULL, NULL);
497 if (hProfile == NULL) return NULL;
498
499 cmsSetProfileVersion(ContextID, hProfile, 4.4);
500
501 cmsSetDeviceClass(ContextID, hProfile, cmsSigAbstractClass);
502 cmsSetColorSpace(ContextID, hProfile, cmsSigLabData);
503 cmsSetPCS(ContextID, hProfile, cmsSigLabData);
504
505 if (!SetTextTags(ContextID, hProfile, L"Lab identity built-in")) goto Error;
506
507 // An empty LUTs is all we need
508 LUT = cmsPipelineAlloc(ContextID, 3, 3);
509 if (LUT == NULL) goto Error;
510
511 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
512 goto Error;
513
514 if (!cmsWriteTag(ContextID, hProfile, cmsSigAToB0Tag, LUT)) goto Error;
515 cmsPipelineFree(ContextID, LUT);
516
517 return hProfile;
518
519 Error:
520
521 if (LUT != NULL)
522 cmsPipelineFree(ContextID, LUT);
523
524 if (hProfile != NULL)
525 cmsCloseProfile(ContextID, hProfile);
526
527 return NULL;
528 }
529
530
531 // Creates a fake XYZ identity
532 cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(cmsContext ContextID)
533 {
534 cmsHPROFILE hProfile;
535 cmsPipeline* LUT = NULL;
536
537 hProfile = cmsCreateRGBProfile(ContextID, cmsD50_xyY(ContextID), NULL, NULL);
538 if (hProfile == NULL) return NULL;
539
540 cmsSetProfileVersion(ContextID, hProfile, 4.4);
541
542 cmsSetDeviceClass(ContextID, hProfile, cmsSigAbstractClass);
543 cmsSetColorSpace(ContextID, hProfile, cmsSigXYZData);
544 cmsSetPCS(ContextID, hProfile, cmsSigXYZData);
545
546 if (!SetTextTags(ContextID, hProfile, L"XYZ identity built-in")) goto Error;
547
548 // An identity LUT is all we need
549 LUT = cmsPipelineAlloc(ContextID, 3, 3);
550 if (LUT == NULL) goto Error;
551
552 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
553 goto Error;
554
555 if (!cmsWriteTag(ContextID, hProfile, cmsSigAToB0Tag, LUT)) goto Error;
556 cmsPipelineFree(ContextID, LUT);
557
558 return hProfile;
559
560 Error:
561
562 if (LUT != NULL)
563 cmsPipelineFree(ContextID, LUT);
564
565 if (hProfile != NULL)
566 cmsCloseProfile(ContextID, hProfile);
567
568 return NULL;
569 }
570
571
572
573 //sRGB Curves are defined by:
574 //
575 //If R'sRGB,G'sRGB, B'sRGB < 0.04045
576 //
577 // R = R'sRGB / 12.92
578 // G = G'sRGB / 12.92
579 // B = B'sRGB / 12.92
580 //
581 //
582 //else if R'sRGB,G'sRGB, B'sRGB >= 0.04045
583 //
584 // R = ((R'sRGB + 0.055) / 1.055)^2.4
585 // G = ((G'sRGB + 0.055) / 1.055)^2.4
586 // B = ((B'sRGB + 0.055) / 1.055)^2.4
587
588 static
589 cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)
590 {
591 cmsFloat64Number Parameters[5];
592
593 Parameters[0] = 2.4;
594 Parameters[1] = 1. / 1.055;
595 Parameters[2] = 0.055 / 1.055;
596 Parameters[3] = 1. / 12.92;
597 Parameters[4] = 0.04045;
598
599 return cmsBuildParametricToneCurve(ContextID, 4, Parameters);
600 }
601
602 // Create the ICC virtual profile for sRGB space
603 cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(cmsContext ContextID)
604 {
605 cmsCIExyY D65 = { 0.3127, 0.3290, 1.0 };
606 cmsCIExyYTRIPLE Rec709Primaries = {
607 {0.6400, 0.3300, 1.0},
608 {0.3000, 0.6000, 1.0},
609 {0.1500, 0.0600, 1.0}
610 };
611 cmsToneCurve* Gamma22[3];
612 cmsHPROFILE hsRGB;
613
614 // cmsWhitePointFromTemp(ContextID, &D65, 6504);
615 Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID);
616 if (Gamma22[0] == NULL) return NULL;
617
618 hsRGB = cmsCreateRGBProfile(ContextID, &D65, &Rec709Primaries, Gamma22);
619 cmsFreeToneCurve(ContextID, Gamma22[0]);
620 if (hsRGB == NULL) return NULL;
621
622 if (!SetTextTags(ContextID, hsRGB, L"sRGB built-in")) {
623 cmsCloseProfile(ContextID, hsRGB);
624 return NULL;
625 }
626
627 return hsRGB;
628 }
629
630 /**
631 * Oklab colorspace profile (experimental)
632 *
633 * This virtual profile cannot be saved as an ICC file
634 */
635 cmsHPROFILE cmsCreate_OkLabProfile(cmsContext ctx)
636 {
637 cmsStage* XYZPCS = _cmsStageNormalizeFromXyzFloat(ctx);
638 cmsStage* PCSXYZ = _cmsStageNormalizeToXyzFloat(ctx);
639
640 const double M_D65_D50[] =
641 {
642 1.047886, 0.022919, -0.050216,
643 0.029582, 0.990484, -0.017079,
644 -0.009252, 0.015073, 0.751678
645 };
646
647 const double M_D50_D65[] =
648 {
649 0.955512609517083, -0.023073214184645, 0.063308961782107,
650 -0.028324949364887, 1.009942432477107, 0.021054814890112,
651 0.012328875695483, -0.020535835374141, 1.330713916450354
652 };
653
654 cmsStage* D65toD50 = cmsStageAllocMatrix(ctx, 3, 3, M_D65_D50, NULL);
655 cmsStage* D50toD65 = cmsStageAllocMatrix(ctx, 3, 3, M_D50_D65, NULL);
656
657 const double M_D65_LMS[] =
658 {
659 0.8189330101, 0.3618667424, -0.1288597137,
660 0.0329845436, 0.9293118715, 0.0361456387,
661 0.0482003018, 0.2643662691, 0.6338517070
662 };
663
664 const double M_LMS_D65[] =
665 {
666 1.227013851103521, -0.557799980651822, 0.281256148966468,
667 -0.040580178423281, 1.112256869616830, -0.071676678665601,
668 -0.076381284505707, -0.421481978418013, 1.586163220440795
669 };
670
671 cmsStage* D65toLMS = cmsStageAllocMatrix(ctx, 3, 3, M_D65_LMS, NULL);
672 cmsStage* LMStoD65 = cmsStageAllocMatrix(ctx, 3, 3, M_LMS_D65, NULL);
673
674 cmsToneCurve* CubeRoot = cmsBuildGamma(ctx, 1.0 / 3.0);
675 cmsToneCurve* Cube = cmsBuildGamma(ctx, 3.0);
676
677 cmsToneCurve* Roots[3] = { CubeRoot, CubeRoot, CubeRoot };
678 cmsToneCurve* Cubes[3] = { Cube, Cube, Cube };
679
680 cmsStage* NonLinearityFw = cmsStageAllocToneCurves(ctx, 3, Roots);
681 cmsStage* NonLinearityRv = cmsStageAllocToneCurves(ctx, 3, Cubes);
682
683 const double M_LMSprime_OkLab[] =
684 {
685 0.2104542553, 0.7936177850, -0.0040720468,
686 1.9779984951, -2.4285922050, 0.4505937099,
687 0.0259040371, 0.7827717662, -0.8086757660
688 };
689
690 const double M_OkLab_LMSprime[] =
691 {
692 0.999999998450520, 0.396337792173768, 0.215803758060759,
693 1.000000008881761, -0.105561342323656, -0.063854174771706,
694 1.000000054672411, -0.089484182094966, -1.291485537864092
695 };
696
697 cmsStage* LMSprime_OkLab = cmsStageAllocMatrix(ctx, 3, 3, M_LMSprime_OkLab, NULL);
698 cmsStage* OkLab_LMSprime = cmsStageAllocMatrix(ctx, 3, 3, M_OkLab_LMSprime, NULL);
699
700 cmsPipeline* AToB = cmsPipelineAlloc(ctx, 3, 3);
701 cmsPipeline* BToA = cmsPipelineAlloc(ctx, 3, 3);
702
703 cmsHPROFILE hProfile = cmsCreateProfilePlaceholder(ctx);
704
705 cmsSetProfileVersion(ctx, hProfile, 4.4);
706
707 cmsSetDeviceClass(ctx, hProfile, cmsSigColorSpaceClass);
708 cmsSetColorSpace(ctx, hProfile, cmsSig3colorData);
709 cmsSetPCS(ctx, hProfile, cmsSigXYZData);
710
711 cmsSetHeaderRenderingIntent(ctx, hProfile, INTENT_RELATIVE_COLORIMETRIC);
712
713 /**
714 * Conversion PCS (XYZ/D50) to OkLab
715 */
716 if (!cmsPipelineInsertStage(ctx, BToA, cmsAT_END, PCSXYZ)) goto error;
717 if (!cmsPipelineInsertStage(ctx, BToA, cmsAT_END, D50toD65)) goto error;
718 if (!cmsPipelineInsertStage(ctx, BToA, cmsAT_END, D65toLMS)) goto error;
719 if (!cmsPipelineInsertStage(ctx, BToA, cmsAT_END, NonLinearityFw)) goto error;
720 if (!cmsPipelineInsertStage(ctx, BToA, cmsAT_END, LMSprime_OkLab)) goto error;
721
722 if (!cmsWriteTag(ctx, hProfile, cmsSigBToA0Tag, BToA)) goto error;
723
724 if (!cmsPipelineInsertStage(ctx, AToB, cmsAT_END, OkLab_LMSprime)) goto error;
725 if (!cmsPipelineInsertStage(ctx, AToB, cmsAT_END, NonLinearityRv)) goto error;
726 if (!cmsPipelineInsertStage(ctx, AToB, cmsAT_END, LMStoD65)) goto error;
727 if (!cmsPipelineInsertStage(ctx, AToB, cmsAT_END, D65toD50)) goto error;
728 if (!cmsPipelineInsertStage(ctx, AToB, cmsAT_END, XYZPCS)) goto error;
729
730 if (!cmsWriteTag(ctx, hProfile, cmsSigAToB0Tag, AToB)) goto error;
731
732 cmsPipelineFree(ctx, BToA);
733 cmsPipelineFree(ctx, AToB);
734
735 cmsFreeToneCurve(ctx, CubeRoot);
736 cmsFreeToneCurve(ctx, Cube);
737
738 return hProfile;
739
740 error:
741 cmsPipelineFree(ctx, BToA);
742 cmsPipelineFree(ctx, AToB);
743
744 cmsFreeToneCurve(ctx, CubeRoot);
745 cmsFreeToneCurve(ctx, Cube);
746 cmsCloseProfile(ctx, hProfile);
747
748 return NULL;
749
750 }
751
752
753 typedef struct {
754 cmsFloat64Number Brightness;
755 cmsFloat64Number Contrast;
756 cmsFloat64Number Hue;
757 cmsFloat64Number Saturation;
758 cmsBool lAdjustWP;
759 cmsCIEXYZ WPsrc, WPdest;
760
761 } BCHSWADJUSTS, *LPBCHSWADJUSTS;
762
763
764 static
765 int bchswSampler(cmsContext ContextID, CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
766 {
767 cmsCIELab LabIn, LabOut;
768 cmsCIELCh LChIn, LChOut;
769 cmsCIEXYZ XYZ;
770 LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
771
772
773 cmsLabEncoded2Float(ContextID, &LabIn, In);
774
775
776 cmsLab2LCh(ContextID, &LChIn, &LabIn);
777
778 // Do some adjusts on LCh
779
780 LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
781 LChOut.C = LChIn.C + bchsw -> Saturation;
782 LChOut.h = LChIn.h + bchsw -> Hue;
783
784
785 cmsLCh2Lab(ContextID, &LabOut, &LChOut);
786
787 // Move white point in Lab
788 if (bchsw->lAdjustWP) {
789 cmsLab2XYZ(ContextID, &bchsw->WPsrc, &XYZ, &LabOut);
790 cmsXYZ2Lab(ContextID, &bchsw->WPdest, &LabOut, &XYZ);
791 }
792
793 // Back to encoded
794
795 cmsFloat2LabEncoded(ContextID, Out, &LabOut);
796
797 return TRUE;
798 }
799
800
801 // Creates an abstract profile operating in Lab space for Brightness,
802 // contrast, Saturation and white point displacement
803
804 cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsContext ContextID,
805 cmsUInt32Number nLUTPoints,
806 cmsFloat64Number Bright,
807 cmsFloat64Number Contrast,
808 cmsFloat64Number Hue,
809 cmsFloat64Number Saturation,
810 cmsUInt32Number TempSrc,
811 cmsUInt32Number TempDest)
812 {
813 cmsHPROFILE hICC;
814 cmsPipeline* Pipeline;
815 BCHSWADJUSTS bchsw;
816 cmsCIExyY WhitePnt;
817 cmsStage* CLUT;
818 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
819 cmsUInt32Number i;
820
821 bchsw.Brightness = Bright;
822 bchsw.Contrast = Contrast;
823 bchsw.Hue = Hue;
824 bchsw.Saturation = Saturation;
825 if (TempSrc == TempDest) {
826
827 bchsw.lAdjustWP = FALSE;
828 }
829 else {
830 bchsw.lAdjustWP = TRUE;
831 cmsWhitePointFromTemp(ContextID, &WhitePnt, TempSrc);
832 cmsxyY2XYZ(ContextID, &bchsw.WPsrc, &WhitePnt);
833 cmsWhitePointFromTemp(ContextID, &WhitePnt, TempDest);
834 cmsxyY2XYZ(ContextID, &bchsw.WPdest, &WhitePnt);
835
836 }
837
838 hICC = cmsCreateProfilePlaceholder(ContextID);
839 if (!hICC) // can't allocate
840 return NULL;
841
842 cmsSetDeviceClass(ContextID, hICC, cmsSigAbstractClass);
843 cmsSetColorSpace(ContextID, hICC, cmsSigLabData);
844 cmsSetPCS(ContextID, hICC, cmsSigLabData);
845
846 cmsSetHeaderRenderingIntent(ContextID, hICC, INTENT_PERCEPTUAL);
847
848 // Creates a Pipeline with 3D grid only
849 Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
850 if (Pipeline == NULL) {
851 cmsCloseProfile(ContextID, hICC);
852 return NULL;
853 }
854
855 for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
856 CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
857 if (CLUT == NULL) goto Error;
858
859
860 if (!cmsStageSampleCLut16bit(ContextID, CLUT, bchswSampler, (void*) &bchsw, 0)) {
861
862 // Shouldn't reach here
863 goto Error;
864 }
865
866 if (!cmsPipelineInsertStage(ContextID, Pipeline, cmsAT_END, CLUT)) {
867 goto Error;
868 }
869
870 // Create tags
871 if (!SetTextTags(ContextID, hICC, L"BCHS built-in")) return NULL;
872
873 cmsWriteTag(ContextID, hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ(ContextID));
874
875 cmsWriteTag(ContextID, hICC, cmsSigAToB0Tag, (void*) Pipeline);
876
877 // Pipeline is already on virtual profile
878 cmsPipelineFree(ContextID, Pipeline);
879
880 // Ok, done
881 return hICC;
882
883 Error:
884 cmsPipelineFree(ContextID, Pipeline);
885 cmsCloseProfile(ContextID, hICC);
886 return NULL;
887 }
888
889
890
891 // Creates a fake NULL profile. This profile return 1 channel as always 0.
892 // Is useful only for gamut checking tricks
893 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(cmsContext ContextID)
894 {
895 cmsHPROFILE hProfile;
896 cmsPipeline* LUT = NULL;
897 cmsStage* PostLin;
898 cmsStage* OutLin;
899 cmsToneCurve* EmptyTab[3];
900 cmsUInt16Number Zero[2] = { 0, 0 };
901 const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
902
903 hProfile = cmsCreateProfilePlaceholder(ContextID);
904 if (!hProfile) // can't allocate
905 return NULL;
906
907 cmsSetProfileVersion(ContextID, hProfile, 4.4);
908
909 if (!SetTextTags(ContextID, hProfile, L"NULL profile built-in")) goto Error;
910
911
912 cmsSetDeviceClass(ContextID, hProfile, cmsSigOutputClass);
913 cmsSetColorSpace(ContextID, hProfile, cmsSigGrayData);
914 cmsSetPCS(ContextID, hProfile, cmsSigLabData);
915
916 // Create a valid ICC 4 structure
917 LUT = cmsPipelineAlloc(ContextID, 3, 1);
918 if (LUT == NULL) goto Error;
919
920 EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
921 PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab);
922 OutLin = cmsStageAllocToneCurves(ContextID, 1, EmptyTab);
923 cmsFreeToneCurve(ContextID, EmptyTab[0]);
924
925 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, PostLin))
926 goto Error;
927
928 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
929 goto Error;
930
931 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, OutLin))
932 goto Error;
933
934 if (!cmsWriteTag(ContextID, hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
935 if (!cmsWriteTag(ContextID, hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ(ContextID))) goto Error;
936
937 cmsPipelineFree(ContextID, LUT);
938 return hProfile;
939
940 Error:
941
942 if (LUT != NULL)
943 cmsPipelineFree(ContextID, LUT);
944
945 if (hProfile != NULL)
946 cmsCloseProfile(ContextID, hProfile);
947
948 return NULL;
949 }
950
951
952 static
953 int IsPCS(cmsColorSpaceSignature ColorSpace)
954 {
955 return (ColorSpace == cmsSigXYZData ||
956 ColorSpace == cmsSigLabData);
957 }
958
959
960 static
961 void FixColorSpaces(cmsContext ContextID, cmsHPROFILE hProfile,
962 cmsColorSpaceSignature ColorSpace,
963 cmsColorSpaceSignature PCS,
964 cmsUInt32Number dwFlags)
965 {
966 if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
967
968 if (IsPCS(ColorSpace) && IsPCS(PCS)) {
969
970 cmsSetDeviceClass(ContextID, hProfile, cmsSigAbstractClass);
971 cmsSetColorSpace(ContextID, hProfile, ColorSpace);
972 cmsSetPCS(ContextID, hProfile, PCS);
973 return;
974 }
975
976 if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
977
978 cmsSetDeviceClass(ContextID, hProfile, cmsSigOutputClass);
979 cmsSetPCS(ContextID, hProfile, ColorSpace);
980 cmsSetColorSpace(ContextID, hProfile, PCS);
981 return;
982 }
983
984 if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
985
986 cmsSetDeviceClass(ContextID, hProfile, cmsSigInputClass);
987 cmsSetColorSpace(ContextID, hProfile, ColorSpace);
988 cmsSetPCS(ContextID, hProfile, PCS);
989 return;
990 }
991 }
992
993 cmsSetDeviceClass(ContextID, hProfile, cmsSigLinkClass);
994 cmsSetColorSpace(ContextID, hProfile, ColorSpace);
995 cmsSetPCS(ContextID, hProfile, PCS);
996 }
997
998
999
1000 // This function creates a named color profile dumping all the contents of transform to a single profile
1001 // In this way, LittleCMS may be used to "group" several named color databases into a single profile.
1002 // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
1003 // is the normal PCS for named color profiles.
1004 static
1005 cmsHPROFILE CreateNamedColorDevicelink(cmsContext ContextID, cmsHTRANSFORM xform)
1006 {
1007 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
1008 cmsHPROFILE hICC = NULL;
1009 cmsUInt32Number i, nColors;
1010 cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
1011 cmsUInt32Number InputFormat, OutputFormat;
1012 cmsFormatter16 FromInput, ToOutput;
1013
1014 // Create an empty placeholder
1015 hICC = cmsCreateProfilePlaceholder(ContextID);
1016 if (hICC == NULL) return NULL;
1017
1018 // Critical information
1019 cmsSetDeviceClass(ContextID, hICC, cmsSigNamedColorClass);
1020 cmsSetColorSpace(ContextID, hICC, v ->core->ExitColorSpace);
1021 cmsSetPCS(ContextID, hICC, cmsSigLabData);
1022
1023 // Tag profile with information
1024 if (!SetTextTags(ContextID, hICC, L"Named color devicelink")) goto Error;
1025
1026 Original = cmsGetNamedColorList(xform);
1027 if (Original == NULL) goto Error;
1028
1029 nColors = cmsNamedColorCount(ContextID, Original);
1030 nc2 = cmsDupNamedColorList(ContextID, Original);
1031 if (nc2 == NULL) goto Error;
1032
1033 // Colorant count now depends on the output space
1034 nc2 ->ColorantCount = cmsPipelineOutputChannels(ContextID, v ->core->Lut);
1035
1036 // Make sure we have proper formatters
1037 // We only can afford to change formatters if previous transform is at least 16 bits
1038 if (!(v->core->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1039 cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE,
1040 "CreateNamedColorDevicelink needs transforms created with at least 16 bits of precision");
1041 goto Error;
1042 }
1043 InputFormat = TYPE_NAMED_COLOR_INDEX;
1044 OutputFormat = FLOAT_SH(0) |
1045 COLORSPACE_SH(_cmsLCMScolorSpace(ContextID, v ->core->ExitColorSpace)) |
1046 BYTES_SH(2) |
1047 CHANNELS_SH(cmsChannelsOfColorSpace(ContextID, v ->core->ExitColorSpace));
1048 FromInput = _cmsGetFormatter(ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1049 ToOutput = _cmsGetFormatter(ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1050
1051 if (FromInput == NULL || ToOutput == NULL) {
1052 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1053 goto Error;
1054 }
1055
1056 v ->InputFormat = InputFormat;
1057 v ->OutputFormat = OutputFormat;
1058 v ->FromInput = FromInput;
1059 v ->ToOutput = ToOutput;
1060 _cmsFindFormatter(v, InputFormat, OutputFormat, v->core->dwOriginalFlags);
1061
1062 // Apply the transfor to colorants.
1063 for (i=0; i < nColors; i++) {
1064 cmsDoTransform(ContextID, xform, &i, nc2 ->List[i].DeviceColorant, 1);
1065 }
1066
1067 if (!cmsWriteTag(ContextID, hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
1068 cmsFreeNamedColorList(ContextID, nc2);
1069
1070 return hICC;
1071
1072 Error:
1073 if (hICC != NULL) cmsCloseProfile(ContextID, hICC);
1074 return NULL;
1075 }
1076
1077
1078 // This structure holds information about which MPU can be stored on a profile based on the version
1079
1080 typedef struct {
1081 cmsBool IsV4; // Is a V4 tag?
1082 cmsTagSignature RequiredTag; // Set to 0 for both types
1083 cmsTagTypeSignature LutType; // The LUT type
1084 int nTypes; // Number of types (up to 5)
1085 cmsStageSignature MpeTypes[5]; // 5 is the maximum number
1086
1087 } cmsAllowedLUT;
1088
1089 #define cmsSig0 ((cmsTagSignature) 0)
1090
1091 static const cmsAllowedLUT AllowedLUTTypes[] = {
1092
1093 { FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1094 { FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1095 { FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
1096 { TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
1097 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
1098 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1099 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1100 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }},
1101 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1102 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
1103 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
1104 };
1105
1106 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
1107
1108 // Check a single entry
1109 static
1110 cmsBool CheckOne(cmsContext ContextID, const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
1111 {
1112 cmsStage* mpe;
1113 int n;
1114
1115 for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
1116
1117 if (n >= Tab ->nTypes) return FALSE;
1118 if (cmsStageType(ContextID, mpe) != Tab ->MpeTypes[n]) return FALSE;
1119 }
1120
1121 return (n == Tab ->nTypes);
1122 }
1123
1124
1125 static
1126 const cmsAllowedLUT* FindCombination(cmsContext ContextID, const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1127 {
1128 cmsUInt32Number n;
1129
1130 for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1131
1132 const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1133
1134 if (IsV4 ^ Tab -> IsV4) continue;
1135 if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1136
1137 if (CheckOne(ContextID, Tab, Lut)) return Tab;
1138 }
1139
1140 return NULL;
1141 }
1142
1143
1144 // Does convert a transform into a device link profile
1145 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsContext ContextID, cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1146 {
1147 cmsHPROFILE hProfile = NULL;
1148 cmsUInt32Number FrmIn, FrmOut;
1149 cmsInt32Number ChansIn, ChansOut;
1150 int ColorSpaceBitsIn, ColorSpaceBitsOut;
1151 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1152 cmsPipeline* LUT = NULL;
1153 cmsStage* mpe;
1154 const cmsAllowedLUT* AllowedLUT;
1155 cmsTagSignature DestinationTag;
1156 cmsProfileClassSignature deviceClass;
1157
1158 _cmsAssert(hTransform != NULL);
1159
1160 // Check if the pipeline holding is valid
1161 if (xform->core->Lut == NULL) return NULL;
1162
1163 // Get the first mpe to check for named color
1164 mpe = cmsPipelineGetPtrToFirstStage(ContextID, xform ->core->Lut);
1165
1166 // Check if is a named color transform
1167 if (mpe != NULL) {
1168
1169 if (cmsStageType(ContextID, mpe) == cmsSigNamedColorElemType) {
1170 return CreateNamedColorDevicelink(ContextID, hTransform);
1171 }
1172 }
1173
1174 // First thing to do is to get a copy of the transformation
1175 LUT = cmsPipelineDup(ContextID, xform ->core->Lut);
1176 if (LUT == NULL) return NULL;
1177
1178 // Time to fix the Lab2/Lab4 issue.
1179 if ((xform ->core->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1180
1181 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1182 goto Error;
1183 }
1184
1185 // On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments
1186 if ((xform ->core->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1187
1188 dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
1189 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1190 goto Error;
1191 }
1192
1193
1194 hProfile = cmsCreateProfilePlaceholder(ContextID);
1195 if (!hProfile) goto Error; // can't allocate
1196
1197 cmsSetProfileVersion(ContextID, hProfile, Version);
1198
1199 FixColorSpaces(ContextID, hProfile, xform ->core->EntryColorSpace, xform ->core->ExitColorSpace, dwFlags);
1200
1201 // Optimize the LUT and precalculate a devicelink
1202
1203 ChansIn = cmsChannelsOfColorSpace(ContextID, xform -> core->EntryColorSpace);
1204 ChansOut = cmsChannelsOfColorSpace(ContextID, xform -> core->ExitColorSpace);
1205
1206 ColorSpaceBitsIn = _cmsLCMScolorSpace(ContextID, xform ->core->EntryColorSpace);
1207 ColorSpaceBitsOut = _cmsLCMScolorSpace(ContextID, xform ->core->ExitColorSpace);
1208
1209 FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1210 FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1211
1212 deviceClass = cmsGetDeviceClass(ContextID, hProfile);
1213
1214 if (deviceClass == cmsSigOutputClass)
1215 DestinationTag = cmsSigBToA0Tag;
1216 else
1217 DestinationTag = cmsSigAToB0Tag;
1218
1219 // Check if the profile/version can store the result
1220 if (dwFlags & cmsFLAGS_FORCE_CLUT)
1221 AllowedLUT = NULL;
1222 else
1223 AllowedLUT = FindCombination(ContextID, LUT, Version >= 4.0, DestinationTag);
1224
1225 if (AllowedLUT == NULL) {
1226
1227 // Try to optimize
1228 _cmsOptimizePipeline(ContextID, &LUT, xform->core->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1229 AllowedLUT = FindCombination(ContextID, LUT, Version >= 4.0, DestinationTag);
1230
1231 }
1232
1233 // If no way, then force CLUT that for sure can be written
1234 if (AllowedLUT == NULL) {
1235
1236 cmsStage* FirstStage;
1237 cmsStage* LastStage;
1238
1239 dwFlags |= cmsFLAGS_FORCE_CLUT;
1240 _cmsOptimizePipeline(ContextID, &LUT, xform->core->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1241
1242 // Put identity curves if needed
1243 FirstStage = cmsPipelineGetPtrToFirstStage(ContextID, LUT);
1244 if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType)
1245 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1246 goto Error;
1247
1248 LastStage = cmsPipelineGetPtrToLastStage(ContextID, LUT);
1249 if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType)
1250 if (!cmsPipelineInsertStage(ContextID, LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1251 goto Error;
1252
1253 AllowedLUT = FindCombination(ContextID, LUT, Version >= 4.0, DestinationTag);
1254 }
1255
1256 // Somethings is wrong...
1257 if (AllowedLUT == NULL) {
1258 goto Error;
1259 }
1260
1261
1262 if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1263 cmsPipelineSetSaveAs8bitsFlag(ContextID, LUT, TRUE);
1264
1265 // Tag profile with information
1266 if (!SetTextTags(ContextID, hProfile, L"devicelink")) goto Error;
1267
1268 // Store result
1269 if (!cmsWriteTag(ContextID, hProfile, DestinationTag, LUT)) goto Error;
1270
1271
1272 if (xform->core->InputColorant != NULL) {
1273 if (!cmsWriteTag(ContextID, hProfile, cmsSigColorantTableTag, xform->core->InputColorant)) goto Error;
1274 }
1275
1276 if (xform->core->OutputColorant != NULL) {
1277 if (!cmsWriteTag(ContextID, hProfile, cmsSigColorantTableOutTag, xform->core->OutputColorant)) goto Error;
1278 }
1279
1280 if ((deviceClass == cmsSigLinkClass) && (xform ->core->Sequence != NULL)) {
1281 if (!_cmsWriteProfileSequence(ContextID, hProfile, xform ->core->Sequence)) goto Error;
1282 }
1283
1284 // Set the white point
1285 if (deviceClass == cmsSigInputClass) {
1286 if (!cmsWriteTag(ContextID, hProfile, cmsSigMediaWhitePointTag, &xform->core->EntryWhitePoint)) goto Error;
1287 }
1288 else {
1289 if (!cmsWriteTag(ContextID, hProfile, cmsSigMediaWhitePointTag, &xform ->core->ExitWhitePoint)) goto Error;
1290 }
1291
1292
1293 // Per 7.2.15 in spec 4.3
1294 cmsSetHeaderRenderingIntent(ContextID, hProfile, xform->core->RenderingIntent);
1295
1296 cmsPipelineFree(ContextID, LUT);
1297 return hProfile;
1298
1299 Error:
1300 if (LUT != NULL) cmsPipelineFree(ContextID, LUT);
1301 cmsCloseProfile(ContextID, hProfile);
1302 return NULL;
1303 }