Include modules/lcms in list of mirrored directories. Old history only available in CVS.

This commit is contained in:
jst@mozilla.com 2007-08-10 17:19:03 -07:00
Родитель 36deca453b
Коммит 96ff644aaa
31 изменённых файлов: 26472 добавлений и 0 удалений

4
modules/lcms/AUTHORS Normal file
Просмотреть файл

@ -0,0 +1,4 @@
Main Author:
------------
Marti Maria <info@littlecms.com>

8
modules/lcms/COPYING Normal file
Просмотреть файл

@ -0,0 +1,8 @@
Little CMS
Copyright (c) 1998-2007 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.

46
modules/lcms/Makefile.in Normal file
Просмотреть файл

@ -0,0 +1,46 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is lcms mozilla build integration.
#
# The Initial Developer of the Original Code is IBM Corporation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = lcms
DIRS = include src
include $(topsrcdir)/config/rules.mk

32
modules/lcms/NEWS Normal file
Просмотреть файл

@ -0,0 +1,32 @@
New in ver 1.17
===============
Changes in API
----------------------
WIN64 support
_vsnprintf wrap
BOOL replaced by LCMSBOOL
cmsSetLanguage parameters changed to match ICC spec
removed support for extended gamut descriptor (was never fully implemented)
cmsFLAGS_NODEFAULTRESOURCEDEF moved to 0x01000000
_cmsMalloc wrapper for malloc
_cmsFree wrapper for free
Implementation
----------------------
All errors moved to fatal, since there is no easy recovery strategy
Vulnerability fixes on cmsio1.c
Status check, many functions now check the status when calling other functions. Improved robustness against ill-formed profiles.
_cmsSaveProfile didn't copy tags from a file based profile, fixed.
Utilities
----------
icctrans: CMYKcm support wrongly implemented, fixed.
icclink: linking 3 to 7 channels didn't work in some cases, fixed.

94
modules/lcms/README.1ST Normal file
Просмотреть файл

@ -0,0 +1,94 @@
Read.me for release 1.17
========================
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.
The lcms library is now distributed under
MIT PUBLIC LICENSE
See file COPYING. for details
This is the 15th. public release the engine. It has been tested
across several versions before, but it is possible some
bugs still arises. If so, sorry for the inconvenience, and
please feel free to submit any suggestion/solution (if you can
found it) at:
info@littlecms.com
Note that the aesthetics of resulting colors are due only to
profiles, and not as consequence of the lcms package.
The main site for the package is located at
http://www.littlecms.com
or
http://www.lcms.coloraid.de
Littlecms has also a mailing list on:
http://lists.sourceforge.net/lists/listinfo/lcms-user
Looking forward the lcms project would grow in future, I will
welcome any contribution/optimization/enhancement.
Enjoy!
About profiles
==============
The demo of this package includes some profiles for colorspace
conversions. I figure all of them are in public domain, but
since some contains copyright notice, I will enumerate here
the sources:
Sun Microsystems Java SDK (widely available)
Kodak public FTP site: ftp.kodak.com
ICM Stress demo from microsoft. www.microsoft.com
sRGB from sRGB site www.srgb.com
If you found any of these not to be in public domain,
please notify me. I will remove the offending profile as soon as
posible.
Additional files
================
ICC34.h is the header file the International Color Consortium
has posted for version spec 3.4, with some minor modifications
for improving portability.
You can reach it at
http://www.color.org

Просмотреть файл

@ -0,0 +1,46 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is lcms mozilla build integration.
#
# The Initial Developer of the Original Code is IBM Corporation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = lcms
EXPORTS = icc34.h lcms.h
include $(topsrcdir)/config/rules.mk

1029
modules/lcms/include/icc34.h Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

2044
modules/lcms/include/lcms.h Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,75 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is lcms mozilla build integration.
#
# The Initial Developer of the Original Code is IBM Corporation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = lcms
LIBRARY_NAME = mozlcms
GRE_MODULE = 1
LIBXUL_LIBRARY = 1
DIST_INSTALL = 1
ifeq (,$(MOZ_ENABLE_LIBXUL)$(BUILD_STATIC_LIBS))
ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH)))
ifndef GNU_CC
MAPFILE = $(LIBRARY_NAME).map
DEFFILE = $(win_srcdir)/lcms.def
endif
endif
ifeq (,$(filter-out WINNT WINCE OS2,$(OS_ARCH)))
DEFINES += -DLCMS_DLL=1 -DLCMS_DLL_BUILD=1
else
VISIBILITY_FLAGS =
endif
endif
REQUIRES = $(LCMS_REQUIRES) \
$(NULL)
CSRCS = cmscnvrt.c cmserr.c cmsgamma.c cmsgmt.c cmsintrp.c cmsio1.c \
cmslut.c cmsmatsh.c cmsmtrx.c cmspack.c cmspcs.c cmswtpnt.c \
cmsxform.c cmssamp.c cmscam97.c cmsnamed.c cmsps2.c cmscam02.c \
cmsvirt.c cmscgats.c cmsio0.c
LOCAL_INCLUDES += -I../include
include $(topsrcdir)/config/rules.mk

490
modules/lcms/src/cmscam02.c Normal file
Просмотреть файл

@ -0,0 +1,490 @@
//
// 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.
// CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging.
#include "lcms.h"
LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC);
LCMSAPI void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel);
LCMSAPI void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut);
LCMSAPI void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut);
// ---------- Implementation --------------------------------------------
typedef struct {
double XYZ[3];
double RGB[3];
double RGBc[3];
double RGBp[3];
double RGBpa[3];
double a, b, h, e, H, A, J, Q, s, t, C, M;
double abC[2];
double abs[2];
double abM[2];
} CAM02COLOR, *LPCAM02COLOR;
typedef struct {
CAM02COLOR adoptedWhite;
double LA, Yb;
double F, c, Nc;
int surround;
double n, Nbb, Ncb, z, FL, D;
} cmsCIECAM02, *LPcmsCIECAM02;
static
double compute_n(LPcmsCIECAM02 pMod)
{
return(pMod -> Yb / pMod -> adoptedWhite.XYZ[1]);
}
static
double compute_z(LPcmsCIECAM02 pMod)
{
return(1.48 + pow(pMod -> n, 0.5));
}
static
double computeNbb(LPcmsCIECAM02 pMod)
{
return(0.725 * pow((1.0 / pMod -> n), 0.2));
}
static
double computeFL(LPcmsCIECAM02 pMod)
{
double 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
double computeD(LPcmsCIECAM02 pMod)
{
double 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, LPcmsCIECAM02 pMod)
{
int 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)
{
double 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, LPcmsCIECAM02 pMod)
{
int i;
double 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, LPcmsCIECAM02 pMod)
{
double 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, LPcmsCIECAM02 pMod)
{
double 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, LPcmsCIECAM02 pMod)
{
int i;
double 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)
{
double 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, LPcmsCIECAM02 pMod)
{
int 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);
}
LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC)
{
LPcmsCIECAM02 lpMod;
if((lpMod = (LPcmsCIECAM02) _cmsMalloc(sizeof(cmsCIECAM02))) == NULL) {
return (LCMSHANDLE) NULL;
}
ZeroMemory(lpMod, sizeof(cmsCIECAM02));
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 AVG_SURROUND_4:
lpMod->F = 1.0; // Not included in CAM02
lpMod->c = 0.69;
lpMod->Nc = 1.0;
break;
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 == D_CALCULATE_DISCOUNT) {
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 (LCMSHANDLE) lpMod;
}
void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel)
{
LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
if (lpMod) _cmsFree(lpMod);
}
void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut)
{
CAM02COLOR clr;
LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
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 LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut)
{
CAM02COLOR clr;
LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
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];
}

721
modules/lcms/src/cmscam97.c Normal file
Просмотреть файл

@ -0,0 +1,721 @@
//
// 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.
#include "lcms.h"
/*
typedef struct {
double J;
double C;
double h;
} cmsJCh, FAR* LPcmsJCh;
#define AVG_SURROUND_4 0
#define AVG_SURROUND 1
#define DIM_SURROUND 2
#define DARK_SURROUND 3
#define CUTSHEET_SURROUND 4
typedef struct {
cmsCIEXYZ whitePoint;
double Yb;
double La;
int surround;
double D_value;
} cmsViewingConditions, FAR* LPcmsViewingConditions;
LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM97sInit(LPcmsViewingConditions pVC);
LCMSAPI void LCMSEXPORT cmsCIECAM97sDone(LCMSHANDLE hModel);
LCMSAPI void LCMSEXPORT cmsCIECAM97sForward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut);
LCMSAPI void LCMSEXPORT cmsCIECAM97sReverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut);
*/
// ---------- Implementation --------------------------------------------
// #define USE_CIECAM97s2 1
#ifdef USE_CIECAM97s2
# define NOISE_CONSTANT 3.05
#else
# define NOISE_CONSTANT 2.05
#endif
/*
The model input data are the adapting field luminance in cd/m2
(normally taken to be 20% of the luminance of white in the adapting field),
LA , the relative tristimulus values of the stimulus, XYZ, the relative
tristimulus values of white in the same viewing conditions, Xw Yw Zw ,
and the relative luminance of the background, Yb . Relative tristimulus
values should be expressed on a scale from Y = 0 for a perfect black
to Y = 100 for a perfect reflecting diffuser. Additionally, the
parameters c, for the impact of surround, Nc , a chromatic induction factor,
and F, a factor for degree of adaptation, must be selected according to the
guidelines in table
All CIE tristimulus values are obtained using the CIE 1931
Standard Colorimetric Observer (2°).
*/
typedef struct {
cmsCIEXYZ WP;
int surround;
int calculate_D;
double Yb; // rel. luminance of background
cmsCIEXYZ RefWhite;
double La; // The adapting field luminance in cd/m2
double c; // Impact of surround
double Nc; // Chromatic induction factor
double Fll; // Lightness contrast factor (Removed on rev 2)
double F; // Degree of adaptation
double k;
double Fl;
double Nbb; // The background and chromatic brightness induction factors.
double Ncb;
double z; // base exponential nonlinearity
double n; // background induction factor
double D;
MAT3 MlamRigg;
MAT3 MlamRigg_1;
MAT3 Mhunt;
MAT3 Mhunt_1;
MAT3 Mhunt_x_MlamRigg_1;
MAT3 MlamRigg_x_Mhunt_1;
VEC3 RGB_subw;
VEC3 RGB_subw_prime;
double p;
VEC3 RGB_subwc;
VEC3 RGB_subaw_prime;
double A_subw;
double Q_subw;
} cmsCIECAM97s,FAR *LPcmsCIECAM97s;
// Free model structure
LCMSAPI void LCMSEXPORT cmsCIECAM97sDone(LCMSHANDLE hModel)
{
LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel;
if (lpMod) _cmsFree(lpMod);
}
// Partial discounting for adaptation degree computation
static
double discount(double d, double chan)
{
return (d * chan + 1 - d);
}
// This routine does model exponential nonlinearity on the short wavelenght
// sensitive channel. On CIECAM97s rev 2 this has been reverted to linear.
static
void FwAdaptationDegree(LPcmsCIECAM97s lpMod, LPVEC3 RGBc, LPVEC3 RGB)
{
#ifdef USE_CIECAM97s2
RGBc->n[0] = RGB->n[0]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[0]);
RGBc->n[1] = RGB->n[1]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[1]);
RGBc->n[2] = RGB->n[2]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[2]);
#else
RGBc->n[0] = RGB->n[0]* discount(lpMod->D, 1.0/lpMod->RGB_subw.n[0]);
RGBc->n[1] = RGB->n[1]* discount(lpMod->D, 1.0/lpMod->RGB_subw.n[1]);
RGBc->n[2] = pow(fabs(RGB->n[2]), lpMod ->p) * discount(lpMod->D, (1.0/pow(lpMod->RGB_subw.n[2], lpMod->p)));
// If B happens to be negative, Then Bc is also set to be negative
if (RGB->n[2] < 0)
RGBc->n[2] = -RGBc->n[2];
#endif
}
static
void RvAdaptationDegree(LPcmsCIECAM97s lpMod, LPVEC3 RGBc, LPVEC3 RGB)
{
#ifdef USE_CIECAM97s2
RGBc->n[0] = RGB->n[0]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[0]);
RGBc->n[1] = RGB->n[1]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[1]);
RGBc->n[2] = RGB->n[2]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[2]);
#else
RGBc->n[0] = RGB->n[0]/discount(lpMod->D, 1.0/lpMod->RGB_subw.n[0]);
RGBc->n[1] = RGB->n[1]/discount(lpMod->D, 1.0/lpMod->RGB_subw.n[1]);
RGBc->n[2] = pow(fabs(RGB->n[2]), 1.0/lpMod->p)/pow(discount(lpMod->D, 1.0/pow(lpMod->RGB_subw.n[2], lpMod->p)), 1.0/lpMod->p);
if (RGB->n[2] < 0)
RGBc->n[2] = -RGBc->n[2];
#endif
}
static
void PostAdaptationConeResponses(LPcmsCIECAM97s lpMod, LPVEC3 RGBa_prime, LPVEC3 RGBprime)
{
if (RGBprime->n[0]>=0.0) {
RGBa_prime->n[0]=((40.0*pow(lpMod -> Fl * RGBprime->n[0]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[0]/100.0, 0.73)+2))+1;
}
else
{
RGBa_prime->n[0]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[0])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[0])/100.0, 0.73)+2))+1;
}
if (RGBprime->n[1]>=0.0)
{
RGBa_prime->n[1]=((40.0*pow(lpMod -> Fl * RGBprime->n[1]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[1]/100.0, 0.73)+2))+1;
}
else
{
RGBa_prime->n[1]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[1])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[1])/100.0, 0.73)+2))+1;
}
if (RGBprime->n[2]>=0.0)
{
RGBa_prime->n[2]=((40.0*pow(lpMod -> Fl * RGBprime->n[2]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[2]/100.0, 0.73)+2))+1;
}
else
{
RGBa_prime->n[2]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[2])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[2])/100.0, 0.73)+2))+1;
}
}
// Compute hue quadrature, eccentricity factor, e
static
void ComputeHueQuadrature(double h, double* H, double* e)
{
#define IRED 0
#define IYELLOW 1
#define IGREEN 2
#define IBLUE 3
double e_tab[] = {0.8, 0.7, 1.0, 1.2};
double H_tab[] = { 0, 100, 200, 300};
int p1, p2;
double e1, e2, h1, h2;
if (h >= 20.14 && h < 90.0) { // Red
p1 = IRED;
p2 = IYELLOW;
}
else
if (h >= 90.0 && h < 164.25) { // Yellow
p1 = IYELLOW;
p2 = IGREEN;
}
else
if (h >= 164.25 && h < 237.53) { // Green
p1 = IGREEN;
p2 = IBLUE; }
else { // Blue
p1 = IBLUE;
p2 = IRED;
}
e1 = e_tab[p1]; e2 = e_tab[p2];
h1 = H_tab[p1]; h2 = H_tab[p2];
*e = e1 + ((e2-e1)*(h-h1)/(h2 - h1));
*H = h1 + (100. * (h - h1) / e1) / ((h - h1)/e1 + (h2 - h) / e2);
#undef IRED
#undef IYELLOW
#undef IGREEN
#undef IBLUE
}
LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM97sInit(LPcmsViewingConditions pVC)
{
LPcmsCIECAM97s lpMod;
VEC3 tmp;
if((lpMod = (LPcmsCIECAM97s) _cmsMalloc(sizeof(cmsCIECAM97s))) == NULL) {
return (LCMSHANDLE) NULL;
}
lpMod->WP.X = pVC->whitePoint.X;
lpMod->WP.Y = pVC->whitePoint.Y;
lpMod->WP.Z = pVC->whitePoint.Z;
lpMod->Yb = pVC->Yb;
lpMod->La = pVC->La;
lpMod->surround = pVC->surround;
lpMod->RefWhite.X = 100.0;
lpMod->RefWhite.Y = 100.0;
lpMod->RefWhite.Z = 100.0;
#ifdef USE_CIECAM97s2
VEC3init(&lpMod->MlamRigg.v[0], 0.8562, 0.3372, -0.1934);
VEC3init(&lpMod->MlamRigg.v[1], -0.8360, 1.8327, 0.0033);
VEC3init(&lpMod->MlamRigg.v[2], 0.0357,-0.0469, 1.0112);
VEC3init(&lpMod->MlamRigg_1.v[0], 0.9874, -0.1768, 0.1894);
VEC3init(&lpMod->MlamRigg_1.v[1], 0.4504, 0.4649, 0.0846);
VEC3init(&lpMod->MlamRigg_1.v[2],-0.0139, 0.0278, 0.9861);
#else
// Bradford transform: Lam-Rigg cone responses
VEC3init(&lpMod->MlamRigg.v[0], 0.8951, 0.2664, -0.1614);
VEC3init(&lpMod->MlamRigg.v[1], -0.7502, 1.7135, 0.0367);
VEC3init(&lpMod->MlamRigg.v[2], 0.0389, -0.0685, 1.0296);
// Inverse of Lam-Rigg
VEC3init(&lpMod->MlamRigg_1.v[0], 0.98699, -0.14705, 0.15996);
VEC3init(&lpMod->MlamRigg_1.v[1], 0.43231, 0.51836, 0.04929);
VEC3init(&lpMod->MlamRigg_1.v[2], -0.00853, 0.04004, 0.96849);
#endif
// Hunt-Pointer-Estevez cone responses
VEC3init(&lpMod->Mhunt.v[0], 0.38971, 0.68898, -0.07868);
VEC3init(&lpMod->Mhunt.v[1], -0.22981, 1.18340, 0.04641);
VEC3init(&lpMod->Mhunt.v[2], 0.0, 0.0, 1.0);
// Inverse of Hunt-Pointer-Estevez
VEC3init(&lpMod->Mhunt_1.v[0], 1.91019, -1.11214, 0.20195);
VEC3init(&lpMod->Mhunt_1.v[1], 0.37095, 0.62905, 0.0);
VEC3init(&lpMod->Mhunt_1.v[2], 0.0, 0.0, 1.0);
if (pVC->D_value == -1.0)
lpMod->calculate_D = 1;
else
if (pVC->D_value == -2.0)
lpMod->calculate_D = 2;
else {
lpMod->calculate_D = 0;
lpMod->D = pVC->D_value;
}
// Table I (revised)
switch (lpMod->surround) {
case AVG_SURROUND_4:
lpMod->F = 1.0;
lpMod->c = 0.69;
lpMod->Fll = 0.0; // Not included on Rev 2
lpMod->Nc = 1.0;
break;
case AVG_SURROUND:
lpMod->F = 1.0;
lpMod->c = 0.69;
lpMod->Fll = 1.0;
lpMod->Nc = 1.0;
break;
case DIM_SURROUND:
lpMod->F = 0.99;
lpMod->c = 0.59;
lpMod->Fll = 1.0;
lpMod->Nc = 0.95;
break;
case DARK_SURROUND:
lpMod->F = 0.9;
lpMod->c = 0.525;
lpMod->Fll = 1.0;
lpMod->Nc = 0.8;
break;
case CUTSHEET_SURROUND:
lpMod->F = 0.9;
lpMod->c = 0.41;
lpMod->Fll = 1.0;
lpMod->Nc = 0.8;
break;
default:
lpMod->F = 1.0;
lpMod->c = 0.69;
lpMod->Fll = 1.0;
lpMod->Nc = 1.0;
break;
}
lpMod->k = 1 / (5 * lpMod->La + 1);
lpMod->Fl = lpMod->La * pow(lpMod->k, 4) + 0.1*pow(1 - pow(lpMod->k, 4), 2.0) * pow(5*lpMod->La, 1.0/3.0);
if (lpMod->calculate_D > 0) {
lpMod->D = lpMod->F * (1 - 1 / (1 + 2*pow(lpMod->La, 0.25) + pow(lpMod->La, 2)/300.0));
if (lpMod->calculate_D > 1)
lpMod->D = (lpMod->D + 1.0) / 2;
}
// RGB_subw = [MlamRigg][WP/YWp]
#ifdef USE_CIECAM97s2
MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, (LPVEC3) &lpMod -> WP);
#else
VEC3divK(&tmp, (LPVEC3) &lpMod -> WP, lpMod->WP.Y);
MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, &tmp);
#endif
MAT3per(&lpMod -> Mhunt_x_MlamRigg_1, &lpMod -> Mhunt, &lpMod->MlamRigg_1 );
MAT3per(&lpMod -> MlamRigg_x_Mhunt_1, &lpMod -> MlamRigg, &lpMod -> Mhunt_1 );
// p is used on forward model
lpMod->p = pow(lpMod->RGB_subw.n[2], 0.0834);
FwAdaptationDegree(lpMod, &lpMod->RGB_subwc, &lpMod->RGB_subw);
#if USE_CIECAM97s2
MAT3eval(&lpMod->RGB_subw_prime, &lpMod->Mhunt_x_MlamRigg_1, &lpMod -> RGB_subwc);
#else
VEC3perK(&tmp, &lpMod -> RGB_subwc, lpMod->WP.Y);
MAT3eval(&lpMod->RGB_subw_prime, &lpMod->Mhunt_x_MlamRigg_1, &tmp);
#endif
lpMod->n = lpMod-> Yb / lpMod-> WP.Y;
lpMod->z = 1 + lpMod->Fll * sqrt(lpMod->n);
lpMod->Nbb = lpMod->Ncb = 0.725 / pow(lpMod->n, 0.2);
PostAdaptationConeResponses(lpMod, &lpMod->RGB_subaw_prime, &lpMod->RGB_subw_prime);
lpMod->A_subw=lpMod->Nbb*(2.0*lpMod->RGB_subaw_prime.n[0]+lpMod->RGB_subaw_prime.n[1]+lpMod->RGB_subaw_prime.n[2]/20.0-NOISE_CONSTANT);
return (LCMSHANDLE) lpMod;
}
//
// The forward model: XYZ -> JCh
//
LCMSAPI void LCMSEXPORT cmsCIECAM97sForward(LCMSHANDLE hModel, LPcmsCIEXYZ inPtr, LPcmsJCh outPtr)
{
LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel;
double a, b, h, s, H1val, es, A;
VEC3 In, RGB, RGBc, RGBprime, RGBa_prime;
if (inPtr -> Y <= 0.0) {
outPtr -> J = outPtr -> C = outPtr -> h = 0.0;
return;
}
// An initial chromatic adaptation transform is used to go from the source
// viewing conditions to corresponding colours under the equal-energy-illuminant
// reference viewing conditions. This is handled differently on rev 2
VEC3init(&In, inPtr -> X, inPtr -> Y, inPtr -> Z); // 2.1
#ifdef USE_CIECAM97s2
// Since the chromatic adaptation transform has been linearized, it
// is no longer required to divide the stimulus tristimulus values
// by their own Y tristimulus value prior to the chromatic adaptation.
#else
VEC3divK(&In, &In, inPtr -> Y);
#endif
MAT3eval(&RGB, &lpMod -> MlamRigg, &In); // 2.2
FwAdaptationDegree(lpMod, &RGBc, &RGB);
// The post-adaptation signals for both the sample and the white are then
// transformed from the sharpened cone responses to the Hunt-Pointer-Estevez
// cone responses.
#ifdef USE_CIECAM97s2
#else
VEC3perK(&RGBc, &RGBc, inPtr->Y);
#endif
MAT3eval(&RGBprime, &lpMod->Mhunt_x_MlamRigg_1, &RGBc);
// The post-adaptation cone responses (for both the stimulus and the white)
// are then calculated.
PostAdaptationConeResponses(lpMod, &RGBa_prime, &RGBprime);
// Preliminary red-green and yellow-blue opponent dimensions are calculated
a = RGBa_prime.n[0] - (12.0 * RGBa_prime.n[1] / 11.0) + RGBa_prime.n[2]/11.0;
b = (RGBa_prime.n[0] + RGBa_prime.n[1] - 2.0 * RGBa_prime.n[2]) / 9.0;
// The CIECAM97s hue angle, h, is then calculated
h = (180.0/M_PI)*(atan2(b, a));
while (h < 0)
h += 360.0;
outPtr->h = h;
// hue quadrature and eccentricity factors, e, are calculated
ComputeHueQuadrature(h, &H1val, &es);
// ComputeHueQuadrature(h, &H1val, &h1, &e1, &h2, &e2, &es);
// The achromatic response A
A = lpMod->Nbb * (2.0 * RGBa_prime.n[0] + RGBa_prime.n[1] + RGBa_prime.n[2]/20.0 - NOISE_CONSTANT);
// CIECAM97s Lightness J
outPtr -> J = 100.0 * pow(A / lpMod->A_subw, lpMod->c * lpMod->z);
// CIECAM97s saturation s
s = (50 * hypot (a, b) * 100 * es * (10.0/13.0) * lpMod-> Nc * lpMod->Ncb) / (RGBa_prime.n[0] + RGBa_prime.n[1] + 1.05 * RGBa_prime.n[2]);
// CIECAM97s Chroma C
#ifdef USE_CIECAM97s2
// Eq. 26 has been modified to allow accurate prediction of the Munsell chroma scales.
outPtr->C = 0.7487 * pow(s, 0.973) * pow(outPtr->J/100.0, 0.945 * lpMod->n) * (1.64 - pow(0.29, lpMod->n));
#else
outPtr->C = 2.44 * pow(s, 0.69) * pow(outPtr->J/100.0, 0.67 * lpMod->n) * (1.64 - pow(0.29, lpMod->n));
#endif
}
//
// The reverse model JCh -> XYZ
//
LCMSAPI void LCMSEXPORT cmsCIECAM97sReverse(LCMSHANDLE hModel, LPcmsJCh inPtr, LPcmsCIEXYZ outPtr)
{
LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel;
double J, C, h, A, H1val, es, s, a, b;
double tan_h, sec_h;
double R_suba_prime, G_suba_prime, B_suba_prime;
double R_prime, G_prime, B_prime;
double Y_subc, Y_prime, B_term;
VEC3 tmp;
VEC3 RGB_prime, RGB_subc_Y;
VEC3 Y_over_Y_subc_RGB;
VEC3 XYZ_primeprime_over_Y_subc;
#ifdef USE_CIECAM92s2
VEC3 RGBY;
VEC3 Out;
#endif
J = inPtr->J;
h = inPtr->h;
C = inPtr->C;
if (J <= 0) {
outPtr->X = 0.0;
outPtr->Y = 0.0;
outPtr->Z = 0.0;
return;
}
// (2) From J Obtain A
A = pow(J/100.0, 1/(lpMod->c * lpMod->z)) * lpMod->A_subw;
// (3), (4), (5) Using H Determine h1, h2, e1, e2
// e1 and h1 are the values of e and h for the unique hue having the
// nearest lower valur of h and e2 and h2 are the values of e and h for
// the unique hue having the nearest higher value of h.
ComputeHueQuadrature(h, &H1val, &es);
// (7) Calculate s
s = pow(C / (2.44 * pow(J/100.0, 0.67*lpMod->n) * (1.64 - pow(0.29, lpMod->n))) , (1./0.69));
// (8) Calculate a and b.
// NOTE: sqrt(1 + tan^2) == sec(h)
tan_h = tan ((M_PI/180.)*(h));
sec_h = sqrt(1 + tan_h * tan_h);
if ((h > 90) && (h < 270))
sec_h = -sec_h;
a = s * ( A/lpMod->Nbb + NOISE_CONSTANT) / ( sec_h * 50000.0 * es * lpMod->Nc * lpMod->Ncb/ 13.0 +
s * (11.0 / 23.0 + (108.0/23.0) * tan_h));
b = a * tan_h;
//(9) Calculate R'a G'a and B'a
R_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) + (41.0/61.0) * (11.0/23.0) * a + (288.0/61.0) / 23.0 * b;
G_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) - (81.0/61.0) * (11.0/23.0) * a - (261.0/61.0) / 23.0 * b;
B_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) - (20.0/61.0) * (11.0/23.0) * a - (20.0/61.0) * (315.0/23.0) * b;
// (10) Calculate R', G' and B'
if ((R_suba_prime - 1) < 0) {
R_prime = -100.0 * pow((2.0 - 2.0 * R_suba_prime) /
(39.0 + R_suba_prime), 1.0/0.73);
}
else
{
R_prime = 100.0 * pow((2.0 * R_suba_prime - 2.0) /
(41.0 - R_suba_prime), 1.0/0.73);
}
if ((G_suba_prime - 1) < 0)
{
G_prime = -100.0 * pow((2.0 - 2.0 * G_suba_prime) /
(39.0 + G_suba_prime), 1.0/0.73);
}
else
{
G_prime = 100.0 * pow((2.0 * G_suba_prime - 2.0) /
(41.0 - G_suba_prime), 1.0/0.73);
}
if ((B_suba_prime - 1) < 0)
{
B_prime = -100.0 * pow((2.0 - 2.0 * B_suba_prime) /
(39.0 + B_suba_prime), 1.0/0.73);
}
else
{
B_prime = 100.0 * pow((2.0 * B_suba_prime - 2.0) /
(41.0 - B_suba_prime), 1.0/0.73);
}
// (11) Calculate RcY, GcY and BcY
VEC3init(&RGB_prime, R_prime, G_prime, B_prime);
VEC3divK(&tmp, &RGB_prime, lpMod -> Fl);
MAT3eval(&RGB_subc_Y, &lpMod->MlamRigg_x_Mhunt_1, &tmp);
#ifdef USE_CIECAM97s2
// (12)
RvAdaptationDegree(lpMod, &RGBY, &RGB_subc_Y);
MAT3eval(&Out, &lpMod->MlamRigg_1, &RGBY);
outPtr -> X = Out.n[0];
outPtr -> Y = Out.n[1];
outPtr -> Z = Out.n[2];
#else
// (12) Calculate Yc
Y_subc = 0.43231*RGB_subc_Y.n[0]+0.51836*RGB_subc_Y.n[1]+0.04929*RGB_subc_Y.n[2];
// (13) Calculate (Y/Yc)R, (Y/Yc)G and (Y/Yc)B
VEC3divK(&RGB_subc_Y, &RGB_subc_Y, Y_subc);
RvAdaptationDegree(lpMod, &Y_over_Y_subc_RGB, &RGB_subc_Y);
// (14) Calculate Y'
Y_prime = 0.43231*(Y_over_Y_subc_RGB.n[0]*Y_subc) + 0.51836*(Y_over_Y_subc_RGB.n[1]*Y_subc) + 0.04929 * (Y_over_Y_subc_RGB.n[2]*Y_subc);
if (Y_prime < 0 || Y_subc < 0)
{
// Discard to near black point
outPtr -> X = 0;
outPtr -> Y = 0;
outPtr -> Z = 0;
return;
}
B_term = pow(Y_prime / Y_subc, (1.0 / lpMod->p) - 1);
// (15) Calculate X'', Y'' and Z''
Y_over_Y_subc_RGB.n[2] /= B_term;
MAT3eval(&XYZ_primeprime_over_Y_subc, &lpMod->MlamRigg_1, &Y_over_Y_subc_RGB);
outPtr->X = XYZ_primeprime_over_Y_subc.n[0] * Y_subc;
outPtr->Y = XYZ_primeprime_over_Y_subc.n[1] * Y_subc;
outPtr->Z = XYZ_primeprime_over_Y_subc.n[2] * Y_subc;
#endif
}

