Path: blob/master/src/java.desktop/share/native/liblcms/cmsvirt.c
41149 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// Virtual (built-in) profiles58// -----------------------------------------------------------------------------------5960static61cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description)62{63cmsMLU *DescriptionMLU, *CopyrightMLU;64cmsBool rc = FALSE;65cmsContext ContextID = cmsGetProfileContextID(hProfile);6667DescriptionMLU = cmsMLUalloc(ContextID, 1);68CopyrightMLU = cmsMLUalloc(ContextID, 1);6970if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;7172if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error;73if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error;7475if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error;76if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error;7778rc = TRUE;7980Error:8182if (DescriptionMLU)83cmsMLUfree(DescriptionMLU);84if (CopyrightMLU)85cmsMLUfree(CopyrightMLU);86return rc;87}888990static91cmsBool SetSeqDescTag(cmsHPROFILE hProfile, const char* Model)92{93cmsBool rc = FALSE;94cmsContext ContextID = cmsGetProfileContextID(hProfile);95cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1);9697if (Seq == NULL) return FALSE;9899Seq->seq[0].deviceMfg = (cmsSignature) 0;100Seq->seq[0].deviceModel = (cmsSignature) 0;101102#ifdef CMS_DONT_USE_INT64103Seq->seq[0].attributes[0] = 0;104Seq->seq[0].attributes[1] = 0;105#else106Seq->seq[0].attributes = 0;107#endif108109Seq->seq[0].technology = (cmsTechnologySignature) 0;110111cmsMLUsetASCII( Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS");112cmsMLUsetASCII( Seq->seq[0].Model, cmsNoLanguage, cmsNoCountry, Model);113114if (!_cmsWriteProfileSequence(hProfile, Seq)) goto Error;115116rc = TRUE;117118Error:119if (Seq)120cmsFreeProfileSequenceDescription(Seq);121122return rc;123}124125126127// This function creates a profile based on White point, primaries and128// transfer functions.129cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID,130const cmsCIExyY* WhitePoint,131const cmsCIExyYTRIPLE* Primaries,132cmsToneCurve* const TransferFunction[3])133{134cmsHPROFILE hICC;135cmsMAT3 MColorants;136cmsCIEXYZTRIPLE Colorants;137cmsCIExyY MaxWhite;138cmsMAT3 CHAD;139cmsCIEXYZ WhitePointXYZ;140141hICC = cmsCreateProfilePlaceholder(ContextID);142if (!hICC) // can't allocate143return NULL;144145cmsSetProfileVersion(hICC, 4.3);146147cmsSetDeviceClass(hICC, cmsSigDisplayClass);148cmsSetColorSpace(hICC, cmsSigRgbData);149cmsSetPCS(hICC, cmsSigXYZData);150151cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);152153154// Implement profile using following tags:155//156// 1 cmsSigProfileDescriptionTag157// 2 cmsSigMediaWhitePointTag158// 3 cmsSigRedColorantTag159// 4 cmsSigGreenColorantTag160// 5 cmsSigBlueColorantTag161// 6 cmsSigRedTRCTag162// 7 cmsSigGreenTRCTag163// 8 cmsSigBlueTRCTag164// 9 Chromatic adaptation Tag165// This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II)166// 10 cmsSigChromaticityTag167168169if (!SetTextTags(hICC, L"RGB built-in")) goto Error;170171if (WhitePoint) {172173if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;174175cmsxyY2XYZ(&WhitePointXYZ, WhitePoint);176_cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ());177178// This is a V4 tag, but many CMM does read and understand it no matter which version179if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error;180}181182if (WhitePoint && Primaries) {183184MaxWhite.x = WhitePoint -> x;185MaxWhite.y = WhitePoint -> y;186MaxWhite.Y = 1.0;187188if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error;189190Colorants.Red.X = MColorants.v[0].n[0];191Colorants.Red.Y = MColorants.v[1].n[0];192Colorants.Red.Z = MColorants.v[2].n[0];193194Colorants.Green.X = MColorants.v[0].n[1];195Colorants.Green.Y = MColorants.v[1].n[1];196Colorants.Green.Z = MColorants.v[2].n[1];197198Colorants.Blue.X = MColorants.v[0].n[2];199Colorants.Blue.Y = MColorants.v[1].n[2];200Colorants.Blue.Z = MColorants.v[2].n[2];201202if (!cmsWriteTag(hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error;203if (!cmsWriteTag(hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error;204if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error;205}206207208if (TransferFunction) {209210// Tries to minimize space. Thanks to Richard Hughes for this nice idea211if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error;212213if (TransferFunction[1] == TransferFunction[0]) {214215if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;216217} else {218219if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;220}221222if (TransferFunction[2] == TransferFunction[0]) {223224if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;225226} else {227228if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;229}230}231232if (Primaries) {233if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error;234}235236237return hICC;238239Error:240if (hICC)241cmsCloseProfile(hICC);242return NULL;243}244245cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint,246const cmsCIExyYTRIPLE* Primaries,247cmsToneCurve* const TransferFunction[3])248{249return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction);250}251252253254// This function creates a profile based on White point and transfer function.255cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID,256const cmsCIExyY* WhitePoint,257const cmsToneCurve* TransferFunction)258{259cmsHPROFILE hICC;260cmsCIEXYZ tmp;261262hICC = cmsCreateProfilePlaceholder(ContextID);263if (!hICC) // can't allocate264return NULL;265266cmsSetProfileVersion(hICC, 4.3);267268cmsSetDeviceClass(hICC, cmsSigDisplayClass);269cmsSetColorSpace(hICC, cmsSigGrayData);270cmsSetPCS(hICC, cmsSigXYZData);271cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);272273274// Implement profile using following tags:275//276// 1 cmsSigProfileDescriptionTag277// 2 cmsSigMediaWhitePointTag278// 3 cmsSigGrayTRCTag279280// This conforms a standard Gray DisplayProfile281282// Fill-in the tags283284if (!SetTextTags(hICC, L"gray built-in")) goto Error;285286287if (WhitePoint) {288289cmsxyY2XYZ(&tmp, WhitePoint);290if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error;291}292293if (TransferFunction) {294295if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error;296}297298return hICC;299300Error:301if (hICC)302cmsCloseProfile(hICC);303return NULL;304}305306307308cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint,309const cmsToneCurve* TransferFunction)310{311return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction);312}313314// This is a devicelink operating in the target colorspace with as many transfer functions as components315316cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,317cmsColorSpaceSignature ColorSpace,318cmsToneCurve* const TransferFunctions[])319{320cmsHPROFILE hICC;321cmsPipeline* Pipeline;322cmsUInt32Number nChannels;323324hICC = cmsCreateProfilePlaceholder(ContextID);325if (!hICC)326return NULL;327328cmsSetProfileVersion(hICC, 4.3);329330cmsSetDeviceClass(hICC, cmsSigLinkClass);331cmsSetColorSpace(hICC, ColorSpace);332cmsSetPCS(hICC, ColorSpace);333334cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);335336// Set up channels337nChannels = cmsChannelsOf(ColorSpace);338339// Creates a Pipeline with prelinearization step only340Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);341if (Pipeline == NULL) goto Error;342343344// Copy tables to Pipeline345if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))346goto Error;347348// Create tags349if (!SetTextTags(hICC, L"Linearization built-in")) goto Error;350if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error;351if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error;352353// Pipeline is already on virtual profile354cmsPipelineFree(Pipeline);355356// Ok, done357return hICC;358359Error:360cmsPipelineFree(Pipeline);361if (hICC)362cmsCloseProfile(hICC);363364365return NULL;366}367368cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace,369cmsToneCurve* const TransferFunctions[])370{371return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions);372}373374// Ink-limiting algorithm375//376// Sum = C + M + Y + K377// If Sum > InkLimit378// Ratio= 1 - (Sum - InkLimit) / (C + M + Y)379// if Ratio <0380// Ratio=0381// endif382// Else383// Ratio=1384// endif385//386// C = Ratio * C387// M = Ratio * M388// Y = Ratio * Y389// K: Does not change390391static392int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)393{394cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;395cmsFloat64Number SumCMY, SumCMYK, Ratio;396397InkLimit = (InkLimit * 655.35);398399SumCMY = In[0] + In[1] + In[2];400SumCMYK = SumCMY + In[3];401402if (SumCMYK > InkLimit) {403404Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);405if (Ratio < 0)406Ratio = 0;407}408else Ratio = 1;409410Out[0] = _cmsQuickSaturateWord(In[0] * Ratio); // C411Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M412Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y413414Out[3] = In[3]; // K (untouched)415416return TRUE;417}418419// This is a devicelink operating in CMYK for ink-limiting420421cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,422cmsColorSpaceSignature ColorSpace,423cmsFloat64Number Limit)424{425cmsHPROFILE hICC;426cmsPipeline* LUT;427cmsStage* CLUT;428cmsUInt32Number nChannels;429430if (ColorSpace != cmsSigCmykData) {431cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");432return NULL;433}434435if (Limit < 0.0 || Limit > 400) {436437cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400");438if (Limit < 0) Limit = 0;439if (Limit > 400) Limit = 400;440441}442443hICC = cmsCreateProfilePlaceholder(ContextID);444if (!hICC) // can't allocate445return NULL;446447cmsSetProfileVersion(hICC, 4.3);448449cmsSetDeviceClass(hICC, cmsSigLinkClass);450cmsSetColorSpace(hICC, ColorSpace);451cmsSetPCS(hICC, ColorSpace);452453cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);454455456// Creates a Pipeline with 3D grid only457LUT = cmsPipelineAlloc(ContextID, 4, 4);458if (LUT == NULL) goto Error;459460461nChannels = cmsChannelsOf(ColorSpace);462463CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL);464if (CLUT == NULL) goto Error;465466if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;467468if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) ||469!cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) ||470!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels)))471goto Error;472473// Create tags474if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error;475476if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error;477if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error;478479// cmsPipeline is already on virtual profile480cmsPipelineFree(LUT);481482// Ok, done483return hICC;484485Error:486if (LUT != NULL)487cmsPipelineFree(LUT);488489if (hICC != NULL)490cmsCloseProfile(hICC);491492return NULL;493}494495cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit)496{497return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit);498}499500501// Creates a fake Lab identity.502cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)503{504cmsHPROFILE hProfile;505cmsPipeline* LUT = NULL;506507hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);508if (hProfile == NULL) return NULL;509510cmsSetProfileVersion(hProfile, 2.1);511512cmsSetDeviceClass(hProfile, cmsSigAbstractClass);513cmsSetColorSpace(hProfile, cmsSigLabData);514cmsSetPCS(hProfile, cmsSigLabData);515516if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL;517518// An identity LUT is all we need519LUT = cmsPipelineAlloc(ContextID, 3, 3);520if (LUT == NULL) goto Error;521522if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))523goto Error;524525if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;526cmsPipelineFree(LUT);527528return hProfile;529530Error:531532if (LUT != NULL)533cmsPipelineFree(LUT);534535if (hProfile != NULL)536cmsCloseProfile(hProfile);537538return NULL;539}540541542cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint)543{544return cmsCreateLab2ProfileTHR(NULL, WhitePoint);545}546547548// Creates a fake Lab V4 identity.549cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)550{551cmsHPROFILE hProfile;552cmsPipeline* LUT = NULL;553554hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);555if (hProfile == NULL) return NULL;556557cmsSetProfileVersion(hProfile, 4.3);558559cmsSetDeviceClass(hProfile, cmsSigAbstractClass);560cmsSetColorSpace(hProfile, cmsSigLabData);561cmsSetPCS(hProfile, cmsSigLabData);562563if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error;564565// An empty LUTs is all we need566LUT = cmsPipelineAlloc(ContextID, 3, 3);567if (LUT == NULL) goto Error;568569if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))570goto Error;571572if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;573cmsPipelineFree(LUT);574575return hProfile;576577Error:578579if (LUT != NULL)580cmsPipelineFree(LUT);581582if (hProfile != NULL)583cmsCloseProfile(hProfile);584585return NULL;586}587588cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint)589{590return cmsCreateLab4ProfileTHR(NULL, WhitePoint);591}592593594// Creates a fake XYZ identity595cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID)596{597cmsHPROFILE hProfile;598cmsPipeline* LUT = NULL;599600hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);601if (hProfile == NULL) return NULL;602603cmsSetProfileVersion(hProfile, 4.3);604605cmsSetDeviceClass(hProfile, cmsSigAbstractClass);606cmsSetColorSpace(hProfile, cmsSigXYZData);607cmsSetPCS(hProfile, cmsSigXYZData);608609if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error;610611// An identity LUT is all we need612LUT = cmsPipelineAlloc(ContextID, 3, 3);613if (LUT == NULL) goto Error;614615if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))616goto Error;617618if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;619cmsPipelineFree(LUT);620621return hProfile;622623Error:624625if (LUT != NULL)626cmsPipelineFree(LUT);627628if (hProfile != NULL)629cmsCloseProfile(hProfile);630631return NULL;632}633634635cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)636{637return cmsCreateXYZProfileTHR(NULL);638}639640641//sRGB Curves are defined by:642//643//If R'sRGB,G'sRGB, B'sRGB < 0.04045644//645// R = R'sRGB / 12.92646// G = G'sRGB / 12.92647// B = B'sRGB / 12.92648//649//650//else if R'sRGB,G'sRGB, B'sRGB >= 0.04045651//652// R = ((R'sRGB + 0.055) / 1.055)^2.4653// G = ((G'sRGB + 0.055) / 1.055)^2.4654// B = ((B'sRGB + 0.055) / 1.055)^2.4655656static657cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)658{659cmsFloat64Number Parameters[5];660661Parameters[0] = 2.4;662Parameters[1] = 1. / 1.055;663Parameters[2] = 0.055 / 1.055;664Parameters[3] = 1. / 12.92;665Parameters[4] = 0.04045;666667return cmsBuildParametricToneCurve(ContextID, 4, Parameters);668}669670// Create the ICC virtual profile for sRGB space671cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID)672{673cmsCIExyY D65 = { 0.3127, 0.3290, 1.0 };674cmsCIExyYTRIPLE Rec709Primaries = {675{0.6400, 0.3300, 1.0},676{0.3000, 0.6000, 1.0},677{0.1500, 0.0600, 1.0}678};679cmsToneCurve* Gamma22[3];680cmsHPROFILE hsRGB;681682// cmsWhitePointFromTemp(&D65, 6504);683Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID);684if (Gamma22[0] == NULL) return NULL;685686hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22);687cmsFreeToneCurve(Gamma22[0]);688if (hsRGB == NULL) return NULL;689690if (!SetTextTags(hsRGB, L"sRGB built-in")) {691cmsCloseProfile(hsRGB);692return NULL;693}694695return hsRGB;696}697698cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void)699{700return cmsCreate_sRGBProfileTHR(NULL);701}702703704705typedef struct {706cmsFloat64Number Brightness;707cmsFloat64Number Contrast;708cmsFloat64Number Hue;709cmsFloat64Number Saturation;710cmsBool lAdjustWP;711cmsCIEXYZ WPsrc, WPdest;712713} BCHSWADJUSTS, *LPBCHSWADJUSTS;714715716static717int bchswSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)718{719cmsCIELab LabIn, LabOut;720cmsCIELCh LChIn, LChOut;721cmsCIEXYZ XYZ;722LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;723724725cmsLabEncoded2Float(&LabIn, In);726727728cmsLab2LCh(&LChIn, &LabIn);729730// Do some adjusts on LCh731732LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;733LChOut.C = LChIn.C + bchsw -> Saturation;734LChOut.h = LChIn.h + bchsw -> Hue;735736737cmsLCh2Lab(&LabOut, &LChOut);738739// Move white point in Lab740if (bchsw->lAdjustWP) {741cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut);742cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ);743}744745// Back to encoded746747cmsFloat2LabEncoded(Out, &LabOut);748749return TRUE;750}751752753// Creates an abstract profile operating in Lab space for Brightness,754// contrast, Saturation and white point displacement755756cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,757cmsUInt32Number nLUTPoints,758cmsFloat64Number Bright,759cmsFloat64Number Contrast,760cmsFloat64Number Hue,761cmsFloat64Number Saturation,762cmsUInt32Number TempSrc,763cmsUInt32Number TempDest)764{765cmsHPROFILE hICC;766cmsPipeline* Pipeline;767BCHSWADJUSTS bchsw;768cmsCIExyY WhitePnt;769cmsStage* CLUT;770cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];771cmsUInt32Number i;772773bchsw.Brightness = Bright;774bchsw.Contrast = Contrast;775bchsw.Hue = Hue;776bchsw.Saturation = Saturation;777if (TempSrc == TempDest) {778779bchsw.lAdjustWP = FALSE;780}781else {782bchsw.lAdjustWP = TRUE;783cmsWhitePointFromTemp(&WhitePnt, TempSrc);784cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);785cmsWhitePointFromTemp(&WhitePnt, TempDest);786cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);787788}789790hICC = cmsCreateProfilePlaceholder(ContextID);791if (!hICC) // can't allocate792return NULL;793794cmsSetDeviceClass(hICC, cmsSigAbstractClass);795cmsSetColorSpace(hICC, cmsSigLabData);796cmsSetPCS(hICC, cmsSigLabData);797798cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);799800// Creates a Pipeline with 3D grid only801Pipeline = cmsPipelineAlloc(ContextID, 3, 3);802if (Pipeline == NULL) {803cmsCloseProfile(hICC);804return NULL;805}806807for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;808CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);809if (CLUT == NULL) goto Error;810811812if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {813814// Shouldn't reach here815goto Error;816}817818if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {819goto Error;820}821822// Create tags823if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;824825cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());826827cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);828829// Pipeline is already on virtual profile830cmsPipelineFree(Pipeline);831832// Ok, done833return hICC;834835Error:836cmsPipelineFree(Pipeline);837cmsCloseProfile(hICC);838return NULL;839}840841842CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints,843cmsFloat64Number Bright,844cmsFloat64Number Contrast,845cmsFloat64Number Hue,846cmsFloat64Number Saturation,847cmsUInt32Number TempSrc,848cmsUInt32Number TempDest)849{850return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);851}852853854// Creates a fake NULL profile. This profile return 1 channel as always 0.855// Is useful only for gamut checking tricks856cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)857{858cmsHPROFILE hProfile;859cmsPipeline* LUT = NULL;860cmsStage* PostLin;861cmsStage* OutLin;862cmsToneCurve* EmptyTab[3];863cmsUInt16Number Zero[2] = { 0, 0 };864const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };865866hProfile = cmsCreateProfilePlaceholder(ContextID);867if (!hProfile) // can't allocate868return NULL;869870cmsSetProfileVersion(hProfile, 4.3);871872if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;873874875cmsSetDeviceClass(hProfile, cmsSigOutputClass);876cmsSetColorSpace(hProfile, cmsSigGrayData);877cmsSetPCS(hProfile, cmsSigLabData);878879// Create a valid ICC 4 structure880LUT = cmsPipelineAlloc(ContextID, 3, 1);881if (LUT == NULL) goto Error;882883EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);884PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab);885OutLin = cmsStageAllocToneCurves(ContextID, 1, EmptyTab);886cmsFreeToneCurve(EmptyTab[0]);887888if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))889goto Error;890891if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))892goto Error;893894if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin))895goto Error;896897if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;898if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;899900cmsPipelineFree(LUT);901return hProfile;902903Error:904905if (LUT != NULL)906cmsPipelineFree(LUT);907908if (hProfile != NULL)909cmsCloseProfile(hProfile);910911return NULL;912}913914cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)915{916return cmsCreateNULLProfileTHR(NULL);917}918919920static921int IsPCS(cmsColorSpaceSignature ColorSpace)922{923return (ColorSpace == cmsSigXYZData ||924ColorSpace == cmsSigLabData);925}926927928static929void FixColorSpaces(cmsHPROFILE hProfile,930cmsColorSpaceSignature ColorSpace,931cmsColorSpaceSignature PCS,932cmsUInt32Number dwFlags)933{934if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {935936if (IsPCS(ColorSpace) && IsPCS(PCS)) {937938cmsSetDeviceClass(hProfile, cmsSigAbstractClass);939cmsSetColorSpace(hProfile, ColorSpace);940cmsSetPCS(hProfile, PCS);941return;942}943944if (IsPCS(ColorSpace) && !IsPCS(PCS)) {945946cmsSetDeviceClass(hProfile, cmsSigOutputClass);947cmsSetPCS(hProfile, ColorSpace);948cmsSetColorSpace(hProfile, PCS);949return;950}951952if (IsPCS(PCS) && !IsPCS(ColorSpace)) {953954cmsSetDeviceClass(hProfile, cmsSigInputClass);955cmsSetColorSpace(hProfile, ColorSpace);956cmsSetPCS(hProfile, PCS);957return;958}959}960961cmsSetDeviceClass(hProfile, cmsSigLinkClass);962cmsSetColorSpace(hProfile, ColorSpace);963cmsSetPCS(hProfile, PCS);964}965966967968// This function creates a named color profile dumping all the contents of transform to a single profile969// In this way, LittleCMS may be used to "group" several named color databases into a single profile.970// It has, however, several minor limitations. PCS is always Lab, which is not very critic since this971// is the normal PCS for named color profiles.972static973cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)974{975_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;976cmsHPROFILE hICC = NULL;977cmsUInt32Number i, nColors;978cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;979980// Create an empty placeholder981hICC = cmsCreateProfilePlaceholder(v->ContextID);982if (hICC == NULL) return NULL;983984// Critical information985cmsSetDeviceClass(hICC, cmsSigNamedColorClass);986cmsSetColorSpace(hICC, v ->ExitColorSpace);987cmsSetPCS(hICC, cmsSigLabData);988989// Tag profile with information990if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;991992Original = cmsGetNamedColorList(xform);993if (Original == NULL) goto Error;994995nColors = cmsNamedColorCount(Original);996nc2 = cmsDupNamedColorList(Original);997if (nc2 == NULL) goto Error;998999// Colorant count now depends on the output space1000nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);10011002// Make sure we have proper formatters1003cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,1004FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))1005| BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));10061007// Apply the transfor to colorants.1008for (i=0; i < nColors; i++) {1009cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);1010}10111012if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;1013cmsFreeNamedColorList(nc2);10141015return hICC;10161017Error:1018if (hICC != NULL) cmsCloseProfile(hICC);1019return NULL;1020}102110221023// This structure holds information about which MPU can be stored on a profile based on the version10241025typedef struct {1026cmsBool IsV4; // Is a V4 tag?1027cmsTagSignature RequiredTag; // Set to 0 for both types1028cmsTagTypeSignature LutType; // The LUT type1029int nTypes; // Number of types (up to 5)1030cmsStageSignature MpeTypes[5]; // 5 is the maximum number10311032} cmsAllowedLUT;10331034#define cmsSig0 ((cmsTagSignature) 0)10351036static const cmsAllowedLUT AllowedLUTTypes[] = {10371038{ FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },1039{ FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },1040{ FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },1041{ TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },1042{ TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },1043{ TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },1044{ TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},1045{ TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }},1046{ TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},1047{ TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},1048{ TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}1049};10501051#define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))10521053// Check a single entry1054static1055cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)1056{1057cmsStage* mpe;1058int n;10591060for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {10611062if (n > Tab ->nTypes) return FALSE;1063if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;1064}10651066return (n == Tab ->nTypes);1067}106810691070static1071const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)1072{1073cmsUInt32Number n;10741075for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {10761077const cmsAllowedLUT* Tab = AllowedLUTTypes + n;10781079if (IsV4 ^ Tab -> IsV4) continue;1080if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;10811082if (CheckOne(Tab, Lut)) return Tab;1083}10841085return NULL;1086}108710881089// Does convert a transform into a device link profile1090cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)1091{1092cmsHPROFILE hProfile = NULL;1093cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;1094int ColorSpaceBitsIn, ColorSpaceBitsOut;1095_cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;1096cmsPipeline* LUT = NULL;1097cmsStage* mpe;1098cmsContext ContextID = cmsGetTransformContextID(hTransform);1099const cmsAllowedLUT* AllowedLUT;1100cmsTagSignature DestinationTag;1101cmsProfileClassSignature deviceClass;11021103_cmsAssert(hTransform != NULL);11041105// Get the first mpe to check for named color1106mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);11071108// Check if is a named color transform1109if (mpe != NULL) {11101111if (cmsStageType(mpe) == cmsSigNamedColorElemType) {1112return CreateNamedColorDevicelink(hTransform);1113}1114}11151116// First thing to do is to get a copy of the transformation1117LUT = cmsPipelineDup(xform ->Lut);1118if (LUT == NULL) return NULL;11191120// Time to fix the Lab2/Lab4 issue.1121if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {11221123if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))1124goto Error;1125}11261127// On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments1128if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {11291130dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;1131if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))1132goto Error;1133}113411351136hProfile = cmsCreateProfilePlaceholder(ContextID);1137if (!hProfile) goto Error; // can't allocate11381139cmsSetProfileVersion(hProfile, Version);11401141FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);11421143// Optimize the LUT and precalculate a devicelink11441145ChansIn = cmsChannelsOf(xform -> EntryColorSpace);1146ChansOut = cmsChannelsOf(xform -> ExitColorSpace);11471148ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace);1149ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);11501151FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);1152FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);11531154deviceClass = cmsGetDeviceClass(hProfile);11551156if (deviceClass == cmsSigOutputClass)1157DestinationTag = cmsSigBToA0Tag;1158else1159DestinationTag = cmsSigAToB0Tag;11601161// Check if the profile/version can store the result1162if (dwFlags & cmsFLAGS_FORCE_CLUT)1163AllowedLUT = NULL;1164else1165AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);11661167if (AllowedLUT == NULL) {11681169// Try to optimize1170_cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);1171AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);11721173}11741175// If no way, then force CLUT that for sure can be written1176if (AllowedLUT == NULL) {11771178cmsStage* FirstStage;1179cmsStage* LastStage;11801181dwFlags |= cmsFLAGS_FORCE_CLUT;1182_cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);11831184// Put identity curves if needed1185FirstStage = cmsPipelineGetPtrToFirstStage(LUT);1186if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType)1187if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))1188goto Error;11891190LastStage = cmsPipelineGetPtrToLastStage(LUT);1191if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType)1192if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut)))1193goto Error;11941195AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);1196}11971198// Somethings is wrong...1199if (AllowedLUT == NULL) {1200goto Error;1201}120212031204if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)1205cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);12061207// Tag profile with information1208if (!SetTextTags(hProfile, L"devicelink")) goto Error;12091210// Store result1211if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;121212131214if (xform -> InputColorant != NULL) {1215if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;1216}12171218if (xform -> OutputColorant != NULL) {1219if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;1220}12211222if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {1223if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;1224}12251226// Set the white point1227if (deviceClass == cmsSigInputClass) {1228if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;1229}1230else {1231if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;1232}123312341235// Per 7.2.15 in spec 4.31236cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);12371238cmsPipelineFree(LUT);1239return hProfile;12401241Error:1242if (LUT != NULL) cmsPipelineFree(LUT);1243cmsCloseProfile(hProfile);1244return NULL;1245}124612471248