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;
781 switch (cmsStageInputChannels(mpe)) {
784 _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
787 PreMin = PostMin = "";
790 _cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
801 _cmsIOPrintf(m, "<<\n");
803 if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
805 _cmsIOPrintf(m, "/DecodeDEF [ ");
806 EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
807 _cmsIOPrintf(m, "]\n");
814 if (cmsStageType(mpe) == cmsSigCLutElemType) {
816 _cmsIOPrintf(m, "/Table ");
817 WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0);
818 _cmsIOPrintf(m, "]\n");
822 EmitWhiteBlackD50(m, BlackPoint);
823 EmitIntent(m, Intent);
825 _cmsIOPrintf(m, " >>\n");
826 _cmsIOPrintf(m, "]\n");
832 // Generates a curve from a gray profile
835 cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
837 cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
838 cmsHPROFILE hXYZ = cmsCreateXYZProfile();
839 cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
843 for (i=0; i < 256; i++) {
845 cmsUInt8Number Gray = (cmsUInt8Number) i;
848 cmsDoTransform(xform, &Gray, &XYZ, 1);
850 Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);
854 cmsDeleteTransform(xform);
855 cmsCloseProfile(hXYZ);
861 // Because PostScript has only 8 bits in /Table, we should use
862 // a more perceptually uniform space... I do choose Lab.
865 int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
869 cmsUInt32Number nChannels;
870 cmsUInt32Number InputFormat;
872 cmsHPROFILE Profiles[2];
873 cmsCIEXYZ BlackPointAdaptedToD50;
875 // Does create a device-link based transform.
876 // The DeviceLink is next dumped as working CSA.
878 InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
879 nChannels = T_CHANNELS(InputFormat);
882 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
884 // Adjust output to Lab4
885 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
887 Profiles[0] = hProfile;
890 xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);
891 cmsCloseProfile(hLab);
895 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
899 // Only 1, 3 and 4 channels are allowed
904 cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
905 EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
906 cmsFreeToneCurve(Gray2Y);
912 cmsUInt32Number OutFrm = TYPE_Lab_16;
913 cmsPipeline* DeviceLink;
914 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
916 DeviceLink = cmsPipelineDup(v ->Lut);
917 if (DeviceLink == NULL) return 0;
919 dwFlags |= cmsFLAGS_FORCE_CLUT;
920 _cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
922 rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
923 cmsPipelineFree(DeviceLink);
929 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
934 cmsDeleteTransform(xform);
940 cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)
942 _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
944 return Data -> Double;
948 // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
951 int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
953 cmsColorSpaceSignature ColorSpace;
955 cmsCIEXYZ BlackPointAdaptedToD50;
957 ColorSpace = cmsGetColorSpace(hProfile);
959 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
961 if (ColorSpace == cmsSigGrayData) {
963 cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
964 rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
968 if (ColorSpace == cmsSigRgbData) {
973 memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
975 for (i=0; i < 3; i++)
976 for (j=0; j < 3; j++)
977 Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
979 rc = EmitCIEBasedABC(m, (cmsFloat64Number *) &Mat,
980 _cmsStageGetPtrToCurveSet(Shaper),
981 &BlackPointAdaptedToD50);
985 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
994 // Creates a PostScript color list from a named profile data.
995 // This is a HP extension, and it works in Lab instead of XYZ
998 int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
1000 cmsHTRANSFORM xform;
1004 cmsNAMEDCOLORLIST* NamedColorList;
1006 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1007 xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
1008 if (xform == NULL) return 0;
1010 NamedColorList = cmsGetNamedColorList(xform);
1011 if (NamedColorList == NULL) return 0;
1013 _cmsIOPrintf(m, "<<\n");
1014 _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1015 _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1016 _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1018 nColors = cmsNamedColorCount(NamedColorList);
1021 for (i=0; i < nColors; i++) {
1023 cmsUInt16Number In[1];
1026 In[0] = (cmsUInt16Number) i;
1028 if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1031 cmsDoTransform(xform, In, &Lab, 1);
1032 _cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1037 _cmsIOPrintf(m, ">>\n");
1039 cmsDeleteTransform(xform);
1040 cmsCloseProfile(hLab);
1045 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1047 cmsUInt32Number GenerateCSA(cmsContext ContextID,
1048 cmsHPROFILE hProfile,
1049 cmsUInt32Number Intent,
1050 cmsUInt32Number dwFlags,
1053 cmsUInt32Number dwBytesUsed;
1054 cmsPipeline* lut = NULL;
1055 cmsStage* Matrix, *Shaper;
1058 // Is a named color profile?
1059 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1061 if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
1066 // Any profile class are allowed (including devicelink), but
1067 // output (PCS) colorspace must be XYZ or Lab
1068 cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1070 if (ColorSpace != cmsSigXYZData &&
1071 ColorSpace != cmsSigLabData) {
1073 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
1078 // Read the lut with all necessary conversion stages
1079 lut = _cmsReadInputLUT(hProfile, Intent);
1080 if (lut == NULL) goto Error;
1083 // Tone curves + matrix can be implemented without any LUT
1084 if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
1086 if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
1090 // We need a LUT for the rest
1091 if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
1096 // Done, keep memory usage
1097 dwBytesUsed = mem ->UsedSpace;
1100 if (lut != NULL) cmsPipelineFree(lut);
1102 // Finally, return used byte count
1106 if (lut != NULL) cmsPipelineFree(lut);
1110 // ------------------------------------------------------ Color Rendering Dictionary (CRD)
1116 Black point compensation plus chromatic adaptation:
1118 Step 1 - Chromatic adaptation
1119 =============================
1125 Step 2 - Black point compensation
1126 =================================
1128 (WPout - BPout)*X - WPout*(BPin - BPout)
1129 out = ---------------------------------------
1133 Algorithm discussion
1134 ====================
1136 TransformPQR(WPin, BPin, WPout, BPout, PQR)
1138 Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1141 Algorithm Stack 0...n
1142 ===========================================================
1143 PQR BPout WPout BPin WPin
1144 4 index 3 get WPin PQR BPout WPout BPin WPin
1145 div (PQR/WPin) BPout WPout BPin WPin
1146 2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin
1147 mult WPout*(PQR/WPin) BPout WPout BPin WPin
1149 2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1150 2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1151 sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
1152 mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1154 2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1155 4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1156 3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1158 sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1159 mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1160 sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1162 3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1163 3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1165 sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1177 void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1183 // For absolute colorimetric intent, encode back to relative
1184 // and generate a relative Pipeline
1186 // Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1190 _cmsReadMediaWhitePoint(&White, hProfile);
1192 _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1193 _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1195 _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1197 "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1198 "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1199 "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
1200 White.X, White.Y, White.Z);
1205 _cmsIOPrintf(m,"%% Bradford Cone Space\n"
1206 "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1208 _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1215 _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"
1217 "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1218 "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1219 "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
1224 _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1225 "/TransformPQR [\n");
1227 _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "
1228 "2 index 3 get 2 index 3 get sub mul "
1229 "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1230 "3 index 3 get 3 index 3 get exch sub div "
1231 "exch pop exch pop exch pop exch pop } bind\n");
1233 _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "
1234 "2 index 4 get 2 index 4 get sub mul "
1235 "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1236 "3 index 4 get 3 index 4 get exch sub div "
1237 "exch pop exch pop exch pop exch pop } bind\n");
1239 _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "
1240 "2 index 5 get 2 index 5 get sub mul "
1241 "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1242 "3 index 5 get 3 index 5 get exch sub div "
1243 "exch pop exch pop exch pop exch pop } bind\n]\n");
1252 void EmitXYZ2Lab(cmsIOHANDLER* m)
1254 _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
1255 _cmsIOPrintf(m, "/EncodeLMN [\n");
1256 _cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1257 _cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1258 _cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1259 _cmsIOPrintf(m, "]\n");
1260 _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1261 _cmsIOPrintf(m, "/EncodeABC [\n");
1264 _cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n");
1265 _cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n");
1266 _cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n");
1269 _cmsIOPrintf(m, "]\n");
1274 // Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1275 // I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1276 // space on 3D CLUT, but since space seems not to be a problem here, 33 points
1277 // would give a reasonable accurancy. Note also that CRD tables must operate in
1281 int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags)
1284 cmsHTRANSFORM xform;
1286 cmsUInt32Number OutputFormat;
1288 cmsPipeline* DeviceLink;
1289 cmsHPROFILE Profiles[3];
1290 cmsCIEXYZ BlackPointAdaptedToD50;
1291 cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1292 cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1293 cmsUInt32Number InFrm = TYPE_Lab_16;
1294 int RelativeEncodingIntent;
1295 cmsColorSpaceSignature ColorSpace;
1298 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1299 if (hLab == NULL) return 0;
1301 OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
1302 nChannels = T_CHANNELS(OutputFormat);
1304 ColorSpace = cmsGetColorSpace(hProfile);
1306 // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
1308 RelativeEncodingIntent = Intent;
1309 if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1310 RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1313 // Use V4 Lab always
1315 Profiles[1] = hProfile;
1317 xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
1318 Profiles, 2, TYPE_Lab_DBL,
1319 OutputFormat, RelativeEncodingIntent, 0);
1320 cmsCloseProfile(hLab);
1322 if (xform == NULL) {
1324 cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
1328 // Get a copy of the internal devicelink
1329 v = (_cmsTRANSFORM*) xform;
1330 DeviceLink = cmsPipelineDup(v ->Lut);
1331 if (DeviceLink == NULL) return 0;
1335 dwFlags |= cmsFLAGS_FORCE_CLUT;
1336 _cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
1338 _cmsIOPrintf(m, "<<\n");
1339 _cmsIOPrintf(m, "/ColorRenderingType 1\n");
1342 cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
1344 // Emit headers, etc.
1345 EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1346 EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1350 // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
1351 // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
1352 // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1353 // zero. This would sacrifice a bit of highlights, but failure to do so would cause
1356 if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1359 _cmsIOPrintf(m, "/RenderTable ");
1362 WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);
1364 _cmsIOPrintf(m, " %d {} bind ", nChannels);
1366 for (i=1; i < nChannels; i++)
1367 _cmsIOPrintf(m, "dup ");
1369 _cmsIOPrintf(m, "]\n");
1372 EmitIntent(m, Intent);
1374 _cmsIOPrintf(m, ">>\n");
1376 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1378 _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");
1381 cmsPipelineFree(DeviceLink);
1382 cmsDeleteTransform(xform);
1388 // Builds a ASCII string containing colorant list in 0..1.0 range
1390 void BuildColorantList(char *Colorant, int nColorant, cmsUInt16Number Out[])
1396 if (nColorant > cmsMAXCHANNELS)
1397 nColorant = cmsMAXCHANNELS;
1399 for (j=0; j < nColorant; j++) {
1401 sprintf(Buff, "%.3f", Out[j] / 65535.0);
1402 strcat(Colorant, Buff);
1403 if (j < nColorant -1)
1404 strcat(Colorant, " ");
1410 // Creates a PostScript color list from a named profile data.
1411 // This is a HP extension.
1414 int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cmsUInt32Number dwFlags)
1416 cmsHTRANSFORM xform;
1417 int i, nColors, nColorant;
1418 cmsUInt32Number OutputFormat;
1421 cmsNAMEDCOLORLIST* NamedColorList;
1424 OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
1425 nColorant = T_CHANNELS(OutputFormat);
1428 xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
1429 if (xform == NULL) return 0;
1432 NamedColorList = cmsGetNamedColorList(xform);
1433 if (NamedColorList == NULL) return 0;
1435 _cmsIOPrintf(m, "<<\n");
1436 _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
1437 _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1438 _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1440 nColors = cmsNamedColorCount(NamedColorList);
1442 for (i=0; i < nColors; i++) {
1444 cmsUInt16Number In[1];
1445 cmsUInt16Number Out[cmsMAXCHANNELS];
1447 In[0] = (cmsUInt16Number) i;
1449 if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1452 cmsDoTransform(xform, In, Out, 1);
1453 BuildColorantList(Colorant, nColorant, Out);
1454 _cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant);
1457 _cmsIOPrintf(m, " >>");
1459 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1461 _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");
1464 cmsDeleteTransform(xform);
1470 // This one does create a Color Rendering Dictionary.
1471 // CRD are always LUT-Based, no matter if profile is
1472 // implemented as matrix-shaper.
1475 cmsUInt32Number GenerateCRD(cmsContext ContextID,
1476 cmsHPROFILE hProfile,
1477 cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1480 cmsUInt32Number dwBytesUsed;
1482 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1484 EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1488 // Is a named color profile?
1489 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1491 if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1497 // CRD are always implemented as LUT
1499 if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1504 if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1506 _cmsIOPrintf(mem, "%%%%EndResource\n");
1507 _cmsIOPrintf(mem, "\n%% CRD End\n");
1510 // Done, keep memory usage
1511 dwBytesUsed = mem ->UsedSpace;
1513 // Finally, return used byte count
1516 cmsUNUSED_PARAMETER(ContextID);
1522 cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
1523 cmsPSResourceType Type,
1524 cmsHPROFILE hProfile,
1525 cmsUInt32Number Intent,
1526 cmsUInt32Number dwFlags,
1534 case cmsPS_RESOURCE_CSA:
1535 rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
1539 case cmsPS_RESOURCE_CRD:
1540 rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
1549 cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
1550 cmsHPROFILE hProfile,
1551 cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1552 void* Buffer, cmsUInt32Number dwBufferLen)
1555 cmsUInt32Number dwBytesUsed;
1557 // Set up the serialization engine
1559 mem = cmsOpenIOhandlerFromNULL(ContextID);
1561 mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1565 dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
1567 // Get rid of memory stream
1568 cmsCloseIOhandler(mem);
1575 // Does create a Color Space Array on XYZ colorspace for PostScript usage
1576 cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
1577 cmsHPROFILE hProfile,
1578 cmsUInt32Number Intent,
1579 cmsUInt32Number dwFlags,
1581 cmsUInt32Number dwBufferLen)
1584 cmsUInt32Number dwBytesUsed;
1587 mem = cmsOpenIOhandlerFromNULL(ContextID);
1589 mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1593 dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
1595 // Get rid of memory stream
1596 cmsCloseIOhandler(mem);