2460
modules/lcms/src/cmscgats.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

637
modules/lcms/src/cmscnvrt.c Normal file
Просмотреть файл

@ -0,0 +1,637 @@
//
// 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.
#include "lcms.h"
/*
This module provides conversion stages for handling intents.
The chain of evaluation in a transform is:
PCS1 PCS2 PCS3 PCS4
|From | |From | |Conversion | |Preview | |Gamut | |Conversion | |To | |To |
|Input|->|Device|->|Stage 1 |->|handling|->|Checking|->|Stage 2 |->|Device|->|output |
-------- ------- ------------- --------- ---------- ------------- ------- ---------
AToB0 prew0 gamut BToA0
Formatting LUT Adjusting LUT LUT Adjusting LUT Formatting
Intent Intent 1 intent intent Intent 2 Intent
Some of these LUT may be missing
There are two intents involved here, the intent of the transform itself, and the
intent the proof is being done, if is the case. Since the first intent is to be
applied to preview, is the proofing intent. The second intent identifies the
transform intent. Input data of any stage is taked as relative colorimetric
always.
NOTES: V4 states than perceptual & saturation intents between mixed v2 & v4 profiles should
scale PCS from a black point equal to ZERO in v2 profiles to the reference media black of
perceptual v4 PCS. Since I found many v2 profiles to be using a perceptual intent with black
point not zero at all, I'm implementing that as a black point compensation from whatever
black from perceptal intent to the reference media black for v4 profiles.
*/
int cdecl cmsChooseCnvrt(int Absolute,
int Phase1, LPcmsCIEXYZ BlackPointIn,
LPcmsCIEXYZ WhitePointIn,
LPcmsCIEXYZ IlluminantIn,
LPMAT3 ChromaticAdaptationMatrixIn,
int Phase2, LPcmsCIEXYZ BlackPointOut,
LPcmsCIEXYZ WhitePointOut,
LPcmsCIEXYZ IlluminantOut,
LPMAT3 ChromaticAdaptationMatrixOut,
int DoBlackPointCompensation,
double AdaptationState,
_cmsADJFN *fn1,
LPWMAT3 wm, LPWVEC3 wof);
// -------------------------------------------------------------------------
// D50 - Widely used
LCMSAPI LPcmsCIEXYZ LCMSEXPORT cmsD50_XYZ(void)
{
static cmsCIEXYZ D50XYZ = {D50X, D50Y, D50Z};
return &D50XYZ;
}
LCMSAPI LPcmsCIExyY LCMSEXPORT cmsD50_xyY(void)
{
static cmsCIExyY D50xyY;
cmsXYZ2xyY(&D50xyY, cmsD50_XYZ());
return &D50xyY;
}
// ---------------- From LUT to LUT --------------------------
// Calculate m, offset Relativ -> Absolute undoing any chromatic
// adaptation done by the profile.
#ifdef _MSC_VER
#pragma warning(disable : 4100 4505)
#endif
// join scalings to obtain:
// relative input to absolute and then to relative output
static
void Rel2RelStepAbsCoefs(double AdaptationState,
LPcmsCIEXYZ BlackPointIn,
LPcmsCIEXYZ WhitePointIn,
LPcmsCIEXYZ IlluminantIn,
LPMAT3 ChromaticAdaptationMatrixIn,
LPcmsCIEXYZ BlackPointOut,
LPcmsCIEXYZ WhitePointOut,
LPcmsCIEXYZ IlluminantOut,
LPMAT3 ChromaticAdaptationMatrixOut,
LPMAT3 m, LPVEC3 of)
{
VEC3 WtPtIn, WtPtInAdapted;
VEC3 WtPtOut, WtPtOutAdapted;
MAT3 Scale, m1, m2, m3;
VEC3init(&WtPtIn, WhitePointIn->X, WhitePointIn->Y, WhitePointIn->Z);
MAT3eval(&WtPtInAdapted, ChromaticAdaptationMatrixIn, &WtPtIn);
VEC3init(&WtPtOut, WhitePointOut->X, WhitePointOut->Y, WhitePointOut->Z);
MAT3eval(&WtPtOutAdapted, ChromaticAdaptationMatrixOut, &WtPtOut);
VEC3init(&Scale.v[0], WtPtInAdapted.n[0] / WtPtOutAdapted.n[0], 0, 0);
VEC3init(&Scale.v[1], 0, WtPtInAdapted.n[1] / WtPtOutAdapted.n[1], 0);
VEC3init(&Scale.v[2], 0, 0, WtPtInAdapted.n[2] / WtPtOutAdapted.n[2]);
// Adaptation state
if (AdaptationState == 1.0) {
// Observer is fully adapted. Keep chromatic adaptation
CopyMemory(m, &Scale, sizeof(MAT3));
}
else {
// Observer is not adapted, undo the chromatic adaptation
m1 = *ChromaticAdaptationMatrixIn;
MAT3inverse(&m1, &m2);
MAT3per(&m3, &m2, &Scale);
MAT3per(m, &m3, ChromaticAdaptationMatrixOut);
}
VEC3init(of, 0.0, 0.0, 0.0);
}
// The (in)famous black point compensation. Right now implemented as
// a linear scaling in XYZ
static
void ComputeBlackPointCompensationFactors(LPcmsCIEXYZ BlackPointIn,
LPcmsCIEXYZ WhitePointIn,
LPcmsCIEXYZ IlluminantIn,
LPcmsCIEXYZ BlackPointOut,
LPcmsCIEXYZ WhitePointOut,
LPcmsCIEXYZ IlluminantOut,
LPMAT3 m, LPVEC3 of)
{
cmsCIEXYZ RelativeBlackPointIn, RelativeBlackPointOut;
double ax, ay, az, bx, by, bz, tx, ty, tz;
// At first, convert both black points to relative.
cmsAdaptToIlluminant(&RelativeBlackPointIn, WhitePointIn, IlluminantIn, BlackPointIn);
cmsAdaptToIlluminant(&RelativeBlackPointOut, WhitePointOut, IlluminantOut, BlackPointOut);
// Now we need to compute a matrix plus an offset m and of such of
// [m]*bpin + off = bpout
// [m]*D50 + off = D50
//
// This is a linear scaling in the form ax+b, where
// a = (bpout - D50) / (bpin - D50)
// b = - D50* (bpout - bpin) / (bpin - D50)
tx = RelativeBlackPointIn.X - IlluminantIn ->X;
ty = RelativeBlackPointIn.Y - IlluminantIn ->Y;
tz = RelativeBlackPointIn.Z - IlluminantIn ->Z;
ax = (RelativeBlackPointOut.X - IlluminantOut ->X) / tx;
ay = (RelativeBlackPointOut.Y - IlluminantOut ->Y) / ty;
az = (RelativeBlackPointOut.Z - IlluminantOut ->Z) / tz;
bx = - IlluminantOut -> X * (RelativeBlackPointOut.X - RelativeBlackPointIn.X) / tx;
by = - IlluminantOut -> Y * (RelativeBlackPointOut.Y - RelativeBlackPointIn.Y) / ty;
bz = - IlluminantOut -> Z * (RelativeBlackPointOut.Z - RelativeBlackPointIn.Z) / tz;
MAT3identity(m);
m->v[VX].n[0] = ax;
m->v[VY].n[1] = ay;
m->v[VZ].n[2] = az;
VEC3init(of, bx, by, bz);
}
// Return TRUE if both m and of are empy -- "m" being identity and "of" being 0
static
LCMSBOOL IdentityParameters(LPWMAT3 m, LPWVEC3 of)
{
WVEC3 wv0;
VEC3initF(&wv0, 0, 0, 0);
if (!MAT3isIdentity(m, 0.00001)) return FALSE;
if (!VEC3equal(of, &wv0, 0.00001)) return FALSE;
return TRUE;
}
// ----------------------------------------- Inter PCS conversions
// XYZ to XYZ linear scaling. Aso used on Black point compensation
static
void XYZ2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
{
WVEC3 a, r;
a.n[0] = In[0] << 1;
a.n[1] = In[1] << 1;
a.n[2] = In[2] << 1;
MAT3evalW(&r, m, &a);
Out[0] = _cmsClampWord((r.n[VX] + of->n[VX]) >> 1);
Out[1] = _cmsClampWord((r.n[VY] + of->n[VY]) >> 1);
Out[2] = _cmsClampWord((r.n[VZ] + of->n[VZ]) >> 1);
}
// XYZ to Lab, scaling first
static
void XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
{
WORD XYZ[3];
XYZ2XYZ(In, XYZ, m, of);
cmsXYZ2LabEncoded(XYZ, Out);
}
// Lab to XYZ, then scalling
static
void Lab2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
{
WORD XYZ[3];
cmsLab2XYZEncoded(In, XYZ);
XYZ2XYZ(XYZ, Out, m, of);
}
// Lab to XYZ, scalling and then, back to Lab
static
void Lab2XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
{
WORD XYZ[3], XYZ2[3];
cmsLab2XYZEncoded(In, XYZ);
XYZ2XYZ(XYZ, XYZ2, m, of);
cmsXYZ2LabEncoded(XYZ2, Out);
}
// ------------------------------------------------------------------
// Dispatcher for XYZ Relative LUT
static
int FromXYZRelLUT(int Absolute,
LPcmsCIEXYZ BlackPointIn,
LPcmsCIEXYZ WhitePointIn,
LPcmsCIEXYZ IlluminantIn,
LPMAT3 ChromaticAdaptationMatrixIn,
int Phase2, LPcmsCIEXYZ BlackPointOut,
LPcmsCIEXYZ WhitePointOut,
LPcmsCIEXYZ IlluminantOut,
LPMAT3 ChromaticAdaptationMatrixOut,
int DoBlackPointCompensation,
double AdaptationState,
_cmsADJFN *fn1,
LPMAT3 m, LPVEC3 of)
{
switch (Phase2) {
// From relative XYZ to Relative XYZ.
case XYZRel:
if (Absolute)
{
// From input relative to absolute, and then
// back to output relative
Rel2RelStepAbsCoefs(AdaptationState,
BlackPointIn,
WhitePointIn,
IlluminantIn,
ChromaticAdaptationMatrixIn,
BlackPointOut,
WhitePointOut,
IlluminantOut,
ChromaticAdaptationMatrixOut,
m, of);
*fn1 = XYZ2XYZ;
}
else
{
// XYZ Relative to XYZ relative, no op required
*fn1 = NULL;
if (DoBlackPointCompensation) {
*fn1 = XYZ2XYZ;
ComputeBlackPointCompensationFactors(BlackPointIn,
WhitePointIn,
IlluminantIn,
BlackPointOut,
WhitePointOut,
IlluminantOut,
m, of);
}
}
break;
// From relative XYZ to Relative Lab
case LabRel:
// First pass XYZ to absolute, then to relative and
// finally to Lab. I use here D50 for output in order
// to prepare the "to Lab" conversion.
if (Absolute)
{
Rel2RelStepAbsCoefs(AdaptationState,
BlackPointIn,
WhitePointIn,
IlluminantIn,
ChromaticAdaptationMatrixIn,
BlackPointOut,
WhitePointOut,
IlluminantOut,
ChromaticAdaptationMatrixOut,
m, of);
*fn1 = XYZ2Lab;
}
else
{
// Just Convert to Lab
MAT3identity(m);
VEC3init(of, 0, 0, 0);
*fn1 = XYZ2Lab;
if (DoBlackPointCompensation) {
ComputeBlackPointCompensationFactors(BlackPointIn,
WhitePointIn,
IlluminantIn,
BlackPointOut,
WhitePointOut,
IlluminantOut,
m, of);
}
}
break;
default: return FALSE;
}
return TRUE;
}
// From Lab Relative type LUT
static
int FromLabRelLUT(int Absolute,
LPcmsCIEXYZ BlackPointIn,
LPcmsCIEXYZ WhitePointIn,
LPcmsCIEXYZ IlluminantIn,
LPMAT3 ChromaticAdaptationMatrixIn,
int Phase2, LPcmsCIEXYZ BlackPointOut,
LPcmsCIEXYZ WhitePointOut,
LPcmsCIEXYZ IlluminantOut,
LPMAT3 ChromaticAdaptationMatrixOut,
int DoBlackPointCompensation,
double AdaptationState,
_cmsADJFN *fn1,
LPMAT3 m, LPVEC3 of)
{
switch (Phase2) {
// From Lab Relative to XYZ Relative, very usual case
case XYZRel:
if (Absolute) { // Absolute intent
// From lab relative, to XYZ absolute, and then,
// back to XYZ relative
Rel2RelStepAbsCoefs(AdaptationState,
BlackPointIn,
WhitePointIn,
cmsD50_XYZ(),
ChromaticAdaptationMatrixIn,
BlackPointOut,
WhitePointOut,
IlluminantOut,
ChromaticAdaptationMatrixOut,
m, of);
*fn1 = Lab2XYZ;
}
else
{
// From Lab relative, to XYZ relative.
*fn1 = Lab2XYZ;
if (DoBlackPointCompensation) {
ComputeBlackPointCompensationFactors(BlackPointIn,
WhitePointIn,
IlluminantIn,
BlackPointOut,
WhitePointOut,
IlluminantOut,
m, of);
}
}
break;
case LabRel:
if (Absolute) {
// First pass to XYZ using the input illuminant
// * InIlluminant / D50, then to absolute. Then
// to relative, but for input
Rel2RelStepAbsCoefs(AdaptationState,
BlackPointIn,
WhitePointIn, IlluminantIn,
ChromaticAdaptationMatrixIn,
BlackPointOut,
WhitePointOut, cmsD50_XYZ(),
ChromaticAdaptationMatrixOut,
m, of);
*fn1 = Lab2XYZ2Lab;
}
else
{ // Lab -> Lab relative don't need any adjust unless
// black point compensation
*fn1 = NULL;
if (DoBlackPointCompensation) {
*fn1 = Lab2XYZ2Lab;
ComputeBlackPointCompensationFactors(BlackPointIn,
WhitePointIn,
IlluminantIn,
BlackPointOut,
WhitePointOut,
IlluminantOut,
m, of);
}
}
break;
default: return FALSE;
}
return TRUE;
}
// This function does calculate the necessary conversion operations
// needed from transpassing data from a LUT to a LUT. The conversion
// is modeled as a pointer of function and two coefficients, a and b
// The function is actually called only if not null pointer is provided,
// and the two paramaters are passed in. There are several types of
// conversions, but basically they do a linear scalling and a interchange
// Main dispatcher
int cmsChooseCnvrt(int Absolute,
int Phase1, LPcmsCIEXYZ BlackPointIn,
LPcmsCIEXYZ WhitePointIn,
LPcmsCIEXYZ IlluminantIn,
LPMAT3 ChromaticAdaptationMatrixIn,
int Phase2, LPcmsCIEXYZ BlackPointOut,
LPcmsCIEXYZ WhitePointOut,
LPcmsCIEXYZ IlluminantOut,
LPMAT3 ChromaticAdaptationMatrixOut,
int DoBlackPointCompensation,
double AdaptationState,
_cmsADJFN *fn1,
LPWMAT3 wm, LPWVEC3 wof)
{
int rc;
MAT3 m;
VEC3 of;
MAT3identity(&m);
VEC3init(&of, 0, 0, 0);
switch (Phase1) {
// Input LUT is giving XYZ relative values.
case XYZRel: rc = FromXYZRelLUT(Absolute,
BlackPointIn,
WhitePointIn,
IlluminantIn,
ChromaticAdaptationMatrixIn,
Phase2,
BlackPointOut,
WhitePointOut,
IlluminantOut,
ChromaticAdaptationMatrixOut,
DoBlackPointCompensation,
AdaptationState,
fn1, &m, &of);
break;
// Input LUT is giving Lab relative values
case LabRel: rc = FromLabRelLUT(Absolute,
BlackPointIn,
WhitePointIn,
IlluminantIn,
ChromaticAdaptationMatrixIn,
Phase2,
BlackPointOut,
WhitePointOut,
IlluminantOut,
ChromaticAdaptationMatrixOut,
DoBlackPointCompensation,
AdaptationState,
fn1, &m, &of);
break;
// Unrecognized combination
default: cmsSignalError(LCMS_ERRC_ABORTED, "(internal) Phase error");
return FALSE;
}
MAT3toFix(wm, &m);
VEC3toFix(wof, &of);
// Do some optimization -- discard conversion if identity parameters.
if (*fn1 == XYZ2XYZ || *fn1 == Lab2XYZ2Lab) {
if (IdentityParameters(wm, wof))
*fn1 = NULL;
}
return rc;
}

110
modules/lcms/src/cmserr.c Normal file
Просмотреть файл

@ -0,0 +1,110 @@
//
// 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.
#include "lcms.h"
// As a rule, only the functions visible from API can signal
// errors.
void cdecl cmsSignalError(int ErrorCode, const char *ErrorText, ...);
int LCMSEXPORT cmsErrorAction(int lAbort);
void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn);
// ******************************************************************
static int nDoAbort = LCMS_ERROR_ABORT;
static cmsErrorHandlerFunction UserErrorHandler = (cmsErrorHandlerFunction) NULL;
int LCMSEXPORT cmsErrorAction(int nAction)
{
int nOld = nDoAbort;
nDoAbort = nAction;
return nOld;
}
void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn)
{
UserErrorHandler = Fn;
}
// Default error handler
void cmsSignalError(int ErrorCode, const char *ErrorText, ...)
{
va_list args;
if (nDoAbort == LCMS_ERROR_IGNORE) return;
va_start(args, ErrorText);
if (UserErrorHandler != NULL) {
char Buffer[1024];
vsnprintf(Buffer, 1023, ErrorText, args);
va_end(args);
if (UserErrorHandler(ErrorCode, Buffer)) {
return;
}
}
#if defined( __CONSOLE__ ) || defined( NON_WINDOWS )
fprintf(stderr, "lcms: Error #%d; ", ErrorCode);
vfprintf(stderr, ErrorText, args);
fprintf(stderr, "\n");
va_end(args);
if (nDoAbort == LCMS_ERROR_ABORT) exit(1);
#else
{
char Buffer1[1024];
char Buffer2[256];
snprintf(Buffer1, 767, "Error #%x; ", ErrorCode);
vsnprintf(Buffer2, 255, ErrorText, args);
strcat(Buffer1, Buffer2);
MessageBox(NULL, Buffer1, "Little cms",
MB_OK|MB_ICONSTOP|MB_TASKMODAL);
va_end(args);
if (nDoAbort == LCMS_ERROR_ABORT) {
#ifdef __BORLANDC__
_cexit();
#endif
FatalAppExit(0, "lcms is terminating application");
}
}
#endif
}

954
modules/lcms/src/cmsgamma.c Normal file
Просмотреть файл

