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