diff options
Diffstat (limited to 'debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c')
| -rwxr-xr-x | debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c | 1717 |
1 files changed, 1717 insertions, 0 deletions
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c new file mode 100755 index 00000000..6c352a70 --- /dev/null +++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c @@ -0,0 +1,1717 @@ +// +// Little cms +// Copyright (C) 1998-2007 Marti Maria +// +// 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. + + +// Postscript level 2 operators + + + +#include "lcms.h" +#include <time.h> +#include <stdarg.h> + +// PostScript ColorRenderingDictionary and ColorSpaceArray + +LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCSA(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen); +LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRD(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen); +LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRDEx(cmsHPROFILE hProfile, int Intent, DWORD dwFlags, LPVOID Buffer, DWORD dwBufferLen); +// -------------------------------------------------------------------- Implementation + +#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 precission, 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 LUT-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 D50X 0.0 D50Y 0.0 D50Z ] + /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 on dependig + of profile implementation. Since here is no interpolation tables, I do + the conversion directly to XYZ + + + + [ /CIEBasedABC + << + /DecodeABC [ {transfer1} {transfer2} {transfer3} ] + /MatrixABC [Matrix] + /RangeLMN [ 0.0 D50X 0.0 D50Y 0.0 D50Z ] + /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 + + +*/ + +static icTagSignature Device2PCSTab[] = {icSigAToB0Tag, // Perceptual + icSigAToB1Tag, // Relative colorimetric + icSigAToB2Tag, // Saturation + icSigAToB1Tag }; // Absolute colorimetric + // (Relative/WhitePoint) + + +// --------------------------------------------------------------- Memory Stream +// +// This struct holds the memory block currently being write +// + +typedef struct { + LPBYTE Block; + LPBYTE Ptr; + DWORD dwMax; + DWORD dwUsed; + int MaxCols; + int Col; + int HasError; + + } MEMSTREAM, FAR* LPMEMSTREAM; + + +typedef struct { + LPLUT Lut; + LPMEMSTREAM m; + + int FirstComponent; + int SecondComponent; + + int bps; + const char* PreMaj; + const char* PostMaj; + const char* PreMin; + const char* PostMin; + + int lIsInput; // Handle L* encoding + int FixWhite; // Force mapping of pure white + + icColorSpaceSignature ColorSpace; // ColorSpace of profile + + + } SAMPLERCARGO, FAR* LPSAMPLERCARGO; + + +// Creates a ready to use memory stream +static +LPMEMSTREAM CreateMemStream(LPBYTE Buffer, DWORD dwMax, int MaxCols) +{ + LPMEMSTREAM m = (LPMEMSTREAM) _cmsMalloc(sizeof(MEMSTREAM)); + if (m == NULL) return NULL; + + ZeroMemory(m, sizeof(MEMSTREAM)); + + m -> Block = m -> Ptr = Buffer; + m -> dwMax = dwMax; + m -> dwUsed = 0; + m -> MaxCols = MaxCols; + m -> Col = 0; + m -> HasError = 0; + + return m; +} + + + +// Convert to byte +static +BYTE Word2Byte(WORD w) +{ + return (BYTE) floor((double) w / 257.0 + 0.5); +} + + +// Convert to byte (using ICC2 notation) + +static +BYTE L2Byte(WORD w) +{ + int ww = w + 0x0080; + + if (ww > 0xFFFF) return 0xFF; + + return (BYTE) ((WORD) (ww >> 8) & 0xFF); +} + +// Write a raw, uncooked byte. Check for space +static +void WriteRawByte(LPMEMSTREAM m, BYTE b) +{ + if (m -> dwUsed + 1 > m -> dwMax) { + m -> HasError = 1; + } + + if (!m ->HasError && m ->Block) { + *m ->Ptr++ = b; + } + + m -> dwUsed++; +} + +// Write a cooked byte +static +void WriteByte(LPMEMSTREAM m, BYTE b) +{ + static const BYTE Hex[] = "0123456789ABCDEF"; + BYTE c; + + c = Hex[(b >> 4) & 0x0f]; + WriteRawByte(m, c); + + c = Hex[b & 0x0f]; + WriteRawByte(m, c); + + m -> Col += 2; + + if (m -> Col > m -> MaxCols) { + + WriteRawByte(m, '\n'); + m -> Col = 0; + } + +} + +// Does write a formatted string. Guaranteed to be 2048 bytes at most. +static +void Writef(LPMEMSTREAM m, const char *frm, ...) +{ + va_list args; + LPBYTE pt; + BYTE Buffer[2048]; + + va_start(args, frm); + + vsnprintf((char*) Buffer, 2048, frm, args); + + for (pt = Buffer; *pt; pt++) { + + WriteRawByte(m, *pt); + } + + va_end(args); +} + + + +// ----------------------------------------------------------------- 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(LPMEMSTREAM m, const char* Title, cmsHPROFILE hProfile) +{ + + time_t timer; + + time(&timer); + + Writef(m, "%%!PS-Adobe-3.0\n"); + Writef(m, "%%\n"); + Writef(m, "%% %s\n", Title); + Writef(m, "%% Source: %s\n", RemoveCR(cmsTakeProductName(hProfile))); + Writef(m, "%% Description: %s\n", RemoveCR(cmsTakeProductDesc(hProfile))); + Writef(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!! + Writef(m, "%%\n"); + Writef(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(LPMEMSTREAM m, LPcmsCIEXYZ BlackPoint) +{ + + Writef(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X, + BlackPoint -> Y, + BlackPoint -> Z); + + Writef(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X, + cmsD50_XYZ()->Y, + cmsD50_XYZ()->Z); +} + + +static +void EmitRangeCheck(LPMEMSTREAM m) +{ + Writef(m, "dup 0.0 lt { pop 0.0 } if " + "dup 1.0 gt { pop 1.0 } if "); + +} + +// Does write the intent + +static +void EmitIntent(LPMEMSTREAM m, int 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; + } + + Writef(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 +// + +/* +static +void EmitL2Y(LPMEMSTREAM m) +{ + Writef(m, + "{ " + "100 mul 16 add 116 div " // (L * 100 + 16) / 116 + "dup 6 29 div ge " // >= 6 / 29 ? + "{ dup dup mul mul } " // yes, ^3 and done + "{ 4 29 div sub 108 841 div mul } " // no, slope limiting + "ifelse } bind "); +} +*/ + + +// Lab -> XYZ, see the discussion above + +static +void EmitLab2XYZ(LPMEMSTREAM m) +{ + Writef(m, "/RangeABC [ 0 1 0 1 0 1]\n"); + Writef(m, "/DecodeABC [\n"); + Writef(m, "{100 mul 16 add 116 div } bind\n"); + Writef(m, "{255 mul 128 sub 500 div } bind\n"); + Writef(m, "{255 mul 128 sub 200 div } bind\n"); + Writef(m, "]\n"); + Writef(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n"); + Writef(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n"); + Writef(m, "/DecodeLMN [\n"); + Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n"); + Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n"); + Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n"); + Writef(m, "]\n"); +} + + + +// Outputs a table of words. It does use 16 bits + +static +void Emit1Gamma(LPMEMSTREAM m, LPWORD Table, int nEntries) +{ + int i; + double gamma; + + + if (nEntries <= 0) return; // Empty table + + // Suppress whole if identity + if (cmsIsLinear(Table, nEntries)) { + Writef(m, "{} "); + return; + } + + + // Check if is really an exponential. If so, emit "exp" + gamma = cmsEstimateGammaEx(Table, nEntries, 0.001); + if (gamma > 0) { + Writef(m, "{ %g exp } bind ", gamma); + return; + } + + Writef(m, "{ "); + + // Bounds check + EmitRangeCheck(m); + + // Emit intepolation code + + // PostScript code Stack + // =============== ======================== + // v + Writef(m, " ["); + + // TODO: Check for endianess!!! + + for (i=0; i < nEntries; i++) { + Writef(m, "%d ", Table[i]); + } + + Writef(m, "] "); // v tab + + Writef(m, "dup "); // v tab tab + Writef(m, "length 1 sub "); // v tab dom + Writef(m, "3 -1 roll "); // tab dom v + Writef(m, "mul "); // tab val2 + Writef(m, "dup "); // tab val2 val2 + Writef(m, "dup "); // tab val2 val2 val2 + Writef(m, "floor cvi "); // tab val2 val2 cell0 + Writef(m, "exch "); // tab val2 cell0 val2 + Writef(m, "ceiling cvi "); // tab val2 cell0 cell1 + Writef(m, "3 index "); // tab val2 cell0 cell1 tab + Writef(m, "exch "); // tab val2 cell0 tab cell1 + Writef(m, "get "); // tab val2 cell0 y1 + Writef(m, "4 -1 roll "); // val2 cell0 y1 tab + Writef(m, "3 -1 roll "); // val2 y1 tab cell0 + Writef(m, "get "); // val2 y1 y0 + Writef(m, "dup "); // val2 y1 y0 y0 + Writef(m, "3 1 roll "); // val2 y0 y1 y0 + Writef(m, "sub "); // val2 y0 (y1-y0) + Writef(m, "3 -1 roll "); // y0 (y1-y0) val2 + Writef(m, "dup "); // y0 (y1-y0) val2 val2 + Writef(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2) + Writef(m, "sub "); // y0 (y1-y0) rest + Writef(m, "mul "); // y0 t1 + Writef(m, "add "); // y + Writef(m, "65535 div "); // result + + Writef(m, " } bind "); +} + + +// Compare gamma table + +static +LCMSBOOL GammaTableEquals(LPWORD g1, LPWORD g2, int nEntries) +{ + return memcmp(g1, g2, nEntries* sizeof(WORD)) == 0; +} + + +// Does write a set of gamma curves + +static +void EmitNGamma(LPMEMSTREAM m, int n, LPWORD g[], int nEntries) +{ + int i; + + for( i=0; i < n; i++ ) + { + if (i > 0 && GammaTableEquals(g[i-1], g[i], nEntries)) { + + Writef(m, "dup "); + } + else { + Emit1Gamma(m, g[i], nEntries); + } + } + +} + + +// Check whatever a profile has CLUT tables (only on input) + +static +LCMSBOOL IsLUTbased(cmsHPROFILE hProfile, int Intent) +{ + icTagSignature Tag; + + // Check if adequate tag is present + Tag = Device2PCSTab[Intent]; + + if (cmsIsTag(hProfile, Tag)) return 1; + + // If not present, revert to default (perceptual) + Tag = icSigAToB0Tag; + + // If no tag present, try matrix-shaper + return cmsIsTag(hProfile, Tag); +} + + + +// 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 LUT values, normalized to 0..ffff +// +// Returning a value other than 0 does terminate the sampling process +// +// Each row contains LUT 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 begining of whole block. + +static +int OutputValueSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +{ + LPSAMPLERCARGO sc = (LPSAMPLERCARGO) Cargo; + unsigned int 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)) { + + WORD* Black; + WORD* White; + int nOutputs; + + if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs)) + return 0; + + for (i=0; i < (unsigned int) nOutputs; i++) + Out[i] = White[i]; + } + + + } + } + + + // Hadle the parenthesis on rows + + if (In[0] != sc ->FirstComponent) { + + if (sc ->FirstComponent != -1) { + + Writef(sc ->m, sc ->PostMin); + sc ->SecondComponent = -1; + Writef(sc ->m, sc ->PostMaj); + } + + // Begin block + sc->m->Col = 0; + + Writef(sc ->m, sc ->PreMaj); + sc ->FirstComponent = In[0]; + } + + + if (In[1] != sc ->SecondComponent) { + + if (sc ->SecondComponent != -1) { + + Writef(sc ->m, sc ->PostMin); + } + + Writef(sc ->m, sc ->PreMin); + sc ->SecondComponent = In[1]; + } + + + + // Dump table. Could be Word or byte based on + // depending on bps member (16 bps mode is not currently + // being used at all, but is here for future ampliations) + + for (i=0; i < sc -> Lut ->OutputChan; i++) { + + WORD wWordOut = Out[i]; + + if (sc ->bps == 8) { + + // Value as byte + BYTE wByteOut; + + // If is input, convert from Lab2 to Lab4 (just divide by 256) + + if (sc ->lIsInput) { + + + wByteOut = L2Byte(wWordOut); + } + else + wByteOut = Word2Byte(wWordOut); + + WriteByte(sc -> m, wByteOut); + } + else { + + // Value as word + WriteByte(sc -> m, (BYTE) (wWordOut & 0xFF)); + WriteByte(sc -> m, (BYTE) ((wWordOut >> 8) & 0xFF)); + } + } + + return 1; +} + +// Writes a LUT on memstream. Could be 8 or 16 bits based + +static +void WriteCLUT(LPMEMSTREAM m, LPLUT Lut, int bps, const char* PreMaj, + const char* PostMaj, + const char* PreMin, + const char* PostMin, + int lIsInput, + int FixWhite, + icColorSpaceSignature ColorSpace) +{ + unsigned int i; + SAMPLERCARGO sc; + + sc.FirstComponent = -1; + sc.SecondComponent = -1; + sc.Lut = Lut; + sc.m = m; + sc.bps = bps; + sc.PreMaj = PreMaj; + sc.PostMaj= PostMaj; + + sc.PreMin = PreMin; + sc.PostMin = PostMin; + sc.lIsInput = lIsInput; + sc.FixWhite = FixWhite; + sc.ColorSpace = ColorSpace; + + Writef(m, "["); + + for (i=0; i < Lut ->InputChan; i++) + Writef(m, " %d ", Lut ->cLutPoints); + + Writef(m, " [\n"); + + + + cmsSample3DGrid(Lut, OutputValueSampler, (LPVOID) &sc, SAMPLER_INSPECT); + + + Writef(m, PostMin); + Writef(m, PostMaj); + Writef(m, "] "); + + + +} + + +// Dumps CIEBasedA Color Space Array + +static +int EmitCIEBasedA(LPMEMSTREAM m, LPWORD Tab, int nEntries, LPcmsCIEXYZ BlackPoint) +{ + + Writef(m, "[ /CIEBasedA\n"); + Writef(m, " <<\n"); + + Writef(m, "/DecodeA "); + + Emit1Gamma(m,Tab, nEntries); + + Writef(m, " \n"); + + Writef(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n"); + Writef(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); + + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, INTENT_PERCEPTUAL); + + Writef(m, ">>\n"); + Writef(m, "]\n"); + + return 1; +} + + +// Dumps CIEBasedABC Color Space Array + +static +int EmitCIEBasedABC(LPMEMSTREAM m, LPWORD L[], int nEntries, LPWMAT3 Matrix, LPcmsCIEXYZ BlackPoint) +{ + int i; + + Writef(m, "[ /CIEBasedABC\n"); + Writef(m, "<<\n"); + Writef(m, "/DecodeABC [ "); + + EmitNGamma(m, 3, L, nEntries); + + Writef(m, "]\n"); + + Writef(m, "/MatrixABC [ " ); + + for( i=0; i < 3; i++ ) { + + Writef(m, "%.6f %.6f %.6f ", + FIXED_TO_DOUBLE(Matrix->v[0].n[i]), + FIXED_TO_DOUBLE(Matrix->v[1].n[i]), + FIXED_TO_DOUBLE(Matrix->v[2].n[i])); + } + + + Writef(m, "]\n"); + + Writef(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); + + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, INTENT_PERCEPTUAL); + + Writef(m, ">>\n"); + Writef(m, "]\n"); + + + return 1; +} + + +static +int EmitCIEBasedDEF(LPMEMSTREAM m, LPLUT Lut, int Intent, LPcmsCIEXYZ BlackPoint) +{ + const char* PreMaj; + const char* PostMaj; + const char* PreMin, *PostMin; + + switch (Lut ->InputChan) { + case 3: + + Writef(m, "[ /CIEBasedDEF\n"); + PreMaj ="<"; + PostMaj= ">\n"; + PreMin = PostMin = ""; + break; + case 4: + Writef(m, "[ /CIEBasedDEFG\n"); + PreMaj = "["; + PostMaj = "]\n"; + PreMin = "<"; + PostMin = ">\n"; + break; + default: + return 0; + + } + + Writef(m, "<<\n"); + + if (Lut ->wFlags & LUT_HASTL1) { + + Writef(m, "/DecodeDEF [ "); + EmitNGamma(m, Lut ->InputChan, Lut ->L1, Lut ->CLut16params.nSamples); + Writef(m, "]\n"); + } + + + + if (Lut ->wFlags & LUT_HAS3DGRID) { + + Writef(m, "/Table "); + WriteCLUT(m, Lut, 8, PreMaj, PostMaj, PreMin, PostMin, TRUE, FALSE, (icColorSpaceSignature) 0); + Writef(m, "]\n"); + } + + EmitLab2XYZ(m); + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, Intent); + + Writef(m, " >>\n"); + Writef(m, "]\n"); + + + return 1; +} + +// Generates a curve from a gray profile + +static +LPGAMMATABLE ExtractGray2Y(cmsHPROFILE hProfile, int Intent) +{ + LPGAMMATABLE Out = cmsAllocGamma(256); + cmsHPROFILE hXYZ = cmsCreateXYZProfile(); + cmsHTRANSFORM xform = cmsCreateTransform(hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOTPRECALC); + int i; + + for (i=0; i < 256; i++) { + + BYTE Gray = (BYTE) i; + cmsCIEXYZ XYZ; + + cmsDoTransform(xform, &Gray, &XYZ, 1); + + Out ->GammaTable[i] =_cmsClampWord((int) floor(XYZ.Y * 65535.0 + 0.5)); + } + + cmsDeleteTransform(xform); + cmsCloseProfile(hXYZ); + return Out; +} + + + +// Because PostScrip has only 8 bits in /Table, we should use +// a more perceptually uniform space... I do choose Lab. + +static +int WriteInputLUT(LPMEMSTREAM m, cmsHPROFILE hProfile, int Intent) +{ + cmsHPROFILE hLab; + cmsHTRANSFORM xform; + icColorSpaceSignature ColorSpace; + int nChannels; + DWORD InputFormat; + int rc; + cmsHPROFILE Profiles[2]; + cmsCIEXYZ BlackPointAdaptedToD50; + + // Does create a device-link based transform. + // The DeviceLink is next dumped as working CSA. + + hLab = cmsCreateLabProfile(NULL); + ColorSpace = cmsGetColorSpace(hProfile); + nChannels = _cmsChannelsOf(ColorSpace); + InputFormat = CHANNELS_SH(nChannels) | BYTES_SH(2); + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent,LCMS_BPFLAGS_D50_ADAPTED); + + // Is a devicelink profile? + if (cmsGetDeviceClass(hProfile) == icSigLinkClass) { + + // if devicelink output already Lab, use it directly + + if (cmsGetPCS(hProfile) == icSigLabData) { + + xform = cmsCreateTransform(hProfile, InputFormat, NULL, + TYPE_Lab_DBL, Intent, 0); + } + else { + + // Nope, adjust output to Lab if possible + + Profiles[0] = hProfile; + Profiles[1] = hLab; + + xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, + TYPE_Lab_DBL, Intent, 0); + } + + + } + else { + + // This is a normal profile + xform = cmsCreateTransform(hProfile, InputFormat, hLab, + TYPE_Lab_DBL, Intent, 0); + } + + + + if (xform == NULL) { + + cmsSignalError(LCMS_ERRC_ABORTED, "Cannot create transform Profile -> Lab"); + return 0; + } + + // Only 1, 3 and 4 channels are allowed + + switch (nChannels) { + + case 1: { + LPGAMMATABLE Gray2Y = ExtractGray2Y(hProfile, Intent); + EmitCIEBasedA(m, Gray2Y->GammaTable, Gray2Y ->nEntries, &BlackPointAdaptedToD50); + cmsFreeGamma(Gray2Y); + } + break; + + case 3: + case 4: { + LPLUT DeviceLink; + _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; + + if (v ->DeviceLink) + rc = EmitCIEBasedDEF(m, v->DeviceLink, Intent, &BlackPointAdaptedToD50); + else { + DeviceLink = _cmsPrecalculateDeviceLink(xform, 0); + rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); + cmsFreeLUT(DeviceLink); + } + } + break; + + default: + + cmsSignalError(LCMS_ERRC_ABORTED, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels); + return 0; + } + + + cmsDeleteTransform(xform); + cmsCloseProfile(hLab); + return 1; +} + + + +// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based + +static +int WriteInputMatrixShaper(LPMEMSTREAM m, cmsHPROFILE hProfile) +{ + icColorSpaceSignature ColorSpace; + LPMATSHAPER MatShaper; + int rc; + cmsCIEXYZ BlackPointAdaptedToD50; + + + ColorSpace = cmsGetColorSpace(hProfile); + MatShaper = cmsBuildInputMatrixShaper(hProfile); + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_BPFLAGS_D50_ADAPTED); + + if (MatShaper == NULL) { + + cmsSignalError(LCMS_ERRC_ABORTED, "This profile is not suitable for input"); + return 0; + } + + if (ColorSpace == icSigGrayData) { + + rc = EmitCIEBasedA(m, MatShaper ->L[0], + MatShaper ->p16.nSamples, + &BlackPointAdaptedToD50); + + } + else + if (ColorSpace == icSigRgbData) { + + + rc = EmitCIEBasedABC(m, MatShaper->L, + MatShaper ->p16.nSamples, + &MatShaper ->Matrix, + &BlackPointAdaptedToD50); + } + else { + + cmsSignalError(LCMS_ERRC_ABORTED, "Profile is not suitable for CSA. Unsupported colorspace."); + return 0; + } + + cmsFreeMatShaper(MatShaper); + 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(LPMEMSTREAM m, cmsHPROFILE hNamedColor, int Intent) +{ + cmsHTRANSFORM xform; + cmsHPROFILE hLab; + int i, nColors; + char ColorName[32]; + + + hLab = cmsCreateLabProfile(NULL); + xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, + hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC); + if (xform == NULL) return 0; + + + Writef(m, "<<\n"); + Writef(m, "(colorlistcomment) (%s)\n", "Named color CSA"); + Writef(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); + Writef(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); + + nColors = cmsNamedColorCount(xform); + + + for (i=0; i < nColors; i++) { + + WORD In[1]; + cmsCIELab Lab; + + In[0] = (WORD) i; + + if (!cmsNamedColorInfo(xform, i, ColorName, NULL, NULL)) + continue; + + cmsDoTransform(xform, In, &Lab, 1); + Writef(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b); + } + + + + Writef(m, ">>\n"); + + cmsDeleteTransform(xform); + cmsCloseProfile(hLab); + return 1; +} + + +// Does create a Color Space Array on XYZ colorspace for PostScript usage + +DWORD LCMSEXPORT cmsGetPostScriptCSA(cmsHPROFILE hProfile, + int Intent, + LPVOID Buffer, DWORD dwBufferLen) +{ + + LPMEMSTREAM mem; + DWORD dwBytesUsed; + + // Set up the serialization engine + mem = CreateMemStream((LPBYTE) Buffer, dwBufferLen, MAXPSCOLS); + if (!mem) return 0; + + + // Is a named color profile? + if (cmsGetDeviceClass(hProfile) == icSigNamedColorClass) { + + if (!WriteNamedColorCSA(mem, hProfile, Intent)) { + + _cmsFree((void*) mem); + return 0; + } + } + else { + + + // Any profile class are allowed (including devicelink), but + // output (PCS) colorspace must be XYZ or Lab + icColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); + + if (ColorSpace != icSigXYZData && + ColorSpace != icSigLabData) { + + cmsSignalError(LCMS_ERRC_ABORTED, "Invalid output color space"); + _cmsFree((void*) mem); + return 0; + } + + // Is there any CLUT? + if (IsLUTbased(hProfile, Intent)) { + + // Yes, so handle as LUT-based + if (!WriteInputLUT(mem, hProfile, Intent)) { + + _cmsFree((void*) mem); + return 0; + } + } + else { + + // No, try Matrix-shaper (this only works on XYZ) + + if (!WriteInputMatrixShaper(mem, hProfile)) { + + _cmsFree((void*) mem); // Something went wrong + return 0; + } + } + } + + + // Done, keep memory usage + dwBytesUsed = mem ->dwUsed; + + // Get rid of memory stream + _cmsFree((void*) mem); + + // Finally, return used byte count + return dwBytesUsed; +} + +// ------------------------------------------------------ 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(LPMEMSTREAM m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute) +{ + + + if (lIsAbsolute) { + + // For absolute colorimetric intent, encode back to relative + // and generate a relative LUT + + // Relative encoding is obtained across XYZpcs*(D50/WhitePoint) + + cmsCIEXYZ White; + + cmsTakeMediaWhitePoint(&White, hProfile); + + Writef(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n"); + Writef(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); + + Writef(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; + } + + + Writef(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"); + + Writef(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); + + + // No BPC + + if (!DoBPC) { + + Writef(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 + + Writef(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n" + "/TransformPQR [\n"); + + Writef(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"); + + Writef(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"); + + Writef(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(LPMEMSTREAM m) +{ + Writef(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n"); + Writef(m, "/EncodeLMN [\n"); + Writef(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + Writef(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + Writef(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + Writef(m, "]\n"); + Writef(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n"); + Writef(m, "/EncodeABC [\n"); + + + Writef(m, "{ 116 mul 16 sub 100 div } bind\n"); + Writef(m, "{ 500 mul 128 add 256 div } bind\n"); + Writef(m, "{ 200 mul 128 add 256 div } bind\n"); + + + Writef(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 accurancy. Note also that CRD tables must operate in +// 8 bits. + +static +int WriteOutputLUT(LPMEMSTREAM m, cmsHPROFILE hProfile, int Intent, DWORD dwFlags) +{ + cmsHPROFILE hLab; + cmsHTRANSFORM xform; + icColorSpaceSignature ColorSpace; + int i, nChannels; + DWORD OutputFormat; + _LPcmsTRANSFORM v; + LPLUT DeviceLink; + cmsHPROFILE Profiles[3]; + cmsCIEXYZ BlackPointAdaptedToD50; + LCMSBOOL lFreeDeviceLink = FALSE; + LCMSBOOL lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); + LCMSBOOL lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); + int RelativeEncodingIntent; + + + + hLab = cmsCreateLabProfile(NULL); + + ColorSpace = cmsGetColorSpace(hProfile); + nChannels = _cmsChannelsOf(ColorSpace); + OutputFormat = CHANNELS_SH(nChannels) | BYTES_SH(2); + + // For absolute colorimetric, the LUT is encoded as relative + // in order to preserve precission. + + RelativeEncodingIntent = Intent; + if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC) + RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC; + + + // Is a devicelink profile? + if (cmsGetDeviceClass(hProfile) == icSigLinkClass) { + + // if devicelink input already in Lab + + if (ColorSpace == icSigLabData) { + + // adjust input to Lab to our v4 + + Profiles[0] = hLab; + Profiles[1] = hProfile; + + xform = cmsCreateMultiprofileTransform(Profiles, 2, TYPE_Lab_DBL, + OutputFormat, RelativeEncodingIntent, + dwFlags|cmsFLAGS_NOWHITEONWHITEFIXUP|cmsFLAGS_NOPRELINEARIZATION); + + } + else { + cmsSignalError(LCMS_ERRC_ABORTED, "Cannot use devicelink profile for CRD creation"); + return 0; + } + + + } + else { + + // This is a normal profile + xform = cmsCreateTransform(hLab, TYPE_Lab_DBL, hProfile, + OutputFormat, RelativeEncodingIntent, dwFlags|cmsFLAGS_NOWHITEONWHITEFIXUP|cmsFLAGS_NOPRELINEARIZATION); + } + + if (xform == NULL) { + + cmsSignalError(LCMS_ERRC_ABORTED, "Cannot create transform Lab -> Profile in CRD creation"); + return 0; + } + + // Get the internal precalculated devicelink + + v = (_LPcmsTRANSFORM) xform; + DeviceLink = v ->DeviceLink; + + if (!DeviceLink) { + + DeviceLink = _cmsPrecalculateDeviceLink(xform, cmsFLAGS_NOPRELINEARIZATION); + lFreeDeviceLink = TRUE; + } + + Writef(m, "<<\n"); + Writef(m, "/ColorRenderingType 1\n"); + + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, LCMS_BPFLAGS_D50_ADAPTED); + + // Emit headers, etc. + EmitWhiteBlackD50(m, &BlackPointAdaptedToD50); + EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC); + EmitXYZ2Lab(m); + + if (DeviceLink ->wFlags & LUT_HASTL1) { + + // Shouldn't happen + cmsSignalError(LCMS_ERRC_ABORTED, "Internal error (prelinearization on CRD)"); + return 0; + } + + + // 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; + + Writef(m, "/RenderTable "); + + WriteCLUT(m, DeviceLink, 8, "<", ">\n", "", "", FALSE, + lFixWhite, ColorSpace); + + Writef(m, " %d {} bind ", nChannels); + + for (i=1; i < nChannels; i++) + Writef(m, "dup "); + + Writef(m, "]\n"); + + + EmitIntent(m, Intent); + + Writef(m, ">>\n"); + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + Writef(m, "/Current exch /ColorRendering defineresource pop\n"); + } + + if (lFreeDeviceLink) cmsFreeLUT(DeviceLink); + cmsDeleteTransform(xform); + cmsCloseProfile(hLab); + + return 1; +} + + +// Builds a ASCII string containing colorant list in 0..1.0 range +static +void BuildColorantList(char *Colorant, int nColorant, WORD Out[]) +{ + char Buff[32]; + int j; + + Colorant[0] = 0; + if (nColorant > MAXCHANNELS) + nColorant = MAXCHANNELS; + + for (j=0; j < nColorant; j++) { + + sprintf(Buff, "%.3f", Out[j] / 65535.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(LPMEMSTREAM m, cmsHPROFILE hNamedColor, int Intent, DWORD dwFlags) +{ + cmsHTRANSFORM xform; + int i, nColors, nColorant; + DWORD OutputFormat; + char ColorName[32]; + char Colorant[128]; + + nColorant = _cmsChannelsOf(cmsGetColorSpace(hNamedColor)); + OutputFormat = CHANNELS_SH(nColorant) | BYTES_SH(2); + + xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, + NULL, OutputFormat, Intent, cmsFLAGS_NOTPRECALC); + if (xform == NULL) return 0; + + + Writef(m, "<<\n"); + Writef(m, "(colorlistcomment) (%s) \n", "Named profile"); + Writef(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); + Writef(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); + + nColors = cmsNamedColorCount(xform); + + + for (i=0; i < nColors; i++) { + + WORD In[1]; + WORD Out[MAXCHANNELS]; + + In[0] = (WORD) i; + + if (!cmsNamedColorInfo(xform, i, ColorName, NULL, NULL)) + continue; + + cmsDoTransform(xform, In, Out, 1); + BuildColorantList(Colorant, nColorant, Out); + Writef(m, " (%s) [ %s ]\n", ColorName, Colorant); + } + + Writef(m, " >>"); + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + Writef(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. + +DWORD LCMSEXPORT cmsGetPostScriptCRDEx(cmsHPROFILE hProfile, + int Intent, DWORD dwFlags, + LPVOID Buffer, DWORD dwBufferLen) +{ + + LPMEMSTREAM mem; + DWORD dwBytesUsed; + + // Set up the serialization artifact + mem = CreateMemStream((LPBYTE) Buffer, dwBufferLen, MAXPSCOLS); + if (!mem) return 0; + + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile); + } + + + // Is a named color profile? + if (cmsGetDeviceClass(hProfile) == icSigNamedColorClass) { + + if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) { + + _cmsFree((void*) mem); + return 0; + } + } + else { + + // CRD are always implemented as LUT. + + + if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) { + _cmsFree((void*) mem); + return 0; + } + } + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + Writef(mem, "%%%%EndResource\n"); + Writef(mem, "\n%% CRD End\n"); + } + + // Done, keep memory usage + dwBytesUsed = mem ->dwUsed; + + // Get rid of memory stream + _cmsFree((void*) mem); + + // Finally, return used byte count + return dwBytesUsed; +} + + +// For compatibility with previous versions + +DWORD LCMSEXPORT cmsGetPostScriptCRD(cmsHPROFILE hProfile, + int Intent, + LPVOID Buffer, DWORD dwBufferLen) +{ + return cmsGetPostScriptCRDEx(hProfile, Intent, 0, Buffer, dwBufferLen); +} |