@ -0,0 +1,954 @@
//
// 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.
#include "lcms.h"
// Gamma handling.
LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries);
void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma);
void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3]);
LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma);
LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE Src);
LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma);
LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[]);
LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma);
LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma, int nPoints);
LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda);
LCMSBOOL cdecl _cmsSmoothEndpoints(LPWORD Table, int nPoints);
// Sampled curves
LPSAMPLEDCURVE cdecl cmsAllocSampledCurve(int nItems);
void cdecl cmsFreeSampledCurve(LPSAMPLEDCURVE p);
void cdecl cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max);
void cdecl cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max);
LCMSBOOL cdecl cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double SmoothingLambda);
void cdecl cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints);
LPSAMPLEDCURVE cdecl cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints);
double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t);
double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold);
// ----------------------------------------------------------------------------------------
#define MAX_KNOTS 4096
typedef float vec[MAX_KNOTS+1];
// Ciclic-redundant-check for assuring table is a true representation of parametric curve
// The usual polynomial, which is used for AAL5, FDDI, and probably Ethernet
#define QUOTIENT 0x04c11db7
static
unsigned int Crc32(unsigned int result, LPVOID ptr, int len)
{
int i,j;
BYTE octet;
LPBYTE data = (LPBYTE) ptr;
for (i=0; i < len; i++) {
octet = *data++;
for (j=0; j < 8; j++) {
if (result & 0x80000000) {
result = (result << 1) ^ QUOTIENT ^ (octet >> 7);
}
else
{
result = (result << 1) ^ (octet >> 7);
}
octet <<= 1;
}
}
return result;
}
// Get CRC of gamma table
unsigned int _cmsCrc32OfGammaTable(LPGAMMATABLE Table)
{
unsigned int crc = ~0U;
crc = Crc32(crc, &Table -> Seed.Type, sizeof(int));
crc = Crc32(crc, Table ->Seed.Params, sizeof(double)*10);
crc = Crc32(crc, &Table ->nEntries, sizeof(int));
crc = Crc32(crc, Table ->GammaTable, sizeof(WORD) * Table -> nEntries);
return ~crc;
}
LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries)
{
LPGAMMATABLE p;
size_t size;
if (nEntries > 65530 || nEntries < 0) {
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't create gammatable of more than 65530 entries");
return NULL;
}
size = sizeof(GAMMATABLE) + (sizeof(WORD) * (nEntries-1));
p = (LPGAMMATABLE) _cmsMalloc(size);
if (!p) return NULL;
ZeroMemory(p, size);
p -> Seed.Type = 0;
p -> nEntries = nEntries;
return p;
}
void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma)
{
if (Gamma) _cmsFree(Gamma);
}
void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3])
{
cmsFreeGamma(Gamma[0]);
cmsFreeGamma(Gamma[1]);
cmsFreeGamma(Gamma[2]);
Gamma[0] = Gamma[1] = Gamma[2] = NULL;
}
// Duplicate a gamma table
LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE In)
{
LPGAMMATABLE Ptr;
size_t size;
Ptr = cmsAllocGamma(In -> nEntries);
if (Ptr == NULL) return NULL;
size = sizeof(GAMMATABLE) + (sizeof(WORD) * (In -> nEntries-1));
CopyMemory(Ptr, In, size);
return Ptr;
}
// Handle gamma using interpolation tables. The resulting curves can become
// very stange, but are pleasent to eye.
LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma,
LPGAMMATABLE OutGamma)
{
register int i;
L16PARAMS L16In, L16Out;
LPWORD InPtr, OutPtr;
LPGAMMATABLE p;
p = cmsAllocGamma(256);
if (!p) return NULL;
cmsCalcL16Params(InGamma -> nEntries, &L16In);
InPtr = InGamma -> GammaTable;
cmsCalcL16Params(OutGamma -> nEntries, &L16Out);
OutPtr = OutGamma-> GammaTable;
for (i=0; i < 256; i++)
{
WORD wValIn, wValOut;
wValIn = cmsLinearInterpLUT16(RGB_8_TO_16(i), InPtr, &L16In);
wValOut = cmsReverseLinearInterpLUT16(wValIn, OutPtr, &L16Out);
p -> GammaTable[i] = wValOut;
}
return p;
}
// New method, using smoothed parametric curves. This works FAR better.
// We want to get
//
// y = f(g^-1(x)) ; f = ingamma, g = outgamma
//
// And this can be parametrized as
//
// y = f(t)
// x = g(t)
LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma,
LPGAMMATABLE OutGamma, int nPoints)
{
LPSAMPLEDCURVE x, y, r;
LPGAMMATABLE res;
x = cmsConvertGammaToSampledCurve(InGamma, nPoints);
y = cmsConvertGammaToSampledCurve(OutGamma, nPoints);
r = cmsJoinSampledCurves(y, x, nPoints);
// Does clean "hair"
cmsSmoothSampledCurve(r, 0.001);
cmsClampSampledCurve(r, 0.0, 65535.0);
cmsFreeSampledCurve(x);
cmsFreeSampledCurve(y);
res = cmsConvertSampledCurveToGamma(r, 65535.0);
cmsFreeSampledCurve(r);
return res;
}
// Reverse a gamma table
LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma)
{
register int i;
L16PARAMS L16In;
LPWORD InPtr;
LPGAMMATABLE p;
// Try to reverse it analytically whatever possible
if (InGamma -> Seed.Type > 0 && InGamma -> Seed.Type <= 5 &&
_cmsCrc32OfGammaTable(InGamma) == InGamma -> Seed.Crc32) {
return cmsBuildParametricGamma(nResultSamples, -(InGamma -> Seed.Type), InGamma ->Seed.Params);
}
// Nope, reverse the table
p = cmsAllocGamma(nResultSamples);
if (!p) return NULL;
cmsCalcL16Params(InGamma -> nEntries, &L16In);
InPtr = InGamma -> GammaTable;
for (i=0; i < nResultSamples; i++)
{
WORD wValIn, wValOut;
wValIn = _cmsQuantizeVal(i, nResultSamples);
wValOut = cmsReverseLinearInterpLUT16(wValIn, InPtr, &L16In);
p -> GammaTable[i] = wValOut;
}
return p;
}
// Parametric curves
//
// Parameters goes as: Gamma, a, b, c, d, e, f
// Type is the ICC type +1
// if type is negative, then the curve is analyticaly inverted
LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[])
{
LPGAMMATABLE Table;
double R, Val, dval, e;
int i;
int ParamsByType[] = { 0, 1, 3, 4, 5, 7 };
Table = cmsAllocGamma(nEntries);
if (NULL == Table) return NULL;
Table -> Seed.Type = Type;
CopyMemory(Table ->Seed.Params, Params, ParamsByType[abs(Type)] * sizeof(double));
for (i=0; i < nEntries; i++) {
R = (double) i / (nEntries-1);
switch (Type) {
// X = Y ^ Gamma
case 1:
Val = pow(R, Params[0]);
break;
// Type 1 Reversed: X = Y ^1/gamma
case -1:
Val = pow(R, 1/Params[0]);
break;
// CIE 122-1966
// Y = (aX + b)^Gamma | X >= -b/a
// Y = 0 | else
case 2:
if (R >= -Params[2] / Params[1]) {
e = Params[1]*R + Params[2];
if (e > 0)
Val = pow(e, Params[0]);
else
Val = 0;
}
else
Val = 0;
break;
// Type 2 Reversed
// X = (Y ^1/g - b) / a
case -2:
Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
if (Val < 0)
Val = 0;
break;
// IEC 61966-3
// Y = (aX + b)^Gamma | X <= -b/a
// Y = c | else
case 3:
if (R >= -Params[2] / Params[1]) {
e = Params[1]*R + Params[2];
Val = pow(e, Params[0]) + Params[3];
}
else
Val = Params[3];
break;
// Type 3 reversed
// X=((Y-c)^1/g - b)/a | (Y>=c)
// X=-b/a | (Y<c)
case -3:
if (R >= Params[3]) {
e = R - Params[3];
Val = (pow(e, 1/Params[0]) - Params[2]) / Params[1];
if (Val < 0) Val = 0;
}
else {
Val = -Params[2] / Params[1];
}
break;
// IEC 61966-2.1 (sRGB)
// Y = (aX + b)^Gamma | X >= d
// Y = cX | X < d
case 4:
if (R >= Params[4]) {
e = Params[1]*R + Params[2];
if (e > 0)
Val = pow(e, Params[0]);
else
Val = 0;
}
else
Val = R * Params[3];
break;
// Type 4 reversed
// X=((Y^1/g-b)/a) | Y >= (ad+b)^g
// X=Y/c | Y< (ad+b)^g
case -4:
if (R >= pow(Params[1] * Params[4] + Params[2], Params[0])) {
Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
}
else {
Val = R / Params[3];
}
break;
// Y = (aX + b)^Gamma + e | X <= d
// Y = cX + f | else
case 5:
if (R >= Params[4]) {
e = Params[1]*R + Params[2];
Val = pow(e, Params[0]) + Params[5];
}
else
Val = R*Params[3] + Params[6];
break;
// Reversed type 5
// X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e)
// X=(Y-f)/c | else
case -5:
if (R >= pow(Params[1] * Params[4], Params[0]) + Params[5]) {
Val = pow(R - Params[5], 1/Params[0]) - Params[2] / Params[1];
}
else {
Val = (R - Params[6]) / Params[3];
}
break;
default:
cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported parametric curve type=%d", abs(Type)-1);
cmsFreeGamma(Table);
return NULL;
}
// Saturate
dval = Val * 65535.0 + .5;
if (dval > 65535.) dval = 65535.0;
if (dval < 0) dval = 0;
Table->GammaTable[i] = (WORD) floor(dval);
}
Table -> Seed.Crc32 = _cmsCrc32OfGammaTable(Table);
return Table;
}
// Build a gamma table based on gamma constant
LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma)
{
return cmsBuildParametricGamma(nEntries, 1, &Gamma);
}
// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite
// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press.
//
// Smoothing and interpolation with second differences.
//
// Input: weights (w), data (y): vector from 1 to m.
// Input: smoothing parameter (lambda), length (m).
// Output: smoothed vector (z): vector from 1 to m.
static
void smooth2(vec w, vec y, vec z, float lambda, int m)
{
int i, i1, i2;
vec c, d, e;
d[1] = w[1] + lambda;
c[1] = -2 * lambda / d[1];
e[1] = lambda /d[1];
z[1] = w[1] * y[1];
d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1];
c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2];
e[2] = lambda / d[2];
z[2] = w[2] * y[2] - c[1] * z[1];
for (i = 3; i < m - 1; i++) {
i1 = i - 1; i2 = i - 2;
d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i];
e[i] = lambda / d[i];
z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2];
}
i1 = m - 2; i2 = m - 3;
d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1];
z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2];
i1 = m - 1; i2 = m - 2;
d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m];
z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m];
for (i = m - 2; 1<= i; i--)
z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2];
}
// Smooths a curve sampled at regular intervals
LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda)
{
vec w, y, z;
int i, nItems, Zeros, Poles;
if (cmsIsLinear(Tab->GammaTable, Tab->nEntries)) return FALSE; // Nothing to do
nItems = Tab -> nEntries;
if (nItems > MAX_KNOTS) {
cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothGamma: too many points.");
return FALSE;
}
ZeroMemory(w, nItems * sizeof(float));
ZeroMemory(y, nItems * sizeof(float));
ZeroMemory(z, nItems * sizeof(float));
for (i=0; i < nItems; i++)
{
y[i+1] = (float) Tab -> GammaTable[i];
w[i+1] = 1.0;
}
smooth2(w, y, z, (float) lambda, nItems);
// Do some reality - checking...
Zeros = Poles = 0;
for (i=nItems; i > 1; --i) {
if (z[i] == 0.) Zeros++;
if (z[i] >= 65535.) Poles++;
if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
}
if (Zeros > (nItems / 3)) return FALSE; // Degenerated, mostly zeros
if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles
// Seems ok
for (i=0; i < nItems; i++) {
// Clamp to WORD
float v = z[i+1];
if (v < 0) v = 0;
if (v > 65535.) v = 65535.;
Tab -> GammaTable[i] = (WORD) floor(v + .5);
}
return TRUE;
}
// Check if curve is exponential, return gamma if so.
double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold)
{
double gamma, sum, sum2;
double n, x, y, Std;
int i;
sum = sum2 = n = 0;
// Does exclude endpoints
for (i=1; i < nEntries - 1; i++) {
x = (double) i / (nEntries - 1);
y = (double) GammaTable[i] / 65535.;
// Avoid 7% on lower part to prevent
// artifacts due to linear ramps
if (y > 0. && y < 1. && x > 0.07) {
gamma = log(y) / log(x);
sum += gamma;
sum2 += gamma * gamma;
n++;
}
}
// Take a look on SD to see if gamma isn't exponential at all
Std = sqrt((n * sum2 - sum * sum) / (n*(n-1)));
if (Std > Thereshold)
return -1.0;
return (sum / n); // The mean
}
double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t)
{
return cmsEstimateGammaEx(t->GammaTable, t->nEntries, 0.7);
}
// -----------------------------------------------------------------Sampled curves
// Allocate a empty curve
LPSAMPLEDCURVE cmsAllocSampledCurve(int nItems)
{
LPSAMPLEDCURVE pOut;
pOut = (LPSAMPLEDCURVE) _cmsMalloc(sizeof(SAMPLEDCURVE));
if (pOut == NULL)
return NULL;
if((pOut->Values = (double *) _cmsMalloc(nItems * sizeof(double))) == NULL)
{
_cmsFree(pOut);
return NULL;
}
pOut->nItems = nItems;
ZeroMemory(pOut->Values, nItems * sizeof(double));
return pOut;
}
void cmsFreeSampledCurve(LPSAMPLEDCURVE p)
{
_cmsFree((LPVOID) p -> Values);
_cmsFree((LPVOID) p);
}
// Does duplicate a sampled curve
LPSAMPLEDCURVE cmsDupSampledCurve(LPSAMPLEDCURVE p)
{
LPSAMPLEDCURVE out;
out = cmsAllocSampledCurve(p -> nItems);
if (!out) return NULL;
CopyMemory(out ->Values, p ->Values, p->nItems * sizeof(double));
return out;
}
// Take min, max of curve
void cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max)
{
int i;
*Min = 65536.;
*Max = 0.;
for (i=0; i < p -> nItems; i++) {
double v = p -> Values[i];
if (v < *Min)
*Min = v;
if (v > *Max)
*Max = v;
}
if (*Min < 0) *Min = 0;
if (*Max > 65535.0) *Max = 65535.0;
}
// Clamps to Min, Max
void cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max)
{
int i;
for (i=0; i < p -> nItems; i++) {
double v = p -> Values[i];
if (v < Min)
v = Min;
if (v > Max)
v = Max;
p -> Values[i] = v;
}
}
// Smooths a curve sampled at regular intervals
LCMSBOOL cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double lambda)
{
vec w, y, z;
int i, nItems;
nItems = Tab -> nItems;
if (nItems > MAX_KNOTS) {
cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothSampledCurve: too many points.");
return FALSE;
}
ZeroMemory(w, nItems * sizeof(float));
ZeroMemory(y, nItems * sizeof(float));
ZeroMemory(z, nItems * sizeof(float));
for (i=0; i < nItems; i++)
{
float value = (float) Tab -> Values[i];
y[i+1] = value;
w[i+1] = (float) ((value < 0.0) ? 0 : 1);
}
smooth2(w, y, z, (float) lambda, nItems);
for (i=0; i < nItems; i++) {
Tab -> Values[i] = z[i+1];;
}
return TRUE;
}
// Scale a value v, within domain Min .. Max
// to a domain 0..(nPoints-1)
static
double ScaleVal(double v, double Min, double Max, int nPoints)
{
double a, b;
if (v <= Min) return 0;
if (v >= Max) return (nPoints-1);
a = (double) (nPoints - 1) / (Max - Min);
b = a * Min;
return (a * v) - b;
}
// Does rescale a sampled curve to fit in a 0..(nPoints-1) domain
void cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints)
{
int i;
for (i=0; i < p -> nItems; i++) {
double v = p -> Values[i];
p -> Values[i] = ScaleVal(v, Min, Max, nPoints);
}
}
// Joins two sampled curves for X and Y. Curves should be sorted.
LPSAMPLEDCURVE cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints)
{
int i, j;
LPSAMPLEDCURVE out;
double MinX, MinY, MaxX, MaxY;
double x, y, x1, y1, x2, y2, a, b;
out = cmsAllocSampledCurve(nResultingPoints);
if (out == NULL)
return NULL;
if (X -> nItems != Y -> nItems) {
cmsSignalError(LCMS_ERRC_ABORTED, "cmsJoinSampledCurves: invalid curve.");
cmsFreeSampledCurve(out);
return NULL;
}
// Get endpoints of sampled curves
cmsEndpointsOfSampledCurve(X, &MinX, &MaxX);
cmsEndpointsOfSampledCurve(Y, &MinY, &MaxY);
// Set our points
out ->Values[0] = MinY;
for (i=1; i < nResultingPoints; i++) {
// Scale t to x domain
x = (i * (MaxX - MinX) / (nResultingPoints-1)) + MinX;
// Find interval in which j is within (always up,
// since fn should be monotonic at all)
j = 1;
while ((j < X ->nItems - 1) && X ->Values[j] < x)
j++;
// Now x is within X[j-1], X[j]
x1 = X ->Values[j-1]; x2 = X ->Values[j];
y1 = Y ->Values[j-1]; y2 = Y ->Values[j];
// Interpolate the value
a = (y1 - y2) / (x1 - x2);
b = y1 - a * x1;
y = a* x + b;
out ->Values[i] = y;
}
cmsClampSampledCurve(out, MinY, MaxY);
return out;
}
// Convert between curve types
LPGAMMATABLE cmsConvertSampledCurveToGamma(LPSAMPLEDCURVE Sampled, double Max)
{
LPGAMMATABLE Gamma;
int i, nPoints;
nPoints = Sampled ->nItems;
Gamma = cmsAllocGamma(nPoints);
for (i=0; i < nPoints; i++) {
Gamma->GammaTable[i] = (WORD) floor(ScaleVal(Sampled ->Values[i], 0, Max, 65536) + .5);
}
return Gamma;
}
// Inverse of anterior
LPSAMPLEDCURVE cmsConvertGammaToSampledCurve(LPGAMMATABLE Gamma, int nPoints)
{
LPSAMPLEDCURVE Sampled;
L16PARAMS L16;
int i;
WORD wQuant, wValIn;
if (nPoints > 4096) {
cmsSignalError(LCMS_ERRC_ABORTED, "cmsConvertGammaToSampledCurve: too many points (max=4096)");
return NULL;
}
cmsCalcL16Params(Gamma -> nEntries, &L16);
Sampled = cmsAllocSampledCurve(nPoints);
for (i=0; i < nPoints; i++) {
wQuant = _cmsQuantizeVal(i, nPoints);
wValIn = cmsLinearInterpLUT16(wQuant, Gamma ->GammaTable, &L16);
Sampled ->Values[i] = (float) wValIn;
}
return Sampled;
}
// Smooth endpoints (used in Black/White compensation)
LCMSBOOL _cmsSmoothEndpoints(LPWORD Table, int nEntries)
{
vec w, y, z;
int i, Zeros, Poles;
if (cmsIsLinear(Table, nEntries)) return FALSE; // Nothing to do
if (nEntries > MAX_KNOTS) {
cmsSignalError(LCMS_ERRC_ABORTED, "_cmsSmoothEndpoints: too many points.");
return FALSE;
}
ZeroMemory(w, nEntries * sizeof(float));
ZeroMemory(y, nEntries * sizeof(float));
ZeroMemory(z, nEntries * sizeof(float));
for (i=0; i < nEntries; i++)
{
y[i+1] = (float) Table[i];
w[i+1] = 1.0;
}
w[1] = 65535.0;
w[nEntries] = 65535.0;
smooth2(w, y, z, (float) nEntries, nEntries);
// Do some reality - checking...
Zeros = Poles = 0;
for (i=nEntries; i > 1; --i) {
if (z[i] == 0.) Zeros++;
if (z[i] >= 65535.) Poles++;
if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
}
if (Zeros > (nEntries / 3)) return FALSE; // Degenerated, mostly zeros
if (Poles > (nEntries / 3)) return FALSE; // Degenerated, mostly poles
// Seems ok
for (i=0; i < nEntries; i++) {
// Clamp to WORD
float v = z[i+1];
if (v < 0) v = 0;
if (v > 65535.) v = 65535.;
Table[i] = (WORD) floor(v + .5);
}
return TRUE;
}

1243
modules/lcms/src/cmsgmt.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1105
modules/lcms/src/cmsintrp.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

721
modules/lcms/src/cmsio0.c Normal file
Просмотреть файл

