Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/native/liblcms/cmsio1.c
41149 views
1
/*
2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3
*
4
* This code is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 2 only, as
6
* published by the Free Software Foundation. Oracle designates this
7
* particular file as subject to the "Classpath" exception as provided
8
* by Oracle in the LICENSE file that accompanied this code.
9
*
10
* This code is distributed in the hope that it will be useful, but WITHOUT
11
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
* version 2 for more details (a copy is included in the LICENSE file that
14
* accompanied this code).
15
*
16
* You should have received a copy of the GNU General Public License version
17
* 2 along with this work; if not, write to the Free Software Foundation,
18
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
*
20
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21
* or visit www.oracle.com if you need additional information or have any
22
* questions.
23
*/
24
25
// This file is available under and governed by the GNU General Public
26
// License version 2 only, as published by the Free Software Foundation.
27
// However, the following notice accompanied the original version of this
28
// file:
29
//
30
//---------------------------------------------------------------------------------
31
//
32
// Little Color Management System
33
// Copyright (c) 1998-2020 Marti Maria Saguer
34
//
35
// Permission is hereby granted, free of charge, to any person obtaining
36
// a copy of this software and associated documentation files (the "Software"),
37
// to deal in the Software without restriction, including without limitation
38
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
39
// and/or sell copies of the Software, and to permit persons to whom the Software
40
// is furnished to do so, subject to the following conditions:
41
//
42
// The above copyright notice and this permission notice shall be included in
43
// all copies or substantial portions of the Software.
44
//
45
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
46
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
47
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
48
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
49
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
50
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
51
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52
//
53
//---------------------------------------------------------------------------------
54
//
55
56
#include "lcms2_internal.h"
57
58
// Read tags using low-level functions, provides necessary glue code to adapt versions, etc.
59
60
// LUT tags
61
static const cmsTagSignature Device2PCS16[] = {cmsSigAToB0Tag, // Perceptual
62
cmsSigAToB1Tag, // Relative colorimetric
63
cmsSigAToB2Tag, // Saturation
64
cmsSigAToB1Tag }; // Absolute colorimetric
65
66
static const cmsTagSignature Device2PCSFloat[] = {cmsSigDToB0Tag, // Perceptual
67
cmsSigDToB1Tag, // Relative colorimetric
68
cmsSigDToB2Tag, // Saturation
69
cmsSigDToB3Tag }; // Absolute colorimetric
70
71
static const cmsTagSignature PCS2Device16[] = {cmsSigBToA0Tag, // Perceptual
72
cmsSigBToA1Tag, // Relative colorimetric
73
cmsSigBToA2Tag, // Saturation
74
cmsSigBToA1Tag }; // Absolute colorimetric
75
76
static const cmsTagSignature PCS2DeviceFloat[] = {cmsSigBToD0Tag, // Perceptual
77
cmsSigBToD1Tag, // Relative colorimetric
78
cmsSigBToD2Tag, // Saturation
79
cmsSigBToD3Tag }; // Absolute colorimetric
80
81
82
// Factors to convert from 1.15 fixed point to 0..1.0 range and vice-versa
83
#define InpAdj (1.0/MAX_ENCODEABLE_XYZ) // (65536.0/(65535.0*2.0))
84
#define OutpAdj (MAX_ENCODEABLE_XYZ) // ((2.0*65535.0)/65536.0)
85
86
// Several resources for gray conversions.
87
static const cmsFloat64Number GrayInputMatrix[] = { (InpAdj*cmsD50X), (InpAdj*cmsD50Y), (InpAdj*cmsD50Z) };
88
static const cmsFloat64Number OneToThreeInputMatrix[] = { 1, 1, 1 };
89
static const cmsFloat64Number PickYMatrix[] = { 0, (OutpAdj*cmsD50Y), 0 };
90
static const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
91
92
// Get a media white point fixing some issues found in certain old profiles
93
cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile)
94
{
95
cmsCIEXYZ* Tag;
96
97
_cmsAssert(Dest != NULL);
98
99
Tag = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag);
100
101
// If no wp, take D50
102
if (Tag == NULL) {
103
*Dest = *cmsD50_XYZ();
104
return TRUE;
105
}
106
107
// V2 display profiles should give D50
108
if (cmsGetEncodedICCversion(hProfile) < 0x4000000) {
109
110
if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) {
111
*Dest = *cmsD50_XYZ();
112
return TRUE;
113
}
114
}
115
116
// All seems ok
117
*Dest = *Tag;
118
return TRUE;
119
}
120
121
122
// Chromatic adaptation matrix. Fix some issues as well
123
cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile)
124
{
125
cmsMAT3* Tag;
126
127
_cmsAssert(Dest != NULL);
128
129
Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag);
130
131
if (Tag != NULL) {
132
*Dest = *Tag;
133
return TRUE;
134
}
135
136
// No CHAD available, default it to identity
137
_cmsMAT3identity(Dest);
138
139
// V2 display profiles should give D50
140
if (cmsGetEncodedICCversion(hProfile) < 0x4000000) {
141
142
if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) {
143
144
cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag);
145
146
if (White == NULL) {
147
148
_cmsMAT3identity(Dest);
149
return TRUE;
150
}
151
152
return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ());
153
}
154
}
155
156
return TRUE;
157
}
158
159
160
// Auxiliary, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper
161
static
162
cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile)
163
{
164
cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue;
165
166
_cmsAssert(r != NULL);
167
168
PtrRed = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag);
169
PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag);
170
PtrBlue = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag);
171
172
if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL)
173
return FALSE;
174
175
_cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X, PtrBlue -> X);
176
_cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y, PtrBlue -> Y);
177
_cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z, PtrBlue -> Z);
178
179
return TRUE;
180
}
181
182
183
// Gray input pipeline
184
static
185
cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile)
186
{
187
cmsToneCurve *GrayTRC;
188
cmsPipeline* Lut;
189
cmsContext ContextID = cmsGetProfileContextID(hProfile);
190
191
GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag);
192
if (GrayTRC == NULL) return NULL;
193
194
Lut = cmsPipelineAlloc(ContextID, 1, 3);
195
if (Lut == NULL)
196
goto Error;
197
198
if (cmsGetPCS(hProfile) == cmsSigLabData) {
199
200
// In this case we implement the profile as an identity matrix plus 3 tone curves
201
cmsUInt16Number Zero[2] = { 0x8080, 0x8080 };
202
cmsToneCurve* EmptyTab;
203
cmsToneCurve* LabCurves[3];
204
205
EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
206
207
if (EmptyTab == NULL)
208
goto Error;
209
210
LabCurves[0] = GrayTRC;
211
LabCurves[1] = EmptyTab;
212
LabCurves[2] = EmptyTab;
213
214
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)) ||
215
!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) {
216
cmsFreeToneCurve(EmptyTab);
217
goto Error;
218
}
219
220
cmsFreeToneCurve(EmptyTab);
221
222
}
223
else {
224
225
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)) ||
226
!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL)))
227
goto Error;
228
}
229
230
return Lut;
231
232
Error:
233
cmsPipelineFree(Lut);
234
return NULL;
235
}
236
237
// RGB Matrix shaper
238
static
239
cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile)
240
{
241
cmsPipeline* Lut;
242
cmsMAT3 Mat;
243
cmsToneCurve *Shapes[3];
244
cmsContext ContextID = cmsGetProfileContextID(hProfile);
245
int i, j;
246
247
if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL;
248
249
// XYZ PCS in encoded in 1.15 format, and the matrix output comes in 0..0xffff range, so
250
// we need to adjust the output by a factor of (0x10000/0xffff) to put data in
251
// a 1.16 range, and then a >> 1 to obtain 1.15. The total factor is (65536.0)/(65535.0*2)
252
253
for (i=0; i < 3; i++)
254
for (j=0; j < 3; j++)
255
Mat.v[i].n[j] *= InpAdj;
256
257
258
Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag);
259
Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag);
260
Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag);
261
262
if (!Shapes[0] || !Shapes[1] || !Shapes[2])
263
return NULL;
264
265
Lut = cmsPipelineAlloc(ContextID, 3, 3);
266
if (Lut != NULL) {
267
268
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) ||
269
!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL)))
270
goto Error;
271
272
// Note that it is certainly possible a single profile would have a LUT based
273
// tag for output working in lab and a matrix-shaper for the fallback cases.
274
// This is not allowed by the spec, but this code is tolerant to those cases
275
if (cmsGetPCS(hProfile) == cmsSigLabData) {
276
277
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocXYZ2Lab(ContextID)))
278
goto Error;
279
}
280
281
}
282
283
return Lut;
284
285
Error:
286
cmsPipelineFree(Lut);
287
return NULL;
288
}
289
290
291
292
// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
293
static
294
cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
295
{
296
cmsContext ContextID = cmsGetProfileContextID(hProfile);
297
cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
298
cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile);
299
cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
300
301
if (Lut == NULL) return NULL;
302
303
// input and output of transform are in lcms 0..1 encoding. If XYZ or Lab spaces are used,
304
// these need to be normalized into the appropriate ranges (Lab = 100,0,0, XYZ=1.0,1.0,1.0)
305
if ( spc == cmsSigLabData)
306
{
307
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
308
goto Error;
309
}
310
else if (spc == cmsSigXYZData)
311
{
312
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
313
goto Error;
314
}
315
316
if ( PCS == cmsSigLabData)
317
{
318
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
319
goto Error;
320
}
321
else if( PCS == cmsSigXYZData)
322
{
323
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
324
goto Error;
325
}
326
327
return Lut;
328
329
Error:
330
cmsPipelineFree(Lut);
331
return NULL;
332
}
333
334
335
// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc
336
// is adjusted here in order to create a LUT that takes care of all those details.
337
// We add intent = 0xffffffff as a way to read matrix shaper always, no matter of other LUT
338
cmsPipeline* CMSEXPORT _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
339
{
340
cmsTagTypeSignature OriginalType;
341
cmsTagSignature tag16;
342
cmsTagSignature tagFloat;
343
cmsContext ContextID = cmsGetProfileContextID(hProfile);
344
345
// On named color, take the appropriate tag
346
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
347
348
cmsPipeline* Lut;
349
cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag);
350
351
if (nc == NULL) return NULL;
352
353
Lut = cmsPipelineAlloc(ContextID, 0, 0);
354
if (Lut == NULL) {
355
cmsFreeNamedColorList(nc);
356
return NULL;
357
}
358
359
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) ||
360
!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) {
361
cmsPipelineFree(Lut);
362
return NULL;
363
}
364
return Lut;
365
}
366
367
// This is an attempt to reuse this function to retrieve the matrix-shaper as pipeline no
368
// matter other LUT are present and have precedence. Intent = 0xffffffff can be used for that.
369
if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) {
370
371
tag16 = Device2PCS16[Intent];
372
tagFloat = Device2PCSFloat[Intent];
373
374
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
375
376
// Floating point LUT are always V4, but the encoding range is no
377
// longer 0..1.0, so we need to add an stage depending on the color space
378
return _cmsReadFloatInputTag(hProfile, tagFloat);
379
}
380
381
// Revert to perceptual if no tag is found
382
if (!cmsIsTag(hProfile, tag16)) {
383
tag16 = Device2PCS16[0];
384
}
385
386
if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
387
388
// Check profile version and LUT type. Do the necessary adjustments if needed
389
390
// First read the tag
391
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
392
if (Lut == NULL) return NULL;
393
394
// After reading it, we have now info about the original type
395
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
396
397
// The profile owns the Lut, so we need to copy it
398
Lut = cmsPipelineDup(Lut);
399
400
// We need to adjust data only for Lab16 on output
401
if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
402
return Lut;
403
404
// If the input is Lab, add also a conversion at the begin
405
if (cmsGetColorSpace(hProfile) == cmsSigLabData &&
406
!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
407
goto Error;
408
409
// Add a matrix for conversion V2 to V4 Lab PCS
410
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
411
goto Error;
412
413
return Lut;
414
Error:
415
cmsPipelineFree(Lut);
416
return NULL;
417
}
418
}
419
420
// Lut was not found, try to create a matrix-shaper
421
422
// Check if this is a grayscale profile.
423
if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
424
425
// if so, build appropriate conversion tables.
426
// The tables are the PCS iluminant, scaled across GrayTRC
427
return BuildGrayInputMatrixPipeline(hProfile);
428
}
429
430
// Not gray, create a normal matrix-shaper
431
return BuildRGBInputMatrixShaper(hProfile);
432
}
433
434
// ---------------------------------------------------------------------------------------------------------------
435
436
// Gray output pipeline.
437
// XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be
438
// given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve.
439
// The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well.
440
441
static
442
cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile)
443
{
444
cmsToneCurve *GrayTRC, *RevGrayTRC;
445
cmsPipeline* Lut;
446
cmsContext ContextID = cmsGetProfileContextID(hProfile);
447
448
GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag);
449
if (GrayTRC == NULL) return NULL;
450
451
RevGrayTRC = cmsReverseToneCurve(GrayTRC);
452
if (RevGrayTRC == NULL) return NULL;
453
454
Lut = cmsPipelineAlloc(ContextID, 3, 1);
455
if (Lut == NULL) {
456
cmsFreeToneCurve(RevGrayTRC);
457
return NULL;
458
}
459
460
if (cmsGetPCS(hProfile) == cmsSigLabData) {
461
462
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
463
goto Error;
464
}
465
else {
466
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL)))
467
goto Error;
468
}
469
470
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC)))
471
goto Error;
472
473
cmsFreeToneCurve(RevGrayTRC);
474
return Lut;
475
476
Error:
477
cmsFreeToneCurve(RevGrayTRC);
478
cmsPipelineFree(Lut);
479
return NULL;
480
}
481
482
483
static
484
cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile)
485
{
486
cmsPipeline* Lut;
487
cmsToneCurve *Shapes[3], *InvShapes[3];
488
cmsMAT3 Mat, Inv;
489
int i, j;
490
cmsContext ContextID = cmsGetProfileContextID(hProfile);
491
492
if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile))
493
return NULL;
494
495
if (!_cmsMAT3inverse(&Mat, &Inv))
496
return NULL;
497
498
// XYZ PCS in encoded in 1.15 format, and the matrix input should come in 0..0xffff range, so
499
// we need to adjust the input by a << 1 to obtain a 1.16 fixed and then by a factor of
500
// (0xffff/0x10000) to put data in 0..0xffff range. Total factor is (2.0*65535.0)/65536.0;
501
502
for (i=0; i < 3; i++)
503
for (j=0; j < 3; j++)
504
Inv.v[i].n[j] *= OutpAdj;
505
506
Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag);
507
Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag);
508
Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag);
509
510
if (!Shapes[0] || !Shapes[1] || !Shapes[2])
511
return NULL;
512
513
InvShapes[0] = cmsReverseToneCurve(Shapes[0]);
514
InvShapes[1] = cmsReverseToneCurve(Shapes[1]);
515
InvShapes[2] = cmsReverseToneCurve(Shapes[2]);
516
517
if (!InvShapes[0] || !InvShapes[1] || !InvShapes[2]) {
518
return NULL;
519
}
520
521
Lut = cmsPipelineAlloc(ContextID, 3, 3);
522
if (Lut != NULL) {
523
524
// Note that it is certainly possible a single profile would have a LUT based
525
// tag for output working in lab and a matrix-shaper for the fallback cases.
526
// This is not allowed by the spec, but this code is tolerant to those cases
527
if (cmsGetPCS(hProfile) == cmsSigLabData) {
528
529
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLab2XYZ(ContextID)))
530
goto Error;
531
}
532
533
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)) ||
534
!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes)))
535
goto Error;
536
}
537
538
cmsFreeToneCurveTriple(InvShapes);
539
return Lut;
540
Error:
541
cmsFreeToneCurveTriple(InvShapes);
542
cmsPipelineFree(Lut);
543
return NULL;
544
}
545
546
547
// Change CLUT interpolation to trilinear
548
static
549
void ChangeInterpolationToTrilinear(cmsPipeline* Lut)
550
{
551
cmsStage* Stage;
552
553
for (Stage = cmsPipelineGetPtrToFirstStage(Lut);
554
Stage != NULL;
555
Stage = cmsStageNext(Stage)) {
556
557
if (cmsStageType(Stage) == cmsSigCLutElemType) {
558
559
_cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data;
560
561
CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR;
562
_cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params);
563
}
564
}
565
}
566
567
568
// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
569
static
570
cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
571
{
572
cmsContext ContextID = cmsGetProfileContextID(hProfile);
573
cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
574
cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
575
cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile);
576
577
if (Lut == NULL) return NULL;
578
579
// If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding,
580
// and since the formatter has already accommodated to 0..1.0, we should undo this change
581
if ( PCS == cmsSigLabData)
582
{
583
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
584
goto Error;
585
}
586
else
587
if (PCS == cmsSigXYZData)
588
{
589
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
590
goto Error;
591
}
592
593
// the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline
594
if ( dataSpace == cmsSigLabData)
595
{
596
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
597
goto Error;
598
}
599
else if (dataSpace == cmsSigXYZData)
600
{
601
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
602
goto Error;
603
}
604
605
return Lut;
606
607
Error:
608
cmsPipelineFree(Lut);
609
return NULL;
610
}
611
612
// Create an output MPE LUT from agiven profile. Version mismatches are handled here
613
cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
614
{
615
cmsTagTypeSignature OriginalType;
616
cmsTagSignature tag16;
617
cmsTagSignature tagFloat;
618
cmsContext ContextID = cmsGetProfileContextID(hProfile);
619
620
621
if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) {
622
623
tag16 = PCS2Device16[Intent];
624
tagFloat = PCS2DeviceFloat[Intent];
625
626
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
627
628
// Floating point LUT are always V4
629
return _cmsReadFloatOutputTag(hProfile, tagFloat);
630
}
631
632
// Revert to perceptual if no tag is found
633
if (!cmsIsTag(hProfile, tag16)) {
634
tag16 = PCS2Device16[0];
635
}
636
637
if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
638
639
// Check profile version and LUT type. Do the necessary adjustments if needed
640
641
// First read the tag
642
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
643
if (Lut == NULL) return NULL;
644
645
// After reading it, we have info about the original type
646
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
647
648
// The profile owns the Lut, so we need to copy it
649
Lut = cmsPipelineDup(Lut);
650
if (Lut == NULL) return NULL;
651
652
// Now it is time for a controversial stuff. I found that for 3D LUTS using
653
// Lab used as indexer space, trilinear interpolation should be used
654
if (cmsGetPCS(hProfile) == cmsSigLabData)
655
ChangeInterpolationToTrilinear(Lut);
656
657
// We need to adjust data only for Lab and Lut16 type
658
if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
659
return Lut;
660
661
// Add a matrix for conversion V4 to V2 Lab PCS
662
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
663
goto Error;
664
665
// If the output is Lab, add also a conversion at the end
666
if (cmsGetColorSpace(hProfile) == cmsSigLabData)
667
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
668
goto Error;
669
670
return Lut;
671
Error:
672
cmsPipelineFree(Lut);
673
return NULL;
674
}
675
}
676
677
// Lut not found, try to create a matrix-shaper
678
679
// Check if this is a grayscale profile.
680
if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
681
682
// if so, build appropriate conversion tables.
683
// The tables are the PCS iluminant, scaled across GrayTRC
684
return BuildGrayOutputPipeline(hProfile);
685
}
686
687
// Not gray, create a normal matrix-shaper, which only operates in XYZ space
688
return BuildRGBOutputMatrixShaper(hProfile);
689
}
690
691
// ---------------------------------------------------------------------------------------------------------------
692
693
// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded
694
static
695
cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
696
{
697
cmsContext ContextID = cmsGetProfileContextID(hProfile);
698
cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat));
699
cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
700
cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile);
701
702
if (Lut == NULL) return NULL;
703
704
if (spc == cmsSigLabData)
705
{
706
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
707
goto Error;
708
}
709
else
710
if (spc == cmsSigXYZData)
711
{
712
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
713
goto Error;
714
}
715
716
if (PCS == cmsSigLabData)
717
{
718
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
719
goto Error;
720
}
721
else
722
if (PCS == cmsSigXYZData)
723
{
724
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
725
goto Error;
726
}
727
728
return Lut;
729
Error:
730
cmsPipelineFree(Lut);
731
return NULL;
732
}
733
734
// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The
735
// tag name here may default to AToB0
736
cmsPipeline* CMSEXPORT _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
737
{
738
cmsPipeline* Lut;
739
cmsTagTypeSignature OriginalType;
740
cmsTagSignature tag16;
741
cmsTagSignature tagFloat;
742
cmsContext ContextID = cmsGetProfileContextID(hProfile);
743
744
745
if (Intent > INTENT_ABSOLUTE_COLORIMETRIC)
746
return NULL;
747
748
tag16 = Device2PCS16[Intent];
749
tagFloat = Device2PCSFloat[Intent];
750
751
// On named color, take the appropriate tag
752
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
753
754
cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*)cmsReadTag(hProfile, cmsSigNamedColor2Tag);
755
756
if (nc == NULL) return NULL;
757
758
Lut = cmsPipelineAlloc(ContextID, 0, 0);
759
if (Lut == NULL)
760
goto Error;
761
762
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE)))
763
goto Error;
764
765
if (cmsGetColorSpace(hProfile) == cmsSigLabData)
766
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
767
goto Error;
768
769
return Lut;
770
Error:
771
cmsPipelineFree(Lut);
772
cmsFreeNamedColorList(nc);
773
return NULL;
774
}
775
776
777
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
778
779
// Floating point LUT are always V
780
return _cmsReadFloatDevicelinkTag(hProfile, tagFloat);
781
}
782
783
tagFloat = Device2PCSFloat[0];
784
if (cmsIsTag(hProfile, tagFloat)) {
785
786
return cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat));
787
}
788
789
if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
790
791
tag16 = Device2PCS16[0];
792
if (!cmsIsTag(hProfile, tag16)) return NULL;
793
}
794
795
// Check profile version and LUT type. Do the necessary adjustments if needed
796
797
// Read the tag
798
Lut = (cmsPipeline*)cmsReadTag(hProfile, tag16);
799
if (Lut == NULL) return NULL;
800
801
// The profile owns the Lut, so we need to copy it
802
Lut = cmsPipelineDup(Lut);
803
if (Lut == NULL) return NULL;
804
805
// Now it is time for a controversial stuff. I found that for 3D LUTS using
806
// Lab used as indexer space, trilinear interpolation should be used
807
if (cmsGetPCS(hProfile) == cmsSigLabData)
808
ChangeInterpolationToTrilinear(Lut);
809
810
// After reading it, we have info about the original type
811
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
812
813
// We need to adjust data for Lab16 on output
814
if (OriginalType != cmsSigLut16Type) return Lut;
815
816
// Here it is possible to get Lab on both sides
817
818
if (cmsGetColorSpace(hProfile) == cmsSigLabData) {
819
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
820
goto Error2;
821
}
822
823
if (cmsGetPCS(hProfile) == cmsSigLabData) {
824
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
825
goto Error2;
826
}
827
828
return Lut;
829
830
Error2:
831
cmsPipelineFree(Lut);
832
return NULL;
833
}
834
835
// ---------------------------------------------------------------------------------------------------------------
836
837
// Returns TRUE if the profile is implemented as matrix-shaper
838
cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile)
839
{
840
switch (cmsGetColorSpace(hProfile)) {
841
842
case cmsSigGrayData:
843
844
return cmsIsTag(hProfile, cmsSigGrayTRCTag);
845
846
case cmsSigRgbData:
847
848
return (cmsIsTag(hProfile, cmsSigRedColorantTag) &&
849
cmsIsTag(hProfile, cmsSigGreenColorantTag) &&
850
cmsIsTag(hProfile, cmsSigBlueColorantTag) &&
851
cmsIsTag(hProfile, cmsSigRedTRCTag) &&
852
cmsIsTag(hProfile, cmsSigGreenTRCTag) &&
853
cmsIsTag(hProfile, cmsSigBlueTRCTag));
854
855
default:
856
857
return FALSE;
858
}
859
}
860
861
// Returns TRUE if the intent is implemented as CLUT
862
cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection)
863
{
864
const cmsTagSignature* TagTable;
865
866
// For devicelinks, the supported intent is that one stated in the header
867
if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) {
868
return (cmsGetHeaderRenderingIntent(hProfile) == Intent);
869
}
870
871
switch (UsedDirection) {
872
873
case LCMS_USED_AS_INPUT: TagTable = Device2PCS16; break;
874
case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device16; break;
875
876
// For proofing, we need rel. colorimetric in output. Let's do some recursion
877
case LCMS_USED_AS_PROOF:
878
return cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) &&
879
cmsIsIntentSupported(hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT);
880
881
default:
882
cmsSignalError(cmsGetProfileContextID(hProfile), cmsERROR_RANGE, "Unexpected direction (%d)", UsedDirection);
883
return FALSE;
884
}
885
886
return cmsIsTag(hProfile, TagTable[Intent]);
887
888
}
889
890
891
// Return info about supported intents
892
cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile,
893
cmsUInt32Number Intent, cmsUInt32Number UsedDirection)
894
{
895
896
if (cmsIsCLUT(hProfile, Intent, UsedDirection)) return TRUE;
897
898
// Is there any matrix-shaper? If so, the intent is supported. This is a bit odd, since V2 matrix shaper
899
// does not fully support relative colorimetric because they cannot deal with non-zero black points, but
900
// many profiles claims that, and this is certainly not true for V4 profiles. Lets answer "yes" no matter
901
// the accuracy would be less than optimal in rel.col and v2 case.
902
903
return cmsIsMatrixShaper(hProfile);
904
}
905
906
907
// ---------------------------------------------------------------------------------------------------------------
908
909
// Read both, profile sequence description and profile sequence id if present. Then combine both to
910
// create qa unique structure holding both. Shame on ICC to store things in such complicated way.
911
cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile)
912
{
913
cmsSEQ* ProfileSeq;
914
cmsSEQ* ProfileId;
915
cmsSEQ* NewSeq;
916
cmsUInt32Number i;
917
918
// Take profile sequence description first
919
ProfileSeq = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceDescTag);
920
921
// Take profile sequence ID
922
ProfileId = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceIdTag);
923
924
if (ProfileSeq == NULL && ProfileId == NULL) return NULL;
925
926
if (ProfileSeq == NULL) return cmsDupProfileSequenceDescription(ProfileId);
927
if (ProfileId == NULL) return cmsDupProfileSequenceDescription(ProfileSeq);
928
929
// We have to mix both together. For that they must agree
930
if (ProfileSeq ->n != ProfileId ->n) return cmsDupProfileSequenceDescription(ProfileSeq);
931
932
NewSeq = cmsDupProfileSequenceDescription(ProfileSeq);
933
934
// Ok, proceed to the mixing
935
if (NewSeq != NULL) {
936
for (i=0; i < ProfileSeq ->n; i++) {
937
938
memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID));
939
NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description);
940
}
941
}
942
return NewSeq;
943
}
944
945
// Dump the contents of profile sequence in both tags (if v4 available)
946
cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq)
947
{
948
if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE;
949
950
if (cmsGetEncodedICCversion(hProfile) >= 0x4000000) {
951
952
if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE;
953
}
954
955
return TRUE;
956
}
957
958
959
// Auxiliary, read and duplicate a MLU if found.
960
static
961
cmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig)
962
{
963
cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig);
964
if (mlu == NULL) return NULL;
965
966
return cmsMLUdup(mlu);
967
}
968
969
// Create a sequence description out of an array of profiles
970
cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[])
971
{
972
cmsUInt32Number i;
973
cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles);
974
975
if (seq == NULL) return NULL;
976
977
for (i=0; i < nProfiles; i++) {
978
979
cmsPSEQDESC* ps = &seq ->seq[i];
980
cmsHPROFILE h = hProfiles[i];
981
cmsTechnologySignature* techpt;
982
983
cmsGetHeaderAttributes(h, &ps ->attributes);
984
cmsGetHeaderProfileID(h, ps ->ProfileID.ID8);
985
ps ->deviceMfg = cmsGetHeaderManufacturer(h);
986
ps ->deviceModel = cmsGetHeaderModel(h);
987
988
techpt = (cmsTechnologySignature*) cmsReadTag(h, cmsSigTechnologyTag);
989
if (techpt == NULL)
990
ps ->technology = (cmsTechnologySignature) 0;
991
else
992
ps ->technology = *techpt;
993
994
ps ->Manufacturer = GetMLUFromProfile(h, cmsSigDeviceMfgDescTag);
995
ps ->Model = GetMLUFromProfile(h, cmsSigDeviceModelDescTag);
996
ps ->Description = GetMLUFromProfile(h, cmsSigProfileDescriptionTag);
997
998
}
999
1000
return seq;
1001
}
1002
1003
// -------------------------------------------------------------------------------------------------------------------
1004
1005
1006
static
1007
const cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info)
1008
{
1009
cmsTagSignature sig;
1010
1011
switch (Info) {
1012
1013
case cmsInfoDescription:
1014
sig = cmsSigProfileDescriptionTag;
1015
break;
1016
1017
case cmsInfoManufacturer:
1018
sig = cmsSigDeviceMfgDescTag;
1019
break;
1020
1021
case cmsInfoModel:
1022
sig = cmsSigDeviceModelDescTag;
1023
break;
1024
1025
case cmsInfoCopyright:
1026
sig = cmsSigCopyrightTag;
1027
break;
1028
1029
default: return NULL;
1030
}
1031
1032
1033
return (cmsMLU*) cmsReadTag(hProfile, sig);
1034
}
1035
1036
1037
1038
cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info,
1039
const char LanguageCode[3], const char CountryCode[3],
1040
wchar_t* Buffer, cmsUInt32Number BufferSize)
1041
{
1042
const cmsMLU* mlu = GetInfo(hProfile, Info);
1043
if (mlu == NULL) return 0;
1044
1045
return cmsMLUgetWide(mlu, LanguageCode, CountryCode, Buffer, BufferSize);
1046
}
1047
1048
1049
cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info,
1050
const char LanguageCode[3], const char CountryCode[3],
1051
char* Buffer, cmsUInt32Number BufferSize)
1052
{
1053
const cmsMLU* mlu = GetInfo(hProfile, Info);
1054
if (mlu == NULL) return 0;
1055
1056
return cmsMLUgetASCII(mlu, LanguageCode, CountryCode, Buffer, BufferSize);
1057
}
1058
1059