Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/native/liblcms/cmsps2.c
41152 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
// PostScript ColorRenderingDictionary and ColorSpaceArray
59
60
61
#define MAXPSCOLS 60 // Columns on tables
62
63
/*
64
Implementation
65
--------------
66
67
PostScript does use XYZ as its internal PCS. But since PostScript
68
interpolation tables are limited to 8 bits, I use Lab as a way to
69
improve the accuracy, favoring perceptual results. So, for the creation
70
of each CRD, CSA the profiles are converted to Lab via a device
71
link between profile -> Lab or Lab -> profile. The PS code necessary to
72
convert Lab <-> XYZ is also included.
73
74
75
76
Color Space Arrays (CSA)
77
==================================================================================
78
79
In order to obtain precision, code chooses between three ways to implement
80
the device -> XYZ transform. These cases identifies monochrome profiles (often
81
implemented as a set of curves), matrix-shaper and Pipeline-based.
82
83
Monochrome
84
-----------
85
86
This is implemented as /CIEBasedA CSA. The prelinearization curve is
87
placed into /DecodeA section, and matrix equals to D50. Since here is
88
no interpolation tables, I do the conversion directly to XYZ
89
90
NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT
91
flag is forced on such profiles.
92
93
[ /CIEBasedA
94
<<
95
/DecodeA { transfer function } bind
96
/MatrixA [D50]
97
/RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]
98
/WhitePoint [D50]
99
/BlackPoint [BP]
100
/RenderingIntent (intent)
101
>>
102
]
103
104
On simpler profiles, the PCS is already XYZ, so no conversion is required.
105
106
107
Matrix-shaper based
108
-------------------
109
110
This is implemented both with /CIEBasedABC or /CIEBasedDEF depending on the
111
profile implementation. Since here there are no interpolation tables, I do
112
the conversion directly to XYZ
113
114
115
116
[ /CIEBasedABC
117
<<
118
/DecodeABC [ {transfer1} {transfer2} {transfer3} ]
119
/MatrixABC [Matrix]
120
/RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]
121
/DecodeLMN [ { / 2} dup dup ]
122
/WhitePoint [D50]
123
/BlackPoint [BP]
124
/RenderingIntent (intent)
125
>>
126
]
127
128
129
CLUT based
130
----------
131
132
Lab is used in such cases.
133
134
[ /CIEBasedDEF
135
<<
136
/DecodeDEF [ <prelinearization> ]
137
/Table [ p p p [<...>]]
138
/RangeABC [ 0 1 0 1 0 1]
139
/DecodeABC[ <postlinearization> ]
140
/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]
141
% -128/500 1+127/500 0 1 -127/200 1+128/200
142
/MatrixABC [ 1 1 1 1 0 0 0 0 -1]
143
/WhitePoint [D50]
144
/BlackPoint [BP]
145
/RenderingIntent (intent)
146
]
147
148
149
Color Rendering Dictionaries (CRD)
150
==================================
151
These are always implemented as CLUT, and always are using Lab. Since CRD are expected to
152
be used as resources, the code adds the definition as well.
153
154
<<
155
/ColorRenderingType 1
156
/WhitePoint [ D50 ]
157
/BlackPoint [BP]
158
/MatrixPQR [ Bradford ]
159
/RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ]
160
/TransformPQR [
161
{4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind
162
{4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind
163
{4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind
164
]
165
/MatrixABC <...>
166
/EncodeABC <...>
167
/RangeABC <.. used for XYZ -> Lab>
168
/EncodeLMN
169
/RenderTable [ p p p [<...>]]
170
171
/RenderingIntent (Perceptual)
172
>>
173
/Current exch /ColorRendering defineresource pop
174
175
176
The following stages are used to convert from XYZ to Lab
177
--------------------------------------------------------
178
179
Input is given at LMN stage on X, Y, Z
180
181
Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn)
182
183
/EncodeLMN [
184
185
{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
186
{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
187
{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
188
189
]
190
191
192
MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn)
193
194
| 0 1 0|
195
| 1 -1 0|
196
| 0 1 -1|
197
198
/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]
199
200
EncodeABC finally gives Lab values.
201
202
/EncodeABC [
203
{ 116 mul 16 sub 100 div } bind
204
{ 500 mul 128 add 255 div } bind
205
{ 200 mul 128 add 255 div } bind
206
]
207
208
The following stages are used to convert Lab to XYZ
209
----------------------------------------------------
210
211
/RangeABC [ 0 1 0 1 0 1]
212
/DecodeABC [ { 100 mul 16 add 116 div } bind
213
{ 255 mul 128 sub 500 div } bind
214
{ 255 mul 128 sub 200 div } bind
215
]
216
217
/MatrixABC [ 1 1 1 1 0 0 0 0 -1]
218
/DecodeLMN [
219
{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind
220
{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind
221
{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind
222
]
223
224
225
*/
226
227
/*
228
229
PostScript algorithms discussion.
230
=========================================================================================================
231
232
1D interpolation algorithm
233
234
235
1D interpolation (float)
236
------------------------
237
238
val2 = Domain * Value;
239
240
cell0 = (int) floor(val2);
241
cell1 = (int) ceil(val2);
242
243
rest = val2 - cell0;
244
245
y0 = LutTable[cell0] ;
246
y1 = LutTable[cell1] ;
247
248
y = y0 + (y1 - y0) * rest;
249
250
251
252
PostScript code Stack
253
================================================
254
255
{ % v
256
<check 0..1.0>
257
[array] % v tab
258
dup % v tab tab
259
length 1 sub % v tab dom
260
261
3 -1 roll % tab dom v
262
263
mul % tab val2
264
dup % tab val2 val2
265
dup % tab val2 val2 val2
266
floor cvi % tab val2 val2 cell0
267
exch % tab val2 cell0 val2
268
ceiling cvi % tab val2 cell0 cell1
269
270
3 index % tab val2 cell0 cell1 tab
271
exch % tab val2 cell0 tab cell1
272
get % tab val2 cell0 y1
273
274
4 -1 roll % val2 cell0 y1 tab
275
3 -1 roll % val2 y1 tab cell0
276
get % val2 y1 y0
277
278
dup % val2 y1 y0 y0
279
3 1 roll % val2 y0 y1 y0
280
281
sub % val2 y0 (y1-y0)
282
3 -1 roll % y0 (y1-y0) val2
283
dup % y0 (y1-y0) val2 val2
284
floor cvi % y0 (y1-y0) val2 floor(val2)
285
sub % y0 (y1-y0) rest
286
mul % y0 t1
287
add % y
288
65535 div % result
289
290
} bind
291
292
293
*/
294
295
296
// This struct holds the memory block currently being write
297
typedef struct {
298
_cmsStageCLutData* Pipeline;
299
cmsIOHANDLER* m;
300
301
int FirstComponent;
302
int SecondComponent;
303
304
const char* PreMaj;
305
const char* PostMaj;
306
const char* PreMin;
307
const char* PostMin;
308
309
int FixWhite; // Force mapping of pure white
310
311
cmsColorSpaceSignature ColorSpace; // ColorSpace of profile
312
313
314
} cmsPsSamplerCargo;
315
316
static int _cmsPSActualColumn = 0;
317
318
319
// Convert to byte
320
static
321
cmsUInt8Number Word2Byte(cmsUInt16Number w)
322
{
323
return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5);
324
}
325
326
327
// Write a cooked byte
328
static
329
void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b)
330
{
331
_cmsIOPrintf(m, "%02x", b);
332
_cmsPSActualColumn += 2;
333
334
if (_cmsPSActualColumn > MAXPSCOLS) {
335
336
_cmsIOPrintf(m, "\n");
337
_cmsPSActualColumn = 0;
338
}
339
}
340
341
// ----------------------------------------------------------------- PostScript generation
342
343
344
// Removes offending carriage returns
345
346
static
347
char* RemoveCR(const char* txt)
348
{
349
static char Buffer[2048];
350
char* pt;
351
352
strncpy(Buffer, txt, 2047);
353
Buffer[2047] = 0;
354
for (pt = Buffer; *pt; pt++)
355
if (*pt == '\n' || *pt == '\r') *pt = ' ';
356
357
return Buffer;
358
359
}
360
361
static
362
void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile)
363
{
364
time_t timer;
365
cmsMLU *Description, *Copyright;
366
char DescASCII[256], CopyrightASCII[256];
367
368
time(&timer);
369
370
Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag);
371
Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag);
372
373
DescASCII[0] = DescASCII[255] = 0;
374
CopyrightASCII[0] = CopyrightASCII[255] = 0;
375
376
if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255);
377
if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255);
378
379
_cmsIOPrintf(m, "%%!PS-Adobe-3.0\n");
380
_cmsIOPrintf(m, "%%\n");
381
_cmsIOPrintf(m, "%% %s\n", Title);
382
_cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII));
383
_cmsIOPrintf(m, "%% %s\n", RemoveCR(CopyrightASCII));
384
_cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!!
385
_cmsIOPrintf(m, "%%\n");
386
_cmsIOPrintf(m, "%%%%BeginResource\n");
387
388
}
389
390
391
// Emits White & Black point. White point is always D50, Black point is the device
392
// Black point adapted to D50.
393
394
static
395
void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint)
396
{
397
398
_cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X,
399
BlackPoint -> Y,
400
BlackPoint -> Z);
401
402
_cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X,
403
cmsD50_XYZ()->Y,
404
cmsD50_XYZ()->Z);
405
}
406
407
408
static
409
void EmitRangeCheck(cmsIOHANDLER* m)
410
{
411
_cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if "
412
"dup 1.0 gt { pop 1.0 } if ");
413
414
}
415
416
// Does write the intent
417
418
static
419
void EmitIntent(cmsIOHANDLER* m, cmsUInt32Number RenderingIntent)
420
{
421
const char *intent;
422
423
switch (RenderingIntent) {
424
425
case INTENT_PERCEPTUAL: intent = "Perceptual"; break;
426
case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break;
427
case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break;
428
case INTENT_SATURATION: intent = "Saturation"; break;
429
430
default: intent = "Undefined"; break;
431
}
432
433
_cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent );
434
}
435
436
//
437
// Convert L* to Y
438
//
439
// Y = Yn*[ (L* + 16) / 116] ^ 3 if (L*) >= 6 / 29
440
// = Yn*( L* / 116) / 7.787 if (L*) < 6 / 29
441
//
442
443
// Lab -> XYZ, see the discussion above
444
445
static
446
void EmitLab2XYZ(cmsIOHANDLER* m)
447
{
448
_cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n");
449
_cmsIOPrintf(m, "/DecodeABC [\n");
450
_cmsIOPrintf(m, "{100 mul 16 add 116 div } bind\n");
451
_cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n");
452
_cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n");
453
_cmsIOPrintf(m, "]\n");
454
_cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n");
455
_cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n");
456
_cmsIOPrintf(m, "/DecodeLMN [\n");
457
_cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n");
458
_cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n");
459
_cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n");
460
_cmsIOPrintf(m, "]\n");
461
}
462
463
static
464
void EmitSafeGuardBegin(cmsIOHANDLER* m, const char* name)
465
{
466
_cmsIOPrintf(m, "%%LCMS2: Save previous definition of %s on the operand stack\n", name);
467
_cmsIOPrintf(m, "currentdict /%s known { /%s load } { null } ifelse\n", name, name);
468
}
469
470
static
471
void EmitSafeGuardEnd(cmsIOHANDLER* m, const char* name, int depth)
472
{
473
_cmsIOPrintf(m, "%%LCMS2: Restore previous definition of %s\n", name);
474
if (depth > 1) {
475
// cycle topmost items on the stack to bring the previous definition to the front
476
_cmsIOPrintf(m, "%d -1 roll ", depth);
477
}
478
_cmsIOPrintf(m, "dup null eq { pop currentdict /%s undef } { /%s exch def } ifelse\n", name, name);
479
}
480
481
// Outputs a table of words. It does use 16 bits
482
483
static
484
void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table, const char* name)
485
{
486
cmsUInt32Number i;
487
cmsFloat64Number gamma;
488
489
if (Table == NULL) return; // Error
490
491
if (Table ->nEntries <= 0) return; // Empty table
492
493
// Suppress whole if identity
494
if (cmsIsToneCurveLinear(Table)) return;
495
496
// Check if is really an exponential. If so, emit "exp"
497
gamma = cmsEstimateGamma(Table, 0.001);
498
if (gamma > 0) {
499
_cmsIOPrintf(m, "/%s { %g exp } bind def\n", name, gamma);
500
return;
501
}
502
503
EmitSafeGuardBegin(m, "lcms2gammatable");
504
_cmsIOPrintf(m, "/lcms2gammatable [");
505
506
for (i=0; i < Table->nEntries; i++) {
507
if (i % 10 == 0)
508
_cmsIOPrintf(m, "\n ");
509
_cmsIOPrintf(m, "%d ", Table->Table16[i]);
510
}
511
512
_cmsIOPrintf(m, "] def\n");
513
514
515
// Emit interpolation code
516
517
// PostScript code Stack
518
// =============== ========================
519
// v
520
_cmsIOPrintf(m, "/%s {\n ", name);
521
522
// Bounds check
523
EmitRangeCheck(m);
524
525
_cmsIOPrintf(m, "\n //lcms2gammatable "); // v tab
526
_cmsIOPrintf(m, "dup "); // v tab tab
527
_cmsIOPrintf(m, "length 1 sub "); // v tab dom
528
_cmsIOPrintf(m, "3 -1 roll "); // tab dom v
529
_cmsIOPrintf(m, "mul "); // tab val2
530
_cmsIOPrintf(m, "dup "); // tab val2 val2
531
_cmsIOPrintf(m, "dup "); // tab val2 val2 val2
532
_cmsIOPrintf(m, "floor cvi "); // tab val2 val2 cell0
533
_cmsIOPrintf(m, "exch "); // tab val2 cell0 val2
534
_cmsIOPrintf(m, "ceiling cvi "); // tab val2 cell0 cell1
535
_cmsIOPrintf(m, "3 index "); // tab val2 cell0 cell1 tab
536
_cmsIOPrintf(m, "exch "); // tab val2 cell0 tab cell1
537
_cmsIOPrintf(m, "get\n "); // tab val2 cell0 y1
538
_cmsIOPrintf(m, "4 -1 roll "); // val2 cell0 y1 tab
539
_cmsIOPrintf(m, "3 -1 roll "); // val2 y1 tab cell0
540
_cmsIOPrintf(m, "get "); // val2 y1 y0
541
_cmsIOPrintf(m, "dup "); // val2 y1 y0 y0
542
_cmsIOPrintf(m, "3 1 roll "); // val2 y0 y1 y0
543
_cmsIOPrintf(m, "sub "); // val2 y0 (y1-y0)
544
_cmsIOPrintf(m, "3 -1 roll "); // y0 (y1-y0) val2
545
_cmsIOPrintf(m, "dup "); // y0 (y1-y0) val2 val2
546
_cmsIOPrintf(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2)
547
_cmsIOPrintf(m, "sub "); // y0 (y1-y0) rest
548
_cmsIOPrintf(m, "mul "); // y0 t1
549
_cmsIOPrintf(m, "add "); // y
550
_cmsIOPrintf(m, "65535 div\n"); // result
551
552
_cmsIOPrintf(m, "} bind def\n");
553
554
EmitSafeGuardEnd(m, "lcms2gammatable", 1);
555
}
556
557
558
// Compare gamma table
559
560
static
561
cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Number nG1, cmsUInt32Number nG2)
562
{
563
if (nG1 != nG2) return FALSE;
564
return memcmp(g1, g2, nG1 * sizeof(cmsUInt16Number)) == 0;
565
}
566
567
568
// Does write a set of gamma curves
569
570
static
571
void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[], const char* nameprefix)
572
{
573
cmsUInt32Number i;
574
static char buffer[2048];
575
576
for( i=0; i < n; i++ )
577
{
578
if (g[i] == NULL) return; // Error
579
580
if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i-1]->nEntries, g[i]->nEntries)) {
581
582
_cmsIOPrintf(m, "/%s%d /%s%d load def\n", nameprefix, i, nameprefix, i-1);
583
}
584
else {
585
snprintf(buffer, sizeof(buffer), "%s%d", nameprefix, (int) i);
586
buffer[sizeof(buffer)-1] = '\0';
587
Emit1Gamma(m, g[i], buffer);
588
}
589
}
590
591
}
592
593
594
// Following code dumps a LUT onto memory stream
595
596
597
// This is the sampler. Intended to work in SAMPLER_INSPECT mode,
598
// that is, the callback will be called for each knot with
599
//
600
// In[] The grid location coordinates, normalized to 0..ffff
601
// Out[] The Pipeline values, normalized to 0..ffff
602
//
603
// Returning a value other than 0 does terminate the sampling process
604
//
605
// Each row contains Pipeline values for all but first component. So, I
606
// detect row changing by keeping a copy of last value of first
607
// component. -1 is used to mark beginning of whole block.
608
609
static
610
int OutputValueSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
611
{
612
cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;
613
cmsUInt32Number i;
614
615
616
if (sc -> FixWhite) {
617
618
if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8]
619
620
if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&
621
(In[2] >= 0x7800 && In[2] <= 0x8800)) {
622
623
cmsUInt16Number* Black;
624
cmsUInt16Number* White;
625
cmsUInt32Number nOutputs;
626
627
if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))
628
return 0;
629
630
for (i=0; i < nOutputs; i++)
631
Out[i] = White[i];
632
}
633
634
635
}
636
}
637
638
639
// Hadle the parenthesis on rows
640
641
if (In[0] != sc ->FirstComponent) {
642
643
if (sc ->FirstComponent != -1) {
644
645
_cmsIOPrintf(sc ->m, sc ->PostMin);
646
sc ->SecondComponent = -1;
647
_cmsIOPrintf(sc ->m, sc ->PostMaj);
648
}
649
650
// Begin block
651
_cmsPSActualColumn = 0;
652
653
_cmsIOPrintf(sc ->m, sc ->PreMaj);
654
sc ->FirstComponent = In[0];
655
}
656
657
658
if (In[1] != sc ->SecondComponent) {
659
660
if (sc ->SecondComponent != -1) {
661
662
_cmsIOPrintf(sc ->m, sc ->PostMin);
663
}
664
665
_cmsIOPrintf(sc ->m, sc ->PreMin);
666
sc ->SecondComponent = In[1];
667
}
668
669
// Dump table.
670
671
for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {
672
673
cmsUInt16Number wWordOut = Out[i];
674
cmsUInt8Number wByteOut; // Value as byte
675
676
677
// We always deal with Lab4
678
679
wByteOut = Word2Byte(wWordOut);
680
WriteByte(sc -> m, wByteOut);
681
}
682
683
return 1;
684
}
685
686
// Writes a Pipeline on memstream. Could be 8 or 16 bits based
687
688
static
689
void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
690
const char* PostMaj,
691
const char* PreMin,
692
const char* PostMin,
693
int FixWhite,
694
cmsColorSpaceSignature ColorSpace)
695
{
696
cmsUInt32Number i;
697
cmsPsSamplerCargo sc;
698
699
sc.FirstComponent = -1;
700
sc.SecondComponent = -1;
701
sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
702
sc.m = m;
703
sc.PreMaj = PreMaj;
704
sc.PostMaj= PostMaj;
705
706
sc.PreMin = PreMin;
707
sc.PostMin = PostMin;
708
sc.FixWhite = FixWhite;
709
sc.ColorSpace = ColorSpace;
710
711
_cmsIOPrintf(m, "[");
712
713
for (i=0; i < sc.Pipeline->Params->nInputs; i++)
714
_cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
715
716
_cmsIOPrintf(m, " [\n");
717
718
cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT);
719
720
_cmsIOPrintf(m, PostMin);
721
_cmsIOPrintf(m, PostMaj);
722
_cmsIOPrintf(m, "] ");
723
724
}
725
726
727
// Dumps CIEBasedA Color Space Array
728
729
static
730
int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
731
{
732
733
_cmsIOPrintf(m, "[ /CIEBasedA\n");
734
_cmsIOPrintf(m, " <<\n");
735
736
EmitSafeGuardBegin(m, "lcms2gammaproc");
737
Emit1Gamma(m, Curve, "lcms2gammaproc");
738
739
_cmsIOPrintf(m, "/DecodeA /lcms2gammaproc load\n");
740
EmitSafeGuardEnd(m, "lcms2gammaproc", 3);
741
742
_cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
743
_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
744
745
EmitWhiteBlackD50(m, BlackPoint);
746
EmitIntent(m, INTENT_PERCEPTUAL);
747
748
_cmsIOPrintf(m, ">>\n");
749
_cmsIOPrintf(m, "]\n");
750
751
return 1;
752
}
753
754
755
// Dumps CIEBasedABC Color Space Array
756
757
static
758
int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)
759
{
760
int i;
761
762
_cmsIOPrintf(m, "[ /CIEBasedABC\n");
763
_cmsIOPrintf(m, "<<\n");
764
765
EmitSafeGuardBegin(m, "lcms2gammaproc0");
766
EmitSafeGuardBegin(m, "lcms2gammaproc1");
767
EmitSafeGuardBegin(m, "lcms2gammaproc2");
768
EmitNGamma(m, 3, CurveSet, "lcms2gammaproc");
769
_cmsIOPrintf(m, "/DecodeABC [\n");
770
_cmsIOPrintf(m, " /lcms2gammaproc0 load\n");
771
_cmsIOPrintf(m, " /lcms2gammaproc1 load\n");
772
_cmsIOPrintf(m, " /lcms2gammaproc2 load\n");
773
_cmsIOPrintf(m, "]\n");
774
EmitSafeGuardEnd(m, "lcms2gammaproc2", 3);
775
EmitSafeGuardEnd(m, "lcms2gammaproc1", 3);
776
EmitSafeGuardEnd(m, "lcms2gammaproc0", 3);
777
778
_cmsIOPrintf(m, "/MatrixABC [ " );
779
780
for( i=0; i < 3; i++ ) {
781
782
_cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],
783
Matrix[i + 3*1],
784
Matrix[i + 3*2]);
785
}
786
787
788
_cmsIOPrintf(m, "]\n");
789
790
_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
791
792
EmitWhiteBlackD50(m, BlackPoint);
793
EmitIntent(m, INTENT_PERCEPTUAL);
794
795
_cmsIOPrintf(m, ">>\n");
796
_cmsIOPrintf(m, "]\n");
797
798
799
return 1;
800
}
801
802
803
static
804
int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint)
805
{
806
const char* PreMaj;
807
const char* PostMaj;
808
const char* PreMin, * PostMin;
809
cmsStage* mpe;
810
int i, numchans;
811
static char buffer[2048];
812
813
mpe = Pipeline->Elements;
814
815
switch (cmsStageInputChannels(mpe)) {
816
case 3:
817
_cmsIOPrintf(m, "[ /CIEBasedDEF\n");
818
PreMaj = "<";
819
PostMaj = ">\n";
820
PreMin = PostMin = "";
821
break;
822
823
case 4:
824
_cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
825
PreMaj = "[";
826
PostMaj = "]\n";
827
PreMin = "<";
828
PostMin = ">\n";
829
break;
830
831
default:
832
return 0;
833
834
}
835
836
_cmsIOPrintf(m, "<<\n");
837
838
if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
839
840
numchans = (int) cmsStageOutputChannels(mpe);
841
for (i = 0; i < numchans; ++i) {
842
snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);
843
buffer[sizeof(buffer) - 1] = '\0';
844
EmitSafeGuardBegin(m, buffer);
845
}
846
EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe), "lcms2gammaproc");
847
_cmsIOPrintf(m, "/DecodeDEF [\n");
848
for (i = 0; i < numchans; ++i) {
849
snprintf(buffer, sizeof(buffer), " /lcms2gammaproc%d load\n", i);
850
buffer[sizeof(buffer) - 1] = '\0';
851
_cmsIOPrintf(m, buffer);
852
}
853
_cmsIOPrintf(m, "]\n");
854
for (i = numchans - 1; i >= 0; --i) {
855
snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);
856
buffer[sizeof(buffer) - 1] = '\0';
857
EmitSafeGuardEnd(m, buffer, 3);
858
}
859
860
mpe = mpe->Next;
861
}
862
863
if (cmsStageType(mpe) == cmsSigCLutElemType) {
864
865
_cmsIOPrintf(m, "/Table ");
866
WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature)0);
867
_cmsIOPrintf(m, "]\n");
868
}
869
870
EmitLab2XYZ(m);
871
EmitWhiteBlackD50(m, BlackPoint);
872
EmitIntent(m, Intent);
873
874
_cmsIOPrintf(m, " >>\n");
875
_cmsIOPrintf(m, "]\n");
876
877
return 1;
878
}
879
880
// Generates a curve from a gray profile
881
882
static
883
cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent)
884
{
885
cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
886
cmsHPROFILE hXYZ = cmsCreateXYZProfile();
887
cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
888
int i;
889
890
if (Out != NULL && xform != NULL) {
891
for (i=0; i < 256; i++) {
892
893
cmsUInt8Number Gray = (cmsUInt8Number) i;
894
cmsCIEXYZ XYZ;
895
896
cmsDoTransform(xform, &Gray, &XYZ, 1);
897
898
Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);
899
}
900
}
901
902
if (xform) cmsDeleteTransform(xform);
903
if (hXYZ) cmsCloseProfile(hXYZ);
904
return Out;
905
}
906
907
908
909
// Because PostScript has only 8 bits in /Table, we should use
910
// a more perceptually uniform space... I do choose Lab.
911
912
static
913
int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
914
{
915
cmsHPROFILE hLab;
916
cmsHTRANSFORM xform;
917
cmsUInt32Number nChannels;
918
cmsUInt32Number InputFormat;
919
int rc;
920
cmsHPROFILE Profiles[2];
921
cmsCIEXYZ BlackPointAdaptedToD50;
922
923
// Does create a device-link based transform.
924
// The DeviceLink is next dumped as working CSA.
925
926
InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
927
nChannels = T_CHANNELS(InputFormat);
928
929
930
cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
931
932
// Adjust output to Lab4
933
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
934
935
Profiles[0] = hProfile;
936
Profiles[1] = hLab;
937
938
xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);
939
cmsCloseProfile(hLab);
940
941
if (xform == NULL) {
942
943
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
944
return 0;
945
}
946
947
// Only 1, 3 and 4 channels are allowed
948
949
switch (nChannels) {
950
951
case 1: {
952
cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
953
EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
954
cmsFreeToneCurve(Gray2Y);
955
}
956
break;
957
958
case 3:
959
case 4: {
960
cmsUInt32Number OutFrm = TYPE_Lab_16;
961
cmsPipeline* DeviceLink;
962
_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
963
964
DeviceLink = cmsPipelineDup(v ->Lut);
965
if (DeviceLink == NULL) return 0;
966
967
dwFlags |= cmsFLAGS_FORCE_CLUT;
968
_cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
969
970
rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
971
cmsPipelineFree(DeviceLink);
972
if (rc == 0) return 0;
973
}
974
break;
975
976
default:
977
978
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels are supported for CSA. This profile has %d channels.", nChannels);
979
return 0;
980
}
981
982
983
cmsDeleteTransform(xform);
984
985
return 1;
986
}
987
988
static
989
cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)
990
{
991
_cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
992
993
return Data -> Double;
994
}
995
996
997
// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
998
static
999
int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
1000
{
1001
cmsColorSpaceSignature ColorSpace;
1002
int rc;
1003
cmsCIEXYZ BlackPointAdaptedToD50;
1004
1005
ColorSpace = cmsGetColorSpace(hProfile);
1006
1007
cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
1008
1009
if (ColorSpace == cmsSigGrayData) {
1010
1011
cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
1012
rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
1013
1014
}
1015
else
1016
if (ColorSpace == cmsSigRgbData) {
1017
1018
cmsMAT3 Mat;
1019
int i, j;
1020
1021
memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
1022
1023
for (i = 0; i < 3; i++)
1024
for (j = 0; j < 3; j++)
1025
Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
1026
1027
rc = EmitCIEBasedABC(m, (cmsFloat64Number *)&Mat,
1028
_cmsStageGetPtrToCurveSet(Shaper),
1029
&BlackPointAdaptedToD50);
1030
}
1031
else {
1032
1033
cmsSignalError(m->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
1034
return 0;
1035
}
1036
1037
return rc;
1038
}
1039
1040
1041
1042
// Creates a PostScript color list from a named profile data.
1043
// This is a HP extension, and it works in Lab instead of XYZ
1044
1045
static
1046
int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent)
1047
{
1048
cmsHTRANSFORM xform;
1049
cmsHPROFILE hLab;
1050
cmsUInt32Number i, nColors;
1051
char ColorName[cmsMAX_PATH];
1052
cmsNAMEDCOLORLIST* NamedColorList;
1053
1054
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1055
xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
1056
if (xform == NULL) return 0;
1057
1058
NamedColorList = cmsGetNamedColorList(xform);
1059
if (NamedColorList == NULL) return 0;
1060
1061
_cmsIOPrintf(m, "<<\n");
1062
_cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1063
_cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1064
_cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1065
1066
nColors = cmsNamedColorCount(NamedColorList);
1067
1068
1069
for (i=0; i < nColors; i++) {
1070
1071
cmsUInt16Number In[1];
1072
cmsCIELab Lab;
1073
1074
In[0] = (cmsUInt16Number) i;
1075
1076
if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1077
continue;
1078
1079
cmsDoTransform(xform, In, &Lab, 1);
1080
_cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1081
}
1082
1083
1084
1085
_cmsIOPrintf(m, ">>\n");
1086
1087
cmsDeleteTransform(xform);
1088
cmsCloseProfile(hLab);
1089
return 1;
1090
}
1091
1092
1093
// Does create a Color Space Array on XYZ colorspace for PostScript usage
1094
static
1095
cmsUInt32Number GenerateCSA(cmsContext ContextID,
1096
cmsHPROFILE hProfile,
1097
cmsUInt32Number Intent,
1098
cmsUInt32Number dwFlags,
1099
cmsIOHANDLER* mem)
1100
{
1101
cmsUInt32Number dwBytesUsed;
1102
cmsPipeline* lut = NULL;
1103
cmsStage* Matrix, *Shaper;
1104
1105
1106
// Is a named color profile?
1107
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1108
1109
if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
1110
}
1111
else {
1112
1113
1114
// Any profile class are allowed (including devicelink), but
1115
// output (PCS) colorspace must be XYZ or Lab
1116
cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1117
1118
if (ColorSpace != cmsSigXYZData &&
1119
ColorSpace != cmsSigLabData) {
1120
1121
cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
1122
goto Error;
1123
}
1124
1125
1126
// Read the lut with all necessary conversion stages
1127
lut = _cmsReadInputLUT(hProfile, Intent);
1128
if (lut == NULL) goto Error;
1129
1130
1131
// Tone curves + matrix can be implemented without any LUT
1132
if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
1133
1134
if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
1135
1136
}
1137
else {
1138
// We need a LUT for the rest
1139
if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
1140
}
1141
}
1142
1143
1144
// Done, keep memory usage
1145
dwBytesUsed = mem ->UsedSpace;
1146
1147
// Get rid of LUT
1148
if (lut != NULL) cmsPipelineFree(lut);
1149
1150
// Finally, return used byte count
1151
return dwBytesUsed;
1152
1153
Error:
1154
if (lut != NULL) cmsPipelineFree(lut);
1155
return 0;
1156
}
1157
1158
// ------------------------------------------------------ Color Rendering Dictionary (CRD)
1159
1160
1161
1162
/*
1163
1164
Black point compensation plus chromatic adaptation:
1165
1166
Step 1 - Chromatic adaptation
1167
=============================
1168
1169
WPout
1170
X = ------- PQR
1171
Wpin
1172
1173
Step 2 - Black point compensation
1174
=================================
1175
1176
(WPout - BPout)*X - WPout*(BPin - BPout)
1177
out = ---------------------------------------
1178
WPout - BPin
1179
1180
1181
Algorithm discussion
1182
====================
1183
1184
TransformPQR(WPin, BPin, WPout, BPout, PQR)
1185
1186
Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1187
1188
1189
Algorithm Stack 0...n
1190
===========================================================
1191
PQR BPout WPout BPin WPin
1192
4 index 3 get WPin PQR BPout WPout BPin WPin
1193
div (PQR/WPin) BPout WPout BPin WPin
1194
2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin
1195
mult WPout*(PQR/WPin) BPout WPout BPin WPin
1196
1197
2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1198
2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1199
sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
1200
mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1201
1202
2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1203
4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1204
3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1205
1206
sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1207
mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1208
sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1209
1210
3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1211
3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1212
exch
1213
sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1214
div
1215
1216
exch pop
1217
exch pop
1218
exch pop
1219
exch pop
1220
1221
*/
1222
1223
1224
static
1225
void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1226
{
1227
1228
1229
if (lIsAbsolute) {
1230
1231
// For absolute colorimetric intent, encode back to relative
1232
// and generate a relative Pipeline
1233
1234
// Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1235
1236
cmsCIEXYZ White;
1237
1238
_cmsReadMediaWhitePoint(&White, hProfile);
1239
1240
_cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1241
_cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1242
1243
_cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1244
"/TransformPQR [\n"
1245
"{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1246
"{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1247
"{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
1248
White.X, White.Y, White.Z);
1249
return;
1250
}
1251
1252
1253
_cmsIOPrintf(m,"%% Bradford Cone Space\n"
1254
"/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1255
1256
_cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1257
1258
1259
// No BPC
1260
1261
if (!DoBPC) {
1262
1263
_cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"
1264
"/TransformPQR [\n"
1265
"{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1266
"{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1267
"{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
1268
} else {
1269
1270
// BPC
1271
1272
_cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1273
"/TransformPQR [\n");
1274
1275
_cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "
1276
"2 index 3 get 2 index 3 get sub mul "
1277
"2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1278
"3 index 3 get 3 index 3 get exch sub div "
1279
"exch pop exch pop exch pop exch pop } bind\n");
1280
1281
_cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "
1282
"2 index 4 get 2 index 4 get sub mul "
1283
"2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1284
"3 index 4 get 3 index 4 get exch sub div "
1285
"exch pop exch pop exch pop exch pop } bind\n");
1286
1287
_cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "
1288
"2 index 5 get 2 index 5 get sub mul "
1289
"2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1290
"3 index 5 get 3 index 5 get exch sub div "
1291
"exch pop exch pop exch pop exch pop } bind\n]\n");
1292
1293
}
1294
}
1295
1296
1297
static
1298
void EmitXYZ2Lab(cmsIOHANDLER* m)
1299
{
1300
_cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
1301
_cmsIOPrintf(m, "/EncodeLMN [\n");
1302
_cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1303
_cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1304
_cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1305
_cmsIOPrintf(m, "]\n");
1306
_cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1307
_cmsIOPrintf(m, "/EncodeABC [\n");
1308
1309
1310
_cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n");
1311
_cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n");
1312
_cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n");
1313
1314
1315
_cmsIOPrintf(m, "]\n");
1316
1317
1318
}
1319
1320
// Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1321
// I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1322
// space on 3D CLUT, but since space seems not to be a problem here, 33 points
1323
// would give a reasonable accuracy. Note also that CRD tables must operate in
1324
// 8 bits.
1325
1326
static
1327
int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
1328
{
1329
cmsHPROFILE hLab;
1330
cmsHTRANSFORM xform;
1331
cmsUInt32Number i, nChannels;
1332
cmsUInt32Number OutputFormat;
1333
_cmsTRANSFORM* v;
1334
cmsPipeline* DeviceLink;
1335
cmsHPROFILE Profiles[3];
1336
cmsCIEXYZ BlackPointAdaptedToD50;
1337
cmsBool lDoBPC = (cmsBool) (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1338
cmsBool lFixWhite = (cmsBool) !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1339
cmsUInt32Number InFrm = TYPE_Lab_16;
1340
cmsUInt32Number RelativeEncodingIntent;
1341
cmsColorSpaceSignature ColorSpace;
1342
1343
1344
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1345
if (hLab == NULL) return 0;
1346
1347
OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
1348
nChannels = T_CHANNELS(OutputFormat);
1349
1350
ColorSpace = cmsGetColorSpace(hProfile);
1351
1352
// For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
1353
1354
RelativeEncodingIntent = Intent;
1355
if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1356
RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1357
1358
1359
// Use V4 Lab always
1360
Profiles[0] = hLab;
1361
Profiles[1] = hProfile;
1362
1363
xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
1364
Profiles, 2, TYPE_Lab_DBL,
1365
OutputFormat, RelativeEncodingIntent, 0);
1366
cmsCloseProfile(hLab);
1367
1368
if (xform == NULL) {
1369
1370
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
1371
return 0;
1372
}
1373
1374
// Get a copy of the internal devicelink
1375
v = (_cmsTRANSFORM*) xform;
1376
DeviceLink = cmsPipelineDup(v ->Lut);
1377
if (DeviceLink == NULL) return 0;
1378
1379
1380
// We need a CLUT
1381
dwFlags |= cmsFLAGS_FORCE_CLUT;
1382
_cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
1383
1384
_cmsIOPrintf(m, "<<\n");
1385
_cmsIOPrintf(m, "/ColorRenderingType 1\n");
1386
1387
1388
cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
1389
1390
// Emit headers, etc.
1391
EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1392
EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1393
EmitXYZ2Lab(m);
1394
1395
1396
// FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
1397
// does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
1398
// zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1399
// zero. This would sacrifice a bit of highlights, but failure to do so would cause
1400
// scum dot. Ouch.
1401
1402
if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1403
lFixWhite = FALSE;
1404
1405
_cmsIOPrintf(m, "/RenderTable ");
1406
1407
1408
WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);
1409
1410
_cmsIOPrintf(m, " %d {} bind ", nChannels);
1411
1412
for (i=1; i < nChannels; i++)
1413
_cmsIOPrintf(m, "dup ");
1414
1415
_cmsIOPrintf(m, "]\n");
1416
1417
1418
EmitIntent(m, Intent);
1419
1420
_cmsIOPrintf(m, ">>\n");
1421
1422
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1423
1424
_cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");
1425
}
1426
1427
cmsPipelineFree(DeviceLink);
1428
cmsDeleteTransform(xform);
1429
1430
return 1;
1431
}
1432
1433
1434
// Builds a ASCII string containing colorant list in 0..1.0 range
1435
static
1436
void BuildColorantList(char *Colorant, cmsUInt32Number nColorant, cmsUInt16Number Out[])
1437
{
1438
char Buff[32];
1439
cmsUInt32Number j;
1440
1441
Colorant[0] = 0;
1442
if (nColorant > cmsMAXCHANNELS)
1443
nColorant = cmsMAXCHANNELS;
1444
1445
for (j = 0; j < nColorant; j++) {
1446
1447
snprintf(Buff, 31, "%.3f", Out[j] / 65535.0);
1448
Buff[31] = 0;
1449
strcat(Colorant, Buff);
1450
if (j < nColorant - 1)
1451
strcat(Colorant, " ");
1452
1453
}
1454
}
1455
1456
1457
// Creates a PostScript color list from a named profile data.
1458
// This is a HP extension.
1459
1460
static
1461
int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
1462
{
1463
cmsHTRANSFORM xform;
1464
cmsUInt32Number i, nColors, nColorant;
1465
cmsUInt32Number OutputFormat;
1466
char ColorName[cmsMAX_PATH];
1467
char Colorant[512];
1468
cmsNAMEDCOLORLIST* NamedColorList;
1469
1470
1471
OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
1472
nColorant = T_CHANNELS(OutputFormat);
1473
1474
1475
xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
1476
if (xform == NULL) return 0;
1477
1478
1479
NamedColorList = cmsGetNamedColorList(xform);
1480
if (NamedColorList == NULL) return 0;
1481
1482
_cmsIOPrintf(m, "<<\n");
1483
_cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
1484
_cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1485
_cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1486
1487
nColors = cmsNamedColorCount(NamedColorList);
1488
1489
for (i=0; i < nColors; i++) {
1490
1491
cmsUInt16Number In[1];
1492
cmsUInt16Number Out[cmsMAXCHANNELS];
1493
1494
In[0] = (cmsUInt16Number) i;
1495
1496
if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1497
continue;
1498
1499
cmsDoTransform(xform, In, Out, 1);
1500
BuildColorantList(Colorant, nColorant, Out);
1501
_cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant);
1502
}
1503
1504
_cmsIOPrintf(m, " >>");
1505
1506
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1507
1508
_cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");
1509
}
1510
1511
cmsDeleteTransform(xform);
1512
return 1;
1513
}
1514
1515
1516
1517
// This one does create a Color Rendering Dictionary.
1518
// CRD are always LUT-Based, no matter if profile is
1519
// implemented as matrix-shaper.
1520
1521
static
1522
cmsUInt32Number GenerateCRD(cmsContext ContextID,
1523
cmsHPROFILE hProfile,
1524
cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1525
cmsIOHANDLER* mem)
1526
{
1527
cmsUInt32Number dwBytesUsed;
1528
1529
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1530
1531
EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1532
}
1533
1534
1535
// Is a named color profile?
1536
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1537
1538
if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1539
return 0;
1540
}
1541
}
1542
else {
1543
1544
// CRD are always implemented as LUT
1545
1546
if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1547
return 0;
1548
}
1549
}
1550
1551
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1552
1553
_cmsIOPrintf(mem, "%%%%EndResource\n");
1554
_cmsIOPrintf(mem, "\n%% CRD End\n");
1555
}
1556
1557
// Done, keep memory usage
1558
dwBytesUsed = mem ->UsedSpace;
1559
1560
// Finally, return used byte count
1561
return dwBytesUsed;
1562
1563
cmsUNUSED_PARAMETER(ContextID);
1564
}
1565
1566
1567
1568
1569
cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
1570
cmsPSResourceType Type,
1571
cmsHPROFILE hProfile,
1572
cmsUInt32Number Intent,
1573
cmsUInt32Number dwFlags,
1574
cmsIOHANDLER* io)
1575
{
1576
cmsUInt32Number rc;
1577
1578
1579
switch (Type) {
1580
1581
case cmsPS_RESOURCE_CSA:
1582
rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
1583
break;
1584
1585
default:
1586
case cmsPS_RESOURCE_CRD:
1587
rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
1588
break;
1589
}
1590
1591
return rc;
1592
}
1593
1594
1595
1596
cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
1597
cmsHPROFILE hProfile,
1598
cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1599
void* Buffer, cmsUInt32Number dwBufferLen)
1600
{
1601
cmsIOHANDLER* mem;
1602
cmsUInt32Number dwBytesUsed;
1603
1604
// Set up the serialization engine
1605
if (Buffer == NULL)
1606
mem = cmsOpenIOhandlerFromNULL(ContextID);
1607
else
1608
mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1609
1610
if (!mem) return 0;
1611
1612
dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
1613
1614
// Get rid of memory stream
1615
cmsCloseIOhandler(mem);
1616
1617
return dwBytesUsed;
1618
}
1619
1620
1621
1622
// Does create a Color Space Array on XYZ colorspace for PostScript usage
1623
cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
1624
cmsHPROFILE hProfile,
1625
cmsUInt32Number Intent,
1626
cmsUInt32Number dwFlags,
1627
void* Buffer,
1628
cmsUInt32Number dwBufferLen)
1629
{
1630
cmsIOHANDLER* mem;
1631
cmsUInt32Number dwBytesUsed;
1632
1633
if (Buffer == NULL)
1634
mem = cmsOpenIOhandlerFromNULL(ContextID);
1635
else
1636
mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1637
1638
if (!mem) return 0;
1639
1640
dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
1641
1642
// Get rid of memory stream
1643
cmsCloseIOhandler(mem);
1644
1645
return dwBytesUsed;
1646
1647
}
1648
1649