@ -0,0 +1,721 @@
//
// 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.
//
// Generic I/O, tag dictionary management, profile struct
#include "lcms.h"
// Memory-based stream ---------------------------------------------------
typedef struct {
LPBYTE Block; // Points to allocated memory
size_t Size; // Size of allocated memory
int Pointer; // Points to current location
int FreeBlockOnClose; // As title
} FILEMEM;
static
LPVOID MemoryOpen(LPBYTE Block, size_t Size, char Mode)
{
FILEMEM* fm = (FILEMEM*) _cmsMalloc(sizeof(FILEMEM));
if (fm == NULL) return NULL;
ZeroMemory(fm, sizeof(FILEMEM));
if (Mode == 'r') {
fm ->Block = (LPBYTE) _cmsMalloc(Size);
if (fm ->Block == NULL) {
_cmsFree(fm);
return NULL;
}
CopyMemory(fm->Block, Block, Size);
fm ->FreeBlockOnClose = TRUE;
}
else {
fm ->Block = Block;
fm ->FreeBlockOnClose = FALSE;
}
fm ->Size = Size;
fm ->Pointer = 0;
return (LPVOID) fm;
}
static
size_t MemoryRead(LPVOID buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc)
{
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
LPBYTE Ptr;
size_t len = size * count;
if (ResData -> Pointer + len > ResData -> Size){
len = (ResData -> Size - ResData -> Pointer);
cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Got %d bytes, block should be of %d bytes", len * size, count * size);
return 0;
}
Ptr = ResData -> Block;
Ptr += ResData -> Pointer;
CopyMemory(buffer, Ptr, len);
ResData -> Pointer += (int) len;
return count;
}
// SEEK_CUR is assumed
static
LCMSBOOL MemorySeek(struct _lcms_iccprofile_struct* Icc, size_t offset)
{
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
if (offset > ResData ->Size) {
cmsSignalError(LCMS_ERRC_ABORTED, "Pointer error; probably corrupted file");
return TRUE;
}
ResData ->Pointer = (DWORD) offset;
return FALSE;
}
// FTell
static
size_t MemoryTell(struct _lcms_iccprofile_struct* Icc)
{
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
return ResData -> Pointer;
}
// Writes data to memory, also keeps used space for further reference. NO CHECK IS PERFORMED
static
LCMSBOOL MemoryWrite(struct _lcms_iccprofile_struct* Icc, size_t size, void *Ptr)
{
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
if (size == 0) return TRUE;
if (ResData != NULL)
CopyMemory(ResData ->Block + Icc ->UsedSpace, Ptr, size);
Icc->UsedSpace += size;
return TRUE;
}
static
LCMSBOOL MemoryClose(struct _lcms_iccprofile_struct* Icc)
{
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
if (ResData ->FreeBlockOnClose) {
if (ResData ->Block) _cmsFree(ResData ->Block);
}
_cmsFree(ResData);
return 0;
}
// File-based stream -------------------------------------------------------
static
LPVOID FileOpen(const char* filename)
{
return (void*) fopen(filename, "rb");
}
static
size_t FileRead(void *buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc)
{
size_t nReaded = fread(buffer, size, count, (FILE*) Icc->stream);
if (nReaded != count) {
cmsSignalError(LCMS_ERRC_ABORTED, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size);
return 0;
}
return nReaded;
}
static
LCMSBOOL FileSeek(struct _lcms_iccprofile_struct* Icc, size_t offset)
{
if (fseek((FILE*) Icc ->stream, (long) offset, SEEK_SET) != 0) {
cmsSignalError(LCMS_ERRC_ABORTED, "Seek error; probably corrupted file");
return TRUE;
}
return FALSE;
}
static
size_t FileTell(struct _lcms_iccprofile_struct* Icc)
{
return ftell((FILE*) Icc ->stream);
}
// Writes data to stream, also keeps used space for further reference
static
LCMSBOOL FileWrite(struct _lcms_iccprofile_struct* Icc, size_t size, LPVOID Ptr)
{
if (size == 0) return TRUE;
Icc->UsedSpace += size;
if (Icc->stream == NULL) {
return TRUE;
}
return (fwrite(Ptr, size, 1, (FILE*) Icc->stream) == 1);
}
static
LCMSBOOL FileClose(struct _lcms_iccprofile_struct* Icc)
{
return fclose((FILE*) Icc ->stream);
}
// ----------------------------------------------------------------------------------------------------
// Creates an empty structure holding all required parameters
cmsHPROFILE _cmsCreateProfilePlaceholder(void)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) _cmsMalloc(sizeof(LCMSICCPROFILE));
if (Icc == NULL) return NULL;
// Empty values
ZeroMemory(Icc, sizeof(LCMSICCPROFILE));
// Make sure illuminant is correct
Icc ->Illuminant = *cmsD50_XYZ();
// Set it to empty
Icc -> TagCount = 0;
// Return the handle
return (cmsHPROFILE) Icc;
}
// Return the number of tags
icInt32Number LCMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
return Icc->TagCount;
}
// Return the tag signature of a given tag number
icTagSignature LCMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, icInt32Number n)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
if (n < 0 || n > Icc->TagCount) return (icTagSignature) 0; // Mark as not available
return Icc ->TagNames[n];
}
// Search for a specific tag in tag dictionary
// Returns position or -1 if tag not found
icInt32Number _cmsSearchTag(LPLCMSICCPROFILE Profile, icTagSignature sig, LCMSBOOL lSignalError)
{
icInt32Number i;
if (sig == 0) return -1; // 0 identifies a special tag holding raw memory.
for (i=0; i < Profile -> TagCount; i++) {
if (sig == Profile -> TagNames[i])
return i;
}
if (lSignalError)
cmsSignalError(LCMS_ERRC_ABORTED, "Tag '%lx' not found", sig);
return -1;
}
// Check existance
LCMSBOOL LCMSEXPORT cmsIsTag(cmsHPROFILE hProfile, icTagSignature sig)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
return _cmsSearchTag(Icc, sig, FALSE) >= 0;
}
// Search for a particular tag, replace if found or add new one else
LPVOID _cmsInitTag(LPLCMSICCPROFILE Icc, icTagSignature sig, size_t size, const void* Init)
{
LPVOID Ptr;
icInt32Number i;
i = _cmsSearchTag(Icc, sig, FALSE);
if (i >=0) {
if (Icc -> TagPtrs[i]) _cmsFree(Icc -> TagPtrs[i]);
}
else {
i = Icc -> TagCount;
Icc -> TagCount++;
if (Icc ->TagCount >= MAX_TABLE_TAG) {
cmsSignalError(LCMS_ERRC_ABORTED, "Too many tags (%d)", MAX_TABLE_TAG);
Icc ->TagCount = MAX_TABLE_TAG-1;
return NULL;
}
}
Ptr = _cmsMalloc(size);
if (Ptr == NULL) return NULL;
CopyMemory(Ptr, Init, size);
Icc ->TagNames[i] = sig;
Icc ->TagSizes[i] = size;
Icc ->TagPtrs[i] = Ptr;
return Ptr;
}
// Creates a profile from file read placeholder
LPLCMSICCPROFILE _cmsCreateProfileFromFilePlaceholder(const char* FileName)
{
LPLCMSICCPROFILE NewIcc;
LPVOID ICCfile = FileOpen(FileName);
if (ICCfile == NULL) {
cmsSignalError(LCMS_ERRC_ABORTED, "File '%s' not found", FileName);
return NULL;
}
NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder();
if (NewIcc == NULL) return NULL;
strncpy(NewIcc -> PhysicalFile, FileName, MAX_PATH-1);
NewIcc -> PhysicalFile[MAX_PATH-1] = 0;
NewIcc ->stream = ICCfile;
NewIcc ->Read = FileRead;
NewIcc ->Seek = FileSeek;
NewIcc ->Tell = FileTell;
NewIcc ->Close = FileClose;
NewIcc ->Write = NULL;
NewIcc ->IsWrite = FALSE;
return NewIcc;
}
// Creates a profile from memory read placeholder
LPLCMSICCPROFILE _cmsCreateProfileFromMemPlaceholder(LPVOID MemPtr, DWORD dwSize)
{
LPLCMSICCPROFILE NewIcc;
LPVOID ICCfile = MemoryOpen((LPBYTE) MemPtr, (size_t) dwSize, 'r');
if (ICCfile == NULL) {
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't allocate %ld bytes for profile", dwSize);
return NULL;
}
NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder();
if (NewIcc == NULL) return NULL;
NewIcc -> PhysicalFile[0] = 0;
NewIcc ->stream = ICCfile;
NewIcc ->Read = MemoryRead;
NewIcc ->Seek = MemorySeek;
NewIcc ->Tell = MemoryTell;
NewIcc ->Close = MemoryClose;
NewIcc ->Write = NULL;
NewIcc ->IsWrite = FALSE;
return NewIcc;
}
// Turn a placeholder into file writter
void _cmsSetSaveToDisk(LPLCMSICCPROFILE Icc, const char* FileName)
{
if (FileName == NULL) {
Icc ->stream = NULL;
}
else {
Icc ->stream = fopen(FileName, "wb");
if (Icc ->stream == NULL)
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to file '%s'", FileName);
}
Icc ->Write = FileWrite; // Save to disk
Icc ->Close = FileClose;
}
// Turn a placeholder into memory writter
void _cmsSetSaveToMemory(LPLCMSICCPROFILE Icc, LPVOID MemPtr, size_t dwSize)
{
if (MemPtr == NULL) {
Icc ->stream = NULL;
}
else {
Icc ->stream = (FILEMEM*) MemoryOpen((LPBYTE) MemPtr, dwSize, 'w');
if (Icc ->stream == NULL)
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to memory");
}
Icc ->Write = MemoryWrite;
Icc ->Close = MemoryClose;
}
// ----------------------------------------------------------------------- Set/Get several struct members
LCMSBOOL LCMSEXPORT cmsTakeMediaWhitePoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
*Dest = Icc -> MediaWhitePoint;
return TRUE;
}
LCMSBOOL LCMSEXPORT cmsTakeMediaBlackPoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
*Dest = Icc -> MediaBlackPoint;
return TRUE;
}
LCMSBOOL LCMSEXPORT cmsTakeIluminant(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
*Dest = Icc -> Illuminant;
return TRUE;
}
int LCMSEXPORT cmsTakeRenderingIntent(cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
return (int) Icc -> RenderingIntent;
}
void LCMSEXPORT cmsSetRenderingIntent(cmsHPROFILE hProfile, int RenderingIntent)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
Icc -> RenderingIntent = (icRenderingIntent) RenderingIntent;
}
DWORD LCMSEXPORT cmsTakeHeaderFlags(cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
return (DWORD) Icc -> flags;
}
void LCMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, DWORD Flags)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
Icc -> flags = (icUInt32Number) Flags;
}
DWORD LCMSEXPORT cmsTakeHeaderAttributes(cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
return (DWORD) Icc -> attributes;
}
void LCMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, DWORD Flags)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
Icc -> attributes = (icUInt32Number) Flags;
}
const BYTE* LCMSEXPORT cmsTakeProfileID(cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
return Icc ->ProfileID;
}
void LCMSEXPORT cmsSetProfileID(cmsHPROFILE hProfile, LPBYTE ProfileID)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
CopyMemory(Icc -> ProfileID, ProfileID, 16);
}
LCMSBOOL LCMSEXPORT cmsTakeCreationDateTime(struct tm *Dest, cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
CopyMemory(Dest, &Icc ->Created, sizeof(struct tm));
return TRUE;
}
icColorSpaceSignature LCMSEXPORT cmsGetPCS(cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
return Icc -> PCS;
}
void LCMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, icColorSpaceSignature pcs)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
Icc -> PCS = pcs;
}
icColorSpaceSignature LCMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
return Icc -> ColorSpace;
}
void LCMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, icColorSpaceSignature sig)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
Icc -> ColorSpace = sig;
}
icProfileClassSignature LCMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
return Icc -> DeviceClass;
}
DWORD LCMSEXPORT cmsGetProfileICCversion(cmsHPROFILE hProfile)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
return (DWORD) Icc -> Version;
}
void LCMSEXPORT cmsSetProfileICCversion(cmsHPROFILE hProfile, DWORD Version)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
Icc -> Version = Version;
}
void LCMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, icProfileClassSignature sig)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
Icc -> DeviceClass = sig;
}
// --------------------------------------------------------------------------------------------------------------
static
int SizeOfGammaTab(LPGAMMATABLE In)
{
return sizeof(GAMMATABLE) + (In -> nEntries - 1)*sizeof(WORD);
}
// Creates a phantom tag holding a memory block
static
LPVOID DupBlock(LPLCMSICCPROFILE Icc, LPVOID Block, size_t size)
{
if (Block != NULL && size > 0)
return _cmsInitTag(Icc, (icTagSignature) 0, size, Block);
else
return NULL;
}
// This is tricky, since LUT structs does have pointers
LCMSBOOL LCMSEXPORT _cmsAddLUTTag(cmsHPROFILE hProfile, icTagSignature sig, const void* lut)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
LPLUT Orig, Stored;
unsigned int i;
// The struct itself
Orig = (LPLUT) lut;
Stored = (LPLUT) _cmsInitTag(Icc, (icTagSignature) sig, sizeof(LUT), lut);
// dup' the memory blocks
for (i=0; i < Orig ->InputChan; i++)
Stored -> L1[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L1[i],
sizeof(WORD) * Orig ->In16params.nSamples);
for (i=0; i < Orig ->OutputChan; i++)
Stored -> L2[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L2[i],
sizeof(WORD) * Orig ->Out16params.nSamples);
Stored -> T = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->T, Orig -> Tsize);
// Zero any additional pointer
Stored ->CLut16params.p8 = NULL;
return TRUE;
}
LCMSBOOL LCMSEXPORT _cmsAddXYZTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* XYZ)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
_cmsInitTag(Icc, sig, sizeof(cmsCIEXYZ), XYZ);
return TRUE;
}
LCMSBOOL LCMSEXPORT _cmsAddTextTag(cmsHPROFILE hProfile, icTagSignature sig, const char* Text)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
_cmsInitTag(Icc, sig, strlen(Text)+1, (LPVOID) Text);
return TRUE;
}
LCMSBOOL LCMSEXPORT _cmsAddGammaTag(cmsHPROFILE hProfile, icTagSignature sig, LPGAMMATABLE TransferFunction)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
_cmsInitTag(Icc, sig, SizeOfGammaTab(TransferFunction), TransferFunction);
return TRUE;
}
LCMSBOOL LCMSEXPORT _cmsAddChromaticityTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIExyYTRIPLE Chrm)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
_cmsInitTag(Icc, sig, sizeof(cmsCIExyYTRIPLE), Chrm);
return TRUE;
}
LCMSBOOL LCMSEXPORT _cmsAddSequenceDescriptionTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsSEQ pseq)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
_cmsInitTag(Icc, sig, sizeof(int) + pseq -> n * sizeof(cmsPSEQDESC), pseq);
return TRUE;
}
LCMSBOOL LCMSEXPORT _cmsAddNamedColorTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
_cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc);
return TRUE;
}
LCMSBOOL LCMSEXPORT _cmsAddDateTimeTag(cmsHPROFILE hProfile, icTagSignature sig, struct tm *DateTime)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
_cmsInitTag(Icc, sig, sizeof(struct tm), DateTime);
return TRUE;
}
LCMSBOOL LCMSEXPORT _cmsAddColorantTableTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
_cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc);
return TRUE;
}
LCMSBOOL LCMSEXPORT _cmsAddChromaticAdaptationTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* mat)
{
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
_cmsInitTag(Icc, sig, 3*sizeof(cmsCIEXYZ), mat);
return TRUE;
}

3642
modules/lcms/src/cmsio1.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

809
modules/lcms/src/cmslut.c Normal file
Просмотреть файл

