grub2: bring back build of aros-side grub2 tools
[AROS.git] / workbench / libs / lcms2 / src / cmsps2.c
blob24ff5f3101ac38fb7708a593f4dc8a8c767d4f3f
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2011 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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
29 // PostScript ColorRenderingDictionary and ColorSpaceArray
32 #define MAXPSCOLS 60 // Columns on tables
35 Implementation
36 --------------
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.
47 Color Space Arrays (CSA)
48 ==================================================================================
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.
54 Monochrome
55 -----------
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
61 NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT
62 flag is forced on such profiles.
64 [ /CIEBasedA
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)
75 On simpler profiles, the PCS is already XYZ, so no conversion is required.
78 Matrix-shaper based
79 -------------------
81 This is implemented both with /CIEBasedABC or /CIEBasedDEF on dependig
82 of profile implementation. Since here there are no interpolation tables, I do
83 the conversion directly to XYZ
87 [ /CIEBasedABC
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)
100 CLUT based
101 ----------
103 Lab is used in such cases.
105 [ /CIEBasedDEF
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)
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.
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
136 /MatrixABC <...>
137 /EncodeABC <...>
138 /RangeABC <.. used for XYZ -> Lab>
139 /EncodeLMN
140 /RenderTable [ p p p [<...>]]
142 /RenderingIntent (Perceptual)
144 /Current exch /ColorRendering defineresource pop
147 The following stages are used to convert from XYZ to Lab
148 --------------------------------------------------------
150 Input is given at LMN stage on X, Y, Z
152 Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn)
154 /EncodeLMN [
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
163 MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn)
165 | 0 1 0|
166 | 1 -1 0|
167 | 0 1 -1|
169 /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]
171 EncodeABC finally gives Lab values.
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
179 The following stages are used to convert Lab to XYZ
180 ----------------------------------------------------
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
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
200 PostScript algorithms discussion.
201 =========================================================================================================
203 1D interpolation algorithm
206 1D interpolation (float)
207 ------------------------
209 val2 = Domain * Value;
211 cell0 = (int) floor(val2);
212 cell1 = (int) ceil(val2);
214 rest = val2 - cell0;
216 y0 = LutTable[cell0] ;
217 y1 = LutTable[cell1] ;
219 y = y0 + (y1 - y0) * rest;
223 PostScript code Stack
224 ================================================
226 { % v
227 <check 0..1.0>
228 [array] % v tab
229 dup % v tab tab
230 length 1 sub % v tab dom
232 3 -1 roll % tab dom v
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
241 3 index % tab val2 cell0 cell1 tab
242 exch % tab val2 cell0 tab cell1
243 get % tab val2 cell0 y1
245 4 -1 roll % val2 cell0 y1 tab
246 3 -1 roll % val2 y1 tab cell0
247 get % val2 y1 y0
249 dup % val2 y1 y0 y0
250 3 1 roll % val2 y0 y1 y0
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
261 } bind
267 // This struct holds the memory block currently being write
268 typedef struct {
269 _cmsStageCLutData* Pipeline;
270 cmsIOHANDLER* m;
272 int FirstComponent;
273 int SecondComponent;
275 const char* PreMaj;
276 const char* PostMaj;
277 const char* PreMin;
278 const char* PostMin;
280 int FixWhite; // Force mapping of pure white
282 cmsColorSpaceSignature ColorSpace; // ColorSpace of profile
285 } cmsPsSamplerCargo;
287 static int _cmsPSActualColumn = 0;
290 // Convert to byte
291 static
292 cmsUInt8Number Word2Byte(cmsUInt16Number w)
294 return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5);
298 // Convert to byte (using ICC2 notation)
300 static
301 cmsUInt8Number L2Byte(cmsUInt16Number w)
303 int ww = w + 0x0080;
305 if (ww > 0xFFFF) return 0xFF;
307 return (cmsUInt8Number) ((cmsUInt16Number) (ww >> 8) & 0xFF);
311 // Write a cooked byte
313 static
314 void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b)
316 _cmsIOPrintf(m, "%02x", b);
317 _cmsPSActualColumn += 2;
319 if (_cmsPSActualColumn > MAXPSCOLS) {
321 _cmsIOPrintf(m, "\n");
322 _cmsPSActualColumn = 0;
326 // ----------------------------------------------------------------- PostScript generation
329 // Removes offending Carriage returns
330 static
331 char* RemoveCR(const char* txt)
333 static char Buffer[2048];
334 char* pt;
336 strncpy(Buffer, txt, 2047);
337 Buffer[2047] = 0;
338 for (pt = Buffer; *pt; pt++)
339 if (*pt == '\n' || *pt == '\r') *pt = ' ';
341 return Buffer;
345 static
346 void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile)
348 time_t timer;
349 cmsMLU *Description, *Copyright;
350 char DescASCII[256], CopyrightASCII[256];
352 time(&timer);
354 Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag);
355 Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag);
357 DescASCII[0] = DescASCII[255] = 0;
358 CopyrightASCII[0] = CopyrightASCII[255] = 0;
360 if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255);
361 if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255);
363 _cmsIOPrintf(m, "%%!PS-Adobe-3.0\n");
364 _cmsIOPrintf(m, "%%\n");
365 _cmsIOPrintf(m, "%% %s\n", Title);
366 _cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII));
367 _cmsIOPrintf(m, "%% %s\n", RemoveCR(CopyrightASCII));
368 _cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!!
369 _cmsIOPrintf(m, "%%\n");
370 _cmsIOPrintf(m, "%%%%BeginResource\n");
375 // Emits White & Black point. White point is always D50, Black point is the device
376 // Black point adapted to D50.
378 static
379 void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint)
382 _cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X,
383 BlackPoint -> Y,
384 BlackPoint -> Z);
386 _cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X,
387 cmsD50_XYZ()->Y,
388 cmsD50_XYZ()->Z);
392 static
393 void EmitRangeCheck(cmsIOHANDLER* m)
395 _cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if "
396 "dup 1.0 gt { pop 1.0 } if ");
400 // Does write the intent
402 static
403 void EmitIntent(cmsIOHANDLER* m, int RenderingIntent)
405 const char *intent;
407 switch (RenderingIntent) {
409 case INTENT_PERCEPTUAL: intent = "Perceptual"; break;
410 case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break;
411 case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break;
412 case INTENT_SATURATION: intent = "Saturation"; break;
414 default: intent = "Undefined"; break;
417 _cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent );
421 // Convert L* to Y
423 // Y = Yn*[ (L* + 16) / 116] ^ 3 if (L*) >= 6 / 29
424 // = Yn*( L* / 116) / 7.787 if (L*) < 6 / 29
428 static
429 void EmitL2Y(cmsIOHANDLER* m)
431 _cmsIOPrintf(m,
432 "{ "
433 "100 mul 16 add 116 div " // (L * 100 + 16) / 116
434 "dup 6 29 div ge " // >= 6 / 29 ?
435 "{ dup dup mul mul } " // yes, ^3 and done
436 "{ 4 29 div sub 108 841 div mul } " // no, slope limiting
437 "ifelse } bind ");
442 // Lab -> XYZ, see the discussion above
444 static
445 void EmitLab2XYZ(cmsIOHANDLER* m)
447 _cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n");
448 _cmsIOPrintf(m, "/DecodeABC [\n");
449 _cmsIOPrintf(m, "{100 mul 16 add 116 div } bind\n");
450 _cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n");
451 _cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n");
452 _cmsIOPrintf(m, "]\n");
453 _cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n");
454 _cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n");
455 _cmsIOPrintf(m, "/DecodeLMN [\n");
456 _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");
457 _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n");
458 _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");
459 _cmsIOPrintf(m, "]\n");
464 // Outputs a table of words. It does use 16 bits
466 static
467 void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
469 cmsUInt32Number i;
470 cmsFloat64Number gamma;
472 if (Table == NULL) return; // Error
474 if (Table ->nEntries <= 0) return; // Empty table
476 // Suppress whole if identity
477 if (cmsIsToneCurveLinear(Table)) return;
479 // Check if is really an exponential. If so, emit "exp"
480 gamma = cmsEstimateGamma(Table, 0.001);
481 if (gamma > 0) {
482 _cmsIOPrintf(m, "{ %g exp } bind ", gamma);
483 return;
486 _cmsIOPrintf(m, "{ ");
488 // Bounds check
489 EmitRangeCheck(m);
491 // Emit intepolation code
493 // PostScript code Stack
494 // =============== ========================
495 // v
496 _cmsIOPrintf(m, " [");
498 for (i=0; i < Table->nEntries; i++) {
499 _cmsIOPrintf(m, "%d ", Table->Table16[i]);
502 _cmsIOPrintf(m, "] "); // v tab
504 _cmsIOPrintf(m, "dup "); // v tab tab
505 _cmsIOPrintf(m, "length 1 sub "); // v tab dom
506 _cmsIOPrintf(m, "3 -1 roll "); // tab dom v
507 _cmsIOPrintf(m, "mul "); // tab val2
508 _cmsIOPrintf(m, "dup "); // tab val2 val2
509 _cmsIOPrintf(m, "dup "); // tab val2 val2 val2
510 _cmsIOPrintf(m, "floor cvi "); // tab val2 val2 cell0
511 _cmsIOPrintf(m, "exch "); // tab val2 cell0 val2
512 _cmsIOPrintf(m, "ceiling cvi "); // tab val2 cell0 cell1
513 _cmsIOPrintf(m, "3 index "); // tab val2 cell0 cell1 tab
514 _cmsIOPrintf(m, "exch "); // tab val2 cell0 tab cell1
515 _cmsIOPrintf(m, "get "); // tab val2 cell0 y1
516 _cmsIOPrintf(m, "4 -1 roll "); // val2 cell0 y1 tab
517 _cmsIOPrintf(m, "3 -1 roll "); // val2 y1 tab cell0
518 _cmsIOPrintf(m, "get "); // val2 y1 y0
519 _cmsIOPrintf(m, "dup "); // val2 y1 y0 y0
520 _cmsIOPrintf(m, "3 1 roll "); // val2 y0 y1 y0
521 _cmsIOPrintf(m, "sub "); // val2 y0 (y1-y0)
522 _cmsIOPrintf(m, "3 -1 roll "); // y0 (y1-y0) val2
523 _cmsIOPrintf(m, "dup "); // y0 (y1-y0) val2 val2
524 _cmsIOPrintf(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2)
525 _cmsIOPrintf(m, "sub "); // y0 (y1-y0) rest
526 _cmsIOPrintf(m, "mul "); // y0 t1
527 _cmsIOPrintf(m, "add "); // y
528 _cmsIOPrintf(m, "65535 div "); // result
530 _cmsIOPrintf(m, " } bind ");
534 // Compare gamma table
536 static
537 cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, int nEntries)
539 return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0;
543 // Does write a set of gamma curves
545 static
546 void EmitNGamma(cmsIOHANDLER* m, int n, cmsToneCurve* g[])
548 int i;
550 for( i=0; i < n; i++ )
552 if (g[i] == NULL) return; // Error
554 if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) {
556 _cmsIOPrintf(m, "dup ");
558 else {
559 Emit1Gamma(m, g[i]);
569 // Following code dumps a LUT onto memory stream
572 // This is the sampler. Intended to work in SAMPLER_INSPECT mode,
573 // that is, the callback will be called for each knot with
575 // In[] The grid location coordinates, normalized to 0..ffff
576 // Out[] The Pipeline values, normalized to 0..ffff
578 // Returning a value other than 0 does terminate the sampling process
580 // Each row contains Pipeline values for all but first component. So, I
581 // detect row changing by keeping a copy of last value of first
582 // component. -1 is used to mark begining of whole block.
584 static
585 int OutputValueSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
587 cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;
588 cmsUInt32Number i;
591 if (sc -> FixWhite) {
593 if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8]
595 if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&
596 (In[2] >= 0x7800 && In[2] <= 0x8800)) {
598 cmsUInt16Number* Black;
599 cmsUInt16Number* White;
600 cmsUInt32Number nOutputs;
602 if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))
603 return 0;
605 for (i=0; i < nOutputs; i++)
606 Out[i] = White[i];
614 // Hadle the parenthesis on rows
616 if (In[0] != sc ->FirstComponent) {
618 if (sc ->FirstComponent != -1) {
620 _cmsIOPrintf(sc ->m, sc ->PostMin);
621 sc ->SecondComponent = -1;
622 _cmsIOPrintf(sc ->m, sc ->PostMaj);
625 // Begin block
626 _cmsPSActualColumn = 0;
628 _cmsIOPrintf(sc ->m, sc ->PreMaj);
629 sc ->FirstComponent = In[0];
633 if (In[1] != sc ->SecondComponent) {
635 if (sc ->SecondComponent != -1) {
637 _cmsIOPrintf(sc ->m, sc ->PostMin);
640 _cmsIOPrintf(sc ->m, sc ->PreMin);
641 sc ->SecondComponent = In[1];
644 // Dump table.
646 for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {
648 cmsUInt16Number wWordOut = Out[i];
649 cmsUInt8Number wByteOut; // Value as byte
652 // We always deal with Lab4
654 wByteOut = Word2Byte(wWordOut);
655 WriteByte(sc -> m, wByteOut);
658 return 1;
661 // Writes a Pipeline on memstream. Could be 8 or 16 bits based
663 static
664 void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
665 const char* PostMaj,
666 const char* PreMin,
667 const char* PostMin,
668 int FixWhite,
669 cmsColorSpaceSignature ColorSpace)
671 cmsUInt32Number i;
672 cmsPsSamplerCargo sc;
674 sc.FirstComponent = -1;
675 sc.SecondComponent = -1;
676 sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
677 sc.m = m;
678 sc.PreMaj = PreMaj;
679 sc.PostMaj= PostMaj;
681 sc.PreMin = PreMin;
682 sc.PostMin = PostMin;
683 sc.FixWhite = FixWhite;
684 sc.ColorSpace = ColorSpace;
686 _cmsIOPrintf(m, "[");
688 for (i=0; i < sc.Pipeline->Params->nInputs; i++)
689 _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
691 _cmsIOPrintf(m, " [\n");
693 cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT);
695 _cmsIOPrintf(m, PostMin);
696 _cmsIOPrintf(m, PostMaj);
697 _cmsIOPrintf(m, "] ");
702 // Dumps CIEBasedA Color Space Array
704 static
705 int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
708 _cmsIOPrintf(m, "[ /CIEBasedA\n");
709 _cmsIOPrintf(m, " <<\n");
711 _cmsIOPrintf(m, "/DecodeA ");
713 Emit1Gamma(m, Curve);
715 _cmsIOPrintf(m, " \n");
717 _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
718 _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
720 EmitWhiteBlackD50(m, BlackPoint);
721 EmitIntent(m, INTENT_PERCEPTUAL);
723 _cmsIOPrintf(m, ">>\n");
724 _cmsIOPrintf(m, "]\n");
726 return 1;
730 // Dumps CIEBasedABC Color Space Array
732 static
733 int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)
735 int i;
737 _cmsIOPrintf(m, "[ /CIEBasedABC\n");
738 _cmsIOPrintf(m, "<<\n");
739 _cmsIOPrintf(m, "/DecodeABC [ ");
741 EmitNGamma(m, 3, CurveSet);
743 _cmsIOPrintf(m, "]\n");
745 _cmsIOPrintf(m, "/MatrixABC [ " );
747 for( i=0; i < 3; i++ ) {
749 _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],
750 Matrix[i + 3*1],
751 Matrix[i + 3*2]);
755 _cmsIOPrintf(m, "]\n");
757 _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
759 EmitWhiteBlackD50(m, BlackPoint);
760 EmitIntent(m, INTENT_PERCEPTUAL);
762 _cmsIOPrintf(m, ">>\n");
763 _cmsIOPrintf(m, "]\n");
766 return 1;
770 static
771 int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXYZ* BlackPoint)
773 const char* PreMaj;
774 const char* PostMaj;
775 const char* PreMin, *PostMin;
776 cmsStage* mpe;
778 mpe = Pipeline ->Elements;
780 switch (cmsStageInputChannels(mpe)) {
781 case 3:
783 _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
784 PreMaj ="<";
785 PostMaj= ">\n";
786 PreMin = PostMin = "";
787 break;
788 case 4:
789 _cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
790 PreMaj = "[";
791 PostMaj = "]\n";
792 PreMin = "<";
793 PostMin = ">\n";
794 break;
795 default:
796 return 0;
800 _cmsIOPrintf(m, "<<\n");
802 if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
804 _cmsIOPrintf(m, "/DecodeDEF [ ");
805 EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
806 _cmsIOPrintf(m, "]\n");
808 mpe = mpe ->Next;
811 if (cmsStageType(mpe) == cmsSigCLutElemType) {
813 _cmsIOPrintf(m, "/Table ");
814 WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0);
815 _cmsIOPrintf(m, "]\n");
818 EmitLab2XYZ(m);
819 EmitWhiteBlackD50(m, BlackPoint);
820 EmitIntent(m, Intent);
822 _cmsIOPrintf(m, " >>\n");
823 _cmsIOPrintf(m, "]\n");
825 return 1;
828 // Generates a curve from a gray profile
830 static
831 cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
833 cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
834 cmsHPROFILE hXYZ = cmsCreateXYZProfile();
835 cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
836 int i;
838 if (Out != NULL) {
839 for (i=0; i < 256; i++) {
841 cmsUInt8Number Gray = (cmsUInt8Number) i;
842 cmsCIEXYZ XYZ;
844 cmsDoTransform(xform, &Gray, &XYZ, 1);
846 Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);
850 cmsDeleteTransform(xform);
851 cmsCloseProfile(hXYZ);
852 return Out;
857 // Because PostScript has only 8 bits in /Table, we should use
858 // a more perceptually uniform space... I do choose Lab.
860 static
861 int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
863 cmsHPROFILE hLab;
864 cmsHTRANSFORM xform;
865 cmsUInt32Number nChannels;
866 cmsUInt32Number InputFormat;
867 int rc;
868 cmsHPROFILE Profiles[2];
869 cmsCIEXYZ BlackPointAdaptedToD50;
871 // Does create a device-link based transform.
872 // The DeviceLink is next dumped as working CSA.
874 InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
875 nChannels = T_CHANNELS(InputFormat);
878 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
880 // Adjust output to Lab4
881 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
883 Profiles[0] = hProfile;
884 Profiles[1] = hLab;
886 xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);
887 cmsCloseProfile(hLab);
889 if (xform == NULL) {
891 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
892 return 0;
895 // Only 1, 3 and 4 channels are allowed
897 switch (nChannels) {
899 case 1: {
900 cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
901 EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
902 cmsFreeToneCurve(Gray2Y);
904 break;
906 case 3:
907 case 4: {
908 cmsUInt32Number OutFrm = TYPE_Lab_16;
909 cmsPipeline* DeviceLink;
910 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
912 DeviceLink = cmsPipelineDup(v ->Lut);
913 if (DeviceLink == NULL) return 0;
915 dwFlags |= cmsFLAGS_FORCE_CLUT;
916 _cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
918 rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
919 cmsPipelineFree(DeviceLink);
920 if (rc == 0) return 0;
922 break;
924 default:
926 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
927 return 0;
931 cmsDeleteTransform(xform);
933 return 1;
936 static
937 cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)
939 _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
941 return Data -> Double;
945 // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
947 static
948 int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
950 cmsColorSpaceSignature ColorSpace;
951 int rc;
952 cmsCIEXYZ BlackPointAdaptedToD50;
954 ColorSpace = cmsGetColorSpace(hProfile);
956 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
958 if (ColorSpace == cmsSigGrayData) {
960 cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
961 rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
964 else
965 if (ColorSpace == cmsSigRgbData) {
967 cmsMAT3 Mat;
968 int i, j;
970 memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
972 for (i=0; i < 3; i++)
973 for (j=0; j < 3; j++)
974 Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
976 rc = EmitCIEBasedABC(m, (cmsFloat64Number *) &Mat,
977 _cmsStageGetPtrToCurveSet(Shaper),
978 &BlackPointAdaptedToD50);
980 else {
982 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
983 return 0;
986 return rc;
991 // Creates a PostScript color list from a named profile data.
992 // This is a HP extension, and it works in Lab instead of XYZ
994 static
995 int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
997 cmsHTRANSFORM xform;
998 cmsHPROFILE hLab;
999 int i, nColors;
1000 char ColorName[32];
1001 cmsNAMEDCOLORLIST* NamedColorList;
1003 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1004 xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
1005 if (xform == NULL) return 0;
1007 NamedColorList = cmsGetNamedColorList(xform);
1008 if (NamedColorList == NULL) return 0;
1010 _cmsIOPrintf(m, "<<\n");
1011 _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1012 _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1013 _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1015 nColors = cmsNamedColorCount(NamedColorList);
1018 for (i=0; i < nColors; i++) {
1020 cmsUInt16Number In[1];
1021 cmsCIELab Lab;
1023 In[0] = (cmsUInt16Number) i;
1025 if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1026 continue;
1028 cmsDoTransform(xform, In, &Lab, 1);
1029 _cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1034 _cmsIOPrintf(m, ">>\n");
1036 cmsDeleteTransform(xform);
1037 cmsCloseProfile(hLab);
1038 return 1;
1042 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1043 static
1044 cmsUInt32Number GenerateCSA(cmsContext ContextID,
1045 cmsHPROFILE hProfile,
1046 cmsUInt32Number Intent,
1047 cmsUInt32Number dwFlags,
1048 cmsIOHANDLER* mem)
1050 cmsUInt32Number dwBytesUsed;
1051 cmsPipeline* lut = NULL;
1052 cmsStage* Matrix, *Shaper;
1055 // Is a named color profile?
1056 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1058 if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
1060 else {
1063 // Any profile class are allowed (including devicelink), but
1064 // output (PCS) colorspace must be XYZ or Lab
1065 cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1067 if (ColorSpace != cmsSigXYZData &&
1068 ColorSpace != cmsSigLabData) {
1070 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
1071 goto Error;
1075 // Read the lut with all necessary conversion stages
1076 lut = _cmsReadInputLUT(hProfile, Intent);
1077 if (lut == NULL) goto Error;
1080 // Tone curves + matrix can be implemented without any LUT
1081 if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
1083 if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
1086 else {
1087 // We need a LUT for the rest
1088 if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
1093 // Done, keep memory usage
1094 dwBytesUsed = mem ->UsedSpace;
1096 // Get rid of LUT
1097 if (lut != NULL) cmsPipelineFree(lut);
1099 // Finally, return used byte count
1100 return dwBytesUsed;
1102 Error:
1103 if (lut != NULL) cmsPipelineFree(lut);
1104 return 0;
1107 // ------------------------------------------------------ Color Rendering Dictionary (CRD)
1113 Black point compensation plus chromatic adaptation:
1115 Step 1 - Chromatic adaptation
1116 =============================
1118 WPout
1119 X = ------- PQR
1120 Wpin
1122 Step 2 - Black point compensation
1123 =================================
1125 (WPout - BPout)*X - WPout*(BPin - BPout)
1126 out = ---------------------------------------
1127 WPout - BPin
1130 Algorithm discussion
1131 ====================
1133 TransformPQR(WPin, BPin, WPout, BPout, PQR)
1135 Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1138 Algorithm Stack 0...n
1139 ===========================================================
1140 PQR BPout WPout BPin WPin
1141 4 index 3 get WPin PQR BPout WPout BPin WPin
1142 div (PQR/WPin) BPout WPout BPin WPin
1143 2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin
1144 mult WPout*(PQR/WPin) BPout WPout BPin WPin
1146 2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1147 2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1148 sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
1149 mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1151 2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1152 4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1153 3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1155 sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1156 mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1157 sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1159 3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1160 3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1161 exch
1162 sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1165 exch pop
1166 exch pop
1167 exch pop
1168 exch pop
1173 static
1174 void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1178 if (lIsAbsolute) {
1180 // For absolute colorimetric intent, encode back to relative
1181 // and generate a relative Pipeline
1183 // Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1185 cmsCIEXYZ White;
1187 _cmsReadMediaWhitePoint(&White, hProfile);
1189 _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1190 _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1192 _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1193 "/TransformPQR [\n"
1194 "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1195 "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1196 "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
1197 White.X, White.Y, White.Z);
1198 return;
1202 _cmsIOPrintf(m,"%% Bradford Cone Space\n"
1203 "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1205 _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1208 // No BPC
1210 if (!DoBPC) {
1212 _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"
1213 "/TransformPQR [\n"
1214 "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1215 "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1216 "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
1217 } else {
1219 // BPC
1221 _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1222 "/TransformPQR [\n");
1224 _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "
1225 "2 index 3 get 2 index 3 get sub mul "
1226 "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1227 "3 index 3 get 3 index 3 get exch sub div "
1228 "exch pop exch pop exch pop exch pop } bind\n");
1230 _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "
1231 "2 index 4 get 2 index 4 get sub mul "
1232 "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1233 "3 index 4 get 3 index 4 get exch sub div "
1234 "exch pop exch pop exch pop exch pop } bind\n");
1236 _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "
1237 "2 index 5 get 2 index 5 get sub mul "
1238 "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1239 "3 index 5 get 3 index 5 get exch sub div "
1240 "exch pop exch pop exch pop exch pop } bind\n]\n");
1248 static
1249 void EmitXYZ2Lab(cmsIOHANDLER* m)
1251 _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
1252 _cmsIOPrintf(m, "/EncodeLMN [\n");
1253 _cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1254 _cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1255 _cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1256 _cmsIOPrintf(m, "]\n");
1257 _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1258 _cmsIOPrintf(m, "/EncodeABC [\n");
1261 _cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n");
1262 _cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n");
1263 _cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n");
1266 _cmsIOPrintf(m, "]\n");
1271 // Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1272 // I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1273 // space on 3D CLUT, but since space seems not to be a problem here, 33 points
1274 // would give a reasonable accurancy. Note also that CRD tables must operate in
1275 // 8 bits.
1277 static
1278 int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
1280 cmsHPROFILE hLab;
1281 cmsHTRANSFORM xform;
1282 int i, nChannels;
1283 cmsUInt32Number OutputFormat;
1284 _cmsTRANSFORM* v;
1285 cmsPipeline* DeviceLink;
1286 cmsHPROFILE Profiles[3];
1287 cmsCIEXYZ BlackPointAdaptedToD50;
1288 cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1289 cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1290 cmsUInt32Number InFrm = TYPE_Lab_16;
1291 int RelativeEncodingIntent;
1292 cmsColorSpaceSignature ColorSpace;
1295 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1296 if (hLab == NULL) return 0;
1298 OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
1299 nChannels = T_CHANNELS(OutputFormat);
1301 ColorSpace = cmsGetColorSpace(hProfile);
1303 // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
1305 RelativeEncodingIntent = Intent;
1306 if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1307 RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1310 // Use V4 Lab always
1311 Profiles[0] = hLab;
1312 Profiles[1] = hProfile;
1314 xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
1315 Profiles, 2, TYPE_Lab_DBL,
1316 OutputFormat, RelativeEncodingIntent, 0);
1317 cmsCloseProfile(hLab);
1319 if (xform == NULL) {
1321 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
1322 return 0;
1325 // Get a copy of the internal devicelink
1326 v = (_cmsTRANSFORM*) xform;
1327 DeviceLink = cmsPipelineDup(v ->Lut);
1328 if (DeviceLink == NULL) return 0;
1331 // We need a CLUT
1332 dwFlags |= cmsFLAGS_FORCE_CLUT;
1333 _cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
1335 _cmsIOPrintf(m, "<<\n");
1336 _cmsIOPrintf(m, "/ColorRenderingType 1\n");
1339 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
1341 // Emit headers, etc.
1342 EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1343 EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1344 EmitXYZ2Lab(m);
1347 // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
1348 // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
1349 // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1350 // zero. This would sacrifice a bit of highlights, but failure to do so would cause
1351 // scum dot. Ouch.
1353 if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1354 lFixWhite = FALSE;
1356 _cmsIOPrintf(m, "/RenderTable ");
1359 WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);
1361 _cmsIOPrintf(m, " %d {} bind ", nChannels);
1363 for (i=1; i < nChannels; i++)
1364 _cmsIOPrintf(m, "dup ");
1366 _cmsIOPrintf(m, "]\n");
1369 EmitIntent(m, Intent);
1371 _cmsIOPrintf(m, ">>\n");
1373 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1375 _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");
1378 cmsPipelineFree(DeviceLink);
1379 cmsDeleteTransform(xform);
1381 return 1;
1385 // Builds a ASCII string containing colorant list in 0..1.0 range
1386 static
1387 void BuildColorantList(char *Colorant, int nColorant, cmsUInt16Number Out[])
1389 char Buff[32];
1390 int j;
1392 Colorant[0] = 0;
1393 if (nColorant > cmsMAXCHANNELS)
1394 nColorant = cmsMAXCHANNELS;
1396 for (j=0; j < nColorant; j++) {
1398 sprintf(Buff, "%.3f", Out[j] / 65535.0);
1399 strcat(Colorant, Buff);
1400 if (j < nColorant -1)
1401 strcat(Colorant, " ");
1407 // Creates a PostScript color list from a named profile data.
1408 // This is a HP extension.
1410 static
1411 int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cmsUInt32Number dwFlags)
1413 cmsHTRANSFORM xform;
1414 int i, nColors, nColorant;
1415 cmsUInt32Number OutputFormat;
1416 char ColorName[32];
1417 char Colorant[128];
1418 cmsNAMEDCOLORLIST* NamedColorList;
1421 OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
1422 nColorant = T_CHANNELS(OutputFormat);
1425 xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
1426 if (xform == NULL) return 0;
1429 NamedColorList = cmsGetNamedColorList(xform);
1430 if (NamedColorList == NULL) return 0;
1432 _cmsIOPrintf(m, "<<\n");
1433 _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
1434 _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1435 _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1437 nColors = cmsNamedColorCount(NamedColorList);
1439 for (i=0; i < nColors; i++) {
1441 cmsUInt16Number In[1];
1442 cmsUInt16Number Out[cmsMAXCHANNELS];
1444 In[0] = (cmsUInt16Number) i;
1446 if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1447 continue;
1449 cmsDoTransform(xform, In, Out, 1);
1450 BuildColorantList(Colorant, nColorant, Out);
1451 _cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant);
1454 _cmsIOPrintf(m, " >>");
1456 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1458 _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");
1461 cmsDeleteTransform(xform);
1462 return 1;
1467 // This one does create a Color Rendering Dictionary.
1468 // CRD are always LUT-Based, no matter if profile is
1469 // implemented as matrix-shaper.
1471 static
1472 cmsUInt32Number GenerateCRD(cmsContext ContextID,
1473 cmsHPROFILE hProfile,
1474 cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1475 cmsIOHANDLER* mem)
1477 cmsUInt32Number dwBytesUsed;
1479 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1481 EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1485 // Is a named color profile?
1486 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1488 if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1489 return 0;
1492 else {
1494 // CRD are always implemented as LUT
1496 if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1497 return 0;
1501 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1503 _cmsIOPrintf(mem, "%%%%EndResource\n");
1504 _cmsIOPrintf(mem, "\n%% CRD End\n");
1507 // Done, keep memory usage
1508 dwBytesUsed = mem ->UsedSpace;
1510 // Finally, return used byte count
1511 return dwBytesUsed;
1513 cmsUNUSED_PARAMETER(ContextID);
1519 cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
1520 cmsPSResourceType Type,
1521 cmsHPROFILE hProfile,
1522 cmsUInt32Number Intent,
1523 cmsUInt32Number dwFlags,
1524 cmsIOHANDLER* io)
1526 cmsUInt32Number rc;
1529 switch (Type) {
1531 case cmsPS_RESOURCE_CSA:
1532 rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
1533 break;
1535 default:
1536 case cmsPS_RESOURCE_CRD:
1537 rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
1538 break;
1541 return rc;
1546 cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
1547 cmsHPROFILE hProfile,
1548 cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1549 void* Buffer, cmsUInt32Number dwBufferLen)
1551 cmsIOHANDLER* mem;
1552 cmsUInt32Number dwBytesUsed;
1554 // Set up the serialization engine
1555 if (Buffer == NULL)
1556 mem = cmsOpenIOhandlerFromNULL(ContextID);
1557 else
1558 mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1560 if (!mem) return 0;
1562 dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
1564 // Get rid of memory stream
1565 cmsCloseIOhandler(mem);
1567 return dwBytesUsed;
1572 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1573 cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
1574 cmsHPROFILE hProfile,
1575 cmsUInt32Number Intent,
1576 cmsUInt32Number dwFlags,
1577 void* Buffer,
1578 cmsUInt32Number dwBufferLen)
1580 cmsIOHANDLER* mem;
1581 cmsUInt32Number dwBytesUsed;
1583 if (Buffer == NULL)
1584 mem = cmsOpenIOhandlerFromNULL(ContextID);
1585 else
1586 mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1588 if (!mem) return 0;
1590 dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
1592 // Get rid of memory stream
1593 cmsCloseIOhandler(mem);
1595 return dwBytesUsed;