1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2011 Marti Maria Saguer
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
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.
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.
66 /DecodeA { transfer function } bind
68 /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]
71 /RenderingIntent (intent)
75 On simpler profiles, the PCS is already XYZ, so no conversion is required.
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
89 /DecodeABC [ {transfer1} {transfer2} {transfer3} ]
91 /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]
92 /DecodeLMN [ { / 2} dup dup ]
95 /RenderingIntent (intent)
103 Lab is used in such cases.
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]
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
129 /MatrixPQR [ Bradford ]
130 /RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ]
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
138 /RangeABC <.. used for XYZ -> Lab>
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)
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)
169 /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]
171 EncodeABC finally gives Lab values.
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]
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);
216 y0 = LutTable[cell0] ;
217 y1 = LutTable[cell1] ;
219 y = y0 + (y1 - y0) * rest;
223 PostScript code Stack
224 ================================================
230 length 1 sub % v tab dom
232 3 -1 roll % tab dom v
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
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
267 // This struct holds the memory block currently being write
269 _cmsStageCLutData
* Pipeline
;
280 int FixWhite
; // Force mapping of pure white
282 cmsColorSpaceSignature ColorSpace
; // ColorSpace of profile
287 static int _cmsPSActualColumn
= 0;
292 cmsUInt8Number
Word2Byte(cmsUInt16Number w
)
294 return (cmsUInt8Number
) floor((cmsFloat64Number
) w
/ 257.0 + 0.5);
298 // Convert to byte (using ICC2 notation)
301 cmsUInt8Number L2Byte(cmsUInt16Number w)
305 if (ww > 0xFFFF) return 0xFF;
307 return (cmsUInt8Number) ((cmsUInt16Number) (ww >> 8) & 0xFF);
311 // Write a cooked byte
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
331 char* RemoveCR(const char* txt
)
333 static char Buffer
[2048];
336 strncpy(Buffer
, txt
, 2047);
338 for (pt
= Buffer
; *pt
; pt
++)
339 if (*pt
== '\n' || *pt
== '\r') *pt
= ' ';
346 void EmitHeader(cmsIOHANDLER
* m
, const char* Title
, cmsHPROFILE hProfile
)
349 cmsMLU
*Description
, *Copyright
;
350 char DescASCII
[256], CopyrightASCII
[256];
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.
379 void EmitWhiteBlackD50(cmsIOHANDLER
* m
, cmsCIEXYZ
* BlackPoint
)
382 _cmsIOPrintf(m
, "/BlackPoint [%f %f %f]\n", BlackPoint
-> X
,
386 _cmsIOPrintf(m
, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X
,
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
403 void EmitIntent(cmsIOHANDLER
* m
, int RenderingIntent
)
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
);
423 // Y = Yn*[ (L* + 16) / 116] ^ 3 if (L*) >= 6 / 29
424 // = Yn*( L* / 116) / 7.787 if (L*) < 6 / 29
429 void EmitL2Y(cmsIOHANDLER* m)
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
442 // Lab -> XYZ, see the discussion above
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
467 void Emit1Gamma(cmsIOHANDLER
* m
, cmsToneCurve
* Table
)
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);
482 _cmsIOPrintf(m
, "{ %g exp } bind ", gamma
);
486 _cmsIOPrintf(m
, "{ ");
491 // Emit intepolation code
493 // PostScript code Stack
494 // =============== ========================
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
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
546 void EmitNGamma(cmsIOHANDLER
* m
, int n
, cmsToneCurve
* g
[])
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 ");
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.
585 int OutputValueSampler(register const cmsUInt16Number In
[], register cmsUInt16Number Out
[], register void* Cargo
)
587 cmsPsSamplerCargo
* sc
= (cmsPsSamplerCargo
*) Cargo
;
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
))
605 for (i
=0; i
< nOutputs
; 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
);
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];
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
);
661 // Writes a Pipeline on memstream. Could be 8 or 16 bits based
664 void WriteCLUT(cmsIOHANDLER
* m
, cmsStage
* mpe
, const char* PreMaj
,
669 cmsColorSpaceSignature ColorSpace
)
672 cmsPsSamplerCargo sc
;
674 sc
.FirstComponent
= -1;
675 sc
.SecondComponent
= -1;
676 sc
.Pipeline
= (_cmsStageCLutData
*) mpe
->Data
;
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
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");
730 // Dumps CIEBasedABC Color Space Array
733 int EmitCIEBasedABC(cmsIOHANDLER
* m
, cmsFloat64Number
* Matrix
, cmsToneCurve
** CurveSet
, cmsCIEXYZ
* BlackPoint
)
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],
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");
771 int EmitCIEBasedDEF(cmsIOHANDLER
* m
, cmsPipeline
* Pipeline
, int Intent
, cmsCIEXYZ
* BlackPoint
)
775 const char* PreMin
, *PostMin
;
778 mpe
= Pipeline
->Elements
;
780 switch (cmsStageInputChannels(mpe
)) {
783 _cmsIOPrintf(m
, "[ /CIEBasedDEF\n");
786 PreMin
= PostMin
= "";
789 _cmsIOPrintf(m
, "[ /CIEBasedDEFG\n");
800 _cmsIOPrintf(m
, "<<\n");
802 if (cmsStageType(mpe
) == cmsSigCurveSetElemType
) {
804 _cmsIOPrintf(m
, "/DecodeDEF [ ");
805 EmitNGamma(m
, cmsStageOutputChannels(mpe
), _cmsStageGetPtrToCurveSet(mpe
));
806 _cmsIOPrintf(m
, "]\n");
811 if (cmsStageType(mpe
) == cmsSigCLutElemType
) {
813 _cmsIOPrintf(m
, "/Table ");
814 WriteCLUT(m
, mpe
, PreMaj
, PostMaj
, PreMin
, PostMin
, FALSE
, (cmsColorSpaceSignature
) 0);
815 _cmsIOPrintf(m
, "]\n");
819 EmitWhiteBlackD50(m
, BlackPoint
);
820 EmitIntent(m
, Intent
);
822 _cmsIOPrintf(m
, " >>\n");
823 _cmsIOPrintf(m
, "]\n");
828 // Generates a curve from a gray profile
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
);
839 for (i
=0; i
< 256; i
++) {
841 cmsUInt8Number Gray
= (cmsUInt8Number
) i
;
844 cmsDoTransform(xform
, &Gray
, &XYZ
, 1);
846 Out
->Table16
[i
] =_cmsQuickSaturateWord(XYZ
.Y
* 65535.0);
850 cmsDeleteTransform(xform
);
851 cmsCloseProfile(hXYZ
);
857 // Because PostScript has only 8 bits in /Table, we should use
858 // a more perceptually uniform space... I do choose Lab.
861 int WriteInputLUT(cmsIOHANDLER
* m
, cmsHPROFILE hProfile
, int Intent
, cmsUInt32Number dwFlags
)
865 cmsUInt32Number nChannels
;
866 cmsUInt32Number InputFormat
;
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
;
886 xform
= cmsCreateMultiprofileTransform(Profiles
, 2, InputFormat
, TYPE_Lab_DBL
, Intent
, 0);
887 cmsCloseProfile(hLab
);
891 cmsSignalError(m
->ContextID
, cmsERROR_COLORSPACE_CHECK
, "Cannot create transform Profile -> Lab");
895 // Only 1, 3 and 4 channels are allowed
900 cmsToneCurve
* Gray2Y
= ExtractGray2Y(m
->ContextID
, hProfile
, Intent
);
901 EmitCIEBasedA(m
, Gray2Y
, &BlackPointAdaptedToD50
);
902 cmsFreeToneCurve(Gray2Y
);
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;
926 cmsSignalError(m
->ContextID
, cmsERROR_COLORSPACE_CHECK
, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels
);
931 cmsDeleteTransform(xform
);
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
948 int WriteInputMatrixShaper(cmsIOHANDLER
* m
, cmsHPROFILE hProfile
, cmsStage
* Matrix
, cmsStage
* Shaper
)
950 cmsColorSpaceSignature ColorSpace
;
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
);
965 if (ColorSpace
== cmsSigRgbData
) {
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
);
982 cmsSignalError(m
->ContextID
, cmsERROR_COLORSPACE_CHECK
, "Profile is not suitable for CSA. Unsupported colorspace.");
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
995 int WriteNamedColorCSA(cmsIOHANDLER
* m
, cmsHPROFILE hNamedColor
, int Intent
)
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];
1023 In
[0] = (cmsUInt16Number
) i
;
1025 if (!cmsNamedColorInfo(NamedColorList
, i
, ColorName
, NULL
, NULL
, NULL
, NULL
))
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
);
1042 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1044 cmsUInt32Number
GenerateCSA(cmsContext ContextID
,
1045 cmsHPROFILE hProfile
,
1046 cmsUInt32Number Intent
,
1047 cmsUInt32Number dwFlags
,
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
;
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");
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
;
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
;
1097 if (lut
!= NULL
) cmsPipelineFree(lut
);
1099 // Finally, return used byte count
1103 if (lut
!= NULL
) cmsPipelineFree(lut
);
1107 // ------------------------------------------------------ Color Rendering Dictionary (CRD)
1113 Black point compensation plus chromatic adaptation:
1115 Step 1 - Chromatic adaptation
1116 =============================
1122 Step 2 - Black point compensation
1123 =================================
1125 (WPout - BPout)*X - WPout*(BPin - BPout)
1126 out = ---------------------------------------
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
1162 sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1174 void EmitPQRStage(cmsIOHANDLER
* m
, cmsHPROFILE hProfile
, int DoBPC
, int 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)
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"
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
);
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");
1212 _cmsIOPrintf(m
, "%% VonKries-like transform in Bradford Cone Space\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");
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");
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
1278 int WriteOutputLUT(cmsIOHANDLER
* m
, cmsHPROFILE hProfile
, int Intent
, cmsUInt32Number dwFlags
)
1281 cmsHTRANSFORM xform
;
1283 cmsUInt32Number OutputFormat
;
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
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");
1325 // Get a copy of the internal devicelink
1326 v
= (_cmsTRANSFORM
*) xform
;
1327 DeviceLink
= cmsPipelineDup(v
->Lut
);
1328 if (DeviceLink
== NULL
) return 0;
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
);
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
1353 if (Intent
== INTENT_ABSOLUTE_COLORIMETRIC
)
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
);
1385 // Builds a ASCII string containing colorant list in 0..1.0 range
1387 void BuildColorantList(char *Colorant
, int nColorant
, cmsUInt16Number Out
[])
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.
1411 int WriteNamedColorCRD(cmsIOHANDLER
* m
, cmsHPROFILE hNamedColor
, int Intent
, cmsUInt32Number dwFlags
)
1413 cmsHTRANSFORM xform
;
1414 int i
, nColors
, nColorant
;
1415 cmsUInt32Number OutputFormat
;
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
))
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
);
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.
1472 cmsUInt32Number
GenerateCRD(cmsContext ContextID
,
1473 cmsHPROFILE hProfile
,
1474 cmsUInt32Number Intent
, cmsUInt32Number dwFlags
,
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
)) {
1494 // CRD are always implemented as LUT
1496 if (!WriteOutputLUT(mem
, hProfile
, Intent
, dwFlags
)) {
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
1513 cmsUNUSED_PARAMETER(ContextID
);
1519 cmsUInt32Number CMSEXPORT
cmsGetPostScriptColorResource(cmsContext ContextID
,
1520 cmsPSResourceType Type
,
1521 cmsHPROFILE hProfile
,
1522 cmsUInt32Number Intent
,
1523 cmsUInt32Number dwFlags
,
1531 case cmsPS_RESOURCE_CSA
:
1532 rc
= GenerateCSA(ContextID
, hProfile
, Intent
, dwFlags
, io
);
1536 case cmsPS_RESOURCE_CRD
:
1537 rc
= GenerateCRD(ContextID
, hProfile
, Intent
, dwFlags
, io
);
1546 cmsUInt32Number CMSEXPORT
cmsGetPostScriptCRD(cmsContext ContextID
,
1547 cmsHPROFILE hProfile
,
1548 cmsUInt32Number Intent
, cmsUInt32Number dwFlags
,
1549 void* Buffer
, cmsUInt32Number dwBufferLen
)
1552 cmsUInt32Number dwBytesUsed
;
1554 // Set up the serialization engine
1556 mem
= cmsOpenIOhandlerFromNULL(ContextID
);
1558 mem
= cmsOpenIOhandlerFromMem(ContextID
, Buffer
, dwBufferLen
, "w");
1562 dwBytesUsed
= cmsGetPostScriptColorResource(ContextID
, cmsPS_RESOURCE_CRD
, hProfile
, Intent
, dwFlags
, mem
);
1564 // Get rid of memory stream
1565 cmsCloseIOhandler(mem
);
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
,
1578 cmsUInt32Number dwBufferLen
)
1581 cmsUInt32Number dwBytesUsed
;
1584 mem
= cmsOpenIOhandlerFromNULL(ContextID
);
1586 mem
= cmsOpenIOhandlerFromMem(ContextID
, Buffer
, dwBufferLen
, "w");
1590 dwBytesUsed
= cmsGetPostScriptColorResource(ContextID
, cmsPS_RESOURCE_CSA
, hProfile
, Intent
, dwFlags
, mem
);
1592 // Get rid of memory stream
1593 cmsCloseIOhandler(mem
);