@ -0,0 +1,809 @@
//
// 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.
#include "lcms.h"
// Pipeline of LUT. Enclosed by {} are new revision 4.0 of ICC spec.
//
// [Mat] -> [L1] -> { [Mat3] -> [Ofs3] -> [L3] ->} [CLUT] { -> [L4] -> [Mat4] -> [Ofs4] } -> [L2]
//
// Some of these stages would be missing. This implements the totality of
// combinations of old and new LUT types as follows:
//
// Lut8 & Lut16
// ============
// [Mat] -> [L1] -> [CLUT] -> [L2]
//
// Mat2, Ofs2, L3, L3, Mat3, Ofs3 are missing
//
// LutAToB
// ========
//
// [L1] -> [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2]
//
// Mat, Mat3, Ofs3, L3 are missing
// L1 = A curves
// L4 = M curves
// L2 = B curves
//
// LutBToA
// =======
//
// [L1] -> [Mat3] -> [Ofs3] -> [L3] -> [CLUT] -> [L2]
//
// Mat, L4, Mat4, Ofs4 are missing
// L1 = B Curves
// L3 = M Curves
// L2 = A curves
//
//
// V2&3 emulation
// ===============
//
// For output, Mat is multiplied by
//
//
// | 0xff00 / 0xffff 0 0 |
// | 0 0xff00 / 0xffff 0 |
// | 0 0 0xff00 / 0xffff |
//
//
// For input, an additional matrix is needed at the very last end of the chain
//
//
// | 0xffff / 0xff00 0 0 |
// | 0 0xffff / 0xff00 0 |
// | 0 0 0xffff / 0xff00 |
//
//
// Which reduces to (val * 257) >> 8
// A couple of macros to convert between revisions
#define FROM_V2_TO_V4(x) (((((x)<<8)+(x))+0x80)>>8) // BY 65535 DIV 65280 ROUND
#define FROM_V4_TO_V2(x) ((((x)<<8)+0x80)/257) // BY 65280 DIV 65535 ROUND
// Lut Creation & Destruction
LPLUT LCMSEXPORT cmsAllocLUT(void)
{
LPLUT NewLUT;
NewLUT = (LPLUT) _cmsMalloc(sizeof(LUT));
if (NewLUT)
ZeroMemory(NewLUT, sizeof(LUT));
return NewLUT;
}
void LCMSEXPORT cmsFreeLUT(LPLUT Lut)
{
unsigned int i;
if (!Lut) return;
if (Lut -> T) free(Lut -> T);
for (i=0; i < Lut -> OutputChan; i++)
{
if (Lut -> L2[i]) free(Lut -> L2[i]);
}
for (i=0; i < Lut -> InputChan; i++)
{
if (Lut -> L1[i]) free(Lut -> L1[i]);
}
if (Lut ->wFlags & LUT_HASTL3) {
for (i=0; i < Lut -> InputChan; i++) {
if (Lut -> L3[i]) free(Lut -> L3[i]);
}
}
if (Lut ->wFlags & LUT_HASTL4) {
for (i=0; i < Lut -> OutputChan; i++) {
if (Lut -> L4[i]) free(Lut -> L4[i]);
}
}
if (Lut ->CLut16params.p8)
free(Lut ->CLut16params.p8);
free(Lut);
}
static
LPVOID DupBlockTab(LPVOID Org, size_t size)
{
LPVOID mem = _cmsMalloc(size);
if (mem != NULL)
CopyMemory(mem, Org, size);
return mem;
}
LPLUT LCMSEXPORT cmsDupLUT(LPLUT Orig)
{
LPLUT NewLUT = cmsAllocLUT();
unsigned int i;
CopyMemory(NewLUT, Orig, sizeof(LUT));
for (i=0; i < Orig ->InputChan; i++)
NewLUT -> L1[i] = (LPWORD) DupBlockTab((LPVOID) Orig ->L1[i],
sizeof(WORD) * Orig ->In16params.nSamples);
for (i=0; i < Orig ->OutputChan; i++)
NewLUT -> L2[i] = (LPWORD) DupBlockTab((LPVOID) Orig ->L2[i],
sizeof(WORD) * Orig ->Out16params.nSamples);
NewLUT -> T = (LPWORD) DupBlockTab((LPVOID) Orig ->T, Orig -> Tsize);
return NewLUT;
}
static
unsigned int UIpow(unsigned int a, unsigned int b)
{
unsigned int rv = 1;
for (; b > 0; b--)
rv *= a;
return rv;
}
LPLUT LCMSEXPORT cmsAlloc3DGrid(LPLUT NewLUT, int clutPoints, int inputChan, int outputChan)
{
DWORD nTabSize;
NewLUT -> wFlags |= LUT_HAS3DGRID;
NewLUT -> cLutPoints = clutPoints;
NewLUT -> InputChan = inputChan;
NewLUT -> OutputChan = outputChan;
nTabSize = (NewLUT -> OutputChan * UIpow(NewLUT->cLutPoints,
NewLUT->InputChan)
* sizeof(WORD));
NewLUT -> T = (LPWORD) _cmsMalloc(nTabSize);
if (NewLUT -> T == NULL) return NULL;
ZeroMemory(NewLUT -> T, nTabSize);
NewLUT ->Tsize = nTabSize;
cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan,
NewLUT -> OutputChan,
&NewLUT -> CLut16params);
return NewLUT;
}
LPLUT LCMSEXPORT cmsAllocLinearTable(LPLUT NewLUT, LPGAMMATABLE Tables[], int nTable)
{
unsigned int i;
LPWORD PtrW;
switch (nTable) {
case 1: NewLUT -> wFlags |= LUT_HASTL1;
cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> In16params);
NewLUT -> InputEntries = Tables[0] -> nEntries;
for (i=0; i < NewLUT -> InputChan; i++) {
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> InputEntries);
if (PtrW == NULL) return NULL;
NewLUT -> L1[i] = PtrW;
CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> InputEntries);
CopyMemory(&NewLUT -> LCurvesSeed[0][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
}
break;
case 2: NewLUT -> wFlags |= LUT_HASTL2;
cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> Out16params);
NewLUT -> OutputEntries = Tables[0] -> nEntries;
for (i=0; i < NewLUT -> OutputChan; i++) {
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> OutputEntries);
if (PtrW == NULL) return NULL;
NewLUT -> L2[i] = PtrW;
CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> OutputEntries);
CopyMemory(&NewLUT -> LCurvesSeed[1][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
}
break;
// 3 & 4 according ICC 4.0 spec
case 3:
NewLUT -> wFlags |= LUT_HASTL3;
cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> L3params);
NewLUT -> L3Entries = Tables[0] -> nEntries;
for (i=0; i < NewLUT -> InputChan; i++) {
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> L3Entries);
if (PtrW == NULL) return NULL;
NewLUT -> L3[i] = PtrW;
CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> L3Entries);
CopyMemory(&NewLUT -> LCurvesSeed[2][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
}
break;
case 4:
NewLUT -> wFlags |= LUT_HASTL4;
cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> L4params);
NewLUT -> L4Entries = Tables[0] -> nEntries;
for (i=0; i < NewLUT -> OutputChan; i++) {
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> L4Entries);
if (PtrW == NULL) return NULL;
NewLUT -> L4[i] = PtrW;
CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> L4Entries);
CopyMemory(&NewLUT -> LCurvesSeed[3][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
}
break;
default:;
}
return NewLUT;
}
// Set the LUT matrix
LPLUT LCMSEXPORT cmsSetMatrixLUT(LPLUT Lut, LPMAT3 M)
{
MAT3toFix(&Lut ->Matrix, M);
if (!MAT3isIdentity(&Lut->Matrix, 0.0001))
Lut ->wFlags |= LUT_HASMATRIX;
return Lut;
}
// Set matrix & offset, v4 compatible
LPLUT LCMSEXPORT cmsSetMatrixLUT4(LPLUT Lut, LPMAT3 M, LPVEC3 off, DWORD dwFlags)
{
WMAT3 WMat;
WVEC3 Woff;
VEC3 Zero = {{0, 0, 0}};
MAT3toFix(&WMat, M);
if (off == NULL)
off = &Zero;
VEC3toFix(&Woff, off);
// Nop if identity
if (MAT3isIdentity(&WMat, 0.0001) &&
(Woff.n[VX] == 0 && Woff.n[VY] == 0 && Woff.n[VZ] == 0))
return Lut;
switch (dwFlags) {
case LUT_HASMATRIX:
Lut ->Matrix = WMat;
Lut ->wFlags |= LUT_HASMATRIX;
break;
case LUT_HASMATRIX3:
Lut ->Mat3 = WMat;
Lut ->Ofs3 = Woff;
Lut ->wFlags |= LUT_HASMATRIX3;
break;
case LUT_HASMATRIX4:
Lut ->Mat4 = WMat;
Lut ->Ofs4 = Woff;
Lut ->wFlags |= LUT_HASMATRIX4;
break;
default:;
}
return Lut;
}
// The full evaluator
void LCMSEXPORT cmsEvalLUT(LPLUT Lut, WORD In[], WORD Out[])
{
register unsigned int i;
WORD StageABC[MAXCHANNELS], StageLMN[MAXCHANNELS];
// Try to speedup things on plain devicelinks
if (Lut ->wFlags == LUT_HAS3DGRID) {
Lut ->CLut16params.Interp3D(In, Out, Lut -> T, &Lut -> CLut16params);
return;
}
// Nope, evaluate whole LUT
for (i=0; i < Lut -> InputChan; i++)
StageABC[i] = In[i];
if (Lut ->wFlags & LUT_V4_OUTPUT_EMULATE_V2) {
// Clamp Lab to avoid overflow
if (StageABC[0] > 0xFF00)
StageABC[0] = 0xFF00;
StageABC[0] = (WORD) FROM_V2_TO_V4(StageABC[0]);
StageABC[1] = (WORD) FROM_V2_TO_V4(StageABC[1]);
StageABC[2] = (WORD) FROM_V2_TO_V4(StageABC[2]);
}
if (Lut ->wFlags & LUT_V2_OUTPUT_EMULATE_V4) {
StageABC[0] = (WORD) FROM_V4_TO_V2(StageABC[0]);
StageABC[1] = (WORD) FROM_V4_TO_V2(StageABC[1]);
StageABC[2] = (WORD) FROM_V4_TO_V2(StageABC[2]);
}
// Matrix handling.
if (Lut -> wFlags & LUT_HASMATRIX) {
WVEC3 InVect, OutVect;
// In LUT8 here comes the special gray axis fixup
if (Lut ->FixGrayAxes) {
StageABC[1] = _cmsClampWord(StageABC[1] - 128);
StageABC[2] = _cmsClampWord(StageABC[2] - 128);
}
// Matrix
InVect.n[VX] = ToFixedDomain(StageABC[0]);
InVect.n[VY] = ToFixedDomain(StageABC[1]);
InVect.n[VZ] = ToFixedDomain(StageABC[2]);
MAT3evalW(&OutVect, &Lut -> Matrix, &InVect);
// PCS in 1Fixed15 format, adjusting
StageABC[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
StageABC[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
StageABC[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
}
// First linearization
if (Lut -> wFlags & LUT_HASTL1)
{
for (i=0; i < Lut -> InputChan; i++)
StageABC[i] = cmsLinearInterpLUT16(StageABC[i],
Lut -> L1[i],
&Lut -> In16params);
}
// Mat3, Ofs3, L3 processing
if (Lut ->wFlags & LUT_HASMATRIX3) {
WVEC3 InVect, OutVect;
InVect.n[VX] = ToFixedDomain(StageABC[0]);
InVect.n[VY] = ToFixedDomain(StageABC[1]);
InVect.n[VZ] = ToFixedDomain(StageABC[2]);
MAT3evalW(&OutVect, &Lut -> Mat3, &InVect);
OutVect.n[VX] += Lut ->Ofs3.n[VX];
OutVect.n[VY] += Lut ->Ofs3.n[VY];
OutVect.n[VZ] += Lut ->Ofs3.n[VZ];
StageABC[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
StageABC[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
StageABC[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
}
if (Lut ->wFlags & LUT_HASTL3) {
for (i=0; i < Lut -> InputChan; i++)
StageABC[i] = cmsLinearInterpLUT16(StageABC[i],
Lut -> L3[i],
&Lut -> L3params);
}
if (Lut -> wFlags & LUT_HAS3DGRID) {
Lut ->CLut16params.Interp3D(StageABC, StageLMN, Lut -> T, &Lut -> CLut16params);
}
else
{
for (i=0; i < Lut -> InputChan; i++)
StageLMN[i] = StageABC[i];
}
// Mat4, Ofs4, L4 processing
if (Lut ->wFlags & LUT_HASTL4) {
for (i=0; i < Lut -> OutputChan; i++)
StageLMN[i] = cmsLinearInterpLUT16(StageLMN[i],
Lut -> L4[i],
&Lut -> L4params);
}
if (Lut ->wFlags & LUT_HASMATRIX4) {
WVEC3 InVect, OutVect;
InVect.n[VX] = ToFixedDomain(StageLMN[0]);
InVect.n[VY] = ToFixedDomain(StageLMN[1]);
InVect.n[VZ] = ToFixedDomain(StageLMN[2]);
MAT3evalW(&OutVect, &Lut -> Mat4, &InVect);
OutVect.n[VX] += Lut ->Ofs4.n[VX];
OutVect.n[VY] += Lut ->Ofs4.n[VY];
OutVect.n[VZ] += Lut ->Ofs4.n[VZ];
StageLMN[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
StageLMN[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
StageLMN[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
}
// Last linearitzation
if (Lut -> wFlags & LUT_HASTL2)
{
for (i=0; i < Lut -> OutputChan; i++)
Out[i] = cmsLinearInterpLUT16(StageLMN[i],
Lut -> L2[i],
&Lut -> Out16params);
}
else
{
for (i=0; i < Lut -> OutputChan; i++)
Out[i] = StageLMN[i];
}
if (Lut ->wFlags & LUT_V4_INPUT_EMULATE_V2) {
Out[0] = (WORD) FROM_V4_TO_V2(Out[0]);
Out[1] = (WORD) FROM_V4_TO_V2(Out[1]);
Out[2] = (WORD) FROM_V4_TO_V2(Out[2]);
}
if (Lut ->wFlags & LUT_V2_INPUT_EMULATE_V4) {
Out[0] = (WORD) FROM_V2_TO_V4(Out[0]);
Out[1] = (WORD) FROM_V2_TO_V4(Out[1]);
Out[2] = (WORD) FROM_V2_TO_V4(Out[2]);
}
}
// Precomputes tables for 8-bit on input devicelink.
//
LPLUT _cmsBlessLUT8(LPLUT Lut)
{
int i, j;
WORD StageABC[3];
Fixed32 v1, v2, v3;
LPL8PARAMS p8;
LPL16PARAMS p = &Lut ->CLut16params;
p8 = (LPL8PARAMS) _cmsMalloc(sizeof(L8PARAMS));
if (p8 == NULL) return NULL;
// values comes * 257, so we can safely take first byte (x << 8 + x)
// if there are prelinearization, is already smelted in tables
for (i=0; i < 256; i++) {
StageABC[0] = StageABC[1] = StageABC[2] = RGB_8_TO_16(i);
if (Lut ->wFlags & LUT_HASTL1) {
for (j=0; j < 3; j++)
StageABC[j] = cmsLinearInterpLUT16(StageABC[j],
Lut -> L1[j],
&Lut -> In16params);
Lut ->wFlags &= ~LUT_HASTL1;
}
v1 = ToFixedDomain(StageABC[0] * p -> Domain);
v2 = ToFixedDomain(StageABC[1] * p -> Domain);
v3 = ToFixedDomain(StageABC[2] * p -> Domain);
p8 ->X0[i] = p->opta3 * FIXED_TO_INT(v1);
p8 ->Y0[i] = p->opta2 * FIXED_TO_INT(v2);
p8 ->Z0[i] = p->opta1 * FIXED_TO_INT(v3);
p8 ->rx[i] = (WORD) FIXED_REST_TO_INT(v1);
p8 ->ry[i] = (WORD) FIXED_REST_TO_INT(v2);
p8 ->rz[i] = (WORD) FIXED_REST_TO_INT(v3);
}
Lut -> CLut16params.p8 = p8;
Lut -> CLut16params.Interp3D = cmsTetrahedralInterp8;
return Lut;
}
// ----------------------------------------------------------- Reverse interpolation
// Here's how it goes. The derivative Df(x) of the function f is the linear
// transformation that best approximates f near the point x. It can be represented
// by a matrix A whose entries are the partial derivatives of the components of f
// with respect to all the coordinates. This is know as the Jacobian
//
// The best linear approximation to f is given by the matrix equation:
//
// y-y0 = A (x-x0)
//
// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this
// linear approximation will give a "better guess" for the zero of f. Thus let y=0,
// and since y0=f(x0) one can solve the above equation for x. This leads to the
// Newton's method formula:
//
// xn+1 = xn - A-1 f(xn)
//
// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the
// fashion described above. Iterating this will give better and better approximations
// if you have a "good enough" initial guess.
#define JACOBIAN_EPSILON 0.001
#define INVERSION_MAX_ITERATIONS 30
// Increment with reflexion on boundary
static
void IncDelta(double *Val)
{
if (*Val < (1.0 - JACOBIAN_EPSILON))
*Val += JACOBIAN_EPSILON;
else
*Val -= JACOBIAN_EPSILON;
}
static
void ToEncoded(WORD Encoded[3], LPVEC3 Float)
{
Encoded[0] = (WORD) floor(Float->n[0] * 65535.0 + 0.5);
Encoded[1] = (WORD) floor(Float->n[1] * 65535.0 + 0.5);
Encoded[2] = (WORD) floor(Float->n[2] * 65535.0 + 0.5);
}
static
void FromEncoded(LPVEC3 Float, WORD Encoded[3])
{
Float->n[0] = Encoded[0] / 65535.0;
Float->n[1] = Encoded[1] / 65535.0;
Float->n[2] = Encoded[2] / 65535.0;
}
// Evaluates the CLUT part of a LUT (4 -> 3 only)
static
void EvalLUTdoubleKLab(LPLUT Lut, const VEC3* In, WORD FixedK, LPcmsCIELab Out)
{
WORD wIn[4], wOut[3];
wIn[0] = (WORD) floor(In ->n[0] * 65535.0 + 0.5);
wIn[1] = (WORD) floor(In ->n[1] * 65535.0 + 0.5);
wIn[2] = (WORD) floor(In ->n[2] * 65535.0 + 0.5);
wIn[3] = FixedK;
cmsEvalLUT(Lut, wIn, wOut);
cmsLabEncoded2Float(Out, wOut);
}
// Builds a Jacobian CMY->Lab
static
void ComputeJacobianLab(LPLUT Lut, LPMAT3 Jacobian, const VEC3* Colorant, WORD K)
{
VEC3 ColorantD;
cmsCIELab Lab, LabD;
int j;
EvalLUTdoubleKLab(Lut, Colorant, K, &Lab);
for (j = 0; j < 3; j++) {
ColorantD.n[0] = Colorant ->n[0];
ColorantD.n[1] = Colorant ->n[1];
ColorantD.n[2] = Colorant ->n[2];
IncDelta(&ColorantD.n[j]);
EvalLUTdoubleKLab(Lut, &ColorantD, K, &LabD);
Jacobian->v[0].n[j] = ((LabD.L - Lab.L) / JACOBIAN_EPSILON);
Jacobian->v[1].n[j] = ((LabD.a - Lab.a) / JACOBIAN_EPSILON);
Jacobian->v[2].n[j] = ((LabD.b - Lab.b) / JACOBIAN_EPSILON);
}
}
// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT, but It
// can be used on CMYK -> Lab LUT to obtain black preservation.
// Target holds LabK in this case
// x1 <- x - [J(x)]^-1 * f(x)
LCMSAPI double LCMSEXPORT cmsEvalLUTreverse(LPLUT Lut, WORD Target[], WORD Result[], LPWORD Hint)
{
int i;
double error, LastError = 1E20;
cmsCIELab fx, Goal;
VEC3 tmp, tmp2, x;
MAT3 Jacobian;
WORD FixedK;
WORD LastResult[4];
// This is our Lab goal
cmsLabEncoded2Float(&Goal, Target);
// Special case for CMYK->Lab
if (Lut ->InputChan == 4)
FixedK = Target[3];
else
FixedK = 0;
// Take the hint as starting point if specified
if (Hint == NULL) {
// Begin at any point, we choose 1/3 of neutral CMY gray
x.n[0] = x.n[1] = x.n[2] = 0.3;
}
else {
FromEncoded(&x, Hint);
}
// Iterate
for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) {
// Get beginning fx
EvalLUTdoubleKLab(Lut, &x, FixedK, &fx);
// Compute error
error = cmsDeltaE(&fx, &Goal);
// If not convergent, return last safe value
if (error >= LastError)
break;
// Keep latest values
LastError = error;
ToEncoded(LastResult, &x);
LastResult[3] = FixedK;
// Obtain slope
ComputeJacobianLab(Lut, &Jacobian, &x, FixedK);
// Solve system
tmp2.n[0] = fx.L - Goal.L;
tmp2.n[1] = fx.a - Goal.a;
tmp2.n[2] = fx.b - Goal.b;
if (!MAT3solve(&tmp, &Jacobian, &tmp2))
break;
// Move our guess
x.n[0] -= tmp.n[0];
x.n[1] -= tmp.n[1];
x.n[2] -= tmp.n[2];
// Some clipping....
VEC3saturate(&x);
}
Result[0] = LastResult[0];
Result[1] = LastResult[1];
Result[2] = LastResult[2];
Result[3] = LastResult[3];
return LastError;
}

377
modules/lcms/src/cmsmatsh.c Normal file
Просмотреть файл

@ -0,0 +1,377 @@
//
// 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.
#include "lcms.h"
// Shaper/Matrix handling
// This routines handles the matrix-shaper method. A note about domain
// is here required. If the shaper-matrix is invoked on INPUT profiles,
// after the shaper process, we have a value between 0 and 0xFFFF. Thus,
// for proper matrix handling, we must convert it to 15fix16, so
// ToFixedDomain might be called. But cmsLinearInterpFixed() returns
// data yet in fixed point, so no additional process is required.
// Then, we obtain data on 15.16, so we need to shift >> by 1 to
// obtain 1.15 PCS format.
// On OUTPUT profiles, things are inverse, we must first expand 1 bit
// by shifting left, and then convert result between 0 and 1.000 to
// RGB, so FromFixedDomain() must be called before pass values to
// shaper. Trickly, there is a situation where this shifts works
// little different. Sometimes, lcms smelts input/output
// matrices into a single, one shaper, process. In such cases, since
// input is encoded from 0 to 0xffff, we must first use the shaper and
// then the matrix, an additional FromFixedDomain() must be used to
// accomodate output values.
// For a sake of simplicity, I will handle this three behaviours
// with different routines, so the flags MATSHAPER_INPUT and MATSHAPER_OUTPUT
// can be conbined to signal smelted matrix-shapers
static
int ComputeTables(LPGAMMATABLE Table[3], LPWORD Out[3], LPL16PARAMS p16)
{
int i, AllLinear;
cmsCalcL16Params(Table[0] -> nEntries, p16);
AllLinear = 0;
for (i=0; i < 3; i++)
{
LPWORD PtrW;
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * p16 -> nSamples);
if (PtrW == NULL) return -1; // Signal error
CopyMemory(PtrW, Table[i] -> GammaTable, sizeof(WORD) * Table[i] -> nEntries);
Out[i] = PtrW; // Set table pointer
// Linear after all?
AllLinear += cmsIsLinear(PtrW, p16 -> nSamples);
}
// If is all linear, then supress table interpolation (this
// will speed greately some trivial operations.
// Return 1 if present, 0 if all linear
if (AllLinear != 3) return 1;
return 0;
}
LPMATSHAPER cmsAllocMatShaper2(LPMAT3 Matrix, LPGAMMATABLE In[], LPGAMMATABLE Out[], DWORD Behaviour)
{
LPMATSHAPER NewMatShaper;
int rc;
NewMatShaper = (LPMATSHAPER) _cmsMalloc(sizeof(MATSHAPER));
if (NewMatShaper)
ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED);
// Fill matrix part
MAT3toFix(&NewMatShaper -> Matrix, Matrix);
// Reality check
if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001))
NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX;
// Now, on the table characteristics
if (Out) {
rc = ComputeTables(Out, NewMatShaper ->L, &NewMatShaper ->p16);
if (rc < 0) {
cmsFreeMatShaper(NewMatShaper);
return NULL;
}
if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER;
}
if (In) {
rc = ComputeTables(In, NewMatShaper ->L2, &NewMatShaper ->p2_16);
if (rc < 0) {
cmsFreeMatShaper(NewMatShaper);
return NULL;
}
if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASINPSHAPER;
}
return NewMatShaper;
}
// Creation & Destruction
LPMATSHAPER cmsAllocMatShaper(LPMAT3 Matrix, LPGAMMATABLE Tables[], DWORD Behaviour)
{
LPMATSHAPER NewMatShaper;
int i, AllLinear;
NewMatShaper = (LPMATSHAPER) _cmsMalloc(sizeof(MATSHAPER));
if (NewMatShaper)
ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED);
// Fill matrix part
MAT3toFix(&NewMatShaper -> Matrix, Matrix);
// Reality check
if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001))
NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX;
// Now, on the table characteristics
cmsCalcL16Params(Tables[0] -> nEntries, &NewMatShaper -> p16);
// Copy tables
AllLinear = 0;
for (i=0; i < 3; i++)
{
LPWORD PtrW;
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewMatShaper -> p16.nSamples);
if (PtrW == NULL) {
cmsFreeMatShaper(NewMatShaper);
return NULL;
}
CopyMemory(PtrW, Tables[i] -> GammaTable,
sizeof(WORD) * Tables[i] -> nEntries);
NewMatShaper -> L[i] = PtrW; // Set table pointer
// Linear after all?
AllLinear += cmsIsLinear(PtrW, NewMatShaper -> p16.nSamples);
}
// If is all linear, then supress table interpolation (this
// will speed greately some trivial operations
if (AllLinear != 3)
NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER;
return NewMatShaper;
}
// Free associated memory
void cmsFreeMatShaper(LPMATSHAPER MatShaper)
{
int i;
if (!MatShaper) return;
for (i=0; i < 3; i++)
{
if (MatShaper -> L[i]) _cmsFree(MatShaper ->L[i]);
if (MatShaper -> L2[i]) _cmsFree(MatShaper ->L2[i]);
}
_cmsFree(MatShaper);
}
// All smelted must postpose gamma to last stage.
static
void AllSmeltedBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
{
WORD tmp[3];
WVEC3 InVect, OutVect;
if (MatShaper -> dwFlags & MATSHAPER_HASINPSHAPER)
{
InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L2[0], &MatShaper -> p2_16);
InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L2[1], &MatShaper -> p2_16);
InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L2[2], &MatShaper -> p2_16);
}
else
{
InVect.n[VX] = ToFixedDomain(In[0]);
InVect.n[VY] = ToFixedDomain(In[1]);
InVect.n[VZ] = ToFixedDomain(In[2]);
}
if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
{
MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
}
else {
OutVect.n[VX] = InVect.n[VX];
OutVect.n[VY] = InVect.n[VY];
OutVect.n[VZ] = InVect.n[VZ];
}
tmp[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
tmp[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
tmp[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
{
Out[0] = cmsLinearInterpLUT16(tmp[0], MatShaper -> L[0], &MatShaper -> p16);
Out[1] = cmsLinearInterpLUT16(tmp[1], MatShaper -> L[1], &MatShaper -> p16);
Out[2] = cmsLinearInterpLUT16(tmp[2], MatShaper -> L[2], &MatShaper -> p16);
}
else
{
Out[0] = tmp[0];
Out[1] = tmp[1];
Out[2] = tmp[2];
}
}
static
void InputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
{
WVEC3 InVect, OutVect;
if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
{
InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L[0], &MatShaper -> p16);
InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L[1], &MatShaper -> p16);
InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L[2], &MatShaper -> p16);
}
else
{
InVect.n[VX] = ToFixedDomain(In[0]);
InVect.n[VY] = ToFixedDomain(In[1]);
InVect.n[VZ] = ToFixedDomain(In[2]);
}
if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
{
MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
}
else
{
OutVect = InVect;
}
// PCS in 1Fixed15 format, adjusting
Out[0] = _cmsClampWord((OutVect.n[VX]) >> 1);
Out[1] = _cmsClampWord((OutVect.n[VY]) >> 1);
Out[2] = _cmsClampWord((OutVect.n[VZ]) >> 1);
}
static
void OutputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
{
WVEC3 InVect, OutVect;
int i;
// We need to convert from XYZ to RGB, here we must
// shift << 1 to pass between 1.15 to 15.16 formats
InVect.n[VX] = (Fixed32) In[0] << 1;
InVect.n[VY] = (Fixed32) In[1] << 1;
InVect.n[VZ] = (Fixed32) In[2] << 1;
if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
{
MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
}
else
{
OutVect = InVect;
}
if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
{
for (i=0; i < 3; i++)
{
Out[i] = cmsLinearInterpLUT16(
_cmsClampWord(FromFixedDomain(OutVect.n[i])),
MatShaper -> L[i],
&MatShaper ->p16);
}
}
else
{
// Result from fixed domain to RGB
Out[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
Out[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
Out[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
}
}
// Master on evaluating shapers, 3 different behaviours
void cmsEvalMatShaper(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
{
if ((MatShaper -> dwFlags & MATSHAPER_ALLSMELTED) == MATSHAPER_ALLSMELTED)
{
AllSmeltedBehaviour(MatShaper, In, Out);
return;
}
if (MatShaper -> dwFlags & MATSHAPER_INPUT)
{
InputBehaviour(MatShaper, In, Out);
return;
}
OutputBehaviour(MatShaper, In, Out);
}

816
modules/lcms/src/cmsmtrx.c Normal file
Просмотреть файл

@ -0,0 +1,816 @@
//
// 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.
// Vector & Matrix stuff
#include "lcms.h"
void cdecl VEC3init(LPVEC3 r, double x, double y, double z);
void cdecl VEC3initF(LPWVEC3 r, double x, double y, double z);
void cdecl VEC3toFix(LPWVEC3 r, LPVEC3 v);
void cdecl VEC3scaleFix(LPWORD r, LPWVEC3 Scale);
void cdecl VEC3swap(LPVEC3 a, LPVEC3 b);
void cdecl VEC3divK(LPVEC3 r, LPVEC3 v, double d);
void cdecl VEC3perK(LPVEC3 r, LPVEC3 v, double d);
void cdecl VEC3perComp(LPVEC3 r, LPVEC3 a, LPVEC3 b);
void cdecl VEC3minus(LPVEC3 r, LPVEC3 a, LPVEC3 b);
void cdecl VEC3scaleAndCut(LPWVEC3 r, LPVEC3 v, double d);
void cdecl VEC3cross(LPVEC3 r, LPVEC3 u, LPVEC3 v);
void cdecl VEC3saturate(LPVEC3 v);
double cdecl VEC3length(LPVEC3 a);
double cdecl VEC3distance(LPVEC3 a, LPVEC3 b);
void cdecl MAT3identity(LPMAT3 a);
void cdecl MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b);
int cdecl MAT3inverse(LPMAT3 a, LPMAT3 b);
LCMSBOOL cdecl MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b);
double cdecl MAT3det(LPMAT3 m);
void cdecl MAT3eval(LPVEC3 r, LPMAT3 a, LPVEC3 v);
void cdecl MAT3toFix(LPWMAT3 r, LPMAT3 v);
void cdecl MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v);
void cdecl MAT3perK(LPMAT3 r, LPMAT3 v, double d);
void cdecl MAT3scaleAndCut(LPWMAT3 r, LPMAT3 v, double d);
// --------------------- Implementation ----------------------------
#define DSWAP(x, y) {double tmp = (x); (x)=(y); (y)=tmp;}
#ifdef USE_ASSEMBLER
#ifdef _MSC_VER
#pragma warning(disable : 4033)
#pragma warning(disable : 4035)
#endif
Fixed32 FixedMul(Fixed32 a, Fixed32 b)
{
ASM {
mov eax, ss:a
mov edx, ss:b
imul edx
add eax, 0x8000
adc edx, 0
shrd eax, edx, 16
}
RET(_EAX);
}
Fixed32 FixedSquare(Fixed32 a)
{
ASM {
pushf
push edx
mov eax, ss:a
imul eax
add eax, 0x8000
adc edx, 0
shrd eax, edx, 16
sar eax, 16
pop edx
popf
}
RET(_EAX);
}
// Linear intERPolation
// a * (h - l) >> 16 + l
Fixed32 FixedLERP(Fixed32 a, Fixed32 l, Fixed32 h)
{
ASM {
mov eax, dword ptr ss:h
mov edx, dword ptr ss:l
push edx
mov ecx, dword ptr ss:a
sub eax, edx
imul ecx
add eax, 0x8000
adc edx, 0
shrd eax, edx, 16
pop edx
add eax, edx
}
RET(_EAX);
}
// a as word is scaled by s as float
WORD FixedScale(WORD a, Fixed32 s)
{
ASM {
xor eax,eax
mov ax, ss:a // This is faster that movzx eax, ss:a
sal eax, 16
mov edx, ss:s
mul edx
add eax, 0x8000
adc edx, 0
mov eax, edx
}
RET(_EAX);
}
#ifdef _MSC_VER
#pragma warning(default : 4033)
#pragma warning(default : 4035)
#endif
#else
// These are floating point versions for compilers that doesn't
// support asm at all. Use with care, since this will slow down
// all operations
Fixed32 FixedMul(Fixed32 a, Fixed32 b)
{
#ifdef USE_INT64
LCMSULONGLONG l = (LCMSULONGLONG) (LCMSSLONGLONG) a * (LCMSULONGLONG) (LCMSSLONGLONG) b + (LCMSULONGLONG) 0x8000;
l >>= 16;
return (Fixed32) l;
#else
return DOUBLE_TO_FIXED(FIXED_TO_DOUBLE(a) * FIXED_TO_DOUBLE(b));
#endif
}
Fixed32 FixedSquare(Fixed32 a)
{
return FixedMul(a, a);
}
Fixed32 FixedLERP(Fixed32 a, Fixed32 l, Fixed32 h)
{
#ifdef USE_INT64
LCMSULONGLONG dif = (LCMSULONGLONG) (h - l) * a + 0x8000;
dif = (dif >> 16) + l;
return (Fixed32) (dif);
#else
double dif = h - l;
dif *= a;
dif /= 65536.0;
dif += l;
return (Fixed32) (dif + 0.5);
#endif
}
WORD FixedScale(WORD a, Fixed32 s)
{
return (WORD) (a * FIXED_TO_DOUBLE(s));
}
#endif
#ifndef USE_INLINE
Fixed32 ToFixedDomain(int a)
{
return a + ((a + 0x7fff) / 0xffff);
}
int FromFixedDomain(Fixed32 a)
{
return a - ((a + 0x7fff) >> 16);
}
#endif
// Initiate a vector (double version)
void VEC3init(LPVEC3 r, double x, double y, double z)
{
r -> n[VX] = x;
r -> n[VY] = y;
r -> n[VZ] = z;
}
// Init a vector (fixed version)
void VEC3initF(LPWVEC3 r, double x, double y, double z)
{
r -> n[VX] = DOUBLE_TO_FIXED(x);
r -> n[VY] = DOUBLE_TO_FIXED(y);
r -> n[VZ] = DOUBLE_TO_FIXED(z);
}
// Convert to fixed point encoding is 1.0 = 0xFFFF
void VEC3toFix(LPWVEC3 r, LPVEC3 v)
{
r -> n[VX] = DOUBLE_TO_FIXED(v -> n[VX]);
r -> n[VY] = DOUBLE_TO_FIXED(v -> n[VY]);
r -> n[VZ] = DOUBLE_TO_FIXED(v -> n[VZ]);
}
// Convert from fixed point
void VEC3fromFix(LPVEC3 r, LPWVEC3 v)
{
r -> n[VX] = FIXED_TO_DOUBLE(v -> n[VX]);
r -> n[VY] = FIXED_TO_DOUBLE(v -> n[VY]);
r -> n[VZ] = FIXED_TO_DOUBLE(v -> n[VZ]);
}
// Swap two double vectors
void VEC3swap(LPVEC3 a, LPVEC3 b)
{
DSWAP(a-> n[VX], b-> n[VX]);
DSWAP(a-> n[VY], b-> n[VY]);
DSWAP(a-> n[VZ], b-> n[VZ]);
}
// Divide a vector by a constant
void VEC3divK(LPVEC3 r, LPVEC3 v, double d)
{
double d_inv = 1./d;
r -> n[VX] = v -> n[VX] * d_inv;
r -> n[VY] = v -> n[VY] * d_inv;
r -> n[VZ] = v -> n[VZ] * d_inv;
}
// Multiply by a constant
void VEC3perK(LPVEC3 r, LPVEC3 v, double d )
{
r -> n[VX] = v -> n[VX] * d;
r -> n[VY] = v -> n[VY] * d;
r -> n[VZ] = v -> n[VZ] * d;
}
void VEC3perComp(LPVEC3 r, LPVEC3 a, LPVEC3 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];
}
// Minus
void VEC3minus(LPVEC3 r, LPVEC3 a, LPVEC3 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];
}
// Check id two vectors are the same, allowing tolerance
static
LCMSBOOL RangeCheck(double l, double h, double v)
{
return (v >= l && v <= h);
}
LCMSBOOL VEC3equal(LPWVEC3 a, LPWVEC3 b, double Tolerance)
{
int i;
double c;
for (i=0; i < 3; i++)
{
c = FIXED_TO_DOUBLE(a -> n[i]);
if (!RangeCheck(c - Tolerance,
c + Tolerance,
FIXED_TO_DOUBLE(b->n[i]))) return FALSE;
}
return TRUE;
}
LCMSBOOL VEC3equalF(LPVEC3 a, LPVEC3 b, double Tolerance)
{
int i;
double c;
for (i=0; i < 3; i++)
{
c = a -> n[i];
if (!RangeCheck(c - Tolerance,
c + Tolerance,
b->n[i])) return FALSE;
}
return TRUE;
}
void VEC3scaleFix(LPWORD r, LPWVEC3 Scale)
{
if (Scale -> n[VX] == 0x00010000L &&
Scale -> n[VY] == 0x00010000L &&
Scale -> n[VZ] == 0x00010000L) return;
r[0] = (WORD) FixedScale(r[0], Scale -> n[VX]);
r[1] = (WORD) FixedScale(r[1], Scale -> n[VY]);
r[2] = (WORD) FixedScale(r[2], Scale -> n[VZ]);
}
// Vector cross product
void VEC3cross(LPVEC3 r, LPVEC3 u, LPVEC3 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];
}
// The vector size
double VEC3length(LPVEC3 a)
{
return sqrt(a ->n[VX] * a ->n[VX] +
a ->n[VY] * a ->n[VY] +
a ->n[VZ] * a ->n[VZ]);
}
// Saturate a vector into 0..1.0 range
void VEC3saturate(LPVEC3 v)
{
int i;
for (i=0; i < 3; i++) {
if (v ->n[i] < 0)
v ->n[i] = 0;
else
if (v ->n[i] > 1.0)
v ->n[i] = 1.0;
}
}
// Euclidean distance
double VEC3distance(LPVEC3 a, LPVEC3 b)
{
double d1 = a ->n[VX] - b ->n[VX];
double d2 = a ->n[VY] - b ->n[VY];
double d3 = a ->n[VZ] - b ->n[VZ];
return sqrt(d1*d1 + d2*d2 + d3*d3);
}
// Identity
void MAT3identity(LPMAT3 a)
{
VEC3init(&a-> v[0], 1.0, 0.0, 0.0);
VEC3init(&a-> v[1], 0.0, 1.0, 0.0);
VEC3init(&a-> v[2], 0.0, 0.0, 1.0);
}
// Check if matrix is Identity. Allow a tolerance as %
LCMSBOOL MAT3isIdentity(LPWMAT3 a, double Tolerance)
{
int i;
MAT3 Idd;
WMAT3 Idf;
MAT3identity(&Idd);
MAT3toFix(&Idf, &Idd);
for (i=0; i < 3; i++)
if (!VEC3equal(&a -> v[i], &Idf.v[i], Tolerance)) return FALSE;
return TRUE;
}
// Multiply two matrices
void MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 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]
VEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2));
VEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2));
VEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2));
#undef ROWCOL //(i, j)
}
// Inverse of a matrix b = a^(-1)
// Gauss-Jordan elimination with partial pivoting
int MAT3inverse(LPMAT3 a, LPMAT3 b)
{
register int i, j, max;
MAT3identity(b);
// Loop over cols of a from left to right, eliminating above and below diag
for (j=0; j<3; j++) { // Find largest pivot in column j among rows j..2
max = j; // Row with largest pivot candidate
for (i=j+1; i<3; i++)
if (fabs(a -> v[i].n[j]) > fabs(a -> v[max].n[j]))
max = i;
// Swap rows max and j in a and b to put pivot on diagonal
VEC3swap(&a -> v[max], &a -> v[j]);
VEC3swap(&b -> v[max], &b -> v[j]);
// Scale row j to have a unit diagonal
if (a -> v[j].n[j]==0.)
return -1; // singular matrix; can't invert
VEC3divK(&b-> v[j], &b -> v[j], a->v[j].n[j]);
VEC3divK(&a-> v[j], &a -> v[j], a->v[j].n[j]);
// Eliminate off-diagonal elems in col j of a, doing identical ops to b
for (i=0; i<3; i++)
if (i !=j) {
VEC3 temp;
VEC3perK(&temp, &b -> v[j], a -> v[i].n[j]);
VEC3minus(&b -> v[i], &b -> v[i], &temp);
VEC3perK(&temp, &a -> v[j], a -> v[i].n[j]);
VEC3minus(&a -> v[i], &a -> v[i], &temp);
}
}
return 1;
}
// Solve a system in the form Ax = b
LCMSBOOL MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b)
{
MAT3 m, a_1;
CopyMemory(&m, a, sizeof(MAT3));
if (!MAT3inverse(&m, &a_1)) return FALSE; // Singular matrix
MAT3eval(x, &a_1, b);
return TRUE;
}
// The determinant
double MAT3det(LPMAT3 m)
{
double a1 = m ->v[VX].n[VX];
double a2 = m ->v[VX].n[VY];
double a3 = m ->v[VX].n[VZ];
double b1 = m ->v[VY].n[VX];
double b2 = m ->v[VY].n[VY];
double b3 = m ->v[VY].n[VZ];
double c1 = m ->v[VZ].n[VX];
double c2 = m ->v[VZ].n[VY];
double c3 = m ->v[VZ].n[VZ];
return a1*b2*c3 - a1*b3*c2 + a2*b3*c1 - a2*b1*c3 - a3*b1*c2 - a3*b2*c1;
}
// linear transform
void MAT3eval(LPVEC3 r, LPMAT3 a, LPVEC3 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];
}
// Ok, this is another bottleneck of performance.
#ifdef USE_ASSEMBLER
// ecx:ebx is result in 64 bits format
// edi points to matrix, esi points to input vector
// since only 3 accesses are in output, this is a stack variable
void MAT3evalW(LPWVEC3 r_, LPWMAT3 a_, LPWVEC3 v_)
{
ASM {
mov esi, dword ptr ss:v_
mov edi, dword ptr ss:a_
// r->n[VX] = FixedMul(a->v[0].n[0], v->n[0]) +
mov eax,dword ptr [esi]
mov edx,dword ptr [edi]
imul edx
mov ecx, eax
mov ebx, edx
// FixedMul(a->v[0].n[1], v->n[1]) +
mov eax,dword ptr [esi+4]
mov edx,dword ptr [edi+4]
imul edx
add ecx, eax
adc ebx, edx
// FixedMul(a->v[0].n[2], v->n[2]);
mov eax,dword ptr [esi+8]
mov edx,dword ptr [edi+8]
imul edx
add ecx, eax
adc ebx, edx
// Back to Fixed 15.16
add ecx, 0x8000
adc ebx, 0
shrd ecx, ebx, 16
push edi
mov edi, dword ptr ss:r_
mov dword ptr [edi], ecx // r -> n[VX]
pop edi
// 2nd row ***************************
// FixedMul(a->v[1].n[0], v->n[0])
mov eax,dword ptr [esi]
mov edx,dword ptr [edi+12]
imul edx
mov ecx, eax
mov ebx, edx
// FixedMul(a->v[1].n[1], v->n[1]) +
mov eax,dword ptr [esi+4]
mov edx,dword ptr [edi+16]
imul edx
add ecx, eax
adc ebx, edx
// FixedMul(a->v[1].n[2], v->n[2]);
mov eax,dword ptr [esi+8]
mov edx,dword ptr [edi+20]
imul edx
add ecx, eax
adc ebx, edx
add ecx, 0x8000
adc ebx, 0
shrd ecx, ebx, 16
push edi
mov edi, dword ptr ss:r_
mov dword ptr [edi+4], ecx // r -> n[VY]
pop edi
// 3d row **************************
// r->n[VZ] = FixedMul(a->v[2].n[0], v->n[0]) +
mov eax,dword ptr [esi]
mov edx,dword ptr [edi+24]
imul edx
mov ecx, eax
mov ebx, edx
// FixedMul(a->v[2].n[1], v->n[1]) +
mov eax,dword ptr [esi+4]
mov edx,dword ptr [edi+28]
imul edx
add ecx, eax
adc ebx, edx
// FixedMul(a->v[2].n[2], v->n[2]);
mov eax,dword ptr [esi+8]
mov edx,dword ptr [edi+32]
imul edx
add ecx, eax
adc ebx, edx
add ecx, 0x8000
adc ebx, 0
shrd ecx, ebx, 16
mov edi, dword ptr ss:r_
mov dword ptr [edi+8], ecx // r -> n[VZ]
}
}
#else
#ifdef USE_FLOAT
void MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v)
{
r->n[VX] = DOUBLE_TO_FIXED(
FIXED_TO_DOUBLE(a->v[0].n[0]) * FIXED_TO_DOUBLE(v->n[0]) +
FIXED_TO_DOUBLE(a->v[0].n[1]) * FIXED_TO_DOUBLE(v->n[1]) +
FIXED_TO_DOUBLE(a->v[0].n[2]) * FIXED_TO_DOUBLE(v->n[2])
);
r->n[VY] = DOUBLE_TO_FIXED(
FIXED_TO_DOUBLE(a->v[1].n[0]) * FIXED_TO_DOUBLE(v->n[0]) +
FIXED_TO_DOUBLE(a->v[1].n[1]) * FIXED_TO_DOUBLE(v->n[1]) +
FIXED_TO_DOUBLE(a->v[1].n[2]) * FIXED_TO_DOUBLE(v->n[2])
);
r->n[VZ] = DOUBLE_TO_FIXED(
FIXED_TO_DOUBLE(a->v[2].n[0]) * FIXED_TO_DOUBLE(v->n[0]) +
FIXED_TO_DOUBLE(a->v[2].n[1]) * FIXED_TO_DOUBLE(v->n[1]) +
FIXED_TO_DOUBLE(a->v[2].n[2]) * FIXED_TO_DOUBLE(v->n[2])
);
}
#else
void MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v)
{
#ifdef USE_INT64
LCMSULONGLONG l1 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[0] *
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] +
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[1] *
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] +
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[2] *
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000;
LCMSULONGLONG l2 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[0] *
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] +
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[1] *
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] +
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[2] *
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000;
LCMSULONGLONG l3 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[0] *
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] +
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[1] *
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] +
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[2] *
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000;
l1 >>= 16;
l2 >>= 16;
l3 >>= 16;
r->n[VX] = (Fixed32) l1;
r->n[VY] = (Fixed32) l2;
r->n[VZ] = (Fixed32) l3;
#else
// FIXME: Rounding should be done at very last stage. There is 1-Contone rounding error!
r->n[VX] = FixedMul(a->v[0].n[0], v->n[0]) +
FixedMul(a->v[0].n[1], v->n[1]) +
FixedMul(a->v[0].n[2], v->n[2]);
r->n[VY] = FixedMul(a->v[1].n[0], v->n[0]) +
FixedMul(a->v[1].n[1], v->n[1]) +
FixedMul(a->v[1].n[2], v->n[2]);
r->n[VZ] = FixedMul(a->v[2].n[0], v->n[0]) +
FixedMul(a->v[2].n[1], v->n[1]) +
FixedMul(a->v[2].n[2], v->n[2]);
#endif
}
#endif
#endif
void MAT3perK(LPMAT3 r, LPMAT3 v, double d)
{
VEC3perK(&r -> v[0], &v -> v[0], d);
VEC3perK(&r -> v[1], &v -> v[1], d);
VEC3perK(&r -> v[2], &v -> v[2], d);
}
void MAT3toFix(LPWMAT3 r, LPMAT3 v)
{
VEC3toFix(&r -> v[0], &v -> v[0]);
VEC3toFix(&r -> v[1], &v -> v[1]);
VEC3toFix(&r -> v[2], &v -> v[2]);
}
void MAT3fromFix(LPMAT3 r, LPWMAT3 v)
{
VEC3fromFix(&r -> v[0], &v -> v[0]);
VEC3fromFix(&r -> v[1], &v -> v[1]);
VEC3fromFix(&r -> v[2], &v -> v[2]);
}
// Scale v by d and store it in r giving INTEGER
void VEC3scaleAndCut(LPWVEC3 r, LPVEC3 v, double d)
{
r -> n[VX] = (int) floor(v -> n[VX] * d + .5);
r -> n[VY] = (int) floor(v -> n[VY] * d + .5);
r -> n[VZ] = (int) floor(v -> n[VZ] * d + .5);
}
void MAT3scaleAndCut(LPWMAT3 r, LPMAT3 v, double d)
{
VEC3scaleAndCut(&r -> v[0], &v -> v[0], d);
VEC3scaleAndCut(&r -> v[1], &v -> v[1], d);
VEC3scaleAndCut(&r -> v[2], &v -> v[2], d);
}

