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