blob: 7cc0c85deb11d365e4b6bc4e9de0bf05b877c2b5 [file] [log] [blame]
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
//
// Little cms
// Copyright (C) 1998-2007 Marti Maria
//
// 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.
// inter PCS conversions XYZ <-> CIE L* a* b*
#include "lcms.h"
/*
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)
Following ICC. PCS in Lab is coded 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*
We are always playing with 16 bits-data, so I will ignore the
8-bits encoding scheme.
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
*/
// On most modern computers, D > 4 M (i.e. a division takes more than 4
// multiplications worth of time), so it is probably preferable to compute
// a 24 bit result directly.
// #define ITERATE 1
static
float CubeRoot(float x)
{
float fr, r;
int ex, shx;
/* Argument reduction */
fr = (float) frexp(x, &ex); /* separate into mantissa and exponent */
shx = ex % 3;
if (shx > 0)
shx -= 3; /* compute shx such that (ex - shx) is divisible by 3 */
ex = (ex - shx) / 3; /* exponent of cube root */
fr = (float) ldexp(fr, shx);
/* 0.125 <= fr < 1.0 */
#ifdef ITERATE
/* Compute seed with a quadratic approximation */
fr = (-0.46946116F * fr + 1.072302F) * fr + 0.3812513F;/* 0.5<=fr<1 */
r = ldexp(fr, ex); /* 6 bits of precision */
/* Newton-Raphson iterations */
r = (float)(2.0/3.0) * r + (float)(1.0/3.0) * x / (r * r); /* 12 bits */
r = (float)(2.0/3.0) * r + (float)(1.0/3.0) * x / (r * r); /* 24 bits */
#else /* ITERATE */
/* Use quartic rational polynomial with error < 2^(-24) */
fr = (float) (((((45.2548339756803022511987494 * fr +
192.2798368355061050458134625) * fr +
119.1654824285581628956914143) * fr +
13.43250139086239872172837314) * fr +
0.1636161226585754240958355063)
/
((((14.80884093219134573786480845 * fr +
151.9714051044435648658557668) * fr +
168.5254414101568283957668343) * fr +
33.9905941350215598754191872) * fr +
1.0));
r = (float) ldexp(fr, ex); /* 24 bits of precision */
#endif
return r;
}
static
double f(double t)
{
const double 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 CubeRoot((float) t);
}
static
double f_1(double t)
{
const double Limit = (24.0/116.0);
if (t <= Limit)
{
double tmp;
tmp = (108.0/841.0) * (t - (16.0/116.0));
if (tmp <= 0.0) return 0.0;
else return tmp;
}
return t * t * t;
}
void LCMSEXPORT cmsXYZ2Lab(LPcmsCIEXYZ WhitePoint, LPcmsCIELab Lab, const cmsCIEXYZ* xyz)
{
double fx, fy, fz;
if (xyz -> X == 0 && xyz -> Y == 0 && xyz -> Z == 0)
{
Lab -> L = 0;
Lab -> a = 0;
Lab -> b = 0;
return;
}
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.;
Lab->a = 500.0*(fx - fy);
Lab->b = 200.0*(fy - fz);
}
void cmsXYZ2LabEncoded(WORD XYZ[3], WORD Lab[3])
{
Fixed32 X, Y, Z;
double x, y, z, L, a, b;
double fx, fy, fz;
Fixed32 wL, wa, wb;
X = (Fixed32) XYZ[0] << 1;
Y = (Fixed32) XYZ[1] << 1;
Z = (Fixed32) XYZ[2] << 1;
if (X==0 && Y==0 && Z==0) {
Lab[0] = 0;
Lab[1] = Lab[2] = 0x8000;
return;
}
// PCS is in D50
x = FIXED_TO_DOUBLE(X) / D50X;
y = FIXED_TO_DOUBLE(Y) / D50Y;
z = FIXED_TO_DOUBLE(Z) / D50Z;
fx = f(x);
fy = f(y);
fz = f(z);
L = 116.* fy - 16.;
a = 500.*(fx - fy);
b = 200.*(fy - fz);
a += 128.;
b += 128.;
wL = (int) (L * 652.800 + .5);
wa = (int) (a * 256.0 + .5);
wb = (int) (b * 256.0 + .5);
Lab[0] = Clamp_L(wL);
Lab[1] = Clamp_ab(wa);
Lab[2] = Clamp_ab(wb);
}
void LCMSEXPORT cmsLab2XYZ(LPcmsCIEXYZ WhitePoint, LPcmsCIEXYZ xyz, const cmsCIELab* Lab)
{
double x, y, z;
if (Lab -> L <= 0) {
xyz -> X = 0;
xyz -> Y = 0;
xyz -> Z = 0;
return;
}
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;
}
void cmsLab2XYZEncoded(WORD Lab[3], WORD XYZ[3])
{
double L, a, b;
double X, Y, Z, x, y, z;
L = ((double) Lab[0] * 100.0) / 65280.0;
if (L==0.0) {
XYZ[0] = 0; XYZ[1] = 0; XYZ[2] = 0;
return;
}
a = ((double) Lab[1] / 256.0) - 128.0;
b = ((double) Lab[2] / 256.0) - 128.0;
y = (L + 16.) / 116.0;
x = y + 0.002 * a;
z = y - 0.005 * b;
X = f_1(x) * D50X;
Y = f_1(y) * D50Y;
Z = f_1(z) * D50Z;
// Convert to 1.15 fixed format PCS
XYZ[0] = _cmsClampWord((int) floor(X * 32768.0 + 0.5));
XYZ[1] = _cmsClampWord((int) floor(Y * 32768.0 + 0.5));
XYZ[2] = _cmsClampWord((int) floor(Z * 32768.0 + 0.5));
}
static
double L2float3(WORD v)
{
Fixed32 fix32;
fix32 = (Fixed32) v;
return (double) fix32 / 652.800;
}
// the a/b part
static
double ab2float3(WORD v)
{
Fixed32 fix32;
fix32 = (Fixed32) v;
return ((double) fix32/256.0)-128.0;
}
static
WORD L2Fix3(double L)
{
return (WORD) (L * 652.800 + 0.5);
}
static
WORD ab2Fix3(double ab)
{
return (WORD) ((ab + 128.0) * 256.0 + 0.5);
}
// ICC 4.0 -- ICC has changed PCS Lab encoding.
static
WORD L2Fix4(double L)
{
return (WORD) (L * 655.35 + 0.5);
}
static
WORD ab2Fix4(double ab)
{
return (WORD) ((ab + 128.0) * 257.0 + 0.5);
}
static
double L2float4(WORD v)
{
Fixed32 fix32;
fix32 = (Fixed32) v;
return (double) fix32 / 655.35;
}
// the a/b part
static
double ab2float4(WORD v)
{
Fixed32 fix32;
fix32 = (Fixed32) v;
return ((double) fix32/257.0)-128.0;
}
void LCMSEXPORT cmsLabEncoded2Float(LPcmsCIELab Lab, const WORD wLab[3])
{
Lab->L = L2float3(wLab[0]);
Lab->a = ab2float3(wLab[1]);
Lab->b = ab2float3(wLab[2]);
}
void LCMSEXPORT cmsLabEncoded2Float4(LPcmsCIELab Lab, const WORD wLab[3])
{
Lab->L = L2float4(wLab[0]);
Lab->a = ab2float4(wLab[1]);
Lab->b = ab2float4(wLab[2]);
}
static
double Clamp_L_double(double L)
{
if (L < 0) L = 0;
if (L > 100) L = 100;
return L;
}
static
double Clamp_ab_double(double ab)
{
if (ab < -128) ab = -128.0;
if (ab > +127.9961) ab = +127.9961;
return ab;
}
void LCMSEXPORT cmsFloat2LabEncoded(WORD wLab[3], const cmsCIELab* fLab)
{
cmsCIELab Lab;
Lab.L = Clamp_L_double(fLab ->L);
Lab.a = Clamp_ab_double(fLab ->a);
Lab.b = Clamp_ab_double(fLab ->b);
wLab[0] = L2Fix3(Lab.L);
wLab[1] = ab2Fix3(Lab.a);
wLab[2] = ab2Fix3(Lab.b);
}
void LCMSEXPORT cmsFloat2LabEncoded4(WORD wLab[3], const cmsCIELab* fLab)
{
cmsCIELab Lab;
Lab.L = fLab ->L;
Lab.a = fLab ->a;
Lab.b = fLab ->b;
if (Lab.L < 0) Lab.L = 0;
if (Lab.L > 100.) Lab.L = 100.;
if (Lab.a < -128.) Lab.a = -128.;
if (Lab.a > 127.) Lab.a = 127.;
if (Lab.b < -128.) Lab.b = -128.;
if (Lab.b > 127.) Lab.b = 127.;
wLab[0] = L2Fix4(Lab.L);
wLab[1] = ab2Fix4(Lab.a);
wLab[2] = ab2Fix4(Lab.b);
}
void LCMSEXPORT cmsLab2LCh(LPcmsCIELCh LCh, const cmsCIELab* Lab)
{
double a, b;
LCh -> L = Clamp_L_double(Lab -> L);
a = Clamp_ab_double(Lab -> a);
b = Clamp_ab_double(Lab -> b);
LCh -> C = pow(a * a + b * b, 0.5);
if (a == 0 && b == 0)
LCh -> h = 0;
else
LCh -> h = atan2(b, a);
LCh -> h *= (180. / M_PI);
while (LCh -> h >= 360.) // Not necessary, but included as a check.
LCh -> h -= 360.;
while (LCh -> h < 0)
LCh -> h += 360.;
}
void LCMSEXPORT cmsLCh2Lab(LPcmsCIELab Lab, const cmsCIELCh* LCh)
{
double h = (LCh -> h * M_PI) / 180.0;
Lab -> L = Clamp_L_double(LCh -> L);
Lab -> a = Clamp_ab_double(LCh -> C * cos(h));
Lab -> b = Clamp_ab_double(LCh -> C * sin(h));
}
// In XYZ All 3 components are encoded using 1.15 fixed point
static
WORD XYZ2Fix(double d)
{
return (WORD) floor(d * 32768.0 + 0.5);
}
void LCMSEXPORT cmsFloat2XYZEncoded(WORD XYZ[3], const cmsCIEXYZ* fXYZ)
{
cmsCIEXYZ xyz;
xyz.X = fXYZ -> X;
xyz.Y = fXYZ -> Y;
xyz.Z = fXYZ -> Z;
// Clamp to encodeable values.
// 1.99997 is reserved as out-of-gamut marker
if (xyz.Y <= 0) {
xyz.X = 0;
xyz.Y = 0;
xyz.Z = 0;
}
if (xyz.X > 1.99996)
xyz.X = 1.99996;
if (xyz.X < 0)
xyz.X = 0;
if (xyz.Y > 1.99996)
xyz.Y = 1.99996;
if (xyz.Y < 0)
xyz.Y = 0;
if (xyz.Z > 1.99996)
xyz.Z = 1.99996;
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 double
static
double XYZ2float(WORD v)
{
Fixed32 fix32;
// From 1.15 to 15.16
fix32 = v << 1;
// From fixed 15.16 to double
return FIXED_TO_DOUBLE(fix32);
}
void LCMSEXPORT cmsXYZEncoded2Float(LPcmsCIEXYZ fXYZ, const WORD XYZ[3])
{
fXYZ -> X = XYZ2float(XYZ[0]);
fXYZ -> Y = XYZ2float(XYZ[1]);
fXYZ -> Z = XYZ2float(XYZ[2]);
}