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