171
modules/lcms/src/cmsnamed.c Normal file
Просмотреть файл

@ -0,0 +1,171 @@
//
// 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.
// Named color support
#include "lcms.h"
static
LPcmsNAMEDCOLORLIST GrowNamedColorList(LPcmsNAMEDCOLORLIST v, int ByElements)
{
if (ByElements > v ->Allocated) {
LPcmsNAMEDCOLORLIST TheNewList;
int NewElements;
size_t size;
if (v ->Allocated == 0)
NewElements = 64; // Initial guess
else
NewElements = v ->Allocated;
while (ByElements > NewElements)
NewElements *= 2;
size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * NewElements);
TheNewList = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size);
if (TheNewList == NULL) {
cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory reallocating named color list");
return NULL;
}
else {
ZeroMemory(TheNewList, size);
CopyMemory(TheNewList, v, sizeof(cmsNAMEDCOLORLIST) + (v ->nColors - 1) * sizeof(cmsNAMEDCOLOR));
TheNewList -> Allocated = NewElements;
_cmsFree(v);
return TheNewList;
}
}
return v;
}
LPcmsNAMEDCOLORLIST cmsAllocNamedColorList(int n)
{
size_t size = sizeof(cmsNAMEDCOLORLIST) + (n - 1) * sizeof(cmsNAMEDCOLOR);
LPcmsNAMEDCOLORLIST v = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size);
if (v == NULL) {
cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory creating named color list");
return NULL;
}
ZeroMemory(v, size);
v ->nColors = n;
v ->Allocated = n;
v ->Prefix[0] = 0;
v ->Suffix[0] = 0;
return v;
}
void cmsFreeNamedColorList(LPcmsNAMEDCOLORLIST v)
{
if (v == NULL) {
cmsSignalError(LCMS_ERRC_RECOVERABLE, "Couldn't free a NULL named color list");
return;
}
_cmsFree(v);
}
LCMSBOOL cmsAppendNamedColor(cmsHTRANSFORM xform, const char* Name, WORD PCS[3], WORD Colorant[MAXCHANNELS])
{
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
LPcmsNAMEDCOLORLIST List;
int i;
if (v ->NamedColorList == NULL) return FALSE;
v ->NamedColorList = GrowNamedColorList(v ->NamedColorList, v->NamedColorList ->nColors + 1);
List = v ->NamedColorList;
for (i=0; i < MAXCHANNELS; i++)
List ->List[List ->nColors].DeviceColorant[i] = Colorant[i];
for (i=0; i < 3; i++)
List ->List[List ->nColors].PCS[i] = PCS[i];
strncpy(List ->List[List ->nColors].Name, Name, MAX_PATH-1);
List ->nColors++;
return TRUE;
}
// Returns named color count
int LCMSEXPORT cmsNamedColorCount(cmsHTRANSFORM xform)
{
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
if (v ->NamedColorList == NULL) return 0;
return v ->NamedColorList ->nColors;
}
LCMSBOOL LCMSEXPORT cmsNamedColorInfo(cmsHTRANSFORM xform, int nColor, char* Name, char* Prefix, char* Suffix)
{
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
if (v ->NamedColorList == NULL) return FALSE;
if (nColor < 0 || nColor >= cmsNamedColorCount(xform)) return FALSE;
if (Name) strncpy(Name, v ->NamedColorList->List[nColor].Name, 31);
if (Prefix) strncpy(Prefix, v ->NamedColorList->Prefix, 31);
if (Suffix) strncpy(Suffix, v ->NamedColorList->Suffix, 31);
return TRUE;
}
int LCMSEXPORT cmsNamedColorIndex(cmsHTRANSFORM xform, const char* Name)
{
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
int i, n;
if (v ->NamedColorList == NULL) return -1;
n = cmsNamedColorCount(xform);
for (i=0; i < n; i++) {
if (stricmp(Name, v ->NamedColorList->List[i].Name) == 0)
return i;
}
return -1;
}

2067
modules/lcms/src/cmspack.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

601
modules/lcms/src/cmspcs.c Normal file
Просмотреть файл

@ -0,0 +1,601 @@
//
// 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]);
}

1713
modules/lcms/src/cmsps2.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

668
modules/lcms/src/cmssamp.c Normal file
Просмотреть файл

@ -0,0 +1,668 @@
//
// 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.
#include "lcms.h"
// ---------------------------------------------------------------------------------
static volatile int GlobalBlackPreservationStrategy = 0;
// Quantize a value 0 <= i < MaxSamples
WORD _cmsQuantizeVal(double i, int MaxSamples)
{
double x;
x = ((double) i * 65535.) / (double) (MaxSamples - 1);
return (WORD) floor(x + .5);
}
// Is a table linear?
int cmsIsLinear(WORD Table[], int nEntries)
{
register int i;
int diff;
for (i=0; i < nEntries; i++) {
diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries));
if (diff > 3)
return 0;
}
return 1;
}
// pow() restricted to integer
static
int ipow(int base, int exp)
{
int res = base;
while (--exp)
res *= base;
return res;
}
// Given n, 0<=n<=clut^dim, returns the colorant.
static
int ComponentOf(int n, int clut, int nColorant)
{
if (nColorant <= 0)
return (n % clut);
n /= ipow(clut, nColorant);
return (n % clut);
}
// This routine does a sweep on whole input space, and calls its callback
// function on knots. returns TRUE if all ok, FALSE otherwise.
LCMSBOOL LCMSEXPORT cmsSample3DGrid(LPLUT Lut, _cmsSAMPLER Sampler, LPVOID Cargo, DWORD dwFlags)
{
int i, t, nTotalPoints, Colorant, index;
WORD In[MAXCHANNELS], Out[MAXCHANNELS];
nTotalPoints = ipow(Lut->cLutPoints, Lut -> InputChan);
index = 0;
for (i = 0; i < nTotalPoints; i++) {
for (t=0; t < (int) Lut -> InputChan; t++) {
Colorant = ComponentOf(i, Lut -> cLutPoints, (Lut -> InputChan - t - 1 ));
In[t] = _cmsQuantizeVal(Colorant, Lut -> cLutPoints);
}
if (dwFlags & SAMPLER_HASTL1) {
for (t=0; t < (int) Lut -> InputChan; t++)
In[t] = cmsReverseLinearInterpLUT16(In[t],
Lut -> L1[t],
&Lut -> In16params);
}
for (t=0; t < (int) Lut -> OutputChan; t++)
Out[t] = Lut->T[index + t];
if (dwFlags & SAMPLER_HASTL2) {
for (t=0; t < (int) Lut -> OutputChan; t++)
Out[t] = cmsLinearInterpLUT16(Out[t],
Lut -> L2[t],
&Lut -> Out16params);
}
if (!Sampler(In, Out, Cargo))
return FALSE;
if (!(dwFlags & SAMPLER_INSPECT)) {
if (dwFlags & SAMPLER_HASTL2) {
for (t=0; t < (int) Lut -> OutputChan; t++)
Out[t] = cmsReverseLinearInterpLUT16(Out[t],
Lut -> L2[t],
&Lut -> Out16params);
}
for (t=0; t < (int) Lut -> OutputChan; t++)
Lut->T[index + t] = Out[t];
}
index += Lut -> OutputChan;
}
return TRUE;
}
// choose reasonable resolution
int _cmsReasonableGridpointsByColorspace(icColorSpaceSignature Colorspace, DWORD 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 stripped resolution
if (dwFlags & cmsFLAGS_LOWRESPRECALC) {
if (nChannels > 4)
return 6; // 6 for Hifi
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
}
// Sampler implemented by another transform. This is a clean way to
// precalculate the devicelink 3D CLUT for almost any transform
static
int XFormSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
cmsDoTransform((cmsHTRANSFORM) Cargo, In, Out, 1);
return TRUE;
}
// This routine does compute the devicelink CLUT containing whole
// transform. Handles any channel number.
LPLUT _cmsPrecalculateDeviceLink(cmsHTRANSFORM h, DWORD dwFlags)
{
_LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h;
LPLUT Grid;
int nGridPoints;
DWORD dwFormatIn, dwFormatOut;
DWORD SaveFormatIn, SaveFormatOut;
int ChannelsIn, ChannelsOut;
LPLUT SaveGamutLUT;
// Remove any gamut checking
SaveGamutLUT = p ->Gamut;
p ->Gamut = NULL;
ChannelsIn = _cmsChannelsOf(p -> EntryColorSpace);
ChannelsOut = _cmsChannelsOf(p -> ExitColorSpace);
nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
Grid = cmsAllocLUT();
if (!Grid) return NULL;
Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsIn, ChannelsOut);
// Compute device link on 16-bit basis
dwFormatIn = (CHANNELS_SH(ChannelsIn)|BYTES_SH(2));
dwFormatOut = (CHANNELS_SH(ChannelsOut)|BYTES_SH(2));
SaveFormatIn = p ->InputFormat;
SaveFormatOut = p ->OutputFormat;
p -> InputFormat = dwFormatIn;
p -> OutputFormat = dwFormatOut;
p -> FromInput = _cmsIdentifyInputFormat(p, dwFormatIn);
p -> ToOutput = _cmsIdentifyOutputFormat(p, dwFormatOut);
// Fix gamut & gamma possible mismatches.
if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION)) {
cmsHTRANSFORM hOne[1];
hOne[0] = h;
_cmsComputePrelinearizationTablesFromXFORM(hOne, 1, Grid);
}
// Attention to this typecast! we can take the luxury to
// do this since cmsHTRANSFORM is only an alias to a pointer
// to the transform struct.
if (!cmsSample3DGrid(Grid, XFormSampler, (LPVOID) p, Grid -> wFlags)) {
cmsFreeLUT(Grid);
Grid = NULL;
}
p ->Gamut = SaveGamutLUT;
p ->InputFormat = SaveFormatIn;
p ->OutputFormat = SaveFormatOut;
return Grid;
}
// Sampler for Black-preserving CMYK->CMYK transforms
typedef struct {
cmsHTRANSFORM cmyk2cmyk;
cmsHTRANSFORM cmyk2Lab;
LPGAMMATABLE KTone;
L16PARAMS KToneParams;
LPLUT LabK2cmyk;
double MaxError;
cmsHTRANSFORM hRoundTrip;
int MaxTAC;
cmsHTRANSFORM hProofOutput;
} BPCARGO, *LPBPCARGO;
// Preserve black only if that is the only ink used
static
int BlackPreservingGrayOnlySampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
BPCARGO* bp = (LPBPCARGO) Cargo;
// If going across black only, keep black only
if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
// TAC does not apply because it is black ink!
Out[0] = Out[1] = Out[2] = 0;
Out[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
return 1;
}
// Keep normal transform for other colors
cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
return 1;
}
// Preserve all K plane.
static
int BlackPreservingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
WORD LabK[4];
double SumCMY, SumCMYK, Error;
cmsCIELab ColorimetricLab, BlackPreservingLab;
BPCARGO* bp = (LPBPCARGO) Cargo;
// Get the K across Tone curve
LabK[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
// If going across black only, keep black only
if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
Out[0] = Out[1] = Out[2] = 0;
Out[3] = LabK[3];
return 1;
}
// Try the original transform, maybe K is already ok (valid on K=0)
cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
if (Out[3] == LabK[3]) return 1;
// No, mesure and keep Lab measurement for further usage
cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);
// Is not black only and the transform doesn't keep black.
// Obtain the Lab of CMYK. After that we have Lab + K
cmsDoTransform(bp ->cmyk2Lab, In, LabK, 1);
// Obtain the corresponding CMY using reverse interpolation.
// As a seed, we use the colorimetric CMY
cmsEvalLUTreverse(bp ->LabK2cmyk, LabK, Out, Out);
// Estimate the error
cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
// Apply TAC if needed
SumCMY = Out[0] + Out[1] + Out[2];
SumCMYK = SumCMY + Out[3];
if (SumCMYK > bp ->MaxTAC) {
double Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY);
if (Ratio < 0)
Ratio = 0;
Out[0] = (WORD) floor(Out[0] * Ratio + 0.5); // C
Out[1] = (WORD) floor(Out[1] * Ratio + 0.5); // M
Out[2] = (WORD) floor(Out[2] * Ratio + 0.5); // Y
}
return 1;
}
// Sample whole gamut to estimate maximum TAC
#ifdef _MSC_VER
#pragma warning(disable : 4100)
#endif
static
int EstimateTAC(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
BPCARGO* bp = (LPBPCARGO) Cargo;
WORD RoundTrip[4];
int Sum;
cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1);
Sum = RoundTrip[0] + RoundTrip[1] + RoundTrip[2] + RoundTrip[3];
if (Sum > bp ->MaxTAC)
bp ->MaxTAC = Sum;
return 1;
}
// Estimate the maximum error
static
int BlackPreservingEstimateErrorSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
BPCARGO* bp = (LPBPCARGO) Cargo;
WORD ColorimetricOut[4];
cmsCIELab ColorimetricLab, BlackPreservingLab;
double Error;
if (In[0] == 0 && In[1] == 0 && In[2] == 0) return 1;
cmsDoTransform(bp->cmyk2cmyk, In, ColorimetricOut, 1);
cmsDoTransform(bp->hProofOutput, ColorimetricOut, &ColorimetricLab, 1);
cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
if (Error > bp ->MaxError)
bp ->MaxError = Error;
return 1;
}
// Setup the K preservation strategy
int LCMSEXPORT cmsSetCMYKPreservationStrategy(int n)
{
int OldVal = GlobalBlackPreservationStrategy;
if (n >= 0)
GlobalBlackPreservationStrategy = n;
return OldVal;
}
#pragma warning(disable: 4550)
// Get a pointer to callback on depending of strategy
static
_cmsSAMPLER _cmsGetBlackPreservationSampler(void)
{
switch (GlobalBlackPreservationStrategy) {
case 0: return BlackPreservingGrayOnlySampler;
default: return BlackPreservingSampler;
}
}
// This is the black-preserving devicelink generator
LPLUT _cmsPrecalculateBlackPreservingDeviceLink(cmsHTRANSFORM hCMYK2CMYK, DWORD dwFlags)
{
_LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK;
BPCARGO Cargo;
LPLUT Grid;
DWORD LocalFlags;
cmsHPROFILE hLab = cmsCreateLabProfile(NULL);
int nGridPoints;
icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual
icSigAToB1Tag, // Relative colorimetric
icSigAToB2Tag, // Saturation
icSigAToB1Tag }; // Absolute colorimetric
// (Relative/WhitePoint)
nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
// Get a copy of inteserting flags for this kind of xform
LocalFlags = cmsFLAGS_NOTPRECALC;
if (p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION)
LocalFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
// Fill in cargo struct
Cargo.cmyk2cmyk = hCMYK2CMYK;
// Compute tone curve.
Cargo.KTone = _cmsBuildKToneCurve(hCMYK2CMYK, 256);
if (Cargo.KTone == NULL) return NULL;
cmsCalcL16Params(Cargo.KTone ->nEntries, &Cargo.KToneParams);
// Create a CMYK->Lab "normal" transform on input, without K-preservation
Cargo.cmyk2Lab = cmsCreateTransform(p ->InputProfile, TYPE_CMYK_16,
hLab, TYPE_Lab_16, p->Intent, LocalFlags);
// We are going to use the reverse of proof direction
Cargo.LabK2cmyk = cmsReadICCLut(p->OutputProfile, Device2PCS[p->Intent]);
// Is there any table available?
if (Cargo.LabK2cmyk == NULL) {
Grid = NULL;
goto Cleanup;
}
// Setup a roundtrip on output profile for TAC estimation
Cargo.hRoundTrip = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
p ->OutputProfile, TYPE_CMYK_16, p->Intent, cmsFLAGS_NOTPRECALC);
// Setup a proof CMYK->Lab on output
Cargo.hProofOutput = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
hLab, TYPE_Lab_DBL, p->Intent, LocalFlags);
// Create an empty LUT for holding K-preserving xform
Grid = cmsAllocLUT();
if (!Grid) goto Cleanup;
Grid = cmsAlloc3DGrid(Grid, nGridPoints, 4, 4);
// Setup formatters
p -> FromInput = _cmsIdentifyInputFormat(p, TYPE_CMYK_16);
p -> ToOutput = _cmsIdentifyOutputFormat(p, TYPE_CMYK_16);
// Step #1, estimate TAC
Cargo.MaxTAC = 0;
if (!cmsSample3DGrid(Grid, EstimateTAC, (LPVOID) &Cargo, 0)) {
cmsFreeLUT(Grid);
Grid = NULL;
goto Cleanup;
}
// Step #2, compute approximation
if (!cmsSample3DGrid(Grid, _cmsGetBlackPreservationSampler(), (LPVOID) &Cargo, 0)) {
cmsFreeLUT(Grid);
Grid = NULL;
goto Cleanup;
}
// Step #3, estimate error
Cargo.MaxError = 0;
cmsSample3DGrid(Grid, BlackPreservingEstimateErrorSampler, (LPVOID) &Cargo, SAMPLER_INSPECT);
Cleanup:
if (Cargo.cmyk2Lab) cmsDeleteTransform(Cargo.cmyk2Lab);
if (Cargo.hRoundTrip) cmsDeleteTransform(Cargo.hRoundTrip);
if (Cargo.hProofOutput) cmsDeleteTransform(Cargo.hProofOutput);
if (hLab) cmsCloseProfile(hLab);
if (Cargo.KTone) cmsFreeGamma(Cargo.KTone);
if (Cargo.LabK2cmyk) cmsFreeLUT(Cargo.LabK2cmyk);
return Grid;
}
// Fix broken LUT. just to obtain other CMS compatibility
static
void PatchLUT(LPLUT Grid, WORD At[], WORD Value[],
int nChannelsOut, int nChannelsIn)
{
LPL16PARAMS p16 = &Grid -> CLut16params;
double px, py, pz, pw;
int x0, y0, z0, w0;
int i, index;
if (Grid ->wFlags & LUT_HASTL1) return; // There is a prelinearization
px = ((double) At[0] * (p16->Domain)) / 65535.0;
py = ((double) At[1] * (p16->Domain)) / 65535.0;
pz = ((double) At[2] * (p16->Domain)) / 65535.0;
pw = ((double) At[3] * (p16->Domain)) / 65535.0;
x0 = (int) floor(px);
y0 = (int) floor(py);
z0 = (int) floor(pz);
w0 = (int) floor(pw);
if (nChannelsIn == 4) {
if (((px - x0) != 0) ||
((py - y0) != 0) ||
((pz - z0) != 0) ||
((pw - w0) != 0)) return; // Not on exact node
index = p16 -> opta4 * x0 +
p16 -> opta3 * y0 +
p16 -> opta2 * z0 +
p16 -> opta1 * w0;
}
else
if (nChannelsIn == 3) {
if (((px - x0) != 0) ||
((py - y0) != 0) ||
((pz - z0) != 0)) return; // Not on exact node
index = p16 -> opta3 * x0 +
p16 -> opta2 * y0 +
p16 -> opta1 * z0;
}
else
if (nChannelsIn == 1) {
if (((px - x0) != 0)) return; // Not on exact node
index = p16 -> opta1 * x0;
}
else {
cmsSignalError(LCMS_ERRC_ABORTED, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn);
return;
}
for (i=0; i < nChannelsOut; i++)
Grid -> T[index + i] = Value[i];
}
LCMSBOOL _cmsFixWhiteMisalignment(_LPcmsTRANSFORM p)
{
WORD *WhitePointIn, *WhitePointOut, *BlackPointIn, *BlackPointOut;
int nOuts, nIns;
if (!p -> DeviceLink) return FALSE;
if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC) return FALSE;
if ((p ->PreviewProfile != NULL) &&
(p ->ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC)) return FALSE;
if (!_cmsEndPointsBySpace(p -> EntryColorSpace,
&WhitePointIn, &BlackPointIn, &nIns)) return FALSE;
if (!_cmsEndPointsBySpace(p -> ExitColorSpace,
&WhitePointOut, &BlackPointOut, &nOuts)) return FALSE;
// Fix white only
PatchLUT(p -> DeviceLink, WhitePointIn, WhitePointOut, nOuts, nIns);
// PatchLUT(p -> DeviceLink, BlackPointIn, BlackPointOut, nOuts, nIns);
return TRUE;
}

