Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/native/sun/java2d/cmm/lcms/cmsps2.c
38918 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 nEntries)
562
{
563
return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0;
564
}
565
566
567
// Does write a set of gamma curves
568
569
static
570
void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[], const char* nameprefix)
571
{
572
cmsUInt32Number i;
573
static char buffer[2048];
574
575
for( i=0; i < n; i++ )
576
{
577
if (g[i] == NULL) return; // Error
578
579
if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) {
580
581
_cmsIOPrintf(m, "/%s%d /%s%d load def\n", nameprefix, i, nameprefix, i-1);
582
}
583
else {
584
snprintf(buffer, sizeof(buffer), "%s%d", nameprefix, i);
585
buffer[sizeof(buffer)-1] = '\0';
586
Emit1Gamma(m, g[i], buffer);
587
}
588
}
589
590
}
591
592
593
// Following code dumps a LUT onto memory stream
594
595
596
// This is the sampler. Intended to work in SAMPLER_INSPECT mode,
597
// that is, the callback will be called for each knot with
598
//
599
// In[] The grid location coordinates, normalized to 0..ffff
600
// Out[] The Pipeline values, normalized to 0..ffff
601
//
602
// Returning a value other than 0 does terminate the sampling process
603
//
604
// Each row contains Pipeline values for all but first component. So, I
605
// detect row changing by keeping a copy of last value of first
606
// component. -1 is used to mark beginning of whole block.
607
608
static
609
int OutputValueSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
610
{
611
cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;
612
cmsUInt32Number i;
613
614
615
if (sc -> FixWhite) {
616
617
if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8]
618
619
if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&
620
(In[2] >= 0x7800 && In[2] <= 0x8800)) {
621
622
cmsUInt16Number* Black;
623
cmsUInt16Number* White;
624
cmsUInt32Number nOutputs;
625
626
if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))
627
return 0;
628
629
for (i=0; i < nOutputs; i++)
630
Out[i] = White[i];
631
}
632
633
634
}
635
}
636
637
638
// Hadle the parenthesis on rows
639
640
if (In[0] != sc ->FirstComponent) {
641
642
if (sc ->FirstComponent != -1) {
643
644
_cmsIOPrintf(sc ->m, sc ->PostMin);
645
sc ->SecondComponent = -1;
646
_cmsIOPrintf(sc ->m, sc ->PostMaj);
647
}
648
649
// Begin block
650
_cmsPSActualColumn = 0;
651
652
_cmsIOPrintf(sc ->m, sc ->PreMaj);
653
sc ->FirstComponent = In[0];
654
}
655
656
657
if (In[1] != sc ->SecondComponent) {
658
659
if (sc ->SecondComponent != -1) {
660
661
_cmsIOPrintf(sc ->m, sc ->PostMin);
662
}
663
664
_cmsIOPrintf(sc ->m, sc ->PreMin);
665
sc ->SecondComponent = In[1];
666
}
667
668
// Dump table.
669
670
for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {
671
672
cmsUInt16Number wWordOut = Out[i];
673
cmsUInt8Number wByteOut; // Value as byte
674
675
676
// We always deal with Lab4
677
678
wByteOut = Word2Byte(wWordOut);
679
WriteByte(sc -> m, wByteOut);
680
}
681
682
return 1;
683
}
684
685
// Writes a Pipeline on memstream. Could be 8 or 16 bits based
686
687
static
688
void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
689
const char* PostMaj,
690
const char* PreMin,
691
const char* PostMin,
692
int FixWhite,
693
cmsColorSpaceSignature ColorSpace)
694
{
695
cmsUInt32Number i;
696
cmsPsSamplerCargo sc;
697
698
sc.FirstComponent = -1;
699
sc.SecondComponent = -1;
700
sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
701
sc.m = m;
702
sc.PreMaj = PreMaj;
703
sc.PostMaj= PostMaj;
704
705
sc.PreMin = PreMin;
706
sc.PostMin = PostMin;
707
sc.FixWhite = FixWhite;
708
sc.ColorSpace = ColorSpace;
709
710
_cmsIOPrintf(m, "[");
711
712
for (i=0; i < sc.Pipeline->Params->nInputs; i++)
713
_cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
714
715
_cmsIOPrintf(m, " [\n");
716
717
cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT);
718
719
_cmsIOPrintf(m, PostMin);
720
_cmsIOPrintf(m, PostMaj);
721
_cmsIOPrintf(m, "] ");
722
723
}
724
725
726
// Dumps CIEBasedA Color Space Array
727
728
static
729
int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
730
{
731
732
_cmsIOPrintf(m, "[ /CIEBasedA\n");
733
_cmsIOPrintf(m, " <<\n");
734
735
EmitSafeGuardBegin(m, "lcms2gammaproc");
736
Emit1Gamma(m, Curve, "lcms2gammaproc");
737
738
_cmsIOPrintf(m, "/DecodeA /lcms2gammaproc load\n");
739
EmitSafeGuardEnd(m, "lcms2gammaproc", 3);
740
741
_cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
742
_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
743
744
EmitWhiteBlackD50(m, BlackPoint);
745
EmitIntent(m, INTENT_PERCEPTUAL);
746
747
_cmsIOPrintf(m, ">>\n");
748
_cmsIOPrintf(m, "]\n");
749
750
return 1;
751
}
752
753
754
// Dumps CIEBasedABC Color Space Array
755
756
static
757
int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)
758
{
759
int i;
760
761
_cmsIOPrintf(m, "[ /CIEBasedABC\n");
762
_cmsIOPrintf(m, "<<\n");
763
764
EmitSafeGuardBegin(m, "lcms2gammaproc0");
765
EmitSafeGuardBegin(m, "lcms2gammaproc1");
766
EmitSafeGuardBegin(m, "lcms2gammaproc2");
767
EmitNGamma(m, 3, CurveSet, "lcms2gammaproc");
768
_cmsIOPrintf(m, "/DecodeABC [\n");
769
_cmsIOPrintf(m, " /lcms2gammaproc0 load\n");
770
_cmsIOPrintf(m, " /lcms2gammaproc1 load\n");
771
_cmsIOPrintf(m, " /lcms2gammaproc2 load\n");
772
_cmsIOPrintf(m, "]\n");
773
EmitSafeGuardEnd(m, "lcms2gammaproc2", 3);
774
EmitSafeGuardEnd(m, "lcms2gammaproc1", 3);
775
EmitSafeGuardEnd(m, "lcms2gammaproc0", 3);
776
777
_cmsIOPrintf(m, "/MatrixABC [ " );
778
779
for( i=0; i < 3; i++ ) {
780
781
_cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],
782
Matrix[i + 3*1],
783
Matrix[i + 3*2]);
784
}
785
786
787
_cmsIOPrintf(m, "]\n");
788
789
_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
790
791
EmitWhiteBlackD50(m, BlackPoint);
792
EmitIntent(m, INTENT_PERCEPTUAL);
793
794
_cmsIOPrintf(m, ">>\n");
795
_cmsIOPrintf(m, "]\n");
796
797
798
return 1;
799
}
800
801
802
static
803
int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint)
804
{
805
const char* PreMaj;
806
const char* PostMaj;
807
const char* PreMin, * PostMin;
808
cmsStage* mpe;
809
int i, numchans;
810
static char buffer[2048];
811
812
mpe = Pipeline->Elements;
813
814
switch (cmsStageInputChannels(mpe)) {
815
case 3:
816
_cmsIOPrintf(m, "[ /CIEBasedDEF\n");
817
PreMaj = "<";
818
PostMaj = ">\n";
819
PreMin = PostMin = "";
820
break;
821
822
case 4:
823
_cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
824
PreMaj = "[";
825
PostMaj = "]\n";
826
PreMin = "<";
827
PostMin = ">\n";
828
break;
829
830
default:
831
return 0;
832
833
}
834
835
_cmsIOPrintf(m, "<<\n");
836
837
if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
838
839
numchans = cmsStageOutputChannels(mpe);
840
for (i = 0; i < numchans; ++i) {
841
snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);
842
buffer[sizeof(buffer) - 1] = '\0';
843
EmitSafeGuardBegin(m, buffer);
844
}
845
EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe), "lcms2gammaproc");
846
_cmsIOPrintf(m, "/DecodeDEF [\n");
847
for (i = 0; i < numchans; ++i) {
848
snprintf(buffer, sizeof(buffer), " /lcms2gammaproc%d load\n", i);
849
buffer[sizeof(buffer) - 1] = '\0';
850
_cmsIOPrintf(m, buffer);
851
}
852
_cmsIOPrintf(m, "]\n");
853
for (i = numchans - 1; i >= 0; --i) {
854
snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);
855
buffer[sizeof(buffer) - 1] = '\0';
856
EmitSafeGuardEnd(m, buffer, 3);
857
}
858
859
mpe = mpe->Next;
860
}
861
862
if (cmsStageType(mpe) == cmsSigCLutElemType) {
863
864
_cmsIOPrintf(m, "/Table ");
865
WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature)0);
866
_cmsIOPrintf(m, "]\n");
867
}
868
869
EmitLab2XYZ(m);
870
EmitWhiteBlackD50(m, BlackPoint);
871
EmitIntent(m, Intent);
872
873
_cmsIOPrintf(m, " >>\n");
874
_cmsIOPrintf(m, "]\n");
875
876
return 1;
877
}
878
879
// Generates a curve from a gray profile
880
881
static
882
cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent)
883
{
884
cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
885
cmsHPROFILE hXYZ = cmsCreateXYZProfile();
886
cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
887
int i;
888
889
if (Out != NULL && xform != NULL) {
890
for (i=0; i < 256; i++) {
891
892
cmsUInt8Number Gray = (cmsUInt8Number) i;
893
cmsCIEXYZ XYZ;
894
895
cmsDoTransform(xform, &Gray, &XYZ, 1);
896
897
Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);
898
}
899
}
900
901
if (xform) cmsDeleteTransform(xform);
902
if (hXYZ) cmsCloseProfile(hXYZ);
903
return Out;
904
}
905
906
907
908
// Because PostScript has only 8 bits in /Table, we should use
909
// a more perceptually uniform space... I do choose Lab.
910
911
static
912
int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
913
{
914
cmsHPROFILE hLab;
915
cmsHTRANSFORM xform;
916
cmsUInt32Number nChannels;
917
cmsUInt32Number InputFormat;
918
int rc;
919
cmsHPROFILE Profiles[2];
920
cmsCIEXYZ BlackPointAdaptedToD50;
921
922
// Does create a device-link based transform.
923
// The DeviceLink is next dumped as working CSA.
924
925
InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
926
nChannels = T_CHANNELS(InputFormat);
927
928
929
cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
930
931
// Adjust output to Lab4
932
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
933
934
Profiles[0] = hProfile;
935
Profiles[1] = hLab;
936
937
xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);
938
cmsCloseProfile(hLab);
939
940
if (xform == NULL) {
941
942
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
943
return 0;
944
}
945
946
// Only 1, 3 and 4 channels are allowed
947
948
switch (nChannels) {
949
950
case 1: {
951
cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
952
EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
953
cmsFreeToneCurve(Gray2Y);
954
}
955
break;
956
957
case 3:
958
case 4: {
959
cmsUInt32Number OutFrm = TYPE_Lab_16;
960
cmsPipeline* DeviceLink;
961
_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
962
963
DeviceLink = cmsPipelineDup(v ->Lut);
964
if (DeviceLink == NULL) return 0;
965
966
dwFlags |= cmsFLAGS_FORCE_CLUT;
967
_cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
968
969
rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
970
cmsPipelineFree(DeviceLink);
971
if (rc == 0) return 0;
972
}
973
break;
974
975
default:
976
977
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels are supported for CSA. This profile has %d channels.", nChannels);
978
return 0;
979
}
980
981
982
cmsDeleteTransform(xform);
983
984
return 1;
985
}
986
987
static
988
cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)
989
{
990
_cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
991
992
return Data -> Double;
993
}
994
995
996
// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
997
static
998
int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
999
{
1000
cmsColorSpaceSignature ColorSpace;
1001
int rc;
1002
cmsCIEXYZ BlackPointAdaptedToD50;
1003
1004
ColorSpace = cmsGetColorSpace(hProfile);
1005
1006
cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
1007
1008
if (ColorSpace == cmsSigGrayData) {
1009
1010
cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
1011
rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
1012
1013
}
1014
else
1015
if (ColorSpace == cmsSigRgbData) {
1016
1017
cmsMAT3 Mat;
1018
int i, j;
1019
1020
memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
1021
1022
for (i = 0; i < 3; i++)
1023
for (j = 0; j < 3; j++)
1024
Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
1025
1026
rc = EmitCIEBasedABC(m, (cmsFloat64Number *)&Mat,
1027
_cmsStageGetPtrToCurveSet(Shaper),
1028
&BlackPointAdaptedToD50);
1029
}
1030
else {
1031
1032
cmsSignalError(m->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
1033
return 0;
1034
}
1035
1036
return rc;
1037
}
1038
1039
1040
1041
// Creates a PostScript color list from a named profile data.
1042
// This is a HP extension, and it works in Lab instead of XYZ
1043
1044
static
1045
int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent)
1046
{
1047
cmsHTRANSFORM xform;
1048
cmsHPROFILE hLab;
1049
cmsUInt32Number i, nColors;
1050
char ColorName[cmsMAX_PATH];
1051
cmsNAMEDCOLORLIST* NamedColorList;
1052
1053
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1054
xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
1055
if (xform == NULL) return 0;
1056
1057
NamedColorList = cmsGetNamedColorList(xform);
1058
if (NamedColorList == NULL) return 0;
1059
1060
_cmsIOPrintf(m, "<<\n");
1061
_cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1062
_cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1063
_cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1064
1065
nColors = cmsNamedColorCount(NamedColorList);
1066
1067
1068
for (i=0; i < nColors; i++) {
1069
1070
cmsUInt16Number In[1];
1071
cmsCIELab Lab;
1072
1073
In[0] = (cmsUInt16Number) i;
1074
1075
if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1076
continue;
1077
1078
cmsDoTransform(xform, In, &Lab, 1);
1079
_cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1080
}
1081
1082
1083
1084
_cmsIOPrintf(m, ">>\n");
1085
1086
cmsDeleteTransform(xform);
1087
cmsCloseProfile(hLab);
1088
return 1;
1089
}
1090
1091
1092
// Does create a Color Space Array on XYZ colorspace for PostScript usage
1093
static
1094
cmsUInt32Number GenerateCSA(cmsContext ContextID,
1095
cmsHPROFILE hProfile,
1096
cmsUInt32Number Intent,
1097
cmsUInt32Number dwFlags,
1098
cmsIOHANDLER* mem)
1099
{
1100
cmsUInt32Number dwBytesUsed;
1101
cmsPipeline* lut = NULL;
1102
cmsStage* Matrix, *Shaper;
1103
1104
1105
// Is a named color profile?
1106
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1107
1108
if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
1109
}
1110
else {
1111
1112
1113
// Any profile class are allowed (including devicelink), but
1114
// output (PCS) colorspace must be XYZ or Lab
1115
cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1116
1117
if (ColorSpace != cmsSigXYZData &&
1118
ColorSpace != cmsSigLabData) {
1119
1120
cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
1121
goto Error;
1122
}
1123
1124
1125
// Read the lut with all necessary conversion stages
1126
lut = _cmsReadInputLUT(hProfile, Intent);
1127
if (lut == NULL) goto Error;
1128
1129
1130
// Tone curves + matrix can be implemented without any LUT
1131
if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
1132
1133
if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
1134
1135
}
1136
else {
1137
// We need a LUT for the rest
1138
if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
1139
}
1140
}
1141
1142
1143
// Done, keep memory usage
1144
dwBytesUsed = mem ->UsedSpace;
1145
1146
// Get rid of LUT
1147
if (lut != NULL) cmsPipelineFree(lut);
1148
1149
// Finally, return used byte count
1150
return dwBytesUsed;
1151
1152
Error:
1153
if (lut != NULL) cmsPipelineFree(lut);
1154
return 0;
1155
}
1156
1157
// ------------------------------------------------------ Color Rendering Dictionary (CRD)
1158
1159
1160
1161
/*
1162
1163
Black point compensation plus chromatic adaptation:
1164
1165
Step 1 - Chromatic adaptation
1166
=============================
1167
1168
WPout
1169
X = ------- PQR
1170
Wpin
1171
1172
Step 2 - Black point compensation
1173
=================================
1174
1175
(WPout - BPout)*X - WPout*(BPin - BPout)
1176
out = ---------------------------------------
1177
WPout - BPin
1178
1179
1180
Algorithm discussion
1181
====================
1182
1183
TransformPQR(WPin, BPin, WPout, BPout, PQR)
1184
1185
Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1186
1187
1188
Algorithm Stack 0...n
1189
===========================================================
1190
PQR BPout WPout BPin WPin
1191
4 index 3 get WPin PQR BPout WPout BPin WPin
1192
div (PQR/WPin) BPout WPout BPin WPin
1193
2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin
1194
mult WPout*(PQR/WPin) BPout WPout BPin WPin
1195
1196
2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1197
2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1198
sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
1199
mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1200
1201
2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1202
4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1203
3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1204
1205
sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1206
mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1207
sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1208
1209
3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1210
3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1211
exch
1212
sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1213
div
1214
1215
exch pop
1216
exch pop
1217
exch pop
1218
exch pop
1219
1220
*/
1221
1222
1223
static
1224
void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1225
{
1226
1227
1228
if (lIsAbsolute) {
1229
1230
// For absolute colorimetric intent, encode back to relative
1231
// and generate a relative Pipeline
1232
1233
// Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1234
1235
cmsCIEXYZ White;
1236
1237
_cmsReadMediaWhitePoint(&White, hProfile);
1238
1239
_cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1240
_cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1241
1242
_cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1243
"/TransformPQR [\n"
1244
"{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1245
"{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1246
"{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
1247
White.X, White.Y, White.Z);
1248
return;
1249
}
1250
1251
1252
_cmsIOPrintf(m,"%% Bradford Cone Space\n"
1253
"/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1254
1255
_cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1256
1257
1258
// No BPC
1259
1260
if (!DoBPC) {
1261
1262
_cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"
1263
"/TransformPQR [\n"
1264
"{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1265
"{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1266
"{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
1267
} else {
1268
1269
// BPC
1270
1271
_cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1272
"/TransformPQR [\n");
1273
1274
_cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "
1275
"2 index 3 get 2 index 3 get sub mul "
1276
"2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1277
"3 index 3 get 3 index 3 get exch sub div "
1278
"exch pop exch pop exch pop exch pop } bind\n");
1279
1280
_cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "
1281
"2 index 4 get 2 index 4 get sub mul "
1282
"2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1283
"3 index 4 get 3 index 4 get exch sub div "
1284
"exch pop exch pop exch pop exch pop } bind\n");
1285
1286
_cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "
1287
"2 index 5 get 2 index 5 get sub mul "
1288
"2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1289
"3 index 5 get 3 index 5 get exch sub div "
1290
"exch pop exch pop exch pop exch pop } bind\n]\n");
1291
1292
}
1293
}
1294
1295
1296
static
1297
void EmitXYZ2Lab(cmsIOHANDLER* m)
1298
{
1299
_cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
1300
_cmsIOPrintf(m, "/EncodeLMN [\n");
1301
_cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1302
_cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1303
_cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1304
_cmsIOPrintf(m, "]\n");
1305
_cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1306
_cmsIOPrintf(m, "/EncodeABC [\n");
1307
1308
1309
_cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n");
1310
_cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n");
1311
_cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n");
1312
1313
1314
_cmsIOPrintf(m, "]\n");
1315
1316
1317
}
1318
1319
// Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1320
// I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1321
// space on 3D CLUT, but since space seems not to be a problem here, 33 points
1322
// would give a reasonable accuracy. Note also that CRD tables must operate in
1323
// 8 bits.
1324
1325
static
1326
int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
1327
{
1328
cmsHPROFILE hLab;
1329
cmsHTRANSFORM xform;
1330
cmsUInt32Number i, nChannels;
1331
cmsUInt32Number OutputFormat;
1332
_cmsTRANSFORM* v;
1333
cmsPipeline* DeviceLink;
1334
cmsHPROFILE Profiles[3];
1335
cmsCIEXYZ BlackPointAdaptedToD50;
1336
cmsBool lDoBPC = (cmsBool) (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1337
cmsBool lFixWhite = (cmsBool) !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1338
cmsUInt32Number InFrm = TYPE_Lab_16;
1339
cmsUInt32Number RelativeEncodingIntent;
1340
cmsColorSpaceSignature ColorSpace;
1341
1342
1343
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1344
if (hLab == NULL) return 0;
1345
1346
OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
1347
nChannels = T_CHANNELS(OutputFormat);
1348
1349
ColorSpace = cmsGetColorSpace(hProfile);
1350
1351
// For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
1352
1353
RelativeEncodingIntent = Intent;
1354
if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1355
RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1356
1357
1358
// Use V4 Lab always
1359
Profiles[0] = hLab;
1360
Profiles[1] = hProfile;
1361
1362
xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
1363
Profiles, 2, TYPE_Lab_DBL,
1364
OutputFormat, RelativeEncodingIntent, 0);
1365
cmsCloseProfile(hLab);
1366
1367
if (xform == NULL) {
1368
1369
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
1370
return 0;
1371
}
1372
1373
// Get a copy of the internal devicelink
1374
v = (_cmsTRANSFORM*) xform;
1375
DeviceLink = cmsPipelineDup(v ->Lut);
1376
if (DeviceLink == NULL) return 0;
1377
1378
1379
// We need a CLUT
1380
dwFlags |= cmsFLAGS_FORCE_CLUT;
1381
_cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
1382
1383
_cmsIOPrintf(m, "<<\n");
1384
_cmsIOPrintf(m, "/ColorRenderingType 1\n");
1385
1386
1387
cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
1388
1389
// Emit headers, etc.
1390
EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1391
EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1392
EmitXYZ2Lab(m);
1393
1394
1395
// FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
1396
// does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
1397
// zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1398
// zero. This would sacrifice a bit of highlights, but failure to do so would cause
1399
// scum dot. Ouch.
1400
1401
if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1402
lFixWhite = FALSE;
1403
1404
_cmsIOPrintf(m, "/RenderTable ");
1405
1406
1407
WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);
1408
1409
_cmsIOPrintf(m, " %d {} bind ", nChannels);
1410
1411
for (i=1; i < nChannels; i++)
1412
_cmsIOPrintf(m, "dup ");
1413
1414
_cmsIOPrintf(m, "]\n");
1415
1416
1417
EmitIntent(m, Intent);
1418
1419
_cmsIOPrintf(m, ">>\n");
1420
1421
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1422
1423
_cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");
1424
}
1425
1426
cmsPipelineFree(DeviceLink);
1427
cmsDeleteTransform(xform);
1428
1429
return 1;
1430
}
1431
1432
1433
// Builds a ASCII string containing colorant list in 0..1.0 range
1434
static
1435
void BuildColorantList(char *Colorant, cmsUInt32Number nColorant, cmsUInt16Number Out[])
1436
{
1437
char Buff[32];
1438
cmsUInt32Number j;
1439
1440
Colorant[0] = 0;
1441
if (nColorant > cmsMAXCHANNELS)
1442
nColorant = cmsMAXCHANNELS;
1443
1444
for (j = 0; j < nColorant; j++) {
1445
1446
snprintf(Buff, 31, "%.3f", Out[j] / 65535.0);
1447
Buff[31] = 0;
1448
strcat(Colorant, Buff);
1449
if (j < nColorant - 1)
1450
strcat(Colorant, " ");
1451
1452
}
1453
}
1454
1455
1456
// Creates a PostScript color list from a named profile data.
1457
// This is a HP extension.
1458
1459
static
1460
int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
1461
{
1462
cmsHTRANSFORM xform;
1463
cmsUInt32Number i, nColors, nColorant;
1464
cmsUInt32Number OutputFormat;
1465
char ColorName[cmsMAX_PATH];
1466
char Colorant[512];
1467
cmsNAMEDCOLORLIST* NamedColorList;
1468
1469
1470
OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
1471
nColorant = T_CHANNELS(OutputFormat);
1472
1473
1474
xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
1475
if (xform == NULL) return 0;
1476
1477
1478
NamedColorList = cmsGetNamedColorList(xform);
1479
if (NamedColorList == NULL) return 0;
1480
1481
_cmsIOPrintf(m, "<<\n");
1482
_cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
1483
_cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1484
_cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1485
1486
nColors = cmsNamedColorCount(NamedColorList);
1487
1488
for (i=0; i < nColors; i++) {
1489
1490
cmsUInt16Number In[1];
1491
cmsUInt16Number Out[cmsMAXCHANNELS];
1492
1493
In[0] = (cmsUInt16Number) i;
1494
1495
if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1496
continue;
1497
1498
cmsDoTransform(xform, In, Out, 1);
1499
BuildColorantList(Colorant, nColorant, Out);
1500
_cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant);
1501
}
1502
1503
_cmsIOPrintf(m, " >>");
1504
1505
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1506
1507
_cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");
1508
}
1509
1510
cmsDeleteTransform(xform);
1511
return 1;
1512
}
1513
1514
1515
1516
// This one does create a Color Rendering Dictionary.
1517
// CRD are always LUT-Based, no matter if profile is
1518
// implemented as matrix-shaper.
1519
1520
static
1521
cmsUInt32Number GenerateCRD(cmsContext ContextID,
1522
cmsHPROFILE hProfile,
1523
cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1524
cmsIOHANDLER* mem)
1525
{
1526
cmsUInt32Number dwBytesUsed;
1527
1528
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1529
1530
EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1531
}
1532
1533
1534
// Is a named color profile?
1535
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1536
1537
if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1538
return 0;
1539
}
1540
}
1541
else {
1542
1543
// CRD are always implemented as LUT
1544
1545
if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1546
return 0;
1547
}
1548
}
1549
1550
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1551
1552
_cmsIOPrintf(mem, "%%%%EndResource\n");
1553
_cmsIOPrintf(mem, "\n%% CRD End\n");
1554
}
1555
1556
// Done, keep memory usage
1557
dwBytesUsed = mem ->UsedSpace;
1558
1559
// Finally, return used byte count
1560
return dwBytesUsed;
1561
1562
cmsUNUSED_PARAMETER(ContextID);
1563
}
1564
1565
1566
1567
1568
cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
1569
cmsPSResourceType Type,
1570
cmsHPROFILE hProfile,
1571
cmsUInt32Number Intent,
1572
cmsUInt32Number dwFlags,
1573
cmsIOHANDLER* io)
1574
{
1575
cmsUInt32Number rc;
1576
1577
1578
switch (Type) {
1579
1580
case cmsPS_RESOURCE_CSA:
1581
rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
1582
break;
1583
1584
default:
1585
case cmsPS_RESOURCE_CRD:
1586
rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
1587
break;
1588
}
1589
1590
return rc;
1591
}
1592
1593
1594
1595
cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
1596
cmsHPROFILE hProfile,
1597
cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1598
void* Buffer, cmsUInt32Number dwBufferLen)
1599
{
1600
cmsIOHANDLER* mem;
1601
cmsUInt32Number dwBytesUsed;
1602
1603
// Set up the serialization engine
1604
if (Buffer == NULL)
1605
mem = cmsOpenIOhandlerFromNULL(ContextID);
1606
else
1607
mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1608
1609
if (!mem) return 0;
1610
1611
dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
1612
1613
// Get rid of memory stream
1614
cmsCloseIOhandler(mem);
1615
1616
return dwBytesUsed;
1617
}
1618
1619
1620
1621
// Does create a Color Space Array on XYZ colorspace for PostScript usage
1622
cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
1623
cmsHPROFILE hProfile,
1624
cmsUInt32Number Intent,
1625
cmsUInt32Number dwFlags,
1626
void* Buffer,
1627
cmsUInt32Number dwBufferLen)
1628
{
1629
cmsIOHANDLER* mem;
1630
cmsUInt32Number dwBytesUsed;
1631
1632
if (Buffer == NULL)
1633
mem = cmsOpenIOhandlerFromNULL(ContextID);
1634
else
1635
mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1636
1637
if (!mem) return 0;
1638
1639
dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
1640
1641
// Get rid of memory stream
1642
cmsCloseIOhandler(mem);
1643
1644
return dwBytesUsed;
1645
1646
}
1647
1648