|  | //--------------------------------------------------------------------------------- | 
|  | // | 
|  | //  Little Color Management System | 
|  | //  Copyright (c) 1998-2023 Marti Maria Saguer | 
|  | // | 
|  | // Permission is hereby granted, free of charge, to any person obtaining | 
|  | // a copy of this software and associated documentation files (the "Software"), | 
|  | // to deal in the Software without restriction, including without limitation | 
|  | // the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
|  | // and/or sell copies of the Software, and to permit persons to whom the Software | 
|  | // is furnished to do so, subject to the following conditions: | 
|  | // | 
|  | // The above copyright notice and this permission notice shall be included in | 
|  | // all copies or substantial portions of the Software. | 
|  | // | 
|  | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
|  | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO | 
|  | // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
|  | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | 
|  | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | 
|  | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | 
|  | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
|  | // | 
|  | //--------------------------------------------------------------------------------- | 
|  | // | 
|  |  | 
|  | #include "lcms2_internal.h" | 
|  |  | 
|  | // PostScript ColorRenderingDictionary and ColorSpaceArray | 
|  |  | 
|  |  | 
|  | #define MAXPSCOLS   60      // Columns on tables | 
|  |  | 
|  | /* | 
|  | Implementation | 
|  | -------------- | 
|  |  | 
|  | PostScript does use XYZ as its internal PCS. But since PostScript | 
|  | interpolation tables are limited to 8 bits, I use Lab as a way to | 
|  | improve the accuracy, favoring perceptual results. So, for the creation | 
|  | of each CRD, CSA the profiles are converted to Lab via a device | 
|  | link between  profile -> Lab or Lab -> profile. The PS code necessary to | 
|  | convert Lab <-> XYZ is also included. | 
|  |  | 
|  |  | 
|  |  | 
|  | Color Space Arrays (CSA) | 
|  | ================================================================================== | 
|  |  | 
|  | In order to obtain precision, code chooses between three ways to implement | 
|  | the device -> XYZ transform. These cases identifies monochrome profiles (often | 
|  | implemented as a set of curves), matrix-shaper and Pipeline-based. | 
|  |  | 
|  | Monochrome | 
|  | ----------- | 
|  |  | 
|  | This is implemented as /CIEBasedA CSA. The prelinearization curve is | 
|  | placed into /DecodeA section, and matrix equals to D50. Since here is | 
|  | no interpolation tables, I do the conversion directly to XYZ | 
|  |  | 
|  | NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT | 
|  | flag is forced on such profiles. | 
|  |  | 
|  | [ /CIEBasedA | 
|  | << | 
|  | /DecodeA { transfer function } bind | 
|  | /MatrixA [D50] | 
|  | /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] | 
|  | /WhitePoint [D50] | 
|  | /BlackPoint [BP] | 
|  | /RenderingIntent (intent) | 
|  | >> | 
|  | ] | 
|  |  | 
|  | On simpler profiles, the PCS is already XYZ, so no conversion is required. | 
|  |  | 
|  |  | 
|  | Matrix-shaper based | 
|  | ------------------- | 
|  |  | 
|  | This is implemented both with /CIEBasedABC or /CIEBasedDEF depending on the | 
|  | profile implementation. Since here there are no interpolation tables, I do | 
|  | the conversion directly to XYZ | 
|  |  | 
|  |  | 
|  |  | 
|  | [ /CIEBasedABC | 
|  | << | 
|  | /DecodeABC [ {transfer1} {transfer2} {transfer3} ] | 
|  | /MatrixABC [Matrix] | 
|  | /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] | 
|  | /DecodeLMN [ { / 2} dup dup ] | 
|  | /WhitePoint [D50] | 
|  | /BlackPoint [BP] | 
|  | /RenderingIntent (intent) | 
|  | >> | 
|  | ] | 
|  |  | 
|  |  | 
|  | CLUT based | 
|  | ---------- | 
|  |  | 
|  | Lab is used in such cases. | 
|  |  | 
|  | [ /CIEBasedDEF | 
|  | << | 
|  | /DecodeDEF [ <prelinearization> ] | 
|  | /Table [ p p p [<...>]] | 
|  | /RangeABC [ 0 1 0 1 0 1] | 
|  | /DecodeABC[ <postlinearization> ] | 
|  | /RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ] | 
|  | % -128/500 1+127/500 0 1  -127/200 1+128/200 | 
|  | /MatrixABC [ 1 1 1 1 0 0 0 0 -1] | 
|  | /WhitePoint [D50] | 
|  | /BlackPoint [BP] | 
|  | /RenderingIntent (intent) | 
|  | ] | 
|  |  | 
|  |  | 
|  | Color Rendering Dictionaries (CRD) | 
|  | ================================== | 
|  | These are always implemented as CLUT, and always are using Lab. Since CRD are expected to | 
|  | be used as resources, the code adds the definition as well. | 
|  |  | 
|  | << | 
|  | /ColorRenderingType 1 | 
|  | /WhitePoint [ D50 ] | 
|  | /BlackPoint [BP] | 
|  | /MatrixPQR [ Bradford ] | 
|  | /RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ] | 
|  | /TransformPQR [ | 
|  | {4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind | 
|  | {4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind | 
|  | {4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind | 
|  | ] | 
|  | /MatrixABC <...> | 
|  | /EncodeABC <...> | 
|  | /RangeABC  <.. used for  XYZ -> Lab> | 
|  | /EncodeLMN | 
|  | /RenderTable [ p p p [<...>]] | 
|  |  | 
|  | /RenderingIntent (Perceptual) | 
|  | >> | 
|  | /Current exch /ColorRendering defineresource pop | 
|  |  | 
|  |  | 
|  | The following stages are used to convert from XYZ to Lab | 
|  | -------------------------------------------------------- | 
|  |  | 
|  | Input is given at LMN stage on X, Y, Z | 
|  |  | 
|  | Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn) | 
|  |  | 
|  | /EncodeLMN [ | 
|  |  | 
|  | { 0.964200  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind | 
|  | { 1.000000  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind | 
|  | { 0.824900  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind | 
|  |  | 
|  | ] | 
|  |  | 
|  |  | 
|  | MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn) | 
|  |  | 
|  | | 0  1  0| | 
|  | | 1 -1  0| | 
|  | | 0  1 -1| | 
|  |  | 
|  | /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ] | 
|  |  | 
|  | EncodeABC finally gives Lab values. | 
|  |  | 
|  | /EncodeABC [ | 
|  | { 116 mul  16 sub 100 div  } bind | 
|  | { 500 mul 128 add 255 div  } bind | 
|  | { 200 mul 128 add 255 div  } bind | 
|  | ] | 
|  |  | 
|  | The following stages are used to convert Lab to XYZ | 
|  | ---------------------------------------------------- | 
|  |  | 
|  | /RangeABC [ 0 1 0 1 0 1] | 
|  | /DecodeABC [ { 100 mul 16 add 116 div } bind | 
|  | { 255 mul 128 sub 500 div } bind | 
|  | { 255 mul 128 sub 200 div } bind | 
|  | ] | 
|  |  | 
|  | /MatrixABC [ 1 1 1 1 0 0 0 0 -1] | 
|  | /DecodeLMN [ | 
|  | {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind | 
|  | {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind | 
|  | {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind | 
|  | ] | 
|  |  | 
|  |  | 
|  | */ | 
|  |  | 
|  | /* | 
|  |  | 
|  | PostScript algorithms discussion. | 
|  | ========================================================================================================= | 
|  |  | 
|  | 1D interpolation algorithm | 
|  |  | 
|  |  | 
|  | 1D interpolation (float) | 
|  | ------------------------ | 
|  |  | 
|  | val2 = Domain * Value; | 
|  |  | 
|  | cell0 = (int) floor(val2); | 
|  | cell1 = (int) ceil(val2); | 
|  |  | 
|  | rest = val2 - cell0; | 
|  |  | 
|  | y0 = LutTable[cell0] ; | 
|  | y1 = LutTable[cell1] ; | 
|  |  | 
|  | y = y0 + (y1 - y0) * rest; | 
|  |  | 
|  |  | 
|  |  | 
|  | PostScript code                   Stack | 
|  | ================================================ | 
|  |  | 
|  | {                                 % v | 
|  | <check 0..1.0> | 
|  | [array]                         % v tab | 
|  | dup                             % v tab tab | 
|  | length 1 sub                    % v tab dom | 
|  |  | 
|  | 3 -1 roll                       % tab dom v | 
|  |  | 
|  | mul                             % tab val2 | 
|  | dup                             % tab val2 val2 | 
|  | dup                             % tab val2 val2 val2 | 
|  | floor cvi                       % tab val2 val2 cell0 | 
|  | exch                            % tab val2 cell0 val2 | 
|  | ceiling cvi                     % tab val2 cell0 cell1 | 
|  |  | 
|  | 3 index                         % tab val2 cell0 cell1 tab | 
|  | exch                            % tab val2 cell0 tab cell1 | 
|  | get                             % tab val2 cell0 y1 | 
|  |  | 
|  | 4 -1 roll                       % val2 cell0 y1 tab | 
|  | 3 -1 roll                       % val2 y1 tab cell0 | 
|  | get                             % val2 y1 y0 | 
|  |  | 
|  | dup                             % val2 y1 y0 y0 | 
|  | 3 1 roll                        % val2 y0 y1 y0 | 
|  |  | 
|  | sub                             % val2 y0 (y1-y0) | 
|  | 3 -1 roll                       % y0 (y1-y0) val2 | 
|  | dup                             % y0 (y1-y0) val2 val2 | 
|  | floor cvi                       % y0 (y1-y0) val2 floor(val2) | 
|  | sub                             % y0 (y1-y0) rest | 
|  | mul                             % y0 t1 | 
|  | add                             % y | 
|  | 65535 div                       % result | 
|  |  | 
|  | } bind | 
|  |  | 
|  |  | 
|  | */ | 
|  |  | 
|  |  | 
|  | // This struct holds the memory block currently being write | 
|  | typedef struct { | 
|  | _cmsStageCLutData* Pipeline; | 
|  | cmsIOHANDLER* m; | 
|  |  | 
|  | int FirstComponent; | 
|  | int SecondComponent; | 
|  |  | 
|  | const char* PreMaj; | 
|  | const char* PostMaj; | 
|  | const char* PreMin; | 
|  | const char* PostMin; | 
|  |  | 
|  | int  FixWhite;    // Force mapping of pure white | 
|  |  | 
|  | cmsColorSpaceSignature  ColorSpace;  // ColorSpace of profile | 
|  |  | 
|  |  | 
|  | } cmsPsSamplerCargo; | 
|  |  | 
|  | static int _cmsPSActualColumn = 0; | 
|  |  | 
|  |  | 
|  | // Convert to byte | 
|  | static | 
|  | cmsUInt8Number Word2Byte(cmsUInt16Number w) | 
|  | { | 
|  | return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Write a cooked byte | 
|  | static | 
|  | void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b) | 
|  | { | 
|  | _cmsIOPrintf(m, "%02x", b); | 
|  | _cmsPSActualColumn += 2; | 
|  |  | 
|  | if (_cmsPSActualColumn > MAXPSCOLS) { | 
|  |  | 
|  | _cmsIOPrintf(m, "\n"); | 
|  | _cmsPSActualColumn = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // ----------------------------------------------------------------- PostScript generation | 
|  |  | 
|  |  | 
|  | // Removes offending carriage returns | 
|  |  | 
|  | static | 
|  | char* RemoveCR(const char* txt) | 
|  | { | 
|  | static char Buffer[2048]; | 
|  | char* pt; | 
|  |  | 
|  | strncpy(Buffer, txt, 2047); | 
|  | Buffer[2047] = 0; | 
|  | for (pt = Buffer; *pt; pt++) | 
|  | if (*pt == '\n' || *pt == '\r') *pt = ' '; | 
|  |  | 
|  | return Buffer; | 
|  |  | 
|  | } | 
|  |  | 
|  | static | 
|  | void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile) | 
|  | { | 
|  | time_t timer; | 
|  | cmsMLU *Description, *Copyright; | 
|  | char DescASCII[256], CopyrightASCII[256]; | 
|  |  | 
|  | time(&timer); | 
|  |  | 
|  | Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag); | 
|  | Copyright   = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag); | 
|  |  | 
|  | DescASCII[0] = DescASCII[255] = 0; | 
|  | CopyrightASCII[0] = CopyrightASCII[255] = 0; | 
|  |  | 
|  | if (Description != NULL) cmsMLUgetASCII(Description,  cmsNoLanguage, cmsNoCountry, DescASCII,       255); | 
|  | if (Copyright != NULL)   cmsMLUgetASCII(Copyright,    cmsNoLanguage, cmsNoCountry, CopyrightASCII,  255); | 
|  |  | 
|  | _cmsIOPrintf(m, "%%!PS-Adobe-3.0\n"); | 
|  | _cmsIOPrintf(m, "%%\n"); | 
|  | _cmsIOPrintf(m, "%% %s\n", Title); | 
|  | _cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII)); | 
|  | _cmsIOPrintf(m, "%%         %s\n", RemoveCR(CopyrightASCII)); | 
|  | _cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!! | 
|  | _cmsIOPrintf(m, "%%\n"); | 
|  | _cmsIOPrintf(m, "%%%%BeginResource\n"); | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | // Emits White & Black point. White point is always D50, Black point is the device | 
|  | // Black point adapted to D50. | 
|  |  | 
|  | static | 
|  | void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint) | 
|  | { | 
|  |  | 
|  | _cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X, | 
|  | BlackPoint -> Y, | 
|  | BlackPoint -> Z); | 
|  |  | 
|  | _cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X, | 
|  | cmsD50_XYZ()->Y, | 
|  | cmsD50_XYZ()->Z); | 
|  | } | 
|  |  | 
|  |  | 
|  | static | 
|  | void EmitRangeCheck(cmsIOHANDLER* m) | 
|  | { | 
|  | _cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if " | 
|  | "dup 1.0 gt { pop 1.0 } if "); | 
|  |  | 
|  | } | 
|  |  | 
|  | // Does write the intent | 
|  |  | 
|  | static | 
|  | void EmitIntent(cmsIOHANDLER* m, cmsUInt32Number RenderingIntent) | 
|  | { | 
|  | const char *intent; | 
|  |  | 
|  | switch (RenderingIntent) { | 
|  |  | 
|  | case INTENT_PERCEPTUAL:            intent = "Perceptual"; break; | 
|  | case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break; | 
|  | case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break; | 
|  | case INTENT_SATURATION:            intent = "Saturation"; break; | 
|  |  | 
|  | default: intent = "Undefined"; break; | 
|  | } | 
|  |  | 
|  | _cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent ); | 
|  | } | 
|  |  | 
|  | // | 
|  | //  Convert L* to Y | 
|  | // | 
|  | //      Y = Yn*[ (L* + 16) / 116] ^ 3   if (L*) >= 6 / 29 | 
|  | //        = Yn*( L* / 116) / 7.787      if (L*) < 6 / 29 | 
|  | // | 
|  |  | 
|  | // Lab -> XYZ, see the discussion above | 
|  |  | 
|  | static | 
|  | void EmitLab2XYZ(cmsIOHANDLER* m) | 
|  | { | 
|  | _cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n"); | 
|  | _cmsIOPrintf(m, "/DecodeABC [\n"); | 
|  | _cmsIOPrintf(m, "{100 mul  16 add 116 div } bind\n"); | 
|  | _cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n"); | 
|  | _cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n"); | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  | _cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n"); | 
|  | _cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n"); | 
|  | _cmsIOPrintf(m, "/DecodeLMN [\n"); | 
|  | _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"); | 
|  | _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n"); | 
|  | _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"); | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  | } | 
|  |  | 
|  | static | 
|  | void EmitSafeGuardBegin(cmsIOHANDLER* m, const char* name) | 
|  | { | 
|  | _cmsIOPrintf(m, "%%LCMS2: Save previous definition of %s on the operand stack\n", name); | 
|  | _cmsIOPrintf(m, "currentdict /%s known { /%s load } { null } ifelse\n", name, name); | 
|  | } | 
|  |  | 
|  | static | 
|  | void EmitSafeGuardEnd(cmsIOHANDLER* m, const char* name, int depth) | 
|  | { | 
|  | _cmsIOPrintf(m, "%%LCMS2: Restore previous definition of %s\n", name); | 
|  | if (depth > 1) { | 
|  | // cycle topmost items on the stack to bring the previous definition to the front | 
|  | _cmsIOPrintf(m, "%d -1 roll ", depth); | 
|  | } | 
|  | _cmsIOPrintf(m, "dup null eq { pop currentdict /%s undef } { /%s exch def } ifelse\n", name, name); | 
|  | } | 
|  |  | 
|  | // Outputs a table of words. It does use 16 bits | 
|  |  | 
|  | static | 
|  | void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table, const char* name) | 
|  | { | 
|  | cmsUInt32Number i; | 
|  | cmsFloat64Number gamma; | 
|  |  | 
|  | if (Table == NULL) return; // Error | 
|  |  | 
|  | if (Table ->nEntries <= 0) return;  // Empty table | 
|  |  | 
|  | // Suppress whole if identity | 
|  | if (cmsIsToneCurveLinear(Table)) return; | 
|  |  | 
|  | // Check if is really an exponential. If so, emit "exp" | 
|  | gamma = cmsEstimateGamma(Table, 0.001); | 
|  | if (gamma > 0) { | 
|  | _cmsIOPrintf(m, "/%s { %g exp } bind def\n", name, gamma); | 
|  | return; | 
|  | } | 
|  |  | 
|  | EmitSafeGuardBegin(m, "lcms2gammatable"); | 
|  | _cmsIOPrintf(m, "/lcms2gammatable ["); | 
|  |  | 
|  | for (i=0; i < Table->nEntries; i++) { | 
|  | if (i % 10 == 0) | 
|  | _cmsIOPrintf(m, "\n  "); | 
|  | _cmsIOPrintf(m, "%d ", Table->Table16[i]); | 
|  | } | 
|  |  | 
|  | _cmsIOPrintf(m, "] def\n"); | 
|  |  | 
|  |  | 
|  | // Emit interpolation code | 
|  |  | 
|  | // PostScript code                            Stack | 
|  | // ===============                            ======================== | 
|  | // v | 
|  | _cmsIOPrintf(m, "/%s {\n  ", name); | 
|  |  | 
|  | // Bounds check | 
|  | EmitRangeCheck(m); | 
|  |  | 
|  | _cmsIOPrintf(m, "\n  //lcms2gammatable ");    // v tab | 
|  | _cmsIOPrintf(m, "dup ");                      // v tab tab | 
|  | _cmsIOPrintf(m, "length 1 sub ");             // v tab dom | 
|  | _cmsIOPrintf(m, "3 -1 roll ");                // tab dom v | 
|  | _cmsIOPrintf(m, "mul ");                      // tab val2 | 
|  | _cmsIOPrintf(m, "dup ");                      // tab val2 val2 | 
|  | _cmsIOPrintf(m, "dup ");                      // tab val2 val2 val2 | 
|  | _cmsIOPrintf(m, "floor cvi ");                // tab val2 val2 cell0 | 
|  | _cmsIOPrintf(m, "exch ");                     // tab val2 cell0 val2 | 
|  | _cmsIOPrintf(m, "ceiling cvi ");              // tab val2 cell0 cell1 | 
|  | _cmsIOPrintf(m, "3 index ");                  // tab val2 cell0 cell1 tab | 
|  | _cmsIOPrintf(m, "exch ");                     // tab val2 cell0 tab cell1 | 
|  | _cmsIOPrintf(m, "get\n  ");                   // tab val2 cell0 y1 | 
|  | _cmsIOPrintf(m, "4 -1 roll ");                // val2 cell0 y1 tab | 
|  | _cmsIOPrintf(m, "3 -1 roll ");                // val2 y1 tab cell0 | 
|  | _cmsIOPrintf(m, "get ");                      // val2 y1 y0 | 
|  | _cmsIOPrintf(m, "dup ");                      // val2 y1 y0 y0 | 
|  | _cmsIOPrintf(m, "3 1 roll ");                 // val2 y0 y1 y0 | 
|  | _cmsIOPrintf(m, "sub ");                      // val2 y0 (y1-y0) | 
|  | _cmsIOPrintf(m, "3 -1 roll ");                // y0 (y1-y0) val2 | 
|  | _cmsIOPrintf(m, "dup ");                      // y0 (y1-y0) val2 val2 | 
|  | _cmsIOPrintf(m, "floor cvi ");                // y0 (y1-y0) val2 floor(val2) | 
|  | _cmsIOPrintf(m, "sub ");                      // y0 (y1-y0) rest | 
|  | _cmsIOPrintf(m, "mul ");                      // y0 t1 | 
|  | _cmsIOPrintf(m, "add ");                      // y | 
|  | _cmsIOPrintf(m, "65535 div\n");               // result | 
|  |  | 
|  | _cmsIOPrintf(m, "} bind def\n"); | 
|  |  | 
|  | EmitSafeGuardEnd(m, "lcms2gammatable", 1); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Compare gamma table | 
|  |  | 
|  | static | 
|  | cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Number nG1, cmsUInt32Number nG2) | 
|  | { | 
|  | if (nG1 != nG2) return FALSE; | 
|  | return memcmp(g1, g2, nG1 * sizeof(cmsUInt16Number)) == 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | // Does write a set of gamma curves | 
|  |  | 
|  | static | 
|  | void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[], const char* nameprefix) | 
|  | { | 
|  | cmsUInt32Number i; | 
|  | static char buffer[2048]; | 
|  |  | 
|  | for( i=0; i < n; i++ ) | 
|  | { | 
|  | if (g[i] == NULL) return; // Error | 
|  |  | 
|  | if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i-1]->nEntries, g[i]->nEntries)) { | 
|  |  | 
|  | _cmsIOPrintf(m, "/%s%d /%s%d load def\n", nameprefix, i, nameprefix, i-1); | 
|  | } | 
|  | else { | 
|  | snprintf(buffer, sizeof(buffer), "%s%d", nameprefix, (int) i); | 
|  | buffer[sizeof(buffer)-1] = '\0'; | 
|  | Emit1Gamma(m, g[i], buffer); | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | // Following code dumps a LUT onto memory stream | 
|  |  | 
|  |  | 
|  | // This is the sampler. Intended to work in SAMPLER_INSPECT mode, | 
|  | // that is, the callback will be called for each knot with | 
|  | // | 
|  | //          In[]  The grid location coordinates, normalized to 0..ffff | 
|  | //          Out[] The Pipeline values, normalized to 0..ffff | 
|  | // | 
|  | //  Returning a value other than 0 does terminate the sampling process | 
|  | // | 
|  | //  Each row contains Pipeline values for all but first component. So, I | 
|  | //  detect row changing by keeping a copy of last value of first | 
|  | //  component. -1 is used to mark beginning of whole block. | 
|  |  | 
|  | static | 
|  | int OutputValueSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) | 
|  | { | 
|  | cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo; | 
|  | cmsUInt32Number i; | 
|  |  | 
|  |  | 
|  | if (sc -> FixWhite) { | 
|  |  | 
|  | if (In[0] == 0xFFFF) {  // Only in L* = 100, ab = [-8..8] | 
|  |  | 
|  | if ((In[1] >= 0x7800 && In[1] <= 0x8800) && | 
|  | (In[2] >= 0x7800 && In[2] <= 0x8800)) { | 
|  |  | 
|  | cmsUInt16Number* Black; | 
|  | cmsUInt16Number* White; | 
|  | cmsUInt32Number nOutputs; | 
|  |  | 
|  | if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs)) | 
|  | return 0; | 
|  |  | 
|  | for (i=0; i < nOutputs; i++) | 
|  | Out[i] = White[i]; | 
|  | } | 
|  |  | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Hadle the parenthesis on rows | 
|  |  | 
|  | if (In[0] != sc ->FirstComponent) { | 
|  |  | 
|  | if (sc ->FirstComponent != -1) { | 
|  |  | 
|  | _cmsIOPrintf(sc ->m, sc ->PostMin); | 
|  | sc ->SecondComponent = -1; | 
|  | _cmsIOPrintf(sc ->m, sc ->PostMaj); | 
|  | } | 
|  |  | 
|  | // Begin block | 
|  | _cmsPSActualColumn = 0; | 
|  |  | 
|  | _cmsIOPrintf(sc ->m, sc ->PreMaj); | 
|  | sc ->FirstComponent = In[0]; | 
|  | } | 
|  |  | 
|  |  | 
|  | if (In[1] != sc ->SecondComponent) { | 
|  |  | 
|  | if (sc ->SecondComponent != -1) { | 
|  |  | 
|  | _cmsIOPrintf(sc ->m, sc ->PostMin); | 
|  | } | 
|  |  | 
|  | _cmsIOPrintf(sc ->m, sc ->PreMin); | 
|  | sc ->SecondComponent = In[1]; | 
|  | } | 
|  |  | 
|  | // Dump table. | 
|  |  | 
|  | for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) { | 
|  |  | 
|  | cmsUInt16Number wWordOut = Out[i]; | 
|  | cmsUInt8Number wByteOut;           // Value as byte | 
|  |  | 
|  |  | 
|  | // We always deal with Lab4 | 
|  |  | 
|  | wByteOut = Word2Byte(wWordOut); | 
|  | WriteByte(sc -> m, wByteOut); | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | // Writes a Pipeline on memstream. Could be 8 or 16 bits based | 
|  |  | 
|  | static | 
|  | void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj, | 
|  | const char* PostMaj, | 
|  | const char* PreMin, | 
|  | const char* PostMin, | 
|  | int FixWhite, | 
|  | cmsColorSpaceSignature ColorSpace) | 
|  | { | 
|  | cmsUInt32Number i; | 
|  | cmsPsSamplerCargo sc; | 
|  |  | 
|  | sc.FirstComponent = -1; | 
|  | sc.SecondComponent = -1; | 
|  | sc.Pipeline = (_cmsStageCLutData *) mpe ->Data; | 
|  | sc.m   = m; | 
|  | sc.PreMaj = PreMaj; | 
|  | sc.PostMaj= PostMaj; | 
|  |  | 
|  | sc.PreMin   = PreMin; | 
|  | sc.PostMin  = PostMin; | 
|  | sc.FixWhite = FixWhite; | 
|  | sc.ColorSpace = ColorSpace; | 
|  |  | 
|  | _cmsIOPrintf(m, "["); | 
|  |  | 
|  | for (i=0; i < sc.Pipeline->Params->nInputs; i++) | 
|  | _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]); | 
|  |  | 
|  | _cmsIOPrintf(m, " [\n"); | 
|  |  | 
|  | cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT); | 
|  |  | 
|  | _cmsIOPrintf(m, PostMin); | 
|  | _cmsIOPrintf(m, PostMaj); | 
|  | _cmsIOPrintf(m, "] "); | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | // Dumps CIEBasedA Color Space Array | 
|  |  | 
|  | static | 
|  | int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint) | 
|  | { | 
|  |  | 
|  | _cmsIOPrintf(m, "[ /CIEBasedA\n"); | 
|  | _cmsIOPrintf(m, "  <<\n"); | 
|  |  | 
|  | EmitSafeGuardBegin(m, "lcms2gammaproc"); | 
|  | Emit1Gamma(m, Curve, "lcms2gammaproc"); | 
|  |  | 
|  | _cmsIOPrintf(m, "/DecodeA /lcms2gammaproc load\n"); | 
|  | EmitSafeGuardEnd(m, "lcms2gammaproc", 3); | 
|  |  | 
|  | _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n"); | 
|  | _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); | 
|  |  | 
|  | EmitWhiteBlackD50(m, BlackPoint); | 
|  | EmitIntent(m, INTENT_PERCEPTUAL); | 
|  |  | 
|  | _cmsIOPrintf(m, ">>\n"); | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | // Dumps CIEBasedABC Color Space Array | 
|  |  | 
|  | static | 
|  | int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | _cmsIOPrintf(m, "[ /CIEBasedABC\n"); | 
|  | _cmsIOPrintf(m, "<<\n"); | 
|  |  | 
|  | EmitSafeGuardBegin(m, "lcms2gammaproc0"); | 
|  | EmitSafeGuardBegin(m, "lcms2gammaproc1"); | 
|  | EmitSafeGuardBegin(m, "lcms2gammaproc2"); | 
|  | EmitNGamma(m, 3, CurveSet, "lcms2gammaproc"); | 
|  | _cmsIOPrintf(m, "/DecodeABC [\n"); | 
|  | _cmsIOPrintf(m, "   /lcms2gammaproc0 load\n"); | 
|  | _cmsIOPrintf(m, "   /lcms2gammaproc1 load\n"); | 
|  | _cmsIOPrintf(m, "   /lcms2gammaproc2 load\n"); | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  | EmitSafeGuardEnd(m, "lcms2gammaproc2", 3); | 
|  | EmitSafeGuardEnd(m, "lcms2gammaproc1", 3); | 
|  | EmitSafeGuardEnd(m, "lcms2gammaproc0", 3); | 
|  |  | 
|  | _cmsIOPrintf(m, "/MatrixABC [ " ); | 
|  |  | 
|  | for( i=0; i < 3; i++ ) { | 
|  |  | 
|  | _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0], | 
|  | Matrix[i + 3*1], | 
|  | Matrix[i + 3*2]); | 
|  | } | 
|  |  | 
|  |  | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  |  | 
|  | _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); | 
|  |  | 
|  | EmitWhiteBlackD50(m, BlackPoint); | 
|  | EmitIntent(m, INTENT_PERCEPTUAL); | 
|  |  | 
|  | _cmsIOPrintf(m, ">>\n"); | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  |  | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | static | 
|  | int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint) | 
|  | { | 
|  | const char* PreMaj; | 
|  | const char* PostMaj; | 
|  | const char* PreMin, * PostMin; | 
|  | cmsStage* mpe; | 
|  | int i, numchans; | 
|  | static char buffer[2048]; | 
|  |  | 
|  | mpe = Pipeline->Elements; | 
|  |  | 
|  | switch (cmsStageInputChannels(mpe)) { | 
|  | case 3: | 
|  | _cmsIOPrintf(m, "[ /CIEBasedDEF\n"); | 
|  | PreMaj = "<"; | 
|  | PostMaj = ">\n"; | 
|  | PreMin = PostMin = ""; | 
|  | break; | 
|  |  | 
|  | case 4: | 
|  | _cmsIOPrintf(m, "[ /CIEBasedDEFG\n"); | 
|  | PreMaj = "["; | 
|  | PostMaj = "]\n"; | 
|  | PreMin = "<"; | 
|  | PostMin = ">\n"; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return 0; | 
|  |  | 
|  | } | 
|  |  | 
|  | _cmsIOPrintf(m, "<<\n"); | 
|  |  | 
|  | if (cmsStageType(mpe) == cmsSigCurveSetElemType) { | 
|  |  | 
|  | numchans = (int) cmsStageOutputChannels(mpe); | 
|  | for (i = 0; i < numchans; ++i) { | 
|  | snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i); | 
|  | buffer[sizeof(buffer) - 1] = '\0'; | 
|  | EmitSafeGuardBegin(m, buffer); | 
|  | } | 
|  | EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe), "lcms2gammaproc"); | 
|  | _cmsIOPrintf(m, "/DecodeDEF [\n"); | 
|  | for (i = 0; i < numchans; ++i) { | 
|  | snprintf(buffer, sizeof(buffer), "  /lcms2gammaproc%d load\n", i); | 
|  | buffer[sizeof(buffer) - 1] = '\0'; | 
|  | _cmsIOPrintf(m, buffer); | 
|  | } | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  | for (i = numchans - 1; i >= 0; --i) { | 
|  | snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i); | 
|  | buffer[sizeof(buffer) - 1] = '\0'; | 
|  | EmitSafeGuardEnd(m, buffer, 3); | 
|  | } | 
|  |  | 
|  | mpe = mpe->Next; | 
|  | } | 
|  |  | 
|  | if (cmsStageType(mpe) == cmsSigCLutElemType) { | 
|  |  | 
|  | _cmsIOPrintf(m, "/Table "); | 
|  | WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature)0); | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  | } | 
|  |  | 
|  | EmitLab2XYZ(m); | 
|  | EmitWhiteBlackD50(m, BlackPoint); | 
|  | EmitIntent(m, Intent); | 
|  |  | 
|  | _cmsIOPrintf(m, "   >>\n"); | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | // Generates a curve from a gray profile | 
|  |  | 
|  | static | 
|  | cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent) | 
|  | { | 
|  | cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); | 
|  | cmsHPROFILE hXYZ  = cmsCreateXYZProfile(); | 
|  | cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE); | 
|  | int i; | 
|  |  | 
|  | if (Out != NULL && xform != NULL) { | 
|  | for (i=0; i < 256; i++) { | 
|  |  | 
|  | cmsUInt8Number Gray = (cmsUInt8Number) i; | 
|  | cmsCIEXYZ XYZ; | 
|  |  | 
|  | cmsDoTransform(xform, &Gray, &XYZ, 1); | 
|  |  | 
|  | Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (xform) cmsDeleteTransform(xform); | 
|  | if (hXYZ) cmsCloseProfile(hXYZ); | 
|  | return Out; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | // Because PostScript has only 8 bits in /Table, we should use | 
|  | // a more perceptually uniform space... I do choose Lab. | 
|  |  | 
|  | static | 
|  | int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) | 
|  | { | 
|  | cmsHPROFILE hLab; | 
|  | cmsHTRANSFORM xform; | 
|  | cmsUInt32Number nChannels; | 
|  | cmsUInt32Number InputFormat; | 
|  | int rc; | 
|  | cmsHPROFILE Profiles[2]; | 
|  | cmsCIEXYZ BlackPointAdaptedToD50; | 
|  |  | 
|  | // Does create a device-link based transform. | 
|  | // The DeviceLink is next dumped as working CSA. | 
|  |  | 
|  | InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); | 
|  | nChannels   = T_CHANNELS(InputFormat); | 
|  |  | 
|  |  | 
|  | cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); | 
|  |  | 
|  | // Adjust output to Lab4 | 
|  | hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); | 
|  |  | 
|  | Profiles[0] = hProfile; | 
|  | Profiles[1] = hLab; | 
|  |  | 
|  | xform = cmsCreateMultiprofileTransform(Profiles, 2,  InputFormat, TYPE_Lab_DBL, Intent, 0); | 
|  | cmsCloseProfile(hLab); | 
|  |  | 
|  | if (xform == NULL) { | 
|  |  | 
|  | cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Only 1, 3 and 4 channels are allowed | 
|  |  | 
|  | switch (nChannels) { | 
|  |  | 
|  | case 1: { | 
|  | cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent); | 
|  | EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50); | 
|  | cmsFreeToneCurve(Gray2Y); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 3: | 
|  | case 4: { | 
|  | cmsUInt32Number OutFrm = TYPE_Lab_16; | 
|  | cmsPipeline* DeviceLink; | 
|  | _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; | 
|  |  | 
|  | DeviceLink = cmsPipelineDup(v ->Lut); | 
|  | if (DeviceLink == NULL) return 0; | 
|  |  | 
|  | dwFlags |= cmsFLAGS_FORCE_CLUT; | 
|  | _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); | 
|  |  | 
|  | rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); | 
|  | cmsPipelineFree(DeviceLink); | 
|  | if (rc == 0) return 0; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  |  | 
|  | cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels are supported for CSA. This profile has %d channels.", nChannels); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | cmsDeleteTransform(xform); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static | 
|  | cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe) | 
|  | { | 
|  | _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; | 
|  |  | 
|  | return Data -> Double; | 
|  | } | 
|  |  | 
|  |  | 
|  | // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based | 
|  | static | 
|  | int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper) | 
|  | { | 
|  | cmsColorSpaceSignature ColorSpace; | 
|  | int rc; | 
|  | cmsCIEXYZ BlackPointAdaptedToD50; | 
|  |  | 
|  | ColorSpace = cmsGetColorSpace(hProfile); | 
|  |  | 
|  | cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0); | 
|  |  | 
|  | if (ColorSpace == cmsSigGrayData) { | 
|  |  | 
|  | cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper); | 
|  | rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50); | 
|  |  | 
|  | } | 
|  | else | 
|  | if (ColorSpace == cmsSigRgbData) { | 
|  |  | 
|  | cmsMAT3 Mat; | 
|  | int i, j; | 
|  |  | 
|  | memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat)); | 
|  |  | 
|  | for (i = 0; i < 3; i++) | 
|  | for (j = 0; j < 3; j++) | 
|  | Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ; | 
|  |  | 
|  | rc = EmitCIEBasedABC(m, (cmsFloat64Number *)&Mat, | 
|  | _cmsStageGetPtrToCurveSet(Shaper), | 
|  | &BlackPointAdaptedToD50); | 
|  | } | 
|  | else { | 
|  |  | 
|  | cmsSignalError(m->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace."); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | // Creates a PostScript color list from a named profile data. | 
|  | // This is a HP extension, and it works in Lab instead of XYZ | 
|  |  | 
|  | static | 
|  | int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent) | 
|  | { | 
|  | cmsHTRANSFORM xform; | 
|  | cmsHPROFILE   hLab; | 
|  | cmsUInt32Number i, nColors; | 
|  | char ColorName[cmsMAX_PATH]; | 
|  | cmsNAMEDCOLORLIST* NamedColorList; | 
|  |  | 
|  | hLab  = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); | 
|  | xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0); | 
|  | if (xform == NULL) return 0; | 
|  |  | 
|  | NamedColorList = cmsGetNamedColorList(xform); | 
|  | if (NamedColorList == NULL) return 0; | 
|  |  | 
|  | _cmsIOPrintf(m, "<<\n"); | 
|  | _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA"); | 
|  | _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); | 
|  | _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); | 
|  |  | 
|  | nColors   = cmsNamedColorCount(NamedColorList); | 
|  |  | 
|  |  | 
|  | for (i=0; i < nColors; i++) { | 
|  |  | 
|  | cmsUInt16Number In[1]; | 
|  | cmsCIELab Lab; | 
|  |  | 
|  | In[0] = (cmsUInt16Number) i; | 
|  |  | 
|  | if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) | 
|  | continue; | 
|  |  | 
|  | cmsDoTransform(xform, In, &Lab, 1); | 
|  | _cmsIOPrintf(m, "  (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b); | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | _cmsIOPrintf(m, ">>\n"); | 
|  |  | 
|  | cmsDeleteTransform(xform); | 
|  | cmsCloseProfile(hLab); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | // Does create a Color Space Array on XYZ colorspace for PostScript usage | 
|  | static | 
|  | cmsUInt32Number GenerateCSA(cmsContext ContextID, | 
|  | cmsHPROFILE hProfile, | 
|  | cmsUInt32Number Intent, | 
|  | cmsUInt32Number dwFlags, | 
|  | cmsIOHANDLER* mem) | 
|  | { | 
|  | cmsUInt32Number dwBytesUsed; | 
|  | cmsPipeline* lut = NULL; | 
|  | cmsStage* Matrix, *Shaper; | 
|  |  | 
|  |  | 
|  | // Is a named color profile? | 
|  | if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { | 
|  |  | 
|  | if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error; | 
|  | } | 
|  | else { | 
|  |  | 
|  |  | 
|  | // Any profile class are allowed (including devicelink), but | 
|  | // output (PCS) colorspace must be XYZ or Lab | 
|  | cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); | 
|  |  | 
|  | if (ColorSpace != cmsSigXYZData && | 
|  | ColorSpace != cmsSigLabData) { | 
|  |  | 
|  | cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space"); | 
|  | goto Error; | 
|  | } | 
|  |  | 
|  |  | 
|  | // Read the lut with all necessary conversion stages | 
|  | lut = _cmsReadInputLUT(hProfile, Intent); | 
|  | if (lut == NULL) goto Error; | 
|  |  | 
|  |  | 
|  | // Tone curves + matrix can be implemented without any LUT | 
|  | if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) { | 
|  |  | 
|  | if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error; | 
|  |  | 
|  | } | 
|  | else { | 
|  | // We need a LUT for the rest | 
|  | if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Done, keep memory usage | 
|  | dwBytesUsed = mem ->UsedSpace; | 
|  |  | 
|  | // Get rid of LUT | 
|  | if (lut != NULL) cmsPipelineFree(lut); | 
|  |  | 
|  | // Finally, return used byte count | 
|  | return dwBytesUsed; | 
|  |  | 
|  | Error: | 
|  | if (lut != NULL) cmsPipelineFree(lut); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // ------------------------------------------------------ Color Rendering Dictionary (CRD) | 
|  |  | 
|  |  | 
|  |  | 
|  | /* | 
|  |  | 
|  | Black point compensation plus chromatic adaptation: | 
|  |  | 
|  | Step 1 - Chromatic adaptation | 
|  | ============================= | 
|  |  | 
|  | WPout | 
|  | X = ------- PQR | 
|  | Wpin | 
|  |  | 
|  | Step 2 - Black point compensation | 
|  | ================================= | 
|  |  | 
|  | (WPout - BPout)*X - WPout*(BPin - BPout) | 
|  | out = --------------------------------------- | 
|  | WPout - BPin | 
|  |  | 
|  |  | 
|  | Algorithm discussion | 
|  | ==================== | 
|  |  | 
|  | TransformPQR(WPin, BPin, WPout, BPout, PQR) | 
|  |  | 
|  | Wpin,etc= { Xws Yws Zws Pws Qws Rws } | 
|  |  | 
|  |  | 
|  | Algorithm             Stack 0...n | 
|  | =========================================================== | 
|  | PQR BPout WPout BPin WPin | 
|  | 4 index 3 get         WPin PQR BPout WPout BPin WPin | 
|  | div                   (PQR/WPin) BPout WPout BPin WPin | 
|  | 2 index 3 get         WPout (PQR/WPin) BPout WPout BPin WPin | 
|  | mult                  WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  |  | 
|  | 2 index 3 get         WPout WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  | 2 index 3 get         BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  | sub                   (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  | mult                  (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  |  | 
|  | 2 index 3 get         WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  | 4 index 3 get         BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  | 3 index 3 get         BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  |  | 
|  | sub                   (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  | mult                  (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin | 
|  | sub                   (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin | 
|  |  | 
|  | 3 index 3 get         BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin | 
|  | 3 index 3 get         WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin | 
|  | exch | 
|  | sub                   (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin | 
|  | div | 
|  |  | 
|  | exch pop | 
|  | exch pop | 
|  | exch pop | 
|  | exch pop | 
|  |  | 
|  | */ | 
|  |  | 
|  |  | 
|  | static | 
|  | void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute) | 
|  | { | 
|  |  | 
|  |  | 
|  | if (lIsAbsolute) { | 
|  |  | 
|  | // For absolute colorimetric intent, encode back to relative | 
|  | // and generate a relative Pipeline | 
|  |  | 
|  | // Relative encoding is obtained across XYZpcs*(D50/WhitePoint) | 
|  |  | 
|  | cmsCIEXYZ White; | 
|  |  | 
|  | _cmsReadMediaWhitePoint(&White, hProfile); | 
|  |  | 
|  | _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n"); | 
|  | _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); | 
|  |  | 
|  | _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n" | 
|  | "/TransformPQR [\n" | 
|  | "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n" | 
|  | "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n" | 
|  | "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n", | 
|  | White.X, White.Y, White.Z); | 
|  | return; | 
|  | } | 
|  |  | 
|  |  | 
|  | _cmsIOPrintf(m,"%% Bradford Cone Space\n" | 
|  | "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n"); | 
|  |  | 
|  | _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); | 
|  |  | 
|  |  | 
|  | // No BPC | 
|  |  | 
|  | if (!DoBPC) { | 
|  |  | 
|  | _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n" | 
|  | "/TransformPQR [\n" | 
|  | "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n" | 
|  | "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n" | 
|  | "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n"); | 
|  | } else { | 
|  |  | 
|  | // BPC | 
|  |  | 
|  | _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n" | 
|  | "/TransformPQR [\n"); | 
|  |  | 
|  | _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul " | 
|  | "2 index 3 get 2 index 3 get sub mul " | 
|  | "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub " | 
|  | "3 index 3 get 3 index 3 get exch sub div " | 
|  | "exch pop exch pop exch pop exch pop } bind\n"); | 
|  |  | 
|  | _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul " | 
|  | "2 index 4 get 2 index 4 get sub mul " | 
|  | "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub " | 
|  | "3 index 4 get 3 index 4 get exch sub div " | 
|  | "exch pop exch pop exch pop exch pop } bind\n"); | 
|  |  | 
|  | _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul " | 
|  | "2 index 5 get 2 index 5 get sub mul " | 
|  | "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub " | 
|  | "3 index 5 get 3 index 5 get exch sub div " | 
|  | "exch pop exch pop exch pop exch pop } bind\n]\n"); | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static | 
|  | void EmitXYZ2Lab(cmsIOHANDLER* m) | 
|  | { | 
|  | _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n"); | 
|  | _cmsIOPrintf(m, "/EncodeLMN [\n"); | 
|  | _cmsIOPrintf(m, "{ 0.964200  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); | 
|  | _cmsIOPrintf(m, "{ 1.000000  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); | 
|  | _cmsIOPrintf(m, "{ 0.824900  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  | _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n"); | 
|  | _cmsIOPrintf(m, "/EncodeABC [\n"); | 
|  |  | 
|  |  | 
|  | _cmsIOPrintf(m, "{ 116 mul  16 sub 100 div  } bind\n"); | 
|  | _cmsIOPrintf(m, "{ 500 mul 128 add 256 div  } bind\n"); | 
|  | _cmsIOPrintf(m, "{ 200 mul 128 add 256 div  } bind\n"); | 
|  |  | 
|  |  | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  |  | 
|  |  | 
|  | } | 
|  |  | 
|  | // Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces | 
|  | // I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted | 
|  | // space on 3D CLUT, but since space seems not to be a problem here, 33 points | 
|  | // would give a reasonable accuracy. Note also that CRD tables must operate in | 
|  | // 8 bits. | 
|  |  | 
|  | static | 
|  | int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) | 
|  | { | 
|  | cmsHPROFILE hLab; | 
|  | cmsHTRANSFORM xform; | 
|  | cmsUInt32Number i, nChannels; | 
|  | cmsUInt32Number OutputFormat; | 
|  | _cmsTRANSFORM* v; | 
|  | cmsPipeline* DeviceLink; | 
|  | cmsHPROFILE Profiles[3]; | 
|  | cmsCIEXYZ BlackPointAdaptedToD50; | 
|  | cmsBool lDoBPC = (cmsBool) (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); | 
|  | cmsBool lFixWhite = (cmsBool) !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); | 
|  | cmsUInt32Number InFrm = TYPE_Lab_16; | 
|  | cmsUInt32Number RelativeEncodingIntent; | 
|  | cmsColorSpaceSignature ColorSpace; | 
|  |  | 
|  |  | 
|  | hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); | 
|  | if (hLab == NULL) return 0; | 
|  |  | 
|  | OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); | 
|  | nChannels    = T_CHANNELS(OutputFormat); | 
|  |  | 
|  | ColorSpace = cmsGetColorSpace(hProfile); | 
|  |  | 
|  | // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision. | 
|  |  | 
|  | RelativeEncodingIntent = Intent; | 
|  | if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC) | 
|  | RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC; | 
|  |  | 
|  |  | 
|  | // Use V4 Lab always | 
|  | Profiles[0] = hLab; | 
|  | Profiles[1] = hProfile; | 
|  |  | 
|  | xform = cmsCreateMultiprofileTransformTHR(m ->ContextID, | 
|  | Profiles, 2, TYPE_Lab_DBL, | 
|  | OutputFormat, RelativeEncodingIntent, 0); | 
|  | cmsCloseProfile(hLab); | 
|  |  | 
|  | if (xform == NULL) { | 
|  |  | 
|  | cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Get a copy of the internal devicelink | 
|  | v = (_cmsTRANSFORM*) xform; | 
|  | DeviceLink = cmsPipelineDup(v ->Lut); | 
|  | if (DeviceLink == NULL) return 0; | 
|  |  | 
|  |  | 
|  | // We need a CLUT | 
|  | dwFlags |= cmsFLAGS_FORCE_CLUT; | 
|  | _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); | 
|  |  | 
|  | _cmsIOPrintf(m, "<<\n"); | 
|  | _cmsIOPrintf(m, "/ColorRenderingType 1\n"); | 
|  |  | 
|  |  | 
|  | cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); | 
|  |  | 
|  | // Emit headers, etc. | 
|  | EmitWhiteBlackD50(m, &BlackPointAdaptedToD50); | 
|  | EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC); | 
|  | EmitXYZ2Lab(m); | 
|  |  | 
|  |  | 
|  | // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab | 
|  | // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127, | 
|  | // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to | 
|  | // zero. This would sacrifice a bit of highlights, but failure to do so would cause | 
|  | // scum dot. Ouch. | 
|  |  | 
|  | if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) | 
|  | lFixWhite = FALSE; | 
|  |  | 
|  | _cmsIOPrintf(m, "/RenderTable "); | 
|  |  | 
|  |  | 
|  | WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace); | 
|  |  | 
|  | _cmsIOPrintf(m, " %d {} bind ", nChannels); | 
|  |  | 
|  | for (i=1; i < nChannels; i++) | 
|  | _cmsIOPrintf(m, "dup "); | 
|  |  | 
|  | _cmsIOPrintf(m, "]\n"); | 
|  |  | 
|  |  | 
|  | EmitIntent(m, Intent); | 
|  |  | 
|  | _cmsIOPrintf(m, ">>\n"); | 
|  |  | 
|  | if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { | 
|  |  | 
|  | _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n"); | 
|  | } | 
|  |  | 
|  | cmsPipelineFree(DeviceLink); | 
|  | cmsDeleteTransform(xform); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | // Builds a ASCII string containing colorant list in 0..1.0 range | 
|  | static | 
|  | void BuildColorantList(char *Colorant, cmsUInt32Number nColorant, cmsUInt16Number Out[]) | 
|  | { | 
|  | char Buff[32]; | 
|  | cmsUInt32Number j; | 
|  |  | 
|  | Colorant[0] = 0; | 
|  | if (nColorant > cmsMAXCHANNELS) | 
|  | nColorant = cmsMAXCHANNELS; | 
|  |  | 
|  | for (j = 0; j < nColorant; j++) { | 
|  |  | 
|  | snprintf(Buff, 31, "%.3f", Out[j] / 65535.0); | 
|  | Buff[31] = 0; | 
|  | strcat(Colorant, Buff); | 
|  | if (j < nColorant - 1) | 
|  | strcat(Colorant, " "); | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Creates a PostScript color list from a named profile data. | 
|  | // This is a HP extension. | 
|  |  | 
|  | static | 
|  | int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent, cmsUInt32Number dwFlags) | 
|  | { | 
|  | cmsHTRANSFORM xform; | 
|  | cmsUInt32Number i, nColors, nColorant; | 
|  | cmsUInt32Number OutputFormat; | 
|  | char ColorName[cmsMAX_PATH]; | 
|  | char Colorant[512]; | 
|  | cmsNAMEDCOLORLIST* NamedColorList; | 
|  |  | 
|  |  | 
|  | OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE); | 
|  | nColorant    = T_CHANNELS(OutputFormat); | 
|  |  | 
|  |  | 
|  | xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags); | 
|  | if (xform == NULL) return 0; | 
|  |  | 
|  |  | 
|  | NamedColorList = cmsGetNamedColorList(xform); | 
|  | if (NamedColorList == NULL) return 0; | 
|  |  | 
|  | _cmsIOPrintf(m, "<<\n"); | 
|  | _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile"); | 
|  | _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); | 
|  | _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); | 
|  |  | 
|  | nColors   = cmsNamedColorCount(NamedColorList); | 
|  |  | 
|  | for (i=0; i < nColors; i++) { | 
|  |  | 
|  | cmsUInt16Number In[1]; | 
|  | cmsUInt16Number Out[cmsMAXCHANNELS]; | 
|  |  | 
|  | In[0] = (cmsUInt16Number) i; | 
|  |  | 
|  | if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) | 
|  | continue; | 
|  |  | 
|  | cmsDoTransform(xform, In, Out, 1); | 
|  | BuildColorantList(Colorant, nColorant, Out); | 
|  | _cmsIOPrintf(m, "  (%s) [ %s ]\n", ColorName, Colorant); | 
|  | } | 
|  |  | 
|  | _cmsIOPrintf(m, "   >>"); | 
|  |  | 
|  | if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { | 
|  |  | 
|  | _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n"); | 
|  | } | 
|  |  | 
|  | cmsDeleteTransform(xform); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | // This one does create a Color Rendering Dictionary. | 
|  | // CRD are always LUT-Based, no matter if profile is | 
|  | // implemented as matrix-shaper. | 
|  |  | 
|  | static | 
|  | cmsUInt32Number  GenerateCRD(cmsContext ContextID, | 
|  | cmsHPROFILE hProfile, | 
|  | cmsUInt32Number Intent, cmsUInt32Number dwFlags, | 
|  | cmsIOHANDLER* mem) | 
|  | { | 
|  | cmsUInt32Number dwBytesUsed; | 
|  |  | 
|  | if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { | 
|  |  | 
|  | EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Is a named color profile? | 
|  | if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { | 
|  |  | 
|  | if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | else { | 
|  |  | 
|  | // CRD are always implemented as LUT | 
|  |  | 
|  | if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { | 
|  |  | 
|  | _cmsIOPrintf(mem, "%%%%EndResource\n"); | 
|  | _cmsIOPrintf(mem, "\n%% CRD End\n"); | 
|  | } | 
|  |  | 
|  | // Done, keep memory usage | 
|  | dwBytesUsed = mem ->UsedSpace; | 
|  |  | 
|  | // Finally, return used byte count | 
|  | return dwBytesUsed; | 
|  |  | 
|  | cmsUNUSED_PARAMETER(ContextID); | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  | cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, | 
|  | cmsPSResourceType Type, | 
|  | cmsHPROFILE hProfile, | 
|  | cmsUInt32Number Intent, | 
|  | cmsUInt32Number dwFlags, | 
|  | cmsIOHANDLER* io) | 
|  | { | 
|  | cmsUInt32Number  rc; | 
|  |  | 
|  |  | 
|  | switch (Type) { | 
|  |  | 
|  | case cmsPS_RESOURCE_CSA: | 
|  | rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | case cmsPS_RESOURCE_CRD: | 
|  | rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, | 
|  | cmsHPROFILE hProfile, | 
|  | cmsUInt32Number Intent, cmsUInt32Number dwFlags, | 
|  | void* Buffer, cmsUInt32Number dwBufferLen) | 
|  | { | 
|  | cmsIOHANDLER* mem; | 
|  | cmsUInt32Number dwBytesUsed; | 
|  |  | 
|  | // Set up the serialization engine | 
|  | if (Buffer == NULL) | 
|  | mem = cmsOpenIOhandlerFromNULL(ContextID); | 
|  | else | 
|  | mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); | 
|  |  | 
|  | if (!mem) return 0; | 
|  |  | 
|  | dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem); | 
|  |  | 
|  | // Get rid of memory stream | 
|  | cmsCloseIOhandler(mem); | 
|  |  | 
|  | return dwBytesUsed; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | // Does create a Color Space Array on XYZ colorspace for PostScript usage | 
|  | cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, | 
|  | cmsHPROFILE hProfile, | 
|  | cmsUInt32Number Intent, | 
|  | cmsUInt32Number dwFlags, | 
|  | void* Buffer, | 
|  | cmsUInt32Number dwBufferLen) | 
|  | { | 
|  | cmsIOHANDLER* mem; | 
|  | cmsUInt32Number dwBytesUsed; | 
|  |  | 
|  | if (Buffer == NULL) | 
|  | mem = cmsOpenIOhandlerFromNULL(ContextID); | 
|  | else | 
|  | mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); | 
|  |  | 
|  | if (!mem) return 0; | 
|  |  | 
|  | dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem); | 
|  |  | 
|  | // Get rid of memory stream | 
|  | cmsCloseIOhandler(mem); | 
|  |  | 
|  | return dwBytesUsed; | 
|  |  | 
|  | } |