899
modules/lcms/src/cmsvirt.c Normal file
Просмотреть файл

@ -0,0 +1,899 @@
//
// 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.
#include "lcms.h"
// Virtual (built-in) profiles
// -----------------------------------------------------------------------------------
// This function creates a profile based on White point, primaries and
// transfer functions.
cmsHPROFILE LCMSEXPORT cmsCreateRGBProfile(LPcmsCIExyY WhitePoint,
LPcmsCIExyYTRIPLE Primaries,
LPGAMMATABLE TransferFunction[3])
{
cmsHPROFILE hICC;
cmsCIEXYZ tmp;
MAT3 MColorants;
cmsCIEXYZTRIPLE Colorants;
cmsCIExyY MaxWhite;
hICC = _cmsCreateProfilePlaceholder();
if (!hICC) // can't allocate
return NULL;
cmsSetDeviceClass(hICC, icSigDisplayClass);
cmsSetColorSpace(hICC, icSigRgbData);
cmsSetPCS(hICC, icSigXYZData);
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
// Implement profile using following tags:
//
// 1 icSigProfileDescriptionTag
// 2 icSigMediaWhitePointTag
// 3 icSigRedColorantTag
// 4 icSigGreenColorantTag
// 5 icSigBlueColorantTag
// 6 icSigRedTRCTag
// 7 icSigGreenTRCTag
// 8 icSigBlueTRCTag
// This conforms a standard RGB DisplayProfile as says ICC, and then I add
// 9 icSigChromaticityTag
// As addendum II
// Fill-in the tags
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms RGB virtual profile");
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "rgb built-in");
if (WhitePoint) {
cmsxyY2XYZ(&tmp, WhitePoint);
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp);
}
if (WhitePoint && Primaries) {
MaxWhite.x = WhitePoint -> x;
MaxWhite.y = WhitePoint -> y;
MaxWhite.Y = 1.0;
if (!cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries))
{
cmsCloseProfile(hICC);
return NULL;
}
cmsAdaptMatrixToD50(&MColorants, &MaxWhite);
Colorants.Red.X = MColorants.v[0].n[0];
Colorants.Red.Y = MColorants.v[1].n[0];
Colorants.Red.Z = MColorants.v[2].n[0];
Colorants.Green.X = MColorants.v[0].n[1];
Colorants.Green.Y = MColorants.v[1].n[1];
Colorants.Green.Z = MColorants.v[2].n[1];
Colorants.Blue.X = MColorants.v[0].n[2];
Colorants.Blue.Y = MColorants.v[1].n[2];
Colorants.Blue.Z = MColorants.v[2].n[2];
cmsAddTag(hICC, icSigRedColorantTag, (LPVOID) &Colorants.Red);
cmsAddTag(hICC, icSigBlueColorantTag, (LPVOID) &Colorants.Blue);
cmsAddTag(hICC, icSigGreenColorantTag, (LPVOID) &Colorants.Green);
}
if (TransferFunction) {
// In case of gamma, we must dup' the table pointer
cmsAddTag(hICC, icSigRedTRCTag, (LPVOID) TransferFunction[0]);
cmsAddTag(hICC, icSigGreenTRCTag, (LPVOID) TransferFunction[1]);
cmsAddTag(hICC, icSigBlueTRCTag, (LPVOID) TransferFunction[2]);
}
if (Primaries) {
cmsAddTag(hICC, icSigChromaticityTag, (LPVOID) Primaries);
}
return hICC;
}
// This function creates a profile based on White point and transfer function.
cmsHPROFILE LCMSEXPORT cmsCreateGrayProfile(LPcmsCIExyY WhitePoint,
LPGAMMATABLE TransferFunction)
{
cmsHPROFILE hICC;
cmsCIEXYZ tmp;
hICC = _cmsCreateProfilePlaceholder();
if (!hICC) // can't allocate
return NULL;
cmsSetDeviceClass(hICC, icSigDisplayClass);
cmsSetColorSpace(hICC, icSigGrayData);
cmsSetPCS(hICC, icSigXYZData);
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
// Implement profile using following tags:
//
// 1 icSigProfileDescriptionTag
// 2 icSigMediaWhitePointTag
// 6 icSigGrayTRCTag
// This conforms a standard Gray DisplayProfile
// Fill-in the tags
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms gray virtual profile");
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "gray built-in");
if (WhitePoint) {
cmsxyY2XYZ(&tmp, WhitePoint);
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp);
}
if (TransferFunction) {
// In case of gamma, we must dup' the table pointer
cmsAddTag(hICC, icSigGrayTRCTag, (LPVOID) TransferFunction);
}
return hICC;
}
static
int IsPCS(icColorSpaceSignature ColorSpace)
{
return (ColorSpace == icSigXYZData ||
ColorSpace == icSigLabData);
}
static
void FixColorSpaces(cmsHPROFILE hProfile,
icColorSpaceSignature ColorSpace,
icColorSpaceSignature PCS,
DWORD dwFlags)
{
if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
if (IsPCS(ColorSpace) && IsPCS(PCS)) {
cmsSetDeviceClass(hProfile, icSigAbstractClass);
cmsSetColorSpace(hProfile, ColorSpace);
cmsSetPCS(hProfile, PCS);
return;
}
if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
cmsSetDeviceClass(hProfile, icSigOutputClass);
cmsSetPCS(hProfile, ColorSpace);
cmsSetColorSpace(hProfile, PCS);
return;
}
if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
cmsSetDeviceClass(hProfile, icSigInputClass);
cmsSetColorSpace(hProfile, ColorSpace);
cmsSetPCS(hProfile, PCS);
return;
}
}
cmsSetDeviceClass(hProfile, icSigLinkClass);
cmsSetColorSpace(hProfile, ColorSpace);
cmsSetPCS(hProfile, PCS);
}
static
cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
{
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
cmsHPROFILE hICC;
cmsCIEXYZ WhitePoint;
int i, nColors;
size_t Size;
LPcmsNAMEDCOLORLIST nc2;
hICC = _cmsCreateProfilePlaceholder();
if (hICC == NULL) return NULL;
cmsSetRenderingIntent(hICC, v -> Intent);
cmsSetDeviceClass(hICC, icSigNamedColorClass);
cmsSetColorSpace(hICC, v ->ExitColorSpace);
cmsSetPCS(hICC, cmsGetPCS(v ->InputProfile));
cmsTakeMediaWhitePoint(&WhitePoint, v ->InputProfile);
cmsAddTag(hICC, icSigMediaWhitePointTag, &WhitePoint);
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS");
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Named color Device link");
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Named color Device link");
nColors = cmsNamedColorCount(xform);
nc2 = cmsAllocNamedColorList(nColors);
Size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * (nColors-1));
CopyMemory(nc2, v->NamedColorList, Size);
nc2 ->ColorantCount = _cmsChannelsOf(v ->ExitColorSpace);
for (i=0; i < nColors; i++) {
cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
}
cmsAddTag(hICC, icSigNamedColor2Tag, (void*) nc2);
cmsFreeNamedColorList(nc2);
return hICC;
}
// Does convert a transform into a device link profile
cmsHPROFILE LCMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, DWORD dwFlags)
{
cmsHPROFILE hICC;
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) hTransform;
LPLUT Lut;
LCMSBOOL MustFreeLUT;
LPcmsNAMEDCOLORLIST InputColorant = NULL;
LPcmsNAMEDCOLORLIST OutputColorant = NULL;
// Check if is a named color transform
if (cmsGetDeviceClass(v ->InputProfile) == icSigNamedColorClass) {
return CreateNamedColorDevicelink(hTransform);
}
if (v ->DeviceLink) {
Lut = v -> DeviceLink;
MustFreeLUT = FALSE;
}
else {
Lut = _cmsPrecalculateDeviceLink(hTransform, dwFlags);
if (!Lut) return NULL;
MustFreeLUT = TRUE;
}
hICC = _cmsCreateProfilePlaceholder();
if (!hICC) { // can't allocate
if (MustFreeLUT) cmsFreeLUT(Lut);
return NULL;
}
FixColorSpaces(hICC, v -> EntryColorSpace, v -> ExitColorSpace, dwFlags);
cmsSetRenderingIntent(hICC, v -> Intent);
// Implement devicelink profile using following tags:
//
// 1 icSigProfileDescriptionTag
// 2 icSigMediaWhitePointTag
// 3 icSigAToB0Tag
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS");
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Device link");
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Device link");
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
if (cmsGetDeviceClass(hICC) == icSigOutputClass) {
cmsAddTag(hICC, icSigBToA0Tag, (LPVOID) Lut);
}
else
cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
// Try to read input and output colorant table
if (cmsIsTag(v ->InputProfile, icSigColorantTableTag)) {
// Input table can only come in this way.
InputColorant = cmsReadColorantTable(v ->InputProfile, icSigColorantTableTag);
}
// Output is a little bit more complex.
if (cmsGetDeviceClass(v ->OutputProfile) == icSigLinkClass) {
// This tag may exist only on devicelink profiles.
if (cmsIsTag(v ->OutputProfile, icSigColorantTableOutTag)) {
OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableOutTag);
}
} else {
if (cmsIsTag(v ->OutputProfile, icSigColorantTableTag)) {
OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableTag);
}
}
if (InputColorant)
cmsAddTag(hICC, icSigColorantTableTag, InputColorant);
if (OutputColorant)
cmsAddTag(hICC, icSigColorantTableOutTag, OutputColorant);
if (MustFreeLUT) cmsFreeLUT(Lut);
if (InputColorant) cmsFreeNamedColorList(InputColorant);
if (OutputColorant) cmsFreeNamedColorList(OutputColorant);
return hICC;
}
// This is a devicelink operating in the target colorspace with as many transfer
// functions as components
cmsHPROFILE LCMSEXPORT cmsCreateLinearizationDeviceLink(icColorSpaceSignature ColorSpace,
LPGAMMATABLE TransferFunctions[])
{
cmsHPROFILE hICC;
LPLUT Lut;
hICC = _cmsCreateProfilePlaceholder();
if (!hICC) // can't allocate
return NULL;
cmsSetDeviceClass(hICC, icSigLinkClass);
cmsSetColorSpace(hICC, ColorSpace);
cmsSetPCS(hICC, ColorSpace);
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
// Creates a LUT with prelinearization step only
Lut = cmsAllocLUT();
if (Lut == NULL) return NULL;
// Set up channels
Lut ->InputChan = Lut ->OutputChan = _cmsChannelsOf(ColorSpace);
// Copy tables to LUT
cmsAllocLinearTable(Lut, TransferFunctions, 1);
// Create tags
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms linearization device link");
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "linearization built-in");
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
// LUT is already on virtual profile
cmsFreeLUT(Lut);
// Ok, done
return hICC;
}
// Ink-limiting algorithm
//
// Sum = C + M + Y + K
// If Sum > InkLimit
// Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
// if Ratio <0
// Ratio=0
// endif
// Else
// Ratio=1
// endif
//
// C = Ratio * C
// M = Ratio * M
// Y = Ratio * Y
// K: Does not change
static
int InkLimitingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
double InkLimit = *(double *) Cargo;
double SumCMY, SumCMYK, Ratio;
InkLimit = (InkLimit * 655.35);
SumCMY = In[0] + In[1] + In[2];
SumCMYK = SumCMY + In[3];
if (SumCMYK > InkLimit) {
Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
if (Ratio < 0)
Ratio = 0;
}
else Ratio = 1;
Out[0] = (WORD) floor(In[0] * Ratio + 0.5); // C
Out[1] = (WORD) floor(In[1] * Ratio + 0.5); // M
Out[2] = (WORD) floor(In[2] * Ratio + 0.5); // Y
Out[3] = In[3]; // K (untouched)
return TRUE;
}
// This is a devicelink operating in CMYK for ink-limiting
cmsHPROFILE LCMSEXPORT cmsCreateInkLimitingDeviceLink(icColorSpaceSignature ColorSpace,
double Limit)
{
cmsHPROFILE hICC;
LPLUT Lut;
if (ColorSpace != icSigCmykData) {
cmsSignalError(LCMS_ERRC_ABORTED, "InkLimiting: Only CMYK currently supported");
return NULL;
}
if (Limit < 0.0 || Limit > 400) {
cmsSignalError(LCMS_ERRC_WARNING, "InkLimiting: Limit should be between 0..400");
if (Limit < 0) Limit = 0;
if (Limit > 400) Limit = 400;
}
hICC = _cmsCreateProfilePlaceholder();
if (!hICC) // can't allocate
return NULL;
cmsSetDeviceClass(hICC, icSigLinkClass);
cmsSetColorSpace(hICC, ColorSpace);
cmsSetPCS(hICC, ColorSpace);
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
// Creates a LUT with 3D grid only
Lut = cmsAllocLUT();
if (Lut == NULL) {
cmsCloseProfile(hICC);
return NULL;
}
cmsAlloc3DGrid(Lut, 17, _cmsChannelsOf(ColorSpace),
_cmsChannelsOf(ColorSpace));
if (!cmsSample3DGrid(Lut, InkLimitingSampler, (LPVOID) &Limit, 0)) {
// Shouldn't reach here
cmsFreeLUT(Lut);
cmsCloseProfile(hICC);
return NULL;
}
// Create tags
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms ink limiting device link");
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "ink limiting built-in");
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
// LUT is already on virtual profile
cmsFreeLUT(Lut);
// Ok, done
return hICC;
}
static
LPLUT Create3x3EmptyLUT(void)
{
LPLUT AToB0 = cmsAllocLUT();
if (AToB0 == NULL) return NULL;
AToB0 -> InputChan = AToB0 -> OutputChan = 3;
return AToB0;
}
// Creates a fake Lab identity.
cmsHPROFILE LCMSEXPORT cmsCreateLabProfile(LPcmsCIExyY WhitePoint)
{
cmsHPROFILE hProfile;
LPLUT Lut;
hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
if (hProfile == NULL) return NULL;
cmsSetDeviceClass(hProfile, icSigAbstractClass);
cmsSetColorSpace(hProfile, icSigLabData);
cmsSetPCS(hProfile, icSigLabData);
cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity");
cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab built-in");
// An empty LUTs is all we need
Lut = Create3x3EmptyLUT();
if (Lut == NULL) {
cmsCloseProfile(hProfile);
return NULL;
}
cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut);
cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
cmsFreeLUT(Lut);
return hProfile;
}
// Creates a fake Lab identity.
cmsHPROFILE LCMSEXPORT cmsCreateLab4Profile(LPcmsCIExyY WhitePoint)
{
cmsHPROFILE hProfile;
LPLUT Lut;
hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
if (hProfile == NULL) return NULL;
cmsSetProfileICCversion(hProfile, 0x4000000);
cmsSetDeviceClass(hProfile, icSigAbstractClass);
cmsSetColorSpace(hProfile, icSigLabData);
cmsSetPCS(hProfile, icSigLabData);
cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity v4");
cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab v4 built-in");
// An empty LUTs is all we need
Lut = Create3x3EmptyLUT();
if (Lut == NULL) {
cmsCloseProfile(hProfile);
return NULL;
}
Lut -> wFlags |= LUT_V4_INPUT_EMULATE_V2;
cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut);
Lut -> wFlags |= LUT_V4_OUTPUT_EMULATE_V2;
cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
cmsFreeLUT(Lut);
return hProfile;
}
// Creates a fake XYZ identity
cmsHPROFILE LCMSEXPORT cmsCreateXYZProfile(void)
{
cmsHPROFILE hProfile;
LPLUT Lut;
hProfile = cmsCreateRGBProfile(cmsD50_xyY(), NULL, NULL);
if (hProfile == NULL) return NULL;
cmsSetDeviceClass(hProfile, icSigAbstractClass);
cmsSetColorSpace(hProfile, icSigXYZData);
cmsSetPCS(hProfile, icSigXYZData);
cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms XYZ identity");
cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "XYZ built-in");
// An empty LUTs is all we need
Lut = Create3x3EmptyLUT();
if (Lut == NULL) {
cmsCloseProfile(hProfile);
return NULL;
}
cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut);
cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
cmsAddTag(hProfile, icSigPreview0Tag, (LPVOID) Lut);
cmsFreeLUT(Lut);
return hProfile;
}
/*
If RsRGB,GsRGB, BsRGB < 0.04045
R = RsRGB / 12.92
G = GsRGB / 12.92
B = BsRGB / 12.92
else if RsRGB,GsRGB, BsRGB >= 0.04045
R = ((RsRGB + 0.055) / 1.055)^2.4
G = ((GsRGB + 0.055) / 1.055)^2.4
B = ((BsRGB + 0.055) / 1.055)^2.4
*/
static
LPGAMMATABLE Build_sRGBGamma(void)
{
double Parameters[5];
Parameters[0] = 2.4;
Parameters[1] = 1. / 1.055;
Parameters[2] = 0.055 / 1.055;
Parameters[3] = 1. / 12.92;
Parameters[4] = 0.04045; // d
return cmsBuildParametricGamma(1024, 4, Parameters);
}
// Create the ICC virtual profile for sRGB space
cmsHPROFILE LCMSEXPORT cmsCreate_sRGBProfile(void)
{
cmsCIExyY D65;
cmsCIExyYTRIPLE Rec709Primaries = {
{0.6400, 0.3300, 1.0},
{0.3000, 0.6000, 1.0},
{0.1500, 0.0600, 1.0}
};
LPGAMMATABLE Gamma22[3];
cmsHPROFILE hsRGB;
cmsWhitePointFromTemp(6504, &D65);
Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma();
hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma22);
cmsFreeGamma(Gamma22[0]);
if (hsRGB == NULL) return NULL;
cmsAddTag(hsRGB, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
cmsAddTag(hsRGB, icSigDeviceModelDescTag, (LPVOID) "sRGB built-in");
cmsAddTag(hsRGB, icSigProfileDescriptionTag, (LPVOID) "sRGB built-in");
return hsRGB;
}
typedef struct {
double Brightness;
double Contrast;
double Hue;
double Saturation;
cmsCIEXYZ WPsrc, WPdest;
} BCHSWADJUSTS, *LPBCHSWADJUSTS;
static
int bchswSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
cmsCIELab LabIn, LabOut;
cmsCIELCh LChIn, LChOut;
cmsCIEXYZ XYZ;
LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
cmsLabEncoded2Float(&LabIn, In);
cmsLab2LCh(&LChIn, &LabIn);
// Do some adjusts on LCh
LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
LChOut.C = LChIn.C + bchsw -> Saturation;
LChOut.h = LChIn.h + bchsw -> Hue;
cmsLCh2Lab(&LabOut, &LChOut);
// Move white point in Lab
cmsLab2XYZ(&bchsw ->WPsrc, &XYZ, &LabOut);
cmsXYZ2Lab(&bchsw ->WPdest, &LabOut, &XYZ);
// Back to encoded
cmsFloat2LabEncoded(Out, &LabOut);
return TRUE;
}
// Creates an abstract profile operating in Lab space for Brightness,
// contrast, Saturation and white point displacement
cmsHPROFILE LCMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints,
double Bright,
double Contrast,
double Hue,
double Saturation,
int TempSrc,
int TempDest)
{
cmsHPROFILE hICC;
LPLUT Lut;
BCHSWADJUSTS bchsw;
cmsCIExyY WhitePnt;
bchsw.Brightness = Bright;
bchsw.Contrast = Contrast;
bchsw.Hue = Hue;
bchsw.Saturation = Saturation;
cmsWhitePointFromTemp(TempSrc, &WhitePnt);
cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
cmsWhitePointFromTemp(TempDest, &WhitePnt);
cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
hICC = _cmsCreateProfilePlaceholder();
if (!hICC) // can't allocate
return NULL;
cmsSetDeviceClass(hICC, icSigAbstractClass);
cmsSetColorSpace(hICC, icSigLabData);
cmsSetPCS(hICC, icSigLabData);
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
// Creates a LUT with 3D grid only
Lut = cmsAllocLUT();
if (Lut == NULL) {
cmsCloseProfile(hICC);
return NULL;
}
cmsAlloc3DGrid(Lut, nLUTPoints, 3, 3);
if (!cmsSample3DGrid(Lut, bchswSampler, (LPVOID) &bchsw, 0)) {
// Shouldn't reach here
cmsFreeLUT(Lut);
cmsCloseProfile(hICC);
return NULL;
}
// Create tags
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms BCHSW abstract profile");
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "BCHSW built-in");
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
// LUT is already on virtual profile
cmsFreeLUT(Lut);
// Ok, done
return hICC;
}
// Creates a fake NULL profile. This profile return 1 channel as always 0.
// Is useful only for gamut checking tricks
cmsHPROFILE LCMSEXPORT cmsCreateNULLProfile(void)
{
cmsHPROFILE hProfile;
LPLUT Lut;
LPGAMMATABLE EmptyTab;
hProfile = _cmsCreateProfilePlaceholder();
if (!hProfile) // can't allocate
return NULL;
cmsSetDeviceClass(hProfile, icSigOutputClass);
cmsSetColorSpace(hProfile, icSigGrayData);
cmsSetPCS(hProfile, icSigLabData);
// An empty LUTs is all we need
Lut = cmsAllocLUT();
if (Lut == NULL) {
cmsCloseProfile(hProfile);
return NULL;
}
Lut -> InputChan = 3;
Lut -> OutputChan = 1;
EmptyTab = cmsAllocGamma(2);
EmptyTab ->GammaTable[0] = 0;
EmptyTab ->GammaTable[1] = 0;
cmsAllocLinearTable(Lut, &EmptyTab, 2);
cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
cmsFreeLUT(Lut);
cmsFreeGamma(EmptyTab);
return hProfile;
}

