Removed the libs directory containing win32 compiled versions of libpng, libtiff and liblcms. Added a thirdparty directory to include main source files of libtiff, libpng, libz and liblcms to enable support of these formats in the codec executables. CMake will try to statically build these libraries if they are not found on the system. Note that these third party libraries are not required to build libopenjpeg (which has no dependencies).
This commit is contained in:
483
thirdparty/liblcms2/src/cmscam02.c
vendored
Normal file
483
thirdparty/liblcms2/src/cmscam02.c
vendored
Normal file
@@ -0,0 +1,483 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
// CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging.
|
||||
|
||||
// ---------- Implementation --------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmsFloat64Number XYZ[3];
|
||||
cmsFloat64Number RGB[3];
|
||||
cmsFloat64Number RGBc[3];
|
||||
cmsFloat64Number RGBp[3];
|
||||
cmsFloat64Number RGBpa[3];
|
||||
cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M;
|
||||
cmsFloat64Number abC[2];
|
||||
cmsFloat64Number abs[2];
|
||||
cmsFloat64Number abM[2];
|
||||
|
||||
} CAM02COLOR;
|
||||
|
||||
typedef struct {
|
||||
|
||||
CAM02COLOR adoptedWhite;
|
||||
cmsFloat64Number LA, Yb;
|
||||
cmsFloat64Number F, c, Nc;
|
||||
cmsUInt32Number surround;
|
||||
cmsFloat64Number n, Nbb, Ncb, z, FL, D;
|
||||
|
||||
cmsContext ContextID;
|
||||
|
||||
} cmsCIECAM02;
|
||||
|
||||
|
||||
static
|
||||
cmsFloat64Number compute_n(cmsCIECAM02* pMod)
|
||||
{
|
||||
return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]);
|
||||
}
|
||||
|
||||
static
|
||||
cmsFloat64Number compute_z(cmsCIECAM02* pMod)
|
||||
{
|
||||
return (1.48 + pow(pMod -> n, 0.5));
|
||||
}
|
||||
|
||||
static
|
||||
cmsFloat64Number computeNbb(cmsCIECAM02* pMod)
|
||||
{
|
||||
return (0.725 * pow((1.0 / pMod -> n), 0.2));
|
||||
}
|
||||
|
||||
static
|
||||
cmsFloat64Number computeFL(cmsCIECAM02* pMod)
|
||||
{
|
||||
cmsFloat64Number k, FL;
|
||||
|
||||
k = 1.0 / ((5.0 * pMod->LA) + 1.0);
|
||||
FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 *
|
||||
(pow((1.0 - pow(k, 4.0)), 2.0)) *
|
||||
(pow((5.0 * pMod->LA), (1.0 / 3.0)));
|
||||
|
||||
return FL;
|
||||
}
|
||||
|
||||
static
|
||||
cmsFloat64Number computeD(cmsCIECAM02* pMod)
|
||||
{
|
||||
cmsFloat64Number D;
|
||||
|
||||
D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0)));
|
||||
|
||||
return D;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR XYZtoCAT02(CAM02COLOR clr)
|
||||
{
|
||||
clr.RGB[0] = (clr.XYZ[0] * 0.7328) + (clr.XYZ[1] * 0.4296) + (clr.XYZ[2] * -0.1624);
|
||||
clr.RGB[1] = (clr.XYZ[0] * -0.7036) + (clr.XYZ[1] * 1.6975) + (clr.XYZ[2] * 0.0061);
|
||||
clr.RGB[2] = (clr.XYZ[0] * 0.0030) + (clr.XYZ[1] * 0.0136) + (clr.XYZ[2] * 0.9834);
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod)
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] *
|
||||
(pMod->D / pMod -> adoptedWhite.RGB[i])) +
|
||||
(1.0 - pMod->D)) * clr.RGB[i];
|
||||
}
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR CAT02toHPE(CAM02COLOR clr)
|
||||
{
|
||||
cmsFloat64Number M[9];
|
||||
|
||||
M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628));
|
||||
M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698));
|
||||
M[2] =(( 0.38971 * 0.182745) + (0.68898 * 0.072098) + (-0.07868 * 1.015326));
|
||||
M[3] =((-0.22981 * 1.096124) + (1.18340 * 0.454369) + ( 0.04641 * -0.009628));
|
||||
M[4] =((-0.22981 * -0.278869) + (1.18340 * 0.473533) + ( 0.04641 * -0.005698));
|
||||
M[5] =((-0.22981 * 0.182745) + (1.18340 * 0.072098) + ( 0.04641 * 1.015326));
|
||||
M[6] =(-0.009628);
|
||||
M[7] =(-0.005698);
|
||||
M[8] =( 1.015326);
|
||||
|
||||
clr.RGBp[0] = (clr.RGBc[0] * M[0]) + (clr.RGBc[1] * M[1]) + (clr.RGBc[2] * M[2]);
|
||||
clr.RGBp[1] = (clr.RGBc[0] * M[3]) + (clr.RGBc[1] * M[4]) + (clr.RGBc[2] * M[5]);
|
||||
clr.RGBp[2] = (clr.RGBc[0] * M[6]) + (clr.RGBc[1] * M[7]) + (clr.RGBc[2] * M[8]);
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR NonlinearCompression(CAM02COLOR clr, cmsCIECAM02* pMod)
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
cmsFloat64Number temp;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (clr.RGBp[i] < 0) {
|
||||
|
||||
temp = pow((-1.0 * pMod->FL * clr.RGBp[i] / 100.0), 0.42);
|
||||
clr.RGBpa[i] = (-1.0 * 400.0 * temp) / (temp + 27.13) + 0.1;
|
||||
}
|
||||
else {
|
||||
temp = pow((pMod->FL * clr.RGBp[i] / 100.0), 0.42);
|
||||
clr.RGBpa[i] = (400.0 * temp) / (temp + 27.13) + 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
clr.A = (((2.0 * clr.RGBpa[0]) + clr.RGBpa[1] +
|
||||
(clr.RGBpa[2] / 20.0)) - 0.305) * pMod->Nbb;
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR ComputeCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)
|
||||
{
|
||||
cmsFloat64Number a, b, temp, e, t, r2d, d2r;
|
||||
|
||||
a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0);
|
||||
b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0;
|
||||
|
||||
r2d = (180.0 / 3.141592654);
|
||||
if (a == 0) {
|
||||
if (b == 0) clr.h = 0;
|
||||
else if (b > 0) clr.h = 90;
|
||||
else clr.h = 270;
|
||||
}
|
||||
else if (a > 0) {
|
||||
temp = b / a;
|
||||
if (b > 0) clr.h = (r2d * atan(temp));
|
||||
else if (b == 0) clr.h = 0;
|
||||
else clr.h = (r2d * atan(temp)) + 360;
|
||||
}
|
||||
else {
|
||||
temp = b / a;
|
||||
clr.h = (r2d * atan(temp)) + 180;
|
||||
}
|
||||
|
||||
d2r = (3.141592654 / 180.0);
|
||||
e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *
|
||||
(cos((clr.h * d2r + 2.0)) + 3.8);
|
||||
|
||||
if (clr.h < 20.14) {
|
||||
temp = ((clr.h + 122.47)/1.2) + ((20.14 - clr.h)/0.8);
|
||||
clr.H = 300 + (100*((clr.h + 122.47)/1.2)) / temp;
|
||||
}
|
||||
else if (clr.h < 90.0) {
|
||||
temp = ((clr.h - 20.14)/0.8) + ((90.00 - clr.h)/0.7);
|
||||
clr.H = (100*((clr.h - 20.14)/0.8)) / temp;
|
||||
}
|
||||
else if (clr.h < 164.25) {
|
||||
temp = ((clr.h - 90.00)/0.7) + ((164.25 - clr.h)/1.0);
|
||||
clr.H = 100 + ((100*((clr.h - 90.00)/0.7)) / temp);
|
||||
}
|
||||
else if (clr.h < 237.53) {
|
||||
temp = ((clr.h - 164.25)/1.0) + ((237.53 - clr.h)/1.2);
|
||||
clr.H = 200 + ((100*((clr.h - 164.25)/1.0)) / temp);
|
||||
}
|
||||
else {
|
||||
temp = ((clr.h - 237.53)/1.2) + ((360 - clr.h + 20.14)/0.8);
|
||||
clr.H = 300 + ((100*((clr.h - 237.53)/1.2)) / temp);
|
||||
}
|
||||
|
||||
clr.J = 100.0 * pow((clr.A / pMod->adoptedWhite.A),
|
||||
(pMod->c * pMod->z));
|
||||
|
||||
clr.Q = (4.0 / pMod->c) * pow((clr.J / 100.0), 0.5) *
|
||||
(pMod->adoptedWhite.A + 4.0) * pow(pMod->FL, 0.25);
|
||||
|
||||
t = (e * pow(((a * a) + (b * b)), 0.5)) /
|
||||
(clr.RGBpa[0] + clr.RGBpa[1] +
|
||||
((21.0 / 20.0) * clr.RGBpa[2]));
|
||||
|
||||
clr.C = pow(t, 0.9) * pow((clr.J / 100.0), 0.5) *
|
||||
pow((1.64 - pow(0.29, pMod->n)), 0.73);
|
||||
|
||||
clr.M = clr.C * pow(pMod->FL, 0.25);
|
||||
clr.s = 100.0 * pow((clr.M / clr.Q), 0.5);
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)
|
||||
{
|
||||
|
||||
cmsFloat64Number t, e, p1, p2, p3, p4, p5, hr, d2r;
|
||||
d2r = 3.141592654 / 180.0;
|
||||
|
||||
t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) *
|
||||
(pow((1.64 - pow(0.29, pMod->n)), 0.73)))),
|
||||
(1.0 / 0.9) );
|
||||
e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *
|
||||
(cos((clr.h * d2r + 2.0)) + 3.8);
|
||||
|
||||
clr.A = pMod->adoptedWhite.A * pow(
|
||||
(clr.J / 100.0),
|
||||
(1.0 / (pMod->c * pMod->z)));
|
||||
|
||||
p1 = e / t;
|
||||
p2 = (clr.A / pMod->Nbb) + 0.305;
|
||||
p3 = 21.0 / 20.0;
|
||||
|
||||
hr = clr.h * d2r;
|
||||
|
||||
if (fabs(sin(hr)) >= fabs(cos(hr))) {
|
||||
p4 = p1 / sin(hr);
|
||||
clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
|
||||
(p4 + (2.0 + p3) * (220.0 / 1403.0) *
|
||||
(cos(hr) / sin(hr)) - (27.0 / 1403.0) +
|
||||
p3 * (6300.0 / 1403.0));
|
||||
clr.a = clr.b * (cos(hr) / sin(hr));
|
||||
}
|
||||
else {
|
||||
p5 = p1 / cos(hr);
|
||||
clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
|
||||
(p5 + (2.0 + p3) * (220.0 / 1403.0) -
|
||||
((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) *
|
||||
(sin(hr) / cos(hr)));
|
||||
clr.b = clr.a * (sin(hr) / cos(hr));
|
||||
}
|
||||
|
||||
clr.RGBpa[0] = ((460.0 / 1403.0) * p2) +
|
||||
((451.0 / 1403.0) * clr.a) +
|
||||
((288.0 / 1403.0) * clr.b);
|
||||
clr.RGBpa[1] = ((460.0 / 1403.0) * p2) -
|
||||
((891.0 / 1403.0) * clr.a) -
|
||||
((261.0 / 1403.0) * clr.b);
|
||||
clr.RGBpa[2] = ((460.0 / 1403.0) * p2) -
|
||||
((220.0 / 1403.0) * clr.a) -
|
||||
((6300.0 / 1403.0) * clr.b);
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR InverseNonlinearity(CAM02COLOR clr, cmsCIECAM02* pMod)
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
cmsFloat64Number c1;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1;
|
||||
else c1 = 1;
|
||||
clr.RGBp[i] = c1 * (100.0 / pMod->FL) *
|
||||
pow(((27.13 * fabs(clr.RGBpa[i] - 0.1)) /
|
||||
(400.0 - fabs(clr.RGBpa[i] - 0.1))),
|
||||
(1.0 / 0.42));
|
||||
}
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR HPEtoCAT02(CAM02COLOR clr)
|
||||
{
|
||||
cmsFloat64Number M[9];
|
||||
|
||||
M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950));
|
||||
M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054));
|
||||
M[2] = (( 0.7328 * 0.201908) + (0.4296 * 0.000008) - 0.1624);
|
||||
M[3] = ((-0.7036 * 1.910197) + (1.6975 * 0.370950));
|
||||
M[4] = ((-0.7036 * -1.112124) + (1.6975 * 0.629054));
|
||||
M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061);
|
||||
M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950));
|
||||
M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054));
|
||||
M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);;
|
||||
|
||||
clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]);
|
||||
clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]);
|
||||
clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]);
|
||||
return clr;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod)
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
clr.RGB[i] = clr.RGBc[i] /
|
||||
((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D);
|
||||
}
|
||||
return clr;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR CAT02toXYZ(CAM02COLOR clr)
|
||||
{
|
||||
clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745);
|
||||
clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098);
|
||||
clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326);
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
|
||||
cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC)
|
||||
{
|
||||
cmsCIECAM02* lpMod;
|
||||
|
||||
_cmsAssert(pVC != NULL);
|
||||
|
||||
if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lpMod ->ContextID = ContextID;
|
||||
|
||||
lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X;
|
||||
lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y;
|
||||
lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z;
|
||||
|
||||
lpMod -> LA = pVC ->La;
|
||||
lpMod -> Yb = pVC ->Yb;
|
||||
lpMod -> D = pVC ->D_value;
|
||||
lpMod -> surround = pVC ->surround;
|
||||
|
||||
switch (lpMod -> surround) {
|
||||
|
||||
|
||||
case CUTSHEET_SURROUND:
|
||||
lpMod->F = 0.8;
|
||||
lpMod->c = 0.41;
|
||||
lpMod->Nc = 0.8;
|
||||
break;
|
||||
|
||||
case DARK_SURROUND:
|
||||
lpMod -> F = 0.8;
|
||||
lpMod -> c = 0.525;
|
||||
lpMod -> Nc = 0.8;
|
||||
break;
|
||||
|
||||
case DIM_SURROUND:
|
||||
lpMod -> F = 0.9;
|
||||
lpMod -> c = 0.59;
|
||||
lpMod -> Nc = 0.95;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Average surround
|
||||
lpMod -> F = 1.0;
|
||||
lpMod -> c = 0.69;
|
||||
lpMod -> Nc = 1.0;
|
||||
}
|
||||
|
||||
lpMod -> n = compute_n(lpMod);
|
||||
lpMod -> z = compute_z(lpMod);
|
||||
lpMod -> Nbb = computeNbb(lpMod);
|
||||
lpMod -> FL = computeFL(lpMod);
|
||||
|
||||
if (lpMod -> D == D_CALCULATE) {
|
||||
lpMod -> D = computeD(lpMod);
|
||||
}
|
||||
|
||||
lpMod -> Ncb = lpMod -> Nbb;
|
||||
|
||||
lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite);
|
||||
lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod);
|
||||
lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite);
|
||||
lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod);
|
||||
|
||||
return (cmsHANDLE) lpMod;
|
||||
|
||||
}
|
||||
|
||||
void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel)
|
||||
{
|
||||
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
|
||||
|
||||
if (lpMod) _cmsFree(lpMod ->ContextID, lpMod);
|
||||
}
|
||||
|
||||
|
||||
void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut)
|
||||
{
|
||||
CAM02COLOR clr;
|
||||
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
|
||||
|
||||
_cmsAssert(lpMod != NULL);
|
||||
_cmsAssert(pIn != NULL);
|
||||
_cmsAssert(pOut != NULL);
|
||||
|
||||
clr.XYZ[0] = pIn ->X;
|
||||
clr.XYZ[1] = pIn ->Y;
|
||||
clr.XYZ[2] = pIn ->Z;
|
||||
|
||||
clr = XYZtoCAT02(clr);
|
||||
clr = ChromaticAdaptation(clr, lpMod);
|
||||
clr = CAT02toHPE(clr);
|
||||
clr = NonlinearCompression(clr, lpMod);
|
||||
clr = ComputeCorrelates(clr, lpMod);
|
||||
|
||||
pOut ->J = clr.J;
|
||||
pOut ->C = clr.C;
|
||||
pOut ->h = clr.h;
|
||||
}
|
||||
|
||||
void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut)
|
||||
{
|
||||
CAM02COLOR clr;
|
||||
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
|
||||
|
||||
_cmsAssert(lpMod != NULL);
|
||||
_cmsAssert(pIn != NULL);
|
||||
_cmsAssert(pOut != NULL);
|
||||
|
||||
clr.J = pIn -> J;
|
||||
clr.C = pIn -> C;
|
||||
clr.h = pIn -> h;
|
||||
|
||||
clr = InverseCorrelates(clr, lpMod);
|
||||
clr = InverseNonlinearity(clr, lpMod);
|
||||
clr = HPEtoCAT02(clr);
|
||||
clr = InverseChromaticAdaptation(clr, lpMod);
|
||||
clr = CAT02toXYZ(clr);
|
||||
|
||||
pOut ->X = clr.XYZ[0];
|
||||
pOut ->Y = clr.XYZ[1];
|
||||
pOut ->Z = clr.XYZ[2];
|
||||
}
|
||||
|
||||
2655
thirdparty/liblcms2/src/cmscgats.c
vendored
Normal file
2655
thirdparty/liblcms2/src/cmscgats.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1039
thirdparty/liblcms2/src/cmscnvrt.c
vendored
Normal file
1039
thirdparty/liblcms2/src/cmscnvrt.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
428
thirdparty/liblcms2/src/cmserr.c
vendored
Normal file
428
thirdparty/liblcms2/src/cmserr.c
vendored
Normal file
@@ -0,0 +1,428 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
// I am so tired about incompatibilities on those functions that here are some replacements
|
||||
// that hopefully would be fully portable.
|
||||
|
||||
// compare two strings ignoring case
|
||||
int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2)
|
||||
{
|
||||
register const unsigned char *us1 = (const unsigned char *)s1,
|
||||
*us2 = (const unsigned char *)s2;
|
||||
|
||||
while (toupper(*us1) == toupper(*us2++))
|
||||
if (*us1++ == '\0')
|
||||
return (0);
|
||||
return (toupper(*us1) - toupper(*--us2));
|
||||
}
|
||||
|
||||
// long int because C99 specifies ftell in such way (7.19.9.2)
|
||||
long int CMSEXPORT cmsfilelength(FILE* f)
|
||||
{
|
||||
long int p , n;
|
||||
|
||||
p = ftell(f); // register current file position
|
||||
|
||||
if (fseek(f, 0, SEEK_END) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = ftell(f);
|
||||
fseek(f, p, SEEK_SET); // file position restored
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
// Memory handling ------------------------------------------------------------------
|
||||
//
|
||||
// This is the interface to low-level memory management routines. By default a simple
|
||||
// wrapping to malloc/free/realloc is provided, although there is a limit on the max
|
||||
// amount of memoy that can be reclaimed. This is mostly as a safety feature to
|
||||
// prevent bogus or malintentionated code to allocate huge blocks that otherwise lcms
|
||||
// would never need.
|
||||
|
||||
#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U))
|
||||
|
||||
// User may override this behaviour by using a memory plug-in, which basically replaces
|
||||
// the default memory management functions. In this case, no check is performed and it
|
||||
// is up to the plug-in writter to keep in the safe side. There are only three functions
|
||||
// required to be implemented: malloc, realloc and free, although the user may want to
|
||||
// replace the optional mallocZero, calloc and dup as well.
|
||||
|
||||
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase* Plugin);
|
||||
|
||||
// *********************************************************************************
|
||||
|
||||
// This is the default memory allocation function. It does a very coarse
|
||||
// check of amout of memory, just to prevent exploits
|
||||
static
|
||||
void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size)
|
||||
{
|
||||
if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never allow over maximum
|
||||
|
||||
return (void*) malloc(size);
|
||||
|
||||
cmsUNUSED_PARAMETER(ContextID);
|
||||
}
|
||||
|
||||
// Generic allocate & zero
|
||||
static
|
||||
void* _cmsMallocZeroDefaultFn(cmsContext ContextID, cmsUInt32Number size)
|
||||
{
|
||||
void *pt = _cmsMalloc(ContextID, size);
|
||||
if (pt == NULL) return NULL;
|
||||
|
||||
memset(pt, 0, size);
|
||||
return pt;
|
||||
}
|
||||
|
||||
|
||||
// The default free function. The only check proformed is against NULL pointers
|
||||
static
|
||||
void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr)
|
||||
{
|
||||
// free(NULL) is defined a no-op by C99, therefore it is safe to
|
||||
// avoid the check, but it is here just in case...
|
||||
|
||||
if (Ptr) free(Ptr);
|
||||
|
||||
cmsUNUSED_PARAMETER(ContextID);
|
||||
}
|
||||
|
||||
// The default realloc function. Again it check for exploits. If Ptr is NULL,
|
||||
// realloc behaves the same way as malloc and allocates a new block of size bytes.
|
||||
static
|
||||
void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size)
|
||||
{
|
||||
|
||||
if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never realloc over 512Mb
|
||||
|
||||
return realloc(Ptr, size);
|
||||
|
||||
cmsUNUSED_PARAMETER(ContextID);
|
||||
}
|
||||
|
||||
|
||||
// The default calloc function. Allocates an array of num elements, each one of size bytes
|
||||
// all memory is initialized to zero.
|
||||
static
|
||||
void* _cmsCallocDefaultFn(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)
|
||||
{
|
||||
cmsUInt32Number Total = num * size;
|
||||
|
||||
// Preserve calloc behaviour
|
||||
if (Total == 0) return NULL;
|
||||
|
||||
// Safe check for overflow.
|
||||
if (num >= UINT_MAX / size) return NULL;
|
||||
|
||||
// Check for overflow
|
||||
if (Total < num || Total < size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (Total > MAX_MEMORY_FOR_ALLOC) return NULL; // Never alloc over 512Mb
|
||||
|
||||
return _cmsMallocZero(ContextID, Total);
|
||||
}
|
||||
|
||||
// Generic block duplication
|
||||
static
|
||||
void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number size)
|
||||
{
|
||||
void* mem;
|
||||
|
||||
if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never dup over 512Mb
|
||||
|
||||
mem = _cmsMalloc(ContextID, size);
|
||||
|
||||
if (mem != NULL && Org != NULL)
|
||||
memmove(mem, Org, size);
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
// Pointers to malloc and _cmsFree functions in current environment
|
||||
static void * (* MallocPtr)(cmsContext ContextID, cmsUInt32Number size) = _cmsMallocDefaultFn;
|
||||
static void * (* MallocZeroPtr)(cmsContext ContextID, cmsUInt32Number size) = _cmsMallocZeroDefaultFn;
|
||||
static void (* FreePtr)(cmsContext ContextID, void *Ptr) = _cmsFreeDefaultFn;
|
||||
static void * (* ReallocPtr)(cmsContext ContextID, void *Ptr, cmsUInt32Number NewSize) = _cmsReallocDefaultFn;
|
||||
static void * (* CallocPtr)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)= _cmsCallocDefaultFn;
|
||||
static void * (* DupPtr)(cmsContext ContextID, const void* Org, cmsUInt32Number size) = _cmsDupDefaultFn;
|
||||
|
||||
// Plug-in replacement entry
|
||||
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase *Data)
|
||||
{
|
||||
cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data;
|
||||
|
||||
// NULL forces to reset to defaults
|
||||
if (Data == NULL) {
|
||||
|
||||
MallocPtr = _cmsMallocDefaultFn;
|
||||
MallocZeroPtr= _cmsMallocZeroDefaultFn;
|
||||
FreePtr = _cmsFreeDefaultFn;
|
||||
ReallocPtr = _cmsReallocDefaultFn;
|
||||
CallocPtr = _cmsCallocDefaultFn;
|
||||
DupPtr = _cmsDupDefaultFn;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Check for required callbacks
|
||||
if (Plugin -> MallocPtr == NULL ||
|
||||
Plugin -> FreePtr == NULL ||
|
||||
Plugin -> ReallocPtr == NULL) return FALSE;
|
||||
|
||||
// Set replacement functions
|
||||
MallocPtr = Plugin -> MallocPtr;
|
||||
FreePtr = Plugin -> FreePtr;
|
||||
ReallocPtr = Plugin -> ReallocPtr;
|
||||
|
||||
if (Plugin ->MallocZeroPtr != NULL) MallocZeroPtr = Plugin ->MallocZeroPtr;
|
||||
if (Plugin ->CallocPtr != NULL) CallocPtr = Plugin -> CallocPtr;
|
||||
if (Plugin ->DupPtr != NULL) DupPtr = Plugin -> DupPtr;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Generic allocate
|
||||
void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size)
|
||||
{
|
||||
return MallocPtr(ContextID, size);
|
||||
}
|
||||
|
||||
// Generic allocate & zero
|
||||
void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size)
|
||||
{
|
||||
return MallocZeroPtr(ContextID, size);
|
||||
}
|
||||
|
||||
// Generic calloc
|
||||
void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)
|
||||
{
|
||||
return CallocPtr(ContextID, num, size);
|
||||
}
|
||||
|
||||
// Generic reallocate
|
||||
void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size)
|
||||
{
|
||||
return ReallocPtr(ContextID, Ptr, size);
|
||||
}
|
||||
|
||||
// Generic free memory
|
||||
void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr)
|
||||
{
|
||||
if (Ptr != NULL) FreePtr(ContextID, Ptr);
|
||||
}
|
||||
|
||||
// Generic block duplication
|
||||
void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size)
|
||||
{
|
||||
return DupPtr(ContextID, Org, size);
|
||||
}
|
||||
|
||||
// ********************************************************************************************
|
||||
|
||||
// Sub allocation takes care of many pointers of small size. The memory allocated in
|
||||
// this way have be freed at once. Next function allocates a single chunk for linked list
|
||||
// I prefer this method over realloc due to the big inpact on xput realloc may have if
|
||||
// memory is being swapped to disk. This approach is safer (although thats not true on any platform)
|
||||
static
|
||||
_cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial)
|
||||
{
|
||||
_cmsSubAllocator_chunk* chunk;
|
||||
|
||||
// Create the container
|
||||
chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk));
|
||||
if (chunk == NULL) return NULL;
|
||||
|
||||
// Initialize values
|
||||
chunk ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, Initial);
|
||||
if (chunk ->Block == NULL) {
|
||||
|
||||
// Something went wrong
|
||||
_cmsFree(ContextID, chunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 20K by default
|
||||
if (Initial == 0)
|
||||
Initial = 20*1024;
|
||||
|
||||
chunk ->BlockSize = Initial;
|
||||
chunk ->Used = 0;
|
||||
chunk ->next = NULL;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
// The suballocated is nothing but a pointer to the first element in the list. We also keep
|
||||
// the thread ID in this structure.
|
||||
_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial)
|
||||
{
|
||||
_cmsSubAllocator* sub;
|
||||
|
||||
// Create the container
|
||||
sub = (_cmsSubAllocator*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator));
|
||||
if (sub == NULL) return NULL;
|
||||
|
||||
sub ->ContextID = ContextID;
|
||||
|
||||
sub ->h = _cmsCreateSubAllocChunk(ContextID, Initial);
|
||||
if (sub ->h == NULL) {
|
||||
_cmsFree(ContextID, sub);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
|
||||
// Get rid of whole linked list
|
||||
void _cmsSubAllocDestroy(_cmsSubAllocator* sub)
|
||||
{
|
||||
_cmsSubAllocator_chunk *chunk, *n;
|
||||
|
||||
for (chunk = sub ->h; chunk != NULL; chunk = n) {
|
||||
|
||||
n = chunk->next;
|
||||
if (chunk->Block != NULL) _cmsFree(sub ->ContextID, chunk->Block);
|
||||
_cmsFree(sub ->ContextID, chunk);
|
||||
}
|
||||
|
||||
// Free the header
|
||||
_cmsFree(sub ->ContextID, sub);
|
||||
}
|
||||
|
||||
|
||||
// Get a pointer to small memory block.
|
||||
void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size)
|
||||
{
|
||||
cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used;
|
||||
cmsUInt8Number* ptr;
|
||||
|
||||
size = _cmsALIGNLONG(size);
|
||||
|
||||
// Check for memory. If there is no room, allocate a new chunk of double memory size.
|
||||
if (size > Free) {
|
||||
|
||||
_cmsSubAllocator_chunk* chunk;
|
||||
cmsUInt32Number newSize;
|
||||
|
||||
newSize = sub -> h ->BlockSize * 2;
|
||||
if (newSize < size) newSize = size;
|
||||
|
||||
chunk = _cmsCreateSubAllocChunk(sub -> ContextID, newSize);
|
||||
if (chunk == NULL) return NULL;
|
||||
|
||||
// Link list
|
||||
chunk ->next = sub ->h;
|
||||
sub ->h = chunk;
|
||||
|
||||
}
|
||||
|
||||
ptr = sub -> h ->Block + sub -> h ->Used;
|
||||
sub -> h -> Used += size;
|
||||
|
||||
return (void*) ptr;
|
||||
}
|
||||
|
||||
// Error logging ******************************************************************
|
||||
|
||||
// There is no error handling at all. When a funtion fails, it returns proper value.
|
||||
// For example, all create functions does return NULL on failure. Other return FALSE
|
||||
// It may be interesting, for the developer, to know why the function is failing.
|
||||
// for that reason, lcms2 does offer a logging function. This function does recive
|
||||
// a ENGLISH string with some clues on what is going wrong. You can show this
|
||||
// info to the end user, or just create some sort of log.
|
||||
// The logging function should NOT terminate the program, as this obviously can leave
|
||||
// resources. It is the programmer's responsability to check each function return code
|
||||
// to make sure it didn't fail.
|
||||
|
||||
// Error messages are limited to MAX_ERROR_MESSAGE_LEN
|
||||
|
||||
#define MAX_ERROR_MESSAGE_LEN 1024
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------
|
||||
|
||||
// This is our default log error
|
||||
static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text);
|
||||
|
||||
// The current handler in actual environment
|
||||
static cmsLogErrorHandlerFunction LogErrorHandler = DefaultLogErrorHandlerFunction;
|
||||
|
||||
// The default error logger does nothing.
|
||||
static
|
||||
void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
|
||||
{
|
||||
// fprintf(stderr, "[lcms]: %s\n", Text);
|
||||
// fflush(stderr);
|
||||
|
||||
cmsUNUSED_PARAMETER(ContextID);
|
||||
cmsUNUSED_PARAMETER(ErrorCode);
|
||||
cmsUNUSED_PARAMETER(Text);
|
||||
}
|
||||
|
||||
// Change log error
|
||||
void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn)
|
||||
{
|
||||
if (Fn == NULL)
|
||||
LogErrorHandler = DefaultLogErrorHandlerFunction;
|
||||
else
|
||||
LogErrorHandler = Fn;
|
||||
}
|
||||
|
||||
// Log an error
|
||||
// ErrorText is a text holding an english description of error.
|
||||
void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...)
|
||||
{
|
||||
va_list args;
|
||||
char Buffer[MAX_ERROR_MESSAGE_LEN];
|
||||
|
||||
va_start(args, ErrorText);
|
||||
vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args);
|
||||
va_end(args);
|
||||
|
||||
// Call handler
|
||||
LogErrorHandler(ContextID, ErrorCode, Buffer);
|
||||
}
|
||||
|
||||
// Utility function to print signatures
|
||||
void _cmsTagSignature2String(char String[5], cmsTagSignature sig)
|
||||
{
|
||||
cmsUInt32Number be;
|
||||
|
||||
// Convert to big endian
|
||||
be = _cmsAdjustEndianess32((cmsUInt32Number) sig);
|
||||
|
||||
// Move chars
|
||||
memmove(String, &be, 4);
|
||||
|
||||
// Make sure of terminator
|
||||
String[4] = 0;
|
||||
}
|
||||
|
||||
1138
thirdparty/liblcms2/src/cmsgamma.c
vendored
Normal file
1138
thirdparty/liblcms2/src/cmsgamma.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
591
thirdparty/liblcms2/src/cmsgmt.c
vendored
Normal file
591
thirdparty/liblcms2/src/cmsgmt.c
vendored
Normal file
@@ -0,0 +1,591 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
|
||||
// Auxiliar: append a Lab identity after the given sequence of profiles
|
||||
// and return the transform. Lab profile is closed, rest of profiles are kept open.
|
||||
cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID,
|
||||
cmsUInt32Number nProfiles,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsUInt32Number OutputFormat,
|
||||
const cmsUInt32Number Intents[],
|
||||
const cmsHPROFILE hProfiles[],
|
||||
const cmsBool BPC[],
|
||||
const cmsFloat64Number AdaptationStates[],
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
cmsHTRANSFORM xform;
|
||||
cmsHPROFILE hLab;
|
||||
cmsHPROFILE ProfileList[256];
|
||||
cmsBool BPCList[256];
|
||||
cmsFloat64Number AdaptationList[256];
|
||||
cmsUInt32Number IntentList[256];
|
||||
cmsUInt32Number i;
|
||||
|
||||
// This is a rather big number and there is no need of dynamic memory
|
||||
// since we are adding a profile, 254 + 1 = 255 and this is the limit
|
||||
if (nProfiles > 254) return NULL;
|
||||
|
||||
// The output space
|
||||
hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
|
||||
if (hLab == NULL) return NULL;
|
||||
|
||||
// Create a copy of parameters
|
||||
for (i=0; i < nProfiles; i++) {
|
||||
|
||||
ProfileList[i] = hProfiles[i];
|
||||
BPCList[i] = BPC[i];
|
||||
AdaptationList[i] = AdaptationStates[i];
|
||||
IntentList[i] = Intents[i];
|
||||
}
|
||||
|
||||
// Place Lab identity at chain's end.
|
||||
ProfileList[nProfiles] = hLab;
|
||||
BPCList[nProfiles] = 0;
|
||||
AdaptationList[nProfiles] = 1.0;
|
||||
IntentList[nProfiles] = INTENT_RELATIVE_COLORIMETRIC;
|
||||
|
||||
// Create the transform
|
||||
xform = cmsCreateExtendedTransform(ContextID, nProfiles + 1, ProfileList,
|
||||
BPCList,
|
||||
IntentList,
|
||||
AdaptationList,
|
||||
NULL, 0,
|
||||
InputFormat,
|
||||
OutputFormat,
|
||||
dwFlags);
|
||||
|
||||
cmsCloseProfile(hLab);
|
||||
|
||||
return xform;
|
||||
}
|
||||
|
||||
|
||||
// Compute K -> L* relationship. Flags may include black point compensation. In this case,
|
||||
// the relationship is assumed from the profile with BPC to a black point zero.
|
||||
static
|
||||
cmsToneCurve* ComputeKToLstar(cmsContext ContextID,
|
||||
cmsUInt32Number nPoints,
|
||||
cmsUInt32Number nProfiles,
|
||||
const cmsUInt32Number Intents[],
|
||||
const cmsHPROFILE hProfiles[],
|
||||
const cmsBool BPC[],
|
||||
const cmsFloat64Number AdaptationStates[],
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
cmsToneCurve* out = NULL;
|
||||
cmsUInt32Number i;
|
||||
cmsHTRANSFORM xform;
|
||||
cmsCIELab Lab;
|
||||
cmsFloat32Number cmyk[4];
|
||||
cmsFloat32Number* SampledPoints;
|
||||
|
||||
xform = _cmsChain2Lab(ContextID, nProfiles, TYPE_CMYK_FLT, TYPE_Lab_DBL, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
|
||||
if (xform == NULL) return NULL;
|
||||
|
||||
SampledPoints = (cmsFloat32Number*) _cmsCalloc(ContextID, nPoints, sizeof(cmsFloat32Number));
|
||||
if (SampledPoints == NULL) goto Error;
|
||||
|
||||
for (i=0; i < nPoints; i++) {
|
||||
|
||||
cmyk[0] = 0;
|
||||
cmyk[1] = 0;
|
||||
cmyk[2] = 0;
|
||||
cmyk[3] = (cmsFloat32Number) ((i * 100.0) / (nPoints-1));
|
||||
|
||||
cmsDoTransform(xform, cmyk, &Lab, 1);
|
||||
SampledPoints[i]= (cmsFloat32Number) (1.0 - Lab.L / 100.0); // Negate K for easier operation
|
||||
}
|
||||
|
||||
out = cmsBuildTabulatedToneCurveFloat(ContextID, nPoints, SampledPoints);
|
||||
|
||||
Error:
|
||||
|
||||
cmsDeleteTransform(xform);
|
||||
if (SampledPoints) _cmsFree(ContextID, SampledPoints);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// Compute Black tone curve on a CMYK -> CMYK transform. This is done by
|
||||
// using the proof direction on both profiles to find K->L* relationship
|
||||
// then joining both curves. dwFlags may include black point compensation.
|
||||
cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID,
|
||||
cmsUInt32Number nPoints,
|
||||
cmsUInt32Number nProfiles,
|
||||
const cmsUInt32Number Intents[],
|
||||
const cmsHPROFILE hProfiles[],
|
||||
const cmsBool BPC[],
|
||||
const cmsFloat64Number AdaptationStates[],
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
cmsToneCurve *in, *out, *KTone;
|
||||
|
||||
// Make sure CMYK -> CMYK
|
||||
if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||
|
||||
cmsGetColorSpace(hProfiles[nProfiles-1])!= cmsSigCmykData) return NULL;
|
||||
|
||||
|
||||
// Make sure last is an output profile
|
||||
if (cmsGetDeviceClass(hProfiles[nProfiles - 1]) != cmsSigOutputClass) return NULL;
|
||||
|
||||
// Create individual curves. BPC works also as each K to L* is
|
||||
// computed as a BPC to zero black point in case of L*
|
||||
in = ComputeKToLstar(ContextID, nPoints, nProfiles - 1, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
|
||||
if (in == NULL) return NULL;
|
||||
|
||||
out = ComputeKToLstar(ContextID, nPoints, 1,
|
||||
Intents + (nProfiles - 1),
|
||||
hProfiles + (nProfiles - 1),
|
||||
BPC + (nProfiles - 1),
|
||||
AdaptationStates + (nProfiles - 1),
|
||||
dwFlags);
|
||||
if (out == NULL) {
|
||||
cmsFreeToneCurve(in);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Build the relationship. This effectively limits the maximum accuracy to 16 bits, but
|
||||
// since this is used on black-preserving LUTs, we are not loosing accuracy in any case
|
||||
KTone = cmsJoinToneCurve(ContextID, in, out, nPoints);
|
||||
|
||||
// Get rid of components
|
||||
cmsFreeToneCurve(in); cmsFreeToneCurve(out);
|
||||
|
||||
// Something went wrong...
|
||||
if (KTone == NULL) return NULL;
|
||||
|
||||
// Make sure it is monotonic
|
||||
if (!cmsIsToneCurveMonotonic(KTone)) {
|
||||
|
||||
cmsFreeToneCurve(KTone);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return KTone;
|
||||
}
|
||||
|
||||
|
||||
// Gamut LUT Creation -----------------------------------------------------------------------------------------
|
||||
|
||||
// Used by gamut & softproofing
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmsHTRANSFORM hInput; // From whatever input color space. 16 bits to DBL
|
||||
cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back
|
||||
cmsFloat64Number Thereshold; // The thereshold after which is considered out of gamut
|
||||
|
||||
} GAMUTCHAIN;
|
||||
|
||||
// This sampler does compute gamut boundaries by comparing original
|
||||
// values with a transform going back and forth. Values above ERR_THERESHOLD
|
||||
// of maximum are considered out of gamut.
|
||||
|
||||
#define ERR_THERESHOLD 5
|
||||
|
||||
|
||||
static
|
||||
int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
|
||||
{
|
||||
GAMUTCHAIN* t = (GAMUTCHAIN* ) Cargo;
|
||||
cmsCIELab LabIn1, LabOut1;
|
||||
cmsCIELab LabIn2, LabOut2;
|
||||
cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS];
|
||||
cmsFloat64Number dE1, dE2, ErrorRatio;
|
||||
|
||||
// Assume in-gamut by default.
|
||||
dE1 = 0.;
|
||||
dE2 = 0;
|
||||
ErrorRatio = 1.0;
|
||||
|
||||
// Convert input to Lab
|
||||
if (t -> hInput != NULL)
|
||||
cmsDoTransform(t -> hInput, In, &LabIn1, 1);
|
||||
|
||||
// converts from PCS to colorant. This always
|
||||
// does return in-gamut values,
|
||||
cmsDoTransform(t -> hForward, &LabIn1, Proof, 1);
|
||||
|
||||
// Now, do the inverse, from colorant to PCS.
|
||||
cmsDoTransform(t -> hReverse, Proof, &LabOut1, 1);
|
||||
|
||||
memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab));
|
||||
|
||||
// Try again, but this time taking Check as input
|
||||
cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1);
|
||||
cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1);
|
||||
|
||||
// Take difference of direct value
|
||||
dE1 = cmsDeltaE(&LabIn1, &LabOut1);
|
||||
|
||||
// Take difference of converted value
|
||||
dE2 = cmsDeltaE(&LabIn2, &LabOut2);
|
||||
|
||||
|
||||
// if dE1 is small and dE2 is small, value is likely to be in gamut
|
||||
if (dE1 < t->Thereshold && dE2 < t->Thereshold)
|
||||
Out[0] = 0;
|
||||
else {
|
||||
|
||||
// if dE1 is small and dE2 is big, undefined. Assume in gamut
|
||||
if (dE1 < t->Thereshold && dE2 > t->Thereshold)
|
||||
Out[0] = 0;
|
||||
else
|
||||
// dE1 is big and dE2 is small, clearly out of gamut
|
||||
if (dE1 > t->Thereshold && dE2 < t->Thereshold)
|
||||
Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5);
|
||||
else {
|
||||
|
||||
// dE1 is big and dE2 is also big, could be due to perceptual mapping
|
||||
// so take error ratio
|
||||
if (dE2 == 0.0)
|
||||
ErrorRatio = dE1;
|
||||
else
|
||||
ErrorRatio = dE1 / dE2;
|
||||
|
||||
if (ErrorRatio > t->Thereshold)
|
||||
Out[0] = (cmsUInt16Number) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5);
|
||||
else
|
||||
Out[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Does compute a gamut LUT going back and forth across pcs -> relativ. colorimetric intent -> pcs
|
||||
// the dE obtained is then annotated on the LUT. Values truely out of gamut are clipped to dE = 0xFFFE
|
||||
// and values changed are supposed to be handled by any gamut remapping, so, are out of gamut as well.
|
||||
//
|
||||
// **WARNING: This algorithm does assume that gamut remapping algorithms does NOT move in-gamut colors,
|
||||
// of course, many perceptual and saturation intents does not work in such way, but relativ. ones should.
|
||||
|
||||
cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID,
|
||||
cmsHPROFILE hProfiles[],
|
||||
cmsBool BPC[],
|
||||
cmsUInt32Number Intents[],
|
||||
cmsFloat64Number AdaptationStates[],
|
||||
cmsUInt32Number nGamutPCSposition,
|
||||
cmsHPROFILE hGamut)
|
||||
{
|
||||
cmsHPROFILE hLab;
|
||||
cmsPipeline* Gamut;
|
||||
cmsStage* CLUT;
|
||||
cmsUInt32Number dwFormat;
|
||||
GAMUTCHAIN Chain;
|
||||
int nChannels, nGridpoints;
|
||||
cmsColorSpaceSignature ColorSpace;
|
||||
cmsUInt32Number i;
|
||||
cmsHPROFILE ProfileList[256];
|
||||
cmsBool BPCList[256];
|
||||
cmsFloat64Number AdaptationList[256];
|
||||
cmsUInt32Number IntentList[256];
|
||||
|
||||
memset(&Chain, 0, sizeof(GAMUTCHAIN));
|
||||
|
||||
|
||||
if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) {
|
||||
cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
|
||||
if (hLab == NULL) return NULL;
|
||||
|
||||
|
||||
// The figure of merit. On matrix-shaper profiles, should be almost zero as
|
||||
// the conversion is pretty exact. On LUT based profiles, different resolutions
|
||||
// of input and output CLUT may result in differences.
|
||||
|
||||
if (cmsIsMatrixShaper(hGamut)) {
|
||||
|
||||
Chain.Thereshold = 1.0;
|
||||
}
|
||||
else {
|
||||
Chain.Thereshold = ERR_THERESHOLD;
|
||||
}
|
||||
|
||||
|
||||
// Create a copy of parameters
|
||||
for (i=0; i < nGamutPCSposition; i++) {
|
||||
ProfileList[i] = hProfiles[i];
|
||||
BPCList[i] = BPC[i];
|
||||
AdaptationList[i] = AdaptationStates[i];
|
||||
IntentList[i] = Intents[i];
|
||||
}
|
||||
|
||||
// Fill Lab identity
|
||||
ProfileList[nGamutPCSposition] = hLab;
|
||||
BPCList[nGamutPCSposition] = 0;
|
||||
AdaptationList[nGamutPCSposition] = 1.0;
|
||||
Intents[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC;
|
||||
|
||||
|
||||
ColorSpace = cmsGetColorSpace(hGamut);
|
||||
|
||||
nChannels = cmsChannelsOf(ColorSpace);
|
||||
nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);
|
||||
dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
|
||||
|
||||
// 16 bits to Lab double
|
||||
Chain.hInput = cmsCreateExtendedTransform(ContextID,
|
||||
nGamutPCSposition + 1,
|
||||
ProfileList,
|
||||
BPCList,
|
||||
Intents,
|
||||
AdaptationList,
|
||||
NULL, 0,
|
||||
dwFormat, TYPE_Lab_DBL,
|
||||
cmsFLAGS_NOCACHE);
|
||||
|
||||
|
||||
// Does create the forward step. Lab double to device
|
||||
dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
|
||||
Chain.hForward = cmsCreateTransformTHR(ContextID,
|
||||
hLab, TYPE_Lab_DBL,
|
||||
hGamut, dwFormat,
|
||||
INTENT_RELATIVE_COLORIMETRIC,
|
||||
cmsFLAGS_NOCACHE);
|
||||
|
||||
// Does create the backwards step
|
||||
Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat,
|
||||
hLab, TYPE_Lab_DBL,
|
||||
INTENT_RELATIVE_COLORIMETRIC,
|
||||
cmsFLAGS_NOCACHE);
|
||||
|
||||
|
||||
// All ok?
|
||||
if (Chain.hForward && Chain.hReverse) {
|
||||
|
||||
// Go on, try to compute gamut LUT from PCS. This consist on a single channel containing
|
||||
// dE when doing a transform back and forth on the colorimetric intent.
|
||||
|
||||
Gamut = cmsPipelineAlloc(ContextID, 3, 1);
|
||||
|
||||
if (Gamut != NULL) {
|
||||
|
||||
CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL);
|
||||
cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT);
|
||||
|
||||
cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
Gamut = NULL; // Didn't work...
|
||||
|
||||
// Free all needed stuff.
|
||||
if (Chain.hInput) cmsDeleteTransform(Chain.hInput);
|
||||
if (Chain.hForward) cmsDeleteTransform(Chain.hForward);
|
||||
if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse);
|
||||
if (hLab) cmsCloseProfile(hLab);
|
||||
|
||||
// And return computed hull
|
||||
return Gamut;
|
||||
}
|
||||
|
||||
// Total Area Coverage estimation ----------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
cmsUInt32Number nOutputChans;
|
||||
cmsHTRANSFORM hRoundTrip;
|
||||
cmsFloat32Number MaxTAC;
|
||||
cmsFloat32Number MaxInput[cmsMAXCHANNELS];
|
||||
|
||||
} cmsTACestimator;
|
||||
|
||||
|
||||
// This callback just accounts the maximum ink dropped in the given node. It does not populate any
|
||||
// memory, as the destination table is NULL. Its only purpose it to know the global maximum.
|
||||
static
|
||||
int EstimateTAC(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo)
|
||||
{
|
||||
cmsTACestimator* bp = (cmsTACestimator*) Cargo;
|
||||
cmsFloat32Number RoundTrip[cmsMAXCHANNELS];
|
||||
cmsUInt32Number i;
|
||||
cmsFloat32Number Sum;
|
||||
|
||||
|
||||
// Evaluate the xform
|
||||
cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1);
|
||||
|
||||
// All all amounts of ink
|
||||
for (Sum=0, i=0; i < bp ->nOutputChans; i++)
|
||||
Sum += RoundTrip[i];
|
||||
|
||||
// If above maximum, keep track of input values
|
||||
if (Sum > bp ->MaxTAC) {
|
||||
|
||||
bp ->MaxTAC = Sum;
|
||||
|
||||
for (i=0; i < bp ->nOutputChans; i++) {
|
||||
bp ->MaxInput[i] = In[i];
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
cmsUNUSED_PARAMETER(Out);
|
||||
}
|
||||
|
||||
|
||||
// Detect Total area coverage of the profile
|
||||
cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsTACestimator bp;
|
||||
cmsUInt32Number dwFormatter;
|
||||
cmsUInt32Number GridPoints[MAX_INPUT_DIMENSIONS];
|
||||
cmsHPROFILE hLab;
|
||||
cmsContext ContextID = cmsGetProfileContextID(hProfile);
|
||||
|
||||
// TAC only works on output profiles
|
||||
if (cmsGetDeviceClass(hProfile) != cmsSigOutputClass) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create a fake formatter for result
|
||||
dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE);
|
||||
|
||||
bp.nOutputChans = T_CHANNELS(dwFormatter);
|
||||
bp.MaxTAC = 0; // Initial TAC is 0
|
||||
|
||||
// for safety
|
||||
if (bp.nOutputChans >= cmsMAXCHANNELS) return 0;
|
||||
|
||||
hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
|
||||
if (hLab == NULL) return 0;
|
||||
// Setup a roundtrip on perceptual intent in output profile for TAC estimation
|
||||
bp.hRoundTrip = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_16,
|
||||
hProfile, dwFormatter, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
|
||||
|
||||
cmsCloseProfile(hLab);
|
||||
if (bp.hRoundTrip == NULL) return 0;
|
||||
|
||||
// For L* we only need black and white. For C* we need many points
|
||||
GridPoints[0] = 6;
|
||||
GridPoints[1] = 74;
|
||||
GridPoints[2] = 74;
|
||||
|
||||
|
||||
if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) {
|
||||
bp.MaxTAC = 0;
|
||||
}
|
||||
|
||||
cmsDeleteTransform(bp.hRoundTrip);
|
||||
|
||||
// Results in %
|
||||
return bp.MaxTAC;
|
||||
}
|
||||
|
||||
|
||||
// Carefully, clamp on CIELab space.
|
||||
|
||||
cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab,
|
||||
double amax, double amin,
|
||||
double bmax, double bmin)
|
||||
{
|
||||
|
||||
// Whole Luma surface to zero
|
||||
|
||||
if (Lab -> L < 0) {
|
||||
|
||||
Lab-> L = Lab->a = Lab-> b = 0.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Clamp white, DISCARD HIGHLIGHTS. This is done
|
||||
// in such way because icc spec doesn't allow the
|
||||
// use of L>100 as a highlight means.
|
||||
|
||||
if (Lab->L > 100)
|
||||
Lab -> L = 100;
|
||||
|
||||
// Check out gamut prism, on a, b faces
|
||||
|
||||
if (Lab -> a < amin || Lab->a > amax||
|
||||
Lab -> b < bmin || Lab->b > bmax) {
|
||||
|
||||
cmsCIELCh LCh;
|
||||
double h, slope;
|
||||
|
||||
// Falls outside a, b limits. Transports to LCh space,
|
||||
// and then do the clipping
|
||||
|
||||
|
||||
if (Lab -> a == 0.0) { // Is hue exactly 90?
|
||||
|
||||
// atan will not work, so clamp here
|
||||
Lab -> b = Lab->b < 0 ? bmin : bmax;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsLab2LCh(&LCh, Lab);
|
||||
|
||||
slope = Lab -> b / Lab -> a;
|
||||
h = LCh.h;
|
||||
|
||||
// There are 4 zones
|
||||
|
||||
if ((h >= 0. && h < 45.) ||
|
||||
(h >= 315 && h <= 360.)) {
|
||||
|
||||
// clip by amax
|
||||
Lab -> a = amax;
|
||||
Lab -> b = amax * slope;
|
||||
}
|
||||
else
|
||||
if (h >= 45. && h < 135.)
|
||||
{
|
||||
// clip by bmax
|
||||
Lab -> b = bmax;
|
||||
Lab -> a = bmax / slope;
|
||||
}
|
||||
else
|
||||
if (h >= 135. && h < 225.) {
|
||||
// clip by amin
|
||||
Lab -> a = amin;
|
||||
Lab -> b = amin * slope;
|
||||
|
||||
}
|
||||
else
|
||||
if (h >= 225. && h < 315.) {
|
||||
// clip by bmin
|
||||
Lab -> b = bmin;
|
||||
Lab -> a = bmin / slope;
|
||||
}
|
||||
else {
|
||||
cmsSignalError(0, cmsERROR_RANGE, "Invalid angle");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
1463
thirdparty/liblcms2/src/cmsintrp.c
vendored
Normal file
1463
thirdparty/liblcms2/src/cmsintrp.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1720
thirdparty/liblcms2/src/cmsio0.c
vendored
Normal file
1720
thirdparty/liblcms2/src/cmsio0.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
760
thirdparty/liblcms2/src/cmsio1.c
vendored
Normal file
760
thirdparty/liblcms2/src/cmsio1.c
vendored
Normal file
@@ -0,0 +1,760 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
// Read tags using low-level functions, provides necessary glue code to adapt versions, etc.
|
||||
|
||||
// LUT tags
|
||||
static const cmsTagSignature Device2PCS16[] = {cmsSigAToB0Tag, // Perceptual
|
||||
cmsSigAToB1Tag, // Relative colorimetric
|
||||
cmsSigAToB2Tag, // Saturation
|
||||
cmsSigAToB1Tag }; // Absolute colorimetric
|
||||
|
||||
static const cmsTagSignature Device2PCSFloat[] = {cmsSigDToB0Tag, // Perceptual
|
||||
cmsSigDToB1Tag, // Relative colorimetric
|
||||
cmsSigDToB2Tag, // Saturation
|
||||
cmsSigDToB3Tag }; // Absolute colorimetric
|
||||
|
||||
static const cmsTagSignature PCS2Device16[] = {cmsSigBToA0Tag, // Perceptual
|
||||
cmsSigBToA1Tag, // Relative colorimetric
|
||||
cmsSigBToA2Tag, // Saturation
|
||||
cmsSigBToA1Tag }; // Absolute colorimetric
|
||||
|
||||
static const cmsTagSignature PCS2DeviceFloat[] = {cmsSigBToD0Tag, // Perceptual
|
||||
cmsSigBToD1Tag, // Relative colorimetric
|
||||
cmsSigBToD2Tag, // Saturation
|
||||
cmsSigBToD3Tag }; // Absolute colorimetric
|
||||
|
||||
|
||||
// Factors to convert from 1.15 fixed point to 0..1.0 range and vice-versa
|
||||
#define InpAdj (1.0/MAX_ENCODEABLE_XYZ) // (65536.0/(65535.0*2.0))
|
||||
#define OutpAdj (MAX_ENCODEABLE_XYZ) // ((2.0*65535.0)/65536.0)
|
||||
|
||||
// Several resources for gray conversions.
|
||||
static const cmsFloat64Number GrayInputMatrix[] = { (InpAdj*cmsD50X), (InpAdj*cmsD50Y), (InpAdj*cmsD50Z) };
|
||||
static const cmsFloat64Number OneToThreeInputMatrix[] = { 1, 1, 1 };
|
||||
static const cmsFloat64Number PickYMatrix[] = { 0, (OutpAdj*cmsD50Y), 0 };
|
||||
static const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
|
||||
|
||||
// Get a media white point fixing some issues found in certain old profiles
|
||||
cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsCIEXYZ* Tag;
|
||||
|
||||
_cmsAssert(Dest != NULL);
|
||||
|
||||
Tag = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag);
|
||||
|
||||
// If no wp, take D50
|
||||
if (Tag == NULL) {
|
||||
*Dest = *cmsD50_XYZ();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// V2 display profiles should give D50
|
||||
if (cmsGetEncodedICCversion(hProfile) < 0x4000000) {
|
||||
|
||||
if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) {
|
||||
*Dest = *cmsD50_XYZ();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// All seems ok
|
||||
*Dest = *Tag;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Chromatic adaptation matrix. Fix some issues as well
|
||||
cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsMAT3* Tag;
|
||||
|
||||
_cmsAssert(Dest != NULL);
|
||||
|
||||
Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag);
|
||||
|
||||
if (Tag != NULL) {
|
||||
|
||||
*Dest = *Tag;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// No CHAD available, default it to identity
|
||||
_cmsMAT3identity(Dest);
|
||||
|
||||
// V2 display profiles should give D50
|
||||
if (cmsGetEncodedICCversion(hProfile) < 0x4000000) {
|
||||
|
||||
if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) {
|
||||
|
||||
cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag);
|
||||
|
||||
if (White == NULL) {
|
||||
|
||||
_cmsMAT3identity(Dest);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return _cmsAdaptationMatrix(Dest, NULL, cmsD50_XYZ(), White);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Auxiliar, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper
|
||||
static
|
||||
cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue;
|
||||
|
||||
_cmsAssert(r != NULL);
|
||||
|
||||
PtrRed = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag);
|
||||
PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag);
|
||||
PtrBlue = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag);
|
||||
|
||||
if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL)
|
||||
return FALSE;
|
||||
|
||||
_cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X, PtrBlue -> X);
|
||||
_cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y, PtrBlue -> Y);
|
||||
_cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z, PtrBlue -> Z);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Gray input pipeline
|
||||
static
|
||||
cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsToneCurve *GrayTRC;
|
||||
cmsPipeline* Lut;
|
||||
cmsContext ContextID = cmsGetProfileContextID(hProfile);
|
||||
|
||||
GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag);
|
||||
if (GrayTRC == NULL) return NULL;
|
||||
|
||||
Lut = cmsPipelineAlloc(ContextID, 1, 3);
|
||||
if (Lut == NULL) return NULL;
|
||||
|
||||
if (cmsGetPCS(hProfile) == cmsSigLabData) {
|
||||
|
||||
// In this case we implement the profile as an identity matrix plus 3 tone curves
|
||||
cmsUInt16Number Zero[2] = { 0x8080, 0x8080 };
|
||||
cmsToneCurve* EmptyTab;
|
||||
cmsToneCurve* LabCurves[3];
|
||||
|
||||
EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
|
||||
|
||||
if (EmptyTab == NULL) {
|
||||
|
||||
cmsPipelineFree(Lut);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LabCurves[0] = GrayTRC;
|
||||
LabCurves[1] = EmptyTab;
|
||||
LabCurves[2] = EmptyTab;
|
||||
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL));
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves));
|
||||
|
||||
cmsFreeToneCurve(EmptyTab);
|
||||
|
||||
}
|
||||
else {
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC));
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL));
|
||||
}
|
||||
|
||||
return Lut;
|
||||
}
|
||||
|
||||
// RGB Matrix shaper
|
||||
static
|
||||
cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsPipeline* Lut;
|
||||
cmsMAT3 Mat;
|
||||
cmsToneCurve *Shapes[3];
|
||||
cmsContext ContextID = cmsGetProfileContextID(hProfile);
|
||||
int i, j;
|
||||
|
||||
if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL;
|
||||
|
||||
// XYZ PCS in encoded in 1.15 format, and the matrix output comes in 0..0xffff range, so
|
||||
// we need to adjust the output by a factor of (0x10000/0xffff) to put data in
|
||||
// a 1.16 range, and then a >> 1 to obtain 1.15. The total factor is (65536.0)/(65535.0*2)
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
for (j=0; j < 3; j++)
|
||||
Mat.v[i].n[j] *= InpAdj;
|
||||
|
||||
|
||||
Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag);
|
||||
Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag);
|
||||
Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag);
|
||||
|
||||
if (!Shapes[0] || !Shapes[1] || !Shapes[2])
|
||||
return NULL;
|
||||
|
||||
Lut = cmsPipelineAlloc(ContextID, 3, 3);
|
||||
if (Lut != NULL) {
|
||||
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes));
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL));
|
||||
}
|
||||
|
||||
return Lut;
|
||||
}
|
||||
|
||||
// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc
|
||||
// is adjusted here in order to create a LUT that takes care of all those details
|
||||
cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent)
|
||||
{
|
||||
cmsTagTypeSignature OriginalType;
|
||||
cmsTagSignature tag16 = Device2PCS16[Intent];
|
||||
cmsTagSignature tagFloat = Device2PCSFloat[Intent];
|
||||
cmsContext ContextID = cmsGetProfileContextID(hProfile);
|
||||
|
||||
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
|
||||
|
||||
// Floating point LUT are always V4, so no adjustment is required
|
||||
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
|
||||
}
|
||||
|
||||
// Revert to perceptual if no tag is found
|
||||
if (!cmsIsTag(hProfile, tag16)) {
|
||||
tag16 = Device2PCS16[0];
|
||||
}
|
||||
|
||||
if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
|
||||
|
||||
// Check profile version and LUT type. Do the necessary adjustments if needed
|
||||
|
||||
// First read the tag
|
||||
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
|
||||
if (Lut == NULL) return NULL;
|
||||
|
||||
// After reading it, we have now info about the original type
|
||||
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
|
||||
|
||||
// The profile owns the Lut, so we need to copy it
|
||||
Lut = cmsPipelineDup(Lut);
|
||||
|
||||
// We need to adjust data only for Lab16 on output
|
||||
if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
|
||||
return Lut;
|
||||
|
||||
// Add a matrix for conversion V2 to V4 Lab PCS
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID));
|
||||
return Lut;
|
||||
}
|
||||
|
||||
// Lut was not found, try to create a matrix-shaper
|
||||
|
||||
// Check if this is a grayscale profile.
|
||||
if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
|
||||
|
||||
// if so, build appropiate conversion tables.
|
||||
// The tables are the PCS iluminant, scaled across GrayTRC
|
||||
return BuildGrayInputMatrixPipeline(hProfile);
|
||||
}
|
||||
|
||||
// Not gray, create a normal matrix-shaper
|
||||
return BuildRGBInputMatrixShaper(hProfile);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Gray output pipeline.
|
||||
// XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be
|
||||
// given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve.
|
||||
// The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well.
|
||||
|
||||
static
|
||||
cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsToneCurve *GrayTRC, *RevGrayTRC;
|
||||
cmsPipeline* Lut;
|
||||
cmsContext ContextID = cmsGetProfileContextID(hProfile);
|
||||
|
||||
GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag);
|
||||
if (GrayTRC == NULL) return NULL;
|
||||
|
||||
RevGrayTRC = cmsReverseToneCurve(GrayTRC);
|
||||
if (RevGrayTRC == NULL) return NULL;
|
||||
|
||||
Lut = cmsPipelineAlloc(ContextID, 3, 1);
|
||||
if (Lut == NULL) {
|
||||
cmsFreeToneCurve(RevGrayTRC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cmsGetPCS(hProfile) == cmsSigLabData) {
|
||||
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL));
|
||||
}
|
||||
else {
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL));
|
||||
}
|
||||
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC));
|
||||
cmsFreeToneCurve(RevGrayTRC);
|
||||
|
||||
return Lut;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static
|
||||
cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsPipeline* Lut;
|
||||
cmsToneCurve *Shapes[3], *InvShapes[3];
|
||||
cmsMAT3 Mat, Inv;
|
||||
int i, j;
|
||||
cmsContext ContextID = cmsGetProfileContextID(hProfile);
|
||||
|
||||
if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile))
|
||||
return NULL;
|
||||
|
||||
if (!_cmsMAT3inverse(&Mat, &Inv))
|
||||
return NULL;
|
||||
|
||||
// XYZ PCS in encoded in 1.15 format, and the matrix input should come in 0..0xffff range, so
|
||||
// we need to adjust the input by a << 1 to obtain a 1.16 fixed and then by a factor of
|
||||
// (0xffff/0x10000) to put data in 0..0xffff range. Total factor is (2.0*65535.0)/65536.0;
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
for (j=0; j < 3; j++)
|
||||
Inv.v[i].n[j] *= OutpAdj;
|
||||
|
||||
Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag);
|
||||
Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag);
|
||||
Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag);
|
||||
|
||||
if (!Shapes[0] || !Shapes[1] || !Shapes[2])
|
||||
return NULL;
|
||||
|
||||
InvShapes[0] = cmsReverseToneCurve(Shapes[0]);
|
||||
InvShapes[1] = cmsReverseToneCurve(Shapes[1]);
|
||||
InvShapes[2] = cmsReverseToneCurve(Shapes[2]);
|
||||
|
||||
if (!InvShapes[0] || !InvShapes[1] || !InvShapes[2]) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Lut = cmsPipelineAlloc(ContextID, 3, 3);
|
||||
if (Lut != NULL) {
|
||||
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL));
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes));
|
||||
}
|
||||
|
||||
cmsFreeToneCurveTriple(InvShapes);
|
||||
return Lut;
|
||||
}
|
||||
|
||||
|
||||
// Change CLUT interpolation to trilinear
|
||||
static
|
||||
void ChangeInterpolationToTrilinear(cmsPipeline* Lut)
|
||||
{
|
||||
cmsStage* Stage;
|
||||
|
||||
for (Stage = cmsPipelineGetPtrToFirstStage(Lut);
|
||||
Stage != NULL;
|
||||
Stage = cmsStageNext(Stage)) {
|
||||
|
||||
if (cmsStageType(Stage) == cmsSigCLutElemType) {
|
||||
|
||||
_cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data;
|
||||
|
||||
CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR;
|
||||
_cmsSetInterpolationRoutine(CLUT ->Params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an output MPE LUT from agiven profile. Version mismatches are handled here
|
||||
cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
|
||||
{
|
||||
cmsTagTypeSignature OriginalType;
|
||||
cmsTagSignature tag16 = PCS2Device16[Intent];
|
||||
cmsTagSignature tagFloat = PCS2DeviceFloat[Intent];
|
||||
cmsContext ContextID = cmsGetProfileContextID(hProfile);
|
||||
|
||||
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
|
||||
|
||||
// Floating point LUT are always V4, so no adjustment is required
|
||||
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
|
||||
}
|
||||
|
||||
// Revert to perceptual if no tag is found
|
||||
if (!cmsIsTag(hProfile, tag16)) {
|
||||
tag16 = PCS2Device16[0];
|
||||
}
|
||||
|
||||
if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
|
||||
|
||||
// Check profile version and LUT type. Do the necessary adjustments if needed
|
||||
|
||||
// First read the tag
|
||||
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
|
||||
if (Lut == NULL) return NULL;
|
||||
|
||||
// After reading it, we have info about the original type
|
||||
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
|
||||
|
||||
// The profile owns the Lut, so we need to copy it
|
||||
Lut = cmsPipelineDup(Lut);
|
||||
if (Lut == NULL) return NULL;
|
||||
|
||||
// Now it is time for a controversial stuff. I found that for 3D LUTS using
|
||||
// Lab used as indexer space, trilinear interpolation should be used
|
||||
if (cmsGetPCS(hProfile) == cmsSigLabData)
|
||||
ChangeInterpolationToTrilinear(Lut);
|
||||
|
||||
// We need to adjust data only for Lab and Lut16 type
|
||||
if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
|
||||
return Lut;
|
||||
|
||||
// Add a matrix for conversion V4 to V2 Lab PCS
|
||||
cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID));
|
||||
return Lut;
|
||||
}
|
||||
|
||||
// Lut not found, try to create a matrix-shaper
|
||||
|
||||
// Check if this is a grayscale profile.
|
||||
if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
|
||||
|
||||
// if so, build appropiate conversion tables.
|
||||
// The tables are the PCS iluminant, scaled across GrayTRC
|
||||
return BuildGrayOutputPipeline(hProfile);
|
||||
}
|
||||
|
||||
// Not gray, create a normal matrix-shaper
|
||||
return BuildRGBOutputMatrixShaper(hProfile);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The
|
||||
// tag name here may default to AToB0
|
||||
cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
|
||||
{
|
||||
cmsPipeline* Lut;
|
||||
cmsTagTypeSignature OriginalType;
|
||||
cmsTagSignature tag16 = Device2PCS16[Intent];
|
||||
cmsTagSignature tagFloat = Device2PCSFloat[Intent];
|
||||
cmsContext ContextID = cmsGetProfileContextID(hProfile);
|
||||
|
||||
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
|
||||
|
||||
// Floating point LUT are always V4, no adjustment is required
|
||||
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
|
||||
}
|
||||
|
||||
tagFloat = Device2PCSFloat[0];
|
||||
if (cmsIsTag(hProfile, tagFloat)) {
|
||||
|
||||
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
|
||||
}
|
||||
|
||||
if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
|
||||
|
||||
tag16 = Device2PCS16[0];
|
||||
if (!cmsIsTag(hProfile, tag16)) return NULL;
|
||||
}
|
||||
|
||||
// Check profile version and LUT type. Do the necessary adjustments if needed
|
||||
|
||||
// Read the tag
|
||||
Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
|
||||
if (Lut == NULL) return NULL;
|
||||
|
||||
// The profile owns the Lut, so we need to copy it
|
||||
Lut = cmsPipelineDup(Lut);
|
||||
if (Lut == NULL) return NULL;
|
||||
|
||||
// Now it is time for a controversial stuff. I found that for 3D LUTS using
|
||||
// Lab used as indexer space, trilinear interpolation should be used
|
||||
if (cmsGetColorSpace(hProfile) == cmsSigLabData)
|
||||
ChangeInterpolationToTrilinear(Lut);
|
||||
|
||||
// After reading it, we have info about the original type
|
||||
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
|
||||
|
||||
// We need to adjust data for Lab16 on output
|
||||
if (OriginalType != cmsSigLut16Type) return Lut;
|
||||
|
||||
// Here it is possible to get Lab on both sides
|
||||
|
||||
if (cmsGetPCS(hProfile) == cmsSigLabData) {
|
||||
cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID));
|
||||
}
|
||||
|
||||
if (cmsGetColorSpace(hProfile) == cmsSigLabData) {
|
||||
cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID));
|
||||
}
|
||||
|
||||
return Lut;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Returns TRUE if the profile is implemented as matrix-shaper
|
||||
cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile)
|
||||
{
|
||||
switch (cmsGetColorSpace(hProfile)) {
|
||||
|
||||
case cmsSigGrayData:
|
||||
|
||||
return cmsIsTag(hProfile, cmsSigGrayTRCTag);
|
||||
|
||||
case cmsSigRgbData:
|
||||
|
||||
return (cmsIsTag(hProfile, cmsSigRedColorantTag) &&
|
||||
cmsIsTag(hProfile, cmsSigGreenColorantTag) &&
|
||||
cmsIsTag(hProfile, cmsSigBlueColorantTag) &&
|
||||
cmsIsTag(hProfile, cmsSigRedTRCTag) &&
|
||||
cmsIsTag(hProfile, cmsSigGreenTRCTag) &&
|
||||
cmsIsTag(hProfile, cmsSigBlueTRCTag));
|
||||
|
||||
default:
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns TRUE if the intent is implemented as CLUT
|
||||
cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection)
|
||||
{
|
||||
const cmsTagSignature* TagTable;
|
||||
|
||||
// For devicelinks, the supported intent is that one stated in the header
|
||||
if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) {
|
||||
return (cmsGetHeaderRenderingIntent(hProfile) == Intent);
|
||||
}
|
||||
|
||||
switch (UsedDirection) {
|
||||
|
||||
case LCMS_USED_AS_INPUT: TagTable = Device2PCS16; break;
|
||||
case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device16; break;
|
||||
|
||||
// For proofing, we need rel. colorimetric in output. Let's do some recursion
|
||||
case LCMS_USED_AS_PROOF:
|
||||
return cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) &&
|
||||
cmsIsIntentSupported(hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT);
|
||||
|
||||
default:
|
||||
cmsSignalError(cmsGetProfileContextID(hProfile), cmsERROR_RANGE, "Unexpected direction (%d)", UsedDirection);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return cmsIsTag(hProfile, TagTable[Intent]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Return info about supported intents
|
||||
cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile,
|
||||
cmsUInt32Number Intent, cmsUInt32Number UsedDirection)
|
||||
{
|
||||
|
||||
if (cmsIsCLUT(hProfile, Intent, UsedDirection)) return TRUE;
|
||||
|
||||
// Is there any matrix-shaper? If so, the intent is supported. This is a bit odd, since V2 matrix shaper
|
||||
// does not fully support relative colorimetric because they cannot deal with non-zero black points, but
|
||||
// many profiles claims that, and this is certainly not true for V4 profiles. Lets answer "yes" no matter
|
||||
// the accuracy would be less than optimal in rel.col and v2 case.
|
||||
|
||||
return cmsIsMatrixShaper(hProfile);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Read both, profile sequence description and profile sequence id if present. Then combine both to
|
||||
// create qa unique structure holding both. Shame on ICC to store things in such complicated way.
|
||||
|
||||
cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsSEQ* ProfileSeq;
|
||||
cmsSEQ* ProfileId;
|
||||
cmsSEQ* NewSeq;
|
||||
cmsUInt32Number i;
|
||||
|
||||
// Take profile sequence description first
|
||||
ProfileSeq = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceDescTag);
|
||||
|
||||
// Take profile sequence ID
|
||||
ProfileId = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceIdTag);
|
||||
|
||||
if (ProfileSeq == NULL && ProfileId == NULL) return NULL;
|
||||
|
||||
if (ProfileSeq == NULL) return cmsDupProfileSequenceDescription(ProfileId);
|
||||
if (ProfileId == NULL) return cmsDupProfileSequenceDescription(ProfileSeq);
|
||||
|
||||
// We have to mix both together. For that they must agree
|
||||
if (ProfileSeq ->n != ProfileId ->n) return cmsDupProfileSequenceDescription(ProfileSeq);
|
||||
|
||||
NewSeq = cmsDupProfileSequenceDescription(ProfileSeq);
|
||||
|
||||
// Ok, proceed to the mixing
|
||||
for (i=0; i < ProfileSeq ->n; i++) {
|
||||
|
||||
memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID));
|
||||
NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description);
|
||||
}
|
||||
|
||||
return NewSeq;
|
||||
}
|
||||
|
||||
// Dump the contents of profile sequence in both tags (if v4 available)
|
||||
cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq)
|
||||
{
|
||||
if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE;
|
||||
|
||||
if (cmsGetProfileVersion(hProfile) >= 4.0) {
|
||||
|
||||
if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Auxiliar, read and duplicate a MLU if found.
|
||||
static
|
||||
cmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig)
|
||||
{
|
||||
cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig);
|
||||
if (mlu == NULL) return NULL;
|
||||
|
||||
return cmsMLUdup(mlu);
|
||||
}
|
||||
|
||||
// Create a sequence description out of an array of profiles
|
||||
cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[])
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles);
|
||||
|
||||
if (seq == NULL) return NULL;
|
||||
|
||||
for (i=0; i < nProfiles; i++) {
|
||||
|
||||
cmsPSEQDESC* ps = &seq ->seq[i];
|
||||
cmsHPROFILE h = hProfiles[i];
|
||||
cmsTechnologySignature* techpt;
|
||||
|
||||
cmsGetHeaderAttributes(h, &ps ->attributes);
|
||||
cmsGetHeaderProfileID(h, ps ->ProfileID.ID8);
|
||||
ps ->deviceMfg = cmsGetHeaderManufacturer(h);
|
||||
ps ->deviceModel = cmsGetHeaderModel(h);
|
||||
|
||||
techpt = (cmsTechnologySignature*) cmsReadTag(h, cmsSigTechnologyTag);
|
||||
if (techpt == NULL)
|
||||
ps ->technology = (cmsTechnologySignature) 0;
|
||||
else
|
||||
ps ->technology = *techpt;
|
||||
|
||||
ps ->Manufacturer = GetMLUFromProfile(h, cmsSigDeviceMfgDescTag);
|
||||
ps ->Model = GetMLUFromProfile(h, cmsSigDeviceModelDescTag);
|
||||
ps ->Description = GetMLUFromProfile(h, cmsSigProfileDescriptionTag);
|
||||
|
||||
}
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
static
|
||||
const cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info)
|
||||
{
|
||||
cmsTagSignature sig;
|
||||
|
||||
switch (Info) {
|
||||
|
||||
case cmsInfoDescription:
|
||||
sig = cmsSigProfileDescriptionTag;
|
||||
break;
|
||||
|
||||
case cmsInfoManufacturer:
|
||||
sig = cmsSigDeviceMfgDescTag;
|
||||
break;
|
||||
|
||||
case cmsInfoModel:
|
||||
sig = cmsSigDeviceModelDescTag;
|
||||
break;
|
||||
|
||||
case cmsInfoCopyright:
|
||||
sig = cmsSigCopyrightTag;
|
||||
break;
|
||||
|
||||
default: return NULL;
|
||||
}
|
||||
|
||||
|
||||
return (cmsMLU*) cmsReadTag(hProfile, sig);
|
||||
}
|
||||
|
||||
|
||||
|
||||
cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info,
|
||||
const char LanguageCode[3], const char CountryCode[3],
|
||||
wchar_t* Buffer, cmsUInt32Number BufferSize)
|
||||
{
|
||||
const cmsMLU* mlu = GetInfo(hProfile, Info);
|
||||
if (mlu == NULL) return 0;
|
||||
|
||||
return cmsMLUgetWide(mlu, LanguageCode, CountryCode, Buffer, BufferSize);
|
||||
}
|
||||
|
||||
|
||||
cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info,
|
||||
const char LanguageCode[3], const char CountryCode[3],
|
||||
char* Buffer, cmsUInt32Number BufferSize)
|
||||
{
|
||||
const cmsMLU* mlu = GetInfo(hProfile, Info);
|
||||
if (mlu == NULL) return 0;
|
||||
|
||||
return cmsMLUgetASCII(mlu, LanguageCode, CountryCode, Buffer, BufferSize);
|
||||
}
|
||||
1665
thirdparty/liblcms2/src/cmslut.c
vendored
Normal file
1665
thirdparty/liblcms2/src/cmslut.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
317
thirdparty/liblcms2/src/cmsmd5.c
vendored
Normal file
317
thirdparty/liblcms2/src/cmsmd5.c
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
#ifdef CMS_USE_BIG_ENDIAN
|
||||
|
||||
static
|
||||
void byteReverse(cmsUInt8Number * buf, cmsUInt32Number longs)
|
||||
{
|
||||
do {
|
||||
|
||||
cmsUInt32Number t = _cmsAdjustEndianess32(*(cmsUInt32Number *) buf);
|
||||
*(cmsUInt32Number *) buf = t;
|
||||
buf += sizeof(cmsUInt32Number);
|
||||
|
||||
} while (--longs);
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#define byteReverse(buf, len)
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmsUInt32Number buf[4];
|
||||
cmsUInt32Number bits[2];
|
||||
cmsUInt8Number in[64];
|
||||
cmsContext ContextID;
|
||||
|
||||
} _cmsMD5;
|
||||
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
#define STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
|
||||
static
|
||||
void MD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16])
|
||||
|
||||
{
|
||||
register cmsUInt32Number a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||
STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||
STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||
STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||
STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||
STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||
STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||
STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||
STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||
STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||
STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||
STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||
STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||
STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||
STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||
STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||
STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||
STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||
STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||
STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||
STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||
STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||
STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||
STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||
STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||
STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||
STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||
STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||
STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||
STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||
|
||||
STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||
STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||
STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||
STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||
STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||
STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||
STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||
STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||
STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||
STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
|
||||
|
||||
// Create a MD5 object
|
||||
static
|
||||
cmsHANDLE MD5alloc(cmsContext ContextID)
|
||||
{
|
||||
_cmsMD5* ctx = (_cmsMD5*) _cmsMallocZero(ContextID, sizeof(_cmsMD5));
|
||||
if (ctx == NULL) return NULL;
|
||||
|
||||
ctx ->ContextID = ContextID;
|
||||
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
|
||||
return (cmsHANDLE) ctx;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void MD5add(cmsHANDLE Handle, cmsUInt8Number* buf, cmsUInt32Number len)
|
||||
{
|
||||
_cmsMD5* ctx = (_cmsMD5*) Handle;
|
||||
cmsUInt32Number t;
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = t + (len << 3)) < t)
|
||||
ctx->bits[1]++;
|
||||
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f;
|
||||
|
||||
if (t) {
|
||||
|
||||
cmsUInt8Number *p = (cmsUInt8Number *) ctx->in + t;
|
||||
|
||||
t = 64 - t;
|
||||
if (len < t) {
|
||||
memmove(p, buf, len);
|
||||
return;
|
||||
}
|
||||
|
||||
memmove(p, buf, t);
|
||||
byteReverse(ctx->in, 16);
|
||||
|
||||
MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
|
||||
while (len >= 64) {
|
||||
memmove(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
memmove(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
// Destroy the object and return the checksum
|
||||
static
|
||||
void MD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle)
|
||||
{
|
||||
_cmsMD5* ctx = (_cmsMD5*) Handle;
|
||||
cmsUInt32Number count;
|
||||
cmsUInt8Number *p;
|
||||
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
count = 64 - 1 - count;
|
||||
|
||||
if (count < 8) {
|
||||
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
|
||||
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
memset(p, 0, count - 8);
|
||||
}
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
((cmsUInt32Number *) ctx->in)[14] = ctx->bits[0];
|
||||
((cmsUInt32Number *) ctx->in)[15] = ctx->bits[1];
|
||||
|
||||
MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
|
||||
|
||||
byteReverse((cmsUInt8Number *) ctx->buf, 4);
|
||||
memmove(ProfileID ->ID8, ctx->buf, 16);
|
||||
|
||||
_cmsFree(ctx ->ContextID, ctx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Assuming io points to an ICC profile, compute and store MD5 checksum
|
||||
// In the header, rendering intentent, attributes and ID should be set to zero
|
||||
// before computing MD5 checksum (per 6.1.13 in ICC spec)
|
||||
|
||||
cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile)
|
||||
{
|
||||
cmsContext ContextID;
|
||||
cmsUInt32Number BytesNeeded;
|
||||
cmsUInt8Number* Mem = NULL;
|
||||
cmsHANDLE MD5 = NULL;
|
||||
_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
|
||||
_cmsICCPROFILE Keep;
|
||||
|
||||
_cmsAssert(hProfile != NULL);
|
||||
|
||||
ContextID = cmsGetProfileContextID(hProfile);
|
||||
|
||||
// Save a copy of the profile header
|
||||
memmove(&Keep, Icc, sizeof(_cmsICCPROFILE));
|
||||
|
||||
// Set RI, attributes and ID
|
||||
memset(&Icc ->attributes, 0, sizeof(Icc ->attributes));
|
||||
Icc ->RenderingIntent = 0;
|
||||
memset(&Icc ->ProfileID, 0, sizeof(Icc ->ProfileID));
|
||||
|
||||
// Compute needed storage
|
||||
if (!cmsSaveProfileToMem(hProfile, NULL, &BytesNeeded)) goto Error;
|
||||
|
||||
// Allocate memory
|
||||
Mem = (cmsUInt8Number*) _cmsMalloc(ContextID, BytesNeeded);
|
||||
if (Mem == NULL) goto Error;
|
||||
|
||||
// Save to temporary storage
|
||||
if (!cmsSaveProfileToMem(hProfile, Mem, &BytesNeeded)) goto Error;
|
||||
|
||||
// Create MD5 object
|
||||
MD5 = MD5alloc(ContextID);
|
||||
if (MD5 == NULL) goto Error;
|
||||
|
||||
// Add all bytes
|
||||
MD5add(MD5, Mem, BytesNeeded);
|
||||
|
||||
// Temp storage is no longer needed
|
||||
_cmsFree(ContextID, Mem);
|
||||
|
||||
// Restore header
|
||||
memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
|
||||
|
||||
// And store the ID
|
||||
MD5finish(&Icc ->ProfileID, MD5);
|
||||
return TRUE;
|
||||
|
||||
Error:
|
||||
|
||||
// Free resources as something went wrong
|
||||
if (MD5 != NULL) _cmsFree(ContextID, MD5);
|
||||
if (Mem != NULL) _cmsFree(ContextID, Mem);
|
||||
memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
176
thirdparty/liblcms2/src/cmsmtrx.c
vendored
Normal file
176
thirdparty/liblcms2/src/cmsmtrx.c
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
|
||||
#define DSWAP(x, y) {cmsFloat64Number tmp = (x); (x)=(y); (y)=tmp;}
|
||||
|
||||
|
||||
// Initiate a vector
|
||||
void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z)
|
||||
{
|
||||
r -> n[VX] = x;
|
||||
r -> n[VY] = y;
|
||||
r -> n[VZ] = z;
|
||||
}
|
||||
|
||||
// Vector substraction
|
||||
void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b)
|
||||
{
|
||||
r -> n[VX] = a -> n[VX] - b -> n[VX];
|
||||
r -> n[VY] = a -> n[VY] - b -> n[VY];
|
||||
r -> n[VZ] = a -> n[VZ] - b -> n[VZ];
|
||||
}
|
||||
|
||||
// Vector cross product
|
||||
void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v)
|
||||
{
|
||||
r ->n[VX] = u->n[VY] * v->n[VZ] - v->n[VY] * u->n[VZ];
|
||||
r ->n[VY] = u->n[VZ] * v->n[VX] - v->n[VZ] * u->n[VX];
|
||||
r ->n[VZ] = u->n[VX] * v->n[VY] - v->n[VX] * u->n[VY];
|
||||
}
|
||||
|
||||
// Vector dot product
|
||||
cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v)
|
||||
{
|
||||
return u->n[VX] * v->n[VX] + u->n[VY] * v->n[VY] + u->n[VZ] * v->n[VZ];
|
||||
}
|
||||
|
||||
// Euclidean length
|
||||
cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a)
|
||||
{
|
||||
return sqrt(a ->n[VX] * a ->n[VX] +
|
||||
a ->n[VY] * a ->n[VY] +
|
||||
a ->n[VZ] * a ->n[VZ]);
|
||||
}
|
||||
|
||||
// Euclidean distance
|
||||
cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b)
|
||||
{
|
||||
cmsFloat64Number d1 = a ->n[VX] - b ->n[VX];
|
||||
cmsFloat64Number d2 = a ->n[VY] - b ->n[VY];
|
||||
cmsFloat64Number d3 = a ->n[VZ] - b ->n[VZ];
|
||||
|
||||
return sqrt(d1*d1 + d2*d2 + d3*d3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 3x3 Identity
|
||||
void CMSEXPORT _cmsMAT3identity(cmsMAT3* a)
|
||||
{
|
||||
_cmsVEC3init(&a-> v[0], 1.0, 0.0, 0.0);
|
||||
_cmsVEC3init(&a-> v[1], 0.0, 1.0, 0.0);
|
||||
_cmsVEC3init(&a-> v[2], 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
static
|
||||
cmsBool CloseEnough(cmsFloat64Number a, cmsFloat64Number b)
|
||||
{
|
||||
return fabs(b - a) < (1.0 / 65535.0);
|
||||
}
|
||||
|
||||
|
||||
cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a)
|
||||
{
|
||||
cmsMAT3 Identity;
|
||||
int i, j;
|
||||
|
||||
_cmsMAT3identity(&Identity);
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
for (j=0; j < 3; j++)
|
||||
if (!CloseEnough(a ->v[i].n[j], Identity.v[i].n[j])) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Multiply two matrices
|
||||
void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b)
|
||||
{
|
||||
#define ROWCOL(i, j) \
|
||||
a->v[i].n[0]*b->v[0].n[j] + a->v[i].n[1]*b->v[1].n[j] + a->v[i].n[2]*b->v[2].n[j]
|
||||
|
||||
_cmsVEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2));
|
||||
_cmsVEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2));
|
||||
_cmsVEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2));
|
||||
|
||||
#undef ROWCOL //(i, j)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Inverse of a matrix b = a^(-1)
|
||||
cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b)
|
||||
{
|
||||
cmsFloat64Number det, c0, c1, c2;
|
||||
|
||||
c0 = a -> v[1].n[1]*a -> v[2].n[2] - a -> v[1].n[2]*a -> v[2].n[1];
|
||||
c1 = -a -> v[1].n[0]*a -> v[2].n[2] + a -> v[1].n[2]*a -> v[2].n[0];
|
||||
c2 = a -> v[1].n[0]*a -> v[2].n[1] - a -> v[1].n[1]*a -> v[2].n[0];
|
||||
|
||||
det = a -> v[0].n[0]*c0 + a -> v[0].n[1]*c1 + a -> v[0].n[2]*c2;
|
||||
|
||||
if (fabs(det) < MATRIX_DET_TOLERANCE) return FALSE; // singular matrix; can't invert
|
||||
|
||||
b -> v[0].n[0] = c0/det;
|
||||
b -> v[0].n[1] = (a -> v[0].n[2]*a -> v[2].n[1] - a -> v[0].n[1]*a -> v[2].n[2])/det;
|
||||
b -> v[0].n[2] = (a -> v[0].n[1]*a -> v[1].n[2] - a -> v[0].n[2]*a -> v[1].n[1])/det;
|
||||
b -> v[1].n[0] = c1/det;
|
||||
b -> v[1].n[1] = (a -> v[0].n[0]*a -> v[2].n[2] - a -> v[0].n[2]*a -> v[2].n[0])/det;
|
||||
b -> v[1].n[2] = (a -> v[0].n[2]*a -> v[1].n[0] - a -> v[0].n[0]*a -> v[1].n[2])/det;
|
||||
b -> v[2].n[0] = c2/det;
|
||||
b -> v[2].n[1] = (a -> v[0].n[1]*a -> v[2].n[0] - a -> v[0].n[0]*a -> v[2].n[1])/det;
|
||||
b -> v[2].n[2] = (a -> v[0].n[0]*a -> v[1].n[1] - a -> v[0].n[1]*a -> v[1].n[0])/det;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Solve a system in the form Ax = b
|
||||
cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b)
|
||||
{
|
||||
cmsMAT3 m, a_1;
|
||||
|
||||
memmove(&m, a, sizeof(cmsMAT3));
|
||||
|
||||
if (!_cmsMAT3inverse(&m, &a_1)) return FALSE; // Singular matrix
|
||||
|
||||
_cmsMAT3eval(x, &a_1, b);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Evaluate a vector across a matrix
|
||||
void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v)
|
||||
{
|
||||
r->n[VX] = a->v[0].n[VX]*v->n[VX] + a->v[0].n[VY]*v->n[VY] + a->v[0].n[VZ]*v->n[VZ];
|
||||
r->n[VY] = a->v[1].n[VX]*v->n[VX] + a->v[1].n[VY]*v->n[VY] + a->v[1].n[VZ]*v->n[VZ];
|
||||
r->n[VZ] = a->v[2].n[VX]*v->n[VX] + a->v[2].n[VY]*v->n[VY] + a->v[2].n[VZ]*v->n[VZ];
|
||||
}
|
||||
|
||||
|
||||
750
thirdparty/liblcms2/src/cmsnamed.c
vendored
Normal file
750
thirdparty/liblcms2/src/cmsnamed.c
vendored
Normal file
@@ -0,0 +1,750 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
// Multilocalized unicode objects. That is an attempt to encapsulate i18n.
|
||||
|
||||
|
||||
// Allocates an empty multi localizad unicode object
|
||||
cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)
|
||||
{
|
||||
cmsMLU* mlu;
|
||||
|
||||
// nItems should be positive if given
|
||||
if (nItems <= 0) nItems = 2;
|
||||
|
||||
// Create the container
|
||||
mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));
|
||||
if (mlu == NULL) return NULL;
|
||||
|
||||
mlu ->ContextID = ContextID;
|
||||
|
||||
// Create entry array
|
||||
mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));
|
||||
if (mlu ->Entries == NULL) {
|
||||
_cmsFree(ContextID, mlu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Ok, keep indexes up to date
|
||||
mlu ->AllocatedEntries = nItems;
|
||||
mlu ->UsedEntries = 0;
|
||||
|
||||
return mlu;
|
||||
}
|
||||
|
||||
|
||||
// Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two.
|
||||
static
|
||||
cmsBool GrowMLUpool(cmsMLU* mlu)
|
||||
{
|
||||
cmsUInt32Number size;
|
||||
void *NewPtr;
|
||||
|
||||
// Sanity check
|
||||
if (mlu == NULL) return FALSE;
|
||||
|
||||
if (mlu ->PoolSize == 0)
|
||||
size = 256;
|
||||
else
|
||||
size = mlu ->PoolSize * 2;
|
||||
|
||||
// Check for overflow
|
||||
if (size < mlu ->PoolSize) return FALSE;
|
||||
|
||||
// Reallocate the pool
|
||||
NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size);
|
||||
if (NewPtr == NULL) return FALSE;
|
||||
|
||||
|
||||
mlu ->MemPool = NewPtr;
|
||||
mlu ->PoolSize = size;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Grows a ntry table for a MLU. Each time this function is called, table size is multiplied times two.
|
||||
static
|
||||
cmsBool GrowMLUtable(cmsMLU* mlu)
|
||||
{
|
||||
int AllocatedEntries;
|
||||
_cmsMLUentry *NewPtr;
|
||||
|
||||
// Sanity check
|
||||
if (mlu == NULL) return FALSE;
|
||||
|
||||
AllocatedEntries = mlu ->AllocatedEntries * 2;
|
||||
|
||||
// Check for overflow
|
||||
if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;
|
||||
|
||||
// Reallocate the memory
|
||||
NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));
|
||||
if (NewPtr == NULL) return FALSE;
|
||||
|
||||
mlu ->Entries = NewPtr;
|
||||
mlu ->AllocatedEntries = AllocatedEntries;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Search for a specific entry in the structure. Language and Country are used.
|
||||
static
|
||||
int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
|
||||
{
|
||||
int i;
|
||||
|
||||
// Sanity check
|
||||
if (mlu == NULL) return -1;
|
||||
|
||||
// Iterate whole table
|
||||
for (i=0; i < mlu ->UsedEntries; i++) {
|
||||
|
||||
if (mlu ->Entries[i].Country == CountryCode &&
|
||||
mlu ->Entries[i].Language == LanguageCode) return i;
|
||||
}
|
||||
|
||||
// Not found
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add a block of characters to the intended MLU. Language and country are specified.
|
||||
// Only one entry for Language/country pair is allowed.
|
||||
static
|
||||
cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block,
|
||||
cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
|
||||
{
|
||||
cmsUInt32Number Offset;
|
||||
cmsUInt8Number* Ptr;
|
||||
|
||||
// Sanity check
|
||||
if (mlu == NULL) return FALSE;
|
||||
|
||||
// Is there any room available?
|
||||
if (mlu ->UsedEntries >= mlu ->AllocatedEntries) {
|
||||
if (!GrowMLUtable(mlu)) return FALSE;
|
||||
}
|
||||
|
||||
// Only one ASCII string
|
||||
if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed!
|
||||
|
||||
// Check for size
|
||||
while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {
|
||||
|
||||
if (!GrowMLUpool(mlu)) return FALSE;
|
||||
}
|
||||
|
||||
Offset = mlu ->PoolUsed;
|
||||
|
||||
Ptr = (cmsUInt8Number*) mlu ->MemPool;
|
||||
if (Ptr == NULL) return FALSE;
|
||||
|
||||
// Set the entry
|
||||
memmove(Ptr + Offset, Block, size);
|
||||
mlu ->PoolUsed += size;
|
||||
|
||||
mlu ->Entries[mlu ->UsedEntries].StrW = Offset;
|
||||
mlu ->Entries[mlu ->UsedEntries].Len = size;
|
||||
mlu ->Entries[mlu ->UsedEntries].Country = CountryCode;
|
||||
mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode;
|
||||
mlu ->UsedEntries++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Add an ASCII entry.
|
||||
cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)
|
||||
{
|
||||
cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString)+1;
|
||||
wchar_t* WStr;
|
||||
cmsBool rc;
|
||||
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
|
||||
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
|
||||
|
||||
if (mlu == NULL) return FALSE;
|
||||
|
||||
WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len, sizeof(wchar_t));
|
||||
if (WStr == NULL) return FALSE;
|
||||
|
||||
for (i=0; i < len; i++)
|
||||
WStr[i] = (wchar_t) ASCIIString[i];
|
||||
|
||||
rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry);
|
||||
|
||||
_cmsFree(mlu ->ContextID, WStr);
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
// We don't need any wcs support library
|
||||
static
|
||||
cmsUInt32Number mywcslen(const wchar_t *s)
|
||||
{
|
||||
const wchar_t *p;
|
||||
|
||||
p = s;
|
||||
while (*p)
|
||||
p++;
|
||||
|
||||
return (cmsUInt32Number)(p - s);
|
||||
}
|
||||
|
||||
|
||||
// Add a wide entry
|
||||
cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString)
|
||||
{
|
||||
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) Language);
|
||||
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) Country);
|
||||
cmsUInt32Number len;
|
||||
|
||||
if (mlu == NULL) return FALSE;
|
||||
if (WideString == NULL) return FALSE;
|
||||
|
||||
len = (cmsUInt32Number) (mywcslen(WideString) + 1) * sizeof(wchar_t);
|
||||
return AddMLUBlock(mlu, len, WideString, Lang, Cntry);
|
||||
}
|
||||
|
||||
// Duplicating a MLU is as easy as copying all members
|
||||
cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu)
|
||||
{
|
||||
cmsMLU* NewMlu = NULL;
|
||||
|
||||
// Duplicating a NULL obtains a NULL
|
||||
if (mlu == NULL) return NULL;
|
||||
|
||||
NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries);
|
||||
if (NewMlu == NULL) return NULL;
|
||||
|
||||
// Should never happen
|
||||
if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)
|
||||
goto Error;
|
||||
|
||||
// Sanitize...
|
||||
if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error;
|
||||
|
||||
memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry));
|
||||
NewMlu ->UsedEntries = mlu ->UsedEntries;
|
||||
|
||||
// The MLU may be empty
|
||||
if (mlu ->PoolUsed == 0) {
|
||||
NewMlu ->MemPool = NULL;
|
||||
}
|
||||
else {
|
||||
// It is not empty
|
||||
NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed);
|
||||
if (NewMlu ->MemPool == NULL) goto Error;
|
||||
}
|
||||
|
||||
NewMlu ->PoolSize = mlu ->PoolUsed;
|
||||
|
||||
if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error;
|
||||
|
||||
memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);
|
||||
NewMlu ->PoolUsed = mlu ->PoolUsed;
|
||||
|
||||
return NewMlu;
|
||||
|
||||
Error:
|
||||
|
||||
if (NewMlu != NULL) cmsMLUfree(NewMlu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Free any used memory
|
||||
void CMSEXPORT cmsMLUfree(cmsMLU* mlu)
|
||||
{
|
||||
if (mlu) {
|
||||
|
||||
if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries);
|
||||
if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool);
|
||||
|
||||
_cmsFree(mlu ->ContextID, mlu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The algorithm first searches for an exact match of country and language, if not found it uses
|
||||
// the Language. If none is found, first entry is used instead.
|
||||
static
|
||||
const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
|
||||
cmsUInt32Number *len,
|
||||
cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode,
|
||||
cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)
|
||||
{
|
||||
int i;
|
||||
int Best = -1;
|
||||
_cmsMLUentry* v;
|
||||
|
||||
if (mlu == NULL) return NULL;
|
||||
|
||||
if (mlu -> AllocatedEntries <= 0) return NULL;
|
||||
|
||||
for (i=0; i < mlu ->UsedEntries; i++) {
|
||||
|
||||
v = mlu ->Entries + i;
|
||||
|
||||
if (v -> Language == LanguageCode) {
|
||||
|
||||
if (Best == -1) Best = i;
|
||||
|
||||
if (v -> Country == CountryCode) {
|
||||
|
||||
if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
|
||||
if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
|
||||
|
||||
if (len != NULL) *len = v ->Len;
|
||||
|
||||
return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW); // Found exact match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No string found. Return First one
|
||||
if (Best == -1)
|
||||
Best = 0;
|
||||
|
||||
v = mlu ->Entries + Best;
|
||||
|
||||
if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
|
||||
if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
|
||||
|
||||
if (len != NULL) *len = v ->Len;
|
||||
|
||||
return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);
|
||||
}
|
||||
|
||||
|
||||
// Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len
|
||||
cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
|
||||
const char LanguageCode[3], const char CountryCode[3],
|
||||
char* Buffer, cmsUInt32Number BufferSize)
|
||||
{
|
||||
const wchar_t *Wide;
|
||||
cmsUInt32Number StrLen = 0;
|
||||
cmsUInt32Number ASCIIlen, i;
|
||||
|
||||
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
|
||||
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
|
||||
|
||||
// Sanitize
|
||||
if (mlu == NULL) return 0;
|
||||
|
||||
// Get WideChar
|
||||
Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
|
||||
if (Wide == NULL) return 0;
|
||||
|
||||
ASCIIlen = StrLen / sizeof(wchar_t);
|
||||
|
||||
// Maybe we want only to know the len?
|
||||
if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end
|
||||
|
||||
// No buffer size means no data
|
||||
if (BufferSize <= 0) return 0;
|
||||
|
||||
// Some clipping may be required
|
||||
if (BufferSize < ASCIIlen + 1)
|
||||
ASCIIlen = BufferSize - 1;
|
||||
|
||||
// Precess each character
|
||||
for (i=0; i < ASCIIlen; i++) {
|
||||
|
||||
if (Wide[i] == 0)
|
||||
Buffer[i] = 0;
|
||||
else
|
||||
Buffer[i] = (char) Wide[i];
|
||||
}
|
||||
|
||||
// We put a termination "\0"
|
||||
Buffer[ASCIIlen] = 0;
|
||||
return ASCIIlen + 1;
|
||||
}
|
||||
|
||||
// Obtain a wide representation of the MLU, on depending on current locale settings
|
||||
cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
|
||||
const char LanguageCode[3], const char CountryCode[3],
|
||||
wchar_t* Buffer, cmsUInt32Number BufferSize)
|
||||
{
|
||||
const wchar_t *Wide;
|
||||
cmsUInt32Number StrLen = 0;
|
||||
|
||||
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
|
||||
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
|
||||
|
||||
// Sanitize
|
||||
if (mlu == NULL) return 0;
|
||||
|
||||
Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
|
||||
if (Wide == NULL) return 0;
|
||||
|
||||
// Maybe we want only to know the len?
|
||||
if (Buffer == NULL) return StrLen + sizeof(wchar_t);
|
||||
|
||||
// No buffer size means no data
|
||||
if (BufferSize <= 0) return 0;
|
||||
|
||||
// Some clipping may be required
|
||||
if (BufferSize < StrLen + sizeof(wchar_t))
|
||||
StrLen = BufferSize - + sizeof(wchar_t);
|
||||
|
||||
memmove(Buffer, Wide, StrLen);
|
||||
Buffer[StrLen / sizeof(wchar_t)] = 0;
|
||||
|
||||
return StrLen + sizeof(wchar_t);
|
||||
}
|
||||
|
||||
|
||||
// Get also the language and country
|
||||
CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu,
|
||||
const char LanguageCode[3], const char CountryCode[3],
|
||||
char ObtainedLanguage[3], char ObtainedCountry[3])
|
||||
{
|
||||
const wchar_t *Wide;
|
||||
|
||||
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
|
||||
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
|
||||
cmsUInt16Number ObtLang, ObtCode;
|
||||
|
||||
// Sanitize
|
||||
if (mlu == NULL) return FALSE;
|
||||
|
||||
Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode);
|
||||
if (Wide == NULL) return FALSE;
|
||||
|
||||
// Get used language and code
|
||||
*(cmsUInt16Number *)ObtainedLanguage = _cmsAdjustEndianess16(ObtLang);
|
||||
*(cmsUInt16Number *)ObtainedCountry = _cmsAdjustEndianess16(ObtCode);
|
||||
|
||||
ObtainedLanguage[2] = ObtainedCountry[2] = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Named color lists --------------------------------------------------------------------------------------------
|
||||
|
||||
// Grow the list to keep at least NumElements
|
||||
static
|
||||
cmsBool GrowNamedColorList(cmsNAMEDCOLORLIST* v)
|
||||
{
|
||||
cmsUInt32Number size;
|
||||
_cmsNAMEDCOLOR * NewPtr;
|
||||
|
||||
if (v == NULL) return FALSE;
|
||||
|
||||
if (v ->Allocated == 0)
|
||||
size = 64; // Initial guess
|
||||
else
|
||||
size = v ->Allocated * 2;
|
||||
|
||||
// Keep a maximum color lists can grow, 100K entries seems reasonable
|
||||
if (size > 1024*100) return FALSE;
|
||||
|
||||
NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR));
|
||||
if (NewPtr == NULL)
|
||||
return FALSE;
|
||||
|
||||
v ->List = NewPtr;
|
||||
v ->Allocated = size;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Allocate a list for n elements
|
||||
cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix)
|
||||
{
|
||||
cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
|
||||
|
||||
if (v == NULL) return NULL;
|
||||
|
||||
v ->List = NULL;
|
||||
v ->nColors = 0;
|
||||
v ->ContextID = ContextID;
|
||||
|
||||
while (v -> Allocated < n)
|
||||
GrowNamedColorList(v);
|
||||
|
||||
strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix));
|
||||
strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix));
|
||||
v -> ColorantCount = ColorantCount;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// Free a list
|
||||
void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v)
|
||||
{
|
||||
if (v ->List) _cmsFree(v ->ContextID, v ->List);
|
||||
if (v) _cmsFree(v ->ContextID, v);
|
||||
}
|
||||
|
||||
cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
|
||||
{
|
||||
cmsNAMEDCOLORLIST* NewNC;
|
||||
|
||||
if (v == NULL) return NULL;
|
||||
|
||||
NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix);
|
||||
if (NewNC == NULL) return NULL;
|
||||
|
||||
// For really large tables we need this
|
||||
while (NewNC ->Allocated < v ->Allocated)
|
||||
GrowNamedColorList(NewNC);
|
||||
|
||||
memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix));
|
||||
memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix));
|
||||
NewNC ->ColorantCount = v ->ColorantCount;
|
||||
memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR));
|
||||
NewNC ->nColors = v ->nColors;
|
||||
return NewNC;
|
||||
}
|
||||
|
||||
|
||||
// Append a color to a list. List pointer may change if reallocated
|
||||
cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList,
|
||||
const char* Name,
|
||||
cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS])
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
|
||||
if (NamedColorList == NULL) return FALSE;
|
||||
|
||||
if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) {
|
||||
if (!GrowNamedColorList(NamedColorList)) return FALSE;
|
||||
}
|
||||
|
||||
for (i=0; i < NamedColorList ->ColorantCount; i++)
|
||||
NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL? 0 : Colorant[i];
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? 0 : PCS[i];
|
||||
|
||||
if (Name != NULL)
|
||||
strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name,
|
||||
sizeof(NamedColorList ->List[NamedColorList ->nColors].Name));
|
||||
else
|
||||
NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0;
|
||||
|
||||
|
||||
NamedColorList ->nColors++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Returns number of elements
|
||||
cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList)
|
||||
{
|
||||
if (NamedColorList == NULL) return 0;
|
||||
return NamedColorList ->nColors;
|
||||
}
|
||||
|
||||
// Info aboout a given color
|
||||
cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor,
|
||||
char* Name,
|
||||
char* Prefix,
|
||||
char* Suffix,
|
||||
cmsUInt16Number* PCS,
|
||||
cmsUInt16Number* Colorant)
|
||||
{
|
||||
if (NamedColorList == NULL) return FALSE;
|
||||
|
||||
if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE;
|
||||
|
||||
if (Name) strcpy(Name, NamedColorList->List[nColor].Name);
|
||||
if (Prefix) strcpy(Prefix, NamedColorList->Prefix);
|
||||
if (Suffix) strcpy(Suffix, NamedColorList->Suffix);
|
||||
if (PCS)
|
||||
memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number));
|
||||
|
||||
if (Colorant)
|
||||
memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant,
|
||||
sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount);
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Search for a given color name (no prefix or suffix)
|
||||
cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
if (NamedColorList == NULL) return -1;
|
||||
n = cmsNamedColorCount(NamedColorList);
|
||||
for (i=0; i < n; i++) {
|
||||
if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// MPE support -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
static
|
||||
void FreeNamedColorList(cmsStage* mpe)
|
||||
{
|
||||
cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
|
||||
cmsFreeNamedColorList(List);
|
||||
}
|
||||
|
||||
static
|
||||
void* DupNamedColorList(cmsStage* mpe)
|
||||
{
|
||||
cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
|
||||
return cmsDupNamedColorList(List);
|
||||
}
|
||||
|
||||
static
|
||||
void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
|
||||
{
|
||||
cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
|
||||
cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
|
||||
cmsUInt32Number j;
|
||||
|
||||
if (index >= NamedColorList-> nColors) {
|
||||
cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index);
|
||||
}
|
||||
else {
|
||||
for (j=0; j < NamedColorList ->ColorantCount; j++)
|
||||
Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Named color lookup element
|
||||
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList)
|
||||
{
|
||||
return _cmsStageAllocPlaceholder(NamedColorList ->ContextID,
|
||||
cmsSigNamedColorElemType,
|
||||
1, 3,
|
||||
EvalNamedColor,
|
||||
DupNamedColorList,
|
||||
FreeNamedColorList,
|
||||
cmsDupNamedColorList(NamedColorList));
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Retrieve the named color list from a transform. Should be first element in the LUT
|
||||
cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform)
|
||||
{
|
||||
_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
|
||||
cmsStage* mpe = v ->Lut->Elements;
|
||||
|
||||
if (mpe ->Type != cmsSigNamedColorElemType) return NULL;
|
||||
return (cmsNAMEDCOLORLIST*) mpe ->Data;
|
||||
}
|
||||
|
||||
|
||||
// Profile sequence description routines -------------------------------------------------------------------------------------
|
||||
|
||||
cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n)
|
||||
{
|
||||
cmsSEQ* Seq;
|
||||
cmsUInt32Number i;
|
||||
|
||||
if (n == 0) return NULL;
|
||||
|
||||
// In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked
|
||||
// in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door!
|
||||
if (n > 255) return NULL;
|
||||
|
||||
Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ));
|
||||
if (Seq == NULL) return NULL;
|
||||
|
||||
Seq -> ContextID = ContextID;
|
||||
Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC));
|
||||
Seq -> n = n;
|
||||
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
Seq -> seq[i].Manufacturer = NULL;
|
||||
Seq -> seq[i].Model = NULL;
|
||||
Seq -> seq[i].Description = NULL;
|
||||
}
|
||||
|
||||
return Seq;
|
||||
}
|
||||
|
||||
void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq)
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
|
||||
for (i=0; i < pseq ->n; i++) {
|
||||
if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer);
|
||||
if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model);
|
||||
if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description);
|
||||
}
|
||||
|
||||
if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq);
|
||||
_cmsFree(pseq -> ContextID, pseq);
|
||||
}
|
||||
|
||||
cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq)
|
||||
{
|
||||
cmsSEQ *NewSeq;
|
||||
cmsUInt32Number i;
|
||||
|
||||
if (pseq == NULL)
|
||||
return NULL;
|
||||
|
||||
NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ));
|
||||
if (NewSeq == NULL) return NULL;
|
||||
|
||||
|
||||
NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC));
|
||||
if (NewSeq ->seq == NULL) goto Error;
|
||||
|
||||
NewSeq -> ContextID = pseq ->ContextID;
|
||||
NewSeq -> n = pseq ->n;
|
||||
|
||||
for (i=0; i < pseq->n; i++) {
|
||||
|
||||
memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number));
|
||||
|
||||
NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg;
|
||||
NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel;
|
||||
memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID));
|
||||
NewSeq ->seq[i].technology = pseq ->seq[i].technology;
|
||||
|
||||
NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer);
|
||||
NewSeq ->seq[i].Model = cmsMLUdup(pseq ->seq[i].Model);
|
||||
NewSeq ->seq[i].Description = cmsMLUdup(pseq ->seq[i].Description);
|
||||
|
||||
}
|
||||
|
||||
return NewSeq;
|
||||
|
||||
Error:
|
||||
|
||||
cmsFreeProfileSequenceDescription(NewSeq);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
1631
thirdparty/liblcms2/src/cmsopt.c
vendored
Normal file
1631
thirdparty/liblcms2/src/cmsopt.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2558
thirdparty/liblcms2/src/cmspack.c
vendored
Normal file
2558
thirdparty/liblcms2/src/cmspack.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
926
thirdparty/liblcms2/src/cmspcs.c
vendored
Normal file
926
thirdparty/liblcms2/src/cmspcs.c
vendored
Normal file
@@ -0,0 +1,926 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
// inter PCS conversions XYZ <-> CIE L* a* b*
|
||||
/*
|
||||
|
||||
|
||||
CIE 15:2004 CIELab is defined as:
|
||||
|
||||
L* = 116*f(Y/Yn) - 16 0 <= L* <= 100
|
||||
a* = 500*[f(X/Xn) - f(Y/Yn)]
|
||||
b* = 200*[f(Y/Yn) - f(Z/Zn)]
|
||||
|
||||
and
|
||||
|
||||
f(t) = t^(1/3) 1 >= t > (24/116)^3
|
||||
(841/108)*t + (16/116) 0 <= t <= (24/116)^3
|
||||
|
||||
|
||||
Reverse transform is:
|
||||
|
||||
X = Xn*[a* / 500 + (L* + 16) / 116] ^ 3 if (X/Xn) > (24/116)
|
||||
= Xn*(a* / 500 + L* / 116) / 7.787 if (X/Xn) <= (24/116)
|
||||
|
||||
|
||||
|
||||
PCS in Lab2 is encoded as:
|
||||
|
||||
8 bit Lab PCS:
|
||||
|
||||
L* 0..100 into a 0..ff byte.
|
||||
a* t + 128 range is -128.0 +127.0
|
||||
b*
|
||||
|
||||
16 bit Lab PCS:
|
||||
|
||||
L* 0..100 into a 0..ff00 word.
|
||||
a* t + 128 range is -128.0 +127.9961
|
||||
b*
|
||||
|
||||
|
||||
|
||||
Interchange Space Component Actual Range Encoded Range
|
||||
CIE XYZ X 0 -> 1.99997 0x0000 -> 0xffff
|
||||
CIE XYZ Y 0 -> 1.99997 0x0000 -> 0xffff
|
||||
CIE XYZ Z 0 -> 1.99997 0x0000 -> 0xffff
|
||||
|
||||
Version 2,3
|
||||
-----------
|
||||
|
||||
CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xff00
|
||||
CIELAB (16 bit) a* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff
|
||||
CIELAB (16 bit) b* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff
|
||||
|
||||
|
||||
Version 4
|
||||
---------
|
||||
|
||||
CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xffff
|
||||
CIELAB (16 bit) a* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff
|
||||
CIELAB (16 bit) b* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff
|
||||
|
||||
*/
|
||||
|
||||
// Conversions
|
||||
void CMSEXPORT cmsXYZ2xyY(cmsCIExyY* Dest, const cmsCIEXYZ* Source)
|
||||
{
|
||||
cmsFloat64Number ISum;
|
||||
|
||||
ISum = 1./(Source -> X + Source -> Y + Source -> Z);
|
||||
|
||||
Dest -> x = (Source -> X) * ISum;
|
||||
Dest -> y = (Source -> Y) * ISum;
|
||||
Dest -> Y = Source -> Y;
|
||||
}
|
||||
|
||||
void CMSEXPORT cmsxyY2XYZ(cmsCIEXYZ* Dest, const cmsCIExyY* Source)
|
||||
{
|
||||
Dest -> X = (Source -> x / Source -> y) * Source -> Y;
|
||||
Dest -> Y = Source -> Y;
|
||||
Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y;
|
||||
}
|
||||
|
||||
static
|
||||
cmsFloat64Number f(cmsFloat64Number t)
|
||||
{
|
||||
const cmsFloat64Number Limit = (24.0/116.0) * (24.0/116.0) * (24.0/116.0);
|
||||
|
||||
if (t <= Limit)
|
||||
return (841.0/108.0) * t + (16.0/116.0);
|
||||
else
|
||||
return pow(t, 1.0/3.0);
|
||||
}
|
||||
|
||||
static
|
||||
cmsFloat64Number f_1(cmsFloat64Number t)
|
||||
{
|
||||
const cmsFloat64Number Limit = (24.0/116.0);
|
||||
|
||||
if (t <= Limit) {
|
||||
return (108.0/841.0) * (t - (16.0/116.0));
|
||||
}
|
||||
|
||||
return t * t * t;
|
||||
}
|
||||
|
||||
|
||||
// Standard XYZ to Lab. it can handle negative XZY numbers in some cases
|
||||
void CMSEXPORT cmsXYZ2Lab(const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz)
|
||||
{
|
||||
cmsFloat64Number fx, fy, fz;
|
||||
|
||||
if (WhitePoint == NULL)
|
||||
WhitePoint = cmsD50_XYZ();
|
||||
|
||||
fx = f(xyz->X / WhitePoint->X);
|
||||
fy = f(xyz->Y / WhitePoint->Y);
|
||||
fz = f(xyz->Z / WhitePoint->Z);
|
||||
|
||||
Lab->L = 116.0*fy - 16.0;
|
||||
Lab->a = 500.0*(fx - fy);
|
||||
Lab->b = 200.0*(fy - fz);
|
||||
}
|
||||
|
||||
|
||||
// Standard XYZ to Lab. It can return negative XYZ in some cases
|
||||
void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab)
|
||||
{
|
||||
cmsFloat64Number x, y, z;
|
||||
|
||||
if (WhitePoint == NULL)
|
||||
WhitePoint = cmsD50_XYZ();
|
||||
|
||||
y = (Lab-> L + 16.0) / 116.0;
|
||||
x = y + 0.002 * Lab -> a;
|
||||
z = y - 0.005 * Lab -> b;
|
||||
|
||||
xyz -> X = f_1(x) * WhitePoint -> X;
|
||||
xyz -> Y = f_1(y) * WhitePoint -> Y;
|
||||
xyz -> Z = f_1(z) * WhitePoint -> Z;
|
||||
|
||||
}
|
||||
|
||||
static
|
||||
cmsFloat64Number L2float2(cmsUInt16Number v)
|
||||
{
|
||||
return (cmsFloat64Number) v / 652.800;
|
||||
}
|
||||
|
||||
// the a/b part
|
||||
static
|
||||
cmsFloat64Number ab2float2(cmsUInt16Number v)
|
||||
{
|
||||
return ((cmsFloat64Number) v / 256.0) - 128.0;
|
||||
}
|
||||
|
||||
static
|
||||
cmsUInt16Number L2Fix2(cmsFloat64Number L)
|
||||
{
|
||||
return _cmsQuickSaturateWord(L * 652.8);
|
||||
}
|
||||
|
||||
static
|
||||
cmsUInt16Number ab2Fix2(cmsFloat64Number ab)
|
||||
{
|
||||
return _cmsQuickSaturateWord((ab + 128.0) * 256.0);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
cmsFloat64Number L2float4(cmsUInt16Number v)
|
||||
{
|
||||
return (cmsFloat64Number) v / 655.35;
|
||||
}
|
||||
|
||||
// the a/b part
|
||||
static
|
||||
cmsFloat64Number ab2float4(cmsUInt16Number v)
|
||||
{
|
||||
return ((cmsFloat64Number) v / 257.0) - 128.0;
|
||||
}
|
||||
|
||||
|
||||
void CMSEXPORT cmsLabEncoded2FloatV2(cmsCIELab* Lab, const cmsUInt16Number wLab[3])
|
||||
{
|
||||
Lab->L = L2float2(wLab[0]);
|
||||
Lab->a = ab2float2(wLab[1]);
|
||||
Lab->b = ab2float2(wLab[2]);
|
||||
}
|
||||
|
||||
|
||||
void CMSEXPORT cmsLabEncoded2Float(cmsCIELab* Lab, const cmsUInt16Number wLab[3])
|
||||
{
|
||||
Lab->L = L2float4(wLab[0]);
|
||||
Lab->a = ab2float4(wLab[1]);
|
||||
Lab->b = ab2float4(wLab[2]);
|
||||
}
|
||||
|
||||
static
|
||||
cmsFloat64Number Clamp_L_doubleV2(cmsFloat64Number L)
|
||||
{
|
||||
const cmsFloat64Number L_max = (cmsFloat64Number) (0xFFFF * 100.0) / 0xFF00;
|
||||
|
||||
if (L < 0) L = 0;
|
||||
if (L > L_max) L = L_max;
|
||||
|
||||
return L;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
cmsFloat64Number Clamp_ab_doubleV2(cmsFloat64Number ab)
|
||||
{
|
||||
if (ab < MIN_ENCODEABLE_ab2) ab = MIN_ENCODEABLE_ab2;
|
||||
if (ab > MAX_ENCODEABLE_ab2) ab = MAX_ENCODEABLE_ab2;
|
||||
|
||||
return ab;
|
||||
}
|
||||
|
||||
void CMSEXPORT cmsFloat2LabEncodedV2(cmsUInt16Number wLab[3], const cmsCIELab* fLab)
|
||||
{
|
||||
cmsCIELab Lab;
|
||||
|
||||
Lab.L = Clamp_L_doubleV2(fLab ->L);
|
||||
Lab.a = Clamp_ab_doubleV2(fLab ->a);
|
||||
Lab.b = Clamp_ab_doubleV2(fLab ->b);
|
||||
|
||||
wLab[0] = L2Fix2(Lab.L);
|
||||
wLab[1] = ab2Fix2(Lab.a);
|
||||
wLab[2] = ab2Fix2(Lab.b);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
cmsFloat64Number Clamp_L_doubleV4(cmsFloat64Number L)
|
||||
{
|
||||
if (L < 0) L = 0;
|
||||
if (L > 100.0) L = 100.0;
|
||||
|
||||
return L;
|
||||
}
|
||||
|
||||
static
|
||||
cmsFloat64Number Clamp_ab_doubleV4(cmsFloat64Number ab)
|
||||
{
|
||||
if (ab < MIN_ENCODEABLE_ab4) ab = MIN_ENCODEABLE_ab4;
|
||||
if (ab > MAX_ENCODEABLE_ab4) ab = MAX_ENCODEABLE_ab4;
|
||||
|
||||
return ab;
|
||||
}
|
||||
|
||||
static
|
||||
cmsUInt16Number L2Fix4(cmsFloat64Number L)
|
||||
{
|
||||
return _cmsQuickSaturateWord(L * 655.35);
|
||||
}
|
||||
|
||||
static
|
||||
cmsUInt16Number ab2Fix4(cmsFloat64Number ab)
|
||||
{
|
||||
return _cmsQuickSaturateWord((ab + 128.0) * 257.0);
|
||||
}
|
||||
|
||||
void CMSEXPORT cmsFloat2LabEncoded(cmsUInt16Number wLab[3], const cmsCIELab* fLab)
|
||||
{
|
||||
cmsCIELab Lab;
|
||||
|
||||
Lab.L = Clamp_L_doubleV4(fLab ->L);
|
||||
Lab.a = Clamp_ab_doubleV4(fLab ->a);
|
||||
Lab.b = Clamp_ab_doubleV4(fLab ->b);
|
||||
|
||||
wLab[0] = L2Fix4(Lab.L);
|
||||
wLab[1] = ab2Fix4(Lab.a);
|
||||
wLab[2] = ab2Fix4(Lab.b);
|
||||
}
|
||||
|
||||
// Auxiliar: convert to Radians
|
||||
static
|
||||
cmsFloat64Number RADIANS(cmsFloat64Number deg)
|
||||
{
|
||||
return (deg * M_PI) / 180.;
|
||||
}
|
||||
|
||||
|
||||
// Auxiliar: atan2 but operating in degrees and returning 0 if a==b==0
|
||||
static
|
||||
cmsFloat64Number atan2deg(cmsFloat64Number a, cmsFloat64Number b)
|
||||
{
|
||||
cmsFloat64Number h;
|
||||
|
||||
if (a == 0 && b == 0)
|
||||
h = 0;
|
||||
else
|
||||
h = atan2(a, b);
|
||||
|
||||
h *= (180. / M_PI);
|
||||
|
||||
while (h > 360.)
|
||||
h -= 360.;
|
||||
|
||||
while ( h < 0)
|
||||
h += 360.;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
// Auxiliar: Square
|
||||
static
|
||||
cmsFloat64Number Sqr(cmsFloat64Number v)
|
||||
{
|
||||
return v * v;
|
||||
}
|
||||
// From cylindrical coordinates. No check is performed, then negative values are allowed
|
||||
void CMSEXPORT cmsLab2LCh(cmsCIELCh* LCh, const cmsCIELab* Lab)
|
||||
{
|
||||
LCh -> L = Lab -> L;
|
||||
LCh -> C = pow(Sqr(Lab ->a) + Sqr(Lab ->b), 0.5);
|
||||
LCh -> h = atan2deg(Lab ->b, Lab ->a);
|
||||
}
|
||||
|
||||
|
||||
// To cylindrical coordinates. No check is performed, then negative values are allowed
|
||||
void CMSEXPORT cmsLCh2Lab(cmsCIELab* Lab, const cmsCIELCh* LCh)
|
||||
{
|
||||
cmsFloat64Number h = (LCh -> h * M_PI) / 180.0;
|
||||
|
||||
Lab -> L = LCh -> L;
|
||||
Lab -> a = LCh -> C * cos(h);
|
||||
Lab -> b = LCh -> C * sin(h);
|
||||
}
|
||||
|
||||
// In XYZ All 3 components are encoded using 1.15 fixed point
|
||||
static
|
||||
cmsUInt16Number XYZ2Fix(cmsFloat64Number d)
|
||||
{
|
||||
return _cmsQuickSaturateWord(d * 32768.0);
|
||||
}
|
||||
|
||||
void CMSEXPORT cmsFloat2XYZEncoded(cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ)
|
||||
{
|
||||
cmsCIEXYZ xyz;
|
||||
|
||||
xyz.X = fXYZ -> X;
|
||||
xyz.Y = fXYZ -> Y;
|
||||
xyz.Z = fXYZ -> Z;
|
||||
|
||||
// Clamp to encodeable values.
|
||||
if (xyz.Y <= 0) {
|
||||
|
||||
xyz.X = 0;
|
||||
xyz.Y = 0;
|
||||
xyz.Z = 0;
|
||||
}
|
||||
|
||||
if (xyz.X > MAX_ENCODEABLE_XYZ)
|
||||
xyz.X = MAX_ENCODEABLE_XYZ;
|
||||
|
||||
if (xyz.X < 0)
|
||||
xyz.X = 0;
|
||||
|
||||
if (xyz.Y > MAX_ENCODEABLE_XYZ)
|
||||
xyz.Y = MAX_ENCODEABLE_XYZ;
|
||||
|
||||
if (xyz.Y < 0)
|
||||
xyz.Y = 0;
|
||||
|
||||
if (xyz.Z > MAX_ENCODEABLE_XYZ)
|
||||
xyz.Z = MAX_ENCODEABLE_XYZ;
|
||||
|
||||
if (xyz.Z < 0)
|
||||
xyz.Z = 0;
|
||||
|
||||
|
||||
XYZ[0] = XYZ2Fix(xyz.X);
|
||||
XYZ[1] = XYZ2Fix(xyz.Y);
|
||||
XYZ[2] = XYZ2Fix(xyz.Z);
|
||||
}
|
||||
|
||||
|
||||
// To convert from Fixed 1.15 point to cmsFloat64Number
|
||||
static
|
||||
cmsFloat64Number XYZ2float(cmsUInt16Number v)
|
||||
{
|
||||
cmsS15Fixed16Number fix32;
|
||||
|
||||
// From 1.15 to 15.16
|
||||
fix32 = v << 1;
|
||||
|
||||
// From fixed 15.16 to cmsFloat64Number
|
||||
return _cms15Fixed16toDouble(fix32);
|
||||
}
|
||||
|
||||
|
||||
void CMSEXPORT cmsXYZEncoded2Float(cmsCIEXYZ* fXYZ, const cmsUInt16Number XYZ[3])
|
||||
{
|
||||
fXYZ -> X = XYZ2float(XYZ[0]);
|
||||
fXYZ -> Y = XYZ2float(XYZ[1]);
|
||||
fXYZ -> Z = XYZ2float(XYZ[2]);
|
||||
}
|
||||
|
||||
|
||||
// Returns dE on two Lab values
|
||||
cmsFloat64Number CMSEXPORT cmsDeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2)
|
||||
{
|
||||
cmsFloat64Number dL, da, db;
|
||||
|
||||
dL = fabs(Lab1 -> L - Lab2 -> L);
|
||||
da = fabs(Lab1 -> a - Lab2 -> a);
|
||||
db = fabs(Lab1 -> b - Lab2 -> b);
|
||||
|
||||
return pow(Sqr(dL) + Sqr(da) + Sqr(db), 0.5);
|
||||
}
|
||||
|
||||
|
||||
// Return the CIE94 Delta E
|
||||
cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2)
|
||||
{
|
||||
cmsCIELCh LCh1, LCh2;
|
||||
cmsFloat64Number dE, dL, dC, dh, dhsq;
|
||||
cmsFloat64Number c12, sc, sh;
|
||||
|
||||
dL = fabs(Lab1 ->L - Lab2 ->L);
|
||||
|
||||
cmsLab2LCh(&LCh1, Lab1);
|
||||
cmsLab2LCh(&LCh2, Lab2);
|
||||
|
||||
dC = fabs(LCh1.C - LCh2.C);
|
||||
dE = cmsDeltaE(Lab1, Lab2);
|
||||
|
||||
dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC);
|
||||
if (dhsq < 0)
|
||||
dh = 0;
|
||||
else
|
||||
dh = pow(dhsq, 0.5);
|
||||
|
||||
c12 = sqrt(LCh1.C * LCh2.C);
|
||||
|
||||
sc = 1.0 + (0.048 * c12);
|
||||
sh = 1.0 + (0.014 * c12);
|
||||
|
||||
return sqrt(Sqr(dL) + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh));
|
||||
}
|
||||
|
||||
|
||||
// Auxiliary
|
||||
static
|
||||
cmsFloat64Number ComputeLBFD(const cmsCIELab* Lab)
|
||||
{
|
||||
cmsFloat64Number yt;
|
||||
|
||||
if (Lab->L > 7.996969)
|
||||
yt = (Sqr((Lab->L+16)/116)*((Lab->L+16)/116))*100;
|
||||
else
|
||||
yt = 100 * (Lab->L / 903.3);
|
||||
|
||||
return (54.6 * (M_LOG10E * (log(yt + 1.5))) - 9.6);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// bfd - gets BFD(1:1) difference between Lab1, Lab2
|
||||
cmsFloat64Number CMSEXPORT cmsBFDdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2)
|
||||
{
|
||||
cmsFloat64Number lbfd1,lbfd2,AveC,Aveh,dE,deltaL,
|
||||
deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd;
|
||||
cmsCIELCh LCh1, LCh2;
|
||||
|
||||
|
||||
lbfd1 = ComputeLBFD(Lab1);
|
||||
lbfd2 = ComputeLBFD(Lab2);
|
||||
deltaL = lbfd2 - lbfd1;
|
||||
|
||||
cmsLab2LCh(&LCh1, Lab1);
|
||||
cmsLab2LCh(&LCh2, Lab2);
|
||||
|
||||
deltaC = LCh2.C - LCh1.C;
|
||||
AveC = (LCh1.C+LCh2.C)/2;
|
||||
Aveh = (LCh1.h+LCh2.h)/2;
|
||||
|
||||
dE = cmsDeltaE(Lab1, Lab2);
|
||||
|
||||
if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC)))
|
||||
deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC));
|
||||
else
|
||||
deltah =0;
|
||||
|
||||
|
||||
dc = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521;
|
||||
g = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000));
|
||||
t = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))-
|
||||
0.040*cos((2*Aveh-136)/(180/M_PI))+
|
||||
0.070*cos((3*Aveh-31)/(180/M_PI))+
|
||||
0.049*cos((4*Aveh+114)/(180/M_PI))-
|
||||
0.015*cos((5*Aveh-103)/(180/M_PI)));
|
||||
|
||||
dh = dc*(g*t+1-g);
|
||||
rh = -0.260*cos((Aveh-308)/(180/M_PI))-
|
||||
0.379*cos((2*Aveh-160)/(180/M_PI))-
|
||||
0.636*cos((3*Aveh+254)/(180/M_PI))+
|
||||
0.226*cos((4*Aveh+140)/(180/M_PI))-
|
||||
0.194*cos((5*Aveh+280)/(180/M_PI));
|
||||
|
||||
rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000));
|
||||
rt = rh*rc;
|
||||
|
||||
bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh)));
|
||||
|
||||
return bfd;
|
||||
}
|
||||
|
||||
|
||||
// cmc - CMC(l:c) difference between Lab1, Lab2
|
||||
cmsFloat64Number CMSEXPORT cmsCMCdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c)
|
||||
{
|
||||
cmsFloat64Number dE,dL,dC,dh,sl,sc,sh,t,f,cmc;
|
||||
cmsCIELCh LCh1, LCh2;
|
||||
|
||||
if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
|
||||
|
||||
cmsLab2LCh(&LCh1, Lab1);
|
||||
cmsLab2LCh(&LCh2, Lab2);
|
||||
|
||||
|
||||
dL = Lab2->L-Lab1->L;
|
||||
dC = LCh2.C-LCh1.C;
|
||||
|
||||
dE = cmsDeltaE(Lab1, Lab2);
|
||||
|
||||
if (Sqr(dE)>(Sqr(dL)+Sqr(dC)))
|
||||
dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC));
|
||||
else
|
||||
dh =0;
|
||||
|
||||
if ((LCh1.h > 164) && (LCh1.h < 345))
|
||||
t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI))));
|
||||
else
|
||||
t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI))));
|
||||
|
||||
sc = 0.0638 * LCh1.C / (1 + 0.0131 * LCh1.C) + 0.638;
|
||||
sl = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L);
|
||||
|
||||
if (Lab1->L<16)
|
||||
sl = 0.511;
|
||||
|
||||
f = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900));
|
||||
sh = sc*(t*f+1-f);
|
||||
cmc = sqrt(Sqr(dL/(l*sl))+Sqr(dC/(c*sc))+Sqr(dh/sh));
|
||||
|
||||
return cmc;
|
||||
}
|
||||
|
||||
// dE2000 The weightings KL, KC and KH can be modified to reflect the relative
|
||||
// importance of lightness, chroma and hue in different industrial applications
|
||||
cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2,
|
||||
cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh)
|
||||
{
|
||||
cmsFloat64Number L1 = Lab1->L;
|
||||
cmsFloat64Number a1 = Lab1->a;
|
||||
cmsFloat64Number b1 = Lab1->b;
|
||||
cmsFloat64Number C = sqrt( Sqr(a1) + Sqr(b1) );
|
||||
|
||||
cmsFloat64Number Ls = Lab2 ->L;
|
||||
cmsFloat64Number as = Lab2 ->a;
|
||||
cmsFloat64Number bs = Lab2 ->b;
|
||||
cmsFloat64Number Cs = sqrt( Sqr(as) + Sqr(bs) );
|
||||
|
||||
cmsFloat64Number G = 0.5 * ( 1 - sqrt(pow((C + Cs) / 2 , 7.0) / (pow((C + Cs) / 2, 7.0) + pow(25.0, 7.0) ) ));
|
||||
|
||||
cmsFloat64Number a_p = (1 + G ) * a1;
|
||||
cmsFloat64Number b_p = b1;
|
||||
cmsFloat64Number C_p = sqrt( Sqr(a_p) + Sqr(b_p));
|
||||
cmsFloat64Number h_p = atan2deg(b_p, a_p);
|
||||
|
||||
|
||||
cmsFloat64Number a_ps = (1 + G) * as;
|
||||
cmsFloat64Number b_ps = bs;
|
||||
cmsFloat64Number C_ps = sqrt(Sqr(a_ps) + Sqr(b_ps));
|
||||
cmsFloat64Number h_ps = atan2deg(b_ps, a_ps);
|
||||
|
||||
cmsFloat64Number meanC_p =(C_p + C_ps) / 2;
|
||||
|
||||
cmsFloat64Number hps_plus_hp = h_ps + h_p;
|
||||
cmsFloat64Number hps_minus_hp = h_ps - h_p;
|
||||
|
||||
cmsFloat64Number meanh_p = fabs(hps_minus_hp) <= 180.000001 ? (hps_plus_hp)/2 :
|
||||
(hps_plus_hp) < 360 ? (hps_plus_hp + 360)/2 :
|
||||
(hps_plus_hp - 360)/2;
|
||||
|
||||
cmsFloat64Number delta_h = (hps_minus_hp) <= -180.000001 ? (hps_minus_hp + 360) :
|
||||
(hps_minus_hp) > 180 ? (hps_minus_hp - 360) :
|
||||
(hps_minus_hp);
|
||||
cmsFloat64Number delta_L = (Ls - L1);
|
||||
cmsFloat64Number delta_C = (C_ps - C_p );
|
||||
|
||||
|
||||
cmsFloat64Number delta_H =2 * sqrt(C_ps*C_p) * sin(RADIANS(delta_h) / 2);
|
||||
|
||||
cmsFloat64Number T = 1 - 0.17 * cos(RADIANS(meanh_p-30))
|
||||
+ 0.24 * cos(RADIANS(2*meanh_p))
|
||||
+ 0.32 * cos(RADIANS(3*meanh_p + 6))
|
||||
- 0.2 * cos(RADIANS(4*meanh_p - 63));
|
||||
|
||||
cmsFloat64Number Sl = 1 + (0.015 * Sqr((Ls + L1) /2- 50) )/ sqrt(20 + Sqr( (Ls+L1)/2 - 50) );
|
||||
|
||||
cmsFloat64Number Sc = 1 + 0.045 * (C_p + C_ps)/2;
|
||||
cmsFloat64Number Sh = 1 + 0.015 * ((C_ps + C_p)/2) * T;
|
||||
|
||||
cmsFloat64Number delta_ro = 30 * exp( -Sqr(((meanh_p - 275 ) / 25)));
|
||||
|
||||
cmsFloat64Number Rc = 2 * sqrt(( pow(meanC_p, 7.0) )/( pow(meanC_p, 7.0) + pow(25.0, 7.0)));
|
||||
|
||||
cmsFloat64Number Rt = -sin(2 * RADIANS(delta_ro)) * Rc;
|
||||
|
||||
cmsFloat64Number deltaE00 = sqrt( Sqr(delta_L /(Sl * Kl)) +
|
||||
Sqr(delta_C/(Sc * Kc)) +
|
||||
Sqr(delta_H/(Sh * Kh)) +
|
||||
Rt*(delta_C/(Sc * Kc)) * (delta_H / (Sh * Kh)));
|
||||
|
||||
return deltaE00;
|
||||
}
|
||||
|
||||
// This function returns a number of gridpoints to be used as LUT table. It assumes same number
|
||||
// of gripdpoints in all dimensions. Flags may override the choice.
|
||||
int _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags)
|
||||
{
|
||||
int nChannels;
|
||||
|
||||
// Already specified?
|
||||
if (dwFlags & 0x00FF0000) {
|
||||
// Yes, grab'em
|
||||
return (dwFlags >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
nChannels = cmsChannelsOf(Colorspace);
|
||||
|
||||
// HighResPrecalc is maximum resolution
|
||||
if (dwFlags & cmsFLAGS_HIGHRESPRECALC) {
|
||||
|
||||
if (nChannels > 4)
|
||||
return 7; // 7 for Hifi
|
||||
|
||||
if (nChannels == 4) // 23 for CMYK
|
||||
return 23;
|
||||
|
||||
return 49; // 49 for RGB and others
|
||||
}
|
||||
|
||||
|
||||
// LowResPrecal is lower resolution
|
||||
if (dwFlags & cmsFLAGS_LOWRESPRECALC) {
|
||||
|
||||
if (nChannels > 4)
|
||||
return 6; // 6 for more than 4 channels
|
||||
|
||||
if (nChannels == 1)
|
||||
return 33; // For monochrome
|
||||
|
||||
return 17; // 17 for remaining
|
||||
}
|
||||
|
||||
// Default values
|
||||
if (nChannels > 4)
|
||||
return 7; // 7 for Hifi
|
||||
|
||||
if (nChannels == 4)
|
||||
return 17; // 17 for CMYK
|
||||
|
||||
return 33; // 33 for RGB
|
||||
}
|
||||
|
||||
|
||||
cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space,
|
||||
cmsUInt16Number **White,
|
||||
cmsUInt16Number **Black,
|
||||
cmsUInt32Number *nOutputs)
|
||||
{
|
||||
// Only most common spaces
|
||||
|
||||
static cmsUInt16Number RGBblack[4] = { 0, 0, 0 };
|
||||
static cmsUInt16Number RGBwhite[4] = { 0xffff, 0xffff, 0xffff };
|
||||
static cmsUInt16Number CMYKblack[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; // 400% of ink
|
||||
static cmsUInt16Number CMYKwhite[4] = { 0, 0, 0, 0 };
|
||||
static cmsUInt16Number LABblack[4] = { 0, 0x8080, 0x8080 }; // V4 Lab encoding
|
||||
static cmsUInt16Number LABwhite[4] = { 0xFFFF, 0x8080, 0x8080 };
|
||||
static cmsUInt16Number CMYblack[4] = { 0xffff, 0xffff, 0xffff };
|
||||
static cmsUInt16Number CMYwhite[4] = { 0, 0, 0 };
|
||||
static cmsUInt16Number Grayblack[4] = { 0 };
|
||||
static cmsUInt16Number GrayWhite[4] = { 0xffff };
|
||||
|
||||
switch (Space) {
|
||||
|
||||
case cmsSigGrayData: if (White) *White = GrayWhite;
|
||||
if (Black) *Black = Grayblack;
|
||||
if (nOutputs) *nOutputs = 1;
|
||||
return TRUE;
|
||||
|
||||
case cmsSigRgbData: if (White) *White = RGBwhite;
|
||||
if (Black) *Black = RGBblack;
|
||||
if (nOutputs) *nOutputs = 3;
|
||||
return TRUE;
|
||||
|
||||
case cmsSigLabData: if (White) *White = LABwhite;
|
||||
if (Black) *Black = LABblack;
|
||||
if (nOutputs) *nOutputs = 3;
|
||||
return TRUE;
|
||||
|
||||
case cmsSigCmykData: if (White) *White = CMYKwhite;
|
||||
if (Black) *Black = CMYKblack;
|
||||
if (nOutputs) *nOutputs = 4;
|
||||
return TRUE;
|
||||
|
||||
case cmsSigCmyData: if (White) *White = CMYwhite;
|
||||
if (Black) *Black = CMYblack;
|
||||
if (nOutputs) *nOutputs = 3;
|
||||
return TRUE;
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Several utilities -------------------------------------------------------
|
||||
|
||||
// Translate from our colorspace to ICC representation
|
||||
|
||||
cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation)
|
||||
{
|
||||
switch (OurNotation) {
|
||||
|
||||
case 1:
|
||||
case PT_GRAY: return cmsSigGrayData;
|
||||
|
||||
case 2:
|
||||
case PT_RGB: return cmsSigRgbData;
|
||||
|
||||
case PT_CMY: return cmsSigCmyData;
|
||||
case PT_CMYK: return cmsSigCmykData;
|
||||
case PT_YCbCr:return cmsSigYCbCrData;
|
||||
case PT_YUV: return cmsSigLuvData;
|
||||
case PT_XYZ: return cmsSigXYZData;
|
||||
|
||||
case PT_LabV2:
|
||||
case PT_Lab: return cmsSigLabData;
|
||||
|
||||
case PT_YUVK: return cmsSigLuvKData;
|
||||
case PT_HSV: return cmsSigHsvData;
|
||||
case PT_HLS: return cmsSigHlsData;
|
||||
case PT_Yxy: return cmsSigYxyData;
|
||||
|
||||
case PT_MCH1: return cmsSigMCH1Data;
|
||||
case PT_MCH2: return cmsSigMCH2Data;
|
||||
case PT_MCH3: return cmsSigMCH3Data;
|
||||
case PT_MCH4: return cmsSigMCH4Data;
|
||||
case PT_MCH5: return cmsSigMCH5Data;
|
||||
case PT_MCH6: return cmsSigMCH6Data;
|
||||
case PT_MCH7: return cmsSigMCH7Data;
|
||||
case PT_MCH8: return cmsSigMCH8Data;
|
||||
|
||||
case PT_MCH9: return cmsSigMCH9Data;
|
||||
case PT_MCH10: return cmsSigMCHAData;
|
||||
case PT_MCH11: return cmsSigMCHBData;
|
||||
case PT_MCH12: return cmsSigMCHCData;
|
||||
case PT_MCH13: return cmsSigMCHDData;
|
||||
case PT_MCH14: return cmsSigMCHEData;
|
||||
case PT_MCH15: return cmsSigMCHFData;
|
||||
|
||||
default: return (cmsColorSpaceSignature) (-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace)
|
||||
{
|
||||
switch (ProfileSpace) {
|
||||
|
||||
case cmsSigGrayData: return PT_GRAY;
|
||||
case cmsSigRgbData: return PT_RGB;
|
||||
case cmsSigCmyData: return PT_CMY;
|
||||
case cmsSigCmykData: return PT_CMYK;
|
||||
case cmsSigYCbCrData:return PT_YCbCr;
|
||||
case cmsSigLuvData: return PT_YUV;
|
||||
case cmsSigXYZData: return PT_XYZ;
|
||||
case cmsSigLabData: return PT_Lab;
|
||||
case cmsSigLuvKData: return PT_YUVK;
|
||||
case cmsSigHsvData: return PT_HSV;
|
||||
case cmsSigHlsData: return PT_HLS;
|
||||
case cmsSigYxyData: return PT_Yxy;
|
||||
|
||||
case cmsSig1colorData:
|
||||
case cmsSigMCH1Data: return PT_MCH1;
|
||||
|
||||
case cmsSig2colorData:
|
||||
case cmsSigMCH2Data: return PT_MCH2;
|
||||
|
||||
case cmsSig3colorData:
|
||||
case cmsSigMCH3Data: return PT_MCH3;
|
||||
|
||||
case cmsSig4colorData:
|
||||
case cmsSigMCH4Data: return PT_MCH4;
|
||||
|
||||
case cmsSig5colorData:
|
||||
case cmsSigMCH5Data: return PT_MCH5;
|
||||
|
||||
case cmsSig6colorData:
|
||||
case cmsSigMCH6Data: return PT_MCH6;
|
||||
|
||||
case cmsSigMCH7Data:
|
||||
case cmsSig7colorData:return PT_MCH7;
|
||||
|
||||
case cmsSigMCH8Data:
|
||||
case cmsSig8colorData:return PT_MCH8;
|
||||
|
||||
case cmsSigMCH9Data:
|
||||
case cmsSig9colorData:return PT_MCH9;
|
||||
|
||||
case cmsSigMCHAData:
|
||||
case cmsSig10colorData:return PT_MCH10;
|
||||
|
||||
case cmsSigMCHBData:
|
||||
case cmsSig11colorData:return PT_MCH11;
|
||||
|
||||
case cmsSigMCHCData:
|
||||
case cmsSig12colorData:return PT_MCH12;
|
||||
|
||||
case cmsSigMCHDData:
|
||||
case cmsSig13colorData:return PT_MCH13;
|
||||
|
||||
case cmsSigMCHEData:
|
||||
case cmsSig14colorData:return PT_MCH14;
|
||||
|
||||
case cmsSigMCHFData:
|
||||
case cmsSig15colorData:return PT_MCH15;
|
||||
|
||||
default: return (cmsColorSpaceSignature) (-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace)
|
||||
{
|
||||
switch (ColorSpace) {
|
||||
|
||||
case cmsSigGrayData: return 1;
|
||||
|
||||
case cmsSig2colorData: return 2;
|
||||
|
||||
case cmsSigXYZData:
|
||||
case cmsSigLabData:
|
||||
case cmsSigLuvData:
|
||||
case cmsSigYCbCrData:
|
||||
case cmsSigYxyData:
|
||||
case cmsSigRgbData:
|
||||
case cmsSigHsvData:
|
||||
case cmsSigHlsData:
|
||||
case cmsSigCmyData:
|
||||
case cmsSig3colorData: return 3;
|
||||
|
||||
case cmsSigLuvKData:
|
||||
case cmsSigCmykData:
|
||||
case cmsSig4colorData: return 4;
|
||||
|
||||
case cmsSigMCH5Data:
|
||||
case cmsSig5colorData: return 5;
|
||||
|
||||
case cmsSigMCH6Data:
|
||||
case cmsSig6colorData: return 6;
|
||||
|
||||
case cmsSigMCH7Data:
|
||||
case cmsSig7colorData: return 7;
|
||||
|
||||
case cmsSigMCH8Data:
|
||||
case cmsSig8colorData: return 8;
|
||||
|
||||
case cmsSigMCH9Data:
|
||||
case cmsSig9colorData: return 9;
|
||||
|
||||
case cmsSigMCHAData:
|
||||
case cmsSig10colorData: return 10;
|
||||
|
||||
case cmsSigMCHBData:
|
||||
case cmsSig11colorData: return 11;
|
||||
|
||||
case cmsSigMCHCData:
|
||||
case cmsSig12colorData: return 12;
|
||||
|
||||
case cmsSigMCHDData:
|
||||
case cmsSig13colorData: return 13;
|
||||
|
||||
case cmsSigMCHEData:
|
||||
case cmsSig14colorData: return 14;
|
||||
|
||||
case cmsSigMCHFData:
|
||||
case cmsSig15colorData: return 15;
|
||||
|
||||
default: return 3;
|
||||
}
|
||||
}
|
||||
612
thirdparty/liblcms2/src/cmsplugin.c
vendored
Normal file
612
thirdparty/liblcms2/src/cmsplugin.c
vendored
Normal file
@@ -0,0 +1,612 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
// Encoding & Decoding support functions
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
// Little-Endian to Big-Endian
|
||||
|
||||
// Adjust a word value after being readed/ before being written from/to an ICC profile
|
||||
cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word)
|
||||
{
|
||||
#ifndef CMS_USE_BIG_ENDIAN
|
||||
|
||||
cmsUInt8Number* pByte = (cmsUInt8Number*) &Word;
|
||||
cmsUInt8Number tmp;
|
||||
|
||||
tmp = pByte[0];
|
||||
pByte[0] = pByte[1];
|
||||
pByte[1] = tmp;
|
||||
#endif
|
||||
|
||||
return Word;
|
||||
}
|
||||
|
||||
|
||||
// Transports to properly encoded values - note that icc profiles does use big endian notation.
|
||||
|
||||
// 1 2 3 4
|
||||
// 4 3 2 1
|
||||
|
||||
cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number DWord)
|
||||
{
|
||||
#ifndef CMS_USE_BIG_ENDIAN
|
||||
|
||||
cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
|
||||
cmsUInt8Number temp1;
|
||||
cmsUInt8Number temp2;
|
||||
|
||||
temp1 = *pByte++;
|
||||
temp2 = *pByte++;
|
||||
*(pByte-1) = *pByte;
|
||||
*pByte++ = temp2;
|
||||
*(pByte-3) = *pByte;
|
||||
*pByte = temp1;
|
||||
#endif
|
||||
return DWord;
|
||||
}
|
||||
|
||||
// 1 2 3 4 5 6 7 8
|
||||
// 8 7 6 5 4 3 2 1
|
||||
|
||||
void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number QWord)
|
||||
{
|
||||
|
||||
#ifndef CMS_USE_BIG_ENDIAN
|
||||
|
||||
cmsUInt8Number* pIn = (cmsUInt8Number*) &QWord;
|
||||
cmsUInt8Number* pOut = (cmsUInt8Number*) Result;
|
||||
|
||||
_cmsAssert(Result != NULL);
|
||||
|
||||
pOut[7] = pIn[0];
|
||||
pOut[6] = pIn[1];
|
||||
pOut[5] = pIn[2];
|
||||
pOut[4] = pIn[3];
|
||||
pOut[3] = pIn[4];
|
||||
pOut[2] = pIn[5];
|
||||
pOut[1] = pIn[6];
|
||||
pOut[0] = pIn[7];
|
||||
|
||||
#else
|
||||
|
||||
_cmsAssert(Result != NULL);
|
||||
|
||||
*Result = QWord;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Auxiliar -- read 8, 16 and 32-bit numbers
|
||||
cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n)
|
||||
{
|
||||
cmsUInt8Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1)
|
||||
return FALSE;
|
||||
|
||||
if (n != NULL) *n = tmp;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n)
|
||||
{
|
||||
cmsUInt16Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1)
|
||||
return FALSE;
|
||||
|
||||
if (n != NULL) *n = _cmsAdjustEndianess16(tmp);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array)
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
|
||||
if (Array != NULL) {
|
||||
if (!_cmsReadUInt16Number(io, Array + i)) return FALSE;
|
||||
}
|
||||
else {
|
||||
if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n)
|
||||
{
|
||||
cmsUInt32Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
|
||||
return FALSE;
|
||||
|
||||
if (n != NULL) *n = _cmsAdjustEndianess32(tmp);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
|
||||
{
|
||||
cmsUInt32Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
if (io -> Read(io, &tmp, sizeof(cmsFloat32Number), 1) != 1)
|
||||
return FALSE;
|
||||
|
||||
if (n != NULL) {
|
||||
|
||||
tmp = _cmsAdjustEndianess32(tmp);
|
||||
*n = *(cmsFloat32Number*) &tmp;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
|
||||
{
|
||||
cmsUInt64Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
|
||||
return FALSE;
|
||||
|
||||
if (n != NULL) _cmsAdjustEndianess64(n, tmp);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n)
|
||||
{
|
||||
cmsUInt32Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
|
||||
return FALSE;
|
||||
|
||||
if (n != NULL) {
|
||||
*n = _cms15Fixed16toDouble(_cmsAdjustEndianess32(tmp));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Jun-21-2000: Some profiles (those that comes with W2K) comes
|
||||
// with the media white (media black?) x 100. Add a sanity check
|
||||
|
||||
static
|
||||
void NormalizeXYZ(cmsCIEXYZ* Dest)
|
||||
{
|
||||
while (Dest -> X > 2. &&
|
||||
Dest -> Y > 2. &&
|
||||
Dest -> Z > 2.) {
|
||||
|
||||
Dest -> X /= 10.;
|
||||
Dest -> Y /= 10.;
|
||||
Dest -> Z /= 10.;
|
||||
}
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
|
||||
{
|
||||
cmsEncodedXYZNumber xyz;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
|
||||
|
||||
if (XYZ != NULL) {
|
||||
|
||||
XYZ->X = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.X));
|
||||
XYZ->Y = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Y));
|
||||
XYZ->Z = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Z));
|
||||
|
||||
NormalizeXYZ(XYZ);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n)
|
||||
{
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n)
|
||||
{
|
||||
cmsUInt16Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
tmp = _cmsAdjustEndianess16(n);
|
||||
if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
_cmsAssert(Array != NULL);
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n)
|
||||
{
|
||||
cmsUInt32Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
tmp = _cmsAdjustEndianess32(n);
|
||||
if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
|
||||
{
|
||||
cmsUInt32Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
tmp = *(cmsUInt32Number*) &n;
|
||||
tmp = _cmsAdjustEndianess32(tmp);
|
||||
if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number n)
|
||||
{
|
||||
cmsUInt64Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
_cmsAdjustEndianess64(&tmp, n);
|
||||
if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n)
|
||||
{
|
||||
cmsUInt32Number tmp;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
tmp = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(n));
|
||||
if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
|
||||
{
|
||||
cmsEncodedXYZNumber xyz;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
_cmsAssert(XYZ != NULL);
|
||||
|
||||
xyz.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->X));
|
||||
xyz.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Y));
|
||||
xyz.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Z));
|
||||
|
||||
return io -> Write(io, sizeof(cmsEncodedXYZNumber), &xyz);
|
||||
}
|
||||
|
||||
// from Fixed point 8.8 to double
|
||||
cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
|
||||
{
|
||||
cmsUInt8Number msb, lsb;
|
||||
|
||||
lsb = (cmsUInt8Number) (fixed8 & 0xff);
|
||||
msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff);
|
||||
|
||||
return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0));
|
||||
}
|
||||
|
||||
cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
|
||||
{
|
||||
cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val);
|
||||
return (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
|
||||
}
|
||||
|
||||
// from Fixed point 15.16 to double
|
||||
cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
|
||||
{
|
||||
cmsFloat64Number floater, sign, mid;
|
||||
int Whole, FracPart;
|
||||
|
||||
sign = (fix32 < 0 ? -1 : 1);
|
||||
fix32 = abs(fix32);
|
||||
|
||||
Whole = (cmsUInt16Number)(fix32 >> 16) & 0xffff;
|
||||
FracPart = (cmsUInt16Number)(fix32 & 0xffff);
|
||||
|
||||
mid = (cmsFloat64Number) FracPart / 65536.0;
|
||||
floater = (cmsFloat64Number) Whole + mid;
|
||||
|
||||
return sign * floater;
|
||||
}
|
||||
|
||||
// from double to Fixed point 15.16
|
||||
cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v)
|
||||
{
|
||||
return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
|
||||
}
|
||||
|
||||
// Date/Time functions
|
||||
|
||||
void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest)
|
||||
{
|
||||
|
||||
_cmsAssert(Dest != NULL);
|
||||
_cmsAssert(Source != NULL);
|
||||
|
||||
Dest->tm_sec = _cmsAdjustEndianess16(Source->seconds);
|
||||
Dest->tm_min = _cmsAdjustEndianess16(Source->minutes);
|
||||
Dest->tm_hour = _cmsAdjustEndianess16(Source->hours);
|
||||
Dest->tm_mday = _cmsAdjustEndianess16(Source->day);
|
||||
Dest->tm_mon = _cmsAdjustEndianess16(Source->month) - 1;
|
||||
Dest->tm_year = _cmsAdjustEndianess16(Source->year) - 1900;
|
||||
Dest->tm_wday = -1;
|
||||
Dest->tm_yday = -1;
|
||||
Dest->tm_isdst = 0;
|
||||
}
|
||||
|
||||
void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source)
|
||||
{
|
||||
_cmsAssert(Dest != NULL);
|
||||
_cmsAssert(Source != NULL);
|
||||
|
||||
Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec);
|
||||
Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min);
|
||||
Dest->hours = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour);
|
||||
Dest->day = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday);
|
||||
Dest->month = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1));
|
||||
Dest->year = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900));
|
||||
}
|
||||
|
||||
// Read base and return type base
|
||||
cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io)
|
||||
{
|
||||
_cmsTagBase Base;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1)
|
||||
return (cmsTagTypeSignature) 0;
|
||||
|
||||
return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
|
||||
}
|
||||
|
||||
// Setup base marker
|
||||
cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig)
|
||||
{
|
||||
_cmsTagBase Base;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
|
||||
memset(&Base.reserved, 0, sizeof(Base.reserved));
|
||||
return io -> Write(io, sizeof(_cmsTagBase), &Base);
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io)
|
||||
{
|
||||
cmsUInt8Number Buffer[4];
|
||||
cmsUInt32Number NextAligned, At;
|
||||
cmsUInt32Number BytesToNextAlignedPos;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
At = io -> Tell(io);
|
||||
NextAligned = _cmsALIGNLONG(At);
|
||||
BytesToNextAlignedPos = NextAligned - At;
|
||||
if (BytesToNextAlignedPos == 0) return TRUE;
|
||||
if (BytesToNextAlignedPos > 4) return FALSE;
|
||||
|
||||
return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1);
|
||||
}
|
||||
|
||||
cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io)
|
||||
{
|
||||
cmsUInt8Number Buffer[4];
|
||||
cmsUInt32Number NextAligned, At;
|
||||
cmsUInt32Number BytesToNextAlignedPos;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
|
||||
At = io -> Tell(io);
|
||||
NextAligned = _cmsALIGNLONG(At);
|
||||
BytesToNextAlignedPos = NextAligned - At;
|
||||
if (BytesToNextAlignedPos == 0) return TRUE;
|
||||
if (BytesToNextAlignedPos > 4) return FALSE;
|
||||
|
||||
memset(Buffer, 0, BytesToNextAlignedPos);
|
||||
return io -> Write(io, BytesToNextAlignedPos, Buffer);
|
||||
}
|
||||
|
||||
|
||||
// To deal with text streams. 2K at most
|
||||
cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
|
||||
{
|
||||
va_list args;
|
||||
int len;
|
||||
cmsUInt8Number Buffer[2048];
|
||||
cmsBool rc;
|
||||
|
||||
_cmsAssert(io != NULL);
|
||||
_cmsAssert(frm != NULL);
|
||||
|
||||
va_start(args, frm);
|
||||
|
||||
len = vsnprintf((char*) Buffer, 2047, frm, args);
|
||||
if (len < 0) return FALSE; // Truncated, which is a fatal error for us
|
||||
|
||||
rc = io ->Write(io, len, Buffer);
|
||||
|
||||
va_end(args);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// Plugin memory management -------------------------------------------------------------------------------------------------
|
||||
|
||||
static _cmsSubAllocator* PluginPool = NULL;
|
||||
|
||||
// Specialized malloc for plug-ins, that is freed upon exit.
|
||||
void* _cmsPluginMalloc(cmsUInt32Number size)
|
||||
{
|
||||
if (PluginPool == NULL)
|
||||
PluginPool = _cmsCreateSubAlloc(0, 4*1024);
|
||||
|
||||
return _cmsSubAlloc(PluginPool, size);
|
||||
}
|
||||
|
||||
|
||||
// Main plug-in dispatcher
|
||||
cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
|
||||
{
|
||||
cmsPluginBase* Plugin;
|
||||
|
||||
for (Plugin = (cmsPluginBase*) Plug_in;
|
||||
Plugin != NULL;
|
||||
Plugin = Plugin -> Next) {
|
||||
|
||||
if (Plugin -> Magic != cmsPluginMagicNumber) {
|
||||
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (Plugin ->ExpectedVersion > LCMS_VERSION) {
|
||||
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
|
||||
Plugin ->ExpectedVersion, LCMS_VERSION);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (Plugin -> Type) {
|
||||
|
||||
case cmsPluginMemHandlerSig:
|
||||
if (!_cmsRegisterMemHandlerPlugin(Plugin)) return FALSE;
|
||||
break;
|
||||
|
||||
case cmsPluginInterpolationSig:
|
||||
if (!_cmsRegisterInterpPlugin(Plugin)) return FALSE;
|
||||
break;
|
||||
|
||||
case cmsPluginTagTypeSig:
|
||||
if (!_cmsRegisterTagTypePlugin(Plugin)) return FALSE;
|
||||
break;
|
||||
|
||||
case cmsPluginTagSig:
|
||||
if (!_cmsRegisterTagPlugin(Plugin)) return FALSE;
|
||||
break;
|
||||
|
||||
case cmsPluginFormattersSig:
|
||||
if (!_cmsRegisterFormattersPlugin(Plugin)) return FALSE;
|
||||
break;
|
||||
|
||||
case cmsPluginRenderingIntentSig:
|
||||
if (!_cmsRegisterRenderingIntentPlugin(Plugin)) return FALSE;
|
||||
break;
|
||||
|
||||
case cmsPluginParametricCurveSig:
|
||||
if (!_cmsRegisterParametricCurvesPlugin(Plugin)) return FALSE;
|
||||
break;
|
||||
|
||||
case cmsPluginMultiProcessElementSig:
|
||||
if (!_cmsRegisterMultiProcessElementPlugin(Plugin)) return FALSE;
|
||||
break;
|
||||
|
||||
case cmsPluginOptimizationSig:
|
||||
if (!_cmsRegisterOptimizationPlugin(Plugin)) return FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep a reference to the plug-in
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Revert all plug-ins to default
|
||||
void CMSEXPORT cmsUnregisterPlugins(void)
|
||||
{
|
||||
_cmsRegisterMemHandlerPlugin(NULL);
|
||||
_cmsRegisterInterpPlugin(NULL);
|
||||
_cmsRegisterTagTypePlugin(NULL);
|
||||
_cmsRegisterTagPlugin(NULL);
|
||||
_cmsRegisterFormattersPlugin(NULL);
|
||||
_cmsRegisterRenderingIntentPlugin(NULL);
|
||||
_cmsRegisterParametricCurvesPlugin(NULL);
|
||||
_cmsRegisterMultiProcessElementPlugin(NULL);
|
||||
_cmsRegisterOptimizationPlugin(NULL);
|
||||
|
||||
if (PluginPool != NULL)
|
||||
_cmsSubAllocDestroy(PluginPool);
|
||||
|
||||
PluginPool = NULL;
|
||||
}
|
||||
1595
thirdparty/liblcms2/src/cmsps2.c
vendored
Normal file
1595
thirdparty/liblcms2/src/cmsps2.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
266
thirdparty/liblcms2/src/cmssamp.c
vendored
Normal file
266
thirdparty/liblcms2/src/cmssamp.c
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
|
||||
|
||||
// This file contains routines for resampling and LUT optimization, black point detection
|
||||
// and black preservation.
|
||||
|
||||
// Black point detection -------------------------------------------------------------------------
|
||||
|
||||
|
||||
// PCS -> PCS round trip transform, always uses relative intent on the device -> pcs
|
||||
static
|
||||
cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent)
|
||||
{
|
||||
cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
|
||||
cmsHTRANSFORM xform;
|
||||
cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE };
|
||||
cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 };
|
||||
cmsHPROFILE hProfiles[4];
|
||||
cmsUInt32Number Intents[4];
|
||||
cmsContext ContextID = cmsGetProfileContextID(hProfile);
|
||||
|
||||
hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab;
|
||||
Intents[0] = INTENT_RELATIVE_COLORIMETRIC; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = INTENT_RELATIVE_COLORIMETRIC;
|
||||
|
||||
xform = cmsCreateExtendedTransform(ContextID, 4, hProfiles, BPC, Intents,
|
||||
States, NULL, 0, TYPE_Lab_DBL, TYPE_Lab_DBL, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
|
||||
|
||||
cmsCloseProfile(hLab);
|
||||
return xform;
|
||||
}
|
||||
|
||||
// Use darker colorants to obtain black point. This works in the relative colorimetric intent and
|
||||
// assumes more ink results in darker colors. No ink limit is assumed.
|
||||
static
|
||||
cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput,
|
||||
cmsUInt32Number Intent,
|
||||
cmsCIEXYZ* BlackPoint,
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
cmsUInt16Number *Black;
|
||||
cmsHTRANSFORM xform;
|
||||
cmsColorSpaceSignature Space;
|
||||
cmsUInt32Number nChannels;
|
||||
cmsUInt32Number dwFormat;
|
||||
cmsHPROFILE hLab;
|
||||
cmsCIELab Lab;
|
||||
cmsCIEXYZ BlackXYZ;
|
||||
cmsContext ContextID = cmsGetProfileContextID(hInput);
|
||||
|
||||
// If the profile does not support input direction, assume Black point 0
|
||||
if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) {
|
||||
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Create a formatter which has n channels and floating point
|
||||
dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2, FALSE);
|
||||
|
||||
// Try to get black by using black colorant
|
||||
Space = cmsGetColorSpace(hInput);
|
||||
|
||||
// This function returns darker colorant in 16 bits for several spaces
|
||||
if (!_cmsEndPointsBySpace(Space, NULL, &Black, &nChannels)) {
|
||||
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (nChannels != T_CHANNELS(dwFormat)) {
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Lab will be used as the output space, but lab2 will avoid recursion
|
||||
hLab = cmsCreateLab2ProfileTHR(ContextID, NULL);
|
||||
if (hLab == NULL) {
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Create the transform
|
||||
xform = cmsCreateTransformTHR(ContextID, hInput, dwFormat,
|
||||
hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
|
||||
cmsCloseProfile(hLab);
|
||||
|
||||
if (xform == NULL) {
|
||||
// Something went wrong. Get rid of open resources and return zero as black
|
||||
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Convert black to Lab
|
||||
cmsDoTransform(xform, Black, &Lab, 1);
|
||||
|
||||
// Force it to be neutral, clip to max. L* of 50
|
||||
Lab.a = Lab.b = 0;
|
||||
if (Lab.L > 50) Lab.L = 50;
|
||||
|
||||
// Free the resources
|
||||
cmsDeleteTransform(xform);
|
||||
|
||||
// Convert from Lab (which is now clipped) to XYZ.
|
||||
cmsLab2XYZ(NULL, &BlackXYZ, &Lab);
|
||||
|
||||
if (BlackPoint != NULL)
|
||||
*BlackPoint = BlackXYZ;
|
||||
|
||||
return TRUE;
|
||||
|
||||
cmsUNUSED_PARAMETER(dwFlags);
|
||||
}
|
||||
|
||||
// Get a black point of output CMYK profile, discounting any ink-limiting embedded
|
||||
// in the profile. For doing that, we use perceptual intent in input direction:
|
||||
// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab
|
||||
static
|
||||
cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile)
|
||||
|
||||
{
|
||||
cmsHTRANSFORM hRoundTrip;
|
||||
cmsCIELab LabIn, LabOut;
|
||||
cmsCIEXYZ BlackXYZ;
|
||||
|
||||
// Is the intent supported by the profile?
|
||||
if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) {
|
||||
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
hRoundTrip = CreateRoundtripXForm(hProfile, INTENT_PERCEPTUAL);
|
||||
if (hRoundTrip == NULL) {
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LabIn.L = LabIn.a = LabIn.b = 0;
|
||||
cmsDoTransform(hRoundTrip, &LabIn, &LabOut, 1);
|
||||
|
||||
// Clip Lab to reasonable limits
|
||||
if (LabOut.L > 50) LabOut.L = 50;
|
||||
LabOut.a = LabOut.b = 0;
|
||||
|
||||
cmsDeleteTransform(hRoundTrip);
|
||||
|
||||
// Convert it to XYZ
|
||||
cmsLab2XYZ(NULL, &BlackXYZ, &LabOut);
|
||||
|
||||
if (BlackPoint != NULL)
|
||||
*BlackPoint = BlackXYZ;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// This function shouldn't exist at all -- there is such quantity of broken
|
||||
// profiles on black point tag, that we must somehow fix chromaticity to
|
||||
// avoid huge tint when doing Black point compensation. This function does
|
||||
// just that. There is a special flag for using black point tag, but turned
|
||||
// off by default because it is bogus on most profiles. The detection algorithm
|
||||
// involves to turn BP to neutral and to use only L component.
|
||||
|
||||
cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
|
||||
{
|
||||
|
||||
// Zero for black point
|
||||
if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) {
|
||||
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// v4 + perceptual & saturation intents does have its own black point, and it is
|
||||
// well specified enough to use it. Black point tag is deprecated in V4.
|
||||
|
||||
if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) &&
|
||||
(Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) {
|
||||
|
||||
// Matrix shaper share MRC & perceptual intents
|
||||
if (cmsIsMatrixShaper(hProfile))
|
||||
return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0);
|
||||
|
||||
// Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents
|
||||
BlackPoint -> X = cmsPERCEPTUAL_BLACK_X;
|
||||
BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y;
|
||||
BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CMS_USE_PROFILE_BLACK_POINT_TAG
|
||||
|
||||
// v2, v4 rel/abs colorimetric
|
||||
if (cmsIsTag(hProfile, cmsSigMediaBlackPointTag) &&
|
||||
Intent == INTENT_RELATIVE_COLORIMETRIC) {
|
||||
|
||||
cmsCIEXYZ *BlackPtr, BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite;
|
||||
cmsCIELab Lab;
|
||||
|
||||
// If black point is specified, then use it,
|
||||
|
||||
BlackPtr = cmsReadTag(hProfile, cmsSigMediaBlackPointTag);
|
||||
if (BlackPtr != NULL) {
|
||||
|
||||
BlackXYZ = *BlackPtr;
|
||||
_cmsReadMediaWhitePoint(&MediaWhite, hProfile);
|
||||
|
||||
// Black point is absolute XYZ, so adapt to D50 to get PCS value
|
||||
cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ);
|
||||
|
||||
// Force a=b=0 to get rid of any chroma
|
||||
cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint);
|
||||
Lab.a = Lab.b = 0;
|
||||
if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50
|
||||
cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab);
|
||||
|
||||
if (BlackPoint != NULL)
|
||||
*BlackPoint = TrustedBlackPoint;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// That is about v2 profiles.
|
||||
|
||||
// If output profile, discount ink-limiting and that's all
|
||||
if (Intent == INTENT_RELATIVE_COLORIMETRIC &&
|
||||
(cmsGetDeviceClass(hProfile) == cmsSigOutputClass) &&
|
||||
(cmsGetColorSpace(hProfile) == cmsSigCmykData))
|
||||
return BlackPointUsingPerceptualBlack(BlackPoint, hProfile);
|
||||
|
||||
// Nope, compute BP using current intent.
|
||||
return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags);
|
||||
}
|
||||
|
||||
|
||||
734
thirdparty/liblcms2/src/cmssm.c
vendored
Normal file
734
thirdparty/liblcms2/src/cmssm.c
vendored
Normal file
@@ -0,0 +1,734 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Gamut boundary description by using Jan Morovic's Segment maxima method
|
||||
// Many thanks to Jan for allowing me to use his algorithm.
|
||||
|
||||
// r = C*
|
||||
// alpha = Hab
|
||||
// theta = L*
|
||||
|
||||
#define SECTORS 16 // number of divisions in alpha and theta
|
||||
|
||||
// Spherical coordinates
|
||||
typedef struct {
|
||||
|
||||
cmsFloat64Number r;
|
||||
cmsFloat64Number alpha;
|
||||
cmsFloat64Number theta;
|
||||
|
||||
} cmsSpherical;
|
||||
|
||||
typedef enum {
|
||||
GP_EMPTY,
|
||||
GP_SPECIFIED,
|
||||
GP_MODELED
|
||||
|
||||
} GDBPointType;
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
GDBPointType Type;
|
||||
cmsSpherical p; // Keep also alpha & theta of maximum
|
||||
|
||||
} cmsGDBPoint;
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmsContext ContextID;
|
||||
cmsGDBPoint Gamut[SECTORS][SECTORS];
|
||||
|
||||
} cmsGDB;
|
||||
|
||||
|
||||
// A line using the parametric form
|
||||
// P = a + t*u
|
||||
typedef struct {
|
||||
|
||||
cmsVEC3 a;
|
||||
cmsVEC3 u;
|
||||
|
||||
} cmsLine;
|
||||
|
||||
|
||||
// A plane using the parametric form
|
||||
// Q = b + r*v + s*w
|
||||
typedef struct {
|
||||
|
||||
cmsVEC3 b;
|
||||
cmsVEC3 v;
|
||||
cmsVEC3 w;
|
||||
|
||||
} cmsPlane;
|
||||
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// ATAN2() which always returns degree positive numbers
|
||||
|
||||
static
|
||||
cmsFloat64Number _cmsAtan2(cmsFloat64Number y, cmsFloat64Number x)
|
||||
{
|
||||
cmsFloat64Number a;
|
||||
|
||||
// Deal with undefined case
|
||||
if (x == 0.0 && y == 0.0) return 0;
|
||||
|
||||
a = (atan2(y, x) * 180.0) / M_PI;
|
||||
|
||||
while (a < 0) {
|
||||
a += 360;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
// Convert to spherical coordinates
|
||||
static
|
||||
void ToSpherical(cmsSpherical* sp, const cmsVEC3* v)
|
||||
{
|
||||
|
||||
cmsFloat64Number L, a, b;
|
||||
|
||||
L = v ->n[VX];
|
||||
a = v ->n[VY];
|
||||
b = v ->n[VZ];
|
||||
|
||||
sp ->r = sqrt( L*L + a*a + b*b );
|
||||
|
||||
if (sp ->r == 0) {
|
||||
sp ->alpha = sp ->theta = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
sp ->alpha = _cmsAtan2(a, b);
|
||||
sp ->theta = _cmsAtan2(sqrt(a*a + b*b), L);
|
||||
}
|
||||
|
||||
|
||||
// Convert to cartesian from spherical
|
||||
static
|
||||
void ToCartesian(cmsVEC3* v, const cmsSpherical* sp)
|
||||
{
|
||||
cmsFloat64Number sin_alpha;
|
||||
cmsFloat64Number cos_alpha;
|
||||
cmsFloat64Number sin_theta;
|
||||
cmsFloat64Number cos_theta;
|
||||
cmsFloat64Number L, a, b;
|
||||
|
||||
sin_alpha = sin((M_PI * sp ->alpha) / 180.0);
|
||||
cos_alpha = cos((M_PI * sp ->alpha) / 180.0);
|
||||
sin_theta = sin((M_PI * sp ->theta) / 180.0);
|
||||
cos_theta = cos((M_PI * sp ->theta) / 180.0);
|
||||
|
||||
a = sp ->r * sin_theta * sin_alpha;
|
||||
b = sp ->r * sin_theta * cos_alpha;
|
||||
L = sp ->r * cos_theta;
|
||||
|
||||
v ->n[VX] = L;
|
||||
v ->n[VY] = a;
|
||||
v ->n[VZ] = b;
|
||||
}
|
||||
|
||||
|
||||
// Quantize sector of a spherical coordinate. Saturate 360, 180 to last sector
|
||||
// The limits are the centers of each sector, so
|
||||
static
|
||||
void QuantizeToSector(const cmsSpherical* sp, int* alpha, int* theta)
|
||||
{
|
||||
*alpha = (int) floor(((sp->alpha * (SECTORS)) / 360.0) );
|
||||
*theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) );
|
||||
|
||||
if (*alpha >= SECTORS)
|
||||
*alpha = SECTORS-1;
|
||||
if (*theta >= SECTORS)
|
||||
*theta = SECTORS-1;
|
||||
}
|
||||
|
||||
|
||||
// Line determined by 2 points
|
||||
static
|
||||
void LineOf2Points(cmsLine* line, cmsVEC3* a, cmsVEC3* b)
|
||||
{
|
||||
|
||||
_cmsVEC3init(&line ->a, a ->n[VX], a ->n[VY], a ->n[VZ]);
|
||||
_cmsVEC3init(&line ->u, b ->n[VX] - a ->n[VX],
|
||||
b ->n[VY] - a ->n[VY],
|
||||
b ->n[VZ] - a ->n[VZ]);
|
||||
}
|
||||
|
||||
|
||||
// Evaluate parametric line
|
||||
static
|
||||
void GetPointOfLine(cmsVEC3* p, const cmsLine* line, cmsFloat64Number t)
|
||||
{
|
||||
p ->n[VX] = line ->a.n[VX] + t * line->u.n[VX];
|
||||
p ->n[VY] = line ->a.n[VY] + t * line->u.n[VY];
|
||||
p ->n[VZ] = line ->a.n[VZ] + t * line->u.n[VZ];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Closest point in sector line1 to sector line2 (both are defined as 0 <=t <= 1)
|
||||
http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
|
||||
|
||||
Copyright 2001, softSurfer (www.softsurfer.com)
|
||||
This code may be freely used and modified for any purpose
|
||||
providing that this copyright notice is included with it.
|
||||
SoftSurfer makes no warranty for this code, and cannot be held
|
||||
liable for any real or imagined damage resulting from its use.
|
||||
Users of this code must verify correctness for their application.
|
||||
|
||||
*/
|
||||
|
||||
static
|
||||
cmsBool ClosestLineToLine(cmsVEC3* r, const cmsLine* line1, const cmsLine* line2)
|
||||
{
|
||||
cmsFloat64Number a, b, c, d, e, D;
|
||||
cmsFloat64Number sc, sN, sD;
|
||||
cmsFloat64Number tc, tN, tD;
|
||||
cmsVEC3 w0;
|
||||
|
||||
_cmsVEC3minus(&w0, &line1 ->a, &line2 ->a);
|
||||
|
||||
a = _cmsVEC3dot(&line1 ->u, &line1 ->u);
|
||||
b = _cmsVEC3dot(&line1 ->u, &line2 ->u);
|
||||
c = _cmsVEC3dot(&line2 ->u, &line2 ->u);
|
||||
d = _cmsVEC3dot(&line1 ->u, &w0);
|
||||
e = _cmsVEC3dot(&line2 ->u, &w0);
|
||||
|
||||
D = a*c - b * b; // Denominator
|
||||
sD = tD = D; // default sD = D >= 0
|
||||
|
||||
if (D < MATRIX_DET_TOLERANCE) { // the lines are almost parallel
|
||||
|
||||
sN = 0.0; // force using point P0 on segment S1
|
||||
sD = 1.0; // to prevent possible division by 0.0 later
|
||||
tN = e;
|
||||
tD = c;
|
||||
}
|
||||
else { // get the closest points on the infinite lines
|
||||
|
||||
sN = (b*e - c*d);
|
||||
tN = (a*e - b*d);
|
||||
|
||||
if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
|
||||
|
||||
sN = 0.0;
|
||||
tN = e;
|
||||
tD = c;
|
||||
}
|
||||
else if (sN > sD) { // sc > 1 => the s=1 edge is visible
|
||||
sN = sD;
|
||||
tN = e + b;
|
||||
tD = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
|
||||
|
||||
tN = 0.0;
|
||||
// recompute sc for this edge
|
||||
if (-d < 0.0)
|
||||
sN = 0.0;
|
||||
else if (-d > a)
|
||||
sN = sD;
|
||||
else {
|
||||
sN = -d;
|
||||
sD = a;
|
||||
}
|
||||
}
|
||||
else if (tN > tD) { // tc > 1 => the t=1 edge is visible
|
||||
|
||||
tN = tD;
|
||||
|
||||
// recompute sc for this edge
|
||||
if ((-d + b) < 0.0)
|
||||
sN = 0;
|
||||
else if ((-d + b) > a)
|
||||
sN = sD;
|
||||
else {
|
||||
sN = (-d + b);
|
||||
sD = a;
|
||||
}
|
||||
}
|
||||
// finally do the division to get sc and tc
|
||||
sc = (fabs(sN) < MATRIX_DET_TOLERANCE ? 0.0 : sN / sD);
|
||||
tc = (fabs(tN) < MATRIX_DET_TOLERANCE ? 0.0 : tN / tD);
|
||||
|
||||
GetPointOfLine(r, line1, sc);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------ Wrapper
|
||||
|
||||
|
||||
// Allocate & free structure
|
||||
cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID)
|
||||
{
|
||||
cmsGDB* gbd = (cmsGDB*) _cmsMallocZero(ContextID, sizeof(cmsGDB));
|
||||
if (gbd == NULL) return NULL;
|
||||
|
||||
gbd -> ContextID = ContextID;
|
||||
|
||||
return (cmsHANDLE) gbd;
|
||||
}
|
||||
|
||||
|
||||
void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD)
|
||||
{
|
||||
cmsGDB* gbd = (cmsGDB*) hGBD;
|
||||
if (hGBD != NULL)
|
||||
_cmsFree(gbd->ContextID, (void*) gbd);
|
||||
}
|
||||
|
||||
|
||||
// Auxiliar to retrieve a pointer to the segmentr containing the Lab value
|
||||
static
|
||||
cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp)
|
||||
{
|
||||
cmsVEC3 v;
|
||||
int alpha, theta;
|
||||
|
||||
// Housekeeping
|
||||
_cmsAssert(gbd != NULL);
|
||||
_cmsAssert(Lab != NULL);
|
||||
_cmsAssert(sp != NULL);
|
||||
|
||||
// Center L* by substracting half of its domain, that's 50
|
||||
_cmsVEC3init(&v, Lab ->L - 50.0, Lab ->a, Lab ->b);
|
||||
|
||||
// Convert to spherical coordinates
|
||||
ToSpherical(sp, &v);
|
||||
|
||||
if (sp ->r < 0 || sp ->alpha < 0 || sp->theta < 0) {
|
||||
cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, "spherical value out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// On which sector it falls?
|
||||
QuantizeToSector(sp, &alpha, &theta);
|
||||
|
||||
if (alpha < 0 || theta < 0 || alpha >= SECTORS || theta >= SECTORS) {
|
||||
cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, " quadrant out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get pointer to the sector
|
||||
return &gbd ->Gamut[theta][alpha];
|
||||
}
|
||||
|
||||
// Add a point to gamut descriptor. Point to add is in Lab color space.
|
||||
// GBD is centered on a=b=0 and L*=50
|
||||
cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab)
|
||||
{
|
||||
cmsGDB* gbd = (cmsGDB*) hGBD;
|
||||
cmsGDBPoint* ptr;
|
||||
cmsSpherical sp;
|
||||
|
||||
|
||||
// Get pointer to the sector
|
||||
ptr = GetPoint(gbd, Lab, &sp);
|
||||
if (ptr == NULL) return FALSE;
|
||||
|
||||
// If no samples at this sector, add it
|
||||
if (ptr ->Type == GP_EMPTY) {
|
||||
|
||||
ptr -> Type = GP_SPECIFIED;
|
||||
ptr -> p = sp;
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
// Substitute only if radius is greater
|
||||
if (sp.r > ptr -> p.r) {
|
||||
|
||||
ptr -> Type = GP_SPECIFIED;
|
||||
ptr -> p = sp;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Check if a given point falls inside gamut
|
||||
cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab)
|
||||
{
|
||||
cmsGDB* gbd = (cmsGDB*) hGBD;
|
||||
cmsGDBPoint* ptr;
|
||||
cmsSpherical sp;
|
||||
|
||||
// Get pointer to the sector
|
||||
ptr = GetPoint(gbd, Lab, &sp);
|
||||
if (ptr == NULL) return FALSE;
|
||||
|
||||
// If no samples at this sector, return no data
|
||||
if (ptr ->Type == GP_EMPTY) return FALSE;
|
||||
|
||||
// In gamut only if radius is greater
|
||||
|
||||
return (sp.r <= ptr -> p.r);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Find near sectors. The list of sectors found is returned on Close[].
|
||||
// The function returns the number of sectors as well.
|
||||
|
||||
// 24 9 10 11 12
|
||||
// 23 8 1 2 13
|
||||
// 22 7 * 3 14
|
||||
// 21 6 5 4 15
|
||||
// 20 19 18 17 16
|
||||
//
|
||||
// Those are the relative movements
|
||||
// {-2,-2}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2},
|
||||
// {-2,-1}, {-1, -1}, {0, -1}, {+1, -1}, {+2, -1},
|
||||
// {-2, 0}, {-1, 0}, {0, 0}, {+1, 0}, {+2, 0},
|
||||
// {-2,+1}, {-1, +1}, {0, +1}, {+1, +1}, {+2, +1},
|
||||
// {-2,+2}, {-1, +2}, {0, +2}, {+1, +2}, {+2, +2}};
|
||||
|
||||
|
||||
static
|
||||
const struct _spiral {
|
||||
|
||||
int AdvX, AdvY;
|
||||
|
||||
} Spiral[] = { {0, -1}, {+1, -1}, {+1, 0}, {+1, +1}, {0, +1}, {-1, +1},
|
||||
{-1, 0}, {-1, -1}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2},
|
||||
{+2, -1}, {+2, 0}, {+2, +1}, {+2, +2}, {+1, +2}, {0, +2},
|
||||
{-1, +2}, {-2, +2}, {-2, +1}, {-2, 0}, {-2, -1}, {-2, -2} };
|
||||
|
||||
#define NSTEPS (sizeof(Spiral) / sizeof(struct _spiral))
|
||||
|
||||
static
|
||||
int FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[])
|
||||
{
|
||||
int nSectors = 0;
|
||||
int i, a, t;
|
||||
cmsGDBPoint* pt;
|
||||
|
||||
for (i=0; i < NSTEPS; i++) {
|
||||
|
||||
a = alpha + Spiral[i].AdvX;
|
||||
t = theta + Spiral[i].AdvY;
|
||||
|
||||
// Cycle at the end
|
||||
a %= SECTORS;
|
||||
t %= SECTORS;
|
||||
|
||||
// Cycle at the begin
|
||||
if (a < 0) a = SECTORS + a;
|
||||
if (t < 0) t = SECTORS + t;
|
||||
|
||||
pt = &gbd ->Gamut[t][a];
|
||||
|
||||
if (pt -> Type != GP_EMPTY) {
|
||||
|
||||
Close[nSectors++] = pt;
|
||||
}
|
||||
}
|
||||
|
||||
return nSectors;
|
||||
}
|
||||
|
||||
|
||||
// Interpolate a missing sector. Method identifies whatever this is top, bottom or mid
|
||||
static
|
||||
cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta)
|
||||
{
|
||||
cmsSpherical sp;
|
||||
cmsVEC3 Lab;
|
||||
cmsVEC3 Centre;
|
||||
cmsLine ray;
|
||||
int nCloseSectors;
|
||||
cmsGDBPoint* Close[NSTEPS];
|
||||
cmsSpherical closel, templ;
|
||||
cmsLine edge;
|
||||
int k, m;
|
||||
|
||||
// Is that point already specified?
|
||||
if (gbd ->Gamut[theta][alpha].Type != GP_EMPTY) return TRUE;
|
||||
|
||||
// Fill close points
|
||||
nCloseSectors = FindNearSectors(gbd, alpha, theta, Close);
|
||||
|
||||
|
||||
// Find a central point on the sector
|
||||
sp.alpha = (cmsFloat64Number) ((alpha + 0.5) * 360.0) / (SECTORS);
|
||||
sp.theta = (cmsFloat64Number) ((theta + 0.5) * 180.0) / (SECTORS);
|
||||
sp.r = 50.0;
|
||||
|
||||
// Convert to Cartesian
|
||||
ToCartesian(&Lab, &sp);
|
||||
|
||||
// Create a ray line from centre to this point
|
||||
_cmsVEC3init(&Centre, 50.0, 0, 0);
|
||||
LineOf2Points(&ray, &Lab, &Centre);
|
||||
|
||||
// For all close sectors
|
||||
closel.r = 0.0;
|
||||
closel.alpha = 0;
|
||||
closel.theta = 0;
|
||||
|
||||
for (k=0; k < nCloseSectors; k++) {
|
||||
|
||||
for(m = k+1; m < nCloseSectors; m++) {
|
||||
|
||||
cmsVEC3 temp, a1, a2;
|
||||
|
||||
// A line from sector to sector
|
||||
ToCartesian(&a1, &Close[k]->p);
|
||||
ToCartesian(&a2, &Close[m]->p);
|
||||
|
||||
LineOf2Points(&edge, &a1, &a2);
|
||||
|
||||
// Find a line
|
||||
ClosestLineToLine(&temp, &ray, &edge);
|
||||
|
||||
// Convert to spherical
|
||||
ToSpherical(&templ, &temp);
|
||||
|
||||
|
||||
if ( templ.r > closel.r &&
|
||||
templ.theta >= (theta*180.0/SECTORS) &&
|
||||
templ.theta <= ((theta+1)*180.0/SECTORS) &&
|
||||
templ.alpha >= (alpha*360.0/SECTORS) &&
|
||||
templ.alpha <= ((alpha+1)*360.0/SECTORS)) {
|
||||
|
||||
closel = templ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gbd ->Gamut[theta][alpha].p = closel;
|
||||
gbd ->Gamut[theta][alpha].Type = GP_MODELED;
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Interpolate missing parts. The algorithm fist computes slices at
|
||||
// theta=0 and theta=Max.
|
||||
cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags)
|
||||
{
|
||||
int alpha, theta;
|
||||
cmsGDB* gbd = (cmsGDB*) hGBD;
|
||||
|
||||
_cmsAssert(hGBD != NULL);
|
||||
|
||||
// Interpolate black
|
||||
for (alpha = 0; alpha <= SECTORS; alpha++) {
|
||||
|
||||
if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE;
|
||||
}
|
||||
|
||||
// Interpolate white
|
||||
for (alpha = 0; alpha <= SECTORS; alpha++) {
|
||||
|
||||
if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE;
|
||||
}
|
||||
|
||||
|
||||
// Interpolate Mid
|
||||
for (theta = 1; theta < SECTORS; theta++) {
|
||||
for (alpha = 0; alpha <= SECTORS; alpha++) {
|
||||
|
||||
if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Done
|
||||
return TRUE;
|
||||
|
||||
cmsUNUSED_PARAMETER(dwFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Great for debug, but not suitable for real use
|
||||
|
||||
#if 0
|
||||
cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname)
|
||||
{
|
||||
FILE* fp;
|
||||
int i, j;
|
||||
cmsGDB* gbd = (cmsGDB*) hGBD;
|
||||
cmsGDBPoint* pt;
|
||||
|
||||
fp = fopen (fname, "wt");
|
||||
if (fp == NULL)
|
||||
return FALSE;
|
||||
|
||||
fprintf (fp, "#VRML V2.0 utf8\n");
|
||||
|
||||
// set the viewing orientation and distance
|
||||
fprintf (fp, "DEF CamTest Group {\n");
|
||||
fprintf (fp, "\tchildren [\n");
|
||||
fprintf (fp, "\t\tDEF Cameras Group {\n");
|
||||
fprintf (fp, "\t\t\tchildren [\n");
|
||||
fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n");
|
||||
fprintf (fp, "\t\t\t\t\tposition 0 0 340\n");
|
||||
fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n");
|
||||
fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n");
|
||||
fprintf (fp, "\t\t\t\t}\n");
|
||||
fprintf (fp, "\t\t\t]\n");
|
||||
fprintf (fp, "\t\t},\n");
|
||||
fprintf (fp, "\t]\n");
|
||||
fprintf (fp, "}\n");
|
||||
|
||||
// Output the background stuff
|
||||
fprintf (fp, "Background {\n");
|
||||
fprintf (fp, "\tskyColor [\n");
|
||||
fprintf (fp, "\t\t.5 .5 .5\n");
|
||||
fprintf (fp, "\t]\n");
|
||||
fprintf (fp, "}\n");
|
||||
|
||||
// Output the shape stuff
|
||||
fprintf (fp, "Transform {\n");
|
||||
fprintf (fp, "\tscale .3 .3 .3\n");
|
||||
fprintf (fp, "\tchildren [\n");
|
||||
|
||||
// Draw the axes as a shape:
|
||||
fprintf (fp, "\t\tShape {\n");
|
||||
fprintf (fp, "\t\t\tappearance Appearance {\n");
|
||||
fprintf (fp, "\t\t\t\tmaterial Material {\n");
|
||||
fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n");
|
||||
fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n");
|
||||
fprintf (fp, "\t\t\t\t\tshininess 0.8\n");
|
||||
fprintf (fp, "\t\t\t\t}\n");
|
||||
fprintf (fp, "\t\t\t}\n");
|
||||
fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n");
|
||||
fprintf (fp, "\t\t\t\tcoord Coordinate {\n");
|
||||
fprintf (fp, "\t\t\t\t\tpoint [\n");
|
||||
fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n");
|
||||
fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0);
|
||||
fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0);
|
||||
fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0);
|
||||
fprintf (fp, "\t\t\t\t}\n");
|
||||
fprintf (fp, "\t\t\t\tcoordIndex [\n");
|
||||
fprintf (fp, "\t\t\t\t\t0, 1, -1\n");
|
||||
fprintf (fp, "\t\t\t\t\t0, 2, -1\n");
|
||||
fprintf (fp, "\t\t\t\t\t0, 3, -1]\n");
|
||||
fprintf (fp, "\t\t\t}\n");
|
||||
fprintf (fp, "\t\t}\n");
|
||||
|
||||
|
||||
fprintf (fp, "\t\tShape {\n");
|
||||
fprintf (fp, "\t\t\tappearance Appearance {\n");
|
||||
fprintf (fp, "\t\t\t\tmaterial Material {\n");
|
||||
fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n");
|
||||
fprintf (fp, "\t\t\t\t\temissiveColor 1 1 1\n");
|
||||
fprintf (fp, "\t\t\t\t\tshininess 0.8\n");
|
||||
fprintf (fp, "\t\t\t\t}\n");
|
||||
fprintf (fp, "\t\t\t}\n");
|
||||
fprintf (fp, "\t\t\tgeometry PointSet {\n");
|
||||
|
||||
// fill in the points here
|
||||
fprintf (fp, "\t\t\t\tcoord Coordinate {\n");
|
||||
fprintf (fp, "\t\t\t\t\tpoint [\n");
|
||||
|
||||
// We need to transverse all gamut hull.
|
||||
for (i=0; i < SECTORS; i++)
|
||||
for (j=0; j < SECTORS; j++) {
|
||||
|
||||
cmsVEC3 v;
|
||||
|
||||
pt = &gbd ->Gamut[i][j];
|
||||
ToCartesian(&v, &pt ->p);
|
||||
|
||||
fprintf (fp, "\t\t\t\t\t%g %g %g", v.n[0]+50, v.n[1], v.n[2]);
|
||||
|
||||
if ((j == SECTORS - 1) && (i == SECTORS - 1))
|
||||
fprintf (fp, "]\n");
|
||||
else
|
||||
fprintf (fp, ",\n");
|
||||
|
||||
}
|
||||
|
||||
fprintf (fp, "\t\t\t\t}\n");
|
||||
|
||||
|
||||
|
||||
// fill in the face colors
|
||||
fprintf (fp, "\t\t\t\tcolor Color {\n");
|
||||
fprintf (fp, "\t\t\t\t\tcolor [\n");
|
||||
|
||||
for (i=0; i < SECTORS; i++)
|
||||
for (j=0; j < SECTORS; j++) {
|
||||
|
||||
cmsVEC3 v;
|
||||
|
||||
pt = &gbd ->Gamut[i][j];
|
||||
|
||||
|
||||
ToCartesian(&v, &pt ->p);
|
||||
|
||||
|
||||
if (pt ->Type == GP_EMPTY)
|
||||
fprintf (fp, "\t\t\t\t\t%g %g %g", 0.0, 0.0, 0.0);
|
||||
else
|
||||
if (pt ->Type == GP_MODELED)
|
||||
fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, .5, .5);
|
||||
else {
|
||||
fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, 1.0, 1.0);
|
||||
|
||||
}
|
||||
|
||||
if ((j == SECTORS - 1) && (i == SECTORS - 1))
|
||||
fprintf (fp, "]\n");
|
||||
else
|
||||
fprintf (fp, ",\n");
|
||||
}
|
||||
fprintf (fp, "\t\t\t}\n");
|
||||
|
||||
|
||||
fprintf (fp, "\t\t\t}\n");
|
||||
fprintf (fp, "\t\t}\n");
|
||||
fprintf (fp, "\t]\n");
|
||||
fprintf (fp, "}\n");
|
||||
|
||||
fclose (fp);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
4955
thirdparty/liblcms2/src/cmstypes.c
vendored
Normal file
4955
thirdparty/liblcms2/src/cmstypes.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1148
thirdparty/liblcms2/src/cmsvirt.c
vendored
Normal file
1148
thirdparty/liblcms2/src/cmsvirt.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
351
thirdparty/liblcms2/src/cmswtpnt.c
vendored
Normal file
351
thirdparty/liblcms2/src/cmswtpnt.c
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
|
||||
// D50 - Widely used
|
||||
const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void)
|
||||
{
|
||||
static cmsCIEXYZ D50XYZ = {cmsD50X, cmsD50Y, cmsD50Z};
|
||||
|
||||
return &D50XYZ;
|
||||
}
|
||||
|
||||
const cmsCIExyY* CMSEXPORT cmsD50_xyY(void)
|
||||
{
|
||||
static cmsCIExyY D50xyY;
|
||||
|
||||
cmsXYZ2xyY(&D50xyY, cmsD50_XYZ());
|
||||
|
||||
return &D50xyY;
|
||||
}
|
||||
|
||||
// Obtains WhitePoint from Temperature
|
||||
cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK)
|
||||
{
|
||||
cmsFloat64Number x, y;
|
||||
cmsFloat64Number T, T2, T3;
|
||||
// cmsFloat64Number M1, M2;
|
||||
|
||||
_cmsAssert(WhitePoint != NULL);
|
||||
|
||||
T = TempK;
|
||||
T2 = T*T; // Square
|
||||
T3 = T2*T; // Cube
|
||||
|
||||
// For correlated color temperature (T) between 4000K and 7000K:
|
||||
|
||||
if (T >= 4000. && T <= 7000.)
|
||||
{
|
||||
x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063;
|
||||
}
|
||||
else
|
||||
// or for correlated color temperature (T) between 7000K and 25000K:
|
||||
|
||||
if (T > 7000.0 && T <= 25000.0)
|
||||
{
|
||||
x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040;
|
||||
}
|
||||
else {
|
||||
cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Obtain y(x)
|
||||
|
||||
y = -3.000*(x*x) + 2.870*x - 0.275;
|
||||
|
||||
// wave factors (not used, but here for futures extensions)
|
||||
|
||||
// M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y);
|
||||
// M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y);
|
||||
|
||||
WhitePoint -> x = x;
|
||||
WhitePoint -> y = y;
|
||||
WhitePoint -> Y = 1.0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmsFloat64Number mirek; // temp (in microreciprocal kelvin)
|
||||
cmsFloat64Number ut; // u coord of intersection w/ blackbody locus
|
||||
cmsFloat64Number vt; // v coord of intersection w/ blackbody locus
|
||||
cmsFloat64Number tt; // slope of ISOTEMPERATURE. line
|
||||
|
||||
} ISOTEMPERATURE;
|
||||
|
||||
static ISOTEMPERATURE isotempdata[] = {
|
||||
// {Mirek, Ut, Vt, Tt }
|
||||
{0, 0.18006, 0.26352, -0.24341},
|
||||
{10, 0.18066, 0.26589, -0.25479},
|
||||
{20, 0.18133, 0.26846, -0.26876},
|
||||
{30, 0.18208, 0.27119, -0.28539},
|
||||
{40, 0.18293, 0.27407, -0.30470},
|
||||
{50, 0.18388, 0.27709, -0.32675},
|
||||
{60, 0.18494, 0.28021, -0.35156},
|
||||
{70, 0.18611, 0.28342, -0.37915},
|
||||
{80, 0.18740, 0.28668, -0.40955},
|
||||
{90, 0.18880, 0.28997, -0.44278},
|
||||
{100, 0.19032, 0.29326, -0.47888},
|
||||
{125, 0.19462, 0.30141, -0.58204},
|
||||
{150, 0.19962, 0.30921, -0.70471},
|
||||
{175, 0.20525, 0.31647, -0.84901},
|
||||
{200, 0.21142, 0.32312, -1.0182 },
|
||||
{225, 0.21807, 0.32909, -1.2168 },
|
||||
{250, 0.22511, 0.33439, -1.4512 },
|
||||
{275, 0.23247, 0.33904, -1.7298 },
|
||||
{300, 0.24010, 0.34308, -2.0637 },
|
||||
{325, 0.24702, 0.34655, -2.4681 },
|
||||
{350, 0.25591, 0.34951, -2.9641 },
|
||||
{375, 0.26400, 0.35200, -3.5814 },
|
||||
{400, 0.27218, 0.35407, -4.3633 },
|
||||
{425, 0.28039, 0.35577, -5.3762 },
|
||||
{450, 0.28863, 0.35714, -6.7262 },
|
||||
{475, 0.29685, 0.35823, -8.5955 },
|
||||
{500, 0.30505, 0.35907, -11.324 },
|
||||
{525, 0.31320, 0.35968, -15.628 },
|
||||
{550, 0.32129, 0.36011, -23.325 },
|
||||
{575, 0.32931, 0.36038, -40.770 },
|
||||
{600, 0.33724, 0.36051, -116.45 }
|
||||
};
|
||||
|
||||
#define NISO sizeof(isotempdata)/sizeof(ISOTEMPERATURE)
|
||||
|
||||
|
||||
// Robertson's method
|
||||
cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint)
|
||||
{
|
||||
int j;
|
||||
cmsFloat64Number us,vs;
|
||||
cmsFloat64Number uj,vj,tj,di,dj,mi,mj;
|
||||
cmsFloat64Number xs, ys;
|
||||
|
||||
_cmsAssert(WhitePoint != NULL);
|
||||
_cmsAssert(TempK != NULL);
|
||||
|
||||
di = mi = 0;
|
||||
xs = WhitePoint -> x;
|
||||
ys = WhitePoint -> y;
|
||||
|
||||
// convert (x,y) to CIE 1960 (u,WhitePoint)
|
||||
|
||||
us = (2*xs) / (-xs + 6*ys + 1.5);
|
||||
vs = (3*ys) / (-xs + 6*ys + 1.5);
|
||||
|
||||
|
||||
for (j=0; j < NISO; j++) {
|
||||
|
||||
uj = isotempdata[j].ut;
|
||||
vj = isotempdata[j].vt;
|
||||
tj = isotempdata[j].tt;
|
||||
mj = isotempdata[j].mirek;
|
||||
|
||||
dj = ((vs - vj) - tj * (us - uj)) / sqrt(1.0 + tj * tj);
|
||||
|
||||
if ((j != 0) && (di/dj < 0.0)) {
|
||||
|
||||
// Found a match
|
||||
*TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
di = dj;
|
||||
mi = mj;
|
||||
}
|
||||
|
||||
// Not found
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
// Compute chromatic adaptation matrix using Chad as cone matrix
|
||||
|
||||
static
|
||||
cmsBool ComputeChromaticAdaptation(cmsMAT3* Conversion,
|
||||
const cmsCIEXYZ* SourceWhitePoint,
|
||||
const cmsCIEXYZ* DestWhitePoint,
|
||||
const cmsMAT3* Chad)
|
||||
|
||||
{
|
||||
|
||||
cmsMAT3 Chad_Inv;
|
||||
cmsVEC3 ConeSourceXYZ, ConeSourceRGB;
|
||||
cmsVEC3 ConeDestXYZ, ConeDestRGB;
|
||||
cmsMAT3 Cone, Tmp;
|
||||
|
||||
|
||||
Tmp = *Chad;
|
||||
if (!_cmsMAT3inverse(&Tmp, &Chad_Inv)) return FALSE;
|
||||
|
||||
_cmsVEC3init(&ConeSourceXYZ, SourceWhitePoint -> X,
|
||||
SourceWhitePoint -> Y,
|
||||
SourceWhitePoint -> Z);
|
||||
|
||||
_cmsVEC3init(&ConeDestXYZ, DestWhitePoint -> X,
|
||||
DestWhitePoint -> Y,
|
||||
DestWhitePoint -> Z);
|
||||
|
||||
_cmsMAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ);
|
||||
_cmsMAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ);
|
||||
|
||||
// Build matrix
|
||||
_cmsVEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0);
|
||||
_cmsVEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0);
|
||||
_cmsVEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]);
|
||||
|
||||
|
||||
// Normalize
|
||||
_cmsMAT3per(&Tmp, &Cone, Chad);
|
||||
_cmsMAT3per(Conversion, &Chad_Inv, &Tmp);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
|
||||
// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed
|
||||
cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll)
|
||||
{
|
||||
cmsMAT3 LamRigg = {{ // Bradford matrix
|
||||
{{ 0.8951, 0.2664, -0.1614 }},
|
||||
{{ -0.7502, 1.7135, 0.0367 }},
|
||||
{{ 0.0389, -0.0685, 1.0296 }}
|
||||
}};
|
||||
|
||||
if (ConeMatrix == NULL)
|
||||
ConeMatrix = &LamRigg;
|
||||
|
||||
return ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix);
|
||||
}
|
||||
|
||||
// Same as anterior, but assuming D50 destination. White point is given in xyY
|
||||
static
|
||||
cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt)
|
||||
{
|
||||
cmsCIEXYZ Dn;
|
||||
cmsMAT3 Bradford;
|
||||
cmsMAT3 Tmp;
|
||||
|
||||
cmsxyY2XYZ(&Dn, SourceWhitePt);
|
||||
|
||||
if (!_cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ())) return FALSE;
|
||||
|
||||
Tmp = *r;
|
||||
_cmsMAT3per(r, &Bradford, &Tmp);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
|
||||
// This is just an approximation, I am not handling all the non-linear
|
||||
// aspects of the RGB to XYZ process, and assumming that the gamma correction
|
||||
// has transitive property in the tranformation chain.
|
||||
//
|
||||
// the alghoritm:
|
||||
//
|
||||
// - First I build the absolute conversion matrix using
|
||||
// primaries in XYZ. This matrix is next inverted
|
||||
// - Then I eval the source white point across this matrix
|
||||
// obtaining the coeficients of the transformation
|
||||
// - Then, I apply these coeficients to the original matrix
|
||||
//
|
||||
cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs)
|
||||
{
|
||||
cmsVEC3 WhitePoint, Coef;
|
||||
cmsMAT3 Result, Primaries;
|
||||
cmsFloat64Number xn, yn;
|
||||
cmsFloat64Number xr, yr;
|
||||
cmsFloat64Number xg, yg;
|
||||
cmsFloat64Number xb, yb;
|
||||
|
||||
xn = WhitePt -> x;
|
||||
yn = WhitePt -> y;
|
||||
xr = Primrs -> Red.x;
|
||||
yr = Primrs -> Red.y;
|
||||
xg = Primrs -> Green.x;
|
||||
yg = Primrs -> Green.y;
|
||||
xb = Primrs -> Blue.x;
|
||||
yb = Primrs -> Blue.y;
|
||||
|
||||
// Build Primaries matrix
|
||||
_cmsVEC3init(&Primaries.v[0], xr, xg, xb);
|
||||
_cmsVEC3init(&Primaries.v[1], yr, yg, yb);
|
||||
_cmsVEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb));
|
||||
|
||||
|
||||
// Result = Primaries ^ (-1) inverse matrix
|
||||
if (!_cmsMAT3inverse(&Primaries, &Result))
|
||||
return FALSE;
|
||||
|
||||
|
||||
_cmsVEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn);
|
||||
|
||||
// Across inverse primaries ...
|
||||
_cmsMAT3eval(&Coef, &Result, &WhitePoint);
|
||||
|
||||
// Give us the Coefs, then I build transformation matrix
|
||||
_cmsVEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb);
|
||||
_cmsVEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb);
|
||||
_cmsVEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb));
|
||||
|
||||
|
||||
return _cmsAdaptMatrixToD50(r, WhitePt);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Adapts a color to a given illuminant. Original color is expected to have
|
||||
// a SourceWhitePt white point.
|
||||
cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result,
|
||||
const cmsCIEXYZ* SourceWhitePt,
|
||||
const cmsCIEXYZ* Illuminant,
|
||||
const cmsCIEXYZ* Value)
|
||||
{
|
||||
cmsMAT3 Bradford;
|
||||
cmsVEC3 In, Out;
|
||||
|
||||
_cmsAssert(Result != NULL);
|
||||
_cmsAssert(SourceWhitePt != NULL);
|
||||
_cmsAssert(Illuminant != NULL);
|
||||
_cmsAssert(Value != NULL);
|
||||
|
||||
if (!_cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant)) return FALSE;
|
||||
|
||||
_cmsVEC3init(&In, Value -> X, Value -> Y, Value -> Z);
|
||||
_cmsMAT3eval(&Out, &Bradford, &In);
|
||||
|
||||
Result -> X = Out.n[0];
|
||||
Result -> Y = Out.n[1];
|
||||
Result -> Z = Out.n[2];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
809
thirdparty/liblcms2/src/cmsxform.c
vendored
Normal file
809
thirdparty/liblcms2/src/cmsxform.c
vendored
Normal file
@@ -0,0 +1,809 @@
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "lcms2_internal.h"
|
||||
|
||||
// Transformations stuff
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// Alarm codes for 16-bit transformations, because the fixed range of containers there are
|
||||
// no values left to mark out of gamut. volatile is C99 per 6.2.5
|
||||
static volatile cmsUInt16Number Alarm[cmsMAXCHANNELS];
|
||||
static volatile cmsFloat64Number GlobalAdaptationState = 0;
|
||||
|
||||
// The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
|
||||
cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
|
||||
{
|
||||
cmsFloat64Number OldVal = GlobalAdaptationState;
|
||||
|
||||
if (d >= 0)
|
||||
GlobalAdaptationState = d;
|
||||
|
||||
return OldVal;
|
||||
}
|
||||
|
||||
// Alarm codes are always global
|
||||
void CMSEXPORT cmsSetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
|
||||
{
|
||||
int i;
|
||||
|
||||
_cmsAssert(NewAlarm != NULL);
|
||||
|
||||
for (i=0; i < cmsMAXCHANNELS; i++)
|
||||
Alarm[i] = NewAlarm[i];
|
||||
}
|
||||
|
||||
// You can get the codes cas well
|
||||
void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
|
||||
{
|
||||
int i;
|
||||
|
||||
_cmsAssert(OldAlarm != NULL);
|
||||
|
||||
for (i=0; i < cmsMAXCHANNELS; i++)
|
||||
OldAlarm[i] = Alarm[i];
|
||||
}
|
||||
|
||||
// Get rid of transform resources
|
||||
void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
|
||||
{
|
||||
_cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
|
||||
|
||||
_cmsAssert(p != NULL);
|
||||
|
||||
if (p -> GamutCheck)
|
||||
cmsPipelineFree(p -> GamutCheck);
|
||||
|
||||
if (p -> Lut)
|
||||
cmsPipelineFree(p -> Lut);
|
||||
|
||||
if (p ->InputColorant)
|
||||
cmsFreeNamedColorList(p ->InputColorant);
|
||||
|
||||
if (p -> OutputColorant)
|
||||
cmsFreeNamedColorList(p ->OutputColorant);
|
||||
|
||||
if (p ->Sequence)
|
||||
cmsFreeProfileSequenceDescription(p ->Sequence);
|
||||
|
||||
LCMS_FREE_LOCK(&p->rwlock);
|
||||
_cmsFree(p ->ContextID, (void *) p);
|
||||
}
|
||||
|
||||
// Apply transform
|
||||
void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
|
||||
const void* InputBuffer,
|
||||
void* OutputBuffer,
|
||||
cmsUInt32Number Size)
|
||||
|
||||
{
|
||||
_cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
|
||||
|
||||
p -> xform(p, InputBuffer, OutputBuffer, Size);
|
||||
}
|
||||
|
||||
|
||||
// Transform routines ----------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
|
||||
// Note that because extended range, we can use a -1.0 value for out of gamut in this case.
|
||||
static
|
||||
void FloatXFORM(_cmsTRANSFORM* p,
|
||||
const void* in,
|
||||
void* out, cmsUInt32Number Size)
|
||||
{
|
||||
cmsUInt8Number* accum;
|
||||
cmsUInt8Number* output;
|
||||
cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
|
||||
cmsFloat32Number OutOfGamut;
|
||||
cmsUInt32Number i, j;
|
||||
|
||||
accum = (cmsUInt8Number*) in;
|
||||
output = (cmsUInt8Number*) out;
|
||||
|
||||
for (i=0; i < Size; i++) {
|
||||
|
||||
accum = p -> FromInputFloat(p, fIn, accum, Size);
|
||||
|
||||
// Any gamut chack to do?
|
||||
if (p ->GamutCheck != NULL) {
|
||||
|
||||
// Evaluate gamut marker.
|
||||
cmsPipelineEvalFloat( fIn, &OutOfGamut, p ->GamutCheck);
|
||||
|
||||
// Is current color out of gamut?
|
||||
if (OutOfGamut > 0.0) {
|
||||
|
||||
// Certainly, out of gamut
|
||||
for (j=0; j < cmsMAXCHANNELS; j++)
|
||||
fOut[j] = -1.0;
|
||||
|
||||
}
|
||||
else {
|
||||
// No, proceed normally
|
||||
cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// No gamut check at all
|
||||
cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
|
||||
}
|
||||
|
||||
// Back to asked representation
|
||||
output = p -> ToOutputFloat(p, fOut, output, Size);
|
||||
}
|
||||
}
|
||||
|
||||
// 16 bit precision -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Null transformation, only applies formatters. No cach<63>
|
||||
static
|
||||
void NullXFORM(_cmsTRANSFORM* p,
|
||||
const void* in,
|
||||
void* out, cmsUInt32Number Size)
|
||||
{
|
||||
cmsUInt8Number* accum;
|
||||
cmsUInt8Number* output;
|
||||
cmsUInt16Number wIn[cmsMAXCHANNELS];
|
||||
cmsUInt32Number i, n;
|
||||
|
||||
accum = (cmsUInt8Number*) in;
|
||||
output = (cmsUInt8Number*) out;
|
||||
n = Size; // Buffer len
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
|
||||
accum = p -> FromInput(p, wIn, accum, Size);
|
||||
output = p -> ToOutput(p, wIn, output, Size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// No gamut check, no cache, 16 bits
|
||||
static
|
||||
void PrecalculatedXFORM(_cmsTRANSFORM* p,
|
||||
const void* in,
|
||||
void* out, cmsUInt32Number Size)
|
||||
{
|
||||
register cmsUInt8Number* accum;
|
||||
register cmsUInt8Number* output;
|
||||
cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
|
||||
cmsUInt32Number i, n;
|
||||
|
||||
accum = (cmsUInt8Number*) in;
|
||||
output = (cmsUInt8Number*) out;
|
||||
n = Size;
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
|
||||
accum = p -> FromInput(p, wIn, accum, Size);
|
||||
p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
|
||||
output = p -> ToOutput(p, wOut, output, Size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Auxiliar: Handle precalculated gamut check
|
||||
static
|
||||
void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
|
||||
const cmsUInt16Number wIn[],
|
||||
cmsUInt16Number wOut[])
|
||||
{
|
||||
cmsUInt16Number wOutOfGamut;
|
||||
|
||||
p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
|
||||
if (wOutOfGamut >= 1) {
|
||||
|
||||
cmsUInt16Number i;
|
||||
|
||||
for (i=0; i < p ->Lut->OutputChannels; i++)
|
||||
wOut[i] = Alarm[i];
|
||||
}
|
||||
else
|
||||
p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
|
||||
}
|
||||
|
||||
// Gamut check, No cach<63>, 16 bits.
|
||||
static
|
||||
void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
|
||||
const void* in,
|
||||
void* out, cmsUInt32Number Size)
|
||||
{
|
||||
cmsUInt8Number* accum;
|
||||
cmsUInt8Number* output;
|
||||
cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
|
||||
cmsUInt32Number i, n;
|
||||
|
||||
accum = (cmsUInt8Number*) in;
|
||||
output = (cmsUInt8Number*) out;
|
||||
n = Size; // Buffer len
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
|
||||
accum = p -> FromInput(p, wIn, accum, Size);
|
||||
TransformOnePixelWithGamutCheck(p, wIn, wOut);
|
||||
output = p -> ToOutput(p, wOut, output, Size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// No gamut check, Cach<63>, 16 bits,
|
||||
static
|
||||
void CachedXFORM(_cmsTRANSFORM* p,
|
||||
const void* in,
|
||||
void* out, cmsUInt32Number Size)
|
||||
{
|
||||
cmsUInt8Number* accum;
|
||||
cmsUInt8Number* output;
|
||||
cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
|
||||
cmsUInt32Number i, n;
|
||||
cmsUInt16Number CacheIn[cmsMAXCHANNELS], CacheOut[cmsMAXCHANNELS];
|
||||
|
||||
accum = (cmsUInt8Number*) in;
|
||||
output = (cmsUInt8Number*) out;
|
||||
n = Size; // Buffer len
|
||||
|
||||
// Empty buffers for quick memcmp
|
||||
memset(wIn, 0, sizeof(wIn));
|
||||
memset(wOut, 0, sizeof(wOut));
|
||||
|
||||
|
||||
LCMS_READ_LOCK(&p ->rwlock);
|
||||
memmove(CacheIn, p ->CacheIn, sizeof(CacheIn));
|
||||
memmove(CacheOut, p ->CacheOut, sizeof(CacheOut));
|
||||
LCMS_UNLOCK(&p ->rwlock);
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
|
||||
accum = p -> FromInput(p, wIn, accum, Size);
|
||||
|
||||
if (memcmp(wIn, CacheIn, sizeof(CacheIn)) == 0) {
|
||||
|
||||
memmove(wOut, CacheOut, sizeof(CacheOut));
|
||||
}
|
||||
else {
|
||||
|
||||
p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
|
||||
|
||||
memmove(CacheIn, wIn, sizeof(CacheIn));
|
||||
memmove(CacheOut, wOut, sizeof(CacheOut));
|
||||
}
|
||||
|
||||
output = p -> ToOutput(p, wOut, output, Size);
|
||||
}
|
||||
|
||||
|
||||
LCMS_WRITE_LOCK(&p ->rwlock);
|
||||
memmove(p->CacheIn, CacheIn, sizeof(CacheIn));
|
||||
memmove(p->CacheOut, CacheOut, sizeof(CacheOut));
|
||||
LCMS_UNLOCK(&p ->rwlock);
|
||||
}
|
||||
|
||||
|
||||
// All those nice features together
|
||||
static
|
||||
void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
|
||||
const void* in,
|
||||
void* out, cmsUInt32Number Size)
|
||||
{
|
||||
cmsUInt8Number* accum;
|
||||
cmsUInt8Number* output;
|
||||
cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
|
||||
cmsUInt32Number i, n;
|
||||
cmsUInt16Number CacheIn[cmsMAXCHANNELS], CacheOut[cmsMAXCHANNELS];
|
||||
|
||||
accum = (cmsUInt8Number*) in;
|
||||
output = (cmsUInt8Number*) out;
|
||||
n = Size; // Buffer len
|
||||
|
||||
// Empty buffers for quick memcmp
|
||||
memset(wIn, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
|
||||
memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
|
||||
|
||||
LCMS_READ_LOCK(&p ->rwlock);
|
||||
memmove(CacheIn, p ->CacheIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
|
||||
memmove(CacheOut, p ->CacheOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
|
||||
LCMS_UNLOCK(&p ->rwlock);
|
||||
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
|
||||
accum = p -> FromInput(p, wIn, accum, Size);
|
||||
|
||||
if (memcmp(wIn, CacheIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS) == 0) {
|
||||
memmove(wOut, CacheOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
|
||||
}
|
||||
else {
|
||||
TransformOnePixelWithGamutCheck(p, wIn, wOut);
|
||||
memmove(CacheIn, wIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
|
||||
memmove(CacheOut, wOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
|
||||
}
|
||||
|
||||
output = p -> ToOutput(p, wOut, output, Size);
|
||||
}
|
||||
|
||||
LCMS_WRITE_LOCK(&p ->rwlock);
|
||||
memmove(p->CacheIn, CacheIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
|
||||
memmove(p->CacheOut, CacheOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
|
||||
LCMS_UNLOCK(&p ->rwlock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Allocate transform struct and set it to defaults
|
||||
static
|
||||
_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFormat, cmsUInt32Number OutputFormat, cmsUInt32Number dwFlags)
|
||||
{
|
||||
// Allocate needed memory
|
||||
_cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
|
||||
if (!p) return NULL;
|
||||
|
||||
// Check whatever this is a true floating point transform
|
||||
if (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat)) {
|
||||
|
||||
// Get formatter function always return a valid union, but the contents of this union may be NULL.
|
||||
p ->FromInputFloat = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
|
||||
p ->ToOutputFloat = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
|
||||
dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
|
||||
|
||||
if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
|
||||
|
||||
cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
|
||||
_cmsFree(ContextID, p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Float transforms don't use cach<63>, always are non-NULL
|
||||
p ->xform = FloatXFORM;
|
||||
}
|
||||
else {
|
||||
|
||||
if (InputFormat == 0 && OutputFormat == 0) {
|
||||
p ->FromInput = p ->ToOutput = NULL;
|
||||
}
|
||||
else {
|
||||
|
||||
int BytesPerPixelInput;
|
||||
|
||||
p ->FromInput = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
|
||||
p ->ToOutput = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
|
||||
|
||||
|
||||
if (p ->FromInput == NULL || p ->ToOutput == NULL) {
|
||||
|
||||
cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
|
||||
_cmsFree(ContextID, p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BytesPerPixelInput = T_BYTES(p ->InputFormat);
|
||||
if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
|
||||
dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
|
||||
|
||||
}
|
||||
|
||||
if (dwFlags & cmsFLAGS_NULLTRANSFORM) {
|
||||
|
||||
p ->xform = NullXFORM;
|
||||
}
|
||||
else {
|
||||
if (dwFlags & cmsFLAGS_NOCACHE) {
|
||||
|
||||
if (dwFlags & cmsFLAGS_GAMUTCHECK)
|
||||
p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cach<63>
|
||||
else
|
||||
p ->xform = PrecalculatedXFORM; // No cach<63>, no gamut check
|
||||
}
|
||||
else {
|
||||
|
||||
if (dwFlags & cmsFLAGS_GAMUTCHECK)
|
||||
p ->xform = CachedXFORMGamutCheck; // Gamut check, cach<63>
|
||||
else
|
||||
p ->xform = CachedXFORM; // No gamut check, cach<63>
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create a mutex for shared memory
|
||||
LCMS_CREATE_LOCK(&p->rwlock);
|
||||
|
||||
p ->InputFormat = InputFormat;
|
||||
p ->OutputFormat = OutputFormat;
|
||||
p ->dwOriginalFlags = dwFlags;
|
||||
p ->ContextID = ContextID;
|
||||
return p;
|
||||
}
|
||||
|
||||
static
|
||||
cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
|
||||
{
|
||||
cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
|
||||
cmsColorSpaceSignature PostColorSpace;
|
||||
int i;
|
||||
|
||||
if (hProfiles[0] == NULL) return FALSE;
|
||||
|
||||
*Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
|
||||
|
||||
for (i=0; i < nProfiles; i++) {
|
||||
|
||||
cmsHPROFILE hProfile = hProfiles[i];
|
||||
|
||||
int lIsInput = (PostColorSpace != cmsSigXYZData) &&
|
||||
(PostColorSpace != cmsSigLabData);
|
||||
|
||||
if (hProfile == NULL) return FALSE;
|
||||
|
||||
if (lIsInput) {
|
||||
|
||||
ColorSpaceIn = cmsGetColorSpace(hProfile);
|
||||
ColorSpaceOut = cmsGetPCS(hProfile);
|
||||
}
|
||||
else {
|
||||
|
||||
ColorSpaceIn = cmsGetPCS(hProfile);
|
||||
ColorSpaceOut = cmsGetColorSpace(hProfile);
|
||||
}
|
||||
|
||||
PostColorSpace = ColorSpaceOut;
|
||||
}
|
||||
|
||||
*Output = PostColorSpace;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Check colorspace
|
||||
static
|
||||
cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
|
||||
{
|
||||
int Space1 = T_COLORSPACE(dwFormat);
|
||||
int Space2 = _cmsLCMScolorSpace(Check);
|
||||
|
||||
if (Space1 == PT_ANY) return TRUE;
|
||||
if (Space1 == Space2) return TRUE;
|
||||
|
||||
if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
|
||||
if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// New to lcms 2.0 -- have all parameters available.
|
||||
cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
|
||||
cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
|
||||
cmsBool BPC[],
|
||||
cmsUInt32Number Intents[],
|
||||
cmsFloat64Number AdaptationStates[],
|
||||
cmsHPROFILE hGamutProfile,
|
||||
cmsUInt32Number nGamutPCSposition,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsUInt32Number OutputFormat,
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
_cmsTRANSFORM* xform;
|
||||
cmsBool FloatTransform;
|
||||
cmsColorSpaceSignature EntryColorSpace;
|
||||
cmsColorSpaceSignature ExitColorSpace;
|
||||
cmsPipeline* Lut;
|
||||
cmsUInt32Number LastIntent = Intents[nProfiles-1];
|
||||
|
||||
// If gamut check is requested, make sure we have a gamut profile
|
||||
if (dwFlags & cmsFLAGS_GAMUTCHECK) {
|
||||
if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
|
||||
}
|
||||
|
||||
// On floating point transforms, inhibit optimizations
|
||||
FloatTransform = (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat));
|
||||
|
||||
if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
|
||||
dwFlags |= cmsFLAGS_NOCACHE;
|
||||
|
||||
// Mark entry/exit spaces
|
||||
if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
|
||||
cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if proper colorspaces
|
||||
if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
|
||||
cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
|
||||
cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a pipeline with all transformations
|
||||
Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
|
||||
if (Lut == NULL) {
|
||||
cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Optimize the LUT if possible
|
||||
_cmsOptimizePipeline(&Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
|
||||
|
||||
|
||||
// All seems ok
|
||||
xform = AllocEmptyTransform(ContextID, InputFormat, OutputFormat, dwFlags);
|
||||
if (xform == NULL) {
|
||||
cmsPipelineFree(Lut);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Keep values
|
||||
xform ->EntryColorSpace = EntryColorSpace;
|
||||
xform ->ExitColorSpace = ExitColorSpace;
|
||||
xform ->Lut = Lut;
|
||||
|
||||
|
||||
// Create a gamut check LUT if requested
|
||||
if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
|
||||
xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
|
||||
BPC, Intents,
|
||||
AdaptationStates,
|
||||
nGamutPCSposition,
|
||||
hGamutProfile);
|
||||
|
||||
|
||||
// Try to read input and output colorant table
|
||||
if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
|
||||
|
||||
// Input table can only come in this way.
|
||||
xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
|
||||
}
|
||||
|
||||
// Output is a little bit more complex.
|
||||
if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
|
||||
|
||||
// This tag may exist only on devicelink profiles.
|
||||
if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
|
||||
|
||||
// It may be NULL if error
|
||||
xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
|
||||
|
||||
xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
|
||||
}
|
||||
}
|
||||
|
||||
// Store the sequence of profiles
|
||||
if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
|
||||
xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
|
||||
}
|
||||
else
|
||||
xform ->Sequence = NULL;
|
||||
|
||||
// If this is a cached transform, init first value, which is zero (16 bits only)
|
||||
if (!(dwFlags & cmsFLAGS_NOCACHE)) {
|
||||
|
||||
memset(&xform ->CacheIn, 0, sizeof(xform ->CacheIn));
|
||||
|
||||
if (xform ->GamutCheck != NULL) {
|
||||
TransformOnePixelWithGamutCheck(xform, xform ->CacheIn, xform->CacheOut);
|
||||
}
|
||||
else {
|
||||
|
||||
xform ->Lut ->Eval16Fn(xform ->CacheIn, xform->CacheOut, xform -> Lut->Data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (cmsHTRANSFORM) xform;
|
||||
}
|
||||
|
||||
// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
|
||||
|
||||
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
|
||||
cmsHPROFILE hProfiles[],
|
||||
cmsUInt32Number nProfiles,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsUInt32Number OutputFormat,
|
||||
cmsUInt32Number Intent,
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
cmsUInt32Number i;
|
||||
cmsBool BPC[256];
|
||||
cmsUInt32Number Intents[256];
|
||||
cmsFloat64Number AdaptationStates[256];
|
||||
|
||||
if (nProfiles <= 0 || nProfiles > 255) {
|
||||
cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i=0; i < nProfiles; i++) {
|
||||
BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
|
||||
Intents[i] = Intent;
|
||||
AdaptationStates[i] = GlobalAdaptationState;
|
||||
}
|
||||
|
||||
|
||||
return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
|
||||
cmsUInt32Number nProfiles,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsUInt32Number OutputFormat,
|
||||
cmsUInt32Number Intent,
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
|
||||
if (nProfiles <= 0 || nProfiles > 255) {
|
||||
cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
|
||||
hProfiles,
|
||||
nProfiles,
|
||||
InputFormat,
|
||||
OutputFormat,
|
||||
Intent,
|
||||
dwFlags);
|
||||
}
|
||||
|
||||
cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
|
||||
cmsHPROFILE Input,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsHPROFILE Output,
|
||||
cmsUInt32Number OutputFormat,
|
||||
cmsUInt32Number Intent,
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
|
||||
cmsHPROFILE hArray[2];
|
||||
|
||||
hArray[0] = Input;
|
||||
hArray[1] = Output;
|
||||
|
||||
return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags);
|
||||
}
|
||||
|
||||
CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsHPROFILE Output,
|
||||
cmsUInt32Number OutputFormat,
|
||||
cmsUInt32Number Intent,
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
|
||||
}
|
||||
|
||||
|
||||
cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
|
||||
cmsHPROFILE InputProfile,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsHPROFILE OutputProfile,
|
||||
cmsUInt32Number OutputFormat,
|
||||
cmsHPROFILE ProofingProfile,
|
||||
cmsUInt32Number nIntent,
|
||||
cmsUInt32Number ProofingIntent,
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
cmsHPROFILE hArray[4];
|
||||
cmsUInt32Number Intents[4];
|
||||
cmsBool BPC[4];
|
||||
cmsFloat64Number Adaptation[4];
|
||||
cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
|
||||
|
||||
|
||||
hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile;
|
||||
Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
|
||||
BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
|
||||
|
||||
Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = GlobalAdaptationState;
|
||||
|
||||
if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
|
||||
return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
|
||||
|
||||
return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
|
||||
ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
|
||||
|
||||
}
|
||||
|
||||
|
||||
cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsHPROFILE OutputProfile,
|
||||
cmsUInt32Number OutputFormat,
|
||||
cmsHPROFILE ProofingProfile,
|
||||
cmsUInt32Number nIntent,
|
||||
cmsUInt32Number ProofingIntent,
|
||||
cmsUInt32Number dwFlags)
|
||||
{
|
||||
return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
|
||||
InputProfile,
|
||||
InputFormat,
|
||||
OutputProfile,
|
||||
OutputFormat,
|
||||
ProofingProfile,
|
||||
nIntent,
|
||||
ProofingIntent,
|
||||
dwFlags);
|
||||
}
|
||||
|
||||
|
||||
// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
|
||||
cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
|
||||
{
|
||||
_cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
|
||||
|
||||
if (xform == NULL) return NULL;
|
||||
return xform -> ContextID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// For backwards compatibility
|
||||
cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsUInt32Number OutputFormat)
|
||||
{
|
||||
|
||||
_cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
|
||||
cmsFormatter16 FromInput, ToOutput;
|
||||
cmsUInt32Number BytesPerPixelInput;
|
||||
|
||||
// We only can afford to change formatters if previous transform is at least 16 bits
|
||||
BytesPerPixelInput = T_BYTES(xform ->InputFormat);
|
||||
if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
|
||||
|
||||
cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FromInput = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
|
||||
ToOutput = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
|
||||
|
||||
if (FromInput == NULL || ToOutput == NULL) {
|
||||
|
||||
cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
xform ->InputFormat = InputFormat;
|
||||
xform ->OutputFormat = OutputFormat;
|
||||
xform ->FromInput = FromInput;
|
||||
xform ->ToOutput = ToOutput;
|
||||
return TRUE;
|
||||
}
|
||||
300
thirdparty/liblcms2/src/lcms2.def
vendored
Normal file
300
thirdparty/liblcms2/src/lcms2.def
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
LIBRARY LCMS2.DLL
|
||||
|
||||
EXPORTS
|
||||
|
||||
_cms15Fixed16toDouble = _cms15Fixed16toDouble
|
||||
_cms8Fixed8toDouble = _cms8Fixed8toDouble
|
||||
cmsAdaptToIlluminant = cmsAdaptToIlluminant
|
||||
_cmsAdjustEndianess16 = _cmsAdjustEndianess16
|
||||
_cmsAdjustEndianess32 = _cmsAdjustEndianess32
|
||||
_cmsAdjustEndianess64 = _cmsAdjustEndianess64
|
||||
cmsAllocNamedColorList = cmsAllocNamedColorList
|
||||
cmsAllocProfileSequenceDescription = cmsAllocProfileSequenceDescription
|
||||
cmsAppendNamedColor = cmsAppendNamedColor
|
||||
cmsBFDdeltaE = cmsBFDdeltaE
|
||||
cmsBuildGamma = cmsBuildGamma
|
||||
cmsBuildParametricToneCurve = cmsBuildParametricToneCurve
|
||||
cmsBuildSegmentedToneCurve = cmsBuildSegmentedToneCurve
|
||||
cmsBuildTabulatedToneCurve16 = cmsBuildTabulatedToneCurve16
|
||||
cmsBuildTabulatedToneCurveFloat = cmsBuildTabulatedToneCurveFloat
|
||||
_cmsCalloc = _cmsCalloc
|
||||
cmsChannelsOf = cmsChannelsOf
|
||||
cmsCIE2000DeltaE = cmsCIE2000DeltaE
|
||||
cmsCIE94DeltaE = cmsCIE94DeltaE
|
||||
cmsCIECAM02Done = cmsCIECAM02Done
|
||||
cmsCIECAM02Forward = cmsCIECAM02Forward
|
||||
cmsCIECAM02Init = cmsCIECAM02Init
|
||||
cmsCIECAM02Reverse = cmsCIECAM02Reverse
|
||||
cmsCloseIOhandler = cmsCloseIOhandler
|
||||
cmsCloseProfile = cmsCloseProfile
|
||||
cmsCMCdeltaE = cmsCMCdeltaE
|
||||
cmsCreate_sRGBProfile = cmsCreate_sRGBProfile
|
||||
cmsCreate_sRGBProfileTHR = cmsCreate_sRGBProfileTHR
|
||||
cmsCreateBCHSWabstractProfile = cmsCreateBCHSWabstractProfile
|
||||
cmsCreateBCHSWabstractProfileTHR = cmsCreateBCHSWabstractProfileTHR
|
||||
cmsCreateExtendedTransform = cmsCreateExtendedTransform
|
||||
cmsCreateGrayProfile = cmsCreateGrayProfile
|
||||
cmsCreateGrayProfileTHR = cmsCreateGrayProfileTHR
|
||||
cmsCreateInkLimitingDeviceLink = cmsCreateInkLimitingDeviceLink
|
||||
cmsCreateInkLimitingDeviceLinkTHR = cmsCreateInkLimitingDeviceLinkTHR
|
||||
cmsCreateLab2Profile = cmsCreateLab2Profile
|
||||
cmsCreateLab2ProfileTHR = cmsCreateLab2ProfileTHR
|
||||
cmsCreateLab4Profile = cmsCreateLab4Profile
|
||||
cmsCreateLab4ProfileTHR = cmsCreateLab4ProfileTHR
|
||||
cmsCreateLinearizationDeviceLink = cmsCreateLinearizationDeviceLink
|
||||
cmsCreateLinearizationDeviceLinkTHR = cmsCreateLinearizationDeviceLinkTHR
|
||||
cmsCreateMultiprofileTransform = cmsCreateMultiprofileTransform
|
||||
cmsCreateMultiprofileTransformTHR = cmsCreateMultiprofileTransformTHR
|
||||
cmsCreateNULLProfile = cmsCreateNULLProfile
|
||||
cmsCreateNULLProfileTHR = cmsCreateNULLProfileTHR
|
||||
cmsCreateProfilePlaceholder = cmsCreateProfilePlaceholder
|
||||
cmsCreateProofingTransform = cmsCreateProofingTransform
|
||||
cmsCreateProofingTransformTHR = cmsCreateProofingTransformTHR
|
||||
cmsCreateRGBProfile = cmsCreateRGBProfile
|
||||
cmsCreateRGBProfileTHR = cmsCreateRGBProfileTHR
|
||||
cmsCreateTransform = cmsCreateTransform
|
||||
cmsCreateTransformTHR = cmsCreateTransformTHR
|
||||
cmsCreateXYZProfile = cmsCreateXYZProfile
|
||||
cmsCreateXYZProfileTHR = cmsCreateXYZProfileTHR
|
||||
cmsD50_xyY = cmsD50_xyY
|
||||
cmsD50_XYZ = cmsD50_XYZ
|
||||
_cmsDecodeDateTimeNumber = _cmsDecodeDateTimeNumber
|
||||
_cmsDefaultICCintents = _cmsDefaultICCintents
|
||||
cmsDeleteTransform = cmsDeleteTransform
|
||||
cmsDeltaE = cmsDeltaE
|
||||
cmsDetectBlackPoint = cmsDetectBlackPoint
|
||||
cmsDetectTAC = cmsDetectTAC
|
||||
cmsDesaturateLab = cmsDesaturateLab
|
||||
cmsDoTransform = cmsDoTransform
|
||||
_cmsDoubleTo15Fixed16 = _cmsDoubleTo15Fixed16
|
||||
_cmsDoubleTo8Fixed8 = _cmsDoubleTo8Fixed8
|
||||
_cmsDupMem = _cmsDupMem
|
||||
cmsDupNamedColorList = cmsDupNamedColorList
|
||||
cmsDupProfileSequenceDescription = cmsDupProfileSequenceDescription
|
||||
cmsDupToneCurve = cmsDupToneCurve
|
||||
_cmsEncodeDateTimeNumber = _cmsEncodeDateTimeNumber
|
||||
cmsEstimateGamma = cmsEstimateGamma
|
||||
cmsEvalToneCurve16 = cmsEvalToneCurve16
|
||||
cmsEvalToneCurveFloat = cmsEvalToneCurveFloat
|
||||
cmsfilelength = cmsfilelength
|
||||
cmsFloat2LabEncoded = cmsFloat2LabEncoded
|
||||
cmsFloat2LabEncodedV2 = cmsFloat2LabEncodedV2
|
||||
cmsFloat2XYZEncoded = cmsFloat2XYZEncoded
|
||||
cmsFormatterForColorspaceOfProfile = cmsFormatterForColorspaceOfProfile
|
||||
cmsFormatterForPCSOfProfile = cmsFormatterForPCSOfProfile
|
||||
_cmsFree = _cmsFree
|
||||
cmsFreeNamedColorList = cmsFreeNamedColorList
|
||||
cmsFreeProfileSequenceDescription = cmsFreeProfileSequenceDescription
|
||||
cmsFreeToneCurve = cmsFreeToneCurve
|
||||
cmsFreeToneCurveTriple = cmsFreeToneCurveTriple
|
||||
cmsGBDAlloc = cmsGBDAlloc
|
||||
cmsGBDFree = cmsGBDFree
|
||||
cmsGDBAddPoint = cmsGDBAddPoint
|
||||
cmsGDBCheckPoint = cmsGDBCheckPoint
|
||||
cmsGDBCompute = cmsGDBCompute
|
||||
cmsGetAlarmCodes = cmsGetAlarmCodes
|
||||
cmsGetColorSpace = cmsGetColorSpace
|
||||
cmsGetDeviceClass = cmsGetDeviceClass
|
||||
cmsGetEncodedICCversion = cmsGetEncodedICCversion
|
||||
cmsGetHeaderAttributes = cmsGetHeaderAttributes
|
||||
cmsGetHeaderCreationDateTime = cmsGetHeaderCreationDateTime
|
||||
cmsGetHeaderFlags = cmsGetHeaderFlags
|
||||
cmsGetHeaderManufacturer = cmsGetHeaderManufacturer
|
||||
cmsGetHeaderModel = cmsGetHeaderModel
|
||||
cmsGetHeaderProfileID = cmsGetHeaderProfileID
|
||||
cmsGetHeaderRenderingIntent = cmsGetHeaderRenderingIntent
|
||||
cmsGetNamedColorList = cmsGetNamedColorList
|
||||
cmsGetPCS = cmsGetPCS
|
||||
cmsGetPostScriptColorResource = cmsGetPostScriptColorResource
|
||||
cmsGetPostScriptCRD = cmsGetPostScriptCRD
|
||||
cmsGetPostScriptCSA = cmsGetPostScriptCSA
|
||||
cmsGetProfileInfo = cmsGetProfileInfo
|
||||
cmsGetProfileInfoASCII = cmsGetProfileInfoASCII
|
||||
cmsGetProfileContextID = cmsGetProfileContextID
|
||||
cmsGetProfileVersion = cmsGetProfileVersion
|
||||
cmsGetSupportedIntents = cmsGetSupportedIntents
|
||||
cmsGetTagCount = cmsGetTagCount
|
||||
cmsGetTagSignature = cmsGetTagSignature
|
||||
cmsGetTransformContextID = cmsGetTransformContextID
|
||||
_cmsICCcolorSpace = _cmsICCcolorSpace
|
||||
_cmsIOPrintf = _cmsIOPrintf
|
||||
cmsIsCLUT = cmsIsCLUT
|
||||
cmsIsIntentSupported = cmsIsIntentSupported
|
||||
cmsIsMatrixShaper = cmsIsMatrixShaper
|
||||
cmsIsTag = cmsIsTag
|
||||
cmsIsToneCurveDescending = cmsIsToneCurveDescending
|
||||
cmsIsToneCurveLinear = cmsIsToneCurveLinear
|
||||
cmsIsToneCurveMonotonic = cmsIsToneCurveMonotonic
|
||||
cmsIsToneCurveMultisegment = cmsIsToneCurveMultisegment
|
||||
cmsGetToneCurveParametricType = cmsGetToneCurveParametricType
|
||||
cmsIT8Alloc = cmsIT8Alloc
|
||||
cmsIT8DefineDblFormat = cmsIT8DefineDblFormat
|
||||
cmsIT8EnumDataFormat = cmsIT8EnumDataFormat
|
||||
cmsIT8EnumProperties = cmsIT8EnumProperties
|
||||
cmsIT8Free = cmsIT8Free
|
||||
cmsIT8GetData = cmsIT8GetData
|
||||
cmsIT8GetDataDbl = cmsIT8GetDataDbl
|
||||
cmsIT8FindDataFormat = cmsIT8FindDataFormat
|
||||
cmsIT8GetDataRowCol = cmsIT8GetDataRowCol
|
||||
cmsIT8GetDataRowColDbl = cmsIT8GetDataRowColDbl
|
||||
cmsIT8GetPatchName = cmsIT8GetPatchName
|
||||
cmsIT8GetProperty = cmsIT8GetProperty
|
||||
cmsIT8GetPropertyDbl = cmsIT8GetPropertyDbl
|
||||
cmsIT8GetSheetType = cmsIT8GetSheetType
|
||||
cmsIT8LoadFromFile = cmsIT8LoadFromFile
|
||||
cmsIT8LoadFromMem = cmsIT8LoadFromMem
|
||||
cmsIT8SaveToFile = cmsIT8SaveToFile
|
||||
cmsIT8SaveToMem = cmsIT8SaveToMem
|
||||
cmsIT8SetComment = cmsIT8SetComment
|
||||
cmsIT8SetData = cmsIT8SetData
|
||||
cmsIT8SetDataDbl = cmsIT8SetDataDbl
|
||||
cmsIT8SetDataFormat = cmsIT8SetDataFormat
|
||||
cmsIT8SetDataRowCol = cmsIT8SetDataRowCol
|
||||
cmsIT8SetDataRowColDbl = cmsIT8SetDataRowColDbl
|
||||
cmsIT8SetPropertyDbl = cmsIT8SetPropertyDbl
|
||||
cmsIT8SetPropertyHex = cmsIT8SetPropertyHex
|
||||
cmsIT8SetPropertyStr = cmsIT8SetPropertyStr
|
||||
cmsIT8SetPropertyUncooked = cmsIT8SetPropertyUncooked
|
||||
cmsIT8SetSheetType = cmsIT8SetSheetType
|
||||
cmsIT8SetTable = cmsIT8SetTable
|
||||
cmsIT8SetTableByLabel = cmsIT8SetTableByLabel
|
||||
cmsIT8TableCount = cmsIT8TableCount
|
||||
cmsJoinToneCurve = cmsJoinToneCurve
|
||||
cmsLab2LCh = cmsLab2LCh
|
||||
cmsLab2XYZ = cmsLab2XYZ
|
||||
cmsLabEncoded2Float = cmsLabEncoded2Float
|
||||
cmsLabEncoded2FloatV2 = cmsLabEncoded2FloatV2
|
||||
cmsLCh2Lab = cmsLCh2Lab
|
||||
_cmsLCMScolorSpace = _cmsLCMScolorSpace
|
||||
cmsLinkTag = cmsLinkTag
|
||||
cmsPipelineAlloc = cmsPipelineAlloc
|
||||
cmsPipelineCat = cmsPipelineCat
|
||||
cmsPipelineCheckAndRetreiveStages = cmsPipelineCheckAndRetreiveStages
|
||||
cmsPipelineDup = cmsPipelineDup
|
||||
cmsPipelineStageCount = cmsPipelineStageCount
|
||||
cmsPipelineEval16 = cmsPipelineEval16
|
||||
cmsPipelineEvalFloat = cmsPipelineEvalFloat
|
||||
cmsPipelineEvalReverseFloat = cmsPipelineEvalReverseFloat
|
||||
cmsPipelineFree = cmsPipelineFree
|
||||
cmsPipelineGetPtrToFirstStage = cmsPipelineGetPtrToFirstStage
|
||||
cmsPipelineGetPtrToLastStage = cmsPipelineGetPtrToLastStage
|
||||
cmsPipelineInputChannels = cmsPipelineInputChannels
|
||||
cmsPipelineInsertStage = cmsPipelineInsertStage
|
||||
cmsPipelineOutputChannels = cmsPipelineOutputChannels
|
||||
cmsPipelineSetSaveAs8bitsFlag = cmsPipelineSetSaveAs8bitsFlag
|
||||
_cmsPipelineSetOptimizationParameters = _cmsPipelineSetOptimizationParameters
|
||||
cmsPipelineUnlinkStage = cmsPipelineUnlinkStage
|
||||
_cmsMalloc = _cmsMalloc
|
||||
_cmsMallocZero = _cmsMallocZero
|
||||
_cmsMAT3eval = _cmsMAT3eval
|
||||
_cmsMAT3identity = _cmsMAT3identity
|
||||
_cmsMAT3inverse = _cmsMAT3inverse
|
||||
_cmsMAT3isIdentity = _cmsMAT3isIdentity
|
||||
_cmsMAT3per = _cmsMAT3per
|
||||
_cmsMAT3solve = _cmsMAT3solve
|
||||
cmsMD5computeID = cmsMD5computeID
|
||||
cmsMLUalloc = cmsMLUalloc
|
||||
cmsMLUdup = cmsMLUdup
|
||||
cmsMLUfree = cmsMLUfree
|
||||
cmsMLUgetASCII = cmsMLUgetASCII
|
||||
cmsMLUgetTranslation = cmsMLUgetTranslation
|
||||
cmsMLUgetWide = cmsMLUgetWide
|
||||
cmsMLUsetASCII = cmsMLUsetASCII
|
||||
cmsMLUsetWide = cmsMLUsetWide
|
||||
cmsStageAllocCLut16bit = cmsStageAllocCLut16bit
|
||||
cmsStageAllocCLut16bitGranular = cmsStageAllocCLut16bitGranular
|
||||
cmsStageAllocCLutFloat = cmsStageAllocCLutFloat
|
||||
cmsStageAllocCLutFloatGranular = cmsStageAllocCLutFloatGranular
|
||||
cmsStageAllocToneCurves = cmsStageAllocToneCurves
|
||||
cmsStageAllocIdentity = cmsStageAllocIdentity
|
||||
cmsStageAllocMatrix = cmsStageAllocMatrix
|
||||
_cmsStageAllocPlaceholder = _cmsStageAllocPlaceholder
|
||||
cmsStageDup = cmsStageDup
|
||||
cmsStageFree = cmsStageFree
|
||||
cmsStageNext = cmsStageNext
|
||||
cmsStageInputChannels = cmsStageInputChannels
|
||||
cmsStageOutputChannels = cmsStageOutputChannels
|
||||
cmsStageSampleCLut16bit = cmsStageSampleCLut16bit
|
||||
cmsStageSampleCLutFloat = cmsStageSampleCLutFloat
|
||||
cmsStageType = cmsStageType
|
||||
cmsStageData = cmsStageData
|
||||
cmsNamedColorCount = cmsNamedColorCount
|
||||
cmsNamedColorIndex = cmsNamedColorIndex
|
||||
cmsNamedColorInfo = cmsNamedColorInfo
|
||||
cmsOpenIOhandlerFromFile = cmsOpenIOhandlerFromFile
|
||||
cmsOpenIOhandlerFromMem = cmsOpenIOhandlerFromMem
|
||||
cmsOpenIOhandlerFromNULL = cmsOpenIOhandlerFromNULL
|
||||
cmsOpenIOhandlerFromStream = cmsOpenIOhandlerFromStream
|
||||
cmsOpenProfileFromFile = cmsOpenProfileFromFile
|
||||
cmsOpenProfileFromFileTHR = cmsOpenProfileFromFileTHR
|
||||
cmsOpenProfileFromIOhandlerTHR = cmsOpenProfileFromIOhandlerTHR
|
||||
cmsOpenProfileFromMem = cmsOpenProfileFromMem
|
||||
cmsOpenProfileFromMemTHR = cmsOpenProfileFromMemTHR
|
||||
cmsOpenProfileFromStream = cmsOpenProfileFromStream
|
||||
cmsOpenProfileFromStreamTHR = cmsOpenProfileFromStreamTHR
|
||||
cmsPlugin = cmsPlugin
|
||||
_cmsRead15Fixed16Number = _cmsRead15Fixed16Number
|
||||
_cmsReadAlignment = _cmsReadAlignment
|
||||
_cmsReadFloat32Number = _cmsReadFloat32Number
|
||||
cmsReadRawTag = cmsReadRawTag
|
||||
cmsReadTag = cmsReadTag
|
||||
_cmsReadTypeBase = _cmsReadTypeBase
|
||||
_cmsReadUInt16Array = _cmsReadUInt16Array
|
||||
_cmsReadUInt16Number = _cmsReadUInt16Number
|
||||
_cmsReadUInt32Number = _cmsReadUInt32Number
|
||||
_cmsReadUInt64Number = _cmsReadUInt64Number
|
||||
_cmsReadUInt8Number = _cmsReadUInt8Number
|
||||
_cmsReadXYZNumber = _cmsReadXYZNumber
|
||||
_cmsRealloc = _cmsRealloc
|
||||
cmsReverseToneCurve = cmsReverseToneCurve
|
||||
cmsReverseToneCurveEx = cmsReverseToneCurveEx
|
||||
cmsSaveProfileToFile = cmsSaveProfileToFile
|
||||
cmsSaveProfileToIOhandler = cmsSaveProfileToIOhandler
|
||||
cmsSaveProfileToMem = cmsSaveProfileToMem
|
||||
cmsSaveProfileToStream = cmsSaveProfileToStream
|
||||
cmsSetAdaptationState = cmsSetAdaptationState
|
||||
cmsSetAlarmCodes = cmsSetAlarmCodes
|
||||
cmsSetColorSpace = cmsSetColorSpace
|
||||
cmsSetDeviceClass = cmsSetDeviceClass
|
||||
cmsSetEncodedICCversion = cmsSetEncodedICCversion
|
||||
cmsSetHeaderAttributes = cmsSetHeaderAttributes
|
||||
cmsSetHeaderFlags = cmsSetHeaderFlags
|
||||
cmsSetHeaderManufacturer = cmsSetHeaderManufacturer
|
||||
cmsSetHeaderModel = cmsSetHeaderModel
|
||||
cmsSetHeaderProfileID = cmsSetHeaderProfileID
|
||||
cmsSetHeaderRenderingIntent = cmsSetHeaderRenderingIntent
|
||||
cmsSetLogErrorHandler = cmsSetLogErrorHandler
|
||||
cmsSetPCS = cmsSetPCS
|
||||
cmsSetProfileVersion = cmsSetProfileVersion
|
||||
cmsSignalError = cmsSignalError
|
||||
cmsSmoothToneCurve = cmsSmoothToneCurve
|
||||
cmsstrcasecmp = cmsstrcasecmp
|
||||
cmsTempFromWhitePoint = cmsTempFromWhitePoint
|
||||
cmsTransform2DeviceLink = cmsTransform2DeviceLink
|
||||
cmsUnregisterPlugins = cmsUnregisterPlugins
|
||||
_cmsVEC3cross = _cmsVEC3cross
|
||||
_cmsVEC3distance = _cmsVEC3distance
|
||||
_cmsVEC3dot = _cmsVEC3dot
|
||||
_cmsVEC3init = _cmsVEC3init
|
||||
_cmsVEC3length = _cmsVEC3length
|
||||
_cmsVEC3minus = _cmsVEC3minus
|
||||
cmsWhitePointFromTemp = cmsWhitePointFromTemp
|
||||
_cmsWrite15Fixed16Number = _cmsWrite15Fixed16Number
|
||||
_cmsWriteAlignment = _cmsWriteAlignment
|
||||
_cmsWriteFloat32Number = _cmsWriteFloat32Number
|
||||
cmsWriteRawTag = cmsWriteRawTag
|
||||
cmsWriteTag = cmsWriteTag
|
||||
_cmsWriteTypeBase = _cmsWriteTypeBase
|
||||
_cmsWriteUInt16Array = _cmsWriteUInt16Array
|
||||
_cmsWriteUInt16Number = _cmsWriteUInt16Number
|
||||
_cmsWriteUInt32Number = _cmsWriteUInt32Number
|
||||
_cmsWriteUInt64Number = _cmsWriteUInt64Number
|
||||
_cmsWriteUInt8Number = _cmsWriteUInt8Number
|
||||
_cmsWriteXYZNumber = _cmsWriteXYZNumber
|
||||
cmsxyY2XYZ = cmsxyY2XYZ
|
||||
cmsXYZ2Lab = cmsXYZ2Lab
|
||||
cmsXYZ2xyY = cmsXYZ2xyY
|
||||
cmsXYZEncoded2Float = cmsXYZEncoded2Float
|
||||
cmsSliceSpace16 = cmsSliceSpace16
|
||||
cmsSliceSpaceFloat = cmsSliceSpaceFloat
|
||||
cmsChangeBuffersFormat = cmsChangeBuffersFormat
|
||||
652
thirdparty/liblcms2/src/lcms2_internal.h
vendored
Normal file
652
thirdparty/liblcms2/src/lcms2_internal.h
vendored
Normal file
@@ -0,0 +1,652 @@
|
||||
|
||||
//
|
||||
// Little Color Management System
|
||||
// Copyright (c) 1998-2010 Marti Maria Saguer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef _lcms_internal_H
|
||||
|
||||
// Include plug-in foundation
|
||||
#ifndef _lcms_plugin_H
|
||||
# include "lcms2_plugin.h"
|
||||
#endif
|
||||
|
||||
// ctype is part of C99 as per 7.1.2
|
||||
#include <ctype.h>
|
||||
|
||||
// assert macro is part of C99 as per 7.2
|
||||
#include <assert.h>
|
||||
|
||||
// Some needed constants
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
#ifndef M_LOG10E
|
||||
# define M_LOG10E 0.434294481903251827651
|
||||
#endif
|
||||
|
||||
// BorlandC 5.5 is broken on that
|
||||
#ifdef __BORLANDC__
|
||||
#define sinf(x) (float)sin((float)x)
|
||||
#define sqrtf(x) (float)sqrt((float)x)
|
||||
#endif
|
||||
|
||||
|
||||
// Alignment of ICC file format uses 4 bytes (cmsUInt32Number)
|
||||
#define _cmsSIZEOFLONGMINUS1 (sizeof(cmsUInt32Number)-1)
|
||||
#define _cmsALIGNLONG(x) (((x)+_cmsSIZEOFLONGMINUS1) & ~(_cmsSIZEOFLONGMINUS1))
|
||||
|
||||
// Maximum encodeable values in floating point
|
||||
#define MAX_ENCODEABLE_XYZ (1.0 + 32767.0/32768.0)
|
||||
#define MIN_ENCODEABLE_ab2 (-128.0)
|
||||
#define MAX_ENCODEABLE_ab2 ((65535.0/256.0) - 128.0)
|
||||
#define MIN_ENCODEABLE_ab4 (-128.0)
|
||||
#define MAX_ENCODEABLE_ab4 (127.0)
|
||||
|
||||
// Maximum of channels for internal pipeline evaluation
|
||||
#define MAX_STAGE_CHANNELS 128
|
||||
|
||||
// Unused parameter warning supression
|
||||
#define cmsUNUSED_PARAMETER(x) ((void)x)
|
||||
|
||||
// The specification for "inline" is section 6.7.4 of the C99 standard (ISO/IEC 9899:1999).
|
||||
// unfortunately VisualC++ does not conform that
|
||||
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
# define cmsINLINE __inline
|
||||
#else
|
||||
# define cmsINLINE static inline
|
||||
#endif
|
||||
|
||||
// Other replacement functions
|
||||
#ifdef _MSC_VER
|
||||
# ifndef snprintf
|
||||
# define snprintf _snprintf
|
||||
# endif
|
||||
# ifndef vsnprintf
|
||||
# define vsnprintf _vsnprintf
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Pthreads. In windows we use the native WIN32 API instead
|
||||
#ifdef CMS_DONT_USE_PTHREADS
|
||||
typedef int LCMS_RWLOCK_T;
|
||||
# define LCMS_CREATE_LOCK(x)
|
||||
# define LCMS_FREE_LOCK(x)
|
||||
# define LCMS_READ_LOCK(x)
|
||||
# define LCMS_WRITE_LOCK(x)
|
||||
# define LCMS_UNLOCK(x)
|
||||
#else
|
||||
#ifdef CMS_IS_WINDOWS_
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <windows.h>
|
||||
typedef CRITICAL_SECTION LCMS_RWLOCK_T;
|
||||
# define LCMS_CREATE_LOCK(x) InitializeCriticalSection((x))
|
||||
# define LCMS_FREE_LOCK(x) DeleteCriticalSection((x))
|
||||
# define LCMS_READ_LOCK(x) EnterCriticalSection((x))
|
||||
# define LCMS_WRITE_LOCK(x) EnterCriticalSection((x))
|
||||
# define LCMS_UNLOCK(x) LeaveCriticalSection((x))
|
||||
#else
|
||||
# include <pthread.h>
|
||||
typedef pthread_rwlock_t LCMS_RWLOCK_T;
|
||||
# define LCMS_CREATE_LOCK(x) pthread_rwlock_init((x), NULL)
|
||||
# define LCMS_FREE_LOCK(x) pthread_rwlock_destroy((x))
|
||||
# define LCMS_READ_LOCK(x) pthread_rwlock_rdlock((x))
|
||||
# define LCMS_WRITE_LOCK(x) pthread_rwlock_wrlock((x))
|
||||
# define LCMS_UNLOCK(x) pthread_rwlock_unlock((x))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// A fast way to convert from/to 16 <-> 8 bits
|
||||
#define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb))
|
||||
#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((rgb) * 65281 + 8388608) >> 24) & 0xFF)
|
||||
|
||||
// Code analysis is broken on asserts
|
||||
#ifdef _MSC_VER
|
||||
# if (_MSC_VER >= 1500)
|
||||
# define _cmsAssert(a) { assert((a)); __analysis_assume((a)); }
|
||||
# else
|
||||
# define _cmsAssert(a) assert((a))
|
||||
# endif
|
||||
#else
|
||||
# define _cmsAssert(a) assert((a))
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
// Determinant lower than that are assumed zero (used on matrix invert)
|
||||
#define MATRIX_DET_TOLERANCE 0.0001
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
// Fixed point
|
||||
#define FIXED_TO_INT(x) ((x)>>16)
|
||||
#define FIXED_REST_TO_INT(x) ((x)&0xFFFFU)
|
||||
#define ROUND_FIXED_TO_INT(x) (((x)+0x8000)>>16)
|
||||
|
||||
cmsINLINE cmsS15Fixed16Number _cmsToFixedDomain(int a) { return a + ((a + 0x7fff) / 0xffff); }
|
||||
cmsINLINE int _cmsFromFixedDomain(cmsS15Fixed16Number a) { return a - ((a + 0x7fff) >> 16); }
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Fast floor conversion logic. Thanks to Sree Kotay and Stuart Nixon
|
||||
// note than this only works in the range ..-32767...+32767 because
|
||||
// mantissa is interpreted as 15.16 fixed point.
|
||||
// The union is to avoid pointer aliasing overoptimization.
|
||||
cmsINLINE int _cmsQuickFloor(cmsFloat64Number val)
|
||||
{
|
||||
#ifdef CMS_DONT_USE_FAST_FLOOR
|
||||
return (int) floor(val);
|
||||
#else
|
||||
const cmsFloat64Number _lcms_double2fixmagic = 68719476736.0 * 1.5; // 2^36 * 1.5, (52-16=36) uses limited precision to floor
|
||||
union {
|
||||
cmsFloat64Number val;
|
||||
int halves[2];
|
||||
} temp;
|
||||
|
||||
temp.val = val + _lcms_double2fixmagic;
|
||||
|
||||
#ifdef CMS_USE_BIG_ENDIAN
|
||||
return temp.halves[1] >> 16;
|
||||
#else
|
||||
return temp.halves[0] >> 16;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Fast floor restricted to 0..65535.0
|
||||
cmsINLINE cmsUInt16Number _cmsQuickFloorWord(cmsFloat64Number d)
|
||||
{
|
||||
return (cmsUInt16Number) _cmsQuickFloor(d - 32767.0) + 32767U;
|
||||
}
|
||||
|
||||
// Floor to word, taking care of saturation
|
||||
cmsINLINE cmsUInt16Number _cmsQuickSaturateWord(cmsFloat64Number d)
|
||||
{
|
||||
d += 0.5;
|
||||
if (d <= 0) return 0;
|
||||
if (d >= 65535.0) return 0xffff;
|
||||
|
||||
return _cmsQuickFloorWord(d);
|
||||
}
|
||||
|
||||
// Plug-In registering ---------------------------------------------------------------
|
||||
|
||||
// Specialized function for plug-in memory management. No pairing free() since whole pool is freed at once.
|
||||
void* _cmsPluginMalloc(cmsUInt32Number size);
|
||||
|
||||
// Memory management
|
||||
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase* Plugin);
|
||||
|
||||
// Interpolation
|
||||
cmsBool _cmsRegisterInterpPlugin(cmsPluginBase* Plugin);
|
||||
|
||||
// Parametric curves
|
||||
cmsBool _cmsRegisterParametricCurvesPlugin(cmsPluginBase* Plugin);
|
||||
|
||||
// Formatters management
|
||||
cmsBool _cmsRegisterFormattersPlugin(cmsPluginBase* Plugin);
|
||||
|
||||
// Tag type management
|
||||
cmsBool _cmsRegisterTagTypePlugin(cmsPluginBase* Plugin);
|
||||
|
||||
// Tag management
|
||||
cmsBool _cmsRegisterTagPlugin(cmsPluginBase* Plugin);
|
||||
|
||||
// Intent management
|
||||
cmsBool _cmsRegisterRenderingIntentPlugin(cmsPluginBase* Plugin);
|
||||
|
||||
// Multi Process elements
|
||||
cmsBool _cmsRegisterMultiProcessElementPlugin(cmsPluginBase* Plugin);
|
||||
|
||||
// Optimization
|
||||
cmsBool _cmsRegisterOptimizationPlugin(cmsPluginBase* Plugin);
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Suballocators. Those are blocks of memory that is freed at the end on whole block.
|
||||
typedef struct _cmsSubAllocator_chunk_st {
|
||||
|
||||
cmsUInt8Number* Block;
|
||||
cmsUInt32Number BlockSize;
|
||||
cmsUInt32Number Used;
|
||||
|
||||
struct _cmsSubAllocator_chunk_st* next;
|
||||
|
||||
} _cmsSubAllocator_chunk;
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmsContext ContextID;
|
||||
_cmsSubAllocator_chunk* h;
|
||||
|
||||
} _cmsSubAllocator;
|
||||
|
||||
|
||||
_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial);
|
||||
void _cmsSubAllocDestroy(_cmsSubAllocator* s);
|
||||
void* _cmsSubAlloc(_cmsSubAllocator* s, cmsUInt32Number size);
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
// MLU internal representation
|
||||
typedef struct {
|
||||
|
||||
cmsUInt16Number Language;
|
||||
cmsUInt16Number Country;
|
||||
|
||||
cmsUInt32Number StrW; // Offset to current unicode string
|
||||
cmsUInt32Number Len; // Lenght in bytes
|
||||
|
||||
} _cmsMLUentry;
|
||||
|
||||
struct _cms_MLU_struct {
|
||||
|
||||
cmsContext ContextID;
|
||||
|
||||
// The directory
|
||||
int AllocatedEntries;
|
||||
int UsedEntries;
|
||||
_cmsMLUentry* Entries; // Array of pointers to strings allocated in MemPool
|
||||
|
||||
// The Pool
|
||||
cmsUInt32Number PoolSize; // The maximum allocated size
|
||||
cmsUInt32Number PoolUsed; // The used size
|
||||
void* MemPool; // Pointer to begin of memory pool
|
||||
};
|
||||
|
||||
// Named color list internal representation
|
||||
typedef struct {
|
||||
|
||||
char Name[cmsMAX_PATH];
|
||||
cmsUInt16Number PCS[3];
|
||||
cmsUInt16Number DeviceColorant[cmsMAXCHANNELS];
|
||||
|
||||
} _cmsNAMEDCOLOR;
|
||||
|
||||
struct _cms_NAMEDCOLORLIST_struct {
|
||||
|
||||
cmsUInt32Number nColors;
|
||||
cmsUInt32Number Allocated;
|
||||
cmsUInt32Number ColorantCount;
|
||||
|
||||
char Prefix[33]; // Prefix and suffix are defined to be 32 characters at most
|
||||
char Suffix[33];
|
||||
|
||||
_cmsNAMEDCOLOR* List;
|
||||
|
||||
cmsContext ContextID;
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
// This is the internal struct holding profile details.
|
||||
|
||||
// Maximum supported tags in a profile
|
||||
#define MAX_TABLE_TAG 100
|
||||
|
||||
typedef struct _cms_iccprofile_struct {
|
||||
|
||||
// I/O handler
|
||||
cmsIOHANDLER* IOhandler;
|
||||
|
||||
// The thread ID
|
||||
cmsContext ContextID;
|
||||
|
||||
// Creation time
|
||||
struct tm Created;
|
||||
|
||||
// Only most important items found in ICC profiles
|
||||
cmsUInt32Number Version;
|
||||
cmsProfileClassSignature DeviceClass;
|
||||
cmsColorSpaceSignature ColorSpace;
|
||||
cmsColorSpaceSignature PCS;
|
||||
cmsUInt32Number RenderingIntent;
|
||||
cmsUInt32Number flags;
|
||||
cmsUInt32Number manufacturer, model;
|
||||
cmsUInt64Number attributes;
|
||||
|
||||
cmsProfileID ProfileID;
|
||||
|
||||
// Dictionary
|
||||
cmsUInt32Number TagCount;
|
||||
cmsTagSignature TagNames[MAX_TABLE_TAG];
|
||||
cmsTagSignature TagLinked[MAX_TABLE_TAG]; // The tag to wich is linked (0=none)
|
||||
cmsUInt32Number TagSizes[MAX_TABLE_TAG]; // Size on disk
|
||||
cmsUInt32Number TagOffsets[MAX_TABLE_TAG];
|
||||
cmsBool TagSaveAsRaw[MAX_TABLE_TAG]; // True to write uncooked
|
||||
void * TagPtrs[MAX_TABLE_TAG];
|
||||
cmsTagTypeHandler* TagTypeHandlers[MAX_TABLE_TAG]; // Same structure may be serialized on different types
|
||||
// depending on profile version, so we keep track of the // type handler for each tag in the list.
|
||||
// Special
|
||||
cmsBool IsWrite;
|
||||
|
||||
} _cmsICCPROFILE;
|
||||
|
||||
// IO helpers for profiles
|
||||
cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc);
|
||||
cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace);
|
||||
int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks);
|
||||
|
||||
// Tag types
|
||||
cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsTagTypeSignature sig);
|
||||
cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig);
|
||||
cmsTagDescriptor* _cmsGetTagDescriptor(cmsTagSignature sig);
|
||||
|
||||
// Error logging ---------------------------------------------------------------------------------------------------------
|
||||
|
||||
void _cmsTagSignature2String(char String[5], cmsTagSignature sig);
|
||||
|
||||
// Interpolation ---------------------------------------------------------------------------------------------------------
|
||||
|
||||
cmsInterpParams* _cmsComputeInterpParams(cmsContext ContextID, int nSamples, int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags);
|
||||
cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags);
|
||||
void _cmsFreeInterpParams(cmsInterpParams* p);
|
||||
cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p);
|
||||
|
||||
// Curves ----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// This struct holds information about a segment, plus a pointer to the function that implements the evaluation.
|
||||
// In the case of table-based, Eval pointer is set to NULL
|
||||
|
||||
// The gamma function main structure
|
||||
struct _cms_curve_struct {
|
||||
|
||||
cmsInterpParams* InterpParams; // Private optimizations for interpolation
|
||||
|
||||
cmsUInt32Number nSegments; // Number of segments in the curve. Zero for a 16-bit based tables
|
||||
cmsCurveSegment* Segments; // The segments
|
||||
cmsInterpParams** SegInterp; // Array of private optimizations for interpolation in table-based segments
|
||||
|
||||
cmsParametricCurveEvaluator* Evals; // Evaluators (one per segment)
|
||||
|
||||
// 16 bit Table-based representation follows
|
||||
cmsUInt32Number nEntries; // Number of table elements
|
||||
cmsUInt16Number* Table16; // The table itself.
|
||||
};
|
||||
|
||||
|
||||
// Pipelines & Stages ---------------------------------------------------------------------------------------------
|
||||
|
||||
// A single stage
|
||||
struct _cmsStage_struct {
|
||||
|
||||
cmsContext ContextID;
|
||||
|
||||
cmsStageSignature Type; // Identifies the stage
|
||||
cmsStageSignature Implements; // Identifies the *function* of the stage (for optimizations)
|
||||
|
||||
cmsUInt32Number InputChannels; // Input channels -- for optimization purposes
|
||||
cmsUInt32Number OutputChannels; // Output channels -- for optimization purposes
|
||||
|
||||
_cmsStageEvalFn EvalPtr; // Points to fn that evaluates the stage (always in floating point)
|
||||
_cmsStageDupElemFn DupElemPtr; // Points to a fn that duplicates the *data* of the stage
|
||||
_cmsStageFreeElemFn FreePtr; // Points to a fn that sets the *data* of the stage free
|
||||
|
||||
// A generic pointer to whatever memory needed by the stage
|
||||
void* Data;
|
||||
|
||||
// Maintains linked list (used internally)
|
||||
struct _cmsStage_struct* Next;
|
||||
};
|
||||
|
||||
// Data kept in "Element" member of cmsStage
|
||||
|
||||
// Curves
|
||||
typedef struct {
|
||||
cmsUInt32Number nCurves;
|
||||
cmsToneCurve** TheCurves;
|
||||
|
||||
} _cmsStageToneCurvesData;
|
||||
|
||||
// Matrix
|
||||
typedef struct {
|
||||
cmsFloat64Number* Double; // floating point for the matrix
|
||||
cmsFloat64Number* Offset; // The offset
|
||||
|
||||
} _cmsStageMatrixData;
|
||||
|
||||
// CLUT
|
||||
typedef struct {
|
||||
|
||||
union { // Can have only one of both representations at same time
|
||||
cmsUInt16Number* T; // Points to the table 16 bits table
|
||||
cmsFloat32Number* TFloat; // Points to the cmsFloat32Number table
|
||||
|
||||
} Tab;
|
||||
|
||||
cmsInterpParams* Params;
|
||||
cmsUInt32Number nEntries;
|
||||
cmsBool HasFloatValues;
|
||||
|
||||
} _cmsStageCLutData;
|
||||
|
||||
|
||||
// Special Stages (cannot be saved)
|
||||
cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID);
|
||||
cmsStage* _cmsStageAllocXYZ2Lab(cmsContext ContextID);
|
||||
cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID);
|
||||
cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID);
|
||||
cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID);
|
||||
cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID);
|
||||
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList);
|
||||
cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels);
|
||||
cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan);
|
||||
|
||||
// For curve set only
|
||||
cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe);
|
||||
|
||||
|
||||
// Pipeline Evaluator (in floating point)
|
||||
typedef void (* _cmsPipelineEvalFloatFn)(const cmsFloat32Number In[],
|
||||
cmsFloat32Number Out[],
|
||||
const void* Data);
|
||||
|
||||
struct _cmsPipeline_struct {
|
||||
|
||||
cmsStage* Elements; // Points to elements chain
|
||||
cmsUInt32Number InputChannels, OutputChannels;
|
||||
|
||||
// Data & evaluators
|
||||
void *Data;
|
||||
|
||||
_cmsOPTeval16Fn Eval16Fn;
|
||||
_cmsPipelineEvalFloatFn EvalFloatFn;
|
||||
_cmsOPTfreeDataFn FreeDataFn;
|
||||
_cmsOPTdupDataFn DupDataFn;
|
||||
|
||||
cmsContext ContextID; // Environment
|
||||
|
||||
cmsBool SaveAs8Bits; // Implemntation-specific: save as 8 bits if possible
|
||||
};
|
||||
|
||||
// LUT reading & creation -------------------------------------------------------------------------------------------
|
||||
|
||||
// Read tags using low-level function, provide necessary glue code to adapt versions, etc. All those return a brand new copy
|
||||
// of the LUTS, since ownership of original is up to the profile. The user should free allocated resources.
|
||||
|
||||
cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent);
|
||||
cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent);
|
||||
cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent);
|
||||
|
||||
// Special values
|
||||
cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile);
|
||||
cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile);
|
||||
|
||||
// Profile linker --------------------------------------------------------------------------------------------------
|
||||
|
||||
cmsPipeline* _cmsLinkProfiles(cmsContext ContextID,
|
||||
cmsUInt32Number nProfiles,
|
||||
cmsUInt32Number TheIntents[],
|
||||
cmsHPROFILE hProfiles[],
|
||||
cmsBool BPC[],
|
||||
cmsFloat64Number AdaptationStates[],
|
||||
cmsUInt32Number dwFlags);
|
||||
|
||||
// Sequence --------------------------------------------------------------------------------------------------------
|
||||
|
||||
cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile);
|
||||
cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq);
|
||||
cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]);
|
||||
|
||||
|
||||
// LUT optimization ------------------------------------------------------------------------------------------------
|
||||
|
||||
cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, int MaxSamples);
|
||||
int _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags);
|
||||
|
||||
cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space,
|
||||
cmsUInt16Number **White,
|
||||
cmsUInt16Number **Black,
|
||||
cmsUInt32Number *nOutputs);
|
||||
|
||||
cmsBool _cmsOptimizePipeline(cmsPipeline** Lut,
|
||||
int Intent,
|
||||
cmsUInt32Number* InputFormat,
|
||||
cmsUInt32Number* OutputFormat,
|
||||
cmsUInt32Number* dwFlags );
|
||||
|
||||
|
||||
// Hi level LUT building ----------------------------------------------------------------------------------------------
|
||||
|
||||
cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID,
|
||||
cmsHPROFILE hProfiles[],
|
||||
cmsBool BPC[],
|
||||
cmsUInt32Number Intents[],
|
||||
cmsFloat64Number AdaptationStates[],
|
||||
cmsUInt32Number nGamutPCSposition,
|
||||
cmsHPROFILE hGamut);
|
||||
|
||||
|
||||
// Formatters ------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#define cmsFLAGS_CAN_CHANGE_FORMATTER 0x02000000 // Allow change buffer format
|
||||
|
||||
cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type);
|
||||
cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type);
|
||||
|
||||
cmsFormatter _cmsGetFormatter(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8
|
||||
cmsFormatterDirection Dir,
|
||||
cmsUInt32Number dwFlags);
|
||||
|
||||
|
||||
// Transform logic ------------------------------------------------------------------------------------------------------
|
||||
|
||||
struct _cmstransform_struct;
|
||||
|
||||
// Full xform
|
||||
typedef void (* _cmsTransformFn)(struct _cmstransform_struct *Transform,
|
||||
const void* InputBuffer,
|
||||
void* OutputBuffer, cmsUInt32Number Size);
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmsUInt32Number InputFormat, OutputFormat; // Keep formats for further reference
|
||||
cmsUInt32Number StrideIn, StrideOut; // Planar support
|
||||
|
||||
} cmsFormatterInfo;
|
||||
|
||||
// Transformation
|
||||
typedef struct _cmstransform_struct {
|
||||
|
||||
cmsUInt32Number InputFormat, OutputFormat; // Keep formats for further reference
|
||||
|
||||
// Points to transform code
|
||||
_cmsTransformFn xform;
|
||||
|
||||
// Formatters, cannot be embedded into LUT because cache
|
||||
cmsFormatter16 FromInput;
|
||||
cmsFormatter16 ToOutput;
|
||||
|
||||
cmsFormatterFloat FromInputFloat;
|
||||
cmsFormatterFloat ToOutputFloat;
|
||||
|
||||
// 1-pixel cache (16 bits only)
|
||||
cmsUInt16Number CacheIn[cmsMAXCHANNELS];
|
||||
cmsUInt16Number CacheOut[cmsMAXCHANNELS];
|
||||
|
||||
// Semaphor for cache
|
||||
LCMS_RWLOCK_T rwlock;
|
||||
|
||||
// A MPE LUT holding the full (optimized) transform
|
||||
cmsPipeline* Lut;
|
||||
|
||||
// A MPE LUT holding the gamut check. It goes from the input space to bilevel
|
||||
cmsPipeline* GamutCheck;
|
||||
|
||||
// Colorant tables
|
||||
cmsNAMEDCOLORLIST* InputColorant; // Input Colorant table
|
||||
cmsNAMEDCOLORLIST* OutputColorant; // Colorant table (for n chans > CMYK)
|
||||
|
||||
// Informational only
|
||||
cmsColorSpaceSignature EntryColorSpace;
|
||||
cmsColorSpaceSignature ExitColorSpace;
|
||||
|
||||
// Profiles used to create the transform
|
||||
cmsSEQ* Sequence;
|
||||
|
||||
cmsUInt32Number dwOriginalFlags;
|
||||
cmsFloat64Number AdaptationState;
|
||||
|
||||
// The intent of this transform. That is usually the last intent in the profilechain, but may differ
|
||||
cmsUInt32Number RenderingIntent;
|
||||
|
||||
// An id that uniquely identifies the running context. May be null.
|
||||
cmsContext ContextID;
|
||||
|
||||
} _cmsTRANSFORM;
|
||||
|
||||
// --------------------------------------------------------------------------------------------------
|
||||
|
||||
cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID,
|
||||
cmsUInt32Number nProfiles,
|
||||
cmsUInt32Number InputFormat,
|
||||
cmsUInt32Number OutputFormat,
|
||||
const cmsUInt32Number Intents[],
|
||||
const cmsHPROFILE hProfiles[],
|
||||
const cmsBool BPC[],
|
||||
const cmsFloat64Number AdaptationStates[],
|
||||
cmsUInt32Number dwFlags);
|
||||
|
||||
|
||||
cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID,
|
||||
cmsUInt32Number nPoints,
|
||||
cmsUInt32Number nProfiles,
|
||||
const cmsUInt32Number Intents[],
|
||||
const cmsHPROFILE hProfiles[],
|
||||
const cmsBool BPC[],
|
||||
const cmsFloat64Number AdaptationStates[],
|
||||
cmsUInt32Number dwFlags);
|
||||
|
||||
cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll);
|
||||
|
||||
cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries);
|
||||
|
||||
|
||||
#define _lcms_internal_H
|
||||
#endif
|
||||
Reference in New Issue
Block a user