Backout bug 449422.
[wine-gecko.git] / modules / lcms / src / cmsps2.c
blob8bd4f054ddc8f186f91b9ae4781d8045d19bc60a
1 //
2 // Little cms
3 // Copyright (C) 1998-2007 Marti Maria
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the Software
10 // is furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
17 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 // Postscript level 2 operators
28 #include "lcms.h"
29 #include <time.h>
30 #include <stdarg.h>
32 // PostScript ColorRenderingDictionary and ColorSpaceArray
34 LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCSA(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen);
35 LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRD(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen);
36 LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRDEx(cmsHPROFILE hProfile, int Intent, DWORD dwFlags, LPVOID Buffer, DWORD dwBufferLen);
37 // -------------------------------------------------------------------- Implementation
39 #define MAXPSCOLS 60 // Columns on tables
42 Implementation
43 --------------
45 PostScript does use XYZ as its internal PCS. But since PostScript
46 interpolation tables are limited to 8 bits, I use Lab as a way to
47 improve the accuracy, favoring perceptual results. So, for the creation
48 of each CRD, CSA the profiles are converted to Lab via a device
49 link between profile -> Lab or Lab -> profile. The PS code necessary to
50 convert Lab <-> XYZ is also included.
54 Color Space Arrays (CSA)
55 ==================================================================================
57 In order to obtain precission, code chooses between three ways to implement
58 the device -> XYZ transform. These cases identifies monochrome profiles (often
59 implemented as a set of curves), matrix-shaper and LUT-based.
61 Monochrome
62 -----------
64 This is implemented as /CIEBasedA CSA. The prelinearization curve is
65 placed into /DecodeA section, and matrix equals to D50. Since here is
66 no interpolation tables, I do the conversion directly to XYZ
68 NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT
69 flag is forced on such profiles.
71 [ /CIEBasedA
72 <<
73 /DecodeA { transfer function } bind
74 /MatrixA [D50]
75 /RangeLMN [ 0.0 D50X 0.0 D50Y 0.0 D50Z ]
76 /WhitePoint [D50]
77 /BlackPoint [BP]
78 /RenderingIntent (intent)
82 On simpler profiles, the PCS is already XYZ, so no conversion is required.
85 Matrix-shaper based
86 -------------------
88 This is implemented both with /CIEBasedABC or /CIEBasedDEF on dependig
89 of profile implementation. Since here is no interpolation tables, I do
90 the conversion directly to XYZ
94 [ /CIEBasedABC
96 /DecodeABC [ {transfer1} {transfer2} {transfer3} ]
97 /MatrixABC [Matrix]
98 /RangeLMN [ 0.0 D50X 0.0 D50Y 0.0 D50Z ]
99 /DecodeLMN [ { / 2} dup dup ]
100 /WhitePoint [D50]
101 /BlackPoint [BP]
102 /RenderingIntent (intent)
107 CLUT based
108 ----------
110 Lab is used in such cases.
112 [ /CIEBasedDEF
114 /DecodeDEF [ <prelinearization> ]
115 /Table [ p p p [<...>]]
116 /RangeABC [ 0 1 0 1 0 1]
117 /DecodeABC[ <postlinearization> ]
118 /RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]
119 % -128/500 1+127/500 0 1 -127/200 1+128/200
120 /MatrixABC [ 1 1 1 1 0 0 0 0 -1]
121 /WhitePoint [D50]
122 /BlackPoint [BP]
123 /RenderingIntent (intent)
127 Color Rendering Dictionaries (CRD)
128 ==================================
129 These are always implemented as CLUT, and always are using Lab. Since CRD are expected to
130 be used as resources, the code adds the definition as well.
133 /ColorRenderingType 1
134 /WhitePoint [ D50 ]
135 /BlackPoint [BP]
136 /MatrixPQR [ Bradford ]
137 /RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ]
138 /TransformPQR [
139 {4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind
140 {4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind
141 {4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind
143 /MatrixABC <...>
144 /EncodeABC <...>
145 /RangeABC <.. used for XYZ -> Lab>
146 /EncodeLMN
147 /RenderTable [ p p p [<...>]]
149 /RenderingIntent (Perceptual)
151 /Current exch /ColorRendering defineresource pop
154 The following stages are used to convert from XYZ to Lab
155 --------------------------------------------------------
157 Input is given at LMN stage on X, Y, Z
159 Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn)
161 /EncodeLMN [
163 { 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
164 { 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
165 { 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
170 MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn)
172 | 0 1 0|
173 | 1 -1 0|
174 | 0 1 -1|
176 /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]
178 EncodeABC finally gives Lab values.
180 /EncodeABC [
181 { 116 mul 16 sub 100 div } bind
182 { 500 mul 128 add 255 div } bind
183 { 200 mul 128 add 255 div } bind
186 The following stages are used to convert Lab to XYZ
187 ----------------------------------------------------
189 /RangeABC [ 0 1 0 1 0 1]
190 /DecodeABC [ { 100 mul 16 add 116 div } bind
191 { 255 mul 128 sub 500 div } bind
192 { 255 mul 128 sub 200 div } bind
195 /MatrixABC [ 1 1 1 1 0 0 0 0 -1]
196 /DecodeLMN [
197 {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind
198 {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind
199 {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind
207 PostScript algorithms discussion.
208 =========================================================================================================
210 1D interpolation algorithm
213 1D interpolation (float)
214 ------------------------
216 val2 = Domain * Value;
218 cell0 = (int) floor(val2);
219 cell1 = (int) ceil(val2);
221 rest = val2 - cell0;
223 y0 = LutTable[cell0] ;
224 y1 = LutTable[cell1] ;
226 y = y0 + (y1 - y0) * rest;
230 PostScript code Stack
231 ================================================
233 { % v
234 <check 0..1.0>
235 [array] % v tab
236 dup % v tab tab
237 length 1 sub % v tab dom
239 3 -1 roll % tab dom v
241 mul % tab val2
242 dup % tab val2 val2
243 dup % tab val2 val2 val2
244 floor cvi % tab val2 val2 cell0
245 exch % tab val2 cell0 val2
246 ceiling cvi % tab val2 cell0 cell1
248 3 index % tab val2 cell0 cell1 tab
249 exch % tab val2 cell0 tab cell1
250 get % tab val2 cell0 y1
252 4 -1 roll % val2 cell0 y1 tab
253 3 -1 roll % val2 y1 tab cell0
254 get % val2 y1 y0
256 dup % val2 y1 y0 y0
257 3 1 roll % val2 y0 y1 y0
259 sub % val2 y0 (y1-y0)
260 3 -1 roll % y0 (y1-y0) val2
261 dup % y0 (y1-y0) val2 val2
262 floor cvi % y0 (y1-y0) val2 floor(val2)
263 sub % y0 (y1-y0) rest
264 mul % y0 t1
265 add % y
266 65535 div % result
268 } bind
273 static icTagSignature Device2PCSTab[] = {icSigAToB0Tag, // Perceptual
274 icSigAToB1Tag, // Relative colorimetric
275 icSigAToB2Tag, // Saturation
276 icSigAToB1Tag }; // Absolute colorimetric
277 // (Relative/WhitePoint)
280 // --------------------------------------------------------------- Memory Stream
282 // This struct holds the memory block currently being write
285 typedef struct {
286 LPBYTE Block;
287 LPBYTE Ptr;
288 DWORD dwMax;
289 DWORD dwUsed;
290 int MaxCols;
291 int Col;
292 int HasError;
294 } MEMSTREAM, FAR* LPMEMSTREAM;
297 typedef struct {
298 LPLUT Lut;
299 LPMEMSTREAM m;
301 int FirstComponent;
302 int SecondComponent;
304 int bps;
305 const char* PreMaj;
306 const char* PostMaj;
307 const char* PreMin;
308 const char* PostMin;
310 int lIsInput; // Handle L* encoding
311 int FixWhite; // Force mapping of pure white
313 icColorSpaceSignature ColorSpace; // ColorSpace of profile
316 } SAMPLERCARGO, FAR* LPSAMPLERCARGO;
319 // Creates a ready to use memory stream
320 static
321 LPMEMSTREAM CreateMemStream(LPBYTE Buffer, DWORD dwMax, int MaxCols)
323 LPMEMSTREAM m = (LPMEMSTREAM) _cmsMalloc(sizeof(MEMSTREAM));
324 if (m == NULL) return NULL;
326 ZeroMemory(m, sizeof(MEMSTREAM));
328 m -> Block = m -> Ptr = Buffer;
329 m -> dwMax = dwMax;
330 m -> dwUsed = 0;
331 m -> MaxCols = MaxCols;
332 m -> Col = 0;
333 m -> HasError = 0;
335 return m;
340 // Convert to byte
341 static
342 BYTE Word2Byte(WORD w)
344 return (BYTE) floor((double) w / 257.0 + 0.5);
348 // Convert to byte (using ICC2 notation)
350 static
351 BYTE L2Byte(WORD w)
353 int ww = w + 0x0080;
355 if (ww > 0xFFFF) return 0xFF;
357 return (BYTE) ((WORD) (ww >> 8) & 0xFF);
360 // Write a raw, uncooked byte. Check for space
361 static
362 void WriteRawByte(LPMEMSTREAM m, BYTE b)
364 if (m -> dwUsed + 1 > m -> dwMax) {
365 m -> HasError = 1;
368 if (!m ->HasError && m ->Block) {
369 *m ->Ptr++ = b;
372 m -> dwUsed++;
375 // Write a cooked byte
376 static
377 void WriteByte(LPMEMSTREAM m, BYTE b)
379 static const BYTE Hex[] = "0123456789ABCDEF";
380 BYTE c;
382 c = Hex[(b >> 4) & 0x0f];
383 WriteRawByte(m, c);
385 c = Hex[b & 0x0f];
386 WriteRawByte(m, c);
388 m -> Col += 2;
390 if (m -> Col > m -> MaxCols) {
392 WriteRawByte(m, '\n');
393 m -> Col = 0;
398 // Does write a formatted string. Guaranteed to be 2048 bytes at most.
399 static
400 void Writef(LPMEMSTREAM m, const char *frm, ...)
402 va_list args;
403 LPBYTE pt;
404 BYTE Buffer[2048];
406 va_start(args, frm);
408 vsnprintf((char*) Buffer, 2048, frm, args);
410 for (pt = Buffer; *pt; pt++) {
412 WriteRawByte(m, *pt);
415 va_end(args);
420 // ----------------------------------------------------------------- PostScript generation
423 // Removes offending Carriage returns
424 static
425 char* RemoveCR(const char* txt)
427 static char Buffer[2048];
428 char* pt;
430 strncpy(Buffer, txt, 2047);
431 Buffer[2047] = 0;
432 for (pt = Buffer; *pt; pt++)
433 if (*pt == '\n' || *pt == '\r') *pt = ' ';
435 return Buffer;
439 static
440 void EmitHeader(LPMEMSTREAM m, const char* Title, cmsHPROFILE hProfile)
443 time_t timer;
445 time(&timer);
447 Writef(m, "%%!PS-Adobe-3.0\n");
448 Writef(m, "%%\n");
449 Writef(m, "%% %s\n", Title);
450 Writef(m, "%% Source: %s\n", RemoveCR(cmsTakeProductName(hProfile)));
451 Writef(m, "%% Description: %s\n", RemoveCR(cmsTakeProductDesc(hProfile)));
452 Writef(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!!
453 Writef(m, "%%\n");
454 Writef(m, "%%%%BeginResource\n");
459 // Emits White & Black point. White point is always D50, Black point is the device
460 // Black point adapted to D50.
462 static
463 void EmitWhiteBlackD50(LPMEMSTREAM m, LPcmsCIEXYZ BlackPoint)
466 Writef(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X,
467 BlackPoint -> Y,
468 BlackPoint -> Z);
470 Writef(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X,
471 cmsD50_XYZ()->Y,
472 cmsD50_XYZ()->Z);
476 static
477 void EmitRangeCheck(LPMEMSTREAM m)
479 Writef(m, "dup 0.0 lt { pop 0.0 } if "
480 "dup 1.0 gt { pop 1.0 } if ");
484 // Does write the intent
486 static
487 void EmitIntent(LPMEMSTREAM m, int RenderingIntent)
489 const char *intent;
491 switch (RenderingIntent) {
493 case INTENT_PERCEPTUAL: intent = "Perceptual"; break;
494 case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break;
495 case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break;
496 case INTENT_SATURATION: intent = "Saturation"; break;
498 default: intent = "Undefined"; break;
501 Writef(m, "/RenderingIntent (%s)\n", intent );
505 // Convert L* to Y
507 // Y = Yn*[ (L* + 16) / 116] ^ 3 if (L*) >= 6 / 29
508 // = Yn*( L* / 116) / 7.787 if (L*) < 6 / 29
512 static
513 void EmitL2Y(LPMEMSTREAM m)
515 Writef(m,
516 "{ "
517 "100 mul 16 add 116 div " // (L * 100 + 16) / 116
518 "dup 6 29 div ge " // >= 6 / 29 ?
519 "{ dup dup mul mul } " // yes, ^3 and done
520 "{ 4 29 div sub 108 841 div mul } " // no, slope limiting
521 "ifelse } bind ");
526 // Lab -> XYZ, see the discussion above
528 static
529 void EmitLab2XYZ(LPMEMSTREAM m)
531 Writef(m, "/RangeABC [ 0 1 0 1 0 1]\n");
532 Writef(m, "/DecodeABC [\n");
533 Writef(m, "{100 mul 16 add 116 div } bind\n");
534 Writef(m, "{255 mul 128 sub 500 div } bind\n");
535 Writef(m, "{255 mul 128 sub 200 div } bind\n");
536 Writef(m, "]\n");
537 Writef(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n");
538 Writef(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n");
539 Writef(m, "/DecodeLMN [\n");
540 Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n");
541 Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n");
542 Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n");
543 Writef(m, "]\n");
548 // Outputs a table of words. It does use 16 bits
550 static
551 void Emit1Gamma(LPMEMSTREAM m, LPWORD Table, int nEntries)
553 int i;
554 double gamma;
557 if (nEntries <= 0) return; // Empty table
559 // Suppress whole if identity
560 if (cmsIsLinear(Table, nEntries)) return;
562 // Check if is really an exponential. If so, emit "exp"
563 gamma = cmsEstimateGammaEx(Table, nEntries, 0.001);
564 if (gamma > 0) {
565 Writef(m, "{ %g exp } bind ", gamma);
566 return;
569 Writef(m, "{ ");
571 // Bounds check
572 EmitRangeCheck(m);
574 // Emit intepolation code
576 // PostScript code Stack
577 // =============== ========================
578 // v
579 Writef(m, " [");
581 // TODO: Check for endianess!!!
583 for (i=0; i < nEntries; i++) {
584 Writef(m, "%d ", Table[i]);
587 Writef(m, "] "); // v tab
589 Writef(m, "dup "); // v tab tab
590 Writef(m, "length 1 sub "); // v tab dom
591 Writef(m, "3 -1 roll "); // tab dom v
592 Writef(m, "mul "); // tab val2
593 Writef(m, "dup "); // tab val2 val2
594 Writef(m, "dup "); // tab val2 val2 val2
595 Writef(m, "floor cvi "); // tab val2 val2 cell0
596 Writef(m, "exch "); // tab val2 cell0 val2
597 Writef(m, "ceiling cvi "); // tab val2 cell0 cell1
598 Writef(m, "3 index "); // tab val2 cell0 cell1 tab
599 Writef(m, "exch "); // tab val2 cell0 tab cell1
600 Writef(m, "get "); // tab val2 cell0 y1
601 Writef(m, "4 -1 roll "); // val2 cell0 y1 tab
602 Writef(m, "3 -1 roll "); // val2 y1 tab cell0
603 Writef(m, "get "); // val2 y1 y0
604 Writef(m, "dup "); // val2 y1 y0 y0
605 Writef(m, "3 1 roll "); // val2 y0 y1 y0
606 Writef(m, "sub "); // val2 y0 (y1-y0)
607 Writef(m, "3 -1 roll "); // y0 (y1-y0) val2
608 Writef(m, "dup "); // y0 (y1-y0) val2 val2
609 Writef(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2)
610 Writef(m, "sub "); // y0 (y1-y0) rest
611 Writef(m, "mul "); // y0 t1
612 Writef(m, "add "); // y
613 Writef(m, "65535 div "); // result
615 Writef(m, " } bind ");
619 // Compare gamma table
621 static
622 LCMSBOOL GammaTableEquals(LPWORD g1, LPWORD g2, int nEntries)
624 return memcmp(g1, g2, nEntries* sizeof(WORD)) == 0;
628 // Does write a set of gamma curves
630 static
631 void EmitNGamma(LPMEMSTREAM m, int n, LPWORD g[], int nEntries)
633 int i;
635 for( i=0; i < n; i++ )
637 if (i > 0 && GammaTableEquals(g[i-1], g[i], nEntries)) {
639 Writef(m, "dup ");
641 else {
642 Emit1Gamma(m, g[i], nEntries);
649 // Check whatever a profile has CLUT tables (only on input)
651 static
652 LCMSBOOL IsLUTbased(cmsHPROFILE hProfile, int Intent)
654 icTagSignature Tag;
656 // Check if adequate tag is present
657 Tag = Device2PCSTab[Intent];
659 if (cmsIsTag(hProfile, Tag)) return 1;
661 // If not present, revert to default (perceptual)
662 Tag = icSigAToB0Tag;
664 // If no tag present, try matrix-shaper
665 return cmsIsTag(hProfile, Tag);
670 // Following code dumps a LUT onto memory stream
673 // This is the sampler. Intended to work in SAMPLER_INSPECT mode,
674 // that is, the callback will be called for each knot with
676 // In[] The grid location coordinates, normalized to 0..ffff
677 // Out[] The LUT values, normalized to 0..ffff
679 // Returning a value other than 0 does terminate the sampling process
681 // Each row contains LUT values for all but first component. So, I
682 // detect row changing by keeping a copy of last value of first
683 // component. -1 is used to mark begining of whole block.
685 static
686 int OutputValueSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
688 LPSAMPLERCARGO sc = (LPSAMPLERCARGO) Cargo;
689 unsigned int i;
692 if (sc -> FixWhite) {
694 if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8]
696 if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&
697 (In[2] >= 0x7800 && In[2] <= 0x8800)) {
699 WORD* Black;
700 WORD* White;
701 int nOutputs;
703 if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))
704 return 0;
706 for (i=0; i < (unsigned int) nOutputs; i++)
707 Out[i] = White[i];
715 // Hadle the parenthesis on rows
717 if (In[0] != sc ->FirstComponent) {
719 if (sc ->FirstComponent != -1) {
721 Writef(sc ->m, sc ->PostMin);
722 sc ->SecondComponent = -1;
723 Writef(sc ->m, sc ->PostMaj);
726 // Begin block
727 sc->m->Col = 0;
729 Writef(sc ->m, sc ->PreMaj);
730 sc ->FirstComponent = In[0];
734 if (In[1] != sc ->SecondComponent) {
736 if (sc ->SecondComponent != -1) {
738 Writef(sc ->m, sc ->PostMin);
741 Writef(sc ->m, sc ->PreMin);
742 sc ->SecondComponent = In[1];
747 // Dump table. Could be Word or byte based on
748 // depending on bps member (16 bps mode is not currently
749 // being used at all, but is here for future ampliations)
751 for (i=0; i < sc -> Lut ->OutputChan; i++) {
753 WORD wWordOut = Out[i];
755 if (sc ->bps == 8) {
757 // Value as byte
758 BYTE wByteOut;
760 // If is input, convert from Lab2 to Lab4 (just divide by 256)
762 if (sc ->lIsInput) {
765 wByteOut = L2Byte(wWordOut);
767 else
768 wByteOut = Word2Byte(wWordOut);
770 WriteByte(sc -> m, wByteOut);
772 else {
774 // Value as word
775 WriteByte(sc -> m, (BYTE) (wWordOut & 0xFF));
776 WriteByte(sc -> m, (BYTE) ((wWordOut >> 8) & 0xFF));
780 return 1;
783 // Writes a LUT on memstream. Could be 8 or 16 bits based
785 static
786 void WriteCLUT(LPMEMSTREAM m, LPLUT Lut, int bps, const char* PreMaj,
787 const char* PostMaj,
788 const char* PreMin,
789 const char* PostMin,
790 int lIsInput,
791 int FixWhite,
792 icColorSpaceSignature ColorSpace)
794 unsigned int i;
795 SAMPLERCARGO sc;
797 sc.FirstComponent = -1;
798 sc.SecondComponent = -1;
799 sc.Lut = Lut;
800 sc.m = m;
801 sc.bps = bps;
802 sc.PreMaj = PreMaj;
803 sc.PostMaj= PostMaj;
805 sc.PreMin = PreMin;
806 sc.PostMin = PostMin;
807 sc.lIsInput = lIsInput;
808 sc.FixWhite = FixWhite;
809 sc.ColorSpace = ColorSpace;
811 Writef(m, "[");
813 for (i=0; i < Lut ->InputChan; i++)
814 Writef(m, " %d ", Lut ->cLutPoints);
816 Writef(m, " [\n");
820 cmsSample3DGrid(Lut, OutputValueSampler, (LPVOID) &sc, SAMPLER_INSPECT);
823 Writef(m, PostMin);
824 Writef(m, PostMaj);
825 Writef(m, "] ");
832 // Dumps CIEBasedA Color Space Array
834 static
835 int EmitCIEBasedA(LPMEMSTREAM m, LPWORD Tab, int nEntries, LPcmsCIEXYZ BlackPoint)
838 Writef(m, "[ /CIEBasedA\n");
839 Writef(m, " <<\n");
841 Writef(m, "/DecodeA ");
843 Emit1Gamma(m,Tab, nEntries);
845 Writef(m, " \n");
847 Writef(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
848 Writef(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
850 EmitWhiteBlackD50(m, BlackPoint);
851 EmitIntent(m, INTENT_PERCEPTUAL);
853 Writef(m, ">>\n");
854 Writef(m, "]\n");
856 return 1;
860 // Dumps CIEBasedABC Color Space Array
862 static
863 int EmitCIEBasedABC(LPMEMSTREAM m, LPWORD L[], int nEntries, LPWMAT3 Matrix, LPcmsCIEXYZ BlackPoint)
865 int i;
867 Writef(m, "[ /CIEBasedABC\n");
868 Writef(m, "<<\n");
869 Writef(m, "/DecodeABC [ ");
871 EmitNGamma(m, 3, L, nEntries);
873 Writef(m, "]\n");
875 Writef(m, "/MatrixABC [ " );
877 for( i=0; i < 3; i++ ) {
879 Writef(m, "%.6f %.6f %.6f ",
880 FIXED_TO_DOUBLE(Matrix->v[0].n[i]),
881 FIXED_TO_DOUBLE(Matrix->v[1].n[i]),
882 FIXED_TO_DOUBLE(Matrix->v[2].n[i]));
886 Writef(m, "]\n");
888 Writef(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
890 EmitWhiteBlackD50(m, BlackPoint);
891 EmitIntent(m, INTENT_PERCEPTUAL);
893 Writef(m, ">>\n");
894 Writef(m, "]\n");
897 return 1;
901 static
902 int EmitCIEBasedDEF(LPMEMSTREAM m, LPLUT Lut, int Intent, LPcmsCIEXYZ BlackPoint)
904 const char* PreMaj;
905 const char* PostMaj;
906 const char* PreMin, *PostMin;
908 switch (Lut ->InputChan) {
909 case 3:
911 Writef(m, "[ /CIEBasedDEF\n");
912 PreMaj ="<";
913 PostMaj= ">\n";
914 PreMin = PostMin = "";
915 break;
916 case 4:
917 Writef(m, "[ /CIEBasedDEFG\n");
918 PreMaj = "[";
919 PostMaj = "]\n";
920 PreMin = "<";
921 PostMin = ">\n";
922 break;
923 default:
924 return 0;
928 Writef(m, "<<\n");
930 if (Lut ->wFlags & LUT_HASTL1) {
932 Writef(m, "/DecodeDEF [ ");
933 EmitNGamma(m, Lut ->InputChan, Lut ->L1, Lut ->CLut16params.nSamples);
934 Writef(m, "]\n");
939 if (Lut ->wFlags & LUT_HAS3DGRID) {
941 Writef(m, "/Table ");
942 WriteCLUT(m, Lut, 8, PreMaj, PostMaj, PreMin, PostMin, TRUE, FALSE, (icColorSpaceSignature) 0);
943 Writef(m, "]\n");
946 EmitLab2XYZ(m);
947 EmitWhiteBlackD50(m, BlackPoint);
948 EmitIntent(m, Intent);
950 Writef(m, " >>\n");
951 Writef(m, "]\n");
954 return 1;
957 // Generates a curve from a gray profile
959 static
960 LPGAMMATABLE ExtractGray2Y(cmsHPROFILE hProfile, int Intent)
962 LPGAMMATABLE Out = cmsAllocGamma(256);
963 cmsHPROFILE hXYZ = cmsCreateXYZProfile();
964 cmsHTRANSFORM xform = cmsCreateTransform(hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOTPRECALC);
965 int i;
967 for (i=0; i < 256; i++) {
969 BYTE Gray = (BYTE) i;
970 cmsCIEXYZ XYZ;
972 cmsDoTransform(xform, &Gray, &XYZ, 1);
974 Out ->GammaTable[i] =_cmsClampWord((int) floor(XYZ.Y * 65535.0 + 0.5));
977 cmsDeleteTransform(xform);
978 cmsCloseProfile(hXYZ);
979 return Out;
984 // Because PostScrip has only 8 bits in /Table, we should use
985 // a more perceptually uniform space... I do choose Lab.
987 static
988 int WriteInputLUT(LPMEMSTREAM m, cmsHPROFILE hProfile, int Intent)
990 cmsHPROFILE hLab;
991 cmsHTRANSFORM xform;
992 icColorSpaceSignature ColorSpace;
993 int nChannels;
994 DWORD InputFormat;
995 int rc;
996 cmsHPROFILE Profiles[2];
997 cmsCIEXYZ BlackPointAdaptedToD50;
999 // Does create a device-link based transform.
1000 // The DeviceLink is next dumped as working CSA.
1002 hLab = cmsCreateLabProfile(NULL);
1003 ColorSpace = cmsGetColorSpace(hProfile);
1004 nChannels = _cmsChannelsOf(ColorSpace);
1005 InputFormat = CHANNELS_SH(nChannels) | BYTES_SH(2);
1007 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent,LCMS_BPFLAGS_D50_ADAPTED);
1009 // Is a devicelink profile?
1010 if (cmsGetDeviceClass(hProfile) == icSigLinkClass) {
1012 // if devicelink output already Lab, use it directly
1014 if (cmsGetPCS(hProfile) == icSigLabData) {
1016 xform = cmsCreateTransform(hProfile, InputFormat, NULL,
1017 TYPE_Lab_DBL, Intent, 0);
1019 else {
1021 // Nope, adjust output to Lab if possible
1023 Profiles[0] = hProfile;
1024 Profiles[1] = hLab;
1026 xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat,
1027 TYPE_Lab_DBL, Intent, 0);
1032 else {
1034 // This is a normal profile
1035 xform = cmsCreateTransform(hProfile, InputFormat, hLab,
1036 TYPE_Lab_DBL, Intent, 0);
1041 if (xform == NULL) {
1043 cmsSignalError(LCMS_ERRC_ABORTED, "Cannot create transform Profile -> Lab");
1044 return 0;
1047 // Only 1, 3 and 4 channels are allowed
1049 switch (nChannels) {
1051 case 1: {
1052 LPGAMMATABLE Gray2Y = ExtractGray2Y(hProfile, Intent);
1053 EmitCIEBasedA(m, Gray2Y->GammaTable, Gray2Y ->nEntries, &BlackPointAdaptedToD50);
1054 cmsFreeGamma(Gray2Y);
1056 break;
1058 case 3:
1059 case 4: {
1060 LPLUT DeviceLink;
1061 _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
1063 if (v ->DeviceLink)
1064 rc = EmitCIEBasedDEF(m, v->DeviceLink, Intent, &BlackPointAdaptedToD50);
1065 else {
1066 DeviceLink = _cmsPrecalculateDeviceLink(xform, 0);
1067 rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
1068 cmsFreeLUT(DeviceLink);
1071 break;
1073 default:
1075 cmsSignalError(LCMS_ERRC_ABORTED, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
1076 return 0;
1080 cmsDeleteTransform(xform);
1081 cmsCloseProfile(hLab);
1082 return 1;
1087 // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
1089 static
1090 int WriteInputMatrixShaper(LPMEMSTREAM m, cmsHPROFILE hProfile)
1092 icColorSpaceSignature ColorSpace;
1093 LPMATSHAPER MatShaper;
1094 int rc;
1095 cmsCIEXYZ BlackPointAdaptedToD50;
1098 ColorSpace = cmsGetColorSpace(hProfile);
1099 MatShaper = cmsBuildInputMatrixShaper(hProfile);
1101 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_BPFLAGS_D50_ADAPTED);
1103 if (MatShaper == NULL) {
1105 cmsSignalError(LCMS_ERRC_ABORTED, "This profile is not suitable for input");
1106 return 0;
1109 if (ColorSpace == icSigGrayData) {
1111 rc = EmitCIEBasedA(m, MatShaper ->L[0],
1112 MatShaper ->p16.nSamples,
1113 &BlackPointAdaptedToD50);
1116 else
1117 if (ColorSpace == icSigRgbData) {
1120 rc = EmitCIEBasedABC(m, MatShaper->L,
1121 MatShaper ->p16.nSamples,
1122 &MatShaper ->Matrix,
1123 &BlackPointAdaptedToD50);
1125 else {
1127 cmsSignalError(LCMS_ERRC_ABORTED, "Profile is not suitable for CSA. Unsupported colorspace.");
1128 return 0;
1131 cmsFreeMatShaper(MatShaper);
1132 return rc;
1137 // Creates a PostScript color list from a named profile data.
1138 // This is a HP extension, and it works in Lab instead of XYZ
1140 static
1141 int WriteNamedColorCSA(LPMEMSTREAM m, cmsHPROFILE hNamedColor, int Intent)
1143 cmsHTRANSFORM xform;
1144 cmsHPROFILE hLab;
1145 int i, nColors;
1146 char ColorName[32];
1149 hLab = cmsCreateLabProfile(NULL);
1150 xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX,
1151 hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC);
1152 if (xform == NULL) return 0;
1155 Writef(m, "<<\n");
1156 Writef(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1157 Writef(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1158 Writef(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1160 nColors = cmsNamedColorCount(xform);
1163 for (i=0; i < nColors; i++) {
1165 WORD In[1];
1166 cmsCIELab Lab;
1168 In[0] = (WORD) i;
1170 if (!cmsNamedColorInfo(xform, i, ColorName, NULL, NULL))
1171 continue;
1173 cmsDoTransform(xform, In, &Lab, 1);
1174 Writef(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1179 Writef(m, ">>\n");
1181 cmsDeleteTransform(xform);
1182 cmsCloseProfile(hLab);
1183 return 1;
1187 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1189 DWORD LCMSEXPORT cmsGetPostScriptCSA(cmsHPROFILE hProfile,
1190 int Intent,
1191 LPVOID Buffer, DWORD dwBufferLen)
1194 LPMEMSTREAM mem;
1195 DWORD dwBytesUsed;
1197 // Set up the serialization engine
1198 mem = CreateMemStream((LPBYTE) Buffer, dwBufferLen, MAXPSCOLS);
1199 if (!mem) return 0;
1202 // Is a named color profile?
1203 if (cmsGetDeviceClass(hProfile) == icSigNamedColorClass) {
1205 if (!WriteNamedColorCSA(mem, hProfile, Intent)) {
1207 _cmsFree((void*) mem);
1208 return 0;
1211 else {
1214 // Any profile class are allowed (including devicelink), but
1215 // output (PCS) colorspace must be XYZ or Lab
1216 icColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1218 if (ColorSpace != icSigXYZData &&
1219 ColorSpace != icSigLabData) {
1221 cmsSignalError(LCMS_ERRC_ABORTED, "Invalid output color space");
1222 _cmsFree((void*) mem);
1223 return 0;
1226 // Is there any CLUT?
1227 if (IsLUTbased(hProfile, Intent)) {
1229 // Yes, so handle as LUT-based
1230 if (!WriteInputLUT(mem, hProfile, Intent)) {
1232 _cmsFree((void*) mem);
1233 return 0;
1236 else {
1238 // No, try Matrix-shaper (this only works on XYZ)
1240 if (!WriteInputMatrixShaper(mem, hProfile)) {
1242 _cmsFree((void*) mem); // Something went wrong
1243 return 0;
1249 // Done, keep memory usage
1250 dwBytesUsed = mem ->dwUsed;
1252 // Get rid of memory stream
1253 _cmsFree((void*) mem);
1255 // Finally, return used byte count
1256 return dwBytesUsed;
1259 // ------------------------------------------------------ Color Rendering Dictionary (CRD)
1265 Black point compensation plus chromatic adaptation:
1267 Step 1 - Chromatic adaptation
1268 =============================
1270 WPout
1271 X = ------- PQR
1272 Wpin
1274 Step 2 - Black point compensation
1275 =================================
1277 (WPout - BPout)*X - WPout*(BPin - BPout)
1278 out = ---------------------------------------
1279 WPout - BPin
1282 Algorithm discussion
1283 ====================
1285 TransformPQR(WPin, BPin, WPout, BPout, PQR)
1287 Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1290 Algorithm Stack 0...n
1291 ===========================================================
1292 PQR BPout WPout BPin WPin
1293 4 index 3 get WPin PQR BPout WPout BPin WPin
1294 div (PQR/WPin) BPout WPout BPin WPin
1295 2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin
1296 mult WPout*(PQR/WPin) BPout WPout BPin WPin
1298 2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1299 2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1300 sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
1301 mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1303 2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1304 4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1305 3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1307 sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1308 mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1309 sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1311 3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1312 3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1313 exch
1314 sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1315 div
1317 exch pop
1318 exch pop
1319 exch pop
1320 exch pop
1325 static
1326 void EmitPQRStage(LPMEMSTREAM m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1330 if (lIsAbsolute) {
1332 // For absolute colorimetric intent, encode back to relative
1333 // and generate a relative LUT
1335 // Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1337 cmsCIEXYZ White;
1339 cmsTakeMediaWhitePoint(&White, hProfile);
1341 Writef(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1342 Writef(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1344 Writef(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1345 "/TransformPQR [\n"
1346 "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1347 "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1348 "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
1349 White.X, White.Y, White.Z);
1350 return;
1354 Writef(m,"%% Bradford Cone Space\n"
1355 "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1357 Writef(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1360 // No BPC
1362 if (!DoBPC) {
1364 Writef(m, "%% VonKries-like transform in Bradford Cone Space\n"
1365 "/TransformPQR [\n"
1366 "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1367 "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1368 "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
1369 } else {
1371 // BPC
1373 Writef(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1374 "/TransformPQR [\n");
1376 Writef(m, "{4 index 3 get div 2 index 3 get mul "
1377 "2 index 3 get 2 index 3 get sub mul "
1378 "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1379 "3 index 3 get 3 index 3 get exch sub div "
1380 "exch pop exch pop exch pop exch pop } bind\n");
1382 Writef(m, "{4 index 4 get div 2 index 4 get mul "
1383 "2 index 4 get 2 index 4 get sub mul "
1384 "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1385 "3 index 4 get 3 index 4 get exch sub div "
1386 "exch pop exch pop exch pop exch pop } bind\n");
1388 Writef(m, "{4 index 5 get div 2 index 5 get mul "
1389 "2 index 5 get 2 index 5 get sub mul "
1390 "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1391 "3 index 5 get 3 index 5 get exch sub div "
1392 "exch pop exch pop exch pop exch pop } bind\n]\n");
1400 static
1401 void EmitXYZ2Lab(LPMEMSTREAM m)
1403 Writef(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
1404 Writef(m, "/EncodeLMN [\n");
1405 Writef(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1406 Writef(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1407 Writef(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1408 Writef(m, "]\n");
1409 Writef(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1410 Writef(m, "/EncodeABC [\n");
1413 Writef(m, "{ 116 mul 16 sub 100 div } bind\n");
1414 Writef(m, "{ 500 mul 128 add 256 div } bind\n");
1415 Writef(m, "{ 200 mul 128 add 256 div } bind\n");
1418 Writef(m, "]\n");
1423 // Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1424 // I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1425 // space on 3D CLUT, but since space seems not to be a problem here, 33 points
1426 // would give a reasonable accurancy. Note also that CRD tables must operate in
1427 // 8 bits.
1429 static
1430 int WriteOutputLUT(LPMEMSTREAM m, cmsHPROFILE hProfile, int Intent, DWORD dwFlags)
1432 cmsHPROFILE hLab;
1433 cmsHTRANSFORM xform;
1434 icColorSpaceSignature ColorSpace;
1435 int i, nChannels;
1436 DWORD OutputFormat;
1437 _LPcmsTRANSFORM v;
1438 LPLUT DeviceLink;
1439 cmsHPROFILE Profiles[3];
1440 cmsCIEXYZ BlackPointAdaptedToD50;
1441 LCMSBOOL lFreeDeviceLink = FALSE;
1442 LCMSBOOL lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1443 LCMSBOOL lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1444 int RelativeEncodingIntent;
1448 hLab = cmsCreateLabProfile(NULL);
1450 ColorSpace = cmsGetColorSpace(hProfile);
1451 nChannels = _cmsChannelsOf(ColorSpace);
1452 OutputFormat = CHANNELS_SH(nChannels) | BYTES_SH(2);
1454 // For absolute colorimetric, the LUT is encoded as relative
1455 // in order to preserve precission.
1457 RelativeEncodingIntent = Intent;
1458 if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1459 RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1462 // Is a devicelink profile?
1463 if (cmsGetDeviceClass(hProfile) == icSigLinkClass) {
1465 // if devicelink input already in Lab
1467 if (ColorSpace == icSigLabData) {
1469 // adjust input to Lab to our v4
1471 Profiles[0] = hLab;
1472 Profiles[1] = hProfile;
1474 xform = cmsCreateMultiprofileTransform(Profiles, 2, TYPE_Lab_DBL,
1475 OutputFormat, RelativeEncodingIntent,
1476 dwFlags|cmsFLAGS_NOWHITEONWHITEFIXUP|cmsFLAGS_NOPRELINEARIZATION);
1479 else {
1480 cmsSignalError(LCMS_ERRC_ABORTED, "Cannot use devicelink profile for CRD creation");
1481 return 0;
1486 else {
1488 // This is a normal profile
1489 xform = cmsCreateTransform(hLab, TYPE_Lab_DBL, hProfile,
1490 OutputFormat, RelativeEncodingIntent, dwFlags|cmsFLAGS_NOWHITEONWHITEFIXUP|cmsFLAGS_NOPRELINEARIZATION);
1493 if (xform == NULL) {
1495 cmsSignalError(LCMS_ERRC_ABORTED, "Cannot create transform Lab -> Profile in CRD creation");
1496 return 0;
1499 // Get the internal precalculated devicelink
1501 v = (_LPcmsTRANSFORM) xform;
1502 DeviceLink = v ->DeviceLink;
1504 if (!DeviceLink) {
1506 DeviceLink = _cmsPrecalculateDeviceLink(xform, cmsFLAGS_NOPRELINEARIZATION);
1507 lFreeDeviceLink = TRUE;
1510 Writef(m, "<<\n");
1511 Writef(m, "/ColorRenderingType 1\n");
1514 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, LCMS_BPFLAGS_D50_ADAPTED);
1516 // Emit headers, etc.
1517 EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1518 EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1519 EmitXYZ2Lab(m);
1521 if (DeviceLink ->wFlags & LUT_HASTL1) {
1523 // Shouldn't happen
1524 cmsSignalError(LCMS_ERRC_ABORTED, "Internal error (prelinearization on CRD)");
1525 return 0;
1529 // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
1530 // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
1531 // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1532 // zero. This would sacrifice a bit of highlights, but failure to do so would cause
1533 // scum dot. Ouch.
1535 if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1536 lFixWhite = FALSE;
1538 Writef(m, "/RenderTable ");
1540 WriteCLUT(m, DeviceLink, 8, "<", ">\n", "", "", FALSE,
1541 lFixWhite, ColorSpace);
1543 Writef(m, " %d {} bind ", nChannels);
1545 for (i=1; i < nChannels; i++)
1546 Writef(m, "dup ");
1548 Writef(m, "]\n");
1551 EmitIntent(m, Intent);
1553 Writef(m, ">>\n");
1555 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1557 Writef(m, "/Current exch /ColorRendering defineresource pop\n");
1560 if (lFreeDeviceLink) cmsFreeLUT(DeviceLink);
1561 cmsDeleteTransform(xform);
1562 cmsCloseProfile(hLab);
1564 return 1;
1568 // Builds a ASCII string containing colorant list in 0..1.0 range
1569 static
1570 void BuildColorantList(char *Colorant, int nColorant, WORD Out[])
1572 char Buff[32];
1573 int j;
1575 Colorant[0] = 0;
1576 if (nColorant > MAXCHANNELS)
1577 nColorant = MAXCHANNELS;
1579 for (j=0; j < nColorant; j++) {
1581 sprintf(Buff, "%.3f", Out[j] / 65535.0);
1582 strcat(Colorant, Buff);
1583 if (j < nColorant -1)
1584 strcat(Colorant, " ");
1590 // Creates a PostScript color list from a named profile data.
1591 // This is a HP extension.
1593 static
1594 int WriteNamedColorCRD(LPMEMSTREAM m, cmsHPROFILE hNamedColor, int Intent, DWORD dwFlags)
1596 cmsHTRANSFORM xform;
1597 int i, nColors, nColorant;
1598 DWORD OutputFormat;
1599 char ColorName[32];
1600 char Colorant[128];
1602 nColorant = _cmsChannelsOf(cmsGetColorSpace(hNamedColor));
1603 OutputFormat = CHANNELS_SH(nColorant) | BYTES_SH(2);
1605 xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX,
1606 NULL, OutputFormat, Intent, cmsFLAGS_NOTPRECALC);
1607 if (xform == NULL) return 0;
1610 Writef(m, "<<\n");
1611 Writef(m, "(colorlistcomment) (%s) \n", "Named profile");
1612 Writef(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1613 Writef(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1615 nColors = cmsNamedColorCount(xform);
1618 for (i=0; i < nColors; i++) {
1620 WORD In[1];
1621 WORD Out[MAXCHANNELS];
1623 In[0] = (WORD) i;
1625 if (!cmsNamedColorInfo(xform, i, ColorName, NULL, NULL))
1626 continue;
1628 cmsDoTransform(xform, In, Out, 1);
1629 BuildColorantList(Colorant, nColorant, Out);
1630 Writef(m, " (%s) [ %s ]\n", ColorName, Colorant);
1633 Writef(m, " >>");
1635 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1637 Writef(m, " /Current exch /HPSpotTable defineresource pop\n");
1640 cmsDeleteTransform(xform);
1641 return 1;
1646 // This one does create a Color Rendering Dictionary.
1647 // CRD are always LUT-Based, no matter if profile is
1648 // implemented as matrix-shaper.
1650 DWORD LCMSEXPORT cmsGetPostScriptCRDEx(cmsHPROFILE hProfile,
1651 int Intent, DWORD dwFlags,
1652 LPVOID Buffer, DWORD dwBufferLen)
1655 LPMEMSTREAM mem;
1656 DWORD dwBytesUsed;
1658 // Set up the serialization artifact
1659 mem = CreateMemStream((LPBYTE) Buffer, dwBufferLen, MAXPSCOLS);
1660 if (!mem) return 0;
1663 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1665 EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1669 // Is a named color profile?
1670 if (cmsGetDeviceClass(hProfile) == icSigNamedColorClass) {
1672 if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1674 _cmsFree((void*) mem);
1675 return 0;
1678 else {
1680 // CRD are always implemented as LUT.
1683 if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1684 _cmsFree((void*) mem);
1685 return 0;
1689 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1691 Writef(mem, "%%%%EndResource\n");
1692 Writef(mem, "\n%% CRD End\n");
1695 // Done, keep memory usage
1696 dwBytesUsed = mem ->dwUsed;
1698 // Get rid of memory stream
1699 _cmsFree((void*) mem);
1701 // Finally, return used byte count
1702 return dwBytesUsed;
1706 // For compatibility with previous versions
1708 DWORD LCMSEXPORT cmsGetPostScriptCRD(cmsHPROFILE hProfile,
1709 int Intent,
1710 LPVOID Buffer, DWORD dwBufferLen)
1712 return cmsGetPostScriptCRDEx(hProfile, Intent, 0, Buffer, dwBufferLen);