695
modules/lcms/src/cmswtpnt.c Normal file
Просмотреть файл

@ -0,0 +1,695 @@
//
// 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.
#include "lcms.h"
// Conversions
void LCMSEXPORT cmsXYZ2xyY(LPcmsCIExyY Dest, const cmsCIEXYZ* Source)
{
double ISum;
ISum = 1./(Source -> X + Source -> Y + Source -> Z);
Dest -> x = (Source -> X) * ISum;
Dest -> y = (Source -> Y) * ISum;
Dest -> Y = Source -> Y;
}
void LCMSEXPORT cmsxyY2XYZ(LPcmsCIEXYZ 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;
}
// Obtains WhitePoint from Temperature
LCMSBOOL LCMSEXPORT cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint)
{
double x, y;
double T, T2, T3;
// double M1, M2;
// No optimization provided.
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(LCMS_ERRC_ABORTED, "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);
// Fill WhitePoint struct
WhitePoint -> x = x;
WhitePoint -> y = y;
WhitePoint -> Y = 1.0;
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
LCMSBOOL LCMSEXPORT cmsBuildRGB2XYZtransferMatrix(LPMAT3 r, LPcmsCIExyY WhitePt,
LPcmsCIExyYTRIPLE Primrs)
{
VEC3 WhitePoint, Coef;
MAT3 Result, Primaries;
double xn, yn;
double xr, yr;
double xg, yg;
double 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
VEC3init(&Primaries.v[0], xr, xg, xb);
VEC3init(&Primaries.v[1], yr, yg, yb);
VEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb));
// Result = Primaries ^ (-1) inverse matrix
if (!MAT3inverse(&Primaries, &Result))
return FALSE;
VEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn);
// Across inverse primaries ...
MAT3eval(&Coef, &Result, &WhitePoint);
// Give us the Coefs, then I build transformation matrix
VEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb);
VEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb);
VEC3init(&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 TRUE;
}
// Compute chromatic adaptation matrix using Chad as cone matrix
static
void ComputeChromaticAdaptation(LPMAT3 Conversion,
LPcmsCIEXYZ SourceWhitePoint,
LPcmsCIEXYZ DestWhitePoint,
LPMAT3 Chad)
{
MAT3 Chad_Inv;
VEC3 ConeSourceXYZ, ConeSourceRGB;
VEC3 ConeDestXYZ, ConeDestRGB;
MAT3 Cone, Tmp;
Tmp = *Chad;
MAT3inverse(&Tmp, &Chad_Inv);
VEC3init(&ConeSourceXYZ, SourceWhitePoint -> X,
SourceWhitePoint -> Y,
SourceWhitePoint -> Z);
VEC3init(&ConeDestXYZ, DestWhitePoint -> X,
DestWhitePoint -> Y,
DestWhitePoint -> Z);
MAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ);
MAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ);
// Build matrix
VEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0);
VEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0);
VEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]);
// Normalize
MAT3per(&Tmp, &Cone, Chad);
MAT3per(Conversion, &Chad_Inv, &Tmp);
}
// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed
LCMSBOOL cmsAdaptationMatrix(LPMAT3 r, LPMAT3 ConeMatrix, LPcmsCIEXYZ FromIll, LPcmsCIEXYZ ToIll)
{
MAT3 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;
ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix);
return TRUE;
}
// Same as anterior, but assuming D50 destination. White point is given in xyY
LCMSBOOL cmsAdaptMatrixToD50(LPMAT3 r, LPcmsCIExyY SourceWhitePt)
{
cmsCIEXYZ Dn;
MAT3 Bradford;
MAT3 Tmp;
cmsxyY2XYZ(&Dn, SourceWhitePt);
cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ());
Tmp = *r;
MAT3per(r, &Bradford, &Tmp);
return TRUE;
}
// Same as anterior, but assuming D50 source. White point is given in xyY
LCMSBOOL cmsAdaptMatrixFromD50(LPMAT3 r, LPcmsCIExyY DestWhitePt)
{
cmsCIEXYZ Dn;
MAT3 Bradford;
MAT3 Tmp;
cmsxyY2XYZ(&Dn, DestWhitePt);
cmsAdaptationMatrix(&Bradford, NULL, cmsD50_XYZ(), &Dn);
Tmp = *r;
MAT3per(r, &Bradford, &Tmp);
return TRUE;
}
// Adapts a color to a given illuminant. Original color is expected to have
// a SourceWhitePt white point.
LCMSBOOL LCMSEXPORT cmsAdaptToIlluminant(LPcmsCIEXYZ Result,
LPcmsCIEXYZ SourceWhitePt,
LPcmsCIEXYZ Illuminant,
LPcmsCIEXYZ Value)
{
MAT3 Bradford;
VEC3 In, Out;
// BradfordLamRiggChromaticAdaptation(&Bradford, SourceWhitePt, Illuminant);
cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant);
VEC3init(&In, Value -> X, Value -> Y, Value -> Z);
MAT3eval(&Out, &Bradford, &In);
Result -> X = Out.n[0];
Result -> Y = Out.n[1];
Result -> Z = Out.n[2];
return TRUE;
}
typedef struct {
double mirek; // temp (in microreciprocal kelvin)
double ut; // u coord of intersection w/ blackbody locus
double vt; // v coord of intersection w/ blackbody locus
double tt; // slope of ISOTEMPERATURE. line
} ISOTEMPERATURE,FAR* LPISOTEMPERATURE;
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
static
double Robertson(LPcmsCIExyY v)
{
int j;
double us,vs;
double uj,vj,tj,di,dj,mi,mj;
double Tc = -1, xs, ys;
di = mi = 0;
xs = v -> x;
ys = v -> y;
// convert (x,y) to CIE 1960 (u,v)
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 + tj*tj);
if ((j!=0) && (di/dj < 0.0)) {
Tc = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi));
break;
}
di = dj;
mi = mj;
}
if (j == NISO) return -1;
return Tc;
}
static
LCMSBOOL InRange(LPcmsCIExyY a, LPcmsCIExyY b, double tolerance)
{
double dist_x, dist_y;
dist_x = fabs(a->x - b->x);
dist_y = fabs(a->y - b->y);
return (tolerance >= dist_x * dist_x + dist_y * dist_y);
}
typedef struct {
char Name[30];
cmsCIExyY Val;
} WHITEPOINTS,FAR *LPWHITEPOINTS;
static
int FromD40toD150(LPWHITEPOINTS pts)
{
int i, n;
n = 0;
for (i=40; i < 150; i ++)
{
sprintf(pts[n].Name, "D%d", i);
cmsWhitePointFromTemp((int) (i*100.0), &pts[n].Val);
n++;
}
return n;
}
// To be removed in future versions
void _cmsIdentifyWhitePoint(char *Buffer, LPcmsCIEXYZ WhitePt)
{
int i, n;
cmsCIExyY Val;
double T;
WHITEPOINTS SomeIlluminants[140] = {
{"CIE illuminant A", {0.4476, 0.4074, 1.0}},
{"CIE illuminant C", {0.3101, 0.3162, 1.0}},
{"D65 (daylight)", {0.3127, 0.3291, 1.0}},
};
n = FromD40toD150(&SomeIlluminants[3]) + 3;
cmsXYZ2xyY(&Val, WhitePt);
Val.Y = 1.;
for (i=0; i < n; i++)
{
if (InRange(&Val, &SomeIlluminants[i].Val, 0.000005))
{
strcpy(Buffer, "WhitePoint : ");
strcat(Buffer, SomeIlluminants[i].Name);
return;
}
}
T = Robertson(&Val);
if (T > 0)
sprintf(Buffer, "White point near %dK", (int) T);
else
{
sprintf(Buffer, "Unknown white point (X:%1.2g, Y:%1.2g, Z:%1.2g)",
WhitePt -> X, WhitePt -> Y, WhitePt -> Z);
}
}
// Use darker colorant to obtain black point
static
int BlackPointAsDarkerColorant(cmsHPROFILE hInput,
int Intent,
LPcmsCIEXYZ BlackPoint,
DWORD dwFlags)
{
WORD *Black, *White;
cmsHTRANSFORM xform;
icColorSpaceSignature Space;
int nChannels;
DWORD dwFormat;
cmsHPROFILE hLab;
cmsCIELab Lab;
cmsCIEXYZ BlackXYZ, MediaWhite;
// 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 0;
}
// Try to get black by using black colorant
Space = cmsGetColorSpace(hInput);
if (!_cmsEndPointsBySpace(Space, &White, &Black, &nChannels)) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return 0;
}
dwFormat = CHANNELS_SH(nChannels)|BYTES_SH(2);
hLab = cmsCreateLabProfile(NULL);
xform = cmsCreateTransform(hInput, dwFormat,
hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC);
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;
cmsCloseProfile(hLab);
cmsDeleteTransform(xform);
cmsLab2XYZ(NULL, &BlackXYZ, &Lab);
if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) {
*BlackPoint = BlackXYZ;
}
else {
if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)) {
cmsTakeMediaWhitePoint(&MediaWhite, hInput);
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ);
}
else
*BlackPoint = BlackXYZ;
}
return 1;
}
// Get a black point of output CMYK profile, discounting any ink-limiting embedded
// in the profile. For doing that, use perceptual intent in input direction:
// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab
static
int BlackPointUsingPerceptualBlack(LPcmsCIEXYZ BlackPoint,
cmsHPROFILE hProfile,
DWORD dwFlags)
{
cmsHTRANSFORM hPercLab2CMYK, hRelColCMYK2Lab;
cmsHPROFILE hLab;
cmsCIELab LabIn, LabOut;
WORD CMYK[MAXCHANNELS];
cmsCIEXYZ BlackXYZ, MediaWhite;
if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return 0;
}
hLab = cmsCreateLabProfile(NULL);
hPercLab2CMYK = cmsCreateTransform(hLab, TYPE_Lab_DBL,
hProfile, TYPE_CMYK_16,
INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC);
hRelColCMYK2Lab = cmsCreateTransform(hProfile, TYPE_CMYK_16,
hLab, TYPE_Lab_DBL,
INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOTPRECALC);
LabIn.L = LabIn.a = LabIn.b = 0;
cmsDoTransform(hPercLab2CMYK, &LabIn, CMYK, 1);
cmsDoTransform(hRelColCMYK2Lab, CMYK, &LabOut, 1);
if (LabOut.L > 50) LabOut.L = 50;
LabOut.a = LabOut.b = 0;
cmsDeleteTransform(hPercLab2CMYK);
cmsDeleteTransform(hRelColCMYK2Lab);
cmsCloseProfile(hLab);
cmsLab2XYZ(NULL, &BlackXYZ, &LabOut);
if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)){
cmsTakeMediaWhitePoint(&MediaWhite, hProfile);
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ);
}
else
*BlackPoint = BlackXYZ;
return 1;
}
// Get Perceptual black of v4 profiles.
static
int GetV4PerceptualBlack(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, DWORD dwFlags)
{
if (dwFlags & LCMS_BPFLAGS_D50_ADAPTED) {
BlackPoint->X = PERCEPTUAL_BLACK_X;
BlackPoint->Y = PERCEPTUAL_BLACK_Y;
BlackPoint->Z = PERCEPTUAL_BLACK_Z;
}
else {
cmsCIEXYZ D50BlackPoint, MediaWhite;
cmsTakeMediaWhitePoint(&MediaWhite, hProfile);
D50BlackPoint.X = PERCEPTUAL_BLACK_X;
D50BlackPoint.Y = PERCEPTUAL_BLACK_Y;
D50BlackPoint.Z = PERCEPTUAL_BLACK_Z;
// Obtain the absolute XYZ. Adapt perceptual black back from D50 to whatever media white
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &D50BlackPoint);
}
return 1;
}
// 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.
int cmsDetectBlackPoint(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, int Intent, DWORD dwFlags)
{
// v4 + perceptual & saturation intents does have its own black point, and it is
// well specified enough to use it.
if ((cmsGetProfileICCversion(hProfile) >= 0x4000000) &&
(Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) {
// Matrix shaper share MRC & perceptual intents
if (_cmsIsMatrixShaper(hProfile))
return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, cmsFLAGS_NOTPRECALC);
// CLUT based - Get perceptual black point (fixed value)
return GetV4PerceptualBlack(BlackPoint, hProfile, dwFlags);
}
#ifdef HONOR_BLACK_POINT_TAG
// v2, v4 rel/abs colorimetric
if (cmsIsTag(hProfile, icSigMediaBlackPointTag) &&
Intent == INTENT_RELATIVE_COLORIMETRIC) {
cmsCIEXYZ BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite;
cmsCIELab Lab;
// If black point is specified, then use it,
cmsTakeMediaBlackPoint(&BlackXYZ, hProfile);
cmsTakeMediaWhitePoint(&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);
// Return BP as D50 relative or absolute XYZ (depends on flags)
if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED))
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &TrustedBlackPoint);
else
*BlackPoint = TrustedBlackPoint;
return 1;
}
#endif
// That is about v2 profiles.
// If output profile, discount ink-limiting and that's all
if (Intent == INTENT_RELATIVE_COLORIMETRIC &&
(cmsGetDeviceClass(hProfile) == icSigOutputClass) &&
(cmsGetColorSpace(hProfile) == icSigCmykData))
return BlackPointUsingPerceptualBlack(BlackPoint, hProfile, dwFlags);
// Nope, compute BP using current intent.
return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags);
}

2010
modules/lcms/src/cmsxform.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

185
modules/lcms/src/lcms.def Normal file
Просмотреть файл

@ -0,0 +1,185 @@
LIBRARY
EXPORTS
_cmsICCcolorSpace = _cmsICCcolorSpace
_cmsSaveProfile = _cmsSaveProfile
_cmsSaveProfileToMem = _cmsSaveProfileToMem
cmsAdaptToIlluminant = cmsAdaptToIlluminant
cmsAllocGamma = cmsAllocGamma
cmsBuildGamma = cmsBuildGamma
cmsSmoothGamma = cmsSmoothGamma
cmsBuildRGB2XYZtransferMatrix= cmsBuildRGB2XYZtransferMatrix
cmsCloseProfile = cmsCloseProfile
cmsCreateProofingTransform = cmsCreateProofingTransform
cmsCreateRGBProfile = cmsCreateRGBProfile
cmsCreateTransform = cmsCreateTransform
cmsDeleteTransform = cmsDeleteTransform
cmsDoTransform = cmsDoTransform
cmsErrorAction = cmsErrorAction
cmsFreeGamma = cmsFreeGamma
cmsGetAlarmCodes = cmsGetAlarmCodes
cmsGetColorSpace = cmsGetColorSpace
cmsGetDeviceClass = cmsGetDeviceClass
cmsGetPCS = cmsGetPCS
cmsIsIntentSupported = cmsIsIntentSupported
cmsIsTag = cmsIsTag
cmsJoinGamma = cmsJoinGamma
cmsJoinGammaEx = cmsJoinGammaEx
cmsOpenProfileFromFile = cmsOpenProfileFromFile
cmsOpenProfileFromMem = cmsOpenProfileFromMem
cmsReverseGamma = cmsReverseGamma
cmsSetAlarmCodes = cmsSetAlarmCodes
cmsTakeColorants = cmsTakeColorants
cmsTakeIluminant = cmsTakeIluminant
cmsTakeMediaBlackPoint = cmsTakeMediaBlackPoint
cmsTakeMediaWhitePoint = cmsTakeMediaWhitePoint
cmsTakeProductDesc = cmsTakeProductDesc
cmsTakeProductInfo = cmsTakeProductInfo
cmsTakeProductName = cmsTakeProductName
cmsTakeRenderingIntent = cmsTakeRenderingIntent
cmsWhitePointFromTemp = cmsWhitePointFromTemp
cmsXYZ2xyY = cmsXYZ2xyY
cmsxyY2XYZ = cmsxyY2XYZ
cmsLCh2Lab = cmsLCh2Lab
cmsLab2LCh = cmsLab2LCh
cmsLab2XYZ = cmsLab2XYZ
cmsXYZ2Lab = cmsXYZ2Lab
cmsClampLab = cmsClampLab
cmsCreateLabProfile = cmsCreateLabProfile
cmsCreateXYZProfile = cmsCreateXYZProfile
cmsCreate_sRGBProfile = cmsCreate_sRGBProfile
cmsD50_XYZ = cmsD50_XYZ
cmsD50_xyY = cmsD50_xyY
cmsDeltaE = cmsDeltaE
cmsCIE94DeltaE = cmsCIE94DeltaE
cmsBFDdeltaE = cmsBFDdeltaE
cmsCMCdeltaE = cmsCMCdeltaE
cmsCIE2000DeltaE = cmsCIE2000DeltaE
cmsFloat2LabEncoded = cmsFloat2LabEncoded
cmsFloat2XYZEncoded = cmsFloat2XYZEncoded
cmsLabEncoded2Float = cmsLabEncoded2Float
cmsXYZEncoded2Float = cmsXYZEncoded2Float
cmsBuildParametricGamma = cmsBuildParametricGamma
cmsCIECAM97sInit = cmsCIECAM97sInit
cmsCIECAM97sDone = cmsCIECAM97sDone
cmsCIECAM97sForward = cmsCIECAM97sForward
cmsCIECAM97sReverse = cmsCIECAM97sReverse
cmsCIECAM02Init = cmsCIECAM02Init
cmsCIECAM02Done = cmsCIECAM02Done
cmsCIECAM02Forward = cmsCIECAM02Forward
cmsCIECAM02Reverse = cmsCIECAM02Reverse
cmsCreateMultiprofileTransform = cmsCreateMultiprofileTransform
cmsAddTag = cmsAddTag
cmsAllocLUT = cmsAllocLUT
cmsAllocLinearTable = cmsAllocLinearTable
cmsAlloc3DGrid = cmsAlloc3DGrid
cmsFreeLUT = cmsFreeLUT
cmsEvalLUT = cmsEvalLUT
cmsReadICCLut = cmsReadICCLut
cmsSample3DGrid = cmsSample3DGrid
cmsSetMatrixLUT = cmsSetMatrixLUT
cmsSetMatrixLUT4 = cmsSetMatrixLUT4
cmsDupGamma = cmsDupGamma
cmsReadICCGamma = cmsReadICCGamma
cmsReadICCGammaReversed = cmsReadICCGammaReversed
cmsSetErrorHandler = cmsSetErrorHandler
cmsChangeBuffersFormat = cmsChangeBuffersFormat
cmsCreateGrayProfile = cmsCreateGrayProfile
cmsCreateInkLimitingDeviceLink = cmsCreateInkLimitingDeviceLink
cmsCreateLinearizationDeviceLink = cmsCreateLinearizationDeviceLink
cmsEstimateGamma = cmsEstimateGamma
cmsEstimateGammaEx = cmsEstimateGammaEx
cmsNamedColorCount = cmsNamedColorCount
cmsNamedColorInfo = cmsNamedColorInfo
cmsNamedColorIndex = cmsNamedColorIndex
cmsSetColorSpace = cmsSetColorSpace
cmsSetDeviceClass = cmsSetDeviceClass
cmsSetLanguage = cmsSetLanguage
cmsSetPCS = cmsSetPCS
cmsTakeCharTargetData = cmsTakeCharTargetData
cmsTransform2DeviceLink = cmsTransform2DeviceLink
_cmsChannelsOf = _cmsChannelsOf
cmsFreeGammaTriple = cmsFreeGammaTriple
cmsSetRenderingIntent = cmsSetRenderingIntent
cmsDupLUT = cmsDupLUT
cmsGetUserFormatters = cmsGetUserFormatters
cmsSetUserFormatters = cmsSetUserFormatters
cmsCreateBCHSWabstractProfile = cmsCreateBCHSWabstractProfile
cmsGetPostScriptCSA = cmsGetPostScriptCSA
cmsGetPostScriptCRD = cmsGetPostScriptCRD
cmsGetPostScriptCRDEx = cmsGetPostScriptCRDEx
cmsReadProfileSequenceDescription = cmsReadProfileSequenceDescription
cmsTakeManufacturer = cmsTakeManufacturer
cmsTakeModel = cmsTakeModel
cmsSetProfileID = cmsSetProfileID
cmsTakeProfileID = cmsTakeProfileID
cmsSetHeaderFlags = cmsSetHeaderFlags
cmsTakeHeaderFlags = cmsTakeHeaderFlags
cmsTakeCopyright = cmsTakeCopyright
_cmsSetLUTdepth = _cmsSetLUTdepth
_cmsAddXYZTag = _cmsAddXYZTag
_cmsAddLUTTag = _cmsAddLUTTag
_cmsAddTextTag = _cmsAddTextTag
_cmsAddGammaTag = _cmsAddGammaTag
_cmsAddChromaticityTag = _cmsAddChromaticityTag
_cmsAddSequenceDescriptionTag = _cmsAddSequenceDescriptionTag
_cmsAddNamedColorTag = _cmsAddNamedColorTag
_cmsLCMScolorSpace = _cmsLCMScolorSpace
cmsFloat2LabEncoded4 = cmsFloat2LabEncoded4
cmsLabEncoded2Float4 = cmsLabEncoded2Float4
cmsGetProfileICCversion = cmsGetProfileICCversion
cmsIT8Alloc = cmsIT8Alloc
cmsIT8Free = cmsIT8Free
cmsIT8TableCount = cmsIT8TableCount
cmsIT8SetTable = cmsIT8SetTable
cmsIT8LoadFromFile = cmsIT8LoadFromFile
cmsIT8LoadFromMem = cmsIT8LoadFromMem
cmsIT8SaveToFile = cmsIT8SaveToFile
cmsIT8GetSheetType = cmsIT8GetSheetType
cmsIT8SetSheetType = cmsIT8SetSheetType
cmsIT8SetComment = cmsIT8SetComment
cmsIT8SetPropertyStr = cmsIT8SetPropertyStr
cmsIT8SetPropertyDbl = cmsIT8SetPropertyDbl
cmsIT8SetPropertyHex = cmsIT8SetPropertyHex
cmsIT8SetPropertyUncooked = cmsIT8SetPropertyUncooked
cmsIT8GetProperty = cmsIT8GetProperty
cmsIT8GetPropertyDbl = cmsIT8GetPropertyDbl
cmsIT8EnumProperties = cmsIT8EnumProperties
cmsIT8GetDataRowCol = cmsIT8GetDataRowCol
cmsIT8GetDataRowColDbl = cmsIT8GetDataRowColDbl
cmsIT8SetDataRowCol = cmsIT8SetDataRowCol
cmsIT8SetDataRowColDbl = cmsIT8SetDataRowColDbl
cmsIT8GetData = cmsIT8GetData
cmsIT8GetDataDbl = cmsIT8GetDataDbl
cmsIT8SetData = cmsIT8SetData
cmsIT8SetDataDbl = cmsIT8SetDataDbl
cmsIT8SetDataFormat = cmsIT8SetDataFormat
cmsIT8EnumDataFormat = cmsIT8EnumDataFormat
cmsIT8GetPatchName = cmsIT8GetPatchName
cmsIT8SetTableByLabel = cmsIT8SetTableByLabel
cmsReadICCText = cmsReadICCText
cmsReadICCTextEx = cmsReadICCTextEx
cmsCreateLab4Profile = cmsCreateLab4Profile
cmsCreateNULLProfile = cmsCreateNULLProfile
cmsIT8DefineDblFormat = cmsIT8DefineDblFormat
cmsIT8GetDataFormat = cmsIT8GetDataFormat
cmsSetProfileICCversion = cmsSetProfileICCversion
cmsTakeCalibrationDateTime = cmsTakeCalibrationDateTime
cmsTakeCreationDateTime = cmsTakeCreationDateTime
_cmsIsMatrixShaper = _cmsIsMatrixShaper
_cmsAddColorantTableTag = _cmsAddColorantTableTag
_cmsAddDateTimeTag = _cmsAddDateTimeTag
cmsEvalLUTreverse = cmsEvalLUTreverse
cmsGetTagCount = cmsGetTagCount
cmsGetTagSignature = cmsGetTagSignature
cmsIT8SaveToMem = cmsIT8SaveToMem
cmsReadColorantTable = cmsReadColorantTable
cmsSetAdaptationState = cmsSetAdaptationState
cmsSetHeaderAttributes = cmsSetHeaderAttributes
cmsTakeHeaderAttributes = cmsTakeHeaderAttributes
cmsSetCMYKPreservationStrategy = cmsSetCMYKPreservationStrategy
cmsFreeProfileSequenceDescription = cmsFreeProfileSequenceDescription
_cmsAddChromaticAdaptationTag = _cmsAddChromaticAdaptationTag