Path: blob/master/src/java.desktop/share/native/liblcms/cmscam02.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// CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging.5859// ---------- Implementation --------------------------------------------6061typedef struct {6263cmsFloat64Number XYZ[3];64cmsFloat64Number RGB[3];65cmsFloat64Number RGBc[3];66cmsFloat64Number RGBp[3];67cmsFloat64Number RGBpa[3];68cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M;69cmsFloat64Number abC[2];70cmsFloat64Number abs[2];71cmsFloat64Number abM[2];7273} CAM02COLOR;7475typedef struct {7677CAM02COLOR adoptedWhite;78cmsFloat64Number LA, Yb;79cmsFloat64Number F, c, Nc;80cmsUInt32Number surround;81cmsFloat64Number n, Nbb, Ncb, z, FL, D;8283cmsContext ContextID;8485} cmsCIECAM02;868788static89cmsFloat64Number compute_n(cmsCIECAM02* pMod)90{91return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]);92}9394static95cmsFloat64Number compute_z(cmsCIECAM02* pMod)96{97return (1.48 + pow(pMod -> n, 0.5));98}99100static101cmsFloat64Number computeNbb(cmsCIECAM02* pMod)102{103return (0.725 * pow((1.0 / pMod -> n), 0.2));104}105106static107cmsFloat64Number computeFL(cmsCIECAM02* pMod)108{109cmsFloat64Number k, FL;110111k = 1.0 / ((5.0 * pMod->LA) + 1.0);112FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 *113(pow((1.0 - pow(k, 4.0)), 2.0)) *114(pow((5.0 * pMod->LA), (1.0 / 3.0)));115116return FL;117}118119static120cmsFloat64Number computeD(cmsCIECAM02* pMod)121{122cmsFloat64Number D;123124D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0)));125126return D;127}128129130static131CAM02COLOR XYZtoCAT02(CAM02COLOR clr)132{133clr.RGB[0] = (clr.XYZ[0] * 0.7328) + (clr.XYZ[1] * 0.4296) + (clr.XYZ[2] * -0.1624);134clr.RGB[1] = (clr.XYZ[0] * -0.7036) + (clr.XYZ[1] * 1.6975) + (clr.XYZ[2] * 0.0061);135clr.RGB[2] = (clr.XYZ[0] * 0.0030) + (clr.XYZ[1] * 0.0136) + (clr.XYZ[2] * 0.9834);136137return clr;138}139140static141CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod)142{143cmsUInt32Number i;144145for (i = 0; i < 3; i++) {146clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] *147(pMod->D / pMod -> adoptedWhite.RGB[i])) +148(1.0 - pMod->D)) * clr.RGB[i];149}150151return clr;152}153154155static156CAM02COLOR CAT02toHPE(CAM02COLOR clr)157{158cmsFloat64Number M[9];159160M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628));161M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698));162M[2] =(( 0.38971 * 0.182745) + (0.68898 * 0.072098) + (-0.07868 * 1.015326));163M[3] =((-0.22981 * 1.096124) + (1.18340 * 0.454369) + ( 0.04641 * -0.009628));164M[4] =((-0.22981 * -0.278869) + (1.18340 * 0.473533) + ( 0.04641 * -0.005698));165M[5] =((-0.22981 * 0.182745) + (1.18340 * 0.072098) + ( 0.04641 * 1.015326));166M[6] =(-0.009628);167M[7] =(-0.005698);168M[8] =( 1.015326);169170clr.RGBp[0] = (clr.RGBc[0] * M[0]) + (clr.RGBc[1] * M[1]) + (clr.RGBc[2] * M[2]);171clr.RGBp[1] = (clr.RGBc[0] * M[3]) + (clr.RGBc[1] * M[4]) + (clr.RGBc[2] * M[5]);172clr.RGBp[2] = (clr.RGBc[0] * M[6]) + (clr.RGBc[1] * M[7]) + (clr.RGBc[2] * M[8]);173174return clr;175}176177static178CAM02COLOR NonlinearCompression(CAM02COLOR clr, cmsCIECAM02* pMod)179{180cmsUInt32Number i;181cmsFloat64Number temp;182183for (i = 0; i < 3; i++) {184if (clr.RGBp[i] < 0) {185186temp = pow((-1.0 * pMod->FL * clr.RGBp[i] / 100.0), 0.42);187clr.RGBpa[i] = (-1.0 * 400.0 * temp) / (temp + 27.13) + 0.1;188}189else {190temp = pow((pMod->FL * clr.RGBp[i] / 100.0), 0.42);191clr.RGBpa[i] = (400.0 * temp) / (temp + 27.13) + 0.1;192}193}194195clr.A = (((2.0 * clr.RGBpa[0]) + clr.RGBpa[1] +196(clr.RGBpa[2] / 20.0)) - 0.305) * pMod->Nbb;197198return clr;199}200201static202CAM02COLOR ComputeCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)203{204cmsFloat64Number a, b, temp, e, t, r2d, d2r;205206a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0);207b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0;208209r2d = (180.0 / 3.141592654);210if (a == 0) {211if (b == 0) clr.h = 0;212else if (b > 0) clr.h = 90;213else clr.h = 270;214}215else if (a > 0) {216temp = b / a;217if (b > 0) clr.h = (r2d * atan(temp));218else if (b == 0) clr.h = 0;219else clr.h = (r2d * atan(temp)) + 360;220}221else {222temp = b / a;223clr.h = (r2d * atan(temp)) + 180;224}225226d2r = (3.141592654 / 180.0);227e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *228(cos((clr.h * d2r + 2.0)) + 3.8);229230if (clr.h < 20.14) {231temp = ((clr.h + 122.47)/1.2) + ((20.14 - clr.h)/0.8);232clr.H = 300 + (100*((clr.h + 122.47)/1.2)) / temp;233}234else if (clr.h < 90.0) {235temp = ((clr.h - 20.14)/0.8) + ((90.00 - clr.h)/0.7);236clr.H = (100*((clr.h - 20.14)/0.8)) / temp;237}238else if (clr.h < 164.25) {239temp = ((clr.h - 90.00)/0.7) + ((164.25 - clr.h)/1.0);240clr.H = 100 + ((100*((clr.h - 90.00)/0.7)) / temp);241}242else if (clr.h < 237.53) {243temp = ((clr.h - 164.25)/1.0) + ((237.53 - clr.h)/1.2);244clr.H = 200 + ((100*((clr.h - 164.25)/1.0)) / temp);245}246else {247temp = ((clr.h - 237.53)/1.2) + ((360 - clr.h + 20.14)/0.8);248clr.H = 300 + ((100*((clr.h - 237.53)/1.2)) / temp);249}250251clr.J = 100.0 * pow((clr.A / pMod->adoptedWhite.A),252(pMod->c * pMod->z));253254clr.Q = (4.0 / pMod->c) * pow((clr.J / 100.0), 0.5) *255(pMod->adoptedWhite.A + 4.0) * pow(pMod->FL, 0.25);256257t = (e * pow(((a * a) + (b * b)), 0.5)) /258(clr.RGBpa[0] + clr.RGBpa[1] +259((21.0 / 20.0) * clr.RGBpa[2]));260261clr.C = pow(t, 0.9) * pow((clr.J / 100.0), 0.5) *262pow((1.64 - pow(0.29, pMod->n)), 0.73);263264clr.M = clr.C * pow(pMod->FL, 0.25);265clr.s = 100.0 * pow((clr.M / clr.Q), 0.5);266267return clr;268}269270271static272CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)273{274275cmsFloat64Number t, e, p1, p2, p3, p4, p5, hr, d2r;276d2r = 3.141592654 / 180.0;277278t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) *279(pow((1.64 - pow(0.29, pMod->n)), 0.73)))),280(1.0 / 0.9) );281e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *282(cos((clr.h * d2r + 2.0)) + 3.8);283284clr.A = pMod->adoptedWhite.A * pow(285(clr.J / 100.0),286(1.0 / (pMod->c * pMod->z)));287288p1 = e / t;289p2 = (clr.A / pMod->Nbb) + 0.305;290p3 = 21.0 / 20.0;291292hr = clr.h * d2r;293294if (fabs(sin(hr)) >= fabs(cos(hr))) {295p4 = p1 / sin(hr);296clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /297(p4 + (2.0 + p3) * (220.0 / 1403.0) *298(cos(hr) / sin(hr)) - (27.0 / 1403.0) +299p3 * (6300.0 / 1403.0));300clr.a = clr.b * (cos(hr) / sin(hr));301}302else {303p5 = p1 / cos(hr);304clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /305(p5 + (2.0 + p3) * (220.0 / 1403.0) -306((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) *307(sin(hr) / cos(hr)));308clr.b = clr.a * (sin(hr) / cos(hr));309}310311clr.RGBpa[0] = ((460.0 / 1403.0) * p2) +312((451.0 / 1403.0) * clr.a) +313((288.0 / 1403.0) * clr.b);314clr.RGBpa[1] = ((460.0 / 1403.0) * p2) -315((891.0 / 1403.0) * clr.a) -316((261.0 / 1403.0) * clr.b);317clr.RGBpa[2] = ((460.0 / 1403.0) * p2) -318((220.0 / 1403.0) * clr.a) -319((6300.0 / 1403.0) * clr.b);320321return clr;322}323324static325CAM02COLOR InverseNonlinearity(CAM02COLOR clr, cmsCIECAM02* pMod)326{327cmsUInt32Number i;328cmsFloat64Number c1;329330for (i = 0; i < 3; i++) {331if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1;332else c1 = 1;333clr.RGBp[i] = c1 * (100.0 / pMod->FL) *334pow(((27.13 * fabs(clr.RGBpa[i] - 0.1)) /335(400.0 - fabs(clr.RGBpa[i] - 0.1))),336(1.0 / 0.42));337}338339return clr;340}341342static343CAM02COLOR HPEtoCAT02(CAM02COLOR clr)344{345cmsFloat64Number M[9];346347M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950));348M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054));349M[2] = (( 0.7328 * 0.201908) + (0.4296 * 0.000008) - 0.1624);350M[3] = ((-0.7036 * 1.910197) + (1.6975 * 0.370950));351M[4] = ((-0.7036 * -1.112124) + (1.6975 * 0.629054));352M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061);353M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950));354M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054));355M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);;356357clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]);358clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]);359clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]);360return clr;361}362363364static365CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod)366{367cmsUInt32Number i;368for (i = 0; i < 3; i++) {369clr.RGB[i] = clr.RGBc[i] /370((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D);371}372return clr;373}374375376static377CAM02COLOR CAT02toXYZ(CAM02COLOR clr)378{379clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745);380clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098);381clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326);382383return clr;384}385386387cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC)388{389cmsCIECAM02* lpMod;390391_cmsAssert(pVC != NULL);392393if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) {394return NULL;395}396397lpMod ->ContextID = ContextID;398399lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X;400lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y;401lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z;402403lpMod -> LA = pVC ->La;404lpMod -> Yb = pVC ->Yb;405lpMod -> D = pVC ->D_value;406lpMod -> surround = pVC ->surround;407408switch (lpMod -> surround) {409410411case CUTSHEET_SURROUND:412lpMod->F = 0.8;413lpMod->c = 0.41;414lpMod->Nc = 0.8;415break;416417case DARK_SURROUND:418lpMod -> F = 0.8;419lpMod -> c = 0.525;420lpMod -> Nc = 0.8;421break;422423case DIM_SURROUND:424lpMod -> F = 0.9;425lpMod -> c = 0.59;426lpMod -> Nc = 0.95;427break;428429default:430// Average surround431lpMod -> F = 1.0;432lpMod -> c = 0.69;433lpMod -> Nc = 1.0;434}435436lpMod -> n = compute_n(lpMod);437lpMod -> z = compute_z(lpMod);438lpMod -> Nbb = computeNbb(lpMod);439lpMod -> FL = computeFL(lpMod);440441if (lpMod -> D == D_CALCULATE) {442lpMod -> D = computeD(lpMod);443}444445lpMod -> Ncb = lpMod -> Nbb;446447lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite);448lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod);449lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite);450lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod);451452return (cmsHANDLE) lpMod;453454}455456void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel)457{458cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;459460if (lpMod) _cmsFree(lpMod ->ContextID, lpMod);461}462463464void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut)465{466CAM02COLOR clr;467cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;468469_cmsAssert(lpMod != NULL);470_cmsAssert(pIn != NULL);471_cmsAssert(pOut != NULL);472473memset(&clr, 0, sizeof(clr));474475clr.XYZ[0] = pIn ->X;476clr.XYZ[1] = pIn ->Y;477clr.XYZ[2] = pIn ->Z;478479clr = XYZtoCAT02(clr);480clr = ChromaticAdaptation(clr, lpMod);481clr = CAT02toHPE(clr);482clr = NonlinearCompression(clr, lpMod);483clr = ComputeCorrelates(clr, lpMod);484485pOut ->J = clr.J;486pOut ->C = clr.C;487pOut ->h = clr.h;488}489490void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut)491{492CAM02COLOR clr;493cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;494495_cmsAssert(lpMod != NULL);496_cmsAssert(pIn != NULL);497_cmsAssert(pOut != NULL);498499memset(&clr, 0, sizeof(clr));500501clr.J = pIn -> J;502clr.C = pIn -> C;503clr.h = pIn -> h;504505clr = InverseCorrelates(clr, lpMod);506clr = InverseNonlinearity(clr, lpMod);507clr = HPEtoCAT02(clr);508clr = InverseChromaticAdaptation(clr, lpMod);509clr = CAT02toXYZ(clr);510511pOut ->X = clr.XYZ[0];512pOut ->Y = clr.XYZ[1];513pOut ->Z = clr.XYZ[2];514}515516517