Path: blob/master/src/java.desktop/share/native/liblcms/cmsps2.c
41152 views
/*1* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.2*3* This code is free software; you can redistribute it and/or modify it4* under the terms of the GNU General Public License version 2 only, as5* published by the Free Software Foundation. Oracle designates this6* particular file as subject to the "Classpath" exception as provided7* by Oracle in the LICENSE file that accompanied this code.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*/2324// This file is available under and governed by the GNU General Public25// License version 2 only, as published by the Free Software Foundation.26// However, the following notice accompanied the original version of this27// file:28//29//---------------------------------------------------------------------------------30//31// Little Color Management System32// Copyright (c) 1998-2020 Marti Maria Saguer33//34// Permission is hereby granted, free of charge, to any person obtaining35// a copy of this software and associated documentation files (the "Software"),36// to deal in the Software without restriction, including without limitation37// the rights to use, copy, modify, merge, publish, distribute, sublicense,38// and/or sell copies of the Software, and to permit persons to whom the Software39// is furnished to do so, subject to the following conditions:40//41// The above copyright notice and this permission notice shall be included in42// all copies or substantial portions of the Software.43//44// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,45// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO46// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND47// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE48// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION49// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION50// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.51//52//---------------------------------------------------------------------------------53//5455#include "lcms2_internal.h"5657// PostScript ColorRenderingDictionary and ColorSpaceArray585960#define MAXPSCOLS 60 // Columns on tables6162/*63Implementation64--------------6566PostScript does use XYZ as its internal PCS. But since PostScript67interpolation tables are limited to 8 bits, I use Lab as a way to68improve the accuracy, favoring perceptual results. So, for the creation69of each CRD, CSA the profiles are converted to Lab via a device70link between profile -> Lab or Lab -> profile. The PS code necessary to71convert Lab <-> XYZ is also included.72737475Color Space Arrays (CSA)76==================================================================================7778In order to obtain precision, code chooses between three ways to implement79the device -> XYZ transform. These cases identifies monochrome profiles (often80implemented as a set of curves), matrix-shaper and Pipeline-based.8182Monochrome83-----------8485This is implemented as /CIEBasedA CSA. The prelinearization curve is86placed into /DecodeA section, and matrix equals to D50. Since here is87no interpolation tables, I do the conversion directly to XYZ8889NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT90flag is forced on such profiles.9192[ /CIEBasedA93<<94/DecodeA { transfer function } bind95/MatrixA [D50]96/RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]97/WhitePoint [D50]98/BlackPoint [BP]99/RenderingIntent (intent)100>>101]102103On simpler profiles, the PCS is already XYZ, so no conversion is required.104105106Matrix-shaper based107-------------------108109This is implemented both with /CIEBasedABC or /CIEBasedDEF depending on the110profile implementation. Since here there are no interpolation tables, I do111the conversion directly to XYZ112113114115[ /CIEBasedABC116<<117/DecodeABC [ {transfer1} {transfer2} {transfer3} ]118/MatrixABC [Matrix]119/RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]120/DecodeLMN [ { / 2} dup dup ]121/WhitePoint [D50]122/BlackPoint [BP]123/RenderingIntent (intent)124>>125]126127128CLUT based129----------130131Lab is used in such cases.132133[ /CIEBasedDEF134<<135/DecodeDEF [ <prelinearization> ]136/Table [ p p p [<...>]]137/RangeABC [ 0 1 0 1 0 1]138/DecodeABC[ <postlinearization> ]139/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]140% -128/500 1+127/500 0 1 -127/200 1+128/200141/MatrixABC [ 1 1 1 1 0 0 0 0 -1]142/WhitePoint [D50]143/BlackPoint [BP]144/RenderingIntent (intent)145]146147148Color Rendering Dictionaries (CRD)149==================================150These are always implemented as CLUT, and always are using Lab. Since CRD are expected to151be used as resources, the code adds the definition as well.152153<<154/ColorRenderingType 1155/WhitePoint [ D50 ]156/BlackPoint [BP]157/MatrixPQR [ Bradford ]158/RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ]159/TransformPQR [160{4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind161{4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind162{4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind163]164/MatrixABC <...>165/EncodeABC <...>166/RangeABC <.. used for XYZ -> Lab>167/EncodeLMN168/RenderTable [ p p p [<...>]]169170/RenderingIntent (Perceptual)171>>172/Current exch /ColorRendering defineresource pop173174175The following stages are used to convert from XYZ to Lab176--------------------------------------------------------177178Input is given at LMN stage on X, Y, Z179180Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn)181182/EncodeLMN [183184{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind185{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind186{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind187188]189190191MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn)192193| 0 1 0|194| 1 -1 0|195| 0 1 -1|196197/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]198199EncodeABC finally gives Lab values.200201/EncodeABC [202{ 116 mul 16 sub 100 div } bind203{ 500 mul 128 add 255 div } bind204{ 200 mul 128 add 255 div } bind205]206207The following stages are used to convert Lab to XYZ208----------------------------------------------------209210/RangeABC [ 0 1 0 1 0 1]211/DecodeABC [ { 100 mul 16 add 116 div } bind212{ 255 mul 128 sub 500 div } bind213{ 255 mul 128 sub 200 div } bind214]215216/MatrixABC [ 1 1 1 1 0 0 0 0 -1]217/DecodeLMN [218{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind219{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind220{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind221]222223224*/225226/*227228PostScript algorithms discussion.229=========================================================================================================2302311D interpolation algorithm2322332341D interpolation (float)235------------------------236237val2 = Domain * Value;238239cell0 = (int) floor(val2);240cell1 = (int) ceil(val2);241242rest = val2 - cell0;243244y0 = LutTable[cell0] ;245y1 = LutTable[cell1] ;246247y = y0 + (y1 - y0) * rest;248249250251PostScript code Stack252================================================253254{ % v255<check 0..1.0>256[array] % v tab257dup % v tab tab258length 1 sub % v tab dom2592603 -1 roll % tab dom v261262mul % tab val2263dup % tab val2 val2264dup % tab val2 val2 val2265floor cvi % tab val2 val2 cell0266exch % tab val2 cell0 val2267ceiling cvi % tab val2 cell0 cell12682693 index % tab val2 cell0 cell1 tab270exch % tab val2 cell0 tab cell1271get % tab val2 cell0 y12722734 -1 roll % val2 cell0 y1 tab2743 -1 roll % val2 y1 tab cell0275get % val2 y1 y0276277dup % val2 y1 y0 y02783 1 roll % val2 y0 y1 y0279280sub % val2 y0 (y1-y0)2813 -1 roll % y0 (y1-y0) val2282dup % y0 (y1-y0) val2 val2283floor cvi % y0 (y1-y0) val2 floor(val2)284sub % y0 (y1-y0) rest285mul % y0 t1286add % y28765535 div % result288289} bind290291292*/293294295// This struct holds the memory block currently being write296typedef struct {297_cmsStageCLutData* Pipeline;298cmsIOHANDLER* m;299300int FirstComponent;301int SecondComponent;302303const char* PreMaj;304const char* PostMaj;305const char* PreMin;306const char* PostMin;307308int FixWhite; // Force mapping of pure white309310cmsColorSpaceSignature ColorSpace; // ColorSpace of profile311312313} cmsPsSamplerCargo;314315static int _cmsPSActualColumn = 0;316317318// Convert to byte319static320cmsUInt8Number Word2Byte(cmsUInt16Number w)321{322return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5);323}324325326// Write a cooked byte327static328void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b)329{330_cmsIOPrintf(m, "%02x", b);331_cmsPSActualColumn += 2;332333if (_cmsPSActualColumn > MAXPSCOLS) {334335_cmsIOPrintf(m, "\n");336_cmsPSActualColumn = 0;337}338}339340// ----------------------------------------------------------------- PostScript generation341342343// Removes offending carriage returns344345static346char* RemoveCR(const char* txt)347{348static char Buffer[2048];349char* pt;350351strncpy(Buffer, txt, 2047);352Buffer[2047] = 0;353for (pt = Buffer; *pt; pt++)354if (*pt == '\n' || *pt == '\r') *pt = ' ';355356return Buffer;357358}359360static361void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile)362{363time_t timer;364cmsMLU *Description, *Copyright;365char DescASCII[256], CopyrightASCII[256];366367time(&timer);368369Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag);370Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag);371372DescASCII[0] = DescASCII[255] = 0;373CopyrightASCII[0] = CopyrightASCII[255] = 0;374375if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255);376if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255);377378_cmsIOPrintf(m, "%%!PS-Adobe-3.0\n");379_cmsIOPrintf(m, "%%\n");380_cmsIOPrintf(m, "%% %s\n", Title);381_cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII));382_cmsIOPrintf(m, "%% %s\n", RemoveCR(CopyrightASCII));383_cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!!384_cmsIOPrintf(m, "%%\n");385_cmsIOPrintf(m, "%%%%BeginResource\n");386387}388389390// Emits White & Black point. White point is always D50, Black point is the device391// Black point adapted to D50.392393static394void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint)395{396397_cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X,398BlackPoint -> Y,399BlackPoint -> Z);400401_cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X,402cmsD50_XYZ()->Y,403cmsD50_XYZ()->Z);404}405406407static408void EmitRangeCheck(cmsIOHANDLER* m)409{410_cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if "411"dup 1.0 gt { pop 1.0 } if ");412413}414415// Does write the intent416417static418void EmitIntent(cmsIOHANDLER* m, cmsUInt32Number RenderingIntent)419{420const char *intent;421422switch (RenderingIntent) {423424case INTENT_PERCEPTUAL: intent = "Perceptual"; break;425case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break;426case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break;427case INTENT_SATURATION: intent = "Saturation"; break;428429default: intent = "Undefined"; break;430}431432_cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent );433}434435//436// Convert L* to Y437//438// Y = Yn*[ (L* + 16) / 116] ^ 3 if (L*) >= 6 / 29439// = Yn*( L* / 116) / 7.787 if (L*) < 6 / 29440//441442// Lab -> XYZ, see the discussion above443444static445void EmitLab2XYZ(cmsIOHANDLER* m)446{447_cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n");448_cmsIOPrintf(m, "/DecodeABC [\n");449_cmsIOPrintf(m, "{100 mul 16 add 116 div } bind\n");450_cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n");451_cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n");452_cmsIOPrintf(m, "]\n");453_cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n");454_cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n");455_cmsIOPrintf(m, "/DecodeLMN [\n");456_cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n");457_cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n");458_cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n");459_cmsIOPrintf(m, "]\n");460}461462static463void EmitSafeGuardBegin(cmsIOHANDLER* m, const char* name)464{465_cmsIOPrintf(m, "%%LCMS2: Save previous definition of %s on the operand stack\n", name);466_cmsIOPrintf(m, "currentdict /%s known { /%s load } { null } ifelse\n", name, name);467}468469static470void EmitSafeGuardEnd(cmsIOHANDLER* m, const char* name, int depth)471{472_cmsIOPrintf(m, "%%LCMS2: Restore previous definition of %s\n", name);473if (depth > 1) {474// cycle topmost items on the stack to bring the previous definition to the front475_cmsIOPrintf(m, "%d -1 roll ", depth);476}477_cmsIOPrintf(m, "dup null eq { pop currentdict /%s undef } { /%s exch def } ifelse\n", name, name);478}479480// Outputs a table of words. It does use 16 bits481482static483void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table, const char* name)484{485cmsUInt32Number i;486cmsFloat64Number gamma;487488if (Table == NULL) return; // Error489490if (Table ->nEntries <= 0) return; // Empty table491492// Suppress whole if identity493if (cmsIsToneCurveLinear(Table)) return;494495// Check if is really an exponential. If so, emit "exp"496gamma = cmsEstimateGamma(Table, 0.001);497if (gamma > 0) {498_cmsIOPrintf(m, "/%s { %g exp } bind def\n", name, gamma);499return;500}501502EmitSafeGuardBegin(m, "lcms2gammatable");503_cmsIOPrintf(m, "/lcms2gammatable [");504505for (i=0; i < Table->nEntries; i++) {506if (i % 10 == 0)507_cmsIOPrintf(m, "\n ");508_cmsIOPrintf(m, "%d ", Table->Table16[i]);509}510511_cmsIOPrintf(m, "] def\n");512513514// Emit interpolation code515516// PostScript code Stack517// =============== ========================518// v519_cmsIOPrintf(m, "/%s {\n ", name);520521// Bounds check522EmitRangeCheck(m);523524_cmsIOPrintf(m, "\n //lcms2gammatable "); // v tab525_cmsIOPrintf(m, "dup "); // v tab tab526_cmsIOPrintf(m, "length 1 sub "); // v tab dom527_cmsIOPrintf(m, "3 -1 roll "); // tab dom v528_cmsIOPrintf(m, "mul "); // tab val2529_cmsIOPrintf(m, "dup "); // tab val2 val2530_cmsIOPrintf(m, "dup "); // tab val2 val2 val2531_cmsIOPrintf(m, "floor cvi "); // tab val2 val2 cell0532_cmsIOPrintf(m, "exch "); // tab val2 cell0 val2533_cmsIOPrintf(m, "ceiling cvi "); // tab val2 cell0 cell1534_cmsIOPrintf(m, "3 index "); // tab val2 cell0 cell1 tab535_cmsIOPrintf(m, "exch "); // tab val2 cell0 tab cell1536_cmsIOPrintf(m, "get\n "); // tab val2 cell0 y1537_cmsIOPrintf(m, "4 -1 roll "); // val2 cell0 y1 tab538_cmsIOPrintf(m, "3 -1 roll "); // val2 y1 tab cell0539_cmsIOPrintf(m, "get "); // val2 y1 y0540_cmsIOPrintf(m, "dup "); // val2 y1 y0 y0541_cmsIOPrintf(m, "3 1 roll "); // val2 y0 y1 y0542_cmsIOPrintf(m, "sub "); // val2 y0 (y1-y0)543_cmsIOPrintf(m, "3 -1 roll "); // y0 (y1-y0) val2544_cmsIOPrintf(m, "dup "); // y0 (y1-y0) val2 val2545_cmsIOPrintf(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2)546_cmsIOPrintf(m, "sub "); // y0 (y1-y0) rest547_cmsIOPrintf(m, "mul "); // y0 t1548_cmsIOPrintf(m, "add "); // y549_cmsIOPrintf(m, "65535 div\n"); // result550551_cmsIOPrintf(m, "} bind def\n");552553EmitSafeGuardEnd(m, "lcms2gammatable", 1);554}555556557// Compare gamma table558559static560cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Number nG1, cmsUInt32Number nG2)561{562if (nG1 != nG2) return FALSE;563return memcmp(g1, g2, nG1 * sizeof(cmsUInt16Number)) == 0;564}565566567// Does write a set of gamma curves568569static570void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[], const char* nameprefix)571{572cmsUInt32Number i;573static char buffer[2048];574575for( i=0; i < n; i++ )576{577if (g[i] == NULL) return; // Error578579if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i-1]->nEntries, g[i]->nEntries)) {580581_cmsIOPrintf(m, "/%s%d /%s%d load def\n", nameprefix, i, nameprefix, i-1);582}583else {584snprintf(buffer, sizeof(buffer), "%s%d", nameprefix, (int) i);585buffer[sizeof(buffer)-1] = '\0';586Emit1Gamma(m, g[i], buffer);587}588}589590}591592593// Following code dumps a LUT onto memory stream594595596// This is the sampler. Intended to work in SAMPLER_INSPECT mode,597// that is, the callback will be called for each knot with598//599// In[] The grid location coordinates, normalized to 0..ffff600// Out[] The Pipeline values, normalized to 0..ffff601//602// Returning a value other than 0 does terminate the sampling process603//604// Each row contains Pipeline values for all but first component. So, I605// detect row changing by keeping a copy of last value of first606// component. -1 is used to mark beginning of whole block.607608static609int OutputValueSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)610{611cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;612cmsUInt32Number i;613614615if (sc -> FixWhite) {616617if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8]618619if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&620(In[2] >= 0x7800 && In[2] <= 0x8800)) {621622cmsUInt16Number* Black;623cmsUInt16Number* White;624cmsUInt32Number nOutputs;625626if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))627return 0;628629for (i=0; i < nOutputs; i++)630Out[i] = White[i];631}632633634}635}636637638// Hadle the parenthesis on rows639640if (In[0] != sc ->FirstComponent) {641642if (sc ->FirstComponent != -1) {643644_cmsIOPrintf(sc ->m, sc ->PostMin);645sc ->SecondComponent = -1;646_cmsIOPrintf(sc ->m, sc ->PostMaj);647}648649// Begin block650_cmsPSActualColumn = 0;651652_cmsIOPrintf(sc ->m, sc ->PreMaj);653sc ->FirstComponent = In[0];654}655656657if (In[1] != sc ->SecondComponent) {658659if (sc ->SecondComponent != -1) {660661_cmsIOPrintf(sc ->m, sc ->PostMin);662}663664_cmsIOPrintf(sc ->m, sc ->PreMin);665sc ->SecondComponent = In[1];666}667668// Dump table.669670for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {671672cmsUInt16Number wWordOut = Out[i];673cmsUInt8Number wByteOut; // Value as byte674675676// We always deal with Lab4677678wByteOut = Word2Byte(wWordOut);679WriteByte(sc -> m, wByteOut);680}681682return 1;683}684685// Writes a Pipeline on memstream. Could be 8 or 16 bits based686687static688void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,689const char* PostMaj,690const char* PreMin,691const char* PostMin,692int FixWhite,693cmsColorSpaceSignature ColorSpace)694{695cmsUInt32Number i;696cmsPsSamplerCargo sc;697698sc.FirstComponent = -1;699sc.SecondComponent = -1;700sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;701sc.m = m;702sc.PreMaj = PreMaj;703sc.PostMaj= PostMaj;704705sc.PreMin = PreMin;706sc.PostMin = PostMin;707sc.FixWhite = FixWhite;708sc.ColorSpace = ColorSpace;709710_cmsIOPrintf(m, "[");711712for (i=0; i < sc.Pipeline->Params->nInputs; i++)713_cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);714715_cmsIOPrintf(m, " [\n");716717cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT);718719_cmsIOPrintf(m, PostMin);720_cmsIOPrintf(m, PostMaj);721_cmsIOPrintf(m, "] ");722723}724725726// Dumps CIEBasedA Color Space Array727728static729int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)730{731732_cmsIOPrintf(m, "[ /CIEBasedA\n");733_cmsIOPrintf(m, " <<\n");734735EmitSafeGuardBegin(m, "lcms2gammaproc");736Emit1Gamma(m, Curve, "lcms2gammaproc");737738_cmsIOPrintf(m, "/DecodeA /lcms2gammaproc load\n");739EmitSafeGuardEnd(m, "lcms2gammaproc", 3);740741_cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");742_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");743744EmitWhiteBlackD50(m, BlackPoint);745EmitIntent(m, INTENT_PERCEPTUAL);746747_cmsIOPrintf(m, ">>\n");748_cmsIOPrintf(m, "]\n");749750return 1;751}752753754// Dumps CIEBasedABC Color Space Array755756static757int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)758{759int i;760761_cmsIOPrintf(m, "[ /CIEBasedABC\n");762_cmsIOPrintf(m, "<<\n");763764EmitSafeGuardBegin(m, "lcms2gammaproc0");765EmitSafeGuardBegin(m, "lcms2gammaproc1");766EmitSafeGuardBegin(m, "lcms2gammaproc2");767EmitNGamma(m, 3, CurveSet, "lcms2gammaproc");768_cmsIOPrintf(m, "/DecodeABC [\n");769_cmsIOPrintf(m, " /lcms2gammaproc0 load\n");770_cmsIOPrintf(m, " /lcms2gammaproc1 load\n");771_cmsIOPrintf(m, " /lcms2gammaproc2 load\n");772_cmsIOPrintf(m, "]\n");773EmitSafeGuardEnd(m, "lcms2gammaproc2", 3);774EmitSafeGuardEnd(m, "lcms2gammaproc1", 3);775EmitSafeGuardEnd(m, "lcms2gammaproc0", 3);776777_cmsIOPrintf(m, "/MatrixABC [ " );778779for( i=0; i < 3; i++ ) {780781_cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],782Matrix[i + 3*1],783Matrix[i + 3*2]);784}785786787_cmsIOPrintf(m, "]\n");788789_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");790791EmitWhiteBlackD50(m, BlackPoint);792EmitIntent(m, INTENT_PERCEPTUAL);793794_cmsIOPrintf(m, ">>\n");795_cmsIOPrintf(m, "]\n");796797798return 1;799}800801802static803int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint)804{805const char* PreMaj;806const char* PostMaj;807const char* PreMin, * PostMin;808cmsStage* mpe;809int i, numchans;810static char buffer[2048];811812mpe = Pipeline->Elements;813814switch (cmsStageInputChannels(mpe)) {815case 3:816_cmsIOPrintf(m, "[ /CIEBasedDEF\n");817PreMaj = "<";818PostMaj = ">\n";819PreMin = PostMin = "";820break;821822case 4:823_cmsIOPrintf(m, "[ /CIEBasedDEFG\n");824PreMaj = "[";825PostMaj = "]\n";826PreMin = "<";827PostMin = ">\n";828break;829830default:831return 0;832833}834835_cmsIOPrintf(m, "<<\n");836837if (cmsStageType(mpe) == cmsSigCurveSetElemType) {838839numchans = (int) cmsStageOutputChannels(mpe);840for (i = 0; i < numchans; ++i) {841snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);842buffer[sizeof(buffer) - 1] = '\0';843EmitSafeGuardBegin(m, buffer);844}845EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe), "lcms2gammaproc");846_cmsIOPrintf(m, "/DecodeDEF [\n");847for (i = 0; i < numchans; ++i) {848snprintf(buffer, sizeof(buffer), " /lcms2gammaproc%d load\n", i);849buffer[sizeof(buffer) - 1] = '\0';850_cmsIOPrintf(m, buffer);851}852_cmsIOPrintf(m, "]\n");853for (i = numchans - 1; i >= 0; --i) {854snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);855buffer[sizeof(buffer) - 1] = '\0';856EmitSafeGuardEnd(m, buffer, 3);857}858859mpe = mpe->Next;860}861862if (cmsStageType(mpe) == cmsSigCLutElemType) {863864_cmsIOPrintf(m, "/Table ");865WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature)0);866_cmsIOPrintf(m, "]\n");867}868869EmitLab2XYZ(m);870EmitWhiteBlackD50(m, BlackPoint);871EmitIntent(m, Intent);872873_cmsIOPrintf(m, " >>\n");874_cmsIOPrintf(m, "]\n");875876return 1;877}878879// Generates a curve from a gray profile880881static882cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent)883{884cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);885cmsHPROFILE hXYZ = cmsCreateXYZProfile();886cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);887int i;888889if (Out != NULL && xform != NULL) {890for (i=0; i < 256; i++) {891892cmsUInt8Number Gray = (cmsUInt8Number) i;893cmsCIEXYZ XYZ;894895cmsDoTransform(xform, &Gray, &XYZ, 1);896897Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);898}899}900901if (xform) cmsDeleteTransform(xform);902if (hXYZ) cmsCloseProfile(hXYZ);903return Out;904}905906907908// Because PostScript has only 8 bits in /Table, we should use909// a more perceptually uniform space... I do choose Lab.910911static912int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)913{914cmsHPROFILE hLab;915cmsHTRANSFORM xform;916cmsUInt32Number nChannels;917cmsUInt32Number InputFormat;918int rc;919cmsHPROFILE Profiles[2];920cmsCIEXYZ BlackPointAdaptedToD50;921922// Does create a device-link based transform.923// The DeviceLink is next dumped as working CSA.924925InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);926nChannels = T_CHANNELS(InputFormat);927928929cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);930931// Adjust output to Lab4932hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);933934Profiles[0] = hProfile;935Profiles[1] = hLab;936937xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);938cmsCloseProfile(hLab);939940if (xform == NULL) {941942cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");943return 0;944}945946// Only 1, 3 and 4 channels are allowed947948switch (nChannels) {949950case 1: {951cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);952EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);953cmsFreeToneCurve(Gray2Y);954}955break;956957case 3:958case 4: {959cmsUInt32Number OutFrm = TYPE_Lab_16;960cmsPipeline* DeviceLink;961_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;962963DeviceLink = cmsPipelineDup(v ->Lut);964if (DeviceLink == NULL) return 0;965966dwFlags |= cmsFLAGS_FORCE_CLUT;967_cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);968969rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);970cmsPipelineFree(DeviceLink);971if (rc == 0) return 0;972}973break;974975default:976977cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels are supported for CSA. This profile has %d channels.", nChannels);978return 0;979}980981982cmsDeleteTransform(xform);983984return 1;985}986987static988cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)989{990_cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;991992return Data -> Double;993}994995996// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based997static998int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)999{1000cmsColorSpaceSignature ColorSpace;1001int rc;1002cmsCIEXYZ BlackPointAdaptedToD50;10031004ColorSpace = cmsGetColorSpace(hProfile);10051006cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);10071008if (ColorSpace == cmsSigGrayData) {10091010cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);1011rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);10121013}1014else1015if (ColorSpace == cmsSigRgbData) {10161017cmsMAT3 Mat;1018int i, j;10191020memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));10211022for (i = 0; i < 3; i++)1023for (j = 0; j < 3; j++)1024Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;10251026rc = EmitCIEBasedABC(m, (cmsFloat64Number *)&Mat,1027_cmsStageGetPtrToCurveSet(Shaper),1028&BlackPointAdaptedToD50);1029}1030else {10311032cmsSignalError(m->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");1033return 0;1034}10351036return rc;1037}1038103910401041// Creates a PostScript color list from a named profile data.1042// This is a HP extension, and it works in Lab instead of XYZ10431044static1045int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent)1046{1047cmsHTRANSFORM xform;1048cmsHPROFILE hLab;1049cmsUInt32Number i, nColors;1050char ColorName[cmsMAX_PATH];1051cmsNAMEDCOLORLIST* NamedColorList;10521053hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);1054xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);1055if (xform == NULL) return 0;10561057NamedColorList = cmsGetNamedColorList(xform);1058if (NamedColorList == NULL) return 0;10591060_cmsIOPrintf(m, "<<\n");1061_cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");1062_cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");1063_cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");10641065nColors = cmsNamedColorCount(NamedColorList);106610671068for (i=0; i < nColors; i++) {10691070cmsUInt16Number In[1];1071cmsCIELab Lab;10721073In[0] = (cmsUInt16Number) i;10741075if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))1076continue;10771078cmsDoTransform(xform, In, &Lab, 1);1079_cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);1080}1081108210831084_cmsIOPrintf(m, ">>\n");10851086cmsDeleteTransform(xform);1087cmsCloseProfile(hLab);1088return 1;1089}109010911092// Does create a Color Space Array on XYZ colorspace for PostScript usage1093static1094cmsUInt32Number GenerateCSA(cmsContext ContextID,1095cmsHPROFILE hProfile,1096cmsUInt32Number Intent,1097cmsUInt32Number dwFlags,1098cmsIOHANDLER* mem)1099{1100cmsUInt32Number dwBytesUsed;1101cmsPipeline* lut = NULL;1102cmsStage* Matrix, *Shaper;110311041105// Is a named color profile?1106if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {11071108if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;1109}1110else {111111121113// Any profile class are allowed (including devicelink), but1114// output (PCS) colorspace must be XYZ or Lab1115cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);11161117if (ColorSpace != cmsSigXYZData &&1118ColorSpace != cmsSigLabData) {11191120cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");1121goto Error;1122}112311241125// Read the lut with all necessary conversion stages1126lut = _cmsReadInputLUT(hProfile, Intent);1127if (lut == NULL) goto Error;112811291130// Tone curves + matrix can be implemented without any LUT1131if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {11321133if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;11341135}1136else {1137// We need a LUT for the rest1138if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;1139}1140}114111421143// Done, keep memory usage1144dwBytesUsed = mem ->UsedSpace;11451146// Get rid of LUT1147if (lut != NULL) cmsPipelineFree(lut);11481149// Finally, return used byte count1150return dwBytesUsed;11511152Error:1153if (lut != NULL) cmsPipelineFree(lut);1154return 0;1155}11561157// ------------------------------------------------------ Color Rendering Dictionary (CRD)1158115911601161/*11621163Black point compensation plus chromatic adaptation:11641165Step 1 - Chromatic adaptation1166=============================11671168WPout1169X = ------- PQR1170Wpin11711172Step 2 - Black point compensation1173=================================11741175(WPout - BPout)*X - WPout*(BPin - BPout)1176out = ---------------------------------------1177WPout - BPin117811791180Algorithm discussion1181====================11821183TransformPQR(WPin, BPin, WPout, BPout, PQR)11841185Wpin,etc= { Xws Yws Zws Pws Qws Rws }118611871188Algorithm Stack 0...n1189===========================================================1190PQR BPout WPout BPin WPin11914 index 3 get WPin PQR BPout WPout BPin WPin1192div (PQR/WPin) BPout WPout BPin WPin11932 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin1194mult WPout*(PQR/WPin) BPout WPout BPin WPin119511962 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin11972 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin1198sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin1199mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin120012012 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin12024 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin12033 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin12041205sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin1206mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin1207sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin120812093 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin12103 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin1211exch1212sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin1213div12141215exch pop1216exch pop1217exch pop1218exch pop12191220*/122112221223static1224void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)1225{122612271228if (lIsAbsolute) {12291230// For absolute colorimetric intent, encode back to relative1231// and generate a relative Pipeline12321233// Relative encoding is obtained across XYZpcs*(D50/WhitePoint)12341235cmsCIEXYZ White;12361237_cmsReadMediaWhitePoint(&White, hProfile);12381239_cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");1240_cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");12411242_cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"1243"/TransformPQR [\n"1244"{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"1245"{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"1246"{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",1247White.X, White.Y, White.Z);1248return;1249}125012511252_cmsIOPrintf(m,"%% Bradford Cone Space\n"1253"/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");12541255_cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");125612571258// No BPC12591260if (!DoBPC) {12611262_cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"1263"/TransformPQR [\n"1264"{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"1265"{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"1266"{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");1267} else {12681269// BPC12701271_cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"1272"/TransformPQR [\n");12731274_cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "1275"2 index 3 get 2 index 3 get sub mul "1276"2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "1277"3 index 3 get 3 index 3 get exch sub div "1278"exch pop exch pop exch pop exch pop } bind\n");12791280_cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "1281"2 index 4 get 2 index 4 get sub mul "1282"2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "1283"3 index 4 get 3 index 4 get exch sub div "1284"exch pop exch pop exch pop exch pop } bind\n");12851286_cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "1287"2 index 5 get 2 index 5 get sub mul "1288"2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "1289"3 index 5 get 3 index 5 get exch sub div "1290"exch pop exch pop exch pop exch pop } bind\n]\n");12911292}1293}129412951296static1297void EmitXYZ2Lab(cmsIOHANDLER* m)1298{1299_cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");1300_cmsIOPrintf(m, "/EncodeLMN [\n");1301_cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");1302_cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");1303_cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");1304_cmsIOPrintf(m, "]\n");1305_cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");1306_cmsIOPrintf(m, "/EncodeABC [\n");130713081309_cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n");1310_cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n");1311_cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n");131213131314_cmsIOPrintf(m, "]\n");131513161317}13181319// Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces1320// I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted1321// space on 3D CLUT, but since space seems not to be a problem here, 33 points1322// would give a reasonable accuracy. Note also that CRD tables must operate in1323// 8 bits.13241325static1326int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)1327{1328cmsHPROFILE hLab;1329cmsHTRANSFORM xform;1330cmsUInt32Number i, nChannels;1331cmsUInt32Number OutputFormat;1332_cmsTRANSFORM* v;1333cmsPipeline* DeviceLink;1334cmsHPROFILE Profiles[3];1335cmsCIEXYZ BlackPointAdaptedToD50;1336cmsBool lDoBPC = (cmsBool) (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);1337cmsBool lFixWhite = (cmsBool) !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);1338cmsUInt32Number InFrm = TYPE_Lab_16;1339cmsUInt32Number RelativeEncodingIntent;1340cmsColorSpaceSignature ColorSpace;134113421343hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);1344if (hLab == NULL) return 0;13451346OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);1347nChannels = T_CHANNELS(OutputFormat);13481349ColorSpace = cmsGetColorSpace(hProfile);13501351// For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.13521353RelativeEncodingIntent = Intent;1354if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)1355RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;135613571358// Use V4 Lab always1359Profiles[0] = hLab;1360Profiles[1] = hProfile;13611362xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,1363Profiles, 2, TYPE_Lab_DBL,1364OutputFormat, RelativeEncodingIntent, 0);1365cmsCloseProfile(hLab);13661367if (xform == NULL) {13681369cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");1370return 0;1371}13721373// Get a copy of the internal devicelink1374v = (_cmsTRANSFORM*) xform;1375DeviceLink = cmsPipelineDup(v ->Lut);1376if (DeviceLink == NULL) return 0;137713781379// We need a CLUT1380dwFlags |= cmsFLAGS_FORCE_CLUT;1381_cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);13821383_cmsIOPrintf(m, "<<\n");1384_cmsIOPrintf(m, "/ColorRenderingType 1\n");138513861387cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);13881389// Emit headers, etc.1390EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);1391EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);1392EmitXYZ2Lab(m);139313941395// FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab1396// does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,1397// zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to1398// zero. This would sacrifice a bit of highlights, but failure to do so would cause1399// scum dot. Ouch.14001401if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)1402lFixWhite = FALSE;14031404_cmsIOPrintf(m, "/RenderTable ");140514061407WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);14081409_cmsIOPrintf(m, " %d {} bind ", nChannels);14101411for (i=1; i < nChannels; i++)1412_cmsIOPrintf(m, "dup ");14131414_cmsIOPrintf(m, "]\n");141514161417EmitIntent(m, Intent);14181419_cmsIOPrintf(m, ">>\n");14201421if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {14221423_cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");1424}14251426cmsPipelineFree(DeviceLink);1427cmsDeleteTransform(xform);14281429return 1;1430}143114321433// Builds a ASCII string containing colorant list in 0..1.0 range1434static1435void BuildColorantList(char *Colorant, cmsUInt32Number nColorant, cmsUInt16Number Out[])1436{1437char Buff[32];1438cmsUInt32Number j;14391440Colorant[0] = 0;1441if (nColorant > cmsMAXCHANNELS)1442nColorant = cmsMAXCHANNELS;14431444for (j = 0; j < nColorant; j++) {14451446snprintf(Buff, 31, "%.3f", Out[j] / 65535.0);1447Buff[31] = 0;1448strcat(Colorant, Buff);1449if (j < nColorant - 1)1450strcat(Colorant, " ");14511452}1453}145414551456// Creates a PostScript color list from a named profile data.1457// This is a HP extension.14581459static1460int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent, cmsUInt32Number dwFlags)1461{1462cmsHTRANSFORM xform;1463cmsUInt32Number i, nColors, nColorant;1464cmsUInt32Number OutputFormat;1465char ColorName[cmsMAX_PATH];1466char Colorant[512];1467cmsNAMEDCOLORLIST* NamedColorList;146814691470OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);1471nColorant = T_CHANNELS(OutputFormat);147214731474xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);1475if (xform == NULL) return 0;147614771478NamedColorList = cmsGetNamedColorList(xform);1479if (NamedColorList == NULL) return 0;14801481_cmsIOPrintf(m, "<<\n");1482_cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");1483_cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");1484_cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");14851486nColors = cmsNamedColorCount(NamedColorList);14871488for (i=0; i < nColors; i++) {14891490cmsUInt16Number In[1];1491cmsUInt16Number Out[cmsMAXCHANNELS];14921493In[0] = (cmsUInt16Number) i;14941495if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))1496continue;14971498cmsDoTransform(xform, In, Out, 1);1499BuildColorantList(Colorant, nColorant, Out);1500_cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant);1501}15021503_cmsIOPrintf(m, " >>");15041505if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {15061507_cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");1508}15091510cmsDeleteTransform(xform);1511return 1;1512}1513151415151516// This one does create a Color Rendering Dictionary.1517// CRD are always LUT-Based, no matter if profile is1518// implemented as matrix-shaper.15191520static1521cmsUInt32Number GenerateCRD(cmsContext ContextID,1522cmsHPROFILE hProfile,1523cmsUInt32Number Intent, cmsUInt32Number dwFlags,1524cmsIOHANDLER* mem)1525{1526cmsUInt32Number dwBytesUsed;15271528if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {15291530EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);1531}153215331534// Is a named color profile?1535if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {15361537if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {1538return 0;1539}1540}1541else {15421543// CRD are always implemented as LUT15441545if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {1546return 0;1547}1548}15491550if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {15511552_cmsIOPrintf(mem, "%%%%EndResource\n");1553_cmsIOPrintf(mem, "\n%% CRD End\n");1554}15551556// Done, keep memory usage1557dwBytesUsed = mem ->UsedSpace;15581559// Finally, return used byte count1560return dwBytesUsed;15611562cmsUNUSED_PARAMETER(ContextID);1563}15641565156615671568cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,1569cmsPSResourceType Type,1570cmsHPROFILE hProfile,1571cmsUInt32Number Intent,1572cmsUInt32Number dwFlags,1573cmsIOHANDLER* io)1574{1575cmsUInt32Number rc;157615771578switch (Type) {15791580case cmsPS_RESOURCE_CSA:1581rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);1582break;15831584default:1585case cmsPS_RESOURCE_CRD:1586rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);1587break;1588}15891590return rc;1591}1592159315941595cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,1596cmsHPROFILE hProfile,1597cmsUInt32Number Intent, cmsUInt32Number dwFlags,1598void* Buffer, cmsUInt32Number dwBufferLen)1599{1600cmsIOHANDLER* mem;1601cmsUInt32Number dwBytesUsed;16021603// Set up the serialization engine1604if (Buffer == NULL)1605mem = cmsOpenIOhandlerFromNULL(ContextID);1606else1607mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");16081609if (!mem) return 0;16101611dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);16121613// Get rid of memory stream1614cmsCloseIOhandler(mem);16151616return dwBytesUsed;1617}1618161916201621// Does create a Color Space Array on XYZ colorspace for PostScript usage1622cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,1623cmsHPROFILE hProfile,1624cmsUInt32Number Intent,1625cmsUInt32Number dwFlags,1626void* Buffer,1627cmsUInt32Number dwBufferLen)1628{1629cmsIOHANDLER* mem;1630cmsUInt32Number dwBytesUsed;16311632if (Buffer == NULL)1633mem = cmsOpenIOhandlerFromNULL(ContextID);1634else1635mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");16361637if (!mem) return 0;16381639dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);16401641// Get rid of memory stream1642cmsCloseIOhandler(mem);16431644return dwBytesUsed;16451646}164716481649