Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/native/sun/java2d/cmm/lcms/cmsps2.c
38918 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 nEntries)561{562return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0;563}564565566// Does write a set of gamma curves567568static569void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[], const char* nameprefix)570{571cmsUInt32Number i;572static char buffer[2048];573574for( i=0; i < n; i++ )575{576if (g[i] == NULL) return; // Error577578if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) {579580_cmsIOPrintf(m, "/%s%d /%s%d load def\n", nameprefix, i, nameprefix, i-1);581}582else {583snprintf(buffer, sizeof(buffer), "%s%d", nameprefix, i);584buffer[sizeof(buffer)-1] = '\0';585Emit1Gamma(m, g[i], buffer);586}587}588589}590591592// Following code dumps a LUT onto memory stream593594595// This is the sampler. Intended to work in SAMPLER_INSPECT mode,596// that is, the callback will be called for each knot with597//598// In[] The grid location coordinates, normalized to 0..ffff599// Out[] The Pipeline values, normalized to 0..ffff600//601// Returning a value other than 0 does terminate the sampling process602//603// Each row contains Pipeline values for all but first component. So, I604// detect row changing by keeping a copy of last value of first605// component. -1 is used to mark beginning of whole block.606607static608int OutputValueSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)609{610cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;611cmsUInt32Number i;612613614if (sc -> FixWhite) {615616if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8]617618if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&619(In[2] >= 0x7800 && In[2] <= 0x8800)) {620621cmsUInt16Number* Black;622cmsUInt16Number* White;623cmsUInt32Number nOutputs;624625if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))626return 0;627628for (i=0; i < nOutputs; i++)629Out[i] = White[i];630}631632633}634}635636637// Hadle the parenthesis on rows638639if (In[0] != sc ->FirstComponent) {640641if (sc ->FirstComponent != -1) {642643_cmsIOPrintf(sc ->m, sc ->PostMin);644sc ->SecondComponent = -1;645_cmsIOPrintf(sc ->m, sc ->PostMaj);646}647648// Begin block649_cmsPSActualColumn = 0;650651_cmsIOPrintf(sc ->m, sc ->PreMaj);652sc ->FirstComponent = In[0];653}654655656if (In[1] != sc ->SecondComponent) {657658if (sc ->SecondComponent != -1) {659660_cmsIOPrintf(sc ->m, sc ->PostMin);661}662663_cmsIOPrintf(sc ->m, sc ->PreMin);664sc ->SecondComponent = In[1];665}666667// Dump table.668669for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {670671cmsUInt16Number wWordOut = Out[i];672cmsUInt8Number wByteOut; // Value as byte673674675// We always deal with Lab4676677wByteOut = Word2Byte(wWordOut);678WriteByte(sc -> m, wByteOut);679}680681return 1;682}683684// Writes a Pipeline on memstream. Could be 8 or 16 bits based685686static687void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,688const char* PostMaj,689const char* PreMin,690const char* PostMin,691int FixWhite,692cmsColorSpaceSignature ColorSpace)693{694cmsUInt32Number i;695cmsPsSamplerCargo sc;696697sc.FirstComponent = -1;698sc.SecondComponent = -1;699sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;700sc.m = m;701sc.PreMaj = PreMaj;702sc.PostMaj= PostMaj;703704sc.PreMin = PreMin;705sc.PostMin = PostMin;706sc.FixWhite = FixWhite;707sc.ColorSpace = ColorSpace;708709_cmsIOPrintf(m, "[");710711for (i=0; i < sc.Pipeline->Params->nInputs; i++)712_cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);713714_cmsIOPrintf(m, " [\n");715716cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT);717718_cmsIOPrintf(m, PostMin);719_cmsIOPrintf(m, PostMaj);720_cmsIOPrintf(m, "] ");721722}723724725// Dumps CIEBasedA Color Space Array726727static728int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)729{730731_cmsIOPrintf(m, "[ /CIEBasedA\n");732_cmsIOPrintf(m, " <<\n");733734EmitSafeGuardBegin(m, "lcms2gammaproc");735Emit1Gamma(m, Curve, "lcms2gammaproc");736737_cmsIOPrintf(m, "/DecodeA /lcms2gammaproc load\n");738EmitSafeGuardEnd(m, "lcms2gammaproc", 3);739740_cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");741_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");742743EmitWhiteBlackD50(m, BlackPoint);744EmitIntent(m, INTENT_PERCEPTUAL);745746_cmsIOPrintf(m, ">>\n");747_cmsIOPrintf(m, "]\n");748749return 1;750}751752753// Dumps CIEBasedABC Color Space Array754755static756int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)757{758int i;759760_cmsIOPrintf(m, "[ /CIEBasedABC\n");761_cmsIOPrintf(m, "<<\n");762763EmitSafeGuardBegin(m, "lcms2gammaproc0");764EmitSafeGuardBegin(m, "lcms2gammaproc1");765EmitSafeGuardBegin(m, "lcms2gammaproc2");766EmitNGamma(m, 3, CurveSet, "lcms2gammaproc");767_cmsIOPrintf(m, "/DecodeABC [\n");768_cmsIOPrintf(m, " /lcms2gammaproc0 load\n");769_cmsIOPrintf(m, " /lcms2gammaproc1 load\n");770_cmsIOPrintf(m, " /lcms2gammaproc2 load\n");771_cmsIOPrintf(m, "]\n");772EmitSafeGuardEnd(m, "lcms2gammaproc2", 3);773EmitSafeGuardEnd(m, "lcms2gammaproc1", 3);774EmitSafeGuardEnd(m, "lcms2gammaproc0", 3);775776_cmsIOPrintf(m, "/MatrixABC [ " );777778for( i=0; i < 3; i++ ) {779780_cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],781Matrix[i + 3*1],782Matrix[i + 3*2]);783}784785786_cmsIOPrintf(m, "]\n");787788_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");789790EmitWhiteBlackD50(m, BlackPoint);791EmitIntent(m, INTENT_PERCEPTUAL);792793_cmsIOPrintf(m, ">>\n");794_cmsIOPrintf(m, "]\n");795796797return 1;798}799800801static802int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint)803{804const char* PreMaj;805const char* PostMaj;806const char* PreMin, * PostMin;807cmsStage* mpe;808int i, numchans;809static char buffer[2048];810811mpe = Pipeline->Elements;812813switch (cmsStageInputChannels(mpe)) {814case 3:815_cmsIOPrintf(m, "[ /CIEBasedDEF\n");816PreMaj = "<";817PostMaj = ">\n";818PreMin = PostMin = "";819break;820821case 4:822_cmsIOPrintf(m, "[ /CIEBasedDEFG\n");823PreMaj = "[";824PostMaj = "]\n";825PreMin = "<";826PostMin = ">\n";827break;828829default:830return 0;831832}833834_cmsIOPrintf(m, "<<\n");835836if (cmsStageType(mpe) == cmsSigCurveSetElemType) {837838numchans = cmsStageOutputChannels(mpe);839for (i = 0; i < numchans; ++i) {840snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);841buffer[sizeof(buffer) - 1] = '\0';842EmitSafeGuardBegin(m, buffer);843}844EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe), "lcms2gammaproc");845_cmsIOPrintf(m, "/DecodeDEF [\n");846for (i = 0; i < numchans; ++i) {847snprintf(buffer, sizeof(buffer), " /lcms2gammaproc%d load\n", i);848buffer[sizeof(buffer) - 1] = '\0';849_cmsIOPrintf(m, buffer);850}851_cmsIOPrintf(m, "]\n");852for (i = numchans - 1; i >= 0; --i) {853snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);854buffer[sizeof(buffer) - 1] = '\0';855EmitSafeGuardEnd(m, buffer, 3);856}857858mpe = mpe->Next;859}860861if (cmsStageType(mpe) == cmsSigCLutElemType) {862863_cmsIOPrintf(m, "/Table ");864WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature)0);865_cmsIOPrintf(m, "]\n");866}867868EmitLab2XYZ(m);869EmitWhiteBlackD50(m, BlackPoint);870EmitIntent(m, Intent);871872_cmsIOPrintf(m, " >>\n");873_cmsIOPrintf(m, "]\n");874875return 1;876}877878// Generates a curve from a gray profile879880static881cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent)882{883cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);884cmsHPROFILE hXYZ = cmsCreateXYZProfile();885cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);886int i;887888if (Out != NULL && xform != NULL) {889for (i=0; i < 256; i++) {890891cmsUInt8Number Gray = (cmsUInt8Number) i;892cmsCIEXYZ XYZ;893894cmsDoTransform(xform, &Gray, &XYZ, 1);895896Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);897}898}899900if (xform) cmsDeleteTransform(xform);901if (hXYZ) cmsCloseProfile(hXYZ);902return Out;903}904905906907// Because PostScript has only 8 bits in /Table, we should use908// a more perceptually uniform space... I do choose Lab.909910static911int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)912{913cmsHPROFILE hLab;914cmsHTRANSFORM xform;915cmsUInt32Number nChannels;916cmsUInt32Number InputFormat;917int rc;918cmsHPROFILE Profiles[2];919cmsCIEXYZ BlackPointAdaptedToD50;920921// Does create a device-link based transform.922// The DeviceLink is next dumped as working CSA.923924InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);925nChannels = T_CHANNELS(InputFormat);926927928cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);929930// Adjust output to Lab4931hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);932933Profiles[0] = hProfile;934Profiles[1] = hLab;935936xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);937cmsCloseProfile(hLab);938939if (xform == NULL) {940941cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");942return 0;943}944945// Only 1, 3 and 4 channels are allowed946947switch (nChannels) {948949case 1: {950cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);951EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);952cmsFreeToneCurve(Gray2Y);953}954break;955956case 3:957case 4: {958cmsUInt32Number OutFrm = TYPE_Lab_16;959cmsPipeline* DeviceLink;960_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;961962DeviceLink = cmsPipelineDup(v ->Lut);963if (DeviceLink == NULL) return 0;964965dwFlags |= cmsFLAGS_FORCE_CLUT;966_cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);967968rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);969cmsPipelineFree(DeviceLink);970if (rc == 0) return 0;971}972break;973974default:975976cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels are supported for CSA. This profile has %d channels.", nChannels);977return 0;978}979980981cmsDeleteTransform(xform);982983return 1;984}985986static987cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)988{989_cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;990991return Data -> Double;992}993994995// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based996static997int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)998{999cmsColorSpaceSignature ColorSpace;1000int rc;1001cmsCIEXYZ BlackPointAdaptedToD50;10021003ColorSpace = cmsGetColorSpace(hProfile);10041005cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);10061007if (ColorSpace == cmsSigGrayData) {10081009cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);1010rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);10111012}1013else1014if (ColorSpace == cmsSigRgbData) {10151016cmsMAT3 Mat;1017int i, j;10181019memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));10201021for (i = 0; i < 3; i++)1022for (j = 0; j < 3; j++)1023Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;10241025rc = EmitCIEBasedABC(m, (cmsFloat64Number *)&Mat,1026_cmsStageGetPtrToCurveSet(Shaper),1027&BlackPointAdaptedToD50);1028}1029else {10301031cmsSignalError(m->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");1032return 0;1033}10341035return rc;1036}1037103810391040// Creates a PostScript color list from a named profile data.1041// This is a HP extension, and it works in Lab instead of XYZ10421043static1044int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent)1045{1046cmsHTRANSFORM xform;1047cmsHPROFILE hLab;1048cmsUInt32Number i, nColors;1049char ColorName[cmsMAX_PATH];1050cmsNAMEDCOLORLIST* NamedColorList;10511052hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);1053xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);1054if (xform == NULL) return 0;10551056NamedColorList = cmsGetNamedColorList(xform);1057if (NamedColorList == NULL) return 0;10581059_cmsIOPrintf(m, "<<\n");1060_cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");1061_cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");1062_cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");10631064nColors = cmsNamedColorCount(NamedColorList);106510661067for (i=0; i < nColors; i++) {10681069cmsUInt16Number In[1];1070cmsCIELab Lab;10711072In[0] = (cmsUInt16Number) i;10731074if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))1075continue;10761077cmsDoTransform(xform, In, &Lab, 1);1078_cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);1079}1080108110821083_cmsIOPrintf(m, ">>\n");10841085cmsDeleteTransform(xform);1086cmsCloseProfile(hLab);1087return 1;1088}108910901091// Does create a Color Space Array on XYZ colorspace for PostScript usage1092static1093cmsUInt32Number GenerateCSA(cmsContext ContextID,1094cmsHPROFILE hProfile,1095cmsUInt32Number Intent,1096cmsUInt32Number dwFlags,1097cmsIOHANDLER* mem)1098{1099cmsUInt32Number dwBytesUsed;1100cmsPipeline* lut = NULL;1101cmsStage* Matrix, *Shaper;110211031104// Is a named color profile?1105if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {11061107if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;1108}1109else {111011111112// Any profile class are allowed (including devicelink), but1113// output (PCS) colorspace must be XYZ or Lab1114cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);11151116if (ColorSpace != cmsSigXYZData &&1117ColorSpace != cmsSigLabData) {11181119cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");1120goto Error;1121}112211231124// Read the lut with all necessary conversion stages1125lut = _cmsReadInputLUT(hProfile, Intent);1126if (lut == NULL) goto Error;112711281129// Tone curves + matrix can be implemented without any LUT1130if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {11311132if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;11331134}1135else {1136// We need a LUT for the rest1137if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;1138}1139}114011411142// Done, keep memory usage1143dwBytesUsed = mem ->UsedSpace;11441145// Get rid of LUT1146if (lut != NULL) cmsPipelineFree(lut);11471148// Finally, return used byte count1149return dwBytesUsed;11501151Error:1152if (lut != NULL) cmsPipelineFree(lut);1153return 0;1154}11551156// ------------------------------------------------------ Color Rendering Dictionary (CRD)1157115811591160/*11611162Black point compensation plus chromatic adaptation:11631164Step 1 - Chromatic adaptation1165=============================11661167WPout1168X = ------- PQR1169Wpin11701171Step 2 - Black point compensation1172=================================11731174(WPout - BPout)*X - WPout*(BPin - BPout)1175out = ---------------------------------------1176WPout - BPin117711781179Algorithm discussion1180====================11811182TransformPQR(WPin, BPin, WPout, BPout, PQR)11831184Wpin,etc= { Xws Yws Zws Pws Qws Rws }118511861187Algorithm Stack 0...n1188===========================================================1189PQR BPout WPout BPin WPin11904 index 3 get WPin PQR BPout WPout BPin WPin1191div (PQR/WPin) BPout WPout BPin WPin11922 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin1193mult WPout*(PQR/WPin) BPout WPout BPin WPin119411952 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin11962 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin1197sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin1198mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin119912002 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin12014 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin12023 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin12031204sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin1205mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin1206sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin120712083 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin12093 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin1210exch1211sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin1212div12131214exch pop1215exch pop1216exch pop1217exch pop12181219*/122012211222static1223void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)1224{122512261227if (lIsAbsolute) {12281229// For absolute colorimetric intent, encode back to relative1230// and generate a relative Pipeline12311232// Relative encoding is obtained across XYZpcs*(D50/WhitePoint)12331234cmsCIEXYZ White;12351236_cmsReadMediaWhitePoint(&White, hProfile);12371238_cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");1239_cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");12401241_cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"1242"/TransformPQR [\n"1243"{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"1244"{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"1245"{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",1246White.X, White.Y, White.Z);1247return;1248}124912501251_cmsIOPrintf(m,"%% Bradford Cone Space\n"1252"/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");12531254_cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");125512561257// No BPC12581259if (!DoBPC) {12601261_cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"1262"/TransformPQR [\n"1263"{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"1264"{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"1265"{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");1266} else {12671268// BPC12691270_cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"1271"/TransformPQR [\n");12721273_cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "1274"2 index 3 get 2 index 3 get sub mul "1275"2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "1276"3 index 3 get 3 index 3 get exch sub div "1277"exch pop exch pop exch pop exch pop } bind\n");12781279_cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "1280"2 index 4 get 2 index 4 get sub mul "1281"2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "1282"3 index 4 get 3 index 4 get exch sub div "1283"exch pop exch pop exch pop exch pop } bind\n");12841285_cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "1286"2 index 5 get 2 index 5 get sub mul "1287"2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "1288"3 index 5 get 3 index 5 get exch sub div "1289"exch pop exch pop exch pop exch pop } bind\n]\n");12901291}1292}129312941295static1296void EmitXYZ2Lab(cmsIOHANDLER* m)1297{1298_cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");1299_cmsIOPrintf(m, "/EncodeLMN [\n");1300_cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");1301_cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");1302_cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");1303_cmsIOPrintf(m, "]\n");1304_cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");1305_cmsIOPrintf(m, "/EncodeABC [\n");130613071308_cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n");1309_cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n");1310_cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n");131113121313_cmsIOPrintf(m, "]\n");131413151316}13171318// Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces1319// I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted1320// space on 3D CLUT, but since space seems not to be a problem here, 33 points1321// would give a reasonable accuracy. Note also that CRD tables must operate in1322// 8 bits.13231324static1325int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)1326{1327cmsHPROFILE hLab;1328cmsHTRANSFORM xform;1329cmsUInt32Number i, nChannels;1330cmsUInt32Number OutputFormat;1331_cmsTRANSFORM* v;1332cmsPipeline* DeviceLink;1333cmsHPROFILE Profiles[3];1334cmsCIEXYZ BlackPointAdaptedToD50;1335cmsBool lDoBPC = (cmsBool) (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);1336cmsBool lFixWhite = (cmsBool) !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);1337cmsUInt32Number InFrm = TYPE_Lab_16;1338cmsUInt32Number RelativeEncodingIntent;1339cmsColorSpaceSignature ColorSpace;134013411342hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);1343if (hLab == NULL) return 0;13441345OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);1346nChannels = T_CHANNELS(OutputFormat);13471348ColorSpace = cmsGetColorSpace(hProfile);13491350// For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.13511352RelativeEncodingIntent = Intent;1353if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)1354RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;135513561357// Use V4 Lab always1358Profiles[0] = hLab;1359Profiles[1] = hProfile;13601361xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,1362Profiles, 2, TYPE_Lab_DBL,1363OutputFormat, RelativeEncodingIntent, 0);1364cmsCloseProfile(hLab);13651366if (xform == NULL) {13671368cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");1369return 0;1370}13711372// Get a copy of the internal devicelink1373v = (_cmsTRANSFORM*) xform;1374DeviceLink = cmsPipelineDup(v ->Lut);1375if (DeviceLink == NULL) return 0;137613771378// We need a CLUT1379dwFlags |= cmsFLAGS_FORCE_CLUT;1380_cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);13811382_cmsIOPrintf(m, "<<\n");1383_cmsIOPrintf(m, "/ColorRenderingType 1\n");138413851386cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);13871388// Emit headers, etc.1389EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);1390EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);1391EmitXYZ2Lab(m);139213931394// FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab1395// does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,1396// zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to1397// zero. This would sacrifice a bit of highlights, but failure to do so would cause1398// scum dot. Ouch.13991400if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)1401lFixWhite = FALSE;14021403_cmsIOPrintf(m, "/RenderTable ");140414051406WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);14071408_cmsIOPrintf(m, " %d {} bind ", nChannels);14091410for (i=1; i < nChannels; i++)1411_cmsIOPrintf(m, "dup ");14121413_cmsIOPrintf(m, "]\n");141414151416EmitIntent(m, Intent);14171418_cmsIOPrintf(m, ">>\n");14191420if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {14211422_cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");1423}14241425cmsPipelineFree(DeviceLink);1426cmsDeleteTransform(xform);14271428return 1;1429}143014311432// Builds a ASCII string containing colorant list in 0..1.0 range1433static1434void BuildColorantList(char *Colorant, cmsUInt32Number nColorant, cmsUInt16Number Out[])1435{1436char Buff[32];1437cmsUInt32Number j;14381439Colorant[0] = 0;1440if (nColorant > cmsMAXCHANNELS)1441nColorant = cmsMAXCHANNELS;14421443for (j = 0; j < nColorant; j++) {14441445snprintf(Buff, 31, "%.3f", Out[j] / 65535.0);1446Buff[31] = 0;1447strcat(Colorant, Buff);1448if (j < nColorant - 1)1449strcat(Colorant, " ");14501451}1452}145314541455// Creates a PostScript color list from a named profile data.1456// This is a HP extension.14571458static1459int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent, cmsUInt32Number dwFlags)1460{1461cmsHTRANSFORM xform;1462cmsUInt32Number i, nColors, nColorant;1463cmsUInt32Number OutputFormat;1464char ColorName[cmsMAX_PATH];1465char Colorant[512];1466cmsNAMEDCOLORLIST* NamedColorList;146714681469OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);1470nColorant = T_CHANNELS(OutputFormat);147114721473xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);1474if (xform == NULL) return 0;147514761477NamedColorList = cmsGetNamedColorList(xform);1478if (NamedColorList == NULL) return 0;14791480_cmsIOPrintf(m, "<<\n");1481_cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");1482_cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");1483_cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");14841485nColors = cmsNamedColorCount(NamedColorList);14861487for (i=0; i < nColors; i++) {14881489cmsUInt16Number In[1];1490cmsUInt16Number Out[cmsMAXCHANNELS];14911492In[0] = (cmsUInt16Number) i;14931494if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))1495continue;14961497cmsDoTransform(xform, In, Out, 1);1498BuildColorantList(Colorant, nColorant, Out);1499_cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant);1500}15011502_cmsIOPrintf(m, " >>");15031504if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {15051506_cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");1507}15081509cmsDeleteTransform(xform);1510return 1;1511}1512151315141515// This one does create a Color Rendering Dictionary.1516// CRD are always LUT-Based, no matter if profile is1517// implemented as matrix-shaper.15181519static1520cmsUInt32Number GenerateCRD(cmsContext ContextID,1521cmsHPROFILE hProfile,1522cmsUInt32Number Intent, cmsUInt32Number dwFlags,1523cmsIOHANDLER* mem)1524{1525cmsUInt32Number dwBytesUsed;15261527if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {15281529EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);1530}153115321533// Is a named color profile?1534if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {15351536if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {1537return 0;1538}1539}1540else {15411542// CRD are always implemented as LUT15431544if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {1545return 0;1546}1547}15481549if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {15501551_cmsIOPrintf(mem, "%%%%EndResource\n");1552_cmsIOPrintf(mem, "\n%% CRD End\n");1553}15541555// Done, keep memory usage1556dwBytesUsed = mem ->UsedSpace;15571558// Finally, return used byte count1559return dwBytesUsed;15601561cmsUNUSED_PARAMETER(ContextID);1562}15631564156515661567cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,1568cmsPSResourceType Type,1569cmsHPROFILE hProfile,1570cmsUInt32Number Intent,1571cmsUInt32Number dwFlags,1572cmsIOHANDLER* io)1573{1574cmsUInt32Number rc;157515761577switch (Type) {15781579case cmsPS_RESOURCE_CSA:1580rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);1581break;15821583default:1584case cmsPS_RESOURCE_CRD:1585rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);1586break;1587}15881589return rc;1590}1591159215931594cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,1595cmsHPROFILE hProfile,1596cmsUInt32Number Intent, cmsUInt32Number dwFlags,1597void* Buffer, cmsUInt32Number dwBufferLen)1598{1599cmsIOHANDLER* mem;1600cmsUInt32Number dwBytesUsed;16011602// Set up the serialization engine1603if (Buffer == NULL)1604mem = cmsOpenIOhandlerFromNULL(ContextID);1605else1606mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");16071608if (!mem) return 0;16091610dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);16111612// Get rid of memory stream1613cmsCloseIOhandler(mem);16141615return dwBytesUsed;1616}1617161816191620// Does create a Color Space Array on XYZ colorspace for PostScript usage1621cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,1622cmsHPROFILE hProfile,1623cmsUInt32Number Intent,1624cmsUInt32Number dwFlags,1625void* Buffer,1626cmsUInt32Number dwBufferLen)1627{1628cmsIOHANDLER* mem;1629cmsUInt32Number dwBytesUsed;16301631if (Buffer == NULL)1632mem = cmsOpenIOhandlerFromNULL(ContextID);1633else1634mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");16351636if (!mem) return 0;16371638dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);16391640// Get rid of memory stream1641cmsCloseIOhandler(mem);16421643return dwBytesUsed;16441645}164616471648