summaryrefslogtreecommitdiffstats
path: root/debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c')
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsps2.c1717
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);
+}