1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2008 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;
473 if (Table ->nEntries <= 0) return; // Empty table
475 // Suppress whole if identity
476 if (cmsIsToneCurveLinear(Table)) return;
478 // Check if is really an exponential. If so, emit "exp"
479 gamma = cmsEstimateGamma(Table, 0.001);
481 _cmsIOPrintf(m, "{ %g exp } bind ", gamma);
485 _cmsIOPrintf(m, "{ ");
490 // Emit intepolation code
492 // PostScript code Stack
493 // =============== ========================
495 _cmsIOPrintf(m, " [");
497 for (i=0; i < Table->nEntries; i++) {
498 _cmsIOPrintf(m, "%d ", Table->Table16[i]);
501 _cmsIOPrintf(m, "] "); // v tab
503 _cmsIOPrintf(m, "dup "); // v tab tab
504 _cmsIOPrintf(m, "length 1 sub "); // v tab dom
505 _cmsIOPrintf(m, "3 -1 roll "); // tab dom v
506 _cmsIOPrintf(m, "mul "); // tab val2
507 _cmsIOPrintf(m, "dup "); // tab val2 val2
508 _cmsIOPrintf(m, "dup "); // tab val2 val2 val2
509 _cmsIOPrintf(m, "floor cvi "); // tab val2 val2 cell0
510 _cmsIOPrintf(m, "exch "); // tab val2 cell0 val2
511 _cmsIOPrintf(m, "ceiling cvi "); // tab val2 cell0 cell1
512 _cmsIOPrintf(m, "3 index "); // tab val2 cell0 cell1 tab
513 _cmsIOPrintf(m, "exch "); // tab val2 cell0 tab cell1
514 _cmsIOPrintf(m, "get "); // tab val2 cell0 y1
515 _cmsIOPrintf(m, "4 -1 roll "); // val2 cell0 y1 tab
516 _cmsIOPrintf(m, "3 -1 roll "); // val2 y1 tab cell0
517 _cmsIOPrintf(m, "get "); // val2 y1 y0
518 _cmsIOPrintf(m, "dup "); // val2 y1 y0 y0
519 _cmsIOPrintf(m, "3 1 roll "); // val2 y0 y1 y0
520 _cmsIOPrintf(m, "sub "); // val2 y0 (y1-y0)
521 _cmsIOPrintf(m, "3 -1 roll "); // y0 (y1-y0) val2
522 _cmsIOPrintf(m, "dup "); // y0 (y1-y0) val2 val2
523 _cmsIOPrintf(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2)
524 _cmsIOPrintf(m, "sub "); // y0 (y1-y0) rest
525 _cmsIOPrintf(m, "mul "); // y0 t1
526 _cmsIOPrintf(m, "add "); // y
527 _cmsIOPrintf(m, "65535 div "); // result
529 _cmsIOPrintf(m, " } bind ");
533 // Compare gamma table
536 cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, int nEntries)
538 return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0;
542 // Does write a set of gamma curves
545 void EmitNGamma(cmsIOHANDLER* m, int n, cmsToneCurve* g[])
549 for( i=0; i < n; i++ )
551 if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) {
553 _cmsIOPrintf(m, "dup ");
566 // Following code dumps a LUT onto memory stream
569 // This is the sampler. Intended to work in SAMPLER_INSPECT mode,
570 // that is, the callback will be called for each knot with
572 // In[] The grid location coordinates, normalized to 0..ffff
573 // Out[] The Pipeline values, normalized to 0..ffff
575 // Returning a value other than 0 does terminate the sampling process
577 // Each row contains Pipeline values for all but first component. So, I
578 // detect row changing by keeping a copy of last value of first
579 // component. -1 is used to mark begining of whole block.
582 int OutputValueSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
584 cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;
588 if (sc -> FixWhite) {
590 if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8]
592 if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&
593 (In[2] >= 0x7800 && In[2] <= 0x8800)) {
595 cmsUInt16Number* Black;
596 cmsUInt16Number* White;
597 cmsUInt32Number nOutputs;
599 if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))
602 for (i=0; i < nOutputs; i++)
611 // Hadle the parenthesis on rows
613 if (In[0] != sc ->FirstComponent) {
615 if (sc ->FirstComponent != -1) {
617 _cmsIOPrintf(sc ->m, sc ->PostMin);
618 sc ->SecondComponent = -1;
619 _cmsIOPrintf(sc ->m, sc ->PostMaj);
623 _cmsPSActualColumn = 0;
625 _cmsIOPrintf(sc ->m, sc ->PreMaj);
626 sc ->FirstComponent = In[0];
630 if (In[1] != sc ->SecondComponent) {
632 if (sc ->SecondComponent != -1) {
634 _cmsIOPrintf(sc ->m, sc ->PostMin);
637 _cmsIOPrintf(sc ->m, sc ->PreMin);
638 sc ->SecondComponent = In[1];
643 for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {
645 cmsUInt16Number wWordOut = Out[i];
646 cmsUInt8Number wByteOut; // Value as byte
649 // We always deal with Lab4
651 wByteOut = Word2Byte(wWordOut);
652 WriteByte(sc -> m, wByteOut);
658 // Writes a Pipeline on memstream. Could be 8 or 16 bits based
661 void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
666 cmsColorSpaceSignature ColorSpace)
669 cmsPsSamplerCargo sc;
671 sc.FirstComponent = -1;
672 sc.SecondComponent = -1;
673 sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
679 sc.PostMin = PostMin;
680 sc.FixWhite = FixWhite;
681 sc.ColorSpace = ColorSpace;
683 _cmsIOPrintf(m, "[");
685 for (i=0; i < sc.Pipeline->Params->nInputs; i++)
686 _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
688 _cmsIOPrintf(m, " [\n");
690 cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT);
692 _cmsIOPrintf(m, PostMin);
693 _cmsIOPrintf(m, PostMaj);
694 _cmsIOPrintf(m, "] ");
699 // Dumps CIEBasedA Color Space Array
702 int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
705 _cmsIOPrintf(m, "[ /CIEBasedA\n");
706 _cmsIOPrintf(m, " <<\n");
708 _cmsIOPrintf(m, "/DecodeA ");
710 Emit1Gamma(m, Curve);
712 _cmsIOPrintf(m, " \n");
714 _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
715 _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
717 EmitWhiteBlackD50(m, BlackPoint);
718 EmitIntent(m, INTENT_PERCEPTUAL);
720 _cmsIOPrintf(m, ">>\n");
721 _cmsIOPrintf(m, "]\n");
727 // Dumps CIEBasedABC Color Space Array
730 int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)
734 _cmsIOPrintf(m, "[ /CIEBasedABC\n");
735 _cmsIOPrintf(m, "<<\n");
736 _cmsIOPrintf(m, "/DecodeABC [ ");
738 EmitNGamma(m, 3, CurveSet);
740 _cmsIOPrintf(m, "]\n");
742 _cmsIOPrintf(m, "/MatrixABC [ " );
744 for( i=0; i < 3; i++ ) {
746 _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],
752 _cmsIOPrintf(m, "]\n");
754 _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
756 EmitWhiteBlackD50(m, BlackPoint);
757 EmitIntent(m, INTENT_PERCEPTUAL);
759 _cmsIOPrintf(m, ">>\n");
760 _cmsIOPrintf(m, "]\n");
768 int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXYZ* BlackPoint)
772 const char* PreMin, *PostMin;
775 mpe = Pipeline ->Elements;
778 switch (cmsStageInputChannels(mpe)) {
781 _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
784 PreMin = PostMin = "";
787 _cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
798 _cmsIOPrintf(m, "<<\n");
800 if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
802 _cmsIOPrintf(m, "/DecodeDEF [ ");
803 EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
804 _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");
829 // Generates a curve from a gray profile
832 cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
834 cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
835 cmsHPROFILE hXYZ = cmsCreateXYZProfile();
836 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);
849 cmsDeleteTransform(xform);
850 cmsCloseProfile(hXYZ);
856 // Because PostScript has only 8 bits in /Table, we should use
857 // a more perceptually uniform space... I do choose Lab.
860 int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
864 cmsUInt32Number nChannels;
865 cmsUInt32Number InputFormat;
867 cmsHPROFILE Profiles[2];
868 cmsCIEXYZ BlackPointAdaptedToD50;
870 // Does create a device-link based transform.
871 // The DeviceLink is next dumped as working CSA.
873 InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
874 nChannels = T_CHANNELS(InputFormat);
877 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
879 // Adjust output to Lab4
880 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
882 Profiles[0] = hProfile;
885 xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);
886 cmsCloseProfile(hLab);
890 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
894 // Only 1, 3 and 4 channels are allowed
899 cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
900 EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
901 cmsFreeToneCurve(Gray2Y);
907 cmsUInt32Number OutFrm = TYPE_Lab_16;
908 cmsPipeline* DeviceLink;
909 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
911 DeviceLink = cmsPipelineDup(v ->Lut);
912 if (DeviceLink == NULL) return 0;
914 dwFlags |= cmsFLAGS_FORCE_CLUT;
915 _cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
917 rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
918 cmsPipelineFree(DeviceLink);
924 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
929 cmsDeleteTransform(xform);
935 cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)
937 _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
939 return Data -> Double;
943 // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
946 int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
948 cmsColorSpaceSignature ColorSpace;
950 cmsCIEXYZ BlackPointAdaptedToD50;
952 ColorSpace = cmsGetColorSpace(hProfile);
954 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
956 if (ColorSpace == cmsSigGrayData) {
958 cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
959 rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
963 if (ColorSpace == cmsSigRgbData) {
968 memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
970 for (i=0; i < 3; i++)
971 for (j=0; j < 3; j++)
972 Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
974 rc = EmitCIEBasedABC(m, (cmsFloat64Number *) &Mat,
975 _cmsStageGetPtrToCurveSet(Shaper),
976 &BlackPointAdaptedToD50);
980 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
989 // Creates a PostScript color list from a named profile data.
990 // This is a HP extension, and it works in Lab instead of XYZ
993 int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
999 cmsNAMEDCOLORLIST* NamedColorList;
1001 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1002 xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
1003 if (xform == NULL) return 0;
1005 NamedColorList = cmsGetNamedColorList(xform);
1006 if (NamedColorList == NULL) return 0;
1008 _cmsIOPrintf(m, "<<\n");
1009 _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1010 _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1011 _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1013 nColors = cmsNamedColorCount(NamedColorList);
1016 for (i=0; i < nColors; i++) {
1018 cmsUInt16Number In[1];
1021 In[0] = (cmsUInt16Number) i;
1023 if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1026 cmsDoTransform(xform, In, &Lab, 1);
1027 _cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1032 _cmsIOPrintf(m, ">>\n");
1034 cmsDeleteTransform(xform);
1035 cmsCloseProfile(hLab);
1040 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1042 cmsUInt32Number GenerateCSA(cmsContext ContextID,
1043 cmsHPROFILE hProfile,
1044 cmsUInt32Number Intent,
1045 cmsUInt32Number dwFlags,
1048 cmsUInt32Number dwBytesUsed;
1049 cmsPipeline* lut = NULL;
1050 cmsStage* Matrix, *Shaper;
1053 // Is a named color profile?
1054 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1056 if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
1061 // Any profile class are allowed (including devicelink), but
1062 // output (PCS) colorspace must be XYZ or Lab
1063 cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1065 if (ColorSpace != cmsSigXYZData &&
1066 ColorSpace != cmsSigLabData) {
1068 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
1073 // Read the lut with all necessary conversion stages
1074 lut = _cmsReadInputLUT(hProfile, Intent);
1075 if (lut == NULL) goto Error;
1078 // Tone curves + matrix can be implemented without any LUT
1079 if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
1081 if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
1085 // We need a LUT for the rest
1086 if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
1091 // Done, keep memory usage
1092 dwBytesUsed = mem ->UsedSpace;
1095 if (lut != NULL) cmsPipelineFree(lut);
1097 // Finally, return used byte count
1101 if (lut != NULL) cmsPipelineFree(lut);
1105 // ------------------------------------------------------ Color Rendering Dictionary (CRD)
1111 Black point compensation plus chromatic adaptation:
1113 Step 1 - Chromatic adaptation
1114 =============================
1120 Step 2 - Black point compensation
1121 =================================
1123 (WPout - BPout)*X - WPout*(BPin - BPout)
1124 out = ---------------------------------------
1128 Algorithm discussion
1129 ====================
1131 TransformPQR(WPin, BPin, WPout, BPout, PQR)
1133 Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1136 Algorithm Stack 0...n
1137 ===========================================================
1138 PQR BPout WPout BPin WPin
1139 4 index 3 get WPin PQR BPout WPout BPin WPin
1140 div (PQR/WPin) BPout WPout BPin WPin
1141 2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin
1142 mult WPout*(PQR/WPin) BPout WPout BPin WPin
1144 2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1145 2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1146 sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
1147 mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1149 2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1150 4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1151 3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1153 sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1154 mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1155 sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1157 3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1158 3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1160 sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1172 void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1178 // For absolute colorimetric intent, encode back to relative
1179 // and generate a relative Pipeline
1181 // Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1185 _cmsReadMediaWhitePoint(&White, hProfile);
1187 _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1188 _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1190 _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1192 "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1193 "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1194 "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
1195 White.X, White.Y, White.Z);
1200 _cmsIOPrintf(m,"%% Bradford Cone Space\n"
1201 "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1203 _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1210 _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"
1212 "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1213 "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1214 "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
1219 _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1220 "/TransformPQR [\n");
1222 _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "
1223 "2 index 3 get 2 index 3 get sub mul "
1224 "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1225 "3 index 3 get 3 index 3 get exch sub div "
1226 "exch pop exch pop exch pop exch pop } bind\n");
1228 _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "
1229 "2 index 4 get 2 index 4 get sub mul "
1230 "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1231 "3 index 4 get 3 index 4 get exch sub div "
1232 "exch pop exch pop exch pop exch pop } bind\n");
1234 _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "
1235 "2 index 5 get 2 index 5 get sub mul "
1236 "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1237 "3 index 5 get 3 index 5 get exch sub div "
1238 "exch pop exch pop exch pop exch pop } bind\n]\n");
1247 void EmitXYZ2Lab(cmsIOHANDLER* m)
1249 _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
1250 _cmsIOPrintf(m, "/EncodeLMN [\n");
1251 _cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1252 _cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1253 _cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1254 _cmsIOPrintf(m, "]\n");
1255 _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1256 _cmsIOPrintf(m, "/EncodeABC [\n");
1259 _cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n");
1260 _cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n");
1261 _cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n");
1264 _cmsIOPrintf(m, "]\n");
1269 // Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1270 // I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1271 // space on 3D CLUT, but since space seems not to be a problem here, 33 points
1272 // would give a reasonable accurancy. Note also that CRD tables must operate in
1276 int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
1279 cmsHTRANSFORM xform;
1281 cmsUInt32Number OutputFormat;
1283 cmsPipeline* DeviceLink;
1284 cmsHPROFILE Profiles[3];
1285 cmsCIEXYZ BlackPointAdaptedToD50;
1286 cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1287 cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1288 cmsUInt32Number InFrm = TYPE_Lab_16;
1289 int RelativeEncodingIntent;
1290 cmsColorSpaceSignature ColorSpace;
1293 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1294 if (hLab == NULL) return 0;
1296 OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
1297 nChannels = T_CHANNELS(OutputFormat);
1299 ColorSpace = cmsGetColorSpace(hProfile);
1301 // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
1303 RelativeEncodingIntent = Intent;
1304 if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1305 RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1308 // Use V4 Lab always
1310 Profiles[1] = hProfile;
1312 xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
1313 Profiles, 2, TYPE_Lab_DBL,
1314 OutputFormat, RelativeEncodingIntent, 0);
1315 cmsCloseProfile(hLab);
1317 if (xform == NULL) {
1319 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
1323 // Get a copy of the internal devicelink
1324 v = (_cmsTRANSFORM*) xform;
1325 DeviceLink = cmsPipelineDup(v ->Lut);
1326 if (DeviceLink == NULL) return 0;
1330 dwFlags |= cmsFLAGS_FORCE_CLUT;
1331 _cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
1333 _cmsIOPrintf(m, "<<\n");
1334 _cmsIOPrintf(m, "/ColorRenderingType 1\n");
1337 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
1339 // Emit headers, etc.
1340 EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1341 EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1345 // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
1346 // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
1347 // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1348 // zero. This would sacrifice a bit of highlights, but failure to do so would cause
1351 if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1354 _cmsIOPrintf(m, "/RenderTable ");
1357 WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);
1359 _cmsIOPrintf(m, " %d {} bind ", nChannels);
1361 for (i=1; i < nChannels; i++)
1362 _cmsIOPrintf(m, "dup ");
1364 _cmsIOPrintf(m, "]\n");
1367 EmitIntent(m, Intent);
1369 _cmsIOPrintf(m, ">>\n");
1371 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1373 _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");
1376 cmsPipelineFree(DeviceLink);
1377 cmsDeleteTransform(xform);
1383 // Builds a ASCII string containing colorant list in 0..1.0 range
1385 void BuildColorantList(char *Colorant, int nColorant, cmsUInt16Number Out[])
1391 if (nColorant > cmsMAXCHANNELS)
1392 nColorant = cmsMAXCHANNELS;
1394 for (j=0; j < nColorant; j++) {
1396 sprintf(Buff, "%.3f", Out[j] / 65535.0);
1397 strcat(Colorant, Buff);
1398 if (j < nColorant -1)
1399 strcat(Colorant, " ");
1405 // Creates a PostScript color list from a named profile data.
1406 // This is a HP extension.
1409 int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cmsUInt32Number dwFlags)
1411 cmsHTRANSFORM xform;
1412 int i, nColors, nColorant;
1413 cmsUInt32Number OutputFormat;
1416 cmsNAMEDCOLORLIST* NamedColorList;
1419 OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
1420 nColorant = T_CHANNELS(OutputFormat);
1423 xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
1424 if (xform == NULL) return 0;
1427 NamedColorList = cmsGetNamedColorList(xform);
1428 if (NamedColorList == NULL) return 0;
1430 _cmsIOPrintf(m, "<<\n");
1431 _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
1432 _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1433 _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1435 nColors = cmsNamedColorCount(NamedColorList);
1437 for (i=0; i < nColors; i++) {
1439 cmsUInt16Number In[1];
1440 cmsUInt16Number Out[cmsMAXCHANNELS];
1442 In[0] = (cmsUInt16Number) i;
1444 if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1447 cmsDoTransform(xform, In, Out, 1);
1448 BuildColorantList(Colorant, nColorant, Out);
1449 _cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant);
1452 _cmsIOPrintf(m, " >>");
1454 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1456 _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");
1459 cmsDeleteTransform(xform);
1465 // This one does create a Color Rendering Dictionary.
1466 // CRD are always LUT-Based, no matter if profile is
1467 // implemented as matrix-shaper.
1470 cmsUInt32Number GenerateCRD(cmsContext ContextID,
1471 cmsHPROFILE hProfile,
1472 cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1475 cmsUInt32Number dwBytesUsed;
1477 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1479 EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1483 // Is a named color profile?
1484 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1486 if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1492 // CRD are always implemented as LUT
1494 if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1499 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1501 _cmsIOPrintf(mem, "%%%%EndResource\n");
1502 _cmsIOPrintf(mem, "\n%% CRD End\n");
1505 // Done, keep memory usage
1506 dwBytesUsed = mem ->UsedSpace;
1508 // Finally, return used byte count
1511 cmsUNUSED_PARAMETER(ContextID);
1517 cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
1518 cmsPSResourceType Type,
1519 cmsHPROFILE hProfile,
1520 cmsUInt32Number Intent,
1521 cmsUInt32Number dwFlags,
1529 case cmsPS_RESOURCE_CSA:
1530 rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
1534 case cmsPS_RESOURCE_CRD:
1535 rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
1544 cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
1545 cmsHPROFILE hProfile,
1546 cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1547 void* Buffer, cmsUInt32Number dwBufferLen)
1550 cmsUInt32Number dwBytesUsed;
1552 // Set up the serialization engine
1554 mem = cmsOpenIOhandlerFromNULL(ContextID);
1556 mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1560 dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
1562 // Get rid of memory stream
1563 cmsCloseIOhandler(mem);
1570 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1571 cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
1572 cmsHPROFILE hProfile,
1573 cmsUInt32Number Intent,
1574 cmsUInt32Number dwFlags,
1576 cmsUInt32Number dwBufferLen)
1579 cmsUInt32Number dwBytesUsed;
1582 mem = cmsOpenIOhandlerFromNULL(ContextID);
1584 mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1588 dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
1590 // Get rid of memory stream
1591 cmsCloseIOhandler(mem);