summaryrefslogtreecommitdiff
path: root/icc
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-09-01 13:56:46 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-09-01 13:56:46 +0200
commit22f703cab05b7cd368f4de9e03991b7664dc5022 (patch)
tree6f4d50beaa42328e24b1c6b56b6ec059e4ef21a5 /icc
Initial import of argyll version 1.5.1-8debian/1.5.1-8
Diffstat (limited to 'icc')
-rw-r--r--icc/ClayRGB1998.icmbin0 -> 580 bytes
-rw-r--r--icc/Jamfile58
-rw-r--r--icc/License.txt22
-rw-r--r--icc/Makefile82
-rw-r--r--icc/Makefile.IBMNT41
-rw-r--r--icc/Makefile.OSX38
-rw-r--r--icc/Makefile.UNIX38
-rw-r--r--icc/Makefile.WNT38
-rw-r--r--icc/Makefile.am22
-rw-r--r--icc/Readme.txt150
-rw-r--r--icc/afiles26
-rw-r--r--icc/icc.c17838
-rw-r--r--icc/icc.h1992
-rw-r--r--icc/iccV42.h555
-rw-r--r--icc/iccdump.c269
-rw-r--r--icc/icclu.c424
-rw-r--r--icc/iccrw.c311
-rw-r--r--icc/iccstd.c443
-rw-r--r--icc/icctest.c2393
-rw-r--r--icc/lab2lab.icmbin0 -> 500 bytes
-rw-r--r--icc/log.txt173
-rw-r--r--icc/lutest.c3506
-rw-r--r--icc/makezip.ksh6
-rw-r--r--icc/mcheck.c541
-rw-r--r--icc/sRGB.icmbin0 -> 3212 bytes
-rw-r--r--icc/testDE2K.c226
-rw-r--r--icc/todo.txt197
27 files changed, 29389 insertions, 0 deletions
diff --git a/icc/ClayRGB1998.icm b/icc/ClayRGB1998.icm
new file mode 100644
index 0000000..1eedf09
--- /dev/null
+++ b/icc/ClayRGB1998.icm
Binary files differ
diff --git a/icc/Jamfile b/icc/Jamfile
new file mode 100644
index 0000000..abbeddb
--- /dev/null
+++ b/icc/Jamfile
@@ -0,0 +1,58 @@
+
+# JAM style makefile for icclib and friends
+
+#PREF_CCFLAGS = $(CCOPTFLAG) ; # Turn optimisation on
+PREF_CCFLAGS = $(CCDEBUGFLAG) ; # Debugging flags
+#PREF_CCFLAGS = $(CCHEAPDEBUG) ; # Heap Debugging flags
+PREF_LINKFLAGS = $(LINKDEBUGFLAG) ; # Link debugging flags
+
+#if stdio is not wanted in icclib:
+#DEFINES = SEPARATE_STD ;
+
+#Products
+Libraries = libicc ;
+Executables = iccdump icclu ;
+Headers = icc.h ;
+Samples = sRGB.icm ClayRGB1998.icm lab2lab.icm ;
+
+#Install
+InstallBin $(DESTDIR)$(PREFIX)/bin : $(Executables) ;
+#InstallFile $(DESTDIR)$(PREFIX)/h : $(Headers) ;
+#InstallLib $(DESTDIR)$(PREFIX)/lib : $(Libraries) ;
+InstallFile $(DESTDIR)$(PREFIX)/$(REFSUBDIR) : $(Samples) ;
+
+# ICC library
+Library libicc : icc.c ;
+
+# Executable support if SEPARATED_STD
+if SEPARATE_STD in $(DEFINES) {
+ Objects iccstd.c ;
+ LINKOBJS = iccstd ; # Link all utilities here with iccstd
+}
+
+# Link all utilities here with libicc
+LINKLIBS = libicc ;
+
+# All utils are made from a single source file
+MainsFromSources icctest.c lutest.c iccdump.c icclu.c iccrw.c ;
+
+if $(BUILD_JUNK) {
+
+# MainsFromSources tt.c ;
+
+ MainsFromSources mksRGB.c ;
+ MainsFromSources mkAdobeRGB.c ;
+ MainsFromSources mklab2lab.c ;
+# MainsFromSources icm2ary.c ;
+
+ # Check library is compatible with C++
+ Main cppcheck : cppcheck.cpp ;
+
+ # chech CIEDE2000
+ MainsFromSources testDE2K.c ;
+
+ #Monotonic behaviour checker
+ MainsFromSources mcheck.c ;
+}
+
+
diff --git a/icc/License.txt b/icc/License.txt
new file mode 100644
index 0000000..9de79da
--- /dev/null
+++ b/icc/License.txt
@@ -0,0 +1,22 @@
+*************************************************************************
+Copyright (c) 1997-2009 Graeme W. Gill
+
+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.
+
+*************************************************************************
diff --git a/icc/Makefile b/icc/Makefile
new file mode 100644
index 0000000..dbb9992
--- /dev/null
+++ b/icc/Makefile
@@ -0,0 +1,82 @@
+# UNIX style makefile, for icclib and friends.
+# "include" the right environment for your system,
+# by uncommenting the appropriate line:
+
+# Microsoft C++, WinNT setup
+include Makefile.WNT
+
+# IBM C++, WinNT setup
+#include Makefile.IBMNT
+
+# Generic UNIX setup
+#include Makefile.UNIX
+
+# Apple OSX
+#include Makefile.OSX
+
+###############################
+
+#Compile with separate stdio
+#CCDEFINES = $(DEFFLAG)SEPARATE_STD
+
+#Set optimisation on
+CCFLAGS = $(CCFLAGSDEF) $(CCOPTFLAG) $(CCDEFINES)
+
+#Set debugging on
+#CCFLAGS = $(CCFLAGSDEF) $(CCDEBUGFLAG) $(CCDEFINES)
+
+STDHDRS = $(STDHDRSDEF)
+LINKFLAGS = $(LINKFLAGSDEF) $(LINKDEBUGFLAG)
+
+all:: libicc$(SUFLIB) icctest$(SUFEXE) lutest$(SUFEXE) icclu$(SUFEXE) iccdump$(SUFEXE) iccrw$(SUFEXE)
+
+
+icc$(SUFOBJ): icc.c icc.h
+ $(CC) $(CCOF)icc$(SUFOBJ) icc.c
+
+libicc$(SUFLIB): icc$(SUFOBJ)
+ $(LIBU) $(LIBOF)libicc$(SUFLIB) icc$(SUFOBJ)
+ $(RANLIB) libicc$(SUFLIB)
+
+
+# Separate for executables
+iccstd$(SUFOBJ): iccstd.c icc.h
+ $(CC) $(CCOF)iccstd$(SUFOBJ) iccstd.c
+
+
+icctest$(SUFOBJ): icctest.c icc.h
+ $(CC) $(CCOF)icctest$(SUFOBJ) icctest.c
+
+icctest$(SUFEXE): icctest$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+ $(LINK) $(LINKOF)icctest$(SUFEXE) icctest$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+
+
+lutest$(SUFOBJ): lutest.c icc.h
+ $(CC) $(CCOF)lutest$(SUFOBJ) lutest.c
+
+lutest$(SUFEXE): lutest$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+ $(LINK) $(LINKOF)lutest$(SUFEXE) lutest$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+
+
+icclu$(SUFOBJ): icclu.c icc.h
+ $(CC) $(CCOF)icclu$(SUFOBJ) icclu.c
+
+icclu$(SUFEXE): icclu$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+ $(LINK) $(LINKOF)icclu$(SUFEXE) icclu$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+
+
+iccdump$(SUFOBJ): iccdump.c icc.h
+ $(CC) $(CCOF)iccdump$(SUFOBJ) iccdump.c
+
+iccdump$(SUFEXE): iccdump$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+ $(LINK) $(LINKOF)iccdump$(SUFEXE) iccdump$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+
+
+iccrw$(SUFOBJ): iccrw.c icc.h
+ $(CC) $(CCOF)iccrw$(SUFOBJ) iccrw.c
+
+iccrw$(SUFEXE): iccrw$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+ $(LINK) $(LINKOF)iccrw$(SUFEXE) iccrw$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB)
+
+
+
diff --git a/icc/Makefile.IBMNT b/icc/Makefile.IBMNT
new file mode 100644
index 0000000..26a2bd2
--- /dev/null
+++ b/icc/Makefile.IBMNT
@@ -0,0 +1,41 @@
+# IBM C++, WinNT setup
+
+SLASH = \
+SUFLIB = .lib
+SUFOBJ = .obj
+SUFEXE = .exe
+CMDSEP = &
+
+INCFLAG = /I
+DEFFLAG = /D
+UNDEFFLAG = "/U _"
+CCOPTFLAG = /O+ /qtune=pentiumpro
+CCDEBUGFLAG = /Ti+ /O- /Oi- /DEbug
+CCPROFFLAG = /Ti+ /Gh+
+LINKDEBUGFLAG = /DEbug
+LINKPROFFLAG = /DE /NOE cppwpa3.obj
+
+STDHDRSDEF = $(IPF_PATH32)\\include $(IPF_PATH32)\\sdk\\winh
+
+MAKEU = nmake
+LIBU = ilib /Q
+LIBOF = /OUT:
+RANLIB = rem
+AS = masm386
+CCFLAGSDEF = /DNT /c
+CC = icc /Q $(CCFLAGS) $(INCFLAG)$(STDHDRS)
+CCOF = /Fo
+LINKLIBS = $(IPF_PATH32)\\sdk\\lib\\*.lib
+LINKFLAGSDEF =
+LINK = ilink /NOLogo $(LINKFLAGS)
+LINKOF = /OUT:
+
+.SUFFIXES:
+.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE)
+
+.c$(SUFOBJ):
+ $(CC) $(CCOF)$*$(SUFOBJ) $<
+
+
+
+
diff --git a/icc/Makefile.OSX b/icc/Makefile.OSX
new file mode 100644
index 0000000..fb1c88f
--- /dev/null
+++ b/icc/Makefile.OSX
@@ -0,0 +1,38 @@
+# MAC OSX, derived from UNIX setup
+
+SLASH = /
+SUFLIB = .a
+SUFOBJ = .o
+SUFEXE =
+CMDSEP = ;
+
+INCFLAG = -I
+DEFFLAG = -D
+UNDEFFLAG = "-u _"
+CCOPTFLAG = -O
+CCDEBUGFLAG = -g
+CCPROFFLAG =
+LINKDEBUGFLAG =
+LINKPROFFLAG =
+
+STDHDRSDEF = /usr/include
+
+MAKEU = make
+LIBU = ar -r
+LIBOF =
+RANLIB = ranlib
+AS = as
+CCFLAGSDEF = -DUNIX -c
+CC = cc $(CCFLAGS) $(INCFLAG)$(STDHDRS)
+CCOF = -o
+LINKFLAGSDEF = -lm
+LINKLIBS =
+LINK = cc $(LINKFLAGS) $(LINKLIBS)
+LINKOF = -o
+
+.SUFFIXES:
+.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE)
+
+.c$(SUFOBJ):
+ $(CC) $(CCOF)$*$(SUFOBJ) $<
+
diff --git a/icc/Makefile.UNIX b/icc/Makefile.UNIX
new file mode 100644
index 0000000..91d6005
--- /dev/null
+++ b/icc/Makefile.UNIX
@@ -0,0 +1,38 @@
+# Generic UNIX setup
+
+SLASH = /
+SUFLIB = .a
+SUFOBJ = .o
+SUFEXE =
+CMDSEP = ;
+
+INCFLAG = -I
+DEFFLAG = -D
+UNDEFFLAG = "-u _"
+CCOPTFLAG = -O
+CCDEBUGFLAG = -g
+CCPROFFLAG =
+LINKDEBUGFLAG =
+LINKPROFFLAG =
+
+STDHDRSDEF = /usr/include
+
+MAKEU = make
+LIBU = ar -r
+LIBOF = -o
+RANLIB = echo
+AS = as
+CCFLAGSDEF = -DUNIX -c
+CC = cc $(CCFLAGS) $(INCFLAG)$(STDHDRS)
+CCOF = -o
+LINKFLAGSDEF = -lm
+LINKLIBS =
+LINK = cc $(LINKFLAGS) $(LINKLIBS)
+LINKOF = -o
+
+.SUFFIXES:
+.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE)
+
+.c$(SUFOBJ):
+ $(CC) $(CCOF)$*$(SUFOBJ) $<
+
diff --git a/icc/Makefile.WNT b/icc/Makefile.WNT
new file mode 100644
index 0000000..4ca291d
--- /dev/null
+++ b/icc/Makefile.WNT
@@ -0,0 +1,38 @@
+# Microsoft C++, WinNT setup
+
+SLASH = \
+SUFLIB = .lib
+SUFOBJ = .obj
+SUFEXE = .exe
+CMDSEP = &
+
+INCFLAG = /I
+DEFFLAG = /D
+UNDEFFLAG = "/u _"
+CCOPTFLAG = /Ox /GB
+CCDEBUGFLAG = /Z7 /Od
+CCPROFFLAG = /Z7
+LINKDEBUGFLAG = /DEBUG
+LINKPROFFLAG = /PROFILE
+
+STDHDRSDEF = $(MSVCNT)\include
+
+MAKEU = nmake
+LIBU = lib
+LIBOF = /OUT:
+RANLIB = rem
+AS = masm386
+CCFLAGSDEF = /DNT /c
+CC = cl /nologo $(CCFLAGS) $(INCFLAG)$(STDHDRS)
+CCOF = /Fo
+LINKLIBS = $(MSVCNT)/lib/user32.lib $(MSVCNT)/lib/gdi32.lib
+LINKFLAGSDEF = /link /INCREMENTAL:NO
+LINK = link $(LINKFLAGS)
+LINKOF = /OUT:
+
+.SUFFIXES:
+.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE)
+
+.c$(SUFOBJ):
+ $(CC) $(CCOF)$*$(SUFOBJ) $<
+
diff --git a/icc/Makefile.am b/icc/Makefile.am
new file mode 100644
index 0000000..a9ae092
--- /dev/null
+++ b/icc/Makefile.am
@@ -0,0 +1,22 @@
+include $(top_srcdir)/Makefile.shared
+
+LIBICC_VERSION=2.12
+
+lib_LTLIBRARIES = libicc.la
+
+libicc_la_SOURCES = icc.h iccV42.h icc.c iccstd.c
+libicc_la_LDFLAGS = -version-number $(shell echo $(LIBICC_VERSION) | tr . :):0
+
+include_HEADERS = icc.h iccV42.h
+
+LDADD = libicc.la
+
+bin_PROGRAMS = iccdump icclu
+
+check_PROGRAMS = icctest iccrw lutest
+
+refdir = $(datadir)/color/argyll/ref
+
+ref_DATA = $(wildcard *.icm)
+
+EXTRA_DIST = License.txt Readme.txt
diff --git a/icc/Readme.txt b/icc/Readme.txt
new file mode 100644
index 0000000..0ec425e
--- /dev/null
+++ b/icc/Readme.txt
@@ -0,0 +1,150 @@
+ICC profile I/O library (icclib), README file
+---------------------------------------------
+
+This version is part of Argyll V 1.50
+
+-------------------------------------------
+
+Date 22 September 2012, Version 2.14
+
+This distribution contains source code which implements the reading and
+writing of color profile files that conform to the International Color
+Consortium (ICC) Profile Format Specification, Version 3.4.
+
+For more information about the ICC, and for copies of the specification,
+please refer to <http://www.color.org/>
+
+(Note that this software is written from the ICC V3.4 standard, but the
+software and its author are not affiliated with, or otherwise connected
+with the ICC.)
+
+The ICC profile I/O library archive is kept at <http://www.argyllcms.com/>
+
+Motivation
+
+Color is still very much a black art to many programmers dealing with
+computer graphics. The ICC Profile Format is an industry attempt to provide
+an interchange format to help solve the problems of specifying color, and
+in transferring color graphics from, and between systems and devices.
+Although the ICC format has been around a number of years, and has long
+been adopted by companies in the business of providing systems for
+publishing and printing, and is now widely used as part of commercial
+operating system support for device independent color, its uptake in the
+general world of computer graphics has been slow.
+
+The writing of this library was prompted by my private and professional
+enthusiasm for computer graphics, and color. Inspired by other examples of
+freely usable software (notably the Independent JPEG Group's free JPEG
+software, and Sam Leffler's TIFF library), I have decided to make this
+library available under similar terms. I hope that this library will
+provide a starting point for including ICC profile support more widely that
+is currently the case, particularly in open source code projects.
+
+Overview
+
+This package contains a C software implementation of the ICC Profile
+Format, Version 3.4. The ICC Profile Format attempts to provide a
+cross-platform device profile format, that can be used to translate color
+data created on one device into another device's native color space. For a
+fuller explanation of what the ICC Profile Format is all about, please
+refer to http://www.color.org, and the profile specification.
+
+In summary this library provides:
+
+ * Full source code, free for commercial and non-commercial use.
+ * Support for all version 3.4 header elements, Tags and Tag Types.
+ * Conversion to/from machine native representation of all data
+ types.
+ * Support for user defined Tags.
+ * Support for adding/deleting Tags.
+ * Support for Tag type sharing within a file (often used for
+ sharing LUTs amongst intents).
+ * Support for reading/writing embedded profiles, including from/to
+ a memory buffer, rather than a file.
+ * Provides a single function for transforming color values through
+ a profile, including support for intents, forward and reverse
+ transforms, gamut lookup or preview lookup.
+ * Provides support and code examples for creating all profile
+ types, monochrome, matrix and Lut.
+ * Attempts to be platform neutral, and flexibility in its use of
+ system file and memory sub-systems.
+ * Loads Tag Types on demand to conserve memory space.
+
+Changes from V2.11
+
+Many changes have been made to support ArgyllCMS.
+A double memory free bug when iccdump'ing a profile
+that has duplicate tags has been fixed.
+
+Package contents:
+
+ icclib.zip ZIP archive of the following files
+ README.txt This file.
+ License.txt Important! - Permissions for use of this package.
+ icc.c Library source file.
+ iccstd.c Library source that uses stdio and malloc system calls.
+
+ icc.h Library include file. Note machine dependent defines.
+ Includes iccV42.h.
+ iccV42.h Standard ICC header file modified up to Version 4.2 spec.
+ iccdump.c Program that dumps ASCII description of a profile.
+
+ icclu.c Program that allows interactive or batch translation of
+ color values though a profile.
+
+ icctest.c Basic library tag Read/Write example and regression test
+ code.
+
+ lutest.c Color lookup regression test code, and example for creating
+ color profiles.
+
+ iccrw.c Source code skeleton for reading and then re-writing a
+ profile.
+
+ Jamfile JAM style "makefile" see
+ http://www.perforce.com/jam/jam.html
+
+ Makefile Generic style makefile. Edit this to select the appropriate
+ system makefile rules.
+ Makefile.IBMNTMakefile ruleset for Windows NT using the IBM compiler.
+ Makefile.OSX Makefile ruleset for Mac OSX environment.
+ Makefile.UNIX Makefile ruleset for Linux style environment.
+
+ Makefile.WNT Makefile ruleset for Windows NT using the Microsoft Visual
+ C++ compiler.
+
+Style
+
+For handling convenience, I have included all the library source code in
+two files. The down side is that they are both hard to read and navigate
+through. The code could do with some cleaning up and rearrangement, to make
+clear the distinction between public and private elements. (C++ would help
+here, but is less portable.) The code attempts to be ANSI C compliant,
+written in an object oriented style. A tutorial on how to use the library
+would also be a good thing !
+
+The best way to learn how to use the library, is to take a look at
+icctest.c, lutest.c and iccrw.c. The first is used to test writing and
+reading to every type of element, with every possible variation of usage.
+You will need a copy of the ICC spec. handy to understand what it all
+means. The second source file specifically creates and then tests various
+types of profiles, including monochrome, matrix and Lut style profiles. The
+last is a source code skeleton, that reads a profile completely into
+memory, and then writes it out again to a different file.
+
+With the release of version 2.00 of icclib, the library is now as useful as
+it is likely to be, allowing convenient color conversion between PCS
+(profile connection spaces, either XYZ or Lab) and device specific color
+spaces. The library does not attempt to be a complete color management
+system however, lacking profile creation and linking functionality. Icclib
+is part of the Argyll CMS, which does attempt to be a complete color
+management system. Argyll can be used as a more realistic example of the
+use of icclib. I plan at some stage to upgrade icclib to be compatible with
+the recently released ICC V4 spec., but will probably get around to this
+sometime after the next major release of Argyll.
+
+I welcome feedback, positive or negative, so please mail me at
+
+ Graeme at argyllcms dot com
+
+Graeme Gill
diff --git a/icc/afiles b/icc/afiles
new file mode 100644
index 0000000..7df3ba5
--- /dev/null
+++ b/icc/afiles
@@ -0,0 +1,26 @@
+Readme.txt
+License.txt
+todo.txt
+log.txt
+Jamfile
+Makefile
+Makefile.WNT
+Makefile.IBMNT
+Makefile.UNIX
+Makefile.OSX
+afiles
+icc.c
+iccstd.c
+icc.h
+iccV42.h
+iccdump.c
+icclu.c
+iccrw.c
+icctest.c
+lutest.c
+mcheck.c
+testDE2K.c
+makezip.ksh
+sRGB.icm
+ClayRGB1998.icm
+lab2lab.icm
diff --git a/icc/icc.c b/icc/icc.c
new file mode 100644
index 0000000..95dc0e9
--- /dev/null
+++ b/icc/icc.c
@@ -0,0 +1,17838 @@
+
+/*
+ * International Color Consortium Format Library (icclib)
+ * For ICC profile version 3.4
+ *
+ * Author: Graeme W. Gill
+ * Date: 2002/04/22
+ * Version: 2.15
+ *
+ * Copyright 1997 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/*
+ * TTBD:
+ *
+ * Add a "warning mode" to file reading, in which file format
+ * errors are ignored where possible, rather than generating
+ * a fatal error (see ICM_STRICT #define).
+ *
+ * NameColor Dump doesn't handle device space correctly -
+ * should use appropriate interpretation in case device is Lab etc.
+ *
+ * Should recognise & honour unicode 0xFFFE endian marker.
+ * Should generate it on writing too ?
+ *
+ * Add support for copying tags from one icc to another.
+ *
+ * Should fix all write_number failure errors to indicate failed value.
+ * (Partially implemented - need to check all write_number functions)
+ *
+ * Make write fail error messages be specific on which element failed.
+ *
+ * Should add named color space lookup function support.
+ *
+ * Would be nice to add generic ability to add new tag type handling,
+ * so that the base library doesn't need to be modified (ie. VideoCardGamma) ?
+ *
+ * Need to add DeviceSettings and OutputResponse tags to bring up to
+ * ICC.1:1998-09 [started but not complete]
+ *
+ */
+
+#undef ICM_STRICT /* Not fully implimented - switch off strict checking of file format */
+
+/* Make the default grid points of the Lab clut be symetrical about */
+/* a/b 0.0, and also make L = 100.0 fall on a grid point. */
+#define SYMETRICAL_DEFAULT_LAB_RANGE
+
+#define _ICC_C_ /* Turn on implimentation code */
+
+#undef DEBUG_SETLUT /* Show each value being set in setting lut contents */
+#undef DEBUG_SETLUT_CLIP /* Show clipped values when setting LUT */
+#undef DEBUG_LULUT /* Show each value being looked up from lut contents */
+#undef DEBUG_LLULUT /* Debug individual lookup steps (not fully implemented) */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <time.h>
+#ifdef __sun
+#include <unistd.h>
+#endif
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+#include "icc.h"
+
+#ifdef _MSC_VER
+#define vsnprintf _vsnprintf
+#define snprintf _snprintf
+#endif
+
+/* ========================================================== */
+/* Default system interface object implementations */
+
+#ifndef SEPARATE_STD
+#define COMBINED_STD
+
+#include "iccstd.c"
+
+#undef COMBINED_STD
+#endif /* SEPARATE_STD */
+
+/* Forced byte alignment for tag table and tags */
+#define ALIGN_SIZE 4
+
+/* =========================================================== */
+
+#ifdef DEBUG_SETLUT
+#undef DBGSL
+#define DBGSL(xxx) printf xxx ;
+#else
+#undef DBGSL
+#define DBGSL(xxx)
+#endif
+
+#if defined(DEBUG_SETLUT) || defined(DEBUG_SETLUT_CLIP)
+#undef DBGSLC
+#define DBGSLC(xxx) printf xxx ;
+#else
+#undef DBGSLC
+#define DBGSLC(xxx)
+#endif
+
+#ifdef DEBUG_LULUT
+#undef DBGLL
+#define DBGLL(xxx) printf xxx ;
+#else
+#undef DBGLL
+#define DBGLL(xxx)
+#endif
+
+#ifdef DEBUG_LLULUT
+#undef DBLLL
+#define DBLLL(xxx) printf xxx ;
+#else
+#undef DBLLL
+#define DBLLL(xxx)
+#endif
+
+/* =========================================================== */
+/* Overflow protected unsigned int arithmatic functions. */
+/* These functions saturate rather than wrapping around. */
+/* (Divide doesn't need protection) */
+/* They return UINT_MAX if there was an overflow */
+
+/* a + b */
+static unsigned int sat_add(unsigned int a, unsigned int b) {
+ if (b > (UINT_MAX - a))
+ return UINT_MAX;
+ return a + b;
+}
+
+/* a - b */
+static unsigned int sat_sub(unsigned int a, unsigned int b) {
+ if (a < b)
+ return UINT_MAX;
+ return a - b;
+}
+
+/* a * b */
+static unsigned int sat_mul(unsigned int a, unsigned int b) {
+ unsigned int c;
+
+ if (a == 0 || b == 0)
+ return 0;
+
+ if (a > (UINT_MAX/b))
+ return UINT_MAX;
+ else
+ return a * b;
+}
+
+/* A + B + C */
+#define sat_addadd(A, B, C) sat_add(A, sat_add(B, C))
+
+/* A + B * C */
+#define sat_addmul(A, B, C) sat_add(A, sat_mul(B, C))
+
+/* A + B + C * D */
+#define sat_addaddmul(A, B, C, D) sat_add(A, sat_add(B, sat_mul(C, D)))
+
+/* A * B * C */
+#define sat_mul3(A, B, C) sat_mul(A, sat_mul(B, C))
+
+/* a ^ b */
+static unsigned int sat_pow(unsigned int a, unsigned int b) {
+ unsigned int c = 1;
+ for (; b > 0; b--) {
+ c = sat_mul(c, a);
+ if (c == UINT_MAX)
+ break;
+ }
+ return c;
+}
+
+/* Alignment */
+static unsigned int sat_align(unsigned int align_size, unsigned int a) {
+ align_size--;
+
+ if (align_size > (UINT_MAX - a))
+ return UINT_MAX;
+
+ return (a + align_size) & ~align_size;
+}
+
+/* These test functions detect whether an overflow would occur */
+
+/* Return nz if add would overflow */
+static int ovr_add(unsigned int a, unsigned int b) {
+
+ if (b > (UINT_MAX - a))
+ return 1;
+ return 0;
+}
+
+/* Return nz if sub would overflow */
+static int ovr_sub(unsigned int a, unsigned int b) {
+ if (a < b)
+ return 1;
+ return 0;
+}
+
+/* Return nz if mult would overflow */
+static int ovr_mul(unsigned int a, unsigned int b) {
+ if (a > (UINT_MAX/b))
+ return 1;
+ return 0;
+}
+
+
+/* size_t versions of saturating arithmatic */
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t)(-1))
+#endif
+
+/* a + b */
+static size_t ssat_add(size_t a, size_t b) {
+ if (b > (SIZE_MAX - a))
+ return SIZE_MAX;
+ return a + b;
+}
+
+/* a - b */
+static size_t ssat_sub(size_t a, size_t b) {
+ if (a < b)
+ return SIZE_MAX;
+ return a - b;
+}
+
+/* a * b */
+static size_t ssat_mul(size_t a, size_t b) {
+ size_t c;
+
+ if (a == 0 || b == 0)
+ return 0;
+
+ if (a > (SIZE_MAX/b))
+ return SIZE_MAX;
+ else
+ return a * b;
+}
+
+/* ------------------------------------------------- */
+/* Memory image icmFile compatible class */
+/* Buffer is assumed to have been allocated by the given allocator, */
+/* and will be expanded on write. */
+
+/* Get the size of the file */
+static size_t icmFileMem_get_size(icmFile *pp) {
+ icmFileMem *p = (icmFileMem *)pp;
+
+ return p->end - p->start;
+}
+
+/* Set current position to offset. Return 0 on success, nz on failure. */
+static int icmFileMem_seek(
+icmFile *pp,
+unsigned int offset
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ unsigned char *np;
+
+ np = p->start + offset;
+ if (np < p->start || np >= p->end)
+ return 1;
+ p->cur = np;
+ return 0;
+}
+
+/* Read count items of size length. Return number of items successfully read. */
+static size_t icmFileMem_read(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ size_t len;
+
+ len = ssat_mul(size, count);
+ if (len > (p->end - p->cur)) { /* Too much */
+ if (size > 0)
+ count = (p->end - p->cur)/size;
+ else
+ count = 0;
+ }
+ len = size * count;
+ if (len > 0)
+ memmove(buffer, p->cur, len);
+ p->cur += len;
+ return count;
+}
+
+/* Expand the memory buffer file to hold up to pointer ep */
+/* Don't expand if realloc fails */
+static void icmFileMem_filemem_resize(icmFileMem *p, unsigned char *ep) {
+ size_t na, co, ce;
+ unsigned char *nstart;
+
+ /* No need to realloc */
+ if (ep <= p->aend) {
+ return;
+ }
+
+ co = p->cur - p->start; /* Current offset */
+ ce = p->end - p->start; /* Current end */
+ na = ep - p->start; /* new allocated size */
+
+ /* Round new allocation up */
+ if (na <= 1024)
+ na += 1024;
+ else
+ na += 4096;
+
+ if ((nstart = p->al->realloc(p->al, p->start, na)) != NULL) {
+ p->start = nstart;
+ p->cur = nstart + co;
+ p->end = nstart + ce;
+ p->aend = nstart + na;
+ }
+}
+
+/* write count items of size length. Return number of items successfully written. */
+static size_t icmFileMem_write(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ size_t len;
+
+ len = ssat_mul(size, count);
+ if (len > (size_t)(p->aend - p->cur)) /* Try and expand buffer */
+ icmFileMem_filemem_resize(p, p->start + len);
+
+ if (len > (size_t)(p->aend - p->cur)) {
+ if (size > 0)
+ count = (p->aend - p->cur)/size;
+ else
+ count = 0;
+ }
+ len = size * count;
+ if (len > 0)
+ memmove(p->cur, buffer, len);
+ p->cur += len;
+ if (p->end < p->cur)
+ p->end = p->cur;
+ return count;
+}
+
+/* do a printf */
+static int icmFileMem_printf(
+icmFile *pp,
+const char *format,
+...
+) {
+ int rv;
+ va_list args;
+ icmFileMem *p = (icmFileMem *)pp;
+ int len;
+
+ va_start(args, format);
+
+ rv = 1;
+ len = 100; /* Initial allocation for printf */
+ icmFileMem_filemem_resize(p, p->cur + len);
+
+ /* We have to use the available printf functions to resize the buffer if needed. */
+ for (;rv != 0;) {
+ /* vsnprintf() either returns -1 if it doesn't fit, or */
+ /* returns the size-1 needed in order to fit. */
+ len = vsnprintf((char *)p->cur, (p->aend - p->cur), format, args);
+
+ if (len > -1 && ((p->cur + len +1) <= p->aend)) /* Fitted in current allocation */
+ break;
+
+ if (len > -1) /* vsnprintf returned needed size-1 */
+ len = len+2; /* (In case vsnprintf returned 1 less than it needs) */
+ else
+ len *= 2; /* We just have to guess */
+
+ /* Attempt to resize */
+ icmFileMem_filemem_resize(p, p->cur + len);
+
+ /* If resize failed */
+ if ((p->aend - p->cur) < len) {
+ rv = 0;
+ break;
+ }
+ }
+ if (rv != 0) {
+ /* Figure out where end of printf is */
+ len = strlen((char *)p->cur); /* Length excluding nul */
+ p->cur += len;
+ if (p->cur > p->end)
+ p->end = p->cur;
+ rv = len;
+ }
+ va_end(args);
+ return rv;
+}
+
+/* flush all write data out to secondary storage. Return nz on failure. */
+static int icmFileMem_flush(
+icmFile *pp
+) {
+ return 0;
+}
+
+/* Return the memory buffer. Error if not icmFileMem */
+static int icmFileMem_get_buf(
+icmFile *pp,
+unsigned char **buf,
+size_t *len
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ if (buf != NULL)
+ *buf = p->start;
+ if (len != NULL)
+ *len = p->end - p->start;
+ return 0;
+}
+
+/* we're done with the file object, return nz on failure */
+static int icmFileMem_delete(
+icmFile *pp
+) {
+ icmFileMem *p = (icmFileMem *)pp;
+ icmAlloc *al = p->al;
+ int del_al = p->del_al;
+
+ if (p->del_buf) /* Free the memory buffer */
+ al->free(al, p->start);
+ al->free(al, p); /* Free object */
+ if (del_al) /* We are responsible for deleting allocator */
+ al->del(al);
+ return 0;
+}
+
+/* Create a memory image file access class with allocator */
+/* Buffer is used as is. */
+icmFile *new_icmFileMem_a(
+void *base, /* Pointer to base of memory buffer */
+size_t length, /* Number of bytes in buffer */
+icmAlloc *al /* heap allocator */
+) {
+ icmFileMem *p;
+
+ if ((p = (icmFileMem *) al->calloc(al, 1, sizeof(icmFileMem))) == NULL) {
+ return NULL;
+ }
+ p->al = al; /* Heap allocator */
+ p->get_size = icmFileMem_get_size;
+ p->seek = icmFileMem_seek;
+ p->read = icmFileMem_read;
+ p->write = icmFileMem_write;
+ p->gprintf = icmFileMem_printf;
+ p->flush = icmFileMem_flush;
+ p->get_buf = icmFileMem_get_buf;
+ p->del = icmFileMem_delete;
+
+ p->start = (unsigned char *)base;
+ p->cur = p->start;
+ p->aend = p->end = p->start + length;
+
+ return (icmFile *)p;
+}
+
+/* Create a memory image file access class with given allocator */
+/* and delete base when icmFile is deleted. */
+icmFile *new_icmFileMem_ad(void *base, size_t length, icmAlloc *al) {
+ icmFile *fp;
+
+ if ((fp = new_icmFileMem_a(base, length, al)) != NULL) {
+ ((icmFileMem *)fp)->del_buf = 1;
+ }
+
+ return fp;
+}
+
+/* ========================================================== */
+/* Conversion support functions */
+/* Convert between ICC storage types and native C types */
+/* Write routine return non-zero if numbers can't be represented */
+
+/* Unsigned */
+static unsigned int read_UInt8Number(char *p) {
+ unsigned int rv;
+ rv = (unsigned int)((ORD8 *)p)[0];
+ return rv;
+}
+
+static int write_UInt8Number(unsigned int d, char *p) {
+ if (d > 255)
+ return 1;
+ ((ORD8 *)p)[0] = (ORD8)d;
+ return 0;
+}
+
+static unsigned int read_UInt16Number(char *p) {
+ unsigned int rv;
+ rv = 256 * (unsigned int)((ORD8 *)p)[0]
+ + (unsigned int)((ORD8 *)p)[1];
+ return rv;
+}
+
+static int write_UInt16Number(unsigned int d, char *p) {
+ if (d > 65535)
+ return 1;
+ ((ORD8 *)p)[0] = (ORD8)(d >> 8);
+ ((ORD8 *)p)[1] = (ORD8)(d);
+ return 0;
+}
+
+static unsigned int read_UInt32Number(char *p) {
+ unsigned int rv;
+ rv = 16777216 * (unsigned int)((ORD8 *)p)[0]
+ + 65536 * (unsigned int)((ORD8 *)p)[1]
+ + 256 * (unsigned int)((ORD8 *)p)[2]
+ + (unsigned int)((ORD8 *)p)[3];
+ return rv;
+}
+
+static int write_UInt32Number(unsigned int d, char *p) {
+ ((ORD8 *)p)[0] = (ORD8)(d >> 24);
+ ((ORD8 *)p)[1] = (ORD8)(d >> 16);
+ ((ORD8 *)p)[2] = (ORD8)(d >> 8);
+ ((ORD8 *)p)[3] = (ORD8)(d);
+ return 0;
+}
+
+static void read_UInt64Number(icmUint64 *d, char *p) {
+ d->h = 16777216 * (unsigned int)((ORD8 *)p)[0]
+ + 65536 * (unsigned int)((ORD8 *)p)[1]
+ + 256 * (unsigned int)((ORD8 *)p)[2]
+ + (unsigned int)((ORD8 *)p)[3];
+ d->l = 16777216 * (unsigned int)((ORD8 *)p)[4]
+ + 65536 * (unsigned int)((ORD8 *)p)[5]
+ + 256 * (unsigned int)((ORD8 *)p)[6]
+ + (unsigned int)((ORD8 *)p)[7];
+}
+
+static int write_UInt64Number(icmUint64 *d, char *p) {
+ ((ORD8 *)p)[0] = (ORD8)(d->h >> 24);
+ ((ORD8 *)p)[1] = (ORD8)(d->h >> 16);
+ ((ORD8 *)p)[2] = (ORD8)(d->h >> 8);
+ ((ORD8 *)p)[3] = (ORD8)(d->h);
+ ((ORD8 *)p)[4] = (ORD8)(d->l >> 24);
+ ((ORD8 *)p)[5] = (ORD8)(d->l >> 16);
+ ((ORD8 *)p)[6] = (ORD8)(d->l >> 8);
+ ((ORD8 *)p)[7] = (ORD8)(d->l);
+ return 0;
+}
+
+static double read_U8Fixed8Number(char *p) {
+ ORD32 o32;
+ o32 = 256 * (ORD32)((ORD8 *)p)[0] /* Read big endian 16 bit unsigned */
+ + (ORD32)((ORD8 *)p)[1];
+ return (double)o32/256.0;
+}
+
+static int write_U8Fixed8Number(double d, char *p) {
+ ORD32 o32;
+ d = d * 256.0 + 0.5;
+ if (d >= 65536.0)
+ return 1;
+ if (d < 0.0)
+ return 1;
+ o32 = (ORD32)d;
+ ((ORD8 *)p)[0] = (ORD8)((o32) >> 8);
+ ((ORD8 *)p)[1] = (ORD8)((o32));
+ return 0;
+}
+
+static double read_U16Fixed16Number(char *p) {
+ ORD32 o32;
+ o32 = 16777216 * (ORD32)((ORD8 *)p)[0] /* Read big endian 32 bit unsigned */
+ + 65536 * (ORD32)((ORD8 *)p)[1]
+ + 256 * (ORD32)((ORD8 *)p)[2]
+ + (ORD32)((ORD8 *)p)[3];
+ return (double)o32/65536.0;
+}
+
+static int write_U16Fixed16Number(double d, char *p) {
+ ORD32 o32;
+ d = d * 65536.0 + 0.5;
+ if (d >= 4294967296.0)
+ return 1;
+ if (d < 0.0)
+ return 1;
+ o32 = (ORD32)d;
+ ((ORD8 *)p)[0] = (ORD8)((o32) >> 24);
+ ((ORD8 *)p)[1] = (ORD8)((o32) >> 16);
+ ((ORD8 *)p)[2] = (ORD8)((o32) >> 8);
+ ((ORD8 *)p)[3] = (ORD8)((o32));
+ return 0;
+}
+
+
+/* Signed numbers */
+static int read_SInt8Number(char *p) {
+ int rv;
+ rv = (int)((INR8 *)p)[0];
+ return rv;
+}
+
+static int write_SInt8Number(int d, char *p) {
+ if (d > 127)
+ return 1;
+ else if (d < -128)
+ return 1;
+ ((INR8 *)p)[0] = (INR8)d;
+ return 0;
+}
+
+static int read_SInt16Number(char *p) {
+ int rv;
+ rv = 256 * (int)((INR8 *)p)[0]
+ + (int)((ORD8 *)p)[1];
+ return rv;
+}
+
+static int write_SInt16Number(int d, char *p) {
+ if (d > 32767)
+ return 1;
+ else if (d < -32768)
+ return 1;
+ ((INR8 *)p)[0] = (INR8)(d >> 8);
+ ((ORD8 *)p)[1] = (ORD8)(d);
+ return 0;
+}
+
+static int read_SInt32Number(char *p) {
+ int rv;
+ rv = 16777216 * (int)((INR8 *)p)[0]
+ + 65536 * (int)((ORD8 *)p)[1]
+ + 256 * (int)((ORD8 *)p)[2]
+ + (int)((ORD8 *)p)[3];
+ return rv;
+}
+
+static int write_SInt32Number(int d, char *p) {
+ ((INR8 *)p)[0] = (INR8)(d >> 24);
+ ((ORD8 *)p)[1] = (ORD8)(d >> 16);
+ ((ORD8 *)p)[2] = (ORD8)(d >> 8);
+ ((ORD8 *)p)[3] = (ORD8)(d);
+ return 0;
+}
+
+static void read_SInt64Number(icmInt64 *d, char *p) {
+ d->h = 16777216 * (int)((INR8 *)p)[0]
+ + 65536 * (int)((ORD8 *)p)[1]
+ + 256 * (int)((ORD8 *)p)[2]
+ + (int)((ORD8 *)p)[3];
+ d->l = 16777216 * (unsigned int)((ORD8 *)p)[4]
+ + 65536 * (unsigned int)((ORD8 *)p)[5]
+ + 256 * (unsigned int)((ORD8 *)p)[6]
+ + (unsigned int)((ORD8 *)p)[7];
+}
+
+static int write_SInt64Number(icmInt64 *d, char *p) {
+ ((INR8 *)p)[0] = (INR8)(d->h >> 24);
+ ((ORD8 *)p)[1] = (ORD8)(d->h >> 16);
+ ((ORD8 *)p)[2] = (ORD8)(d->h >> 8);
+ ((ORD8 *)p)[3] = (ORD8)(d->h);
+ ((ORD8 *)p)[4] = (ORD8)(d->l >> 24);
+ ((ORD8 *)p)[5] = (ORD8)(d->l >> 16);
+ ((ORD8 *)p)[6] = (ORD8)(d->l >> 8);
+ ((ORD8 *)p)[7] = (ORD8)(d->l);
+ return 0;
+}
+
+static double read_S15Fixed16Number(char *p) {
+ INR32 i32;
+ i32 = 16777216 * (INR32)((INR8 *)p)[0] /* Read big endian 32 bit signed */
+ + 65536 * (INR32)((ORD8 *)p)[1]
+ + 256 * (INR32)((ORD8 *)p)[2]
+ + (INR32)((ORD8 *)p)[3];
+ return (double)i32/65536.0;
+}
+
+static int write_S15Fixed16Number(double d, char *p) {
+ INR32 i32;
+ d = floor(d * 65536.0 + 0.5); /* Beware! (int)(d + 0.5) doesn't work! */
+ if (d >= 2147483648.0)
+ return 1;
+ if (d < -2147483648.0)
+ return 1;
+ i32 = (INR32)d;
+ ((INR8 *)p)[0] = (INR8)((i32) >> 24); /* Write big endian 32 bit signed */
+ ((ORD8 *)p)[1] = (ORD8)((i32) >> 16);
+ ((ORD8 *)p)[2] = (ORD8)((i32) >> 8);
+ ((ORD8 *)p)[3] = (ORD8)((i32));
+ return 0;
+}
+
+/* Device coordinate as 8 bit value range 0.0 - 1.0 */
+static double read_DCS8Number(char *p) {
+ unsigned int rv;
+ rv = (unsigned int)((ORD8 *)p)[0];
+ return (double)rv/255.0;
+}
+
+static int write_DCS8Number(double d, char *p) {
+ ORD32 o32;
+ d = d * 255.0 + 0.5;
+ if (d >= 256.0)
+ return 1;
+ if (d < 0.0)
+ return 1;
+ o32 = (ORD32)d;
+ ((ORD8 *)p)[0] = (ORD8)(o32);
+ return 0;
+}
+
+/* Device coordinate as 16 bit value range 0.0 - 1.0 */
+static double read_DCS16Number(char *p) {
+ unsigned int rv;
+ rv = 256 * (unsigned int)((ORD8 *)p)[0]
+ + (unsigned int)((ORD8 *)p)[1];
+ return (double)rv/65535.0;
+}
+
+static int write_DCS16Number(double d, char *p) {
+ ORD32 o32;
+ d = d * 65535.0 + 0.5;
+ if (d >= 65536.0)
+ return 1;
+ if (d < 0.0)
+ return 1;
+ o32 = (ORD32)d;
+ ((ORD8 *)p)[0] = (ORD8)(o32 >> 8);
+ ((ORD8 *)p)[1] = (ORD8)(o32);
+ return 0;
+}
+
+static void Lut_Lut2XYZ(double *out, double *in);
+static void Lut_XYZ2Lut(double *out, double *in);
+static void Lut_Lut2Lab_8(double *out, double *in);
+static void Lut_Lab2Lut_8(double *out, double *in);
+static void Lut_Lut2LabV2_16(double *out, double *in);
+static void Lut_Lab2LutV2_16(double *out, double *in);
+static void Lut_Lut2LabV4_16(double *out, double *in);
+static void Lut_Lab2LutV4_16(double *out, double *in);
+
+static void Lut_Lut2Y(double *out, double *in);
+static void Lut_Y2Lut(double *out, double *in);
+static void Lut_Lut2L_8(double *out, double *in);
+static void Lut_L2Lut_8(double *out, double *in);
+static void Lut_Lut2LV2_16(double *out, double *in);
+static void Lut_L2LutV2_16(double *out, double *in);
+static void Lut_Lut2LV4_16(double *out, double *in);
+static void Lut_L2LutV4_16(double *out, double *in);
+
+//~~~~888888
+/* read a PCS number. PCS can be profile PCS, profile version Lab, */
+/* or a specific type of Lab, depending on the value of csig: */
+/* icmSigPCSData, icSigXYZData, icmSigLab8Data, icSigLabData, */
+/* icmSigLabV2Data or icmSigLabV4Data */
+/* Do nothing if not one of the above. */
+static void read_PCSNumber(icc *icp, icColorSpaceSignature csig, double pcs[3], char *p) {
+
+ if (csig == icmSigPCSData)
+ csig = icp->header->pcs;
+ if (csig == icSigLabData) {
+ if (icp->ver != 0)
+ csig = icmSigLabV4Data;
+ else
+ csig = icmSigLabV2Data;
+ }
+
+ if (csig == icmSigLab8Data) {
+ pcs[0] = read_DCS8Number(p);
+ pcs[1] = read_DCS8Number(p+1);
+ pcs[2] = read_DCS8Number(p+2);
+ } else {
+ pcs[0] = read_DCS16Number(p);
+ pcs[1] = read_DCS16Number(p+2);
+ pcs[2] = read_DCS16Number(p+4);
+ }
+ switch (csig) {
+ case icSigXYZData:
+ Lut_Lut2XYZ(pcs, pcs);
+ break;
+ case icmSigLab8Data:
+ Lut_Lut2Lab_8(pcs, pcs);
+ break;
+ case icmSigLabV2Data:
+ Lut_Lut2LabV2_16(pcs, pcs);
+ break;
+ case icmSigLabV4Data:
+ Lut_Lut2LabV4_16(pcs, pcs);
+ break;
+ default:
+ break;
+ }
+}
+
+/* write a PCS number. PCS can be profile PCS, profile version Lab, */
+/* or a specific type of Lab, depending on the value of csig: */
+/* icmSigPCSData, icSigXYZData, icmSigLab8Data, icSigLabData, */
+/* icmSigLabV2Data or icmSigLabV4Data */
+/* Return 1 if error */
+static int write_PCSNumber(icc *icp, icColorSpaceSignature csig, double pcs[3], char *p) {
+ double v[3];
+ int j;
+
+ if (csig == icmSigPCSData)
+ csig = icp->header->pcs;
+ if (csig == icSigLabData) {
+ if (icp->ver != 0)
+ csig = icmSigLabV4Data;
+ else
+ csig = icmSigLabV2Data;
+ }
+
+ switch (csig) {
+ case icSigXYZData:
+ Lut_XYZ2Lut(v, pcs);
+ break;
+ case icmSigLab8Data:
+ Lut_Lab2Lut_8(v, pcs);
+ break;
+ case icmSigLabV2Data:
+ Lut_Lab2LutV2_16(v, pcs);
+ break;
+ case icmSigLabV4Data:
+ Lut_Lab2LutV4_16(v, pcs);
+ break;
+ default:
+ return 1;
+ }
+ if (csig == icmSigLab8Data) {
+ for (j = 0; j < 3; j++) {
+ if (write_DCS8Number(v[j], p+j))
+ return 1;
+ }
+ } else {
+ for (j = 0; j < 3; j++) {
+ if (write_DCS16Number(v[j], p+(2 * j)))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Read a given primitive type. Return non-zero on error */
+/* (Not currently used internaly ?) */
+/* Public: */
+int read_Primitive(icc *icp, icmPrimType ptype, void *prim, char *p) {
+
+ switch(ptype) {
+ case icmUInt8Number:
+ *((unsigned int *)prim) = read_UInt8Number(p);
+ return 0;
+ case icmUInt16Number:
+ *((unsigned int *)prim) = read_UInt16Number(p);
+ return 0;
+ case icmUInt32Number:
+ *((unsigned int *)prim) = read_UInt32Number(p);
+ return 0;
+ case icmUInt64Number:
+ read_UInt64Number((icmUint64 *)prim, p);
+ return 0;
+ case icmU8Fixed8Number:
+ *((double *)prim) = read_U8Fixed8Number(p);
+ return 0;
+ case icmU16Fixed16Number:
+ *((double *)prim) = read_U16Fixed16Number(p);
+ return 0;
+ case icmSInt8Number:
+ *((int *)prim) = read_SInt8Number(p);
+ return 0;
+ case icmSInt16Number:
+ *((int *)prim) = read_SInt16Number(p);
+ return 0;
+ case icmSInt32Number:
+ *((int *)prim) = read_SInt32Number(p);
+ return 0;
+ case icmSInt64Number:
+ read_SInt64Number((icmInt64 *)prim, p);
+ return 0;
+ case icmS15Fixed16Number:
+ *((double *)prim) = read_S15Fixed16Number(p);
+ return 0;
+ case icmDCS8Number:
+ *((double *)prim) = read_DCS8Number(p);
+ return 0;
+ case icmDCS16Number:
+ *((double *)prim) = read_DCS16Number(p);
+ return 0;
+ case icmPCSNumber:
+ read_PCSNumber(icp, icmSigPCSData, ((double *)prim), p);
+ return 0;
+ case icmPCSXYZNumber:
+ read_PCSNumber(icp, icSigXYZData, ((double *)prim), p);
+ return 0;
+ case icmPCSLab8Number:
+ read_PCSNumber(icp, icmSigLab8Data, ((double *)prim), p);
+ return 0;
+ case icmPCSLabNumber:
+ read_PCSNumber(icp, icSigLabData, ((double *)prim), p);
+ return 0;
+ case icmPCSLabV2Number:
+ read_PCSNumber(icp, icmSigLabV2Data, ((double *)prim), p);
+ return 0;
+ case icmPCSLabV4Number:
+ read_PCSNumber(icp, icmSigLabV4Data, ((double *)prim), p);
+ return 0;
+ }
+
+ return 2;
+}
+
+/* Write a given primitive type. Return non-zero on error */
+/* (Not currently used internaly ?) */
+/* Public: */
+int write_Primitive(icc *icp, icmPrimType ptype, char *p, void *prim) {
+
+ switch(ptype) {
+ case icmUInt8Number:
+ return write_UInt8Number(*((unsigned int *)prim), p);
+ case icmUInt16Number:
+ return write_UInt16Number(*((unsigned int *)prim), p);
+ case icmUInt32Number:
+ return write_UInt32Number(*((unsigned int *)prim), p);
+ case icmUInt64Number:
+ return write_UInt64Number((icmUint64 *)prim, p);
+ case icmU8Fixed8Number:
+ return write_U8Fixed8Number(*((double *)prim), p);
+ case icmU16Fixed16Number:
+ return write_U16Fixed16Number(*((double *)prim), p);
+ case icmSInt8Number:
+ return write_SInt8Number(*((int *)prim), p);
+ case icmSInt16Number:
+ return write_SInt16Number(*((int *)prim), p);
+ case icmSInt32Number:
+ return write_SInt32Number(*((int *)prim), p);
+ case icmSInt64Number:
+ return write_SInt64Number((icmInt64 *)prim, p);
+ case icmS15Fixed16Number:
+ return write_S15Fixed16Number(*((double *)prim), p);
+ case icmDCS8Number:
+ return write_DCS8Number(*((double *)prim), p);
+ case icmDCS16Number:
+ return write_DCS16Number(*((double *)prim), p);
+ case icmPCSNumber:
+ return write_PCSNumber(icp, icmSigPCSData, ((double *)prim), p);
+ case icmPCSXYZNumber:
+ return write_PCSNumber(icp, icSigXYZData, ((double *)prim), p);
+ case icmPCSLab8Number:
+ return write_PCSNumber(icp, icmSigLab8Data, ((double *)prim), p);
+ case icmPCSLabNumber:
+ return write_PCSNumber(icp, icSigLabData, ((double *)prim), p);
+ case icmPCSLabV2Number:
+ return write_PCSNumber(icp, icmSigLabV2Data, ((double *)prim), p);
+ case icmPCSLabV4Number:
+ return write_PCSNumber(icp, icmSigLabV4Data, ((double *)prim), p);
+ }
+
+ return 2;
+}
+
+/* ---------------------------------------------------------- */
+/* Auiliary function - return a string that represents a tag */
+/* Note - returned buffers are static, can only be used 5 */
+/* times before buffers get reused. */
+char *tag2str(
+ int tag
+) {
+ int i;
+ static int si = 0; /* String buffer index */
+ static char buf[5][20]; /* String buffers */
+ char *bp;
+ unsigned char c[4];
+
+ bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ c[0] = 0xff & (tag >> 24);
+ c[1] = 0xff & (tag >> 16);
+ c[2] = 0xff & (tag >> 8);
+ c[3] = 0xff & (tag >> 0);
+ for (i = 0; i < 4; i++) { /* Can we represent it as a string ? */
+ if (!isprint(c[i]))
+ break;
+ }
+ if (i < 4) { /* Not printable - use hex */
+ sprintf(bp,"0x%x",tag);
+ } else { /* Printable */
+ sprintf(bp,"'%c%c%c%c'",c[0],c[1],c[2],c[3]);
+ }
+ return bp;
+}
+
+/* Auiliary function - return a tag created from a string */
+/* Note there is also the icmMakeTag() macro */
+unsigned int str2tag(
+ const char *str
+) {
+ unsigned int tag;
+ tag = (((unsigned int)str[0]) << 24)
+ + (((unsigned int)str[1]) << 16)
+ + (((unsigned int)str[2]) << 8)
+ + (((unsigned int)str[3]));
+ return tag;
+}
+
+/* helper - return 1 if the string doesn't have a */
+/* null terminator within len, return 0 has null at exactly len, */
+/* and 2 if it has null before len. */
+/* Note: will return 1 if len == 0 */
+static int check_null_string(char *cp, int len) {
+ for (; len > 0; len--) {
+ if (cp[0] == '\000')
+ break;
+ cp++;
+ }
+ if (len == 0)
+ return 1;
+ if (len > 1)
+ return 2;
+ return 0;
+}
+
+/* helper - return 1 if the string doesn't have a */
+/* null terminator within len, return 0 has null at exactly len, */
+/* and 2 if it has null before len. */
+/* Note: will return 1 if len == 0 */
+/* Unicode version */
+static int check_null_string16(char *cp, int len) {
+ for (; len > 0; len--) { /* Length is in characters */
+ if (cp[0] == 0 && cp[1] == 0)
+ break;
+ cp += 2;
+ }
+ if (len == 0)
+ return 1;
+ if (len > 1)
+ return 2;
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Color Space to number of component conversion */
+/* Return 0 on error */
+static unsigned int number_ColorSpaceSignature(icColorSpaceSignature sig) {
+ switch(sig) {
+ case icSigXYZData:
+ return 3;
+ case icSigLabData:
+ return 3;
+ case icSigLuvData:
+ return 3;
+ case icSigYCbCrData:
+ return 3;
+ case icSigYxyData:
+ return 3;
+ case icSigRgbData:
+ return 3;
+ case icSigGrayData:
+ return 1;
+ case icSigHsvData:
+ return 3;
+ case icSigHlsData:
+ return 3;
+ case icSigCmykData:
+ return 4;
+ case icSigCmyData:
+ return 3;
+ case icSig2colorData:
+ return 2;
+ case icSig3colorData:
+ return 3;
+ case icSig4colorData:
+ return 4;
+ case icSig5colorData:
+ case icSigMch5Data:
+ return 5;
+ case icSig6colorData:
+ case icSigMch6Data:
+ return 6;
+ case icSig7colorData:
+ case icSigMch7Data:
+ return 7;
+ case icSig8colorData:
+ case icSigMch8Data:
+ return 8;
+ case icSig9colorData:
+ return 9;
+ case icSig10colorData:
+ return 10;
+ case icSig11colorData:
+ return 11;
+ case icSig12colorData:
+ return 12;
+ case icSig13colorData:
+ return 13;
+ case icSig14colorData:
+ return 14;
+ case icSig15colorData:
+ return 15;
+
+ /* Non-standard and Pseudo spaces */
+ case icmSigYData:
+ return 1;
+ case icmSigLData:
+ return 1;
+ case icmSigL8Data:
+ return 1;
+ case icmSigLV2Data:
+ return 1;
+ case icmSigLV4Data:
+ return 1;
+ case icmSigPCSData:
+ return 3;
+ case icmSigLab8Data:
+ return 3;
+ case icmSigLabV2Data:
+ return 3;
+ case icmSigLabV4Data:
+ return 3;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Public version of above */
+
+/* Return the number of channels for the given color space. Return 0 if unknown. */
+ICCLIB_API unsigned int icmCSSig2nchan(icColorSpaceSignature sig) {
+ return number_ColorSpaceSignature(sig);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Return the individual channel names and number of channels give a colorspace signature. */
+/* Return 0 if it is not a colorspace that itself defines particular channels, */
+/* 1 if it is a colorant based colorspace, and 2 if it is not a colorant based space */
+static int chnames_ColorSpaceSignature(
+icColorSpaceSignature sig,
+char *cvals[] /* Pointers to return for each channel */
+) {
+ switch (sig) {
+ case icSigXYZData:
+ cvals[0] = "CIE X";
+ cvals[1] = "CIE Y";
+ cvals[2] = "CIE Z";
+ return 2;
+
+ case icSigLabData:
+ cvals[0] = "CIE L*";
+ cvals[1] = "CIE a*";
+ cvals[2] = "CIE b*";
+ return 2;
+
+ case icSigLuvData:
+ cvals[0] = "CIE L*";
+ cvals[1] = "CIE u*";
+ cvals[2] = "CIE v*";
+ return 2;
+
+ /* Usually ITU-R BT.601 (was CCIR 601) */
+ case icSigYCbCrData:
+ cvals[0] = "ITU Y";
+ cvals[1] = "ITU Cb";
+ cvals[2] = "ITU Cr";
+ return 2;
+
+ case icSigYxyData:
+ cvals[0] = "CIE Y";
+ cvals[1] = "CIE x";
+ cvals[2] = "CIE y";
+ return 2;
+
+ /* Alvy Ray Smith ? */
+ case icSigHsvData:
+ cvals[0] = "RGB Hue";
+ cvals[1] = "RGB Saturation";
+ cvals[2] = "RGB Value";
+ return 2;
+
+ /* GSPC ? */
+ case icSigHlsData:
+ cvals[0] = "RGB Hue";
+ cvals[1] = "RGB Lightness";
+ cvals[2] = "RGB Saturation";
+ return 2;
+
+ case icSigCmyData:
+ cvals[0] = "Cyan";
+ cvals[1] = "Magenta";
+ cvals[2] = "Yellow";
+ return 1;
+
+ case icSigRgbData:
+ cvals[0] = "Red";
+ cvals[1] = "Green";
+ cvals[2] = "Blue";
+ return 1;
+
+ case icSigCmykData:
+ cvals[0] = "Cyan";
+ cvals[1] = "Magenta";
+ cvals[2] = "Yellow";
+ cvals[3] = "Black";
+ return 1;
+
+
+ /* Non-standard and Pseudo spaces */
+ case icmSigYData:
+ cvals[0] = "CIE Y";
+ return 2;
+
+ case icmSigLData:
+ cvals[0] = "CIE L*";
+ return 2;
+
+ default:
+ break;
+
+ }
+ return 0;
+}
+
+/* Public version of above */
+
+/* Return the individual channel names and number of channels give a colorspace signature. */
+/* Return 0 if it is not a colorspace that itself defines particular channels, */
+/* 1 if it is a colorant based colorspace, and 2 if it is not a colorant based space */
+ICCLIB_API unsigned int icmCSSig2chanNames(icColorSpaceSignature sig, char *cvals[]) {
+
+ return chnames_ColorSpaceSignature(sig, cvals);
+}
+
+/* ------------------------------------------------------- */
+/* Flag dump functions */
+/* Note - returned buffers are static, can only be used 5 */
+/* times before buffers get reused. */
+
+/* Screening Encodings */
+static char *string_ScreenEncodings(unsigned int flags) {
+ static int si = 0; /* String buffer index */
+ static char buf[5][80]; /* String buffers */
+ char *bp, *cp;
+
+ cp = bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ if (flags & icPrtrDefaultScreensTrue) {
+ sprintf(cp,"Default Screen");
+ } else {
+ sprintf(cp,"No Default Screen");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icLinesPerInch) {
+ sprintf(cp,", Lines Per Inch");
+ } else {
+ sprintf(cp,", Lines Per cm");
+ }
+ cp = cp + strlen(cp);
+
+ return bp;
+}
+
+/* Device attributes */
+static char *string_DeviceAttributes(unsigned int flags) {
+ static int si = 0; /* String buffer index */
+ static char buf[5][80]; /* String buffers */
+ char *bp, *cp;
+
+ cp = bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ if (flags & icTransparency) {
+ sprintf(cp,"Transparency");
+ } else {
+ sprintf(cp,"Reflective");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icMatte) {
+ sprintf(cp,", Matte");
+ } else {
+ sprintf(cp,", Glossy");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icNegative) {
+ sprintf(cp,", Negative");
+ } else {
+ sprintf(cp,", Positive");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icBlackAndWhite) {
+ sprintf(cp,", BlackAndWhite");
+ } else {
+ sprintf(cp,", Color");
+ }
+ cp = cp + strlen(cp);
+
+ return bp;
+}
+
+/* Profile header flags */
+static char *string_ProfileHeaderFlags(unsigned int flags) {
+ static int si = 0; /* String buffer index */
+ static char buf[5][80]; /* String buffers */
+ char *bp, *cp;
+
+ cp = bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ if (flags & icEmbeddedProfileTrue) {
+ sprintf(cp,"Embedded Profile");
+ } else {
+ sprintf(cp,"Not Embedded Profile");
+ }
+ cp = cp + strlen(cp);
+ if (flags & icUseWithEmbeddedDataOnly) {
+ sprintf(cp,", Use with embedded data only");
+ } else {
+ sprintf(cp,", Use anywhere");
+ }
+ cp = cp + strlen(cp);
+
+ return bp;
+}
+
+
+static char *string_AsciiOrBinaryData(unsigned int flags) {
+ static int si = 0; /* String buffer index */
+ static char buf[5][80]; /* String buffers */
+ char *bp, *cp;
+
+ cp = bp = buf[si++];
+ si %= 5; /* Rotate through buffers */
+
+ if (flags & icBinaryData) {
+ sprintf(cp,"Binary");
+ } else {
+ sprintf(cp,"Ascii");
+ }
+ cp = cp + strlen(cp);
+
+ return bp;
+}
+
+/* ------------------------------------------------------------ */
+/* Enumeration dump functions */
+/* Note - returned buffers are static, can only be used once */
+/* before buffers get reused if type is unknown. */
+
+/* public tags and sizes */
+static const char *string_TagSignature(icTagSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigAToB0Tag:
+ return "AToB0 Multidimentional Transform";
+ case icSigAToB1Tag:
+ return "AToB1 Multidimentional Transform";
+ case icSigAToB2Tag:
+ return "AToB2 Multidimentional Transform";
+ case icSigBlueColorantTag:
+ return "Blue Colorant";
+ case icSigBlueTRCTag:
+ return "Blue Tone Reproduction Curve";
+ case icSigBToA0Tag:
+ return "BToA0 Multidimentional Transform";
+ case icSigBToA1Tag:
+ return "BToA1 Multidimentional Transform";
+ case icSigBToA2Tag:
+ return "BToA2 Multidimentional Transform";
+ case icSigCalibrationDateTimeTag:
+ return "Calibration Date & Time";
+ case icSigCharTargetTag:
+ return "Characterization Target";
+ case icSigCopyrightTag:
+ return "Copyright";
+ case icSigCrdInfoTag:
+ return "CRD Info";
+ case icSigDeviceMfgDescTag:
+ return "Device Manufacturer Description";
+ case icSigDeviceModelDescTag:
+ return "Device Model Description";
+ case icSigGamutTag:
+ return "Gamut";
+ case icSigGrayTRCTag:
+ return "Gray Tone Reproduction Curve";
+ case icSigGreenColorantTag:
+ return "Green Colorant";
+ case icSigGreenTRCTag:
+ return "Green Tone Reproduction Curve";
+ case icSigLuminanceTag:
+ return "Luminance";
+ case icSigMeasurementTag:
+ return "Measurement";
+ case icSigMediaBlackPointTag:
+ return "Media Black Point";
+ case icSigMediaWhitePointTag:
+ return "Media White Point";
+ case icSigNamedColorTag:
+ return "Named Color";
+ case icSigNamedColor2Tag:
+ return "Named Color 2";
+ case icSigPreview0Tag:
+ return "Preview0";
+ case icSigPreview1Tag:
+ return "Preview1";
+ case icSigPreview2Tag:
+ return "Preview2";
+ case icSigProfileDescriptionTag:
+ return "Profile Description";
+ case icSigProfileSequenceDescTag:
+ return "Profile Sequence";
+ case icSigPs2CRD0Tag:
+ return "PS Level 2 CRD perceptual";
+ case icSigPs2CRD1Tag:
+ return "PS Level 2 CRD colorimetric";
+ case icSigPs2CRD2Tag:
+ return "PS Level 2 CRD saturation";
+ case icSigPs2CRD3Tag:
+ return "PS Level 2 CRD absolute";
+ case icSigPs2CSATag:
+ return "PS Level 2 color space array";
+ case icSigPs2RenderingIntentTag:
+ return "PS Level 2 Rendering Intent";
+ case icSigRedColorantTag:
+ return "Red Colorant";
+ case icSigRedTRCTag:
+ return "Red Tone Reproduction Curve";
+ case icSigScreeningDescTag:
+ return "Screening Description";
+ case icSigScreeningTag:
+ return "Screening Attributes";
+ case icSigTechnologyTag:
+ return "Device Technology";
+ case icSigUcrBgTag:
+ return "Under Color Removal & Black Generation";
+ case icSigVideoCardGammaTag:
+ return "Video Card Gamma Curve";
+ case icSigViewingCondDescTag:
+ return "Viewing Condition Description";
+ case icSigViewingConditionsTag:
+ return "Viewing Condition Paramaters";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* technology signature descriptions */
+static const char *string_TechnologySignature(icTechnologySignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigDigitalCamera:
+ return "Digital Camera";
+ case icSigFilmScanner:
+ return "Film Scanner";
+ case icSigReflectiveScanner:
+ return "Reflective Scanner";
+ case icSigInkJetPrinter:
+ return "InkJet Printer";
+ case icSigThermalWaxPrinter:
+ return "Thermal WaxPrinter";
+ case icSigElectrophotographicPrinter:
+ return "Electrophotographic Printer";
+ case icSigElectrostaticPrinter:
+ return "Electrostatic Printer";
+ case icSigDyeSublimationPrinter:
+ return "DyeSublimation Printer";
+ case icSigPhotographicPaperPrinter:
+ return "Photographic Paper Printer";
+ case icSigFilmWriter:
+ return "Film Writer";
+ case icSigVideoMonitor:
+ return "Video Monitor";
+ case icSigVideoCamera:
+ return "Video Camera";
+ case icSigProjectionTelevision:
+ return "Projection Television";
+ case icSigCRTDisplay:
+ return "Cathode Ray Tube Display";
+ case icSigPMDisplay:
+ return "Passive Matrix Display";
+ case icSigAMDisplay:
+ return "Active Matrix Display";
+ case icSigPhotoCD:
+ return "Photo CD";
+ case icSigPhotoImageSetter:
+ return "Photo ImageSetter";
+ case icSigGravure:
+ return "Gravure";
+ case icSigOffsetLithography:
+ return "Offset Lithography";
+ case icSigSilkscreen:
+ return "Silkscreen";
+ case icSigFlexography:
+ return "Flexography";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* type signatures */
+static const char *string_TypeSignature(icTagTypeSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigCurveType:
+ return "Curve";
+ case icSigDataType:
+ return "Data";
+ case icSigDateTimeType:
+ return "DateTime";
+ case icSigLut16Type:
+ return "Lut16";
+ case icSigLut8Type:
+ return "Lut8";
+ case icSigMeasurementType:
+ return "Measurement";
+ case icSigNamedColorType:
+ return "Named Color";
+ case icSigProfileSequenceDescType:
+ return "Profile Sequence Desc";
+ case icSigS15Fixed16ArrayType:
+ return "S15Fixed16 Array";
+ case icSigScreeningType:
+ return "Screening";
+ case icSigSignatureType:
+ return "Signature";
+ case icSigTextType:
+ return "Text";
+ case icSigTextDescriptionType:
+ return "Text Description";
+ case icSigU16Fixed16ArrayType:
+ return "U16Fixed16 Array";
+ case icSigUcrBgType:
+ return "Under Color Removal & Black Generation";
+ case icSigUInt16ArrayType:
+ return "UInt16 Array";
+ case icSigUInt32ArrayType:
+ return "UInt32 Array";
+ case icSigUInt64ArrayType:
+ return "UInt64 Array";
+ case icSigUInt8ArrayType:
+ return "UInt8 Array";
+ case icSigVideoCardGammaType:
+ return "Video Card Gamma";
+ case icSigViewingConditionsType:
+ return "Viewing Conditions";
+ case icSigXYZType:
+ return "XYZ (Array?)";
+ case icSigNamedColor2Type:
+ return "Named Color 2";
+ case icSigCrdInfoType:
+ return "CRD Info";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* Color Space Signatures */
+static const char *string_ColorSpaceSignature(icColorSpaceSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigXYZData:
+ return "XYZ";
+ case icSigLabData:
+ return "Lab";
+ case icSigLuvData:
+ return "Luv";
+ case icSigYCbCrData:
+ return "YCbCr";
+ case icSigYxyData:
+ return "Yxy";
+ case icSigRgbData:
+ return "RGB";
+ case icSigGrayData:
+ return "Gray";
+ case icSigHsvData:
+ return "HSV";
+ case icSigHlsData:
+ return "HLS";
+ case icSigCmykData:
+ return "CMYK";
+ case icSigCmyData:
+ return "CMY";
+ case icSig2colorData:
+ return "2 Color";
+ case icSig3colorData:
+ return "3 Color";
+ case icSig4colorData:
+ return "4 Color";
+ case icSig5colorData:
+ case icSigMch5Data:
+ return "5 Color";
+ case icSig6colorData:
+ case icSigMch6Data:
+ return "6 Color";
+ case icSig7colorData:
+ case icSigMch7Data:
+ return "7 Color";
+ case icSig8colorData:
+ case icSigMch8Data:
+ return "8 Color";
+ case icSig9colorData:
+ return "9 Color";
+ case icSig10colorData:
+ return "10 Color";
+ case icSig11colorData:
+ return "11 Color";
+ case icSig12colorData:
+ return "12 Color";
+ case icSig13colorData:
+ return "13 Color";
+ case icSig14colorData:
+ return "14 Color";
+ case icSig15colorData:
+ return "15 Color";
+
+ /* Non-standard and Pseudo spaces */
+ case icmSigYData:
+ return "Y";
+ case icmSigLData:
+ return "L";
+ case icmSigL8Data:
+ return "L";
+ case icmSigLV2Data:
+ return "L";
+ case icmSigLV4Data:
+ return "L";
+ case icmSigPCSData:
+ return "PCS";
+ case icmSigLab8Data:
+ return "Lab";
+ case icmSigLabV2Data:
+ return "Lab";
+ case icmSigLabV4Data:
+ return "Lab";
+
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+#ifdef NEVER
+/* Public version of above */
+char *ColorSpaceSignature2str(icColorSpaceSignature sig) {
+ return string_ColorSpaceSignature(sig);
+}
+#endif
+
+
+/* profileClass enumerations */
+static const char *string_ProfileClassSignature(icProfileClassSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigInputClass:
+ return "Input";
+ case icSigDisplayClass:
+ return "Display";
+ case icSigOutputClass:
+ return "Output";
+ case icSigLinkClass:
+ return "Link";
+ case icSigAbstractClass:
+ return "Abstract";
+ case icSigColorSpaceClass:
+ return "Color Space";
+ case icSigNamedColorClass:
+ return "Named Color";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* Platform Signatures */
+static const char *string_PlatformSignature(icPlatformSignature sig) {
+ static char buf[80];
+ switch(sig) {
+ case icSigMacintosh:
+ return "Macintosh";
+ case icSigMicrosoft:
+ return "Microsoft";
+ case icSigSolaris:
+ return "Solaris";
+ case icSigSGI:
+ return "SGI";
+ case icSigTaligent:
+ return "Taligent";
+ case icmSig_nix:
+ return "*nix";
+ default:
+ sprintf(buf,"Unrecognized - %s",tag2str(sig));
+ return buf;
+ }
+}
+
+/* Measurement Geometry, used in the measurmentType tag */
+static const char *string_MeasurementGeometry(icMeasurementGeometry sig) {
+ static char buf[30];
+ switch(sig) {
+ case icGeometryUnknown:
+ return "Unknown";
+ case icGeometry045or450:
+ return "0/45 or 45/0";
+ case icGeometry0dord0:
+ return "0/d or d/0";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Rendering Intents, used in the profile header */
+static const char *string_RenderingIntent(icRenderingIntent sig) {
+ static char buf[30];
+ switch(sig) {
+ case icPerceptual:
+ return "Perceptual";
+ case icRelativeColorimetric:
+ return "Relative Colorimetric";
+ case icSaturation:
+ return "Saturation";
+ case icAbsoluteColorimetric:
+ return "Absolute Colorimetric";
+ case icmAbsolutePerceptual: /* icclib specials */
+ return "Absolute Perceptual";
+ case icmAbsoluteSaturation: /* icclib specials */
+ return "Absolute Saturation";
+ case icmDefaultIntent: /* icclib specials */
+ return "Default Intent";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Transform Lookup function */
+static const char *string_LookupFunc(icmLookupFunc sig) {
+ static char buf[30];
+ switch(sig) {
+ case icmFwd:
+ return "Forward";
+ case icmBwd:
+ return "Backward";
+ case icmGamut:
+ return "Gamut";
+ case icmPreview:
+ return "Preview";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+
+/* Different Spot Shapes currently defined, used for screeningType */
+static const char *string_SpotShape(icSpotShape sig) {
+ static char buf[30];
+ switch(sig) {
+ case icSpotShapeUnknown:
+ return "Unknown";
+ case icSpotShapePrinterDefault:
+ return "Printer Default";
+ case icSpotShapeRound:
+ return "Round";
+ case icSpotShapeDiamond:
+ return "Diamond";
+ case icSpotShapeEllipse:
+ return "Ellipse";
+ case icSpotShapeLine:
+ return "Line";
+ case icSpotShapeSquare:
+ return "Square";
+ case icSpotShapeCross:
+ return "Cross";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Standard Observer, used in the measurmentType tag */
+static const char *string_StandardObserver(icStandardObserver sig) {
+ static char buf[30];
+ switch(sig) {
+ case icStdObsUnknown:
+ return "Unknown";
+ case icStdObs1931TwoDegrees:
+ return "1931 Two Degrees";
+ case icStdObs1964TenDegrees:
+ return "1964 Ten Degrees";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Pre-defined illuminants, used in measurement and viewing conditions type */
+static const char *string_Illuminant(icIlluminant sig) {
+ static char buf[30];
+ switch(sig) {
+ case icIlluminantUnknown:
+ return "Unknown";
+ case icIlluminantD50:
+ return "D50";
+ case icIlluminantD65:
+ return "D65";
+ case icIlluminantD93:
+ return "D93";
+ case icIlluminantF2:
+ return "F2";
+ case icIlluminantD55:
+ return "D55";
+ case icIlluminantA:
+ return "A";
+ case icIlluminantEquiPowerE:
+ return "Equi-Power(E)";
+ case icIlluminantF8:
+ return "F8";
+ default:
+ sprintf(buf,"Unrecognized - 0x%x",sig);
+ return buf;
+ }
+}
+
+/* Return a text abreviation of a color lookup algorithm */
+static const char *string_LuAlg(icmLuAlgType alg) {
+ static char buf[80];
+
+ switch(alg) {
+ case icmMonoFwdType:
+ return "MonoFwd";
+ case icmMonoBwdType:
+ return "MonoBwd";
+ case icmMatrixFwdType:
+ return "MatrixFwd";
+ case icmMatrixBwdType:
+ return "MatrixBwd";
+ case icmLutType:
+ return "Lut";
+ default:
+ sprintf(buf,"Unrecognized - %d",alg);
+ return buf;
+ }
+}
+
+/* Return a string description of the given enumeration value */
+/* Public: */
+const char *icm2str(icmEnumType etype, int enumval) {
+
+ switch(etype) {
+ case icmScreenEncodings:
+ return string_ScreenEncodings((unsigned int) enumval);
+ case icmDeviceAttributes:
+ return string_DeviceAttributes((unsigned int) enumval);
+ case icmProfileHeaderFlags:
+ return string_ProfileHeaderFlags((unsigned int) enumval);
+ case icmAsciiOrBinaryData:
+ return string_AsciiOrBinaryData((unsigned int) enumval);
+ case icmTagSignature:
+ return string_TagSignature((icTagSignature) enumval);
+ case icmTechnologySignature:
+ return string_TechnologySignature((icTechnologySignature) enumval);
+ case icmTypeSignature:
+ return string_TypeSignature((icTagTypeSignature) enumval);
+ case icmColorSpaceSignature:
+ return string_ColorSpaceSignature((icColorSpaceSignature) enumval);
+ case icmProfileClassSignature:
+ return string_ProfileClassSignature((icProfileClassSignature) enumval);
+ case icmPlatformSignature:
+ return string_PlatformSignature((icPlatformSignature) enumval);
+ case icmMeasurementGeometry:
+ return string_MeasurementGeometry((icMeasurementGeometry) enumval);
+ case icmRenderingIntent:
+ return string_RenderingIntent((icRenderingIntent) enumval);
+ case icmTransformLookupFunc:
+ return string_LookupFunc((icmLookupFunc) enumval);
+ case icmSpotShape:
+ return string_SpotShape((icSpotShape) enumval);
+ case icmStandardObserver:
+ return string_StandardObserver((icStandardObserver) enumval);
+ case icmIlluminant:
+ return string_Illuminant((icIlluminant) enumval);
+ case icmLuAlg:
+ return string_LuAlg((icmLuAlgType) enumval);
+ default:
+ return "enum2str got unknown type";
+ }
+}
+
+/* ========================================================== */
+/* Object I/O routines */
+/* ========================================================== */
+/* icmUnknown object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUnknown_get_size(
+ icmBase *pp
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 1); /* 1 byte for each unknown data */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUnknown_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUnknown_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUnknown_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUnknown_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/1; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ p->uttype = (icTagTypeSignature)read_SInt32Number(bp);
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 1) {
+ p->data[i] = read_UInt8Number(bp);
+ }
+ icp->al->free(p->icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUnknown_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUnknown_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUnknown_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->uttype,bp)) != 0) {
+ sprintf(icp->err,"icmUnknown_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp += 8; /* Skip padding */
+
+ /* Write all the data to the buffer */
+ for (i = 0; i < p->size; i++, bp += 1) {
+ if ((rv = write_UInt8Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUnknown_write: write_UInt8umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUnknown_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUnknown_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ unsigned int i, ii, r, ph;
+
+ if (verb <= 1)
+ return;
+
+ op->gprintf(op,"Unknown:\n");
+ op->gprintf(op," Payload size in bytes = %u\n",p->size);
+
+ /* Print one row of binary and ASCII interpretation if verb == 2, All if == 3 */
+ /* else print all of it. */
+ ii = i = ph = 0;
+ for (r = 1;; r++) { /* count rows */
+ int c = 1; /* Character location */
+
+ c = 1;
+ if (ph != 0) { /* Print ASCII under binary */
+ op->gprintf(op," ");
+ i = ii; /* Swap */
+ c += 12;
+ } else {
+ op->gprintf(op," 0x%04lx: ",i);
+ ii = i; /* Swap */
+ c += 12;
+ }
+ while (i < p->size && c < 60) {
+ if (ph == 0)
+ op->gprintf(op,"%02x ",p->data[i]);
+ else {
+ if (isprint(p->data[i]))
+ op->gprintf(op,"%c ",p->data[i]);
+ else
+ op->gprintf(op," ",p->data[i]);
+ }
+ c += 3;
+ i++;
+ }
+ if (ph == 0 || i < p->size)
+ op->gprintf(op,"\n");
+
+ if (ph == 1 && i >= p->size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (ph == 1 && r > 1 && verb < 3) {
+ op->gprintf(op," ...\n");
+ break; /* Print 1 row if not verbose */
+ }
+
+ if (ph == 0)
+ ph = 1;
+ else
+ ph = 0;
+
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUnknown_allocate(
+ icmBase *pp
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned char))) {
+ sprintf(icp->err,"icmUnknown_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned char *) icp->al->calloc(icp->al, p->size, sizeof(unsigned char)))
+ == NULL) {
+ sprintf(icp->err,"icmUnknown_alloc: malloc() of icmUnknown data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUnknown_delete(
+ icmBase *pp
+) {
+ icmUnknown *p = (icmUnknown *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUnknown(
+ icc *icp
+) {
+ icmUnknown *p;
+ if ((p = (icmUnknown *) icp->al->calloc(icp->al,1,sizeof(icmUnknown))) == NULL)
+ return NULL;
+ p->ttype = icmSigUnknownType;
+ p->uttype = icmSigUnknownType;
+ p->refcount = 1;
+ p->get_size = icmUnknown_get_size;
+ p->read = icmUnknown_read;
+ p->write = icmUnknown_write;
+ p->dump = icmUnknown_dump;
+ p->allocate = icmUnknown_allocate;
+ p->del = icmUnknown_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUInt8Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUInt8Array_get_size(
+ icmBase *pp
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 1); /* 1 byte for each UInt8 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUInt8Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUInt8Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt8Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUInt8Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/1; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ icp->al->free(icp->al, buf);
+ sprintf(icp->err,"icmUInt8Array_read: Wrong tag type for icmUInt8Array");
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 1) {
+ p->data[i] = read_UInt8Number(bp);
+ }
+ icp->al->free(p->icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUInt8Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUInt8Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt8Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUInt8Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp += 8; /* Skip padding */
+
+ /* Write all the data to the buffer */
+ for (i = 0; i < p->size; i++, bp += 1) {
+ if ((rv = write_UInt8Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUInt8Array_write: write_UInt8umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUInt8Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUInt8Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"UInt8Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %u\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUInt8Array_allocate(
+ icmBase *pp
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned int))) {
+ sprintf(icp->err,"icmUInt8Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned int *) icp->al->calloc(icp->al, p->size, sizeof(unsigned int)))
+ == NULL) {
+ sprintf(icp->err,"icmUInt8Array_alloc: malloc() of icmUInt8Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUInt8Array_delete(
+ icmBase *pp
+) {
+ icmUInt8Array *p = (icmUInt8Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUInt8Array(
+ icc *icp
+) {
+ icmUInt8Array *p;
+ if ((p = (icmUInt8Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt8Array))) == NULL)
+ return NULL;
+ p->ttype = icSigUInt8ArrayType;
+ p->refcount = 1;
+ p->get_size = icmUInt8Array_get_size;
+ p->read = icmUInt8Array_read;
+ p->write = icmUInt8Array_write;
+ p->dump = icmUInt8Array_dump;
+ p->allocate = icmUInt8Array_allocate;
+ p->del = icmUInt8Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUInt16Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUInt16Array_get_size(
+ icmBase *pp
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 2); /* 2 bytes for each UInt16 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUInt16Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUInt16Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt16Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUInt16Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/2; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmUInt16Array_read: Wrong tag type for icmUInt16Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 2) {
+ p->data[i] = read_UInt16Number(bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUInt16Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUInt16Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt16Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUInt16Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 2) {
+ if ((rv = write_UInt16Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUInt16Array_write: write_UInt16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUInt16Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUInt16Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"UInt16Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %u\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUInt16Array_allocate(
+ icmBase *pp
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned int))) {
+ sprintf(icp->err,"icmUInt16Array_alloc:: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned int *) icp->al->calloc(icp->al, p->size, sizeof(unsigned int)))
+ == NULL) {
+ sprintf(icp->err,"icmUInt16Array_alloc: malloc() of icmUInt16Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUInt16Array_delete(
+ icmBase *pp
+) {
+ icmUInt16Array *p = (icmUInt16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUInt16Array(
+ icc *icp
+) {
+ icmUInt16Array *p;
+ if ((p = (icmUInt16Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt16Array))) == NULL)
+ return NULL;
+ p->ttype = icSigUInt16ArrayType;
+ p->refcount = 1;
+ p->get_size = icmUInt16Array_get_size;
+ p->read = icmUInt16Array_read;
+ p->write = icmUInt16Array_write;
+ p->dump = icmUInt16Array_dump;
+ p->allocate = icmUInt16Array_allocate;
+ p->del = icmUInt16Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUInt32Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUInt32Array_get_size(
+ icmBase *pp
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 4); /* 4 bytes for each UInt32 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUInt32Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUInt32Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt32Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUInt32Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/4; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmUInt32Array_read: Wrong tag type for icmUInt32Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 4) {
+ p->data[i] = read_UInt32Number(bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUInt32Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUInt32Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt32Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUInt32Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 4) {
+ if ((rv = write_UInt32Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUInt32Array_write: write_UInt32umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUInt32Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUInt32Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"UInt32Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %u\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUInt32Array_allocate(
+ icmBase *pp
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned int))) {
+ sprintf(icp->err,"icmUInt32Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned int *) icp->al->calloc(icp->al, p->size, sizeof(unsigned int)))
+ == NULL) {
+ sprintf(icp->err,"icmUInt32Array_alloc: malloc() of icmUInt32Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUInt32Array_delete(
+ icmBase *pp
+) {
+ icmUInt32Array *p = (icmUInt32Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUInt32Array(
+ icc *icp
+) {
+ icmUInt32Array *p;
+ if ((p = (icmUInt32Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt32Array))) == NULL)
+ return NULL;
+ p->ttype = icSigUInt32ArrayType;
+ p->refcount = 1;
+ p->get_size = icmUInt32Array_get_size;
+ p->read = icmUInt32Array_read;
+ p->write = icmUInt32Array_write;
+ p->dump = icmUInt32Array_dump;
+ p->allocate = icmUInt32Array_allocate;
+ p->del = icmUInt32Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUInt64Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUInt64Array_get_size(
+ icmBase *pp
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 8); /* 8 bytes for each UInt64 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUInt64Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmUInt64Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt64Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUInt64Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/8; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmUInt64Array_read: Wrong tag type for icmUInt64Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 8) {
+ read_UInt64Number(&p->data[i], bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUInt64Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUInt64Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUInt64Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUInt64Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 8) {
+ if ((rv = write_UInt64Number(&p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmUInt64Array_write: write_UInt64umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUInt64Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUInt64Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"UInt64Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: h=%lu, l=%lu\n",i,p->data[i].h,p->data[i].l);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUInt64Array_allocate(
+ icmBase *pp
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(icmUint64))) {
+ sprintf(icp->err,"icmUInt64Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmUint64 *) icp->al->calloc(icp->al, p->size, sizeof(icmUint64)))
+ == NULL) {
+ sprintf(icp->err,"icmUInt64Array_alloc: malloc() of icmUInt64Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUInt64Array_delete(
+ icmBase *pp
+) {
+ icmUInt64Array *p = (icmUInt64Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUInt64Array(
+ icc *icp
+) {
+ icmUInt64Array *p;
+ if ((p = (icmUInt64Array *) icp->al->calloc(icp->al,1,sizeof(icmUInt64Array))) == NULL)
+ return NULL;
+ p->ttype = icSigUInt64ArrayType;
+ p->refcount = 1;
+ p->get_size = icmUInt64Array_get_size;
+ p->read = icmUInt64Array_read;
+ p->write = icmUInt64Array_write;
+ p->dump = icmUInt64Array_dump;
+ p->allocate = icmUInt64Array_allocate;
+ p->del = icmUInt64Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmU16Fixed16Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmU16Fixed16Array_get_size(
+ icmBase *pp
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 4); /* 4 byte for each U16Fixed16 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmU16Fixed16Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmU16Fixed16Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmU16Fixed16Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmU16Fixed16Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/4; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmU16Fixed16Array_read: Wrong tag type for icmU16Fixed16Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 4) {
+ p->data[i] = read_U16Fixed16Number(bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmU16Fixed16Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmU16Fixed16Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmU16Fixed16Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmU16Fixed16Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 4) {
+ if ((rv = write_U16Fixed16Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmU16Fixed16Array_write: write_U16Fixed16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmU16Fixed16Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmU16Fixed16Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"U16Fixed16Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %f\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmU16Fixed16Array_allocate(
+ icmBase *pp
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(double))) {
+ sprintf(icp->err,"icmU16Fixed16Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (double *) icp->al->calloc(icp->al, p->size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmU16Fixed16Array_alloc: malloc() of icmU16Fixed16Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmU16Fixed16Array_delete(
+ icmBase *pp
+) {
+ icmU16Fixed16Array *p = (icmU16Fixed16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmU16Fixed16Array(
+ icc *icp
+) {
+ icmU16Fixed16Array *p;
+ if ((p = (icmU16Fixed16Array *) icp->al->calloc(icp->al,1,sizeof(icmU16Fixed16Array))) == NULL)
+ return NULL;
+ p->ttype = icSigU16Fixed16ArrayType;
+ p->refcount = 1;
+ p->get_size = icmU16Fixed16Array_get_size;
+ p->read = icmU16Fixed16Array_read;
+ p->write = icmU16Fixed16Array_write;
+ p->dump = icmU16Fixed16Array_dump;
+ p->allocate = icmU16Fixed16Array_allocate;
+ p->del = icmU16Fixed16Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmS15Fixed16Array object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmS15Fixed16Array_get_size(
+ icmBase *pp
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 4); /* 4 byte for each S15Fixed16 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmS15Fixed16Array_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmS15Fixed16Array_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmS15Fixed16Array_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmS15Fixed16Array_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/4; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmS15Fixed16Array_read: Wrong tag type for icmS15Fixed16Array");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 4) {
+ p->data[i] = read_S15Fixed16Number(bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmS15Fixed16Array_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmS15Fixed16Array_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmS15Fixed16Array_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmS15Fixed16Array_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 4) {
+ if ((rv = write_S15Fixed16Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmS15Fixed16Array_write: write_S15Fixed16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmS15Fixed16Array_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmS15Fixed16Array_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"S15Fixed16Array:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %lu: %f\n",i,p->data[i]);
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmS15Fixed16Array_allocate(
+ icmBase *pp
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(double))) {
+ sprintf(icp->err,"icmS15Fixed16Array_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (double *) icp->al->calloc(icp->al, p->size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmS15Fixed16Array_alloc: malloc() of icmS15Fixed16Array data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmS15Fixed16Array_delete(
+ icmBase *pp
+) {
+ icmS15Fixed16Array *p = (icmS15Fixed16Array *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmS15Fixed16Array(
+ icc *icp
+) {
+ icmS15Fixed16Array *p;
+ if ((p = (icmS15Fixed16Array *) icp->al->calloc(icp->al,1,sizeof(icmS15Fixed16Array))) == NULL)
+ return NULL;
+ p->ttype = icSigS15Fixed16ArrayType;
+ p->refcount = 1;
+ p->get_size = icmS15Fixed16Array_get_size;
+ p->read = icmS15Fixed16Array_read;
+ p->write = icmS15Fixed16Array_write;
+ p->dump = icmS15Fixed16Array_dump;
+ p->allocate = icmS15Fixed16Array_allocate;
+ p->del = icmS15Fixed16Array_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Data conversion support functions */
+static int write_XYZNumber(icmXYZNumber *p, char *d) {
+ int rv;
+ if ((rv = write_S15Fixed16Number(p->X, d + 0)) != 0)
+ return rv;
+ if ((rv = write_S15Fixed16Number(p->Y, d + 4)) != 0)
+ return rv;
+ if ((rv = write_S15Fixed16Number(p->Z, d + 8)) != 0)
+ return rv;
+ return 0;
+}
+
+static int read_XYZNumber(icmXYZNumber *p, char *d) {
+ p->X = read_S15Fixed16Number(d + 0);
+ p->Y = read_S15Fixed16Number(d + 4);
+ p->Z = read_S15Fixed16Number(d + 8);
+ return 0;
+}
+
+
+/* Helper: Return a string that shows the XYZ number value */
+static char *string_XYZNumber(icmXYZNumber *p) {
+ static char buf[40];
+
+ sprintf(buf,"%f, %f, %f", p->X, p->Y, p->Z);
+ return buf;
+}
+
+/* Helper: Return a string that shows the XYZ number value, */
+/* and the Lab D50 number in paren. Note the buffer will be re-used on every call. */
+static char *string_XYZNumber_and_Lab(icmXYZNumber *p) {
+ static char buf[100];
+ double lab[3];
+ lab[0] = p->X;
+ lab[1] = p->Y;
+ lab[2] = p->Z;
+ icmXYZ2Lab(&icmD50, lab, lab);
+ snprintf(buf,sizeof(buf),"%f, %f, %f [Lab %f, %f, %f]", p->X, p->Y, p->Z, lab[0], lab[1], lab[2]);
+ return buf;
+}
+
+/* icmXYZArray object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmXYZArray_get_size(
+ icmBase *pp
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 12); /* 12 bytes for each XYZ */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmXYZArray_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, size;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmXYZArray_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmXYZArray_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmXYZArray_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 8)/12; /* Number of elements in the array */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmXYZArray_read: Wrong tag type for icmXYZArray");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < size; i++, bp += 12) {
+ read_XYZNumber(&p->data[i], bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmXYZArray_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmXYZArray_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmXYZArray_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmXYZArray_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ for (i = 0; i < p->size; i++, bp += 12) {
+ if ((rv = write_XYZNumber(&p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmXYZArray_write: write_XYZumber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmXYZArray_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmXYZArray_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"XYZArray:\n");
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++) {
+ op->gprintf(op," %lu: %s\n",i,string_XYZNumber_and_Lab(&p->data[i]));
+
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmXYZArray_allocate(
+ icmBase *pp
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(icmXYZNumber))) {
+ sprintf(icp->err,"icmXYZArray_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmXYZNumber *) icp->al->malloc(icp->al, sat_mul(p->size, sizeof(icmXYZNumber)))) == NULL) {
+ sprintf(icp->err,"icmXYZArray_alloc: malloc() of icmXYZArray data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmXYZArray_delete(
+ icmBase *pp
+) {
+ icmXYZArray *p = (icmXYZArray *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmXYZArray(
+ icc *icp
+) {
+ icmXYZArray *p;
+ if ((p = (icmXYZArray *) icp->al->calloc(icp->al,1,sizeof(icmXYZArray))) == NULL)
+ return NULL;
+ p->ttype = icSigXYZArrayType;
+ p->refcount = 1;
+ p->get_size = icmXYZArray_get_size;
+ p->read = icmXYZArray_read;
+ p->write = icmXYZArray_write;
+ p->dump = icmXYZArray_dump;
+ p->allocate = icmXYZArray_allocate;
+ p->del = icmXYZArray_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmCurve object */
+
+/* Do a forward lookup through the curve */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmCurve_lookup_fwd(
+ icmCurve *p,
+ double *out,
+ double *in
+) {
+ int rv = 0;
+ if (p->flag == icmCurveLin) {
+ *out = *in;
+ } else if (p->flag == icmCurveGamma) {
+ double val = *in;
+ if (val <= 0.0)
+ *out = 0.0;
+ else
+ *out = pow(val, p->data[0]);
+ } else if (p->size == 0) { /* Table of 0 size */
+ *out = *in;
+ } else { /* Use linear interpolation */
+ unsigned int ix;
+ double val, w;
+ double inputEnt_1 = (double)(p->size-1);
+
+ val = *in * inputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > inputEnt_1) {
+ val = inputEnt_1;
+ rv |= 1;
+ }
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > (p->size-2))
+ ix = (p->size-2);
+ w = val - (double)ix; /* weight */
+ val = p->data[ix];
+ *out = val + w * (p->data[ix+1] - val);
+ }
+ return rv;
+}
+
+/* - - - - - - - - - - - - */
+/* Support for reverse interpolation of 1D lookup tables */
+
+/* Create a reverse curve lookup acceleration table */
+/* return non-zero on error, 2 = malloc error. */
+static int icmTable_setup_bwd(
+ icc *icp, /* Base icc object */
+ icmRevTable *rt, /* Reverse table data to setup */
+ unsigned int size, /* Size of fwd table */
+ double *data /* Table */
+) {
+ unsigned int i;
+
+ rt->size = size; /* Stash pointers to these away */
+ rt->data = data;
+
+ /* Find range of output values */
+ rt->rmin = 1e300;
+ rt->rmax = -1e300;
+ for (i = 0; i < rt->size; i++) {
+ if (rt->data[i] > rt->rmax)
+ rt->rmax = rt->data[i];
+ if (rt->data[i] < rt->rmin)
+ rt->rmin = rt->data[i];
+ }
+
+ /* Decide on reverse granularity */
+ rt->rsize = sat_add(rt->size,2)/2;
+ rt->qscale = (double)rt->rsize/(rt->rmax - rt->rmin); /* Scale factor to quantize to */
+
+ if (ovr_mul(rt->size, sizeof(unsigned int *))) {
+ return 2;
+ }
+ /* Initialize the reverse lookup structures, and get overall min/max */
+ if ((rt->rlists = (unsigned int **) icp->al->calloc(icp->al, rt->rsize, sizeof(unsigned int *))) == NULL) {
+ return 2;
+ }
+
+ /* Assign each output value range bucket lists it intersects */
+ for (i = 0; i < (rt->size-1); i++) {
+ unsigned int s, e, j; /* Start and end indexes (inclusive) */
+ s = (unsigned int)((rt->data[i] - rt->rmin) * rt->qscale);
+ e = (unsigned int)((rt->data[i+1] - rt->rmin) * rt->qscale);
+ if (s >= rt->rsize)
+ s = rt->rsize-1;
+ if (e >= rt->rsize)
+ e = rt->rsize-1;
+ if (s > e) { /* swap */
+ unsigned int t;
+ t = s; s = e; e = t;
+ }
+
+ /* For all buckets that may contain this output range, add index of this output */
+ for (j = s; j <= e; j++) {
+ unsigned int as; /* Allocation size */
+ unsigned int nf; /* Next free slot */
+ if (rt->rlists[j] == NULL) { /* No allocation */
+ as = 5; /* Start with space for 5 */
+ if ((rt->rlists[j] = (unsigned int *) icp->al->calloc(icp->al, as, sizeof(unsigned int))) == NULL) {
+ return 2;
+ }
+ rt->rlists[j][0] = as;
+ nf = rt->rlists[j][1] = 2;
+ } else {
+ as = rt->rlists[j][0]; /* Allocate space for this list */
+ nf = rt->rlists[j][1]; /* Next free location in list */
+ if (nf >= as) { /* need to expand space */
+ if ((as = sat_mul(as, 2)) == UINT_MAX
+ || ovr_mul(as, sizeof(unsigned int))) {
+ return 2;
+ }
+ rt->rlists[j] = (unsigned int *) icp->al->realloc(icp->al,rt->rlists[j], as * sizeof(unsigned int));
+ if (rt->rlists[j] == NULL) {
+ return 2;
+ }
+ rt->rlists[j][0] = as;
+ }
+ }
+ rt->rlists[j][nf++] = i;
+ rt->rlists[j][1] = nf;
+ }
+ }
+ rt->inited = 1;
+ return 0;
+}
+
+/* Free up any data */
+static void icmTable_delete_bwd(
+ icc *icp, /* Base icc */
+ icmRevTable *rt /* Reverse table data to setup */
+) {
+ if (rt->inited != 0) {
+ while (rt->rsize > 0)
+ icp->al->free(icp->al, rt->rlists[--rt->rsize]);
+ icp->al->free(icp->al, rt->rlists);
+ rt->size = 0; /* Don't keep these */
+ rt->data = NULL;
+ }
+}
+
+/* Do a reverse lookup through the curve */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmTable_lookup_bwd(
+ icmRevTable *rt,
+ double *out,
+ double *in
+) {
+ int rv = 0;
+ unsigned int ix, k, i;
+ double oval, ival = *in, val;
+ double rsize_1;
+
+ /* Find appropriate reverse list */
+ rsize_1 = (double)(rt->rsize-1);
+ val = ((ival - rt->rmin) * rt->qscale);
+ if (val < 0.0)
+ val = 0.0;
+ else if (val > rsize_1)
+ val = rsize_1;
+ ix = (unsigned int)floor(val); /* Coordinate */
+
+ if (ix > (rt->size-2))
+ ix = (rt->size-2);
+ if (rt->rlists[ix] != NULL) { /* There is a list of fwd candidates */
+ /* For each candidate forward range */
+ for (i = 2; i < rt->rlists[ix][1]; i++) { /* For all fwd indexes */
+ double lv,hv;
+ k = rt->rlists[ix][i]; /* Base index */
+ lv = rt->data[k];
+ hv = rt->data[k+1];
+ if ((ival >= lv && ival <= hv) /* If this slot contains output value */
+ || (ival >= hv && ival <= lv)) {
+ /* Reverse linear interpolation */
+ if (hv == lv) { /* Technically non-monotonic - due to quantization ? */
+ oval = (k + 0.5)/(rt->size-1.0);
+ } else
+ oval = (k + ((ival - lv)/(hv - lv)))/(rt->size-1.0);
+ /* If we kept looking, we would find multiple */
+ /* solution for non-monotonic curve */
+ *out = oval;
+ return rv;
+ }
+ }
+ }
+
+ /* We have failed to find an exact value, so return the nearest value */
+ /* (This is slow !) */
+ val = fabs(ival - rt->data[0]);
+ for (k = 0, i = 1; i < rt->size; i++) {
+ double er;
+ er = fabs(ival - rt->data[i]);
+ if (er < val) { /* new best */
+ val = er;
+ k = i;
+ }
+ }
+ *out = k/(rt->size-1.0);
+ rv |= 1;
+ return rv;
+}
+
+
+/* - - - - - - - - - - - - */
+
+/* Do a reverse lookup through the curve */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmCurve_lookup_bwd(
+ icmCurve *p,
+ double *out,
+ double *in
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+ if (p->flag == icmCurveLin) {
+ *out = *in;
+ } else if (p->flag == icmCurveGamma) {
+ double val = *in;
+ if (val <= 0.0)
+ *out = 0.0;
+ else
+ *out = pow(val, 1.0/p->data[0]);
+ } else if (p->size == 0) { /* Table of 0 size */
+ *out = *in;
+ } else { /* Use linear interpolation */
+ if (p->rt.inited == 0) {
+ rv = icmTable_setup_bwd(icp, &p->rt, p->size, p->data);
+ if (rv != 0) {
+ sprintf(icp->err,"icmCurve_lookup: Malloc failure in reverse lookup init.");
+ return icp->errc = rv;
+ }
+ }
+ rv = icmTable_lookup_bwd(&p->rt, out, in);
+ }
+ return rv;
+}
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmCurve_get_size(
+ icmBase *pp
+) {
+ icmCurve *p = (icmCurve *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 12); /* 12 bytes for tag, padding and count */
+ len = sat_addmul(len, p->size, 2); /* 2 bytes for each UInt16 */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmCurve_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmCurve *p = (icmCurve *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i;
+ char *bp, *buf, *end;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmCurve_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmCurve_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmCurve_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmCurve_read: Wrong tag type for icmCurve");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ p->size = read_UInt32Number(bp+8);
+ bp = bp + 12;
+
+ /* Set flag up before allocating */
+ if (p->size == 0) { /* Linear curve */
+ p->flag = icmCurveLin;
+ } else if (p->size == 1) { /* Gamma curve */
+ p->flag = icmCurveGamma;
+ } else {
+ p->flag = icmCurveSpec;
+ if (p->size > (len - 12)/2) {
+ sprintf(icp->err,"icmCurve_read: size overflow");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ if (p->flag == icmCurveGamma) { /* Gamma curve */
+ if (bp > end || 1 > (end - bp)) {
+ sprintf(icp->err,"icmCurve_read: Data too short for curve gamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->data[0] = read_U8Fixed8Number(bp);
+ } else if (p->flag == icmCurveSpec) {
+ /* Read all the data from the buffer */
+ for (i = 0; i < p->size; i++, bp += 2) {
+ if (bp > end || 2 > (end - bp)) {
+ sprintf(icp->err,"icmCurve_read: Data too short for curve value");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->data[i] = read_DCS16Number(bp);
+ }
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmCurve_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmCurve *p = (icmCurve *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmCurve_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmCurve_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmCurve_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write count */
+ if ((rv = write_UInt32Number(p->size,bp+8)) != 0) {
+ sprintf(icp->err,"icmCurve_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write all the data to the buffer */
+ bp += 12; /* Skip padding */
+ if (p->flag == icmCurveLin) {
+ if (p->size != 0) {
+ sprintf(icp->err,"icmCurve_write: Must be exactly 0 entry for Linear");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ } else if (p->flag == icmCurveGamma) {
+ if (p->size != 1) {
+ sprintf(icp->err,"icmCurve_write: Must be exactly 1 entry for Gamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if ((rv = write_U8Fixed8Number(p->data[0],bp)) != 0) {
+ sprintf(icp->err,"icmCurve_write: write_U8Fixed8umber(%f) failed",p->data[0]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ } else if (p->flag == icmCurveSpec) {
+ if (p->size < 2) {
+ sprintf(icp->err,"icmCurve_write: Must be 2 or more entries for Specified curve");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ for (i = 0; i < p->size; i++, bp += 2) {
+ if ((rv = write_DCS16Number(p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmCurve_write: write_UInt16umber(%f) failed",p->data[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmCurve_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmCurve_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmCurve *p = (icmCurve *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Curve:\n");
+
+ if (p->flag == icmCurveLin) {
+ op->gprintf(op," Curve is linear\n");
+ } else if (p->flag == icmCurveGamma) {
+ op->gprintf(op," Curve is gamma of %f\n",p->data[0]);
+ } else {
+ op->gprintf(op," No. elements = %lu\n",p->size);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->size; i++)
+ op->gprintf(op," %3lu: %f\n",i,p->data[i]);
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmCurve_allocate(
+ icmBase *pp
+) {
+ icmCurve *p = (icmCurve *)pp;
+ icc *icp = p->icp;
+
+ if (p->flag == icmCurveUndef) {
+ sprintf(icp->err,"icmCurve_alloc: flag not set");
+ return icp->errc = 1;
+ } else if (p->flag == icmCurveLin) {
+ p->size = 0;
+ } else if (p->flag == icmCurveGamma) {
+ p->size = 1;
+ }
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(double))) {
+ sprintf(icp->err,"icmCurve_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (double *) icp->al->calloc(icp->al, p->size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmCurve_alloc: malloc() of icmCurve data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmCurve_delete(
+ icmBase *pp
+) {
+ icmCurve *p = (icmCurve *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icmTable_delete_bwd(icp, &p->rt); /* Free reverse table info */
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmCurve(
+ icc *icp
+) {
+ icmCurve *p;
+ if ((p = (icmCurve *) icp->al->calloc(icp->al,1,sizeof(icmCurve))) == NULL)
+ return NULL;
+ p->ttype = icSigCurveType;
+ p->refcount = 1;
+ p->get_size = icmCurve_get_size;
+ p->read = icmCurve_read;
+ p->write = icmCurve_write;
+ p->dump = icmCurve_dump;
+ p->allocate = icmCurve_allocate;
+ p->del = icmCurve_delete;
+ p->icp = icp;
+
+ p->lookup_fwd = icmCurve_lookup_fwd;
+ p->lookup_bwd = icmCurve_lookup_bwd;
+
+ p->rt.inited = 0;
+
+ p->flag = icmCurveUndef;
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmData object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmData_get_size(
+ icmBase *pp
+) {
+ icmData *p = (icmData *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 12); /* 12 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 1); /* 1 byte for each data element */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmData_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmData *p = (icmData *)pp;
+ icc *icp = p->icp;
+ int rv;
+ unsigned size, f;
+ char *bp, *buf;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmData_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmData_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmData_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = size = (len - 12)/1; /* Number of elements in the array */
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmData_read: Wrong tag type for icmData");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Read the data type flag */
+ f = read_UInt32Number(bp+8);
+ if (f == 0) {
+ p->flag = icmDataASCII;
+ } else if (f == 1) {
+ p->flag = icmDataBin;
+#ifndef ICM_STRICT /* Profile maker sometimes has a problem */
+ } else if (f == 0x01000000) {
+ p->flag = icmDataBin;
+#endif
+ } else {
+ sprintf(icp->err,"icmData_read: Unknown flag value 0x%x",f);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 12; /* Skip padding and flag */
+
+ if (p->size > 0) {
+ if (p->flag == icmDataASCII) {
+ if ((rv = check_null_string(bp,p->size)) == 1) {
+ sprintf(icp->err,"icmData_read: ACSII is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ }
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ memmove((void *)p->data, (void *)bp, p->size);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmData_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmData *p = (icmData *)pp;
+ icc *icp = p->icp;
+ unsigned int len, f;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmData_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmData_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmData_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ switch(p->flag) {
+ case icmDataASCII:
+ f = 0;
+ break;
+ case icmDataBin:
+ f = 1;
+ break;
+ default:
+ sprintf(icp->err,"icmData_write: Unknown Data Flag value");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Write data flag descriptor to the buffer */
+ if ((rv = write_UInt32Number(f,bp+8)) != 0) {
+ sprintf(icp->err,"icmData_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 12; /* Skip padding */
+
+ if (p->data != NULL) {
+ if (p->flag == icmDataASCII) {
+ if ((rv = check_null_string((char *)p->data, p->size)) == 1) {
+ sprintf(icp->err,"icmData_write: ASCII is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ }
+ memmove((void *)bp, (void *)p->data, p->size);
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmData_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmData_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmData *p = (icmData *)pp;
+ unsigned int i, r, c, ii, size = 0;
+ int ph = 0; /* Phase */
+
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Data:\n");
+ switch(p->flag) {
+ case icmDataASCII:
+ op->gprintf(op," ASCII data\n");
+ size = p->size > 0 ? p->size-1 : 0;
+ break;
+ case icmDataBin:
+ op->gprintf(op," Binary data\n");
+ size = p->size;
+ break;
+ case icmDataUndef:
+ op->gprintf(op," Undefined data\n");
+ size = p->size;
+ break;
+ }
+ op->gprintf(op," No. elements = %lu\n",p->size);
+
+ ii = i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+
+ c = 1;
+ if (ph != 0) { /* Print ASCII under binary */
+ op->gprintf(op," ");
+ i = ii;
+ c += 11;
+ } else {
+ op->gprintf(op," 0x%04lx: ",i);
+ ii = i;
+ c += 10;
+ }
+ while (i < size && c < 75) {
+ if (p->flag == icmDataASCII) {
+ if (isprint(p->data[i])) {
+ op->gprintf(op,"%c",p->data[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->data[i]);
+ c += 4;
+ }
+ } else {
+ if (ph == 0)
+ op->gprintf(op,"%02x ",p->data[i]);
+ else {
+ if (isprint(p->data[i]))
+ op->gprintf(op," %c ",p->data[i]);
+ else
+ op->gprintf(op," ",p->data[i]);
+ }
+ c += 3;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ if (verb > 2 && p->flag != icmDataASCII && ph == 0)
+ ph = 1;
+ else
+ ph = 0;
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmData_allocate(
+ icmBase *pp
+) {
+ icmData *p = (icmData *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(unsigned char))) {
+ sprintf(icp->err,"icmData_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (unsigned char *) icp->al->calloc(icp->al, p->size, sizeof(unsigned char))) == NULL) {
+ sprintf(icp->err,"icmData_alloc: malloc() of icmData data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmData_delete(
+ icmBase *pp
+) {
+ icmData *p = (icmData *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmData(
+ icc *icp
+) {
+ icmData *p;
+ if ((p = (icmData *) icp->al->calloc(icp->al,1,sizeof(icmData))) == NULL)
+ return NULL;
+ p->ttype = icSigDataType;
+ p->refcount = 1;
+ p->get_size = icmData_get_size;
+ p->read = icmData_read;
+ p->write = icmData_write;
+ p->dump = icmData_dump;
+ p->allocate = icmData_allocate;
+ p->del = icmData_delete;
+ p->icp = icp;
+
+ p->flag = icmDataUndef;
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmText object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmText_get_size(
+ icmBase *pp
+) {
+ icmText *p = (icmText *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addmul(len, p->size, 1); /* 1 byte for each character element (inc. null) */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmText_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmText *p = (icmText *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf;
+
+ if (len < 8) {
+ sprintf(icp->err,"icmText_read: Tag too short to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmText_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmText_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = (len - 8)/1; /* Number of elements in the array */
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmText_read: Wrong tag type for icmText");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp = bp + 8;
+
+ if (p->size > 0) {
+ if ((rv = check_null_string(bp,p->size)) == 1) {
+ sprintf(icp->err,"icmText_read: text is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ memmove((void *)p->data, (void *)bp, p->size);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmText_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmText *p = (icmText *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmText_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmText_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmText_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp = bp + 8;
+
+ if (p->data != NULL) {
+ if ((rv = check_null_string(p->data, p->size)) == 1) {
+ sprintf(icp->err,"icmText_write: text is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ memmove((void *)bp, (void *)p->data, p->size);
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmText_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmText_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmText *p = (icmText *)pp;
+ unsigned int i, r, c, size;
+
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Text:\n");
+ op->gprintf(op," No. chars = %lu\n",p->size);
+
+ size = p->size > 0 ? p->size-1 : 0;
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 75) {
+ if (isprint(p->data[i])) {
+ op->gprintf(op,"%c",p->data[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->data[i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmText_allocate(
+ icmBase *pp
+) {
+ icmText *p = (icmText *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(char))) {
+ sprintf(icp->err,"icmText_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (char *) icp->al->calloc(icp->al, p->size, sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmText_alloc: malloc() of icmText data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmText_delete(
+ icmBase *pp
+) {
+ icmText *p = (icmText *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmText(
+ icc *icp
+) {
+ icmText *p;
+ if ((p = (icmText *) icp->al->calloc(icp->al,1,sizeof(icmText))) == NULL)
+ return NULL;
+ p->ttype = icSigTextType;
+ p->refcount = 1;
+ p->get_size = icmText_get_size;
+ p->read = icmText_read;
+ p->write = icmText_write;
+ p->dump = icmText_dump;
+ p->allocate = icmText_allocate;
+ p->del = icmText_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Data conversion support functions */
+static int write_DateTimeNumber(icmDateTimeNumber *p, char *d) {
+ int rv;
+ if (p->year < 1900 || p->year > 3000
+ || p->month == 0 || p->month > 12
+ || p->day == 0 || p->day > 31
+ || p->hours > 23
+ || p->minutes > 59
+ || p->seconds > 59)
+ return 1;
+
+ if ((rv = write_UInt16Number(p->year, d + 0)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->month, d + 2)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->day, d + 4)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->hours, d + 6)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->minutes, d + 8)) != 0)
+ return rv;
+ if ((rv = write_UInt16Number(p->seconds, d + 10)) != 0)
+ return rv;
+ return 0;
+}
+
+static int read_DateTimeNumber(icmDateTimeNumber *p, char *d) {
+
+ p->year = read_UInt16Number(d + 0);
+ p->month = read_UInt16Number(d + 2);
+ p->day = read_UInt16Number(d + 4);
+ p->hours = read_UInt16Number(d + 6);
+ p->minutes = read_UInt16Number(d + 8);
+ p->seconds = read_UInt16Number(d + 10);
+
+ /* Sanity check the date and time */
+ if (p->year >= 1900 && p->year <= 3000
+ && p->month != 0 && p->month <= 12
+ && p->day != 0 && p->day <= 31
+ && p->hours <= 23
+ && p->minutes <= 59
+ && p->seconds <= 59)
+ return 0;
+
+#ifdef NEVER
+ printf("Raw year = %d, month = %d, day = %d\n",p->year, p->month, p->day);
+ printf("Raw hour = %d, minutes = %d, seconds = %d\n", p->hours, p->minutes, p->seconds);
+#endif /* NEVER */
+
+#ifdef ICM_STRICT
+ return 1; /* Not legal */
+
+#else
+ /* Be more forgiving */
+
+ /* Check for Adobe problem */
+ if (p->month >= 1900 && p->month <= 3000
+ && p->year != 0 && p->year <= 12
+ && p->hours != 0 && p->hours <= 31
+ && p->day <= 23
+ && p->seconds <= 59
+ && p->minutes <= 59) {
+ unsigned int tt;
+
+ /* Correct Adobe's faulty profile */
+ tt = p->month; p->month = p->year; p->year = tt;
+ tt = p->hours; p->hours = p->day; p->day = tt;
+ tt = p->seconds; p->seconds = p->minutes; p->minutes = tt;
+
+ return 0;
+ }
+
+ /* Hmm. some other sort of corruption. Limit values to sane */
+ if (p->year < 1900) {
+ if (p->year < 100) /* Hmm. didn't use 4 digit year, guess it's 19xx ? */
+ p->year += 1900;
+ else
+ p->year = 1900;
+ } else if (p->year > 3000)
+ p->year = 3000;
+
+ if (p->month == 0)
+ p->month = 1;
+ else if (p->month > 12)
+ p->month = 12;
+
+ if (p->day == 0)
+ p->day = 1;
+ else if (p->day > 31)
+ p->day = 31;
+
+ if (p->hours > 23)
+ p->hours = 23;
+
+ if (p->minutes > 59)
+ p->minutes = 59;
+
+ if (p->seconds > 59)
+ p->seconds = 59;
+
+ return 0;
+#endif
+}
+
+/* Return a string that shows the given date and time */
+static char *string_DateTimeNumber(icmDateTimeNumber *p) {
+ static const char *mstring[13] = {"Bad", "Jan","Feb","Mar","Apr","May","Jun",
+ "Jul","Aug","Sep","Oct","Nov","Dec"};
+ static char buf[80];
+
+ sprintf(buf,"%d %s %4d, %d:%02d:%02d",
+ p->day, mstring[p->month > 12 ? 0 : p->month], p->year,
+ p->hours, p->minutes, p->seconds);
+ return buf;
+}
+
+/* Set the DateTime structure to the current date and time */
+static void setcur_DateTimeNumber(icmDateTimeNumber *p) {
+ time_t cclk;
+ struct tm *ctm;
+
+ cclk = time(NULL);
+ ctm = localtime(&cclk);
+
+ p->year = ctm->tm_year + 1900;
+ p->month = ctm->tm_mon + 1;
+ p->day = ctm->tm_mday;
+ p->hours = ctm->tm_hour;
+ p->minutes = ctm->tm_min;
+ p->seconds = ctm->tm_sec;
+}
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmDateTimeNumber_get_size(
+ icmBase *pp
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 12); /* 12 bytes for Date & Time */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmDateTimeNumber_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf;
+
+ if (len < 20) {
+ sprintf(icp->err,"icmDateTimeNumber_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmDateTimeNumber_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmDateTimeNumber_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmDateTimeNumber_read: Wrong tag type for icmDateTimeNumber");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ /* Read the time and date from buffer */
+ if((rv = read_DateTimeNumber(p, bp)) != 0) {
+ sprintf(icp->err,"icmDateTimeNumber_read: Corrupted DateTime");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmDateTimeNumber_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmDateTimeNumber_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmDateTimeNumber_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmDateTimeNumber_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write all the data to the buffer */
+ bp += 8; /* Skip padding */
+ if ((rv = write_DateTimeNumber(p, bp)) != 0) {
+ sprintf(icp->err,"icmDateTimeNumber_write: write_DateTimeNumber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmDateTimeNumber_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmDateTimeNumber_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"DateTimeNumber:\n");
+ op->gprintf(op," Date = %s\n", string_DateTimeNumber(p));
+}
+
+/* Allocate variable sized data elements */
+static int icmDateTimeNumber_allocate(
+ icmBase *pp
+) {
+ /* Nothing to do */
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmDateTimeNumber_delete(
+ icmBase *pp
+) {
+ icmDateTimeNumber *p = (icmDateTimeNumber *)pp;
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmDateTimeNumber(
+ icc *icp
+) {
+ icmDateTimeNumber *p;
+ if ((p = (icmDateTimeNumber *) icp->al->calloc(icp->al,1,sizeof(icmDateTimeNumber))) == NULL)
+ return NULL;
+ p->ttype = icSigDateTimeType;
+ p->refcount = 1;
+ p->get_size = icmDateTimeNumber_get_size;
+ p->read = icmDateTimeNumber_read;
+ p->write = icmDateTimeNumber_write;
+ p->dump = icmDateTimeNumber_dump;
+ p->allocate = icmDateTimeNumber_allocate;
+ p->del = icmDateTimeNumber_delete;
+ p->icp = icp;
+
+ setcur_DateTimeNumber(p); /* Default to current date and time */
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmLut object */
+
+/* Check if the matrix is non-zero */
+static int icmLut_nu_matrix(
+ icmLut *p /* Pointer to Lut object */
+) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) { /* Rows */
+ for (i = 0; i < 3; i++) { /* Columns */
+ if ( (i == j && p->e[j][i] != 1.0)
+ || (i != j && p->e[j][i] != 0.0))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* return the locations of the minimum and */
+/* maximum values of the given channel, in the clut */
+static void icmLut_min_max(
+ icmLut *p, /* Pointer to Lut object */
+ double *minp, /* Return position of min/max */
+ double *maxp,
+ int chan /* Channel, -1 for average of all */
+) {
+ double *tp;
+ double minv, maxv; /* Values */
+ unsigned int e, ee, f;
+ int gc[MAX_CHAN]; /* Grid coordinate */
+
+ minv = 1e6;
+ maxv = -1e6;
+
+ for (e = 0; e < p->inputChan; e++)
+ gc[e] = 0; /* init coords */
+
+ /* Search the whole table */
+ for (tp = p->clutTable, e = 0; e < p->inputChan; tp += p->outputChan) {
+ double v;
+ if (chan == -1) {
+ for (v = 0.0, f = 0; f < p->outputChan; f++)
+ v += tp[f];
+ } else {
+ v = tp[chan];
+ }
+ if (v < minv) {
+ minv = v;
+ for (ee = 0; ee < p->inputChan; ee++)
+ minp[ee] = gc[ee]/(p->clutPoints-1.0);
+ }
+ if (v > maxv) {
+ maxv = v;
+ for (ee = 0; ee < p->inputChan; ee++)
+ maxp[ee] = gc[ee]/(p->clutPoints-1.0);
+ }
+
+ /* Increment coord */
+ for (e = 0; e < p->inputChan; e++) {
+ if (++gc[e] < p->clutPoints)
+ break; /* No carry */
+ gc[e] = 0;
+ }
+ }
+}
+
+/* Convert XYZ throught Luts matrix */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmLut_lookup_matrix(
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[outputChan] in ICC order - see Table 39 in 6.5.5 */
+double *in /* Input array[inputChan] */
+) {
+ double t0,t1; /* Take care if out == in */
+ t0 = p->e[0][0] * in[0] + p->e[0][1] * in[1] + p->e[0][2] * in[2];
+ t1 = p->e[1][0] * in[0] + p->e[1][1] * in[1] + p->e[1][2] * in[2];
+ out[2] = p->e[2][0] * in[0] + p->e[2][1] * in[1] + p->e[2][2] * in[2];
+ out[0] = t0;
+ out[1] = t1;
+
+ return 0;
+}
+
+/* Convert normalized numbers though this Luts input tables. */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmLut_lookup_input(
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[inputChan] */
+double *in /* Input array[inputChan] */
+) {
+ int rv = 0;
+ unsigned int ix, n;
+ double inputEnt_1 = (double)(p->inputEnt-1);
+ double *table = p->inputTable;
+
+ if (p->inputEnt == 0) { /* Hmm. */
+ for (n = 0; n < p->inputChan; n++)
+ out[n] = in[n];
+ } else {
+ /* Use linear interpolation */
+ for (n = 0; n < p->inputChan; n++, table += p->inputEnt) {
+ double val, w;
+ val = in[n] * inputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > inputEnt_1) {
+ val = inputEnt_1;
+ rv |= 1;
+ }
+ ix = (unsigned int)floor(val); /* Grid coordinate */
+ if (ix > (p->inputEnt-2))
+ ix = (p->inputEnt-2);
+ w = val - (double)ix; /* weight */
+ val = table[ix];
+ out[n] = val + w * (table[ix+1] - val);
+ }
+ }
+ return rv;
+}
+
+/* Convert normalized numbers though this Luts multi-dimensional table. */
+/* using multi-linear interpolation. */
+static int icmLut_lookup_clut_nl(
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[inputChan] */
+double *in /* Input array[outputChan] */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+ double *gp; /* Pointer to grid cube base */
+ double co[MAX_CHAN]; /* Coordinate offset with the grid cell */
+ double *gw, GW[1 << 8]; /* weight for each grid cube corner */
+
+ if (p->inputChan <= 8) {
+ gw = GW; /* Use stack allocation */
+ } else {
+ if ((gw = (double *) icp->al->malloc(icp->al, sat_mul((1 << p->inputChan), sizeof(double)))) == NULL) {
+ sprintf(icp->err,"icmLut_lookup_clut: malloc() failed");
+ return icp->errc = 2;
+ }
+ }
+
+ /* We are using an multi-linear (ie. Trilinear for 3D input) interpolation. */
+ /* The implementation here uses more multiplies that some other schemes, */
+ /* (for instance, see "Tri-Linear Interpolation" by Steve Hill, */
+ /* Graphics Gems IV, page 521), but has less involved bookeeping, */
+ /* needs less local storage for intermediate output values, does fewer */
+ /* output and intermediate value reads, and fp multiplies are fast on */
+ /* todays processors! */
+
+ /* Compute base index into grid and coordinate offsets */
+ {
+ unsigned int e;
+ double clutPoints_1 = (double)(p->clutPoints-1);
+ int clutPoints_2 = p->clutPoints-2;
+ gp = p->clutTable; /* Base of grid array */
+
+ for (e = 0; e < p->inputChan; e++) {
+ unsigned int x;
+ double val;
+ val = in[e] * clutPoints_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > clutPoints_1) {
+ val = clutPoints_1;
+ rv |= 1;
+ }
+ x = (unsigned int)floor(val); /* Grid coordinate */
+ if (x > clutPoints_2)
+ x = clutPoints_2;
+ co[e] = val - (double)x; /* 1.0 - weight */
+ gp += x * p->dinc[e]; /* Add index offset for base of cube */
+ }
+ }
+ /* Compute corner weights needed for interpolation */
+ {
+ unsigned int e;
+ int i, g = 1;
+ gw[0] = 1.0;
+ for (e = 0; e < p->inputChan; e++) {
+ for (i = 0; i < g; i++) {
+ gw[g+i] = gw[i] * co[e];
+ gw[i] *= (1.0 - co[e]);
+ }
+ g *= 2;
+ }
+ }
+ /* Now compute the output values */
+ {
+ int i;
+ unsigned int f;
+ double w = gw[0];
+ double *d = gp + p->dcube[0];
+ for (f = 0; f < p->outputChan; f++) /* Base of cube */
+ out[f] = w * d[f];
+ for (i = 1; i < (1 << p->inputChan); i++) { /* For all other corners of cube */
+ w = gw[i]; /* Strength reduce */
+ d = gp + p->dcube[i];
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * d[f];
+ }
+ }
+ if (gw != GW)
+ icp->al->free(icp->al, (void *)gw);
+ return rv;
+}
+
+/* Convert normalized numbers though this Luts multi-dimensional table */
+/* using simplex interpolation. */
+static int icmLut_lookup_clut_sx(
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[inputChan] */
+double *in /* Input array[outputChan] */
+) {
+ int rv = 0;
+ double *gp; /* Pointer to grid cube base */
+ double co[MAX_CHAN]; /* Coordinate offset with the grid cell */
+ int si[MAX_CHAN]; /* co[] Sort index, [0] = smalest */
+
+ /* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */
+ /* This method is more appropriate for XYZ/RGB/CMYK input spaces, */
+
+ /* Compute base index into grid and coordinate offsets */
+ {
+ unsigned int e;
+ double clutPoints_1 = (double)(p->clutPoints-1);
+ int clutPoints_2 = p->clutPoints-2;
+ gp = p->clutTable; /* Base of grid array */
+
+ for (e = 0; e < p->inputChan; e++) {
+ unsigned int x;
+ double val;
+ val = in[e] * clutPoints_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > clutPoints_1) {
+ val = clutPoints_1;
+ rv |= 1;
+ }
+ x = (unsigned int)floor(val); /* Grid coordinate */
+ if (x > clutPoints_2)
+ x = clutPoints_2;
+ co[e] = val - (double)x; /* 1.0 - weight */
+ gp += x * p->dinc[e]; /* Add index offset for base of cube */
+ }
+ }
+#ifdef NEVER
+ /* Do selection sort on coordinates, smallest to largest. */
+ {
+ int e, f;
+ for (e = 0; e < p->inputChan; e++)
+ si[e] = e; /* Initial unsorted indexes */
+ for (e = 0; e < (p->inputChan-1); e++) {
+ double cosn;
+ cosn = co[si[e]]; /* Current smallest value */
+ for (f = e+1; f < p->inputChan; f++) { /* Check against rest */
+ int tt;
+ tt = si[f];
+ if (cosn > co[tt]) {
+ si[f] = si[e]; /* Exchange */
+ si[e] = tt;
+ cosn = co[tt];
+ }
+ }
+ }
+ }
+#else
+ /* Do insertion sort on coordinates, smallest to largest. */
+ {
+ int f, vf;
+ unsigned int e;
+ double v;
+ for (e = 0; e < p->inputChan; e++)
+ si[e] = e; /* Initial unsorted indexes */
+
+ for (e = 1; e < p->inputChan; e++) {
+ f = e;
+ v = co[si[f]];
+ vf = f;
+ while (f > 0 && co[si[f-1]] > v) {
+ si[f] = si[f-1];
+ f--;
+ }
+ si[f] = vf;
+ }
+ }
+#endif
+ /* Now compute the weightings, simplex vertices and output values */
+ {
+ unsigned int e, f;
+ double w; /* Current vertex weight */
+
+ w = 1.0 - co[si[p->inputChan-1]]; /* Vertex at base of cell */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] = w * gp[f];
+
+ for (e = p->inputChan-1; e > 0; e--) { /* Middle verticies */
+ w = co[si[e]] - co[si[e-1]];
+ gp += p->dinc[si[e]]; /* Move to top of cell in next largest dimension */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * gp[f];
+ }
+
+ w = co[si[0]];
+ gp += p->dinc[si[0]]; /* Far corner from base of cell */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * gp[f];
+ }
+ return rv;
+}
+
+#ifdef NEVER // ~~~99 development code
+
+/* Convert normalized numbers though this Luts multi-dimensional table */
+/* using optimised simplex interpolation. */
+/* This version optimses the simplex split axis depending on the input */
+/* colorspace. */
+static int icmLut_lookup_clut_osx(
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[inputChan] */
+double *in /* Input array[outputChan] */
+) {
+ int rv = 0;
+ double *gp; /* Pointer to grid cube base */
+ double co[MAX_CHAN]; /* Coordinate offset with the grid cell */
+ int si[MAX_CHAN]; /* co[] Sort index, [0] = smalest */
+ char xflip[MAX_CHAN]; /* Optimised simplex axis flip flags */
+
+ /* Compute base index into grid and coordinate offsets */
+ {
+ unsigned int e;
+ double clutPoints_1 = (double)(p->clutPoints-1);
+ int clutPoints_2 = p->clutPoints-2;
+ gp = p->clutTable; /* Base of grid array */
+
+ for (e = 0; e < p->inputChan; e++) {
+ unsigned int x;
+ double val;
+// ~~~999
+#ifdef NEVER
+ xflip[e] = p->finfo[e].bthff;
+ if (in[e] >= p->finfo[e].fth)
+ xflip[e] = p->finfo[e].athff;
+#else
+
+ xflip[e] = 0;
+ if (e == 0)
+ xflip[e] = 1;
+#endif
+ val = in[e] * clutPoints_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > clutPoints_1) {
+ val = clutPoints_1;
+ rv |= 1;
+ }
+ x = (unsigned int)floor(val); /* Grid coordinate */
+ if (x > clutPoints_2)
+ x = clutPoints_2;
+ co[e] = val - (double)x; /* 1.0 - weight */
+ gp += x * p->dinc[e]; /* Add index offset for base of cube */
+ if (xflip[e]) { /* Reverse sense of direction for this axis */
+ co[e] = 1.0 - co[e];
+ gp += p->dinc[e];
+ }
+ }
+ }
+//printf("*");fflush(stdout);
+#ifdef NEVER
+ /* Do selection sort on coordinates, smallest to largest. */
+ {
+ int e, f;
+ for (e = 0; e < p->inputChan; e++)
+ si[e] = e; /* Initial unsorted indexes */
+ for (e = 0; e < (p->inputChan-1); e++) {
+ double cosn;
+ cosn = co[si[e]]; /* Current smallest value */
+ for (f = e+1; f < p->inputChan; f++) { /* Check against rest */
+ int tt;
+ tt = si[f];
+ if (cosn > co[tt]) {
+ si[f] = si[e]; /* Exchange */
+ si[e] = tt;
+ cosn = co[tt];
+ }
+ }
+ }
+ }
+#else
+ /* Do insertion sort on coordinates, smallest to largest. */
+ {
+ int f, vf;
+ unsigned int e;
+ double v;
+ for (e = 0; e < p->inputChan; e++)
+ si[e] = e; /* Initial unsorted indexes */
+
+ for (e = 1; e < p->inputChan; e++) {
+ f = e;
+ v = co[si[f]];
+ vf = f;
+ while (f > 0 && co[si[f-1]] > v) {
+ si[f] = si[f-1];
+ f--;
+ }
+ si[f] = vf;
+ }
+ }
+#endif
+ /* Now compute the weightings, simplex vertices and output values */
+ {
+ unsigned int e, f;
+ double w; /* Current vertex weight */
+
+ w = 1.0 - co[si[p->inputChan-1]]; /* Vertex at base of cell */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] = w * gp[f];
+
+ for (e = p->inputChan-1; e > 0; e--) { /* Middle verticies */
+ w = co[si[e]] - co[si[e-1]];
+ if (xflip[e])
+ gp -= p->dinc[si[e]]; /* Move to top of cell in next largest dimension */
+ else
+ gp += p->dinc[si[e]]; /* Move to top of cell in next largest dimension */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * gp[f];
+ }
+
+ w = co[si[0]];
+ if (xflip[0])
+ gp -= p->dinc[si[0]]; /* Far corner from base of cell */
+ else
+ gp += p->dinc[si[0]]; /* Far corner from base of cell */
+ for (f = 0; f < p->outputChan; f++)
+ out[f] += w * gp[f];
+ }
+ return rv;
+}
+
+#endif /* NEVER */ // ~~~99 development code
+
+
+/* Convert normalized numbers though this Luts output tables. */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+static int icmLut_lookup_output(
+icmLut *p, /* Pointer to Lut object */
+double *out, /* Output array[outputChan] */
+double *in /* Input array[outputChan] */
+) {
+ int rv = 0;
+ unsigned int ix, n;
+ double outputEnt_1 = (double)(p->outputEnt-1);
+ double *table = p->outputTable;
+
+ if (p->outputEnt == 0) { /* Hmm. */
+ for (n = 0; n < p->outputChan; n++)
+ out[n] = in[n];
+ } else {
+ /* Use linear interpolation */
+ for (n = 0; n < p->outputChan; n++, table += p->outputEnt) {
+ double val, w;
+ val = in[n] * outputEnt_1;
+ if (val < 0.0) {
+ val = 0.0;
+ rv |= 1;
+ } else if (val > outputEnt_1) {
+ val = outputEnt_1;
+ rv |= 1;
+ }
+ ix = (unsigned int)floor(val); /* Grid coordinate */
+ if (ix > (p->outputEnt-2))
+ ix = (p->outputEnt-2);
+ w = val - (double)ix; /* weight */
+ val = table[ix];
+ out[n] = val + w * (table[ix+1] - val);
+ }
+ }
+ return rv;
+}
+
+/* ----------------------------------------------- */
+/* Pseudo - Hilbert count sequencer */
+
+/* This multi-dimensional count sequence is a distributed */
+/* Gray code sequence, with direction reversal on every */
+/* alternate power of 2 scale. */
+/* It is intended to aid cache coherence in multi-dimensional */
+/* regular sampling. It approximates the Hilbert curve sequence. */
+
+/* Initialise, returns total usable count */
+unsigned
+psh_init(
+psh *p, /* Pointer to structure to initialise */
+int di, /* Dimensionality */
+unsigned int res, /* Size per coordinate */
+int co[] /* Coordinates to initialise (May be NULL) */
+) {
+ int e;
+
+ p->di = di;
+ p->res = res;
+
+ /* Compute bits */
+ for (p->bits = 0; (1u << p->bits) < res; p->bits++)
+ ;
+
+ /* Compute the total count mask */
+ p->tmask = ((((unsigned)1) << (p->bits * di))-1);
+
+ /* Compute usable count */
+ p->count = 1;
+ for (e = 0; e < di; e++)
+ p->count *= res;
+
+ p->ix = 0;
+
+ if (co != NULL) {
+ for (e = 0; e < di; e++)
+ co[e] = 0;
+ }
+
+ return p->count;
+}
+
+/* Reset the counter */
+void
+psh_reset(
+psh *p /* Pointer to structure */
+) {
+ p->ix = 0;
+}
+
+/* Increment pseudo-hilbert coordinates */
+/* Return non-zero if count rolls over to 0 */
+int
+psh_inc(
+psh *p, /* Pointer to structure */
+int co[] /* Coordinates to return */
+) {
+ int di = p->di;
+ unsigned int res = p->res;
+ unsigned int bits = p->bits;
+ int e;
+
+ do {
+ unsigned int b;
+ int gix; /* Gray code index */
+
+ p->ix = (p->ix + 1) & p->tmask;
+
+ gix = p->ix ^ (p->ix >> 1); /* Convert to gray code index */
+
+ for (e = 0; e < di; e++)
+ co[e] = 0;
+
+ for (b = 0; b < bits; b++) { /* Distribute bits */
+ if (b & 1) {
+ for (e = di-1; e >= 0; e--) { /* In reverse order */
+ co[e] |= (gix & 1) << b;
+ gix >>= 1;
+ }
+ } else {
+ for (e = 0; e < di; e++) { /* In normal order */
+ co[e] |= (gix & 1) << b;
+ gix >>= 1;
+ }
+ }
+ }
+
+ /* Convert from Gray to binary coordinates */
+ for (e = 0; e < di; e++) {
+ unsigned int sh, tv;
+
+ for(sh = 1, tv = co[e];; sh <<= 1) {
+ unsigned ptv = tv;
+ tv ^= (tv >> sh);
+ if (ptv <= 1 || sh == 16)
+ break;
+ }
+ if (tv >= res) /* Dumbo filter - increment again if outside cube range */
+ break;
+ co[e] = tv;
+ }
+
+ } while (e < di);
+
+ return (p->ix == 0);
+}
+
+/* ------------------------------------------------------- */
+
+#ifndef COUNTERS_H
+
+/* Macros for a multi-dimensional counter. */
+
+/* Declare the counter name nn, maximum di mxdi, dimensions di, & count */
+/* This counter can have each dimension range clipped */
+
+#define FCOUNT(nn, mxdi, di) \
+ int nn[mxdi]; /* counter value */ \
+ int nn##_di = (di); /* Number of dimensions */ \
+ int nn##_stt[mxdi]; /* start count value */ \
+ int nn##_res[mxdi]; /* last count +1 */ \
+ int nn##_e /* dimension index */
+
+#define FRECONF(nn, start, endp1) \
+ for (nn##_e = 0; nn##_e < nn##_di; nn##_e++) { \
+ nn##_stt[nn##_e] = (start); /* start count value */ \
+ nn##_res[nn##_e] = (endp1); /* last count +1 */ \
+ }
+
+/* Set the counter value to 0 */
+#define FC_INIT(nn) \
+{ \
+ for (nn##_e = 0; nn##_e < nn##_di; nn##_e++) \
+ nn[nn##_e] = nn##_stt[nn##_e]; \
+ nn##_e = 0; \
+}
+
+/* Increment the counter value */
+#define FC_INC(nn) \
+{ \
+ for (nn##_e = 0; nn##_e < nn##_di; nn##_e++) { \
+ nn[nn##_e]++; \
+ if (nn[nn##_e] < nn##_res[nn##_e]) \
+ break; /* No carry */ \
+ nn[nn##_e] = nn##_stt[nn##_e]; \
+ } \
+}
+
+/* After increment, expression is TRUE if counter is done */
+#define FC_DONE(nn) \
+ (nn##_e >= nn##_di)
+
+#endif /* COUNTERS_H */
+
+/* Parameter to getNormFunc function */
+typedef enum {
+ icmFromLuti = 0, /* return "fromo Lut normalized index" conversion function */
+ icmToLuti = 1, /* return "to Lut normalized index" conversion function */
+ icmFromLutv = 2, /* return "from Lut normalized value" conversion function */
+ icmToLutv = 3 /* return "to Lut normalized value" conversion function */
+} icmNormFlag;
+
+/* Return an appropriate color space normalization function, */
+/* given the color space and Lut type */
+/* Return 0 on success, 1 on match failure */
+static int getNormFunc(
+ icc *icp,
+ icColorSpaceSignature csig,
+ icTagTypeSignature tagSig,
+ icmNormFlag flag,
+ void (**nfunc)(double *out, double *in)
+);
+
+#define CLIP_MARGIN 0.005 /* Margine to allow before reporting clipping = 0.5% */
+
+/* Helper function to set multiple Lut tables simultaneously. */
+/* Note that these tables all have to be compatible in */
+/* having the same configuration and resolution. */
+/* Set errc and return error number in underlying icc */
+/* Set warnc if there is clipping in the output values */
+/* 1 = input table, 2 = main clut, 3 = clut midpoin, 4 = midpoint interp, 5 = output table */
+int icmSetMultiLutTables(
+ int ntables, /* Number of tables to be set, 1..n */
+ icmLut **pp, /* Pointer to array of Lut objects */
+ int flags, /* Setting flags */
+ void *cbctx, /* Opaque callback context pointer value */
+ icColorSpaceSignature insig, /* Input color space */
+ icColorSpaceSignature outsig, /* Output color space */
+ void (*infunc)(void *cbctx, double *out, double *in),
+ /* Input transfer function, inspace->inspace' (NULL = default) */
+ /* Will be called ntables times for each input grid value */
+ double *inmin, double *inmax, /* Maximum range of inspace' values */
+ /* (NULL = default) */
+ void (*clutfunc)(void *cbntx, double *out, double *in),
+ /* inspace' -> outspace[ntables]' transfer function */
+ /* will be called once for each input' grid value, and */
+ /* ntables output values should be written consecutively */
+ /* to out[]. */
+ double *clutmin, double *clutmax, /* Maximum range of outspace' values */
+ /* (NULL = default) */
+ void (*outfunc)(void *cbntx, double *out, double *in))
+ /* Output transfer function, outspace'->outspace (NULL = deflt) */
+ /* Will be called ntables times on each output value */
+{
+ icmLut *p, *pn; /* Pointer to 0'th nd tn'th Lut object */
+ icc *icp; /* Pointer to common icc */
+ int tn;
+ unsigned int e, f, i, n;
+ double **clutTable2 = NULL; /* Cell center values for ICM_CLUT_SET_APXLS */
+ double *clutTable3 = NULL; /* Vertex smoothing radius values [ntables] per entry */
+ int dinc3[MAX_CHAN]; /* Dimensional increment through clut3 (in doubles) */
+ int dcube3[1 << MAX_CHAN]; /* Hyper cube offsets throught clut3 (in doubles) */
+ int ii[MAX_CHAN]; /* Index value */
+ psh counter; /* Pseudo-Hilbert counter */
+// double _iv[4 * MAX_CHAN], *iv = &_iv[MAX_CHAN], *ivn; /* Real index value/table value */
+ int maxchan; /* Actual max of input and output */
+ double *_iv, *iv, *ivn; /* Real index value/table value */
+ double imin[MAX_CHAN], imax[MAX_CHAN];
+ double omin[MAX_CHAN], omax[MAX_CHAN];
+ void (*ifromindex)(double *out, double *in); /* Index to input color space function */
+ void (*itoentry)(double *out, double *in); /* Input color space to entry function */
+ void (*ifromentry)(double *out, double *in); /* Entry to input color space function */
+ void (*otoentry)(double *out, double *in); /* Output colorspace to table value function */
+ void (*ofromentry)(double *out, double *in); /* Table value to output color space function */
+ int clip = 0;
+
+ /* Check that everything is OK to proceed */
+ if (ntables < 1 || ntables > MAX_CHAN) {
+ if (ntables >= 1) {
+ icp = pp[0]->icp;
+ sprintf(icp->err,"icmSetMultiLutTables has illegal number of tables %d",ntables);
+ return icp->errc = 1;
+ } else {
+ /* Can't write error message anywhere */
+ return 1;
+ }
+ }
+
+ p = pp[0];
+ icp = p->icp;
+
+ for (tn = 1; tn < ntables; tn++) {
+ if (pp[tn]->icp != icp) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables base icc is different");
+ return icp->errc = 1;
+ }
+ if (pp[tn]->ttype != p->ttype) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables have different Tage Type");
+ return icp->errc = 1;
+ }
+
+ if (pp[tn]->inputChan != p->inputChan) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables have different inputChan");
+ return icp->errc = 1;
+ }
+ if (pp[tn]->outputChan != p->outputChan) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables have different outputChan");
+ return icp->errc = 1;
+ }
+ if (pp[tn]->clutPoints != p->clutPoints) {
+ sprintf(icp->err,"icmSetMultiLutTables Tables have different clutPoints");
+ return icp->errc = 1;
+ }
+ }
+
+ if (getNormFunc(icp, insig, p->ttype, icmFromLuti, &ifromindex) != 0) {
+ sprintf(icp->err,"icmLut_set_tables index to input colorspace function lookup failed");
+ return icp->errc = 1;
+ }
+ if (getNormFunc(icp, insig, p->ttype, icmToLutv, &itoentry) != 0) {
+ sprintf(icp->err,"icmLut_set_tables input colorspace to table entry function lookup failed");
+ return icp->errc = 1;
+ }
+ if (getNormFunc(icp, insig, p->ttype, icmFromLutv, &ifromentry) != 0) {
+ sprintf(icp->err,"icmLut_set_tables table entry to input colorspace function lookup failed");
+ return icp->errc = 1;
+ }
+
+ if (getNormFunc(icp, outsig, p->ttype, icmToLutv, &otoentry) != 0) {
+ sprintf(icp->err,"icmLut_set_tables output colorspace to table entry function lookup failed");
+ return icp->errc = 1;
+ }
+ if (getNormFunc(icp, outsig, p->ttype, icmFromLutv, &ofromentry) != 0) {
+ sprintf(icp->err,"icmLut_set_tables table entry to output colorspace function lookup failed");
+ return icp->errc = 1;
+ }
+
+ /* Allocate an array to hold the input and output values. */
+ /* It needs to be able to hold di "index under valus as in[], */
+ /* and ntables ICM_CLUT_SET_FILTER values as out[], so we assume maxchan >= di */
+ maxchan = p->inputChan > p->outputChan ? p->inputChan : p->outputChan;
+ if ((_iv = (double *) icp->al->malloc(icp->al, sizeof(double) * maxchan * (ntables+1)))
+ == NULL) {
+ sprintf(icp->err,"icmLut_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ iv = _iv + maxchan; /* Allow for "index under" and smoothing radius values */
+
+ /* Setup input table value min-max */
+ if (inmin == NULL || imax == NULL) {
+#ifdef SYMETRICAL_DEFAULT_LAB_RANGE /* Symetrical default range. */
+ /* We are assuming V2 Lab16 encoding, since this is a lut16type that always uses */
+ /* this encoding */
+ if (insig == icSigLabData) { /* Special case Lab */
+ double mn[3], mx[3];
+ /* This is to ensure that Lab 100,0,0 maps exactly to a clut grid point. */
+ /* This should work well if there is an odd grid resolution, */
+ /* and icclib is being used, as input lookup will */
+ /* be computed using floating point, so that the CLUT input value */
+ /* 0.5 can be represented exactly. */
+ /* Because the symetric range will cause slight clipping, */
+ /* only do it if the input table has sufficient resolution */
+ /* to represent the clipping faithfuly. */
+ if (p->inputEnt >= 64) {
+ if (p->ttype == icSigLut8Type) {
+ mn[0] = 0.0, mn[1] = mn[2] = -127.0;
+ mx[0] = 100.0, mx[1] = mx[2] = 127.0;
+ } else {
+ mn[0] = 0.0, mn[1] = mn[2] = -127.0 - 255.0/256.0;
+ mx[0] = 100.0, mx[1] = mx[2] = 127.0 + 255.0/256.0;
+ }
+ itoentry(imin, mn); /* Convert from input color space to table representation */
+ itoentry(imax, mx);
+ } else {
+ for (e = 0; e < p->inputChan; e++) {
+ imin[e] = 0.0;
+ imax[e] = 1.0;
+ }
+ }
+ } else
+#endif
+ {
+ for (e = 0; e < p->inputChan; e++) {
+ imin[e] = 0.0; /* We are assuming this is true for all other color spaces. */
+ imax[e] = 1.0;
+ }
+ }
+ } else {
+ itoentry(imin, inmin); /* Convert from input color space to table representation */
+ itoentry(imax, inmax);
+ }
+
+ /* Setup output table value min-max */
+ if (clutmin == NULL || clutmax == NULL) {
+#ifdef SYMETRICAL_DEFAULT_LAB_RANGE
+ /* This really isn't doing much, since the full range encoding doesn't need */
+ /* any adjustment to map a*b* 0 to an integer value. */
+ /* We are tweaking the 16 bit L* = 100 to the last index into */
+ /* the output table, which may help its accuracy slightly. */
+ /* We are assuming V2 Lab16 encoding, since this is a lut16type that always uses */
+ /* this encoding */
+ if (outsig == icSigLabData) { /* Special case Lab */
+ double mn[3], mx[3];
+ /* The output of the CLUT will be an 8 or 16 bit value, and we want to */
+ /* adjust the range so that the input grid point holding the white */
+ /* point can encode 0.0 exactly. */
+ /* Note that in the case of the a & b values, the range equates to */
+ /* normalised 0.0 .. 1.0, since 0 can be represented exactly in it. */
+ if (p->outputEnt >= 64) {
+ if (p->ttype == icSigLut8Type) {
+ mn[0] = 0.0, mn[1] = mn[2] = -128.0;
+ mx[0] = 100.0, mx[1] = mx[2] = 127.0;
+ } else {
+ mn[0] = 0.0, mn[1] = mn[2] = -128.0;
+ mx[0] = 100.0, mx[1] = mx[2] = (65535.0 * 255.0)/65280.0 - 128.0;
+ }
+ otoentry(omin, mn);/* Convert from output color space to table representation */
+ otoentry(omax, mx);
+ } else {
+ for (e = 0; e < p->inputChan; e++) {
+ omin[e] = 0.0;
+ omax[e] = 1.0;
+ }
+ }
+ } else
+#endif
+ {
+ for (f = 0; f < p->outputChan; f++) {
+ omin[f] = 0.0; /* We are assuming this is true for all other color spaces. */
+ omax[f] = 1.0;
+ }
+ }
+ } else {
+ otoentry(omin, clutmin);/* Convert from output color space to table representation */
+ otoentry(omax, clutmax);
+ }
+
+ /* Create the input table entry values */
+ for (tn = 0; tn < ntables; tn++) {
+ pn = pp[tn];
+ for (n = 0; n < pn->inputEnt; n++) {
+ double fv;
+ fv = n/(pn->inputEnt-1.0);
+ for (e = 0; e < pn->inputChan; e++)
+ iv[e] = fv;
+
+ ifromindex(iv,iv); /* Convert from index value to input color space value */
+
+ if (infunc != NULL)
+ infunc(cbctx, iv, iv); /* In colorspace -> input table -> In colorspace. */
+
+ itoentry(iv,iv); /* Convert from input color space value to table value */
+
+ /* Expand used range to 0.0 - 1.0, and clip to legal values */
+ /* Note that if the range is reduced, and clipping occurs, */
+ /* then there should be enough resolution within the input */
+ /* table, to represent the sharp edges of the clipping. */
+ for (e = 0; e < pn->inputChan; e++) {
+ double tt;
+ tt = (iv[e] - imin[e])/(imax[e] - imin[e]);
+ if (tt < 0.0) {
+ DBGSLC(("iclip: tt = %f, iv = %f, omin = %f, omax = %f\n",tt,iv[e],omin[e],omax[e]));
+ if (tt < -CLIP_MARGIN)
+ clip = 1;
+ tt = 0.0;
+ } else if (tt > 1.0) {
+ DBGSLC(("iclip: tt = %f, iv = %f, omin = %f, omax = %f\n",tt,iv[e],omin[e],omax[e]));
+ if (tt > (1.0 + CLIP_MARGIN))
+ clip = 1;
+ tt = 1.0;
+ }
+ iv[e] = tt;
+ }
+
+ for (e = 0; e < pn->inputChan; e++) /* Input tables */
+ pn->inputTable[e * pn->inputEnt + n] = iv[e];
+ }
+ }
+
+ /* Allocate space for cell center value lookup */
+ if (flags & ICM_CLUT_SET_APXLS) {
+ if ((clutTable2 = (double **) icp->al->calloc(icp->al,sizeof(double *), ntables)) == NULL) {
+ sprintf(icp->err,"icmLut_set_tables malloc of cube center array failed");
+ icp->al->free(icp->al, _iv);
+ return icp->errc = 1;
+ }
+ for (tn = 0; tn < ntables; tn++) {
+ if ((clutTable2[tn] = (double *) icp->al->calloc(icp->al,sizeof(double),
+ p->clutTable_size)) == NULL) {
+ for (--tn; tn >= 0; tn--)
+ icp->al->free(icp->al, clutTable2[tn]);
+ icp->al->free(icp->al, _iv);
+ icp->al->free(icp->al, clutTable2);
+ sprintf(icp->err,"icmLut_set_tables malloc of cube center array failed");
+ return icp->errc = 1;
+ }
+ }
+ }
+
+ /* Allocate space for smoothing radius values */
+ if (flags & ICM_CLUT_SET_FILTER) {
+ unsigned int j, g, size;
+
+ /* Private: compute dimensional increment though clut3 */
+ i = p->inputChan-1;
+ dinc3[i--] = ntables;
+ for (; i < p->inputChan; i--)
+ dinc3[i] = dinc3[i+1] * p->clutPoints;
+
+ /* Private: compute offsets from base of cube to other corners */
+ for (dcube3[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
+ for (i = 0; i < g; i++)
+ dcube3[g+i] = dcube3[i] + dinc3[j];
+ g *= 2;
+ }
+
+ if ((size = sat_mul(ntables, sat_pow(p->clutPoints,p->inputChan))) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_alloc size overflow");
+ if (flags & ICM_CLUT_SET_APXLS) {
+ for (tn = 0; tn < ntables; tn++)
+ icp->al->free(icp->al, clutTable2[tn]);
+ }
+ icp->al->free(icp->al, clutTable2);
+ icp->al->free(icp->al, _iv);
+ return icp->errc = 1;
+ }
+
+ if ((clutTable3 = (double *) icp->al->calloc(icp->al,sizeof(double),
+ size)) == NULL) {
+ if (flags & ICM_CLUT_SET_APXLS) {
+ for (tn = 0; tn < ntables; tn++)
+ icp->al->free(icp->al, clutTable2[tn]);
+ }
+ icp->al->free(icp->al, clutTable2);
+ icp->al->free(icp->al, _iv);
+ sprintf(icp->err,"icmLut_set_tables malloc of vertex smoothing value array failed");
+ return icp->errc = 1;
+ }
+ }
+
+ /* Create the multi-dimensional lookup table values */
+
+ /* To make this clut function cache friendly, we use the pseudo-hilbert */
+ /* count sequence. This keeps each point close to the last in the */
+ /* multi-dimensional space. This is the point of setting multiple Luts at */
+ /* once too - the assumption is that these tables are all related (different */
+ /* gamut compressions for instance), and hence calling the clutfunc() with */
+ /* close values will maximise reverse lookup cache hit rate. */
+
+ psh_init(&counter, p->inputChan, p->clutPoints, ii); /* Initialise counter */
+
+ /* Itterate through all verticies in the grid */
+ for (;;) {
+ int ti, ti3; /* Table indexes */
+
+ for (ti = e = 0; e < p->inputChan; e++) { /* Input tables */
+ ti += ii[e] * p->dinc[e]; /* Clut index */
+ iv[e] = ii[e]/(p->clutPoints-1.0); /* Vertex coordinates */
+ iv[e] = iv[e] * (imax[e] - imin[e]) + imin[e]; /* Undo expansion to 0.0 - 1.0 */
+ *((int *)&iv[-((int)e)-1]) = ii[e]; /* Trick to supply grid index in iv[] */
+ }
+
+ if (flags & ICM_CLUT_SET_FILTER) {
+ for (ti3 = e = 0; e < p->inputChan; e++) /* Input tables */
+ ti3 += ii[e] * dinc3[e]; /* Clut3 index */
+ }
+
+ DBGSL(("\nix %s\n",icmPiv(p->inputChan, ii)));
+ DBGSL(("raw itv %s to iv'",icmPdv(p->inputChan, iv)));
+ ifromentry(iv,iv); /* Convert from table value to input color space */
+ DBGSL((" %s\n",icmPdv(p->inputChan, iv)));
+
+ /* Apply incolor -> outcolor function we want to represent for all tables */
+ DBGSL(("iv: %s to ov'",icmPdv(p->inputChan, iv)));
+ clutfunc(cbctx, iv, iv);
+ DBGSL((" %s\n",icmPdv(p->outputChan, iv)));
+
+ /* Save the results to the output tables */
+ for (tn = 0, ivn = iv; tn < ntables; ivn += p->outputChan, tn++) {
+ pn = pp[tn];
+
+ DBGSL(("tn %d, ov' %s -> otv",tn,icmPdv(p->outputChan, ivn)));
+ otoentry(ivn,ivn); /* Convert from output color space value to table value */
+ DBGSL((" %s\n -> oval",icmPdv(p->outputChan, ivn)));
+
+ /* Expand used range to 0.0 - 1.0, and clip to legal values */
+ for (f = 0; f < pn->outputChan; f++) {
+ double tt;
+ tt = (ivn[f] - omin[f])/(omax[f] - omin[f]);
+ if (tt < 0.0) {
+ DBGSLC(("lclip: tt = %f, ivn= %f, omin = %f, omax = %f\n",tt,ivn[f],omin[f],omax[f]));
+ if (tt < -CLIP_MARGIN)
+ clip = 2;
+ tt = 0.0;
+ } else if (tt > 1.0) {
+ DBGSLC(("lclip: tt = %f, ivn= %f, omin = %f, omax = %f\n",tt,ivn[f],omin[f],omax[f]));
+ if (tt > (1.0 + CLIP_MARGIN))
+ clip = 2;
+ tt = 1.0;
+ }
+ ivn[f] = tt;
+ }
+
+ for (f = 0; f < pn->outputChan; f++) /* Output chans */
+ pn->clutTable[ti + f] = ivn[f];
+ DBGSL((" %s\n",icmPdv(pn->outputChan, ivn)));
+
+ if (flags & ICM_CLUT_SET_FILTER) {
+ clutTable3[ti3 + tn] = iv[-1 -tn]; /* Filter radiuses */
+ }
+ }
+
+ /* Lookup cell center value if ICM_CLUT_SET_APXLS */
+ if (clutTable2 != NULL) {
+
+ for (e = 0; e < p->inputChan; e++) {
+ if (ii[e] >= (p->clutPoints-1))
+ break; /* Don't lookup beyond last */
+ iv[e] = (ii[e] + 0.5)/(p->clutPoints-1.0); /* Vertex coordinates + 0.5 */
+ iv[e] = iv[e] * (imax[e] - imin[e]) + imin[e]; /* Undo expansion to 0.0 - 1.0 */
+ *((int *)&iv[-((int)e)-1]) = ii[e]; /* Trick to supply grid index in iv[] */
+ /* (Not this is only the base for +0.5) */
+ }
+
+ if (e >= p->inputChan) { /* We're not on the last row */
+
+ ifromentry(iv,iv); /* Convert from table value to input color space */
+
+ /* Apply incolor -> outcolor function we want to represent */
+ clutfunc(cbctx, iv, iv);
+
+ /* Save the results to the output tables */
+ for (tn = 0, ivn = iv; tn < ntables; ivn += p->outputChan, tn++) {
+ pn = pp[tn];
+
+ otoentry(ivn,ivn); /* Convert from output color space value to table value */
+
+ /* Expand used range to 0.0 - 1.0, and clip to legal values */
+ for (f = 0; f < pn->outputChan; f++) {
+ double tt;
+ tt = (ivn[f] - omin[f])/(omax[f] - omin[f]);
+ if (tt < 0.0) {
+ DBGSLC(("lclip: tt = %f, ivn= %f, omin = %f, omax = %f\n",tt,ivn[f],omin[f],omax[f]));
+ if (tt < -CLIP_MARGIN)
+ clip = 3;
+ tt = 0.0;
+ } else if (tt > 1.0) {
+ DBGSLC(("lclip: tt = %f, ivn= %f, omin = %f, omax = %f\n",tt,ivn[f],omin[f],omax[f]));
+ if (tt > (1.0 + CLIP_MARGIN))
+ clip = 3;
+ tt = 1.0;
+ }
+ ivn[f] = tt;
+ }
+
+ for (f = 0; f < pn->outputChan; f++) /* Output chans */
+ clutTable2[tn][ti + f] = ivn[f];
+ }
+ }
+ }
+
+ /* Increment index within block (Reverse index significancd) */
+ if (psh_inc(&counter, ii))
+ break;
+ }
+
+#define APXLS_WHT 0.5
+#define APXLS_DIFF_THRHESH 0.2
+ /* Deal with cell center value, aproximate least squares adjustment. */
+ /* Subtract some of the mean of the surrounding center values from each grid value. */
+ /* Skip the edges so that things like the white point are not changed. */
+ /* Avoid modifying the value if the difference between the */
+ /* interpolated value and the current value is too great, */
+ /* and there is the possibility of different color aliases. */
+ if (clutTable2 != NULL) {
+ int ti; /* cube vertex table index */
+ int ti2; /* cube center table2 index */
+ int ee;
+ double cw = 1.0/(double)(1 << p->inputChan); /* Weight for each cube corner */
+
+ /* For each cell center point except last row */
+ for (e = 0; e < p->inputChan; e++)
+ ii[e] = 0; /* init coords */
+
+ /* Compute linear interpolated value from center values */
+ for (ee = 0; ee < p->inputChan;) {
+
+ /* Compute base index for table2 */
+ for (ti2 = e = 0; e < p->inputChan; e++) /* Input tables */
+ ti2 += ii[e] * p->dinc[e]; /* Clut index */
+
+ ti = ti2 + p->dcube[(1 << p->inputChan)-1]; /* +1 to each coord for vertex index */
+
+ for (tn = 0; tn < ntables; tn++) {
+ double mval[MAX_CHAN], vv;
+ double maxd = 0.0;
+
+ pn = pp[tn];
+
+ /* Compute mean of center values */
+ for (f = 0; f < pn->outputChan; f++) { /* Output chans */
+
+ mval[f] = 0.0;
+ for (i = 0; i < (1 << p->inputChan); i++) { /* For surrounding center values */
+ mval[f] += clutTable2[tn][ti2 + p->dcube[i] + f];
+ }
+ mval[f] = pn->clutTable[ti + f] - mval[f] * cw; /* Diff to mean */
+ vv = fabs(mval[f]);
+ if (vv > maxd)
+ maxd = vv;
+ }
+
+ if (pn->outputChan <= 3 || maxd < APXLS_DIFF_THRHESH) {
+ for (f = 0; f < pn->outputChan; f++) { /* Output chans */
+
+ vv = pn->clutTable[ti + f] + APXLS_WHT * mval[f];
+
+ /* Hmm. This is a bit crude. How do we know valid range is 0-1 ? */
+ /* What about an ink limit ? */
+ if (vv < 0.0) {
+ vv = 0.0;
+ } else if (vv > 1.0) {
+ vv = 1.0;
+ }
+ pn->clutTable[ti + f] = vv;
+ }
+ }
+ }
+
+ /* Increment coord */
+ for (ee = 0; ee < p->inputChan; ee++) {
+ if (++ii[ee] < (p->clutPoints-2)) /* Don't go through upper edge */
+ break; /* No carry */
+ ii[ee] = 0;
+ }
+ }
+
+ /* Done with center values */
+ for (tn = 0; tn < ntables; tn++)
+ icp->al->free(icp->al, clutTable2[tn]);
+ icp->al->free(icp->al, clutTable2);
+ }
+
+ /* Apply any smoothing in the clipped region to the resulting clutTable */
+ if (clutTable3 != NULL) {
+ double *clutTable1; /* Copy of current unfilted values */
+ FCOUNT(cc, MAX_CHAN, p->inputChan); /* Surrounding counter */
+
+ if ((clutTable1 = (double *) icp->al->calloc(icp->al,sizeof(double),
+ p->clutTable_size)) == NULL) {
+ icp->al->free(icp->al, clutTable3);
+ icp->al->free(icp->al, _iv);
+ sprintf(icp->err,"icmLut_set_tables malloc of grid copy failed");
+ return icp->errc = 1;
+ }
+
+ for (tn = 0; tn < ntables; tn++) {
+ int aa;
+ int ee;
+ int ti, ti3; /* Table indexes */
+
+ pn = pp[tn];
+
+ /* For each pass */
+ for (aa = 0; aa < 2; aa++) {
+
+ /* Copy current values */
+ memcpy(clutTable1, pn->clutTable, sizeof(double) * pn->clutTable_size);
+
+ /* Filter each point */
+ for (e = 0; e < pn->inputChan; e++)
+ ii[e] = 0; /* init coords */
+
+ /* Compute linear interpolated error to actual cell center value */
+ for (ee = 0; ee < pn->inputChan;) {
+ double rr; /* Filter radius */
+ int ir; /* Integer radius */
+ double tw; /* Total weight */
+
+ /* Compute base index for this cell */
+ for (ti3 = ti = e = 0; e < pn->inputChan; e++) { /* Input tables */
+ ti += ii[e] * pn->dinc[e]; /* Clut index */
+ ti3 += ii[e] * dinc3[e]; /* Clut3 index */
+ }
+ rr = clutTable3[ti3 + tn] * (pn->clutPoints-1.0);
+ ir = (int)floor(rr + 0.5); /* Don't bother unless 1/2 over vertex */
+
+ if (ir < 1)
+ goto next_vert;
+
+ //FRECONF(cc, -ir, ir + 1); /* Set size of surroundign grid */
+
+ /* Clip scanning cube to be within grid */
+ for (e = 0; e < pn->inputChan; e++) {
+ int cr = ir;
+ if ((ii[e] - ir) < 0)
+ cr = ii[e];
+ if ((ii[e] + ir) >= pn->clutPoints)
+ cr = pn->clutPoints -1 -ii[e];
+
+ cc_stt[e] = -cr;
+ cc_res[e] = cr + 1;
+ }
+
+ for (f = 0; f < pn->outputChan; f++)
+ pn->clutTable[ti + f] = 0.0;
+ tw = 0.0;
+
+ FC_INIT(cc)
+ for (tw = 0.0; !FC_DONE(cc);) {
+ double r;
+ int tti;
+
+ /* Radius of this cell */
+ for (r = 0.0, tti = e = 0; e < pn->inputChan; e++) {
+ int ix;
+ r += cc[e] * cc[e];
+ tti += (ii[e] + cc[e]) * p->dinc[e];
+ }
+ r = sqrt(r);
+
+ if (r <= rr && e >= pn->inputChan) {
+ double w = (rr - r)/rr; /* Triangle weighting */
+ w = sqrt(w);
+ for (f = 0; f < pn->outputChan; f++)
+ pn->clutTable[ti+f] += w * clutTable1[tti + f];
+ tw += w;
+ }
+ FC_INC(cc);
+ }
+ for (f = 0; f < pn->outputChan; f++) {
+ double vv = pn->clutTable[ti+f] / tw;
+ if (vv < 0.0) {
+ vv = 0.0;
+ } else if (vv > 1.0) {
+ vv = 1.0;
+ }
+ pn->clutTable[ti+f] = vv;
+ }
+
+ /* Increment coord */
+ next_vert:;
+ for (ee = 0; ee < pn->inputChan; ee++) {
+ if (++ii[ee] < (pn->clutPoints-1)) /* Don't go through upper edge */
+ break; /* No carry */
+ ii[ee] = 0;
+ }
+ } /* Next grid point to filter */
+ } /* Next pass */
+ } /* Next table */
+
+ free(clutTable1);
+ free(clutTable3);
+ }
+
+ /* Create the output table entry values */
+ for (tn = 0; tn < ntables; tn++) {
+ pn = pp[tn];
+ for (n = 0; n < pn->outputEnt; n++) {
+ double fv;
+ fv = n/(pn->outputEnt-1.0);
+ for (f = 0; f < pn->outputChan; f++)
+ iv[f] = fv;
+
+ /* Undo expansion to 0.0 - 1.0 */
+ for (f = 0; f < pn->outputChan; f++) /* Output tables */
+ iv[f] = iv[f] * (omax[f] - omin[f]) + omin[f];
+
+ ofromentry(iv,iv); /* Convert from table value to output color space value */
+
+ if (outfunc != NULL)
+ outfunc(cbctx, iv, iv); /* Out colorspace -> output table -> out colorspace. */
+
+ otoentry(iv,iv); /* Convert from output color space value to table value */
+
+ /* Clip to legal values */
+ for (f = 0; f < pn->outputChan; f++) {
+ double tt;
+ tt = iv[f];
+ if (tt < 0.0) {
+ DBGSLC(("oclip: tt = %f\n",tt));
+ if (tt < -CLIP_MARGIN)
+ clip = 5;
+ tt = 0.0;
+ } else if (tt > 1.0) {
+ DBGSLC(("oclip: tt = %f\n",tt));
+ if (tt > (1.0 + CLIP_MARGIN))
+ clip = 5;
+ tt = 1.0;
+ }
+ iv[f] = tt;
+ }
+
+ for (f = 0; f < pn->outputChan; f++) /* Input tables */
+ pn->outputTable[f * pn->outputEnt + n] = iv[f];
+ }
+ }
+
+ icp->al->free(icp->al, _iv);
+
+ icp->warnc = 0;
+ if (clip) {
+ DBGSLC(("Returning clip status = %d\n",clip));
+ icp->warnc = clip;
+ }
+
+ return 0;
+}
+
+/* Helper function to initialize a Lut tables contents */
+/* from supplied transfer functions. */
+/* Set errc and return error number */
+/* Set warnc if there is clipping in the output values */
+static int icmLut_set_tables (
+icmLut *p, /* Pointer to Lut object */
+int flags, /* Setting flags */
+void *cbctx, /* Opaque callback context pointer value */
+icColorSpaceSignature insig, /* Input color space */
+icColorSpaceSignature outsig, /* Output color space */
+void (*infunc)(void *cbcntx, double *out, double *in),
+ /* Input transfer function, inspace->inspace' (NULL = default) */
+double *inmin, double *inmax, /* Maximum range of inspace' values (NULL = default) */
+void (*clutfunc)(void *cbctx, double *out, double *in),
+ /* inspace' -> outspace' transfer function */
+double *clutmin, double *clutmax, /* Maximum range of outspace' values (NULL = default) */
+void (*outfunc)(void *cbctx, double *out, double *in)
+ /* Output transfer function, outspace'->outspace (NULL = deflt) */
+) {
+ struct _icmLut *pp[3];
+
+ /* Simply call the multiple table function with one table */
+ pp[0] = p;
+ return icmSetMultiLutTables(1, pp, flags,
+ cbctx, insig, outsig,
+ infunc,
+ inmin, inmax,
+ clutfunc,
+ clutmin, clutmax,
+ outfunc);
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmLut_get_size(
+ icmBase *pp
+) {
+ icmLut *p = (icmLut *)pp;
+ unsigned int len = 0;
+
+ if (p->ttype == icSigLut8Type) {
+ len = sat_add(len, 48); /* tag and header */
+ len = sat_add(len, sat_mul3(1, p->inputChan, p->inputEnt));
+ len = sat_add(len, sat_mul3(1, p->outputChan, sat_pow(p->clutPoints,p->inputChan)));
+ len = sat_add(len, sat_mul3(1, p->outputChan, p->outputEnt));
+ } else {
+ len = sat_add(len, 52); /* tag and header */
+ len = sat_add(len, sat_mul3(2, p->inputChan, p->inputEnt));
+ len = sat_add(len, sat_mul3(2, p->outputChan, sat_pow(p->clutPoints,p->inputChan)));
+ len = sat_add(len, sat_mul3(2, p->outputChan, p->outputEnt));
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmLut_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmLut *p = (icmLut *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i, j, g, size;
+ char *bp, *buf;
+
+ if (len < 4) {
+ sprintf(icp->err,"icmLut_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmLut_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmLut_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
+ if (p->ttype != icSigLut8Type && p->ttype != icSigLut16Type) {
+ sprintf(icp->err,"icmLut_read: Wrong tag type for icmLut");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ if (p->ttype == icSigLut8Type) {
+ if (len < 48) {
+ sprintf(icp->err,"icmLut_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ } else {
+ if (len < 52) {
+ sprintf(icp->err,"icmLut_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+
+ /* Read in the info common to 8 and 16 bit Lut */
+ p->inputChan = read_UInt8Number(bp+8);
+ p->outputChan = read_UInt8Number(bp+9);
+ p->clutPoints = read_UInt8Number(bp+10);
+
+ /* Sanity check */
+ if (p->inputChan < 1) {
+ sprintf(icp->err,"icmLut_read: No input channels!");
+ return icp->errc = 1;
+ }
+
+ if (p->inputChan > MAX_CHAN) {
+ sprintf(icp->err,"icmLut_read: Can't handle > %d input channels\n",MAX_CHAN);
+ return icp->errc = 1;
+ }
+
+ if (p->outputChan > MAX_CHAN) {
+ sprintf(icp->err,"icmLut_read: Can't handle > %d output channels\n",MAX_CHAN);
+ return icp->errc = 1;
+ }
+
+ /* Read 3x3 transform matrix */
+ for (j = 0; j < 3; j++) { /* Rows */
+ for (i = 0; i < 3; i++) { /* Columns */
+ p->e[j][i] = read_S15Fixed16Number(bp + 12 + ((j * 3 + i) * 4));
+ }
+ }
+ /* Read 16 bit specific stuff */
+ if (p->ttype == icSigLut8Type) {
+ p->inputEnt = 256; /* By definition */
+ p->outputEnt = 256; /* By definition */
+ bp = buf+48;
+ } else {
+ p->inputEnt = read_UInt16Number(bp+48);
+ p->outputEnt = read_UInt16Number(bp+50);
+ bp = buf+52;
+ }
+
+ /* Sanity check dimensions. This protects against */
+ /* subsequent integer overflows involving the dimensions. */
+ if ((size = icmLut_get_size((icmBase *)p)) == UINT_MAX
+ || size > len) {
+ sprintf(icp->err,"icmLut_read: Tag wrong size for contents");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read the input tables */
+ size = (p->inputChan * p->inputEnt);
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1)
+ p->inputTable[i] = read_DCS8Number(bp);
+ } else {
+ for (i = 0; i < size; i++, bp += 2)
+ p->inputTable[i] = read_DCS16Number(bp);
+ }
+
+ /* Read the clut table */
+ size = (p->outputChan * sat_pow(p->clutPoints,p->inputChan));
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1)
+ p->clutTable[i] = read_DCS8Number(bp);
+ } else {
+ for (i = 0; i < size; i++, bp += 2)
+ p->clutTable[i] = read_DCS16Number(bp);
+ }
+
+ /* Read the output tables */
+ size = (p->outputChan * p->outputEnt);
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1)
+ p->outputTable[i] = read_DCS8Number(bp);
+ } else {
+ for (i = 0; i < size; i++, bp += 2)
+ p->outputTable[i] = read_DCS16Number(bp);
+ }
+
+ /* Private: compute dimensional increment though clut */
+ /* Note that first channel varies least rapidly. */
+ i = p->inputChan-1;
+ p->dinc[i--] = p->outputChan;
+ for (; i < p->inputChan; i--)
+ p->dinc[i] = p->dinc[i+1] * p->clutPoints;
+
+ /* Private: compute offsets from base of cube to other corners */
+ for (p->dcube[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
+ for (i = 0; i < g; i++)
+ p->dcube[g+i] = p->dcube[i] + p->dinc[j];
+ g *= 2;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmLut_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmLut *p = (icmLut *)pp;
+ icc *icp = p->icp;
+ unsigned int i,j;
+ unsigned int len, size;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmLut_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write the info common to 8 and 16 bit Lut */
+ if ((rv = write_UInt8Number(p->inputChan, bp+8)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt8Number(p->outputChan, bp+9)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt8Number(p->clutPoints, bp+10)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ write_UInt8Number(0, bp+11); /* Set padding to 0 */
+
+ /* Write 3x3 transform matrix */
+ for (j = 0; j < 3; j++) { /* Rows */
+ for (i = 0; i < 3; i++) { /* Columns */
+ if ((rv = write_S15Fixed16Number(p->e[j][i],bp + 12 + ((j * 3 + i) * 4))) != 0) {
+ sprintf(icp->err,"icmLut_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write 16 bit specific stuff */
+ if (p->ttype == icSigLut8Type) {
+ if (p->inputEnt != 256 || p->outputEnt != 256) {
+ sprintf(icp->err,"icmLut_write: 8 bit Input and Output tables must be 256 entries");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp = buf+48;
+ } else {
+ if (p->inputEnt > 4096 || p->outputEnt > 4096) {
+ sprintf(icp->err,"icmLut_write: 16 bit Input and Output tables must each be less than 4096 entries");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if ((rv = write_UInt16Number(p->inputEnt, bp+48)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt16Number(p->outputEnt, bp+50)) != 0) {
+ sprintf(icp->err,"icmLut_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp = buf+52;
+ }
+
+ /* Write the input tables */
+ size = (p->inputChan * p->inputEnt);
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1) {
+ if ((rv = write_DCS8Number(p->inputTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: inputTable write_DCS8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ } else {
+ for (i = 0; i < size; i++, bp += 2) {
+ if ((rv = write_DCS16Number(p->inputTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: inputTable write_DCS16Number(%f) failed",p->inputTable[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write the clut table */
+ size = (p->outputChan * sat_pow(p->clutPoints,p->inputChan));
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1) {
+ if ((rv = write_DCS8Number(p->clutTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: clutTable write_DCS8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ } else {
+ for (i = 0; i < size; i++, bp += 2) {
+ if ((rv = write_DCS16Number(p->clutTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: clutTable write_DCS16Number(%f) failed",p->clutTable[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write the output tables */
+ size = (p->outputChan * p->outputEnt);
+ if (p->ttype == icSigLut8Type) {
+ for (i = 0; i < size; i++, bp += 1) {
+ if ((rv = write_DCS8Number(p->outputTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: outputTable write_DCS8Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ } else {
+ for (i = 0; i < size; i++, bp += 2) {
+ if ((rv = write_DCS16Number(p->outputTable[i], bp)) != 0) {
+ sprintf(icp->err,"icmLut_write: outputTable write_DCS16Number(%f) failed",p->outputTable[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write buffer to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmLut_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmLut_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmLut *p = (icmLut *)pp;
+ if (verb <= 0)
+ return;
+
+ if (p->ttype == icSigLut8Type) {
+ op->gprintf(op,"Lut8:\n");
+ } else {
+ op->gprintf(op,"Lut16:\n");
+ }
+ op->gprintf(op," Input Channels = %u\n",p->inputChan);
+ op->gprintf(op," Output Channels = %u\n",p->outputChan);
+ op->gprintf(op," CLUT resolution = %u\n",p->clutPoints);
+ op->gprintf(op," Input Table entries = %u\n",p->inputEnt);
+ op->gprintf(op," Output Table entries = %u\n",p->outputEnt);
+ op->gprintf(op," XYZ matrix = %f, %f, %f\n",p->e[0][0],p->e[0][1],p->e[0][2]);
+ op->gprintf(op," %f, %f, %f\n",p->e[1][0],p->e[1][1],p->e[1][2]);
+ op->gprintf(op," %f, %f, %f\n",p->e[2][0],p->e[2][1],p->e[2][2]);
+
+ if (verb >= 2) {
+ unsigned int i, j, size;
+ unsigned int ii[MAX_CHAN]; /* maximum no of input channels */
+
+ op->gprintf(op," Input table:\n");
+ for (i = 0; i < p->inputEnt; i++) {
+ op->gprintf(op," %3u: ",i);
+ for (j = 0; j < p->inputChan; j++)
+ op->gprintf(op," %1.10f",p->inputTable[j * p->inputEnt + i]);
+ op->gprintf(op,"\n");
+ }
+
+ op->gprintf(op,"\n CLUT table:\n");
+ if (p->inputChan > MAX_CHAN) {
+ op->gprintf(op," !!Can't dump > %d input channel CLUT table!!\n",MAX_CHAN);
+ } else {
+ size = (p->outputChan * sat_pow(p->clutPoints,p->inputChan));
+ for (j = 0; j < p->inputChan; j++)
+ ii[j] = 0;
+ for (i = 0; i < size;) {
+ unsigned int k;
+ /* Print table entry index */
+ op->gprintf(op," ");
+ for (j = p->inputChan-1; j < p->inputChan; j--)
+ op->gprintf(op," %2u",ii[j]);
+ op->gprintf(op,":");
+ /* Print table entry contents */
+ for (k = 0; k < p->outputChan; k++, i++)
+ op->gprintf(op," %1.10f",p->clutTable[i]);
+ op->gprintf(op,"\n");
+
+ for (j = 0; j < p->inputChan; j++) { /* Increment index */
+ ii[j]++;
+ if (ii[j] < p->clutPoints)
+ break; /* No carry */
+ ii[j] = 0;
+ }
+ }
+ }
+
+ op->gprintf(op,"\n Output table:\n");
+ for (i = 0; i < p->outputEnt; i++) {
+ op->gprintf(op," %3u: ",i);
+ for (j = 0; j < p->outputChan; j++)
+ op->gprintf(op," %1.10f",p->outputTable[j * p->outputEnt + i]);
+ op->gprintf(op,"\n");
+ }
+
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmLut_allocate(
+ icmBase *pp
+) {
+ unsigned int i, j, g, size;
+ icmLut *p = (icmLut *)pp;
+ icc *icp = p->icp;
+
+ /* Sanity check */
+ if (p->inputChan < 1) {
+ sprintf(icp->err,"icmLut_alloc: Can't handle %d input channels\n",p->inputChan);
+ return icp->errc = 1;
+ }
+
+ if (p->inputChan > MAX_CHAN) {
+ sprintf(icp->err,"icmLut_alloc: Can't handle > %d input channels\n",MAX_CHAN);
+ return icp->errc = 1;
+ }
+
+ if (p->outputChan > MAX_CHAN) {
+ sprintf(icp->err,"icmLut_alloc: Can't handle > %d output channels\n",MAX_CHAN);
+ return icp->errc = 1;
+ }
+
+ if ((size = sat_mul(p->inputChan, p->inputEnt)) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_alloc size overflow");
+ return icp->errc = 1;
+ }
+ if (size != p->inputTable_size) {
+ if (ovr_mul(size, sizeof(double))) {
+ sprintf(icp->err,"icmLut_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->inputTable != NULL)
+ icp->al->free(icp->al, p->inputTable);
+ if ((p->inputTable = (double *) icp->al->calloc(icp->al,size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmLut_alloc: calloc() of Lut inputTable data failed");
+ return icp->errc = 2;
+ }
+ p->inputTable_size = size;
+ }
+ if ((size = sat_mul(p->outputChan, sat_pow(p->clutPoints,p->inputChan))) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_alloc size overflow");
+ return icp->errc = 1;
+ }
+ if (size != p->clutTable_size) {
+ if (ovr_mul(size, sizeof(double))) {
+ sprintf(icp->err,"icmLut_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->clutTable != NULL)
+ icp->al->free(icp->al, p->clutTable);
+ if ((p->clutTable = (double *) icp->al->calloc(icp->al,size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmLut_alloc: calloc() of Lut clutTable data failed");
+ return icp->errc = 2;
+ }
+ p->clutTable_size = size;
+ }
+ if ((size = sat_mul(p->outputChan, p->outputEnt)) == UINT_MAX) {
+ sprintf(icp->err,"icmLut_alloc size overflow");
+ return icp->errc = 1;
+ }
+ if (size != p->outputTable_size) {
+ if (ovr_mul(size, sizeof(double))) {
+ sprintf(icp->err,"icmLut_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->outputTable != NULL)
+ icp->al->free(icp->al, p->outputTable);
+ if ((p->outputTable = (double *) icp->al->calloc(icp->al,size, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmLut_alloc: calloc() of Lut outputTable data failed");
+ return icp->errc = 2;
+ }
+ p->outputTable_size = size;
+ }
+
+ /* Private: compute dimensional increment though clut */
+ /* Note that first channel varies least rapidly. */
+ i = p->inputChan-1;
+ p->dinc[i--] = p->outputChan;
+ for (; i < p->inputChan; i--)
+ p->dinc[i] = p->dinc[i+1] * p->clutPoints;
+
+ /* Private: compute offsets from base of cube to other corners */
+ for (p->dcube[0] = 0, g = 1, j = 0; j < p->inputChan; j++) {
+ for (i = 0; i < g; i++)
+ p->dcube[g+i] = p->dcube[i] + p->dinc[j];
+ g *= 2;
+ }
+
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmLut_delete(
+ icmBase *pp
+) {
+ icmLut *p = (icmLut *)pp;
+ icc *icp = p->icp;
+ int i;
+
+ if (p->inputTable != NULL)
+ icp->al->free(icp->al, p->inputTable);
+ if (p->clutTable != NULL)
+ icp->al->free(icp->al, p->clutTable);
+ if (p->outputTable != NULL)
+ icp->al->free(icp->al, p->outputTable);
+ for (i = 0; i < p->inputChan; i++)
+ icmTable_delete_bwd(icp, &p->rit[i]);
+ for (i = 0; i < p->outputChan; i++)
+ icmTable_delete_bwd(icp, &p->rot[i]);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmLut(
+ icc *icp
+) {
+ int i,j;
+ icmLut *p;
+ if ((p = (icmLut *) icp->al->calloc(icp->al,1,sizeof(icmLut))) == NULL)
+ return NULL;
+ p->ttype = icSigLut16Type;
+ p->refcount = 1;
+ p->get_size = icmLut_get_size;
+ p->read = icmLut_read;
+ p->write = icmLut_write;
+ p->dump = icmLut_dump;
+ p->allocate = icmLut_allocate;
+ p->del = icmLut_delete;
+
+ /* Lookup methods */
+ p->nu_matrix = icmLut_nu_matrix;
+ p->min_max = icmLut_min_max;
+ p->lookup_matrix = icmLut_lookup_matrix;
+ p->lookup_input = icmLut_lookup_input;
+ p->lookup_clut_nl = icmLut_lookup_clut_nl;
+ p->lookup_clut_sx = icmLut_lookup_clut_sx;
+ p->lookup_output = icmLut_lookup_output;
+
+ /* Set method */
+ p->set_tables = icmLut_set_tables;
+
+ p->icp = icp;
+
+ /* Set matrix to reasonable default */
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++) {
+ if (i == j)
+ p->e[i][j] = 1.0;
+ else
+ p->e[i][j] = 0.0;
+ }
+
+ /* Init lookups to non-dangerous values */
+ for (i = 0; i < MAX_CHAN; i++)
+ p->dinc[i] = 0;
+
+ for (i = 0; i < (1 << MAX_CHAN); i++)
+ p->dcube[i] = 0;
+
+ for (i = 0; i < MAX_CHAN; i++) {
+ p->rit[i].inited = 0;
+ p->rot[i].inited = 0;
+ }
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* Measurement */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmMeasurement_get_size(
+ icmBase *pp
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for standard observer */
+ len = sat_add(len, 12); /* 12 for XYZ of measurement backing */
+ len = sat_add(len, 4); /* 4 for measurement geometry */
+ len = sat_add(len, 4); /* 4 for measurement flare */
+ len = sat_add(len, 4); /* 4 for standard illuminant */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmMeasurement_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmMeasurement *p = (icmMeasurement *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf;
+
+ if (len < 36) {
+ sprintf(icp->err,"icmMeasurement_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmMeasurement_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmMeasurement_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmMeasurement_read: Wrong tag type for icmMeasurement");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read the encoded standard observer */
+ p->observer = (icStandardObserver)read_SInt32Number(bp + 8);
+
+ /* Read the XYZ values for measurement backing */
+ if ((rv = read_XYZNumber(&p->backing, bp+12)) != 0) {
+ sprintf(icp->err,"icmMeasurement: read_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Read the encoded measurement geometry */
+ p->geometry = (icMeasurementGeometry)read_SInt32Number(bp + 24);
+
+ /* Read the proportion of flare */
+ p->flare = read_U16Fixed16Number(bp + 28);
+
+ /* Read the encoded standard illuminant */
+ p->illuminant = (icIlluminant)read_SInt32Number(bp + 32);
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmMeasurement_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmMeasurement *p = (icmMeasurement *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmMeasurement_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmMeasurement_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmMeasurement_write, type: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write the encoded standard observer */
+ if ((rv = write_SInt32Number((int)p->observer, bp + 8)) != 0) {
+ sprintf(icp->err,"icmMeasurementa_write, observer: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the XYZ values for measurement backing */
+ if ((rv = write_XYZNumber(&p->backing, bp+12)) != 0) {
+ sprintf(icp->err,"icmMeasurement, backing: write_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the encoded measurement geometry */
+ if ((rv = write_SInt32Number((int)p->geometry, bp + 24)) != 0) {
+ sprintf(icp->err,"icmMeasurementa_write, geometry: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the proportion of flare */
+ if ((rv = write_U16Fixed16Number(p->flare, bp + 28)) != 0) {
+ sprintf(icp->err,"icmMeasurementa_write, flare: write_U16Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the encoded standard illuminant */
+ if ((rv = write_SInt32Number((int)p->illuminant, bp + 32)) != 0) {
+ sprintf(icp->err,"icmMeasurementa_write, illuminant: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmMeasurement_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmMeasurement_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmMeasurement *p = (icmMeasurement *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Measurement:\n");
+ op->gprintf(op," Standard Observer = %s\n", string_StandardObserver(p->observer));
+ op->gprintf(op," XYZ for Measurement Backing = %s\n", string_XYZNumber_and_Lab(&p->backing));
+ op->gprintf(op," Measurement Geometry = %s\n", string_MeasurementGeometry(p->geometry));
+ op->gprintf(op," Measurement Flare = %5.1f%%\n", p->flare * 100.0);
+ op->gprintf(op," Standard Illuminant = %s\n", string_Illuminant(p->illuminant));
+}
+
+/* Allocate variable sized data elements */
+static int icmMeasurement_allocate(
+ icmBase *pp
+) {
+ /* Nothing to do */
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmMeasurement_delete(
+ icmBase *pp
+) {
+ icmMeasurement *p = (icmMeasurement *)pp;
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmMeasurement(
+ icc *icp
+) {
+ icmMeasurement *p;
+ if ((p = (icmMeasurement *) icp->al->calloc(icp->al,1,sizeof(icmMeasurement))) == NULL)
+ return NULL;
+ p->ttype = icSigMeasurementType;
+ p->refcount = 1;
+ p->get_size = icmMeasurement_get_size;
+ p->read = icmMeasurement_read;
+ p->write = icmMeasurement_write;
+ p->dump = icmMeasurement_dump;
+ p->allocate = icmMeasurement_allocate;
+ p->del = icmMeasurement_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Named color structure read/write support */
+static int read_NamedColorVal(
+ icmNamedColorVal *p,
+ char *bp,
+ char *end,
+ icColorSpaceSignature pcs, /* Header Profile Connection Space */
+ unsigned int ndc /* Number of device corrds */
+) {
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int mxl; /* Max possible string length */
+ int rv;
+
+ if (bp > end) {
+ sprintf(icp->err,"icmNamedColorVal_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ mxl = (end - bp) < 32 ? (end - bp) : 32;
+ if ((rv = check_null_string(bp,mxl)) == 1) {
+ sprintf(icp->err,"icmNamedColorVal_read: Root name string not terminated");
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ strcpy(p->root, bp);
+ bp += strlen(p->root) + 1;
+ if (bp > end || ndc > (end - bp)) {
+ sprintf(icp->err,"icmNamedColorVal_read: Data too short to read device coords");
+ return icp->errc = 1;
+ }
+ for (i = 0; i < ndc; i++) {
+ p->deviceCoords[i] = read_DCS8Number(bp);
+ bp += 1;
+ }
+ return 0;
+}
+
+static int read_NamedColorVal2(
+ icmNamedColorVal *p,
+ char *bp,
+ char *end,
+ icColorSpaceSignature pcs, /* Header Profile Connection Space */
+ unsigned int ndc /* Number of device coords */
+) {
+ int rv;
+ icc *icp = p->icp;
+ unsigned int i;
+
+ if (bp > end
+ || (32 + 6) > (end - bp)
+ || ndc > (end - bp - 32 - 6)/2) {
+ sprintf(icp->err,"icmNamedColorVal2_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,32)) == 1) {
+ sprintf(icp->err,"icmNamedColorVal2_read: Root name string not terminated");
+ return icp->errc = 1;
+ }
+ memmove((void *)p->root,(void *)(bp + 0),32);
+ switch(pcs) {
+ case icSigXYZData:
+ read_PCSNumber(icp, icSigXYZData, p->pcsCoords, bp+32);
+ break;
+ case icSigLabData:
+ /* namedColor2Type retains legacy Lab encoding */
+ read_PCSNumber(icp, icmSigLabV2Data, p->pcsCoords, bp+32);
+ break;
+ default:
+ return 1; /* Unknown PCS */
+ }
+ for (i = 0; i < ndc; i++)
+ p->deviceCoords[i] = read_DCS16Number(bp + 32 + 6 + 2 * i);
+ return 0;
+}
+
+static int write_NamedColorVal(
+ icmNamedColorVal *p,
+ char *d,
+ icColorSpaceSignature pcs, /* Header Profile Connection Space */
+ unsigned int ndc /* Number of device corrds */
+) {
+ icc *icp = p->icp;
+ unsigned int i;
+ int rv;
+
+ if ((rv = check_null_string(p->root,32)) == 1) {
+ sprintf(icp->err,"icmNamedColorVal_write: Root string names is unterminated");
+ return icp->errc = 1;
+ }
+ strcpy(d, p->root);
+ d += strlen(p->root) + 1;
+ for (i = 0; i < ndc; i++) {
+ if ((rv = write_DCS8Number(p->deviceCoords[i], d)) != 0) {
+ sprintf(icp->err,"icmNamedColorVal_write: write of device coord failed");
+ return icp->errc = 1;
+ }
+ d += 1;
+ }
+ return 0;
+}
+
+static int write_NamedColorVal2(
+ icmNamedColorVal *p,
+ char *bp,
+ icColorSpaceSignature pcs, /* Header Profile Connection Space */
+ unsigned int ndc /* Number of device coords */
+) {
+ icc *icp = p->icp;
+ unsigned int i;
+ int rv;
+
+ if ((rv = check_null_string(p->root,32)) == 1) {
+ sprintf(icp->err,"icmNamedColorVal2_write: Root string names is unterminated");
+ return icp->errc = 1;
+ }
+ rv = 0;
+ memmove((void *)(bp + 0),(void *)p->root,32);
+ switch(pcs) {
+ case icSigXYZData:
+ rv |= write_PCSNumber(icp, icSigXYZData, p->pcsCoords, bp+32);
+ break;
+ case icSigLabData:
+ /* namedColor2Type retains legacy Lab encoding */
+ rv |= write_PCSNumber(icp, icmSigLabV2Data, p->pcsCoords, bp+32);
+ break;
+ default:
+ sprintf(icp->err,"icmNamedColorVal2_write: Unknown PCS");
+ return icp->errc = 1;
+ }
+ if (rv) {
+ sprintf(icp->err,"icmNamedColorVal2_write: write of PCS coord failed");
+ return icp->errc = 1;
+ }
+ for (i = 0; i < ndc; i++) {
+ if ((rv = write_DCS16Number(p->deviceCoords[i], bp + 32 + 6 + 2 * i)) != 0) {
+ sprintf(icp->err,"icmNamedColorVal2_write: write of device coord failed");
+ return icp->errc = 1;
+ }
+ }
+ return 0;
+}
+
+/* - - - - - - - - - - - */
+/* icmNamedColor object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmNamedColor_get_size(
+ icmBase *pp
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ unsigned int len = 0;
+ if (p->ttype == icSigNamedColorType) {
+ unsigned int i;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for vendor specific flags */
+ len = sat_add(len, 4); /* 4 for count of named colors */
+ len = sat_add(len, strlen(p->prefix) + 1); /* prefix of color names */
+ len = sat_add(len, strlen(p->suffix) + 1); /* suffix of color names */
+ for (i = 0; i < p->count; i++) {
+ len = sat_add(len, strlen(p->data[i].root) + 1); /* color names */
+ len = sat_add(len, p->nDeviceCoords * 1); /* bytes for each named color */
+ }
+ } else { /* Named Color 2 */
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for vendor specific flags */
+ len = sat_add(len, 4); /* 4 for count of named colors */
+ len = sat_add(len, 4); /* 4 for number of device coords */
+ len = sat_add(len, 32); /* 32 for prefix of color names */
+ len = sat_add(len, 32); /* 32 for suffix of color names */
+ len = sat_add(len, sat_mul(p->count, (32 + 6 + p->nDeviceCoords * 2)));
+ /* bytes for each named color */
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmNamedColor_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ char *bp, *buf, *end;
+ int rv;
+
+ if (len < 4) {
+ sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmNamedColor_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmNamedColor_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
+ if (p->ttype != icSigNamedColorType && p->ttype != icSigNamedColor2Type) {
+ sprintf(icp->err,"icmNamedColor_read: Wrong tag type for icmNamedColor");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ if (p->ttype == icSigNamedColorType) {
+ if (len < 16) {
+ sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Make sure that the number of device coords in known */
+ p->nDeviceCoords = number_ColorSpaceSignature(icp->header->colorSpace);
+ if (p->nDeviceCoords > MAX_CHAN) {
+ sprintf(icp->err,"icmNamedColor_read: Can't handle more than %d device channels",MAX_CHAN);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ } else { /* icmNC2 */
+ if (len < 84) {
+ sprintf(icp->err,"icmNamedColor_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+
+ /* Read vendor specific flag */
+ p->vendorFlag = read_UInt32Number(bp+8);
+
+ /* Read count of named colors */
+ p->count = read_UInt32Number(bp+12);
+
+ if (p->ttype == icSigNamedColorType) {
+ unsigned int mxl; /* Max possible string length */
+ bp = bp + 16;
+
+ /* Prefix for each color name */
+ if (bp > end) {
+ sprintf(icp->err,"icmNamedColor_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ mxl = (end - bp) < 32 ? (end - bp) : 32;
+ if ((rv = check_null_string(bp,mxl)) == 1) {
+ sprintf(icp->err,"icmNamedColor_read: Color prefix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ strcpy(p->prefix, bp);
+ bp += strlen(p->prefix) + 1;
+
+ /* Suffix for each color name */
+ if (bp > end) {
+ sprintf(icp->err,"icmNamedColor_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ mxl = (end - bp) < 32 ? (end - bp) : 32;
+ if ((rv = check_null_string(bp,mxl)) == 1) {
+ sprintf(icp->err,"icmNamedColor_read: Color suffix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ strcpy(p->suffix, bp);
+ bp += strlen(p->suffix) + 1;
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < p->count; i++) {
+ if ((rv = read_NamedColorVal(p->data+i, bp, end, icp->header->pcs, p->nDeviceCoords)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ bp += strlen(p->data[i].root) + 1;
+ bp += p->nDeviceCoords * 1;
+ }
+ } else { /* icmNC2 */
+ /* Number of device coords per color */
+ p->nDeviceCoords = read_UInt32Number(bp+16);
+ if (p->nDeviceCoords > MAX_CHAN) {
+ sprintf(icp->err,"icmNamedColor_read: Can't handle more than %d device channels",MAX_CHAN);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Prefix for each color name */
+ memmove((void *)p->prefix, (void *)(bp + 20), 32);
+ if ((rv = check_null_string(p->prefix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_read: Color prefix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Suffix for each color name */
+ memmove((void *)p->suffix, (void *)(bp + 52), 32);
+ if ((rv = check_null_string(p->suffix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_read: Color suffix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read all the data from the buffer */
+ bp = bp + 84;
+ for (i = 0; i < p->count; i++) {
+ if ((rv = read_NamedColorVal2(p->data+i, bp, end, icp->header->pcs, p->nDeviceCoords)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ bp += 32 + 6 + p->nDeviceCoords * 2;
+ }
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmNamedColor_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmNamedColor_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmNamedColor_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmNamedColor_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write vendor specific flag */
+ if ((rv = write_UInt32Number(p->vendorFlag, bp+8)) != 0) {
+ sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write count of named colors */
+ if ((rv = write_UInt32Number(p->count, bp+12)) != 0) {
+ sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ if (p->ttype == icSigNamedColorType) {
+ bp = bp + 16;
+
+ /* Prefix for each color name */
+ if ((rv = check_null_string(p->prefix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ strcpy(bp, p->prefix);
+ bp += strlen(p->prefix) + 1;
+
+ /* Suffix for each color name */
+ if ((rv = check_null_string(p->suffix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ strcpy(bp, p->suffix);
+ bp += strlen(p->suffix) + 1;
+
+ /* Write all the data to the buffer */
+
+ for (i = 0; i < p->count; i++) {
+ if ((rv = write_NamedColorVal(p->data+i, bp, icp->header->pcs, p->nDeviceCoords)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ bp += strlen(p->data[i].root) + 1;
+ bp += p->nDeviceCoords * 1;
+ }
+ } else { /* icmNC2 */
+ /* Number of device coords per color */
+ if ((rv = write_UInt32Number(p->nDeviceCoords, bp+16)) != 0) {
+ sprintf(icp->err,"icmNamedColor_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Prefix for each color name */
+ if ((rv = check_null_string(p->prefix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_write: Color prefix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ memmove((void *)(bp + 20), (void *)p->prefix, 32);
+
+ /* Suffix for each color name */
+ if ((rv = check_null_string(p->suffix,32)) == 1) {
+ sprintf(icp->err,"icmNamedColor_write: Color sufix is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ memmove((void *)(bp + 52), (void *)p->suffix, 32);
+
+ /* Write all the data to the buffer */
+ bp = bp + 84;
+ for (i = 0; i < p->count; i++, bp += (32 + 6 + p->nDeviceCoords * 2)) {
+ if ((rv = write_NamedColorVal2(p->data+i, bp, icp->header->pcs, p->nDeviceCoords)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmNamedColor_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmNamedColor_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+ if (verb <= 0)
+ return;
+
+ if (p->ttype == icSigNamedColorType)
+ op->gprintf(op,"NamedColor:\n");
+ else
+ op->gprintf(op,"NamedColor2:\n");
+ op->gprintf(op," Vendor Flag = 0x%x\n",p->vendorFlag);
+ op->gprintf(op," No. colors = %u\n",p->count);
+ op->gprintf(op," No. dev. coords = %u\n",p->nDeviceCoords);
+ op->gprintf(op," Name prefix = '%s'\n",p->prefix);
+ op->gprintf(op," Name suffix = '%s'\n",p->suffix);
+ if (verb >= 2) {
+ unsigned int i, n;
+ icmNamedColorVal *vp;
+ for (i = 0; i < p->count; i++) {
+ vp = p->data + i;
+ op->gprintf(op," Color %lu:\n",i);
+ op->gprintf(op," Name root = '%s'\n",vp->root);
+
+ if (p->ttype == icSigNamedColor2Type) {
+ switch(icp->header->pcs) {
+ case icSigXYZData:
+ op->gprintf(op," XYZ = %f, %f, %f\n",
+ vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
+ break;
+ case icSigLabData:
+ op->gprintf(op," Lab = %f, %f, %f\n",
+ vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
+ break;
+ default:
+ op->gprintf(op," Unexpected PCS\n");
+ break;
+ }
+ }
+ if (p->nDeviceCoords > 0) {
+ op->gprintf(op," Device Coords = ");
+ for (n = 0; n < p->nDeviceCoords; n++) {
+ if (n > 0)
+ op->gprintf(op,", ");
+ op->gprintf(op,"%f",vp->deviceCoords[n]);
+ }
+ op->gprintf(op,"\n");
+ }
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmNamedColor_allocate(
+ icmBase *pp
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+
+ if (p->count != p->_count) {
+ unsigned int i;
+ if (ovr_mul(p->count, sizeof(icmNamedColorVal))) {
+ sprintf(icp->err,"icmNamedColor_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmNamedColorVal *) icp->al->calloc(icp->al,p->count, sizeof(icmNamedColorVal))) == NULL) {
+ sprintf(icp->err,"icmNamedColor_alloc: malloc() of icmNamedColor data failed");
+ return icp->errc = 2;
+ }
+ for (i = 0; i < p->count; i++) {
+ p->data[i].icp = icp; /* Do init */
+ }
+ p->_count = p->count;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmNamedColor_delete(
+ icmBase *pp
+) {
+ icmNamedColor *p = (icmNamedColor *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmNamedColor(
+ icc *icp
+) {
+ icmNamedColor *p;
+ if ((p = (icmNamedColor *) icp->al->calloc(icp->al,1,sizeof(icmNamedColor))) == NULL)
+ return NULL;
+ p->ttype = icSigNamedColor2Type;
+ p->refcount = 1;
+ p->get_size = icmNamedColor_get_size;
+ p->read = icmNamedColor_read;
+ p->write = icmNamedColor_write;
+ p->dump = icmNamedColor_dump;
+ p->allocate = icmNamedColor_allocate;
+ p->del = icmNamedColor_delete;
+ p->icp = icp;
+
+ /* Default the the number of device coords appropriately for NamedColorType */
+ p->nDeviceCoords = number_ColorSpaceSignature(icp->header->colorSpace);
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* Colorant table structure read/write support */
+/* (Contribution from Piet Vandenborre) */
+
+static int read_ColorantTableVal(
+ icmColorantTableVal *p,
+ char *bp,
+ char *end,
+ icColorSpaceSignature pcs /* Header Profile Connection Space */
+) {
+ int rv;
+ icc *icp = p->icp;
+ if (bp > end || (32 + 6) > (end - bp)) {
+ sprintf(icp->err,"icmColorantTableVal_read: Data too short to read");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,32)) == 1) {
+ sprintf(icp->err,"icmColorantTableVal_read: Name string not terminated");
+ return icp->errc = 1;
+ }
+ memmove((void *)p->name,(void *)(bp + 0),32);
+ switch(pcs) {
+ case icSigXYZData:
+ case icSigLabData:
+ read_PCSNumber(icp, pcs, p->pcsCoords, bp+32);
+ break;
+ default:
+ return 1; /* Unknown PCS */
+ }
+ return 0;
+}
+
+static int write_ColorantTableVal(
+ icmColorantTableVal *p,
+ char *bp,
+ icColorSpaceSignature pcs /* Header Profile Connection Space */
+) {
+ int rv;
+ icc *icp = p->icp;
+
+ if ((rv = check_null_string(p->name,32)) == 1) {
+ sprintf(icp->err,"icmColorantTableVal_write: Name string is unterminated");
+ return icp->errc = 1;
+ }
+ memmove((void *)(bp + 0),(void *)p->name,32);
+ rv = 0;
+ switch(pcs) {
+ case icSigXYZData:
+ case icSigLabData:
+ rv |= write_PCSNumber(icp, pcs, p->pcsCoords, bp+32);
+ break;
+ default:
+ sprintf(icp->err,"icmColorantTableVal_write: Unknown PCS");
+ return icp->errc = 1;
+ }
+ if (rv) {
+ sprintf(icp->err,"icmColorantTableVal_write: write of PCS coord failed");
+ return icp->errc = 1;
+ }
+ return 0;
+}
+
+/* - - - - - - - - - - - */
+/* icmColorantTable object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmColorantTable_get_size(
+ icmBase *pp
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ unsigned int len = 0;
+ if (p->ttype == icSigColorantTableType
+ || p->ttype == icmSigAltColorantTableType) {
+ unsigned int i;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for count of colorants */
+ for (i = 0; i < p->count; i++) {
+ len = sat_add(len, 32); /* colorant names - 32 bytes*/
+ len = sat_add(len, 6); /* colorant pcs value - 3 x 16bit number*/
+ }
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmColorantTable_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+ icColorSpaceSignature pcs;
+ unsigned int i;
+ char *bp, *buf, *end;
+ int rv = 0;
+
+ if (icp->header->deviceClass != icSigLinkClass)
+ pcs = icp->header->pcs;
+ else
+ pcs = icSigLabData;
+
+ if (len < 4) {
+ sprintf(icp->err,"icmColorantTable_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmColorantTable_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmColorantTable_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ p->ttype = (icTagTypeSignature)read_SInt32Number(bp);
+ if (p->ttype != icSigColorantTableType
+ && p->ttype != icmSigAltColorantTableType) {
+ sprintf(icp->err,"icmColorantTable_read: Wrong tag type for icmColorantTable");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ if (len < 12) {
+ sprintf(icp->err,"icmColorantTable_read: Tag too small to be legal");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read count of colorants */
+ if (p->ttype == icmSigAltColorantTableType)
+ p->count = read_UInt8Number(bp+8); /* Hmm. This is Little Endian */
+ else
+ p->count = read_UInt32Number(bp+8);
+
+ if (p->count > ((len - 12) / (32 + 6))) {
+ sprintf(icp->err,"icmColorantTable_read count overflow, count %x, len %d",p->count,len);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ bp = bp + 12;
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < p->count; i++, bp += (32 + 6)) {
+ if (p->ttype == icmSigAltColorantTableType /* Hack to reverse little endian */
+ && (end - bp) >= 38) {
+ int tt;
+ tt = *(bp + 32);
+ *(bp+32) = *(bp+33);
+ *(bp+33) = tt;
+ tt = *(bp + 34);
+ *(bp+34) = *(bp+35);
+ *(bp+35) = tt;
+ tt = *(bp + 36);
+ *(bp+36) = *(bp+37);
+ *(bp+37) = tt;
+ }
+ if ((rv = read_ColorantTableVal(p->data+i, bp, end, pcs)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+
+ icp->al->free(icp->al, buf);
+ return rv;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmColorantTable_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+ icColorSpaceSignature pcs;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ if (icp->header->deviceClass != icSigLinkClass)
+ pcs = icp->header->pcs;
+ else
+ pcs = icSigLabData;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmColorantTable_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmColorantTable_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmColorantTable_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write count of colorants */
+ if ((rv = write_UInt32Number(p->count, bp+8)) != 0) {
+ sprintf(icp->err,"icmColorantTable_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ bp = bp + 12;
+
+ /* Write all the data to the buffer */
+ for (i = 0; i < p->count; i++, bp += (32 + 6)) {
+ if ((rv = write_ColorantTableVal(p->data+i, bp, pcs)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmColorantTable_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmColorantTable_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+ icColorSpaceSignature pcs;
+
+ if (icp->header->deviceClass != icSigLinkClass)
+ pcs = icp->header->pcs;
+ else
+ pcs = icSigLabData;
+
+ if (verb <= 0)
+ return;
+
+ if (p->ttype == icSigColorantTableType
+ || p->ttype == icmSigAltColorantTableType)
+ op->gprintf(op,"ColorantTable:\n");
+ op->gprintf(op," No. colorants = %u\n",p->count);
+ if (verb >= 2) {
+ unsigned int i;
+ icmColorantTableVal *vp;
+ for (i = 0; i < p->count; i++) {
+ vp = p->data + i;
+ op->gprintf(op," Colorant %lu:\n",i);
+ op->gprintf(op," Name = '%s'\n",vp->name);
+
+ if (p->ttype == icSigColorantTableType
+ || p->ttype == icmSigAltColorantTableType) {
+
+ switch(pcs) {
+ case icSigXYZData:
+ op->gprintf(op," XYZ = %f, %f, %f\n",
+ vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
+ break;
+ case icSigLabData:
+ op->gprintf(op," Lab = %f, %f, %f\n",
+ vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]);
+ break;
+ default:
+ op->gprintf(op," Unexpected PCS\n");
+ break;
+ }
+ }
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmColorantTable_allocate(
+ icmBase *pp
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+
+ if (p->count != p->_count) {
+ unsigned int i;
+ if (ovr_mul(p->count, sizeof(icmColorantTableVal))) {
+ sprintf(icp->err,"icmColorantTable_alloc: count overflow (%d of %ld bytes)",p->count,sizeof(icmColorantTableVal));
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmColorantTableVal *) icp->al->calloc(icp->al,p->count, sizeof(icmColorantTableVal))) == NULL) {
+ sprintf(icp->err,"icmColorantTable_alloc: malloc() of icmColorantTable data failed");
+ return icp->errc = 2;
+ }
+ for (i = 0; i < p->count; i++) {
+ p->data[i].icp = icp; /* Do init */
+ }
+ p->_count = p->count;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmColorantTable_delete(
+ icmBase *pp
+) {
+ icmColorantTable *p = (icmColorantTable *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmColorantTable(
+ icc *icp
+) {
+ icmColorantTable *p;
+ if ((p = (icmColorantTable *) icp->al->calloc(icp->al,1,sizeof(icmColorantTable))) == NULL)
+ return NULL;
+ p->ttype = icSigColorantTableType;
+ p->refcount = 1;
+ p->get_size = icmColorantTable_get_size;
+ p->read = icmColorantTable_read;
+ p->write = icmColorantTable_write;
+ p->dump = icmColorantTable_dump;
+ p->allocate = icmColorantTable_allocate;
+ p->del = icmColorantTable_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* textDescription */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmTextDescription_get_size(
+ icmBase *pp
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addadd(len, 4, p->size); /* Ascii string length + ascii string */
+ len = sat_addaddmul(len, 8, 2, p->ucSize); /* Unicode language code + length + string */
+ len = sat_addadd(len, 3, 67); /* ScriptCode code, length string */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmTextDescription_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf, *end;
+
+#ifdef ICM_STRICT
+ if (len < (8 + 4 + 8 + 3 /* + 67 */)) {
+#else
+ if (len < (8 + 4 + 8 + 3)) {
+#endif
+ sprintf(icp->err,"icmTextDescription_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmTextDescription_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmTextDescription_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read from the buffer into the structure */
+ if ((rv = p->core_read(p, &bp, end)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* core read the object, return 0 on success, error code on fail */
+static int icmTextDescription_core_read(
+ icmTextDescription *p,
+ char **bpp, /* Pointer to buffer pointer, returns next after read */
+ char *end /* Pointer to past end of read buffer */
+) {
+ icc *icp = p->icp;
+ int rv;
+ char *bp = *bpp;
+
+ if (bp > end || 8 > (end - bp)) {
+ sprintf(icp->err,"icmTextDescription_read: Data too short to type descriptor");
+ *bpp = bp;
+ return icp->errc = 1;
+ }
+
+ p->size = read_UInt32Number(bp);
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Wrong tag type ('%s') for icmTextDescription",
+ tag2str((icTagTypeSignature)read_SInt32Number(bp)));
+ return icp->errc = 1;
+ }
+ bp = bp + 8;
+
+ /* Read the Ascii string */
+ if (bp > end || 4 > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read Ascii header");
+ return icp->errc = 1;
+ }
+ p->size = read_UInt32Number(bp);
+ bp += 4;
+ if (p->size > 0) {
+ if (bp > end || p->size > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read Ascii string");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,p->size)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: ascii string is not terminated");
+ return icp->errc = 1;
+ }
+#ifdef ICM_STRICT
+ if (rv == 2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: ascii string is shorter than count");
+ return icp->errc = 1;
+ }
+#endif
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ return rv;
+ }
+ strcpy(p->desc, bp);
+ bp += p->size;
+ }
+
+ /* Read the Unicode string */
+ if (bp > end || 8 > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read Unicode string");
+ return icp->errc = 1;
+ }
+ p->ucLangCode = read_UInt32Number(bp);
+ bp += 4;
+ p->ucSize = read_UInt32Number(bp);
+ bp += 4;
+ if (p->ucSize > 0) {
+ ORD16 *up;
+ char *tbp;
+ if (bp > end || p->ucSize > (end - bp)/2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read Unicode string");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string16(bp,p->ucSize)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Unicode string is not terminated");
+ return icp->errc = 1;
+ }
+#ifdef ICM_STRICT
+ if (rv == 2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Unicode string is shorter than count");
+ return icp->errc = 1;
+ }
+#endif
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ return rv;
+ }
+ for (up = p->ucDesc, tbp = bp; tbp[0] != 0 || tbp[1] != 0; up++, tbp += 2)
+ *up = read_UInt16Number(tbp);
+ *up = 0; /* Unicode null */
+ bp += p->ucSize * 2;
+ }
+
+ /* Read the ScriptCode string */
+ if (bp > end || 3 > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read ScriptCode header");
+ return icp->errc = 1;
+ }
+ p->scCode = read_UInt16Number(bp);
+ bp += 2;
+ p->scSize = read_UInt8Number(bp);
+ bp += 1;
+ if (p->scSize > 0) {
+ if (p->scSize > 67) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: ScriptCode string too long");
+ return icp->errc = 1;
+ }
+ if (bp > end || p->scSize > (end - bp)) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: Data too short to read ScriptCode string");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,p->scSize)) == 1) {
+#ifdef ICM_STRICT
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_read: ScriptCode string is not terminated");
+ return icp->errc = 1;
+#else
+ /* Patch it up */
+ bp[p->scSize-1] = '\000';
+#endif
+ }
+ memmove((void *)p->scDesc, (void *)bp, p->scSize);
+ } else {
+ memset((void *)p->scDesc, 0, 67);
+ }
+ bp += 67;
+
+ *bpp = bp;
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmTextDescription_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmTextDescription_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmTextDescription_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write to the buffer from the structure */
+ if ((rv = p->core_write(p, &bp)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmTextDescription_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Core write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmTextDescription_core_write(
+ icmTextDescription *p,
+ char **bpp /* Pointer to buffer pointer, returns next after read */
+) {
+ icc *icp = p->icp;
+ char *bp = *bpp;
+ int rv;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_SInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp = bp + 8;
+
+ /* Write the Ascii string */
+ if ((rv = write_UInt32Number(p->size,bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if (p->size > 0) {
+ if ((rv = check_null_string(p->desc,p->size)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: ascii string is not terminated");
+ return icp->errc = 1;
+ }
+ if (rv == 2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: ascii string is shorter than length");
+ return icp->errc = 1;
+ }
+ strcpy(bp, p->desc);
+ bp += strlen(p->desc) + 1;
+ }
+
+ /* Write the Unicode string */
+ if ((rv = write_UInt32Number(p->ucLangCode, bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if ((rv = write_UInt32Number(p->ucSize, bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if (p->ucSize > 0) {
+ ORD16 *up;
+ if ((rv = check_null_string16((char *)p->ucDesc,p->ucSize)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: Unicode string is not terminated");
+ return icp->errc = 1;
+ }
+ if (rv == 2) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: Unicode string is shorter than length");
+ return icp->errc = 1;
+ }
+ for(up = p->ucDesc; *up != 0; up++, bp += 2) {
+ if ((rv = write_UInt16Number(((unsigned int)*up), bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt16Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ }
+ bp[0] = 0; /* null */
+ bp[1] = 0;
+ bp += 2;
+ }
+
+ /* Write the ScriptCode string */
+ if ((rv = write_UInt16Number(p->scCode, bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt16Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 2;
+ if ((rv = write_UInt8Number(p->scSize, bp)) != 0) {
+ sprintf(icp->err,"icmTextDescription_write: write_UInt8Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ bp += 1;
+ if (p->scSize > 0) {
+ if (p->scSize > 67) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: ScriptCode string too long");
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string((char *)p->scDesc,p->scSize)) == 1) {
+ *bpp = bp;
+ sprintf(icp->err,"icmTextDescription_write: ScriptCode string is not terminated");
+ return icp->errc = 1;
+ }
+ memmove((void *)bp, (void *)p->scDesc, 67);
+ } else {
+ memset((void *)bp, 0, 67);
+ }
+ bp += 67;
+
+ *bpp = bp;
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmTextDescription_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ unsigned int i, r, c;
+
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"TextDescription:\n");
+
+ if (p->size > 0) {
+ unsigned int size = p->size > 0 ? p->size-1 : 0;
+ op->gprintf(op," ASCII data, length %lu chars:\n",p->size);
+
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 75) {
+ if (isprint(p->desc[i])) {
+ op->gprintf(op,"%c",p->desc[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->desc[i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ } else {
+ op->gprintf(op," No ASCII data\n");
+ }
+
+ /* Can't dump Unicode or ScriptCode as text with portable code */
+ if (p->ucSize > 0) {
+ unsigned int size = p->ucSize;
+ op->gprintf(op," Unicode Data, Language code 0x%x, length %lu chars\n",
+ p->ucLangCode, p->ucSize);
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 75) {
+ op->gprintf(op,"%04x ",p->ucDesc[i]);
+ c += 5;
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ } else {
+ op->gprintf(op," No Unicode data\n");
+ }
+ if (p->scSize > 0) {
+ unsigned int size = p->scSize;
+ op->gprintf(op," ScriptCode Data, Code 0x%x, length %lu chars\n",
+ p->scCode, p->scSize);
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 75) {
+ op->gprintf(op,"%02x ",p->scDesc[i]);
+ c += 3;
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ } else {
+ op->gprintf(op," No ScriptCode data\n");
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmTextDescription_allocate(
+ icmBase *pp
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ icc *icp = p->icp;
+
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(char))) {
+ sprintf(icp->err,"icmTextDescription_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->desc != NULL)
+ icp->al->free(icp->al, p->desc);
+ if ((p->desc = (char *) icp->al->calloc(icp->al, p->size, sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmTextDescription_alloc: malloc() of Ascii description failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ if (p->ucSize != p->uc_size) {
+ if (ovr_mul(p->ucSize, sizeof(ORD16))) {
+ sprintf(icp->err,"icmTextDescription_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->ucDesc != NULL)
+ icp->al->free(icp->al, p->ucDesc);
+ if ((p->ucDesc = (ORD16 *) icp->al->calloc(icp->al, p->ucSize, sizeof(ORD16))) == NULL) {
+ sprintf(icp->err,"icmTextDescription_alloc: malloc() of Unicode description failed");
+ return icp->errc = 2;
+ }
+ p->uc_size = p->ucSize;
+ }
+ return 0;
+}
+
+/* Free all variable sized elements */
+static void icmTextDescription_unallocate(
+ icmTextDescription *p
+) {
+ icc *icp = p->icp;
+
+ if (p->desc != NULL)
+ icp->al->free(icp->al, p->desc);
+ if (p->ucDesc != NULL)
+ icp->al->free(icp->al, p->ucDesc);
+}
+
+/* Free all storage in the object */
+static void icmTextDescription_delete(
+ icmBase *pp
+) {
+ icmTextDescription *p = (icmTextDescription *)pp;
+ icc *icp = p->icp;
+
+ icmTextDescription_unallocate(p);
+ icp->al->free(icp->al, p);
+}
+
+/* Initialze a named object */
+static void icmTextDescription_init(
+ icmTextDescription *p,
+ icc *icp
+) {
+ memset((void *)p, 0, sizeof(icmTextDescription)); /* Imitate calloc */
+
+ p->ttype = icSigTextDescriptionType;
+ p->refcount = 1;
+ p->get_size = icmTextDescription_get_size;
+ p->read = icmTextDescription_read;
+ p->write = icmTextDescription_write;
+ p->dump = icmTextDescription_dump;
+ p->allocate = icmTextDescription_allocate;
+ p->del = icmTextDescription_delete;
+ p->icp = icp;
+
+ p->core_read = icmTextDescription_core_read;
+ p->core_write = icmTextDescription_core_write;
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmTextDescription(
+ icc *icp
+) {
+ icmTextDescription *p;
+ if ((p = (icmTextDescription *) icp->al->calloc(icp->al,1,sizeof(icmTextDescription))) == NULL)
+ return NULL;
+
+ icmTextDescription_init(p,icp);
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Support for icmDescStruct */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmDescStruct_get_size(
+ icmDescStruct *p
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 20); /* 20 bytes for header info */
+ len = sat_add(len, p->device.get_size((icmBase *)&p->device));
+ if (p->device.size == 0)
+ len = sat_add(len, 1); /* Extra 1 because of zero length desciption */
+ len = sat_add(len, p->model.get_size((icmBase *)&p->model));
+ if (p->model.size == 0)
+ len = sat_add(len, 1); /* Extra 1 because of zero length desciption */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmDescStruct_read(
+ icmDescStruct *p,
+ char **bpp, /* Pointer to buffer pointer, returns next after read */
+ char *end /* Pointer to past end of read buffer */
+) {
+ icc *icp = p->icp;
+ char *bp = *bpp;
+ int rv = 0;
+
+ if (bp > end || 20 > (end - bp)) {
+ sprintf(icp->err,"icmDescStruct_read: Data too short read header");
+ *bpp = bp;
+ return icp->errc = 1;
+ }
+
+ p->deviceMfg = read_SInt32Number(bp + 0);
+ p->deviceModel = read_UInt32Number(bp + 4);
+ read_UInt64Number(&p->attributes, bp + 8);
+ p->technology = (icTechnologySignature) read_UInt32Number(bp + 16);
+ *bpp = bp += 20;
+
+ /* Read the device text description */
+ if ((rv = p->device.core_read(&p->device, bpp, end)) != 0) {
+ return rv;
+ }
+
+ /* Read the model text description */
+ if ((rv = p->model.core_read(&p->model, bpp, end)) != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmDescStruct_write(
+ icmDescStruct *p,
+ char **bpp /* Pointer to buffer pointer, returns next after read */
+) {
+ icc *icp = p->icp;
+ char *bp = *bpp;
+ int rv = 0;
+ char *ttd = NULL;
+ unsigned int tts = 0;
+
+ if ((rv = write_SInt32Number(p->deviceMfg, bp + 0)) != 0) {
+ sprintf(icp->err,"icmDescStruct_write: write_SInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->deviceModel, bp + 4)) != 0) {
+ sprintf(icp->err,"icmDescStruct_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt64Number(&p->attributes, bp + 8)) != 0) {
+ sprintf(icp->err,"icmDescStruct_write: write_UInt64Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->technology, bp + 16)) != 0) {
+ sprintf(icp->err,"icmDescStruct_write: write_UInt32Number() failed");
+ *bpp = bp;
+ return icp->errc = rv;
+ }
+ *bpp = bp += 20;
+
+ /* Make sure the ASCII device text is a minimum size of 1, as per the spec. */
+ ttd = p->device.desc;
+ tts = p->device.size;
+
+ if (p->device.size == 0) {
+ p->device.desc = "";
+ p->device.size = 1;
+ }
+
+ /* Write the device text description */
+ if ((rv = p->device.core_write(&p->device, bpp)) != 0) {
+ return rv;
+ }
+
+ p->device.desc = ttd;
+ p->device.size = tts;
+
+ /* Make sure the ASCII model text is a minimum size of 1, as per the spec. */
+ ttd = p->model.desc;
+ tts = p->model.size;
+
+ if (p->model.size == 0) {
+ p->model.desc = "";
+ p->model.size = 1;
+ }
+
+ /* Write the model text description */
+ if ((rv = p->model.core_write(&p->model, bpp)) != 0) {
+ return rv;
+ }
+
+ p->model.desc = ttd;
+ p->model.size = tts;
+
+ /* Make sure the ASCII model text is a minimum size of 1, as per the spec. */
+ ttd = p->device.desc;
+ tts = p->device.size;
+
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmDescStruct_dump(
+ icmDescStruct *p,
+ icmFile *op, /* Output to dump to */
+ int verb, /* Verbosity level */
+ int index /* Description index */
+) {
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"DescStruct %u:\n",index);
+ if (verb >= 1) {
+ op->gprintf(op," Dev. Mnfctr. = %s\n",tag2str(p->deviceMfg)); /* ~~~ */
+ op->gprintf(op," Dev. Model = %s\n",tag2str(p->deviceModel)); /* ~~~ */
+ op->gprintf(op," Dev. Attrbts = %s\n", string_DeviceAttributes(p->attributes.l));
+ op->gprintf(op," Dev. Technology = %s\n", string_TechnologySignature(p->technology));
+ p->device.dump((icmBase *)&p->device, op,verb);
+ p->model.dump((icmBase *)&p->model, op,verb);
+ op->gprintf(op,"\n");
+ }
+}
+
+/* Allocate variable sized data elements (ie. descriptions) */
+static int icmDescStruct_allocate(
+ icmDescStruct *p
+) {
+ int rv;
+
+ if ((rv = p->device.allocate((icmBase *)&p->device)) != 0) {
+ return rv;
+ }
+ if ((rv = p->model.allocate((icmBase *)&p->model)) != 0) {
+ return rv;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmDescStruct_delete(
+ icmDescStruct *p
+) {
+ icmTextDescription_unallocate(&p->device);
+ icmTextDescription_unallocate(&p->model);
+}
+
+/* Init a DescStruct object */
+static void icmDescStruct_init(
+ icmDescStruct *p,
+ icc *icp
+) {
+
+ p->allocate = icmDescStruct_allocate;
+ p->icp = icp;
+
+ icmTextDescription_init(&p->device, icp);
+ icmTextDescription_init(&p->model, icp);
+}
+
+/* - - - - - - - - - - - - - - - */
+/* icmProfileSequenceDesc object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmProfileSequenceDesc_get_size(
+ icmBase *pp
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ unsigned int len = 0;
+ unsigned int i;
+ len = sat_add(len, 12); /* 12 bytes for tag, padding and count */
+ for (i = 0; i < p->count; i++) { /* All the description structures */
+ len = sat_add(len, icmDescStruct_get_size(&p->data[i]));
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmProfileSequenceDesc_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ char *bp, *buf, *end;
+ int rv = 0;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmProfileSequenceDesc_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmProfileSequenceDesc_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmProfileSequenceDesc_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmProfileSequenceDesc_read: Wrong tag type for icmProfileSequenceDesc");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp += 8; /* Skip padding */
+
+ p->count = read_UInt32Number(bp); /* Number of sequence descriptions */
+ bp += 4;
+
+ /* Read all the sequence descriptions */
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ for (i = 0; i < p->count; i++) {
+ if ((rv = icmDescStruct_read(&p->data[i], &bp, end)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmProfileSequenceDesc_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ if ((rv = write_UInt32Number(p->count,bp+8)) != 0) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp = bp + 12;
+
+ /* Write all the description structures */
+ for (i = 0; i < p->count; i++) {
+ if ((rv = icmDescStruct_write(&p->data[i], &bp)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmProfileSequenceDesc_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmProfileSequenceDesc_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"ProfileSequenceDesc:\n");
+ op->gprintf(op," No. elements = %u\n",p->count);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->count; i++)
+ icmDescStruct_dump(&p->data[i], op, verb-1, i);
+ }
+}
+
+/* Allocate variable sized data elements (ie. count of profile descriptions) */
+static int icmProfileSequenceDesc_allocate(
+ icmBase *pp
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+
+ if (p->count != p->_count) {
+ if (ovr_mul(p->count, sizeof(icmDescStruct))) {
+ sprintf(icp->err,"icmProfileSequenceDesc_allocate: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmDescStruct *) icp->al->calloc(icp->al, p->count, sizeof(icmDescStruct))) == NULL) {
+ sprintf(icp->err,"icmProfileSequenceDesc_allocate Allocation of DescStruct array failed");
+ return icp->errc = 2;
+ }
+ /* Now init the DescStructs */
+ for (i = 0; i < p->count; i++) {
+ icmDescStruct_init(&p->data[i], icp);
+ }
+ p->_count = p->count;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmProfileSequenceDesc_delete(
+ icmBase *pp
+) {
+ icmProfileSequenceDesc *p = (icmProfileSequenceDesc *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++) {
+ icmDescStruct_delete(&p->data[i]); /* Free allocated contents */
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmProfileSequenceDesc(
+ icc *icp
+) {
+ icmProfileSequenceDesc *p;
+ if ((p = (icmProfileSequenceDesc *) icp->al->calloc(icp->al,1,sizeof(icmProfileSequenceDesc))) == NULL)
+ return NULL;
+ p->ttype = icSigProfileSequenceDescType;
+ p->refcount = 1;
+ p->get_size = icmProfileSequenceDesc_get_size;
+ p->read = icmProfileSequenceDesc_read;
+ p->write = icmProfileSequenceDesc_write;
+ p->dump = icmProfileSequenceDesc_dump;
+ p->allocate = icmProfileSequenceDesc_allocate;
+ p->del = icmProfileSequenceDesc_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* Signature */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmSignature_get_size(
+ icmBase *pp
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for signature */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmSignature_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmSignature *p = (icmSignature *)pp;
+ icc *icp = p->icp;
+ char *bp, *buf;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmSignature_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmSignature_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmSignature_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmSignaturSignatureng tag type for icmSignature");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read the encoded measurement geometry */
+ p->sig = (icTechnologySignature)read_SInt32Number(bp + 8);
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmSignature_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmSignature *p = (icmSignature *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmSignature_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmSignature_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmSignature_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write the signature */
+ if ((rv = write_SInt32Number((int)p->sig, bp + 8)) != 0) {
+ sprintf(icp->err,"icmSignaturea_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmSignature_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmSignature_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmSignature *p = (icmSignature *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Signature\n");
+ op->gprintf(op," Technology = %s\n", string_TechnologySignature(p->sig));
+}
+
+/* Allocate variable sized data elements */
+static int icmSignature_allocate(
+ icmBase *pp
+) {
+ /* Nothing to do */
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmSignature_delete(
+ icmBase *pp
+) {
+ icmSignature *p = (icmSignature *)pp;
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmSignature(
+ icc *icp
+) {
+ icmSignature *p;
+ if ((p = (icmSignature *) icp->al->calloc(icp->al,1,sizeof(icmSignature))) == NULL)
+ return NULL;
+ p->ttype = icSigSignatureType;
+ p->refcount = 1;
+ p->get_size = icmSignature_get_size;
+ p->read = icmSignature_read;
+ p->write = icmSignature_write;
+ p->dump = icmSignature_dump;
+ p->allocate = icmSignature_allocate;
+ p->del = icmSignature_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Data conversion support functions */
+static int read_ScreeningData(icmScreeningData *p, char *d) {
+ p->frequency = read_S15Fixed16Number(d + 0);
+ p->angle = read_S15Fixed16Number(d + 4);
+ p->spotShape = (icSpotShape)read_SInt32Number(d + 8);
+ return 0;
+}
+
+static int write_ScreeningData(icmScreeningData *p, char *d) {
+ int rv;
+ if ((rv = write_S15Fixed16Number(p->frequency, d + 0)) != 0)
+ return rv;
+ if ((rv = write_S15Fixed16Number(p->angle, d + 4)) != 0)
+ return rv;
+ if ((rv = write_SInt32Number((int)p->spotShape, d + 8)) != 0)
+ return rv;
+ return 0;
+}
+
+
+/* icmScreening object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmScreening_get_size(
+ icmBase *pp
+) {
+ icmScreening *p = (icmScreening *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 16); /* 16 bytes for tag, padding, flag & channeles */
+ len = sat_addmul(len, p->channels, 12); /* 12 bytes for each channel */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmScreening_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmScreening *p = (icmScreening *)pp;
+ icc *icp = p->icp;
+ int rv = 0;
+ unsigned int i;
+ char *bp, *buf, *end;
+
+ if (len < 12) {
+ sprintf(icp->err,"icmScreening_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmScreening_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmScreening_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmScreening_read: Wrong tag type for icmScreening");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->screeningFlag = read_UInt32Number(bp+8); /* Flags */
+ p->channels = read_UInt32Number(bp+12); /* Number of channels */
+ bp = bp + 16;
+
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+
+ /* Read all the data from the buffer */
+ for (i = 0; i < p->channels; i++, bp += 12) {
+ if (bp > end || 12 > (end - bp)) {
+ sprintf(icp->err,"icmScreening_read: Data too short to read Screening Data");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ read_ScreeningData(&p->data[i], bp);
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmScreening_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmScreening *p = (icmScreening *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmScreening_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmScreening_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmScreening_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ if ((rv = write_UInt32Number(p->screeningFlag,bp+8)) != 0) {
+ sprintf(icp->err,"icmScreening_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->channels,bp+12)) != 0) {
+ sprintf(icp->err,"icmScreening_write: write_UInt32NumberXYZumber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp = bp + 16;
+
+ /* Write all the data to the buffer */
+ for (i = 0; i < p->channels; i++, bp += 12) {
+ if ((rv = write_ScreeningData(&p->data[i],bp)) != 0) {
+ sprintf(icp->err,"icmScreening_write: write_ScreeningData() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmScreening_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmScreening_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmScreening *p = (icmScreening *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Screening:\n");
+ op->gprintf(op," Flags = %s\n", string_ScreenEncodings(p->screeningFlag));
+ op->gprintf(op," No. channels = %u\n",p->channels);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->channels; i++) {
+ op->gprintf(op," %lu:\n",i);
+ op->gprintf(op," Frequency: %f\n",p->data[i].frequency);
+ op->gprintf(op," Angle: %f\n",p->data[i].angle);
+ op->gprintf(op," Spot shape: %s\n", string_SpotShape(p->data[i].spotShape));
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmScreening_allocate(
+ icmBase *pp
+) {
+ icmScreening *p = (icmScreening *)pp;
+ icc *icp = p->icp;
+
+ if (p->channels != p->_channels) {
+ if (ovr_mul(p->channels, sizeof(icmScreeningData))) {
+ sprintf(icp->err,"icmScreening_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ if ((p->data = (icmScreeningData *) icp->al->malloc(icp->al, p->channels * sizeof(icmScreeningData))) == NULL) {
+ sprintf(icp->err,"icmScreening_alloc: malloc() of icmScreening data failed");
+ return icp->errc = 2;
+ }
+ p->_channels = p->channels;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmScreening_delete(
+ icmBase *pp
+) {
+ icmScreening *p = (icmScreening *)pp;
+ icc *icp = p->icp;
+
+ if (p->data != NULL)
+ icp->al->free(icp->al, p->data);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmScreening(
+ icc *icp
+) {
+ icmScreening *p;
+ if ((p = (icmScreening *) icp->al->calloc(icp->al,1,sizeof(icmScreening))) == NULL)
+ return NULL;
+ p->ttype = icSigScreeningType;
+ p->refcount = 1;
+ p->get_size = icmScreening_get_size;
+ p->read = icmScreening_read;
+ p->write = icmScreening_write;
+ p->dump = icmScreening_dump;
+ p->allocate = icmScreening_allocate;
+ p->del = icmScreening_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmUcrBg object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmUcrBg_get_size(
+ icmBase *pp
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addaddmul(len, 4, p->UCRcount, 2); /* Undercolor Removal */
+ len = sat_addaddmul(len, 4, p->BGcount, 2); /* Black Generation */
+ len = sat_add(len, p->size); /* Description string */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmUcrBg_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ int rv;
+ char *bp, *buf, *end;
+
+ if (len < 16) {
+ sprintf(icp->err,"icmUcrBg_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUcrBg_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmUcrBg_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmUcrBg_read: Wrong tag type for icmUcrBg");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->UCRcount = read_UInt32Number(bp+8); /* First curve count */
+ bp = bp + 12;
+
+ if (p->UCRcount > 0) {
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ for (i = 0; i < p->UCRcount; i++, bp += 2) {
+ if (bp > end || 2 > (end - bp)) {
+ sprintf(icp->err,"icmUcrBg_read: Data too short to read UCR Data");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if (p->UCRcount == 1) /* % */
+ p->UCRcurve[i] = (double)read_UInt16Number(bp);
+ else /* 0.0 - 1.0 */
+ p->UCRcurve[i] = read_DCS16Number(bp);
+ }
+ } else {
+ p->UCRcurve = NULL;
+ }
+
+ if (bp > end || 4 > (end - bp)) {
+ sprintf(icp->err,"icmData_read: Data too short to read Black Gen count");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->BGcount = read_UInt32Number(bp); /* First curve count */
+ bp += 4;
+
+ if (p->BGcount > 0) {
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ for (i = 0; i < p->BGcount; i++, bp += 2) {
+ if (bp > end || 2 > (end - bp)) {
+ sprintf(icp->err,"icmUcrBg_read: Data too short to read BG Data");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if (p->BGcount == 1) /* % */
+ p->BGcurve[i] = (double)read_UInt16Number(bp);
+ else /* 0.0 - 1.0 */
+ p->BGcurve[i] = read_DCS16Number(bp);
+ }
+ } else {
+ p->BGcurve = NULL;
+ }
+
+ p->size = end - bp; /* Nominal string length */
+ if (p->size > 0) {
+ if ((rv = check_null_string(bp, p->size)) == 1) {
+ sprintf(icp->err,"icmUcrBg_read: string is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->size = strlen(bp) + 1;
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ memmove((void *)p->string, (void *)bp, p->size);
+ bp += p->size;
+ } else {
+ p->string = NULL;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmUcrBg_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ icc *icp = p->icp;
+ unsigned int i;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmUcrBg_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmUcrBg_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp = bp + 8;
+
+ /* Write UCR curve */
+ if ((rv = write_UInt32Number(p->UCRcount,bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 4;
+
+ for (i = 0; i < p->UCRcount; i++, bp += 2) {
+ if (p->UCRcount == 1) { /* % */
+ if ((rv = write_UInt16Number((unsigned int)(p->UCRcurve[i]+0.5),bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_UInt16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ } else {
+ if ((rv = write_DCS16Number(p->UCRcurve[i],bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->UCRcurve[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ /* Write BG curve */
+ if ((rv = write_UInt32Number(p->BGcount,bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 4;
+
+ for (i = 0; i < p->BGcount; i++, bp += 2) {
+ if (p->BGcount == 1) { /* % */
+ if ((rv = write_UInt16Number((unsigned int)(p->BGcurve[i]+0.5),bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_UInt16umber() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ } else {
+ if ((rv = write_DCS16Number(p->BGcurve[i],bp)) != 0) {
+ sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->BGcurve[i]);
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ if (p->string != NULL) {
+ if ((rv = check_null_string(p->string,p->size)) == 1) {
+ sprintf(icp->err,"icmUcrBg_write: text is not null terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if (rv == 2) {
+ sprintf(icp->err,"icmUcrBg_write: text is shorter than length");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ memmove((void *)bp, (void *)p->string, p->size);
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmUcrBg_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmUcrBg_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Undercolor Removal Curve & Black Generation:\n");
+
+ if (p->UCRcount == 0) {
+ op->gprintf(op," UCR: Not specified\n");
+ } else if (p->UCRcount == 1) {
+ op->gprintf(op," UCR: %f%%\n",p->UCRcurve[0]);
+ } else {
+ op->gprintf(op," UCR curve no. elements = %u\n",p->UCRcount);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->UCRcount; i++)
+ op->gprintf(op," %3lu: %f\n",i,p->UCRcurve[i]);
+ }
+ }
+ if (p->BGcount == 0) {
+ op->gprintf(op," BG: Not specified\n");
+ } else if (p->BGcount == 1) {
+ op->gprintf(op," BG: %f%%\n",p->BGcurve[0]);
+ } else {
+ op->gprintf(op," BG curve no. elements = %u\n",p->BGcount);
+ if (verb >= 2) {
+ unsigned int i;
+ for (i = 0; i < p->BGcount; i++)
+ op->gprintf(op," %3lu: %f\n",i,p->BGcurve[i]);
+ }
+ }
+
+ {
+ unsigned int i, r, c, size;
+ op->gprintf(op," Description:\n");
+ op->gprintf(op," No. chars = %lu\n",p->size);
+
+ size = p->size > 0 ? p->size-1 : 0;
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 73) {
+ if (isprint(p->string[i])) {
+ op->gprintf(op,"%c",p->string[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->string[i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmUcrBg_allocate(
+ icmBase *pp
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ icc *icp = p->icp;
+
+ if (p->UCRcount != p->UCR_count) {
+ if (ovr_mul(p->UCRcount, sizeof(double))) {
+ sprintf(icp->err,"icmUcrBg_allocate: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->UCRcurve != NULL)
+ icp->al->free(icp->al, p->UCRcurve);
+ if ((p->UCRcurve = (double *) icp->al->calloc(icp->al, p->UCRcount, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmUcrBg_allocate: malloc() of UCR curve data failed");
+ return icp->errc = 2;
+ }
+ p->UCR_count = p->UCRcount;
+ }
+ if (p->BGcount != p->BG_count) {
+ if (ovr_mul(p->BGcount, sizeof(double))) {
+ sprintf(icp->err,"icmUcrBg_allocate: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->BGcurve != NULL)
+ icp->al->free(icp->al, p->BGcurve);
+ if ((p->BGcurve = (double *) icp->al->calloc(icp->al, p->BGcount, sizeof(double))) == NULL) {
+ sprintf(icp->err,"icmUcrBg_allocate: malloc() of BG curve data failed");
+ return icp->errc = 2;
+ }
+ p->BG_count = p->BGcount;
+ }
+ if (p->size != p->_size) {
+ if (ovr_mul(p->size, sizeof(char))) {
+ sprintf(icp->err,"icmUcrBg_allocate: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->string != NULL)
+ icp->al->free(icp->al, p->string);
+ if ((p->string = (char *) icp->al->calloc(icp->al, p->size, sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmUcrBg_allocate: malloc() of string data failed");
+ return icp->errc = 2;
+ }
+ p->_size = p->size;
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmUcrBg_delete(
+ icmBase *pp
+) {
+ icmUcrBg *p = (icmUcrBg *)pp;
+ icc *icp = p->icp;
+
+ if (p->UCRcurve != NULL)
+ icp->al->free(icp->al, p->UCRcurve);
+ if (p->BGcurve != NULL)
+ icp->al->free(icp->al, p->BGcurve);
+ if (p->string != NULL)
+ icp->al->free(icp->al, p->string);
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmUcrBg(
+ icc *icp
+) {
+ icmUcrBg *p;
+ if ((p = (icmUcrBg *) icp->al->calloc(icp->al,1,sizeof(icmUcrBg))) == NULL)
+ return NULL;
+ p->ttype = icSigUcrBgType;
+ p->refcount = 1;
+ p->get_size = icmUcrBg_get_size;
+ p->read = icmUcrBg_read;
+ p->write = icmUcrBg_write;
+ p->dump = icmUcrBg_dump;
+ p->allocate = icmUcrBg_allocate;
+ p->del = icmUcrBg_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* VideoCardGamma (ColorSync 2.5 specific - c/o Neil Okamoto) */
+/* 'vcgt' */
+
+static unsigned int icmVideoCardGamma_get_size(
+ icmBase *pp
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ unsigned int len = 0;
+
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 4); /* 4 for gamma type */
+
+ /* compute size of remainder */
+ if (p->tagType == icmVideoCardGammaTableType) {
+ len = sat_add(len, 2); /* 2 bytes for channels */
+ len = sat_add(len, 2); /* 2 for entry count */
+ len = sat_add(len, 2); /* 2 for entry size */
+ len = sat_add(len, sat_mul3(p->u.table.channels, /* compute table size */
+ p->u.table.entryCount, p->u.table.entrySize));
+ }
+ else if (p->tagType == icmVideoCardGammaFormulaType) {
+ len = sat_add(len, 12); /* 4 bytes each for red gamma, min, & max */
+ len = sat_add(len, 12); /* 4 bytes each for green gamma, min & max */
+ len = sat_add(len, 12); /* 4 bytes each for blue gamma, min & max */
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmVideoCardGamma_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ icc *icp = p->icp;
+ int rv, c;
+ char *bp, *buf;
+ ORD8 *pchar;
+ ORD16 *pshort;
+
+ if (len < 18) {
+ sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmVideoCardGamma_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmVideoCardGamma_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmVideoCardGamma_read: Wrong tag type for icmVideoCardGamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read gamma format (eg. table or formula) from the buffer */
+ p->tagType = (icmVideoCardGammaTagType)read_UInt32Number(bp+8);
+
+ /* Read remaining gamma data based on format */
+ if (p->tagType == icmVideoCardGammaTableType) {
+ p->u.table.channels = read_UInt16Number(bp+12);
+ p->u.table.entryCount = read_UInt16Number(bp+14);
+ p->u.table.entrySize = read_UInt16Number(bp+16);
+ if ((len-18) < sat_mul3(p->u.table.channels, p->u.table.entryCount,
+ p->u.table.entrySize)) {
+ sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+ if ((rv = pp->allocate(pp)) != 0) { /* make space for table */
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ /* ~~~~ This should be a table of doubles like the rest of icclib ! ~~~~ */
+ pchar = (ORD8 *)p->u.table.data;
+ pshort = (ORD16 *)p->u.table.data;
+ for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) {
+ switch (p->u.table.entrySize) {
+ case 1:
+ *pchar++ = read_UInt8Number(bp);
+ bp++;
+ break;
+ case 2:
+ *pshort++ = read_UInt16Number(bp);
+ bp+=2;
+ break;
+ default:
+ sprintf(icp->err,"icmVideoCardGamma_read: unsupported table entry size");
+ pp->del(pp);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+ } else if (p->tagType == icmVideoCardGammaFormulaType) {
+ if (len < 48) {
+ sprintf(icp->err,"icmVideoCardGamma_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+ p->u.table.channels = 3; /* Always 3 for formula */
+ p->u.formula.redGamma = read_S15Fixed16Number(bp+12);
+ p->u.formula.redMin = read_S15Fixed16Number(bp+16);
+ p->u.formula.redMax = read_S15Fixed16Number(bp+20);
+ p->u.formula.greenGamma = read_S15Fixed16Number(bp+24);
+ p->u.formula.greenMin = read_S15Fixed16Number(bp+28);
+ p->u.formula.greenMax = read_S15Fixed16Number(bp+32);
+ p->u.formula.blueGamma = read_S15Fixed16Number(bp+36);
+ p->u.formula.blueMin = read_S15Fixed16Number(bp+40);
+ p->u.formula.blueMax = read_S15Fixed16Number(bp+44);
+ } else {
+ sprintf(icp->err,"icmVideoCardGammaTable_read: Unknown gamma format for icmVideoCardGamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmVideoCardGamma_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0, c;
+ ORD8 *pchar;
+ ORD16 *pshort;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmViewingConditions_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmViewingConditions_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write gamma format (eg. table of formula) */
+ if ((rv = write_UInt32Number(p->tagType,bp+8)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write remaining gamma data based on format */
+ if (p->tagType == icmVideoCardGammaTableType) {
+ if ((rv = write_UInt16Number(p->u.table.channels,bp+12)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt16Number(p->u.table.entryCount,bp+14)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt16Number(p->u.table.entrySize,bp+16)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_UInt16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ pchar = (ORD8 *)p->u.table.data;
+ pshort = (ORD16 *)p->u.table.data;
+ for (c=0, bp=bp+18; c<p->u.table.channels*p->u.table.entryCount; c++) {
+ switch (p->u.table.entrySize) {
+ case 1:
+ write_UInt8Number(*pchar++,bp);
+ bp++;
+ break;
+ case 2:
+ write_UInt16Number(*pshort++,bp);
+ bp+=2;
+ break;
+ default:
+ sprintf(icp->err,"icmVideoCardGamma_write: unsupported table entry size");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ }
+ } else if (p->tagType == icmVideoCardGammaFormulaType) {
+ if ((rv = write_S15Fixed16Number(p->u.formula.redGamma,bp+12)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.redMin,bp+16)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.redMax,bp+20)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.greenGamma,bp+24)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.greenMin,bp+28)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.greenMax,bp+32)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.blueGamma,bp+36)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.blueMin,bp+40)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_S15Fixed16Number(p->u.formula.blueMax,bp+44)) != 0) {
+ sprintf(icp->err,"icmVideoCardGamma_write: write_S15Fixed16Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ } else {
+ sprintf(icp->err,"icmVideoCardGammaTable_write: Unknown gamma format for icmVideoCardGamma");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmViewingConditions_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmVideoCardGamma_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ int c,i;
+
+ if (verb <= 0)
+ return;
+
+ if (p->tagType == icmVideoCardGammaTableType) {
+ op->gprintf(op,"VideoCardGammaTable:\n");
+ op->gprintf(op," channels = %d\n", p->u.table.channels);
+ op->gprintf(op," entries = %d\n", p->u.table.entryCount);
+ op->gprintf(op," entrysize = %d\n", p->u.table.entrySize);
+ if (verb >= 2) {
+ /* dump array contents also */
+ for (c=0; c<p->u.table.channels; c++) {
+ op->gprintf(op," channel #%d\n",c);
+ for (i=0; i<p->u.table.entryCount; i++) {
+ if (p->u.table.entrySize == 1) {
+ op->gprintf(op," %d: %d\n",i,((ORD8 *)p->u.table.data)[c*p->u.table.entryCount+i]);
+ }
+ else if (p->u.table.entrySize == 2) {
+ op->gprintf(op," %d: %d\n",i,((ORD16 *)p->u.table.data)[c*p->u.table.entryCount+i]);
+ }
+ }
+ }
+ }
+ } else if (p->tagType == icmVideoCardGammaFormulaType) {
+ op->gprintf(op,"VideoCardGammaFormula:\n");
+ op->gprintf(op," red gamma = %f\n", p->u.formula.redGamma);
+ op->gprintf(op," red min = %f\n", p->u.formula.redMin);
+ op->gprintf(op," red max = %f\n", p->u.formula.redMax);
+ op->gprintf(op," green gamma = %f\n", p->u.formula.greenGamma);
+ op->gprintf(op," green min = %f\n", p->u.formula.greenMin);
+ op->gprintf(op," green max = %f\n", p->u.formula.greenMax);
+ op->gprintf(op," blue gamma = %f\n", p->u.formula.blueGamma);
+ op->gprintf(op," blue min = %f\n", p->u.formula.blueMin);
+ op->gprintf(op," blue max = %f\n", p->u.formula.blueMax);
+ } else {
+ op->gprintf(op," Unknown tag format\n");
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmVideoCardGamma_allocate(
+ icmBase *pp
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ icc *icp = p->icp;
+ unsigned int size;
+
+ /* note: allocation is only relevant for table type
+ * and in that case the channels, entryCount, and entrySize
+ * fields must all be set prior to getting here
+ */
+
+ if (p->tagType == icmVideoCardGammaTableType) {
+ size = sat_mul(p->u.table.channels, p->u.table.entryCount);
+ switch (p->u.table.entrySize) {
+ case 1:
+ size = sat_mul(size, sizeof(ORD8));
+ break;
+ case 2:
+ size = sat_mul(size, sizeof(unsigned short));
+ break;
+ default:
+ sprintf(icp->err,"icmVideoCardGamma_alloc: unsupported table entry size");
+ return icp->errc = 1;
+ }
+ if (size == UINT_MAX) {
+ sprintf(icp->err,"icmVideoCardGamma_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->u.table.data != NULL)
+ icp->al->free(icp->al, p->u.table.data);
+ if ((p->u.table.data = (void*) icp->al->malloc(icp->al, size)) == NULL) {
+ sprintf(icp->err,"icmVideoCardGamma_alloc: malloc() of table data failed");
+ return icp->errc = 2;
+ }
+ }
+
+ return 0;
+}
+
+/* Read a value */
+static double icmVideoCardGamma_lookup(
+ icmVideoCardGamma *p,
+ int chan, /* Channel, 0, 1 or 2 */
+ double iv /* Input value 0.0 - 1.0 */
+) {
+ double ov = 0.0;
+
+ if (chan < 0 || chan > (p->u.table.channels-1)
+ || iv < 0.0 || iv > 1.0)
+ return iv;
+
+ if (p->tagType == icmVideoCardGammaTableType && p->u.table.entryCount == 0) {
+ /* Deal with siliness */
+ ov = iv;
+ } else if (p->tagType == icmVideoCardGammaTableType) {
+ /* Use linear interpolation */
+ unsigned int ix;
+ double val0, val1, w;
+ double inputEnt_1 = (double)(p->u.table.entryCount-1);
+
+ val0 = iv * inputEnt_1;
+ if (val0 < 0.0)
+ val0 = 0.0;
+ else if (val0 > inputEnt_1)
+ val0 = inputEnt_1;
+ ix = (unsigned int)floor(val0); /* Coordinate */
+ if (ix > (p->u.table.entryCount-2))
+ ix = (p->u.table.entryCount-2);
+ w = val0 - (double)ix; /* weight */
+ if (p->u.table.entrySize == 1) {
+ val0 = ((ORD8 *)p->u.table.data)[chan * p->u.table.entryCount + ix]/255.0;
+ val1 = ((ORD8 *)p->u.table.data)[chan * p->u.table.entryCount + ix + 1]/255.0;
+ } else if (p->u.table.entrySize == 2) {
+ val0 = ((ORD16 *)p->u.table.data)[chan * p->u.table.entryCount + ix]/65535.0;
+ val1 = ((ORD16 *)p->u.table.data)[chan * p->u.table.entryCount + ix + 1]/65535.0;
+ } else {
+ val0 = val1 = iv;
+ }
+ ov = val0 + w * (val1 - val0);
+
+ } else if (p->tagType == icmVideoCardGammaFormulaType) {
+ double min, max, gam;
+
+ if (iv == 0) {
+ min = p->u.formula.redMin;
+ max = p->u.formula.redMax;
+ gam = p->u.formula.redGamma;
+ } else if (iv == 1) {
+ min = p->u.formula.greenMin;
+ max = p->u.formula.greenMax;
+ gam = p->u.formula.greenGamma;
+ } else {
+ min = p->u.formula.blueMin;
+ max = p->u.formula.blueMax;
+ gam = p->u.formula.blueGamma;
+ }
+
+ /* The Apple OSX doco confirms this is the formula */
+ ov = min + (max - min) * pow(iv, gam);
+ }
+ return ov;
+}
+
+/* Free all storage in the object */
+static void icmVideoCardGamma_delete(
+ icmBase *pp
+) {
+ icmVideoCardGamma *p = (icmVideoCardGamma *)pp;
+ icc *icp = p->icp;
+
+ if (p->tagType == icmVideoCardGammaTableType && p->u.table.data != NULL)
+ icp->al->free(icp->al, p->u.table.data);
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmVideoCardGamma(
+ icc *icp
+) {
+ icmVideoCardGamma *p;
+ if ((p = (icmVideoCardGamma *) icp->al->calloc(icp->al,1,sizeof(icmVideoCardGamma))) == NULL)
+ return NULL;
+ p->ttype = icSigVideoCardGammaType;
+ p->refcount = 1;
+ p->get_size = icmVideoCardGamma_get_size;
+ p->read = icmVideoCardGamma_read;
+ p->write = icmVideoCardGamma_write;
+ p->lookup = icmVideoCardGamma_lookup;
+ p->dump = icmVideoCardGamma_dump;
+ p->allocate = icmVideoCardGamma_allocate;
+ p->del = icmVideoCardGamma_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* ViewingConditions */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmViewingConditions_get_size(
+ icmBase *pp
+) {
+ unsigned int len = 0;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_add(len, 12); /* 12 for XYZ of illuminant */
+ len = sat_add(len, 12); /* 12 for XYZ of surround */
+ len = sat_add(len, 4); /* 4 for illuminant type */
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmViewingConditions_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmViewingConditions *p = (icmViewingConditions *)pp;
+ icc *icp = p->icp;
+ int rv;
+ char *bp, *buf;
+
+ if (len < 36) {
+ sprintf(icp->err,"icmViewingConditions_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmViewingConditions_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmViewingConditions_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmViewingConditions_read: Wrong tag type for icmViewingConditions");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read the XYZ values for the illuminant */
+ if ((rv = read_XYZNumber(&p->illuminant, bp+8)) != 0) {
+ sprintf(icp->err,"icmViewingConditions: read_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Read the XYZ values for the surround */
+ if ((rv = read_XYZNumber(&p->surround, bp+20)) != 0) {
+ sprintf(icp->err,"icmViewingConditions: read_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Read the encoded standard illuminant */
+ p->stdIlluminant = (icIlluminant)read_SInt32Number(bp + 32);
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmViewingConditions_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmViewingConditions *p = (icmViewingConditions *)pp;
+ icc *icp = p->icp;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmViewingConditions_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmViewingConditions_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmViewingConditions_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+
+ /* Write the XYZ values for the illuminant */
+ if ((rv = write_XYZNumber(&p->illuminant, bp+8)) != 0) {
+ sprintf(icp->err,"icmViewingConditions: write_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the XYZ values for the surround */
+ if ((rv = write_XYZNumber(&p->surround, bp+20)) != 0) {
+ sprintf(icp->err,"icmViewingConditions: write_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write the encoded standard illuminant */
+ if ((rv = write_SInt32Number((int)p->stdIlluminant, bp + 32)) != 0) {
+ sprintf(icp->err,"icmViewingConditionsa_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmViewingConditions_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmViewingConditions_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmViewingConditions *p = (icmViewingConditions *)pp;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Viewing Conditions:\n");
+ op->gprintf(op," XYZ value of illuminant in cd/m^2 = %s\n", string_XYZNumber(&p->illuminant));
+ op->gprintf(op," XYZ value of surround in cd/m^2 = %s\n", string_XYZNumber(&p->surround));
+ op->gprintf(op," Illuminant type = %s\n", string_Illuminant(p->stdIlluminant));
+}
+
+/* Allocate variable sized data elements */
+static int icmViewingConditions_allocate(
+ icmBase *pp
+) {
+ /* Nothing to do */
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmViewingConditions_delete(
+ icmBase *pp
+) {
+ icmViewingConditions *p = (icmViewingConditions *)pp;
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmViewingConditions(
+ icc *icp
+) {
+ icmViewingConditions *p;
+ if ((p = (icmViewingConditions *) icp->al->calloc(icp->al,1,sizeof(icmViewingConditions))) == NULL)
+ return NULL;
+ p->ttype = icSigViewingConditionsType;
+ p->refcount = 1;
+ p->get_size = icmViewingConditions_get_size;
+ p->read = icmViewingConditions_read;
+ p->write = icmViewingConditions_write;
+ p->dump = icmViewingConditions_dump;
+ p->allocate = icmViewingConditions_allocate;
+ p->del = icmViewingConditions_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ---------------------------------------------------------- */
+/* icmCrdInfo object */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmCrdInfo_get_size(
+ icmBase *pp
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ unsigned int len = 0, t;
+ len = sat_add(len, 8); /* 8 bytes for tag and padding */
+ len = sat_addadd(len, 4, p->ppsize); /* Postscript product name */
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ len = sat_addadd(len, 4, p->crdsize[t]); /* crd names */
+ }
+ return len;
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmCrdInfo_read(
+ icmBase *pp,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ icc *icp = p->icp;
+ unsigned int t;
+ int rv;
+ char *bp, *buf, *end;
+
+ if (len < 28) {
+ sprintf(icp->err,"icmCrdInfo_read: Tag too small to be legal");
+ return icp->errc = 1;
+ }
+
+ /* Allocate a file read buffer */
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmCrdInfo_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+ end = buf + len;
+
+ /* Read portion of file into buffer */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, bp, 1, len) != len) {
+ sprintf(icp->err,"icmCrdInfo_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Read type descriptor from the buffer */
+ if (((icTagTypeSignature)read_SInt32Number(bp)) != p->ttype) {
+ sprintf(icp->err,"icmCrdInfo_read: Wrong tag type for icmCrdInfo");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ bp = bp + 8;
+
+ /* Postscript product name */
+ if (bp > end || 4 > (end - bp)) {
+ sprintf(icp->err,"icmCrdInfo_read: Data too short to read Postscript product name");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->ppsize = read_UInt32Number(bp);
+ bp += 4;
+ if (p->ppsize > 0) {
+ if (p->ppsize > (end - bp)) {
+ sprintf(icp->err,"icmCrdInfo_read: Data to short to read Postscript product string");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,p->ppsize)) == 1) {
+ sprintf(icp->err,"icmCrdInfo_read: Postscript product name is not terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ memmove((void *)p->ppname, (void *)bp, p->ppsize);
+ bp += p->ppsize;
+ }
+
+ /* CRD names for the four rendering intents */
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ if (bp > end || 4 > (end - bp)) {
+ sprintf(icp->err,"icmCrdInfo_read: Data too short to read CRD%d name",t);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->crdsize[t] = read_UInt32Number(bp);
+ bp += 4;
+ if (p->crdsize[t] > 0) {
+ if (p->crdsize[t] > (end - bp)) {
+ sprintf(icp->err,"icmCrdInfo_read: Data to short to read CRD%d string",t);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ if ((rv = check_null_string(bp,p->crdsize[t])) == 1) {
+ sprintf(icp->err,"icmCrdInfo_read: CRD%d name is not terminated",t);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ if ((rv = p->allocate((icmBase *)p)) != 0) {
+ icp->al->free(icp->al, buf);
+ return rv;
+ }
+ memmove((void *)p->crdname[t], (void *)bp, p->crdsize[t]);
+ bp += p->crdsize[t];
+ }
+ }
+
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmCrdInfo_write(
+ icmBase *pp,
+ unsigned int of /* File offset to write from */
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ icc *icp = p->icp;
+ unsigned int t;
+ unsigned int len;
+ char *bp, *buf; /* Buffer to write from */
+ int rv;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size((icmBase *)p)) == UINT_MAX) {
+ sprintf(icp->err,"icmCrdInfo_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmCrdInfo_write malloc() failed");
+ return icp->errc = 2;
+ }
+ bp = buf;
+
+ /* Write type descriptor to the buffer */
+ if ((rv = write_SInt32Number((int)p->ttype,bp)) != 0) {
+ sprintf(icp->err,"icmCrdInfo_write: write_SInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ write_SInt32Number(0,bp+4); /* Set padding to 0 */
+ bp = bp + 8;
+
+ /* Postscript product name */
+ if ((rv = write_UInt32Number(p->ppsize,bp)) != 0) {
+ sprintf(icp->err,"icmCrdInfo_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if (p->ppsize > 0) {
+ if ((rv = check_null_string(p->ppname,p->ppsize)) == 1) {
+ sprintf(icp->err,"icmCrdInfo_write: Postscript product name is not terminated");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ memmove((void *)bp, (void *)p->ppname, p->ppsize);
+ bp += p->ppsize;
+ }
+
+ /* CRD names for the four rendering intents */
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ if ((rv = write_UInt32Number(p->crdsize[t],bp)) != 0) {
+ sprintf(icp->err,"icmCrdInfo_write: write_UInt32Number() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ bp += 4;
+ if (p->ppsize > 0) {
+ if ((rv = check_null_string(p->crdname[t],p->crdsize[t])) == 1) {
+ sprintf(icp->err,"icmCrdInfo_write: CRD%d name is not terminated",t);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ /* Haven't checked if rv == 2 is legal or not */
+ memmove((void *)bp, (void *)p->crdname[t], p->crdsize[t]);
+ bp += p->crdsize[t];
+ }
+ }
+
+ /* Write to the file */
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmCrdInfo_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+ icp->al->free(icp->al, buf);
+ return 0;
+}
+
+/* Dump a text description of the object */
+static void icmCrdInfo_dump(
+ icmBase *pp,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ unsigned int i, r, c, size, t;
+
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"PostScript Product name and CRD names:\n");
+
+ op->gprintf(op," Product name:\n");
+ op->gprintf(op," No. chars = %lu\n",p->ppsize);
+
+ size = p->ppsize > 0 ? p->ppsize-1 : 0;
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 73) {
+ if (isprint(p->ppname[i])) {
+ op->gprintf(op,"%c",p->ppname[i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->ppname[i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ op->gprintf(op," CRD%ld name:\n",t);
+ op->gprintf(op," No. chars = %lu\n",p->crdsize[t]);
+
+ size = p->crdsize[t] > 0 ? p->crdsize[t]-1 : 0;
+ i = 0;
+ for (r = 1;; r++) { /* count rows */
+ if (i >= size) {
+ op->gprintf(op,"\n");
+ break;
+ }
+ if (r > 1 && verb < 2) {
+ op->gprintf(op,"...\n");
+ break; /* Print 1 row if not verbose */
+ }
+ c = 1;
+ op->gprintf(op," 0x%04lx: ",i);
+ c += 10;
+ while (i < size && c < 73) {
+ if (isprint(p->crdname[t][i])) {
+ op->gprintf(op,"%c",p->crdname[t][i]);
+ c++;
+ } else {
+ op->gprintf(op,"\\%03o",p->crdname[t][i]);
+ c += 4;
+ }
+ i++;
+ }
+ if (i < size)
+ op->gprintf(op,"\n");
+ }
+ }
+}
+
+/* Allocate variable sized data elements */
+static int icmCrdInfo_allocate(
+ icmBase *pp
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ icc *icp = p->icp;
+ unsigned int t;
+
+ if (p->ppsize != p->_ppsize) {
+ if (ovr_mul(p->ppsize, sizeof(char))) {
+ sprintf(icp->err,"icmCrdInfo_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->ppname != NULL)
+ icp->al->free(icp->al, p->ppname);
+ if ((p->ppname = (char *) icp->al->calloc(icp->al, p->ppsize, sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmCrdInfo_alloc: malloc() of string data failed");
+ return icp->errc = 2;
+ }
+ p->_ppsize = p->ppsize;
+ }
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ if (p->crdsize[t] != p->_crdsize[t]) {
+ if (ovr_mul(p->crdsize[t], sizeof(char))) {
+ sprintf(icp->err,"icmCrdInfo_alloc: size overflow");
+ return icp->errc = 1;
+ }
+ if (p->crdname[t] != NULL)
+ icp->al->free(icp->al, p->crdname[t]);
+ if ((p->crdname[t] = (char *) icp->al->calloc(icp->al, p->crdsize[t], sizeof(char))) == NULL) {
+ sprintf(icp->err,"icmCrdInfo_alloc: malloc() of CRD%d name string failed",t);
+ return icp->errc = 2;
+ }
+ p->_crdsize[t] = p->crdsize[t];
+ }
+ }
+ return 0;
+}
+
+/* Free all storage in the object */
+static void icmCrdInfo_delete(
+ icmBase *pp
+) {
+ icmCrdInfo *p = (icmCrdInfo *)pp;
+ icc *icp = p->icp;
+ unsigned int t;
+
+ if (p->ppname != NULL)
+ icp->al->free(icp->al, p->ppname);
+ for (t = 0; t < 4; t++) { /* For all 4 intents */
+ if (p->crdname[t] != NULL)
+ icp->al->free(icp->al, p->crdname[t]);
+ }
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmBase *new_icmCrdInfo(
+ icc *icp
+) {
+ icmCrdInfo *p;
+ if ((p = (icmCrdInfo *) icp->al->calloc(icp->al,1,sizeof(icmCrdInfo))) == NULL)
+ return NULL;
+ p->ttype = icSigCrdInfoType;
+ p->refcount = 1;
+ p->get_size = icmCrdInfo_get_size;
+ p->read = icmCrdInfo_read;
+ p->write = icmCrdInfo_write;
+ p->dump = icmCrdInfo_dump;
+ p->allocate = icmCrdInfo_allocate;
+ p->del = icmCrdInfo_delete;
+ p->icp = icp;
+
+ return (icmBase *)p;
+}
+
+/* ========================================================== */
+/* icmHeader object */
+/* ========================================================== */
+
+/* Return the number of bytes needed to write this tag */
+static unsigned int icmHeader_get_size(
+ icmHeader *p
+) {
+ return 128; /* By definition */
+}
+
+/* read the object, return 0 on success, error code on fail */
+static int icmHeader_read(
+ icmHeader *p,
+ unsigned int len, /* tag length */
+ unsigned int of /* start offset within file */
+) {
+ icc *icp = p->icp;
+ char *buf;
+ unsigned int tt;
+ int rv = 0;
+
+ if (len != 128) {
+ sprintf(icp->err,"icmHeader_read: Length expected to be 128");
+ return icp->errc = 1;
+ }
+
+ if ((buf = (char *) icp->al->malloc(icp->al, len)) == NULL) {
+ sprintf(icp->err,"icmHeader_read: malloc() failed");
+ return icp->errc = 2;
+ }
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->read(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmHeader_read: fseek() or fread() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Check that the magic number is right */
+ tt = read_SInt32Number(buf+36);
+ if (tt != icMagicNumber) { /* Check magic number */
+ sprintf(icp->err,"icmHeader_read: wrong magic number 0x%x",tt);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+
+ /* Fill in the in-memory structure */
+ p->size = read_UInt32Number(buf + 0); /* Profile size in bytes */
+ if (p->size < (128 + 4)) {
+ sprintf(icp->err,"icmHeader_read: file size %d too small to be legal",p->size);
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ p->cmmId = read_SInt32Number(buf + 4); /* CMM for profile */
+ tt = read_UInt8Number(buf + 8); /* Raw major version number */
+ p->majv = (tt >> 4) * 10 + (tt & 0xf); /* Integer major version number */
+ icp->ver = p->majv > 3 ? 1 : 0; /* Set major version flag in icc */
+ tt = read_UInt8Number(buf + 9); /* Raw minor/bug fix version numbers */
+ p->minv = (tt >> 4); /* Integer minor version number */
+ p->bfv = (tt & 0xf); /* Integer bug fix version number */
+ p->deviceClass = (icProfileClassSignature)
+ read_SInt32Number(buf + 12); /* Type of profile */
+ p->colorSpace = (icColorSpaceSignature)
+ read_SInt32Number(buf + 16); /* Color space of data */
+ p->pcs = (icColorSpaceSignature)
+ read_SInt32Number(buf + 20); /* PCS: XYZ or Lab */
+ if ((rv = read_DateTimeNumber(&p->date, buf + 24)) != 0) { /* Creation Date */
+ sprintf(icp->err,"icmHeader_read: read_DateTimeNumber corrupted");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ p->platform = (icPlatformSignature)
+ read_SInt32Number(buf + 40); /* Primary platform */
+ p->flags = read_UInt32Number(buf + 44); /* Various bits */
+ p->manufacturer = read_SInt32Number(buf + 48); /* Dev manufacturer */
+ p->model = read_SInt32Number(buf + 52); /* Dev model */
+ read_UInt64Number(&p->attributes, buf + 56); /* Device attributes */
+ p->renderingIntent = (icRenderingIntent)
+ read_SInt32Number(buf + 64); /* Rendering intent */
+ if ((rv = read_XYZNumber(&p->illuminant, buf + 68)) != 0) { /* Profile illuminant */
+ sprintf(icp->err,"icmHeader_read: read_XYZNumber error");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ p->creator = read_SInt32Number(buf + 80); /* Profile creator */
+
+ for (tt = 0; tt < 16; tt++)
+ p->id[tt] = icp->ver ? read_UInt8Number(buf + 84 + tt) : 0; /* Profile ID */
+
+ icp->al->free(icp->al, buf);
+
+#ifndef ENABLE_V4
+ if (icp->ver) {
+ sprintf(icp->err,"icmHeader_read: ICC V4 not supported!");
+ return icp->errc = 1;
+ }
+#endif
+ return 0;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+static int icmHeader_write(
+ icmHeader *p,
+ unsigned int of, /* File offset to write from */
+ int doid /* Flag, nz = writing to compute ID */
+) {
+ icc *icp = p->icp;
+ char *buf; /* Buffer to write from */
+ unsigned int len;
+ unsigned int tt;
+ int rv = 0;
+
+ /* Allocate a file write buffer */
+ if ((len = p->get_size(p)) == UINT_MAX) {
+ sprintf(icp->err,"icmHeader_write get_size overflow");
+ return icp->errc = 1;
+ }
+ if ((buf = (char *) icp->al->calloc(icp->al,1,len)) == NULL) { /* Zero it - some CMS are fussy */
+ sprintf(icp->err,"icmHeader_write calloc() failed");
+ return icp->errc = 2;
+ }
+
+ /* Fill in the write buffer */
+ if ((rv = write_UInt32Number(p->size, buf + 0)) != 0) { /* Profile size in bytes */
+ sprintf(icp->err,"icmHeader_write: profile size");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+
+ if ((rv = write_SInt32Number(p->cmmId, buf + 4)) != 0) { /* CMM for profile */
+ sprintf(icp->err,"icmHeader_write: cmmId");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if (p->majv < 0 || p->majv > 99 /* Sanity check version numbers */
+ || p->minv < 0 || p->minv > 9
+ || p->bfv < 0 || p->bfv > 9) {
+ sprintf(icp->err,"icmHeader_write: version number");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 1;
+ }
+ tt = ((p->majv/10) << 4) + (p->majv % 10);
+ if ((rv = write_UInt8Number(tt, buf + 8)) != 0) { /* Raw major version number */
+ sprintf(icp->err,"icmHeader_write: Uint8Number major version");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ tt = (p->minv << 4) + p->bfv;
+ if ((rv = write_UInt8Number(tt, buf + 9)) != 0) { /* Raw minor/bug fix version numbers */
+ sprintf(icp->err,"icmHeader_write: Uint8Number minor/bug fix");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number((int)p->deviceClass, buf + 12)) != 0) { /* Type of profile */
+ sprintf(icp->err,"icmHeader_write: SInt32Number deviceClass");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number((int)p->colorSpace, buf + 16)) != 0) { /* Color space of data */
+ sprintf(icp->err,"icmHeader_write: SInt32Number data color space");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number((int)p->pcs, buf + 20)) != 0) { /* PCS: XYZ or Lab */
+ sprintf(icp->err,"icmHeader_write: SInt32Number PCS");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_DateTimeNumber(&p->date, buf + 24)) != 0) { /* Creation Date */
+ sprintf(icp->err,"icmHeader_write: DateTimeNumber creation");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number(icMagicNumber, buf+36)) != 0) { /* Magic number */
+ sprintf(icp->err,"icmHeader_write: SInt32Number magic");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number((int)p->platform, buf + 40)) != 0) { /* Primary platform */
+ sprintf(icp->err,"icmHeader_write: SInt32Number platform");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt32Number(doid ? 0 : p->flags, buf + 44)) != 0) { /* Various flag bits */
+ sprintf(icp->err,"icmHeader_write: UInt32Number flags");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number(p->manufacturer, buf + 48)) != 0) { /* Dev manufacturer */
+ sprintf(icp->err,"icmHeader_write: SInt32Number manufaturer");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((write_SInt32Number(p->model, buf + 52)) != 0) { /* Dev model */
+ sprintf(icp->err,"icmHeader_write: SInt32Number model");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_UInt64Number(&p->attributes, buf + 56)) != 0) { /* Device attributes */
+ sprintf(icp->err,"icmHeader_write: UInt64Number attributes");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number(doid ? 0 : (int)p->renderingIntent, buf + 64)) != 0) { /* Rendering intent */
+ sprintf(icp->err,"icmHeader_write: SInt32Number rendering intent");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_XYZNumber(&p->illuminant, buf + 68)) != 0) { /* Profile illuminant */
+ sprintf(icp->err,"icmHeader_write: XYZNumber illuminant");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if ((rv = write_SInt32Number(p->creator, buf + 80)) != 0) { /* Profile creator */
+ sprintf(icp->err,"icmHeader_write: SInt32Number creator");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ if (doid == 0 && icp->ver) { /* ID is V4.0+ feature */
+ for (tt = 0; tt < 16; tt++) {
+ if ((rv = write_UInt8Number(p->id[tt], buf + 84 + tt)) != 0) { /* Profile ID */
+ sprintf(icp->err,"icmHeader_write: UInt8Number creator");
+ icp->al->free(icp->al, buf);
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ if ( icp->fp->seek(icp->fp, of) != 0
+ || icp->fp->write(icp->fp, buf, 1, len) != len) {
+ sprintf(icp->err,"icmHeader_write fseek() or fwrite() failed");
+ icp->al->free(icp->al, buf);
+ return icp->errc = 2;
+ }
+
+ icp->al->free(icp->al, buf);
+ return rv;
+}
+
+static void icmHeader_dump(
+ icmHeader *p,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ int i;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"Header:\n");
+ op->gprintf(op," size = %d bytes\n",p->size);
+ op->gprintf(op," CMM = %s\n",tag2str(p->cmmId));
+ op->gprintf(op," Version = %d.%d.%d\n",p->majv, p->minv, p->bfv);
+ op->gprintf(op," Device Class = %s\n", string_ProfileClassSignature(p->deviceClass));
+ op->gprintf(op," Color Space = %s\n", string_ColorSpaceSignature(p->colorSpace));
+ op->gprintf(op," Conn. Space = %s\n", string_ColorSpaceSignature(p->pcs));
+ op->gprintf(op," Date, Time = %s\n", string_DateTimeNumber(&p->date));
+ op->gprintf(op," Platform = %s\n", string_PlatformSignature(p->platform));
+ op->gprintf(op," Flags = %s\n", string_ProfileHeaderFlags(p->flags));
+ op->gprintf(op," Dev. Mnfctr. = %s\n", tag2str(p->manufacturer)); /* ~~~ */
+ op->gprintf(op," Dev. Model = %s\n", tag2str(p->model)); /* ~~~ */
+ op->gprintf(op," Dev. Attrbts = %s\n", string_DeviceAttributes(p->attributes.l));
+ op->gprintf(op," Rndrng Intnt = %s\n", string_RenderingIntent(p->renderingIntent));
+ op->gprintf(op," Illuminant = %s\n", string_XYZNumber_and_Lab(&p->illuminant));
+ op->gprintf(op," Creator = %s\n", tag2str(p->creator)); /* ~~~ */
+ if (p->icp->ver) { /* V4.0+ feature */
+ for (i = 0; i < 16; i++) { /* Check if ID has been set */
+ if (p->id[i] != 0)
+ break;
+ }
+ if (i < 16)
+ op->gprintf(op," ID = %02X%02X%02X%02X%02X%02X%02X%02X"
+ "%02X%02X%02X%02X%02X%02X%02X%02X\n",
+ p->id[0], p->id[1], p->id[2], p->id[3], p->id[4], p->id[5], p->id[6], p->id[7],
+ p->id[8], p->id[9], p->id[10], p->id[11], p->id[12], p->id[13], p->id[14], p->id[15]);
+ else
+ op->gprintf(op," ID = <Not set>\n");
+ }
+ op->gprintf(op,"\n");
+}
+
+static void icmHeader_delete(
+ icmHeader *p
+) {
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* Create an empty object. Return null on error */
+static icmHeader *new_icmHeader(
+ icc *icp
+) {
+ icmHeader *p;
+ if ((p = (icmHeader *) icp->al->calloc(icp->al,1,sizeof(icmHeader))) == NULL)
+ return NULL;
+ p->icp = icp;
+ p->get_size = icmHeader_get_size;
+ p->read = icmHeader_read;
+ p->write = icmHeader_write;
+ p->dump = icmHeader_dump;
+ p->del = icmHeader_delete;
+
+ return p;
+}
+
+/* ---------------------------------------------------------- */
+/* Type vector table. Match the Tag type against the object creator */
+static struct {
+ icTagTypeSignature ttype; /* The tag type signature */
+ icmBase * (*new_obj)(icc *icp);
+} typetable[] = {
+ {icSigColorantTableType, new_icmColorantTable},
+ {icmSigAltColorantTableType, new_icmColorantTable},
+ {icSigCrdInfoType, new_icmCrdInfo},
+ {icSigCurveType, new_icmCurve},
+ {icSigDataType, new_icmData},
+ {icSigDateTimeType, new_icmDateTimeNumber},
+ {icSigLut16Type, new_icmLut},
+ {icSigLut8Type, new_icmLut},
+ {icSigMeasurementType, new_icmMeasurement},
+ {icSigNamedColorType, new_icmNamedColor},
+ {icSigNamedColor2Type, new_icmNamedColor},
+ {icSigProfileSequenceDescType, new_icmProfileSequenceDesc},
+ {icSigS15Fixed16ArrayType, new_icmS15Fixed16Array},
+ {icSigScreeningType, new_icmScreening},
+ {icSigSignatureType, new_icmSignature},
+ {icSigTextDescriptionType, new_icmTextDescription},
+ {icSigTextType, new_icmText},
+ {icSigU16Fixed16ArrayType, new_icmU16Fixed16Array},
+ {icSigUcrBgType, new_icmUcrBg},
+ {icSigVideoCardGammaType, new_icmVideoCardGamma},
+ {icSigUInt16ArrayType, new_icmUInt16Array},
+ {icSigUInt32ArrayType, new_icmUInt32Array},
+ {icSigUInt64ArrayType, new_icmUInt64Array},
+ {icSigUInt8ArrayType, new_icmUInt8Array},
+ {icSigViewingConditionsType, new_icmViewingConditions},
+ {icSigXYZArrayType, new_icmXYZArray},
+ {icMaxEnumType, NULL}
+};
+
+/* Table that lists the legal Types for each Tag Signature */
+static struct {
+ icTagSignature sig;
+ icTagTypeSignature ttypes[4]; /* Arbitrary max of 4 */
+} sigtypetable[] = {
+ {icSigAToB0Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigAToB1Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigAToB2Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigBlueColorantTag, {icSigXYZType,icMaxEnumType}},
+ {icSigBlueTRCTag, {icSigCurveType,icMaxEnumType}},
+ {icSigBToA0Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigBToA1Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigBToA2Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigCalibrationDateTimeTag, {icSigDateTimeType,icMaxEnumType}},
+ {icSigCharTargetTag, {icSigTextType,icMaxEnumType}},
+ {icSigColorantTableTag, {icSigColorantTableType,icmSigAltColorantTableType,
+ icMaxEnumType}},
+ {icSigColorantTableOutTag, {icSigColorantTableType,icmSigAltColorantTableType,
+ icMaxEnumType}},
+ {icSigCopyrightTag, {icSigTextType,icMaxEnumType}},
+ {icSigCrdInfoTag, {icSigCrdInfoType,icMaxEnumType}},
+ {icSigDeviceMfgDescTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigDeviceModelDescTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigGamutTag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigGrayTRCTag, {icSigCurveType,icMaxEnumType}},
+ {icSigGreenColorantTag, {icSigXYZType,icMaxEnumType}},
+ {icSigGreenTRCTag, {icSigCurveType,icMaxEnumType}},
+ {icSigLuminanceTag, {icSigXYZType,icMaxEnumType}},
+ {icSigMeasurementTag, {icSigMeasurementType,icMaxEnumType}},
+ {icSigMediaBlackPointTag, {icSigXYZType,icMaxEnumType}},
+ {icSigMediaWhitePointTag, {icSigXYZType,icMaxEnumType}},
+ {icSigNamedColorTag, {icSigNamedColorType,icMaxEnumType}},
+ {icSigNamedColor2Tag, {icSigNamedColor2Type,icMaxEnumType}},
+ {icSigPreview0Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigPreview1Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigPreview2Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}},
+ {icSigProfileDescriptionTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigProfileSequenceDescTag, {icSigProfileSequenceDescType,icMaxEnumType}},
+ {icSigPs2CRD0Tag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2CRD1Tag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2CRD2Tag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2CRD3Tag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2CSATag, {icSigDataType,icMaxEnumType}},
+ {icSigPs2RenderingIntentTag, {icSigDataType,icMaxEnumType}},
+ {icSigRedColorantTag, {icSigXYZType,icMaxEnumType}},
+ {icSigRedTRCTag, {icSigCurveType,icMaxEnumType}},
+ {icSigScreeningDescTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigScreeningTag, {icSigScreeningType,icMaxEnumType}},
+ {icSigTechnologyTag, {icSigSignatureType,icMaxEnumType}},
+ {icSigUcrBgTag, {icSigUcrBgType,icMaxEnumType}},
+ {icSigVideoCardGammaTag, {icSigVideoCardGammaType,icMaxEnumType}},
+ {icSigViewingCondDescTag, {icSigTextDescriptionType,icMaxEnumType}},
+ {icSigViewingConditionsTag, {icSigViewingConditionsType,icMaxEnumType}},
+ {icMaxEnumTag, {icMaxEnumType}}
+};
+
+/* Fake color tag for specifying PCS */
+#define icSigPCSData ((icColorSpaceSignature) 0x50435320L)
+
+/* Table that lists the required tags for various profiles */
+static struct {
+ icProfileClassSignature sig; /* Profile signature */
+ int chans; /* Data Color channels, -ve for match but try next, */
+ /* -100 for ignore, -200 for ignore and try next */
+ icColorSpaceSignature colsig; /* Data Color space signature, icMaxEnumData for ignore, */
+ /* icSigPCSData for XYZ of Lab */
+ icColorSpaceSignature pcssig; /* PCS Color space signature, icMaxEnumData for ignore, */
+ /* icSigPCSData for XYZ or Lab */
+ icTagSignature tags[12]; /* Arbitrary max of 12 */
+} tagchecktable[] = {
+ {icSigInputClass, -1, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigGrayTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigInputClass, -3, icMaxEnumData, icSigXYZData,
+ {icSigProfileDescriptionTag,
+ icSigRedColorantTag,
+ icSigGreenColorantTag,
+ icSigBlueColorantTag,
+ icSigRedTRCTag,
+ icSigGreenTRCTag,
+ icSigBlueTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigInputClass, -100, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigDisplayClass, -1, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigGrayTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigDisplayClass, -3, icSigRgbData, icSigXYZData, /* Rgb or any 3 component space ?? */
+ {icSigProfileDescriptionTag,
+ icSigRedColorantTag,
+ icSigGreenColorantTag,
+ icSigBlueColorantTag,
+ icSigRedTRCTag,
+ icSigGreenTRCTag,
+ icSigBlueTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ /* Non-3 component Display device */
+ {icSigDisplayClass, -100, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag, /* BToA doesn't seem to be required, which is strange... */
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -1, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigGrayTRCTag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -1, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigBToA0Tag,
+ icSigGamutTag,
+ icSigAToB1Tag,
+ icSigBToA1Tag,
+ icSigAToB2Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -2, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigBToA0Tag,
+ icSigGamutTag,
+ icSigAToB1Tag,
+ icSigBToA1Tag,
+ icSigAToB2Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -3, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigBToA0Tag,
+ icSigGamutTag,
+ icSigAToB1Tag,
+ icSigBToA1Tag,
+ icSigAToB2Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -4, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigBToA0Tag,
+ icSigGamutTag,
+ icSigAToB1Tag,
+ icSigBToA1Tag,
+ icSigAToB2Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigOutputClass, -100, icMaxEnumData, icSigPCSData, /* Assumed from Hexachrome examples */
+ {icSigProfileDescriptionTag,
+ icSigBToA0Tag,
+ icSigBToA1Tag,
+ icSigBToA2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigLinkClass, -100, icMaxEnumData, icMaxEnumData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigProfileSequenceDescTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigColorSpaceClass, -100, icMaxEnumData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigBToA0Tag,
+ icSigAToB0Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigAbstractClass, -100, icSigPCSData, icSigPCSData,
+ {icSigProfileDescriptionTag,
+ icSigAToB0Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigNamedColorClass, -200, icMaxEnumData, icMaxEnumData,
+ {icSigProfileDescriptionTag,
+ icSigNamedColor2Tag,
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icSigNamedColorClass, -100, icMaxEnumData, icMaxEnumData,
+ {icSigProfileDescriptionTag,
+ icSigNamedColorTag, /* Not strictly V3.4 */
+ icSigMediaWhitePointTag,
+ icSigCopyrightTag, icMaxEnumTag}},
+
+ {icMaxEnumClass,-1,icMaxEnumData, icMaxEnumData, {icMaxEnumTag}}
+};
+
+/* ------------------------------------------------------------- */
+
+/* Return the current read fp (if any) */
+static icmFile *icc_get_rfp(icc *p) {
+ return p->fp;
+}
+
+/* Change the version to be non-default (ie. not 2.2.0), */
+/* e.g. ICC V4 (used for creation) */
+/* Return 0 if OK */
+/* Return 1 on error */
+static int icc_set_version(icc *p, icmICCVersion ver) {
+
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_set_version: Header is missing");
+ return p->errc = 1;
+ }
+
+ switch (ver) {
+ case icmVersionDefault:
+ p->header->majv = 2;
+ p->header->minv = 2;
+ p->header->bfv = 0;
+ break;
+ case icmVersion2_3:
+ p->header->majv = 2;
+ p->header->minv = 3;
+ p->header->bfv = 0;
+ break;
+ case icmVersion2_4:
+ p->header->majv = 2;
+ p->header->minv = 4;
+ p->header->bfv = 0;
+ break;
+#ifdef ENABLE_V4
+ case icmVersion4_1:
+ p->header->majv = 4;
+ p->header->minv = 1;
+ p->header->bfv = 0;
+ break;
+#endif
+ default:
+ sprintf(p->err,"icc_set_version: Unsupported version 0x%x",ver);
+ return p->errc = 1;
+ }
+ return 0;
+}
+
+
+/* Check that the ICC profile looks like it will be legal. */
+/* Return non-zero and set error string if not */
+static int check_icc_legal(
+ icc *p
+) {
+ int i, j;
+ icProfileClassSignature sig;
+ icColorSpaceSignature colsig;
+ icColorSpaceSignature pcssig;
+ int dchans;
+
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_check_legal: Header is missing");
+ return p->errc = 1;
+ }
+
+ sig = p->header->deviceClass;
+ colsig = p->header->colorSpace;
+ dchans = number_ColorSpaceSignature(colsig);
+ pcssig = p->header->pcs;
+
+ /* Find a matching table entry */
+ for (i = 0; tagchecktable[i].sig != icMaxEnumType; i++) {
+ if ( tagchecktable[i].sig == sig
+ && ( tagchecktable[i].chans == dchans /* Exactly matches */
+ || tagchecktable[i].chans == -dchans /* Exactly matches, but can try next table */
+ || tagchecktable[i].chans < -99) /* Doesn't have to match or try next table */
+ && ( tagchecktable[i].colsig == colsig
+ || (tagchecktable[i].colsig == icSigPCSData
+ && (colsig == icSigXYZData || colsig == icSigLabData))
+ || tagchecktable[i].colsig == icMaxEnumData)
+ && ( tagchecktable[i].pcssig == pcssig
+ || (tagchecktable[i].pcssig == icSigPCSData
+ && (pcssig == icSigXYZData || pcssig == icSigLabData))
+ || tagchecktable[i].pcssig == icMaxEnumData)) {
+
+ /* Found entry, so now check that all the required tags are present. */
+ for (j = 0; tagchecktable[i].tags[j] != icMaxEnumType; j++) {
+ if (p->find_tag(p, tagchecktable[i].tags[j]) != 0) { /* Not present! */
+ if (tagchecktable[i].chans == -200
+ || tagchecktable[i].chans == -dchans) { /* But can try next table */
+ break;
+ }
+ sprintf(p->err,"icc_check_legal: deviceClass %s is missing required tag %s",
+ tag2str(sig), tag2str(tagchecktable[i].tags[j]));
+ return p->errc = 1;
+ }
+ }
+ if (tagchecktable[i].tags[j] == icMaxEnumType) {
+ break; /* Fount all required tags */
+ }
+ }
+ }
+
+ /* According to the spec. if the deviceClass is:
+ an Abstract Class: both in and out color spaces should be PCS
+ an Link Class: both in and out color spaces can be any, and should
+ be the input space of the first profile in the link, and the
+ input space of the last profile in the link respectively.
+ a Named Class: in and out color spaces are not defined in the spec.
+ Input, Display, Output and ColorSpace Classes, input color
+ space can be any, and the output space must be PCS.
+ ~~ should check this here ???
+ */
+
+ return 0; /* Assume anything is ok */
+}
+
+
+/* read the object, return 0 on success, error code on fail */
+/* NOTE: this doesn't read the tag types, they should be read on demand. */
+/* NOTE: fp ownership is taken even if the function fails. */
+static int icc_read_x(
+ icc *p,
+ icmFile *fp, /* File to read from */
+ unsigned int of, /* File offset to read from */
+ int take_fp /* NZ if icc is to take ownership of fp */
+) {
+ char tcbuf[4]; /* Tag count read buffer */
+ unsigned int i, len;
+ unsigned int minoff, maxoff; /* Minimum and maximum offsets of tag data */
+ int er = 0; /* Error code */
+
+ p->fp = fp;
+ if (take_fp)
+ p->del_fp = 1;
+ p->of = of;
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_read: No header defined");
+ return p->errc = 1;
+ }
+
+ /* Read the header */
+ if (p->header->read(p->header, 128, of)) {
+ return 1;
+ }
+
+ /* Read the tag count */
+ if ( p->fp->seek(p->fp, of + 128) != 0
+ || p->fp->read(p->fp, tcbuf, 1, 4) != 4) {
+ sprintf(p->err,"icc_read: fseek() or fread() failed on tag count");
+ return p->errc = 1;
+ }
+ p->count = read_UInt32Number(tcbuf);
+
+ /* Sanity check it */
+ if (p->count > 357913940 /* (2^32-5)/12 */
+ || (p->count > ((p->header->size - 128 - 4) / 12))) {
+ sprintf(p->err,"icc_read: tag count %d is too large to be legal",p->count);
+ return p->errc = 1;
+ }
+ minoff = 128 + 4 + p->count * 12;
+ maxoff = p->header->size;
+
+ if (p->count > 0) {
+ char *bp, *buf;
+
+ if (ovr_mul(p->count, sizeof(icmTag))) {
+ sprintf(p->err,"icc_read: size overflow");
+ return p->errc = 1;
+ }
+
+ /* Read the table into memory */
+ if ((p->data = (icmTag *) p->al->calloc(p->al, p->count, sizeof(icmTag))) == NULL) {
+ sprintf(p->err,"icc_read: Tag table malloc() failed");
+ return p->errc = 2;
+ }
+
+ len = sat_mul(p->count, 12);
+ if ((buf = (char *) p->al->malloc(p->al, len)) == NULL) {
+ sprintf(p->err,"icc_read: Tag table read buffer malloc() failed");
+ p->al->free(p->al, p->data);
+ p->data = NULL;
+ return p->errc = 2;
+ }
+ if ( p->fp->seek(p->fp, of + 128 + 4) != 0
+ || p->fp->read(p->fp, buf, 1, len) != len) {
+ sprintf(p->err,"icc_read: fseek() or fread() failed on tag table");
+ p->al->free(p->al, p->data);
+ p->data = NULL;
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+
+ /* Fill in the tag table structure for each tag */
+ for (bp = buf, i = 0; i < p->count; i++, bp += 12) {
+ p->data[i].sig = (icTagSignature)read_SInt32Number(bp + 0);
+ p->data[i].offset = read_UInt32Number(bp + 4);
+ p->data[i].size = read_UInt32Number(bp + 8);
+ }
+ p->al->free(p->al, buf);
+
+ /* Check that each tag lies within the nominated space available, */
+ /* and has a reasonable size. */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].offset < minoff
+ || p->data[i].offset > maxoff
+ || p->data[i].size < 4
+ || p->data[i].size > (maxoff - minoff)
+ || (p->data[i].offset + p->data[i].size) < p->data[i].offset /* Overflow */
+ || (p->data[i].offset + p->data[i].size) > p->header->size) {
+ sprintf(p->err,"icc_read: tag %d is out of range of the nominated file size",i);
+ p->al->free(p->al, p->data);
+ p->data = NULL;
+ return p->errc = 1;
+ }
+ }
+
+ /* Read each tag type */
+ for (i = 0; i < p->count; i++) {
+ if ( p->fp->seek(p->fp, of + p->data[i].offset) != 0
+ || p->fp->read(p->fp, tcbuf, 1, 4) != 4) {
+ sprintf(p->err,"icc_read: fseek() or fread() failed on tag headers");
+ p->al->free(p->al, p->data);
+ p->data = NULL;
+ return p->errc = 1;
+ }
+ p->data[i].ttype = (icTagTypeSignature) read_SInt32Number(tcbuf); /* Tag type */
+ p->data[i].objp = NULL; /* Read on demand */
+ }
+ } /* p->count > 0 */
+
+ return er;
+}
+
+/* read the object, return 0 on success, error code on fail */
+/* NOTE: this doesn't read the tag types, they should be read on demand. */
+/* (backward compatible version) */
+static int icc_read(
+ icc *p,
+ icmFile *fp, /* File to read from */
+ unsigned int of /* File offset to read from */
+) {
+ return icc_read_x(p, fp, of, 0);
+}
+
+/* Check the profiles ID. We assume the file has already been read. */
+/* Return 0 if OK, 1 if no ID to check, 2 if doesn't match, 3 if some other error. */
+/* NOTE: this reads the whole file again, to compute the checksum. */
+static int icc_check_id(
+ icc *p,
+ ORD8 *rid /* Optionaly return computed ID */
+) {
+ unsigned char buf[128];
+ ORD8 id[16];
+ icmMD5 *md5 = NULL;
+ unsigned int len;
+
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_check_id: No header defined");
+ return p->errc = 3;
+ }
+ len = p->header->size; /* Claimed size of profile */
+
+ /* See if there is an ID to compare against */
+ for (len = 0; len < 16; len++) {
+ if (p->header->id[len] != 0)
+ break;
+ }
+ if (len >= 16) {
+ return 1;
+ }
+
+ if ((md5 = new_icmMD5(p->al)) == NULL) {
+ sprintf(p->err,"icc_check_id: new_icmMD5 failed");
+ return p->errc = 3;
+ }
+
+ /* Check the header */
+ if ( p->fp->seek(p->fp, p->of) != 0
+ || p->fp->read(p->fp, buf, 1, 128) != 128) {
+ sprintf(p->err,"icc_check_id: fseek() or fread() failed");
+ return p->errc = 3;
+ }
+
+ /* Zero the appropriate bytes in the header */
+ buf[44] = buf[45] = buf[46] = buf[47] = 0;
+ buf[64] = buf[65] = buf[66] = buf[67] = 0;
+ buf[84] = buf[85] = buf[86] = buf[87] =
+ buf[88] = buf[89] = buf[90] = buf[91] =
+ buf[92] = buf[93] = buf[94] = buf[95] =
+ buf[96] = buf[97] = buf[98] = buf[99] = 0;
+
+ md5->add(md5, buf, 128);
+
+ /* Suck in the rest of the profile */
+ for (;len > 0;) {
+ unsigned int rsize = 128;
+ if (rsize > len)
+ rsize = len;
+ if (p->fp->read(p->fp, buf, 1, rsize) != rsize) {
+ sprintf(p->err,"icc_check_id: fread() failed");
+ return p->errc = 3;
+ }
+ md5->add(md5, buf, rsize);
+ len -= rsize;
+ }
+
+ md5->get(md5, id);
+ md5->del(md5);
+
+ if (rid != NULL) {
+ for (len = 0; len < 16; len++)
+ rid[len] = id[len];
+ }
+
+ /* Check the ID */
+ for (len = 0; len < 16; len++) {
+ if (p->header->id[len] != id[len])
+ break;
+ }
+ if (len >= 16) {
+ return 0; /* Matched */
+ }
+ return 2; /* Didn't match */
+}
+
+/* Return the total size needed for the profile */
+/* Return 0 on error. */
+static unsigned int icc_get_size(
+ icc *p
+) {
+ unsigned int i, size = 0;
+
+#ifdef ICM_STRICT
+ /* Check that the right tags etc. are present for a legal ICC profile */
+ if (check_icc_legal(p) != 0) {
+ return 0;
+ }
+#endif /* ICM_STRICT */
+
+ /* Compute the total size and tag element data offsets */
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_get_size: No header defined");
+ p->errc = 1;
+ return 0;
+ }
+
+ size = sat_add(size, p->header->get_size(p->header));
+ /* Assume header is aligned */
+ size = sat_addaddmul(size, 4, p->count, 12); /* Tag table length */
+ size = sat_align(ALIGN_SIZE, size);
+
+ if (size == UINT_MAX) {
+ sprintf(p->err,"icc_get_size: size overflow");
+ return p->errc = 1;
+ }
+
+ /* Reset touched flag for each tag type */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp == NULL) {
+ sprintf(p->err,"icc_get_size: Internal error - NULL tag element");
+ p->errc = 1;
+ return 0;
+ }
+ p->data[i].objp->touched = 0;
+ }
+ /* Get size for each tag type, skipping links */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp->touched == 0) { /* Not alllowed for previously */
+ size = sat_add(size, p->data[i].objp->get_size(p->data[i].objp));
+ size = sat_align(ALIGN_SIZE, size);
+ p->data[i].objp->touched = 1; /* Don't account for this again */
+ }
+ }
+
+ return size; /* Total size needed, or UINT_MAX if overflow */
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+/* NOTE: fp ownership is taken even if the function fails. */
+static int icc_write_x(
+ icc *p,
+ icmFile *fp, /* File to write to */
+ unsigned int of, /* File offset to write to */
+ int take_fp /* NZ if icc is to take ownership of fp */
+) {
+ char *bp, *buf; /* tag table buffer */
+ unsigned int len;
+ int rv = 0;
+ unsigned int i, size = 0;
+ unsigned char pbuf[ALIGN_SIZE];
+
+ p->fp = fp; /* Open file pointer */
+ if (take_fp)
+ p->del_fp = 1;
+ p->of = of; /* Offset of ICC profile */
+
+ for (i = 0; i < ALIGN_SIZE; i++)
+ pbuf[i] = 0;
+
+ /* Check that the right tags etc. are present for a legal ICC profile */
+ if ((rv = check_icc_legal(p)) != 0) {
+ return rv;
+ }
+
+ /* Compute the total size and tag element data offsets */
+ if (p->header == NULL) {
+ sprintf(p->err,"icc_write: No header defined");
+ return p->errc = 1;
+ }
+
+ size = sat_add(size, p->header->get_size(p->header));
+ /* Assume header is aligned */
+ len = sat_addmul(4, p->count, 12); /* Tag table length */
+ len = sat_sub(sat_align(ALIGN_SIZE, sat_add(size, len)), size); /* Aligned size */
+ size = sat_align(ALIGN_SIZE, sat_add(size, len));
+
+ if (len == UINT_MAX) {
+ sprintf(p->err,"icc_write get_size overflow");
+ return p->errc = 1;
+ }
+
+ /* Allocate memory buffer for tag table */
+ if ((buf = (char *) p->al->calloc(p->al, 1, len)) == NULL) {
+ sprintf(p->err,"icc_write calloc() failed");
+ return p->errc = 2;
+ }
+ bp = buf;
+
+ if ((rv = write_UInt32Number(p->count, bp)) != 0) { /* Tag count */
+ sprintf(p->err,"icc_write: write_UInt32Number() failed on tag count");
+ p->al->free(p->al, buf);
+ return p->errc = rv;
+ }
+ bp += 4;
+ /* Reset touched flag for each tag type */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp == NULL) {
+ sprintf(p->err,"icc_write: Internal error - NULL tag element");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+ p->data[i].objp->touched = 0;
+ }
+ /* Set the offset and size for each tag type, create the tag table write data */
+ /* and compute the total profile size. */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp->touched == 0) { /* Allocate space for tag type */
+ p->data[i].offset = size; /* Profile relative target */
+ p->data[i].size = p->data[i].objp->get_size(p->data[i].objp);
+ size = sat_add(size, p->data[i].size);
+ p->data[i].pad = sat_sub(sat_align(ALIGN_SIZE, size), size);
+ size = sat_align(ALIGN_SIZE, size);
+ p->data[i].objp->touched = 1; /* Allocated space for it */
+ if (size == UINT_MAX) {
+ sprintf(p->err,"icc_write: size overflow");
+ return p->errc = 1;
+ }
+ } else { /* must be linked - copy allocation */
+ unsigned int k;
+ for (k = 0; k < p->count; k++) { /* Find linked tag */
+ if (p->data[k].objp == p->data[i].objp)
+ break;
+ }
+ if (k == p->count) {
+ sprintf(p->err,"icc_write: corrupted link");
+ return p->errc = 2;
+ }
+ p->data[i].offset = p->data[k].offset;
+ p->data[i].size = p->data[k].size;
+ p->data[i].pad = p->data[k].pad;
+ }
+ /* Write tag table entry for this tag */
+ if ((rv = write_SInt32Number((int)p->data[i].sig,bp + 0)) != 0) {
+ sprintf(p->err,"icc_write: write_SInt32Number() failed on tag signature");
+ p->al->free(p->al, buf);
+ return p->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->data[i].offset, bp + 4)) != 0) {
+ sprintf(p->err,"icc_write: write_UInt32Number() failed on tag offset");
+ p->al->free(p->al, buf);
+ return p->errc = rv;
+ }
+ if ((rv = write_UInt32Number(p->data[i].size, bp + 8)) != 0) {
+ sprintf(p->err,"icc_write: write_UInt32Number() failed on tag size");
+ p->al->free(p->al, buf);
+ return p->errc = rv;
+ }
+ bp += 12;
+ }
+ p->header->size = size; /* Record total icc padded size */
+
+
+ /* If V4.0+, Compute the MD5 id for the profile. */
+ /* We do this by writing to a fake icmFile */
+ if (p->ver) {
+ icmMD5 *md5 = NULL;
+ icmFile *ofp, *dfp = NULL;
+
+ if ((md5 = new_icmMD5(p->al)) == NULL) {
+ sprintf(p->err,"icc_write: new_icmMD5 failed");
+ p->al->free(p->al, buf);
+ return p->errc = 2;
+ }
+
+ if ((dfp = new_icmFileMD5_a(md5, p->al)) == NULL) {
+ sprintf(p->err,"icc_write: new_icmFileMD5 failed");
+ md5->del(md5);
+ p->al->free(p->al, buf);
+ return p->errc = 2;
+ }
+
+ ofp = p->fp;
+ p->fp = dfp;
+
+ /* Dumy write the header */
+ if ((rv = p->header->write(p->header, 0, 1)) != 0) {
+ p->al->free(p->al, buf);
+ return rv;
+ }
+
+ /* Dumy write the tag table */
+ if ( p->fp->seek(p->fp, 128) != 0
+ || p->fp->write(p->fp, buf, 1, len) != len) {
+ sprintf(p->err,"icc_write: seek() or write() failed");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+
+ /* Dumy write all the tag element data */
+ /* (We invert meaning of touched here) */
+ for (i = 0; i < p->count; i++) { /* For all the tag element data */
+ if (p->data[i].objp->touched == 0)
+ continue; /* Must be linked, and we've already written it */
+ if ((rv = p->data[i].objp->write(p->data[i].objp, p->data[i].offset)) != 0) {
+ p->al->free(p->al, buf);
+ return rv;
+ }
+ /* Pad with 0 to next boundary */
+ if (p->data[i].pad > 0) {
+ if (p->fp->write(p->fp, pbuf, 1, p->data[i].pad) != p->data[i].pad) {
+ sprintf(p->err,"icc_write: write() failed");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+ }
+ p->data[i].objp->touched = 0; /* Written it, so don't write it again. */
+ }
+
+ if (p->fp->flush(p->fp) != 0) {
+ sprintf(p->err,"icc_write flush() failed");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+
+ if ((p->errc = ((icmFileMD5 *)dfp)->get_errc(dfp)) != 0) {
+ sprintf(p->err,"icc_write compute ID failed with code %d", p->errc);
+ p->al->free(p->al, buf);
+ return p->errc;
+ }
+
+ /* Get the MD5 checksum ID */
+ md5->get(md5, p->header->id);
+
+ dfp->del(dfp);
+ md5->del(md5);
+ p->fp = ofp;
+
+ /* Reset the touched flags */
+ for (i = 0; i < p->count; i++)
+ p->data[i].objp->touched = 1;
+ }
+
+ /* Now write out the profile for real. */
+ /* Although it may appear like we're seeking for each element, */
+ /* in fact elements will be written in file order. */
+
+ /* Write the header */
+ if ((rv = p->header->write(p->header, of, 0)) != 0) {
+ p->al->free(p->al, buf);
+ return rv;
+ }
+
+ /* Write the tag table */
+ if ( p->fp->seek(p->fp, of + 128) != 0
+ || p->fp->write(p->fp, buf, 1, len) != len) {
+ sprintf(p->err,"icc_write: seek() or write() failed");
+ p->al->free(p->al, buf);
+ return p->errc = 1;
+ }
+ p->al->free(p->al, buf);
+
+ /* Write all the tag element data */
+ /* (We invert the meaning of touched here) */
+ for (i = 0; i < p->count; i++) { /* For all the tag element data */
+ if (p->data[i].objp->touched == 0)
+ continue; /* Must be linked, and we've already written it */
+ if ((rv = p->data[i].objp->write(p->data[i].objp, of + p->data[i].offset)) != 0) {
+ return rv;
+ }
+ /* Pad with 0 to next boundary */
+ if (p->data[i].pad > 0) {
+ if (p->fp->write(p->fp, pbuf, 1, p->data[i].pad) != p->data[i].pad) {
+ sprintf(p->err,"icc_write: write() failed");
+ return p->errc = 1;
+ }
+ }
+ p->data[i].objp->touched = 0; /* Written it, so don't write it again. */
+ }
+
+ if (p->fp->flush(p->fp) != 0) {
+ sprintf(p->err,"icc_write flush() failed");
+ return p->errc = 1;
+ }
+
+ return rv;
+}
+
+/* Write the contents of the object. Return 0 on sucess, error code on failure */
+/* (backwards compatible version) */
+static int icc_write(
+ icc *p,
+ icmFile *fp, /* File to write to */
+ unsigned int of /* File offset to write to */
+) {
+ return icc_write_x(p, fp, of, 0);
+}
+
+/* Create and add a tag with the given signature. */
+/* Returns a pointer to the element object */
+/* Returns NULL if error - icc->errc will contain */
+/* 2 on system error, */
+/* 3 if unknown tag */
+/* NOTE: that we prevent tag duplication */
+/* NOTE: to create a tag type icmSigUnknownType, set ttype to icmSigUnknownType, */
+/* and set the actual tag type in icmSigUnknownType->uttype */
+static icmBase *icc_add_tag(
+ icc *p,
+ icTagSignature sig, /* Tag signature - may be unknown */
+ icTagTypeSignature ttype /* Tag type */
+) {
+ icmBase *tp;
+ icmBase *nob;
+ int i = 0, ok = 1;
+ unsigned int j;
+
+ if (ttype != icmSigUnknownType) { /* Check only for possibly known types */
+
+ /* Check that a known signature has an acceptable type */
+ for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
+ if (sigtypetable[i].sig == sig) { /* recognized signature */
+ ok = 0;
+ for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
+ if (sigtypetable[i].ttypes[j] == ttype) /* recognized type */
+ ok = 1;
+ }
+ break;
+ }
+ }
+ if (!ok) {
+ sprintf(p->err,"icc_add_tag: wrong tag type for signature");
+ p->errc = 1;
+ return NULL;
+ }
+
+ /* Check that we know how to handle this type */
+ for (i = 0; typetable[i].ttype != icMaxEnumType; i++) {
+ if (typetable[i].ttype == ttype)
+ break;
+ }
+ if (typetable[i].ttype == icMaxEnumType) {
+ sprintf(p->err,"icc_add_tag: unsupported tag type");
+ p->errc = 1;
+ return NULL;
+ }
+ }
+
+ /* Check that this tag doesn't already exist */
+ /* (Perhaps we should simply replace it, rather than erroring ?) */
+ for (j = 0; j < p->count; j++) {
+ if (p->data[j].sig == sig) {
+ sprintf(p->err,"icc_add_tag: Already have tag '%s' in profile",tag2str(p->data[j].sig));
+ p->errc = 1;
+ return NULL;
+ }
+ }
+
+ /* Make space in tag table for new tag item */
+ if (ovr_mul(sat_add(p->count,1), sizeof(icmTag))) {
+ sprintf(p->err,"icc_add_tag: size overflow");
+ p->errc = 1;
+ return NULL;
+ }
+ if (p->data == NULL)
+ tp = (icmBase *)p->al->malloc(p->al, (p->count+1) * sizeof(icmTag));
+ else
+ tp = (icmBase *)p->al->realloc(p->al, (void *)p->data, (p->count+1) * sizeof(icmTag));
+ if (tp == NULL) {
+ sprintf(p->err,"icc_add_tag: Tag table realloc() failed");
+ p->errc = 2;
+ return NULL;
+ }
+ p->data = (icmTag *)tp;
+
+ if (ttype == icmSigUnknownType) {
+ if ((nob = new_icmUnknown(p)) == NULL)
+ return NULL;
+ } else {
+ /* Allocate the empty object */
+ if ((nob = typetable[i].new_obj(p)) == NULL)
+ return NULL;
+ }
+
+ /* Fill out our tag table entry */
+ p->data[p->count].sig = sig; /* The tag signature */
+ p->data[p->count].ttype = nob->ttype = ttype; /* The tag type signature */
+ p->data[p->count].offset = 0; /* Unknown offset yet */
+ p->data[p->count].size = 0; /* Unknown size yet */
+ p->data[p->count].objp = nob; /* Empty object */
+ p->count++;
+
+ return nob;
+}
+
+/* Create and add a tag which is a link to an existing tag. */
+/* Returns a pointer to the element object */
+/* Returns NULL if error - icc->errc will contain */
+/* 3 if incompatible tag */
+/* NOTE: that we prevent tag duplication */
+static icmBase *icc_link_tag(
+ icc *p,
+ icTagSignature sig, /* Tag signature - may be unknown */
+ icTagSignature ex_sig /* Tag signature of tag to link to */
+) {
+ icmBase *tp;
+ unsigned int j, exi;
+ int i, ok = 1;
+
+ /* Search for existing signature */
+ for (exi = 0; exi < p->count; exi++) {
+ if (p->data[exi].sig == ex_sig) /* Found it */
+ break;
+ }
+ if (exi == p->count) {
+ sprintf(p->err,"icc_link_tag: Can't find existing tag '%s'",tag2str(ex_sig));
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (p->data[exi].objp == NULL) {
+ sprintf(p->err,"icc_link_tag: Existing tag '%s' isn't loaded",tag2str(ex_sig));
+ p->errc = 1;
+ return NULL;
+ }
+
+ /* Check that a known signature has an acceptable type */
+ for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
+ if (sigtypetable[i].sig == sig) { /* recognized signature */
+ ok = 0;
+ for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
+ if (sigtypetable[i].ttypes[j] == p->data[exi].ttype) /* recognized type */
+ ok = 1;
+ }
+ break;
+ }
+ }
+ if (!ok) {
+ sprintf(p->err,"icc_link_tag: wrong tag type for signature");
+ p->errc = 1;
+ return NULL;
+ }
+
+ /* Check that this tag doesn't already exits */
+ for (j = 0; j < p->count; j++) {
+ if (p->data[j].sig == sig) {
+ sprintf(p->err,"icc_link_tag: Already have tag '%s' in profile",tag2str(p->data[j].sig));
+ p->errc = 1;
+ return NULL;
+ }
+ }
+
+ /* Make space in tag table for new tag item */
+ if (p->data == NULL)
+ tp = (icmBase *)p->al->malloc(p->al, (p->count+1) * sizeof(icmTag));
+ else
+ tp = (icmBase *)p->al->realloc(p->al, (void *)p->data, (p->count+1) * sizeof(icmTag));
+ if (tp == NULL) {
+ sprintf(p->err,"icc_link_tag: Tag table realloc() failed");
+ p->errc = 2;
+ return NULL;
+ }
+ p->data = (icmTag *)tp;
+
+ /* Fill out our tag table entry */
+ p->data[p->count].sig = sig; /* The tag signature */
+ p->data[p->count].ttype = p->data[exi].ttype; /* The tag type signature */
+ p->data[p->count].offset = p->data[exi].offset; /* Same offset (may not be allocated yet) */
+ p->data[p->count].size = p->data[exi].size; /* Same size (may not be allocated yet) */
+ p->data[p->count].objp = p->data[exi].objp; /* Shared object */
+ p->data[exi].objp->refcount++; /* Bump reference count on tag type */
+ p->count++;
+
+ return p->data[exi].objp;
+}
+
+/* Search for tag signature */
+/* return: */
+/* 0 if found */
+/* 1 if found but not handled type */
+/* 2 if not found */
+/* NOTE: doesn't set icc->errc or icc->err[] */
+/* NOTE: we don't handle tag duplication - you'll always get the first in the file. */
+static int icc_find_tag(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+ int j;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i == p->count)
+ return 2;
+
+ /* See if we can handle this type */
+ for (j = 0; typetable[j].ttype != icMaxEnumType; j++) {
+ if (typetable[j].ttype == p->data[i].ttype)
+ break;
+ }
+ if (typetable[j].ttype == icMaxEnumType)
+ return 1;
+
+ return 0;
+}
+
+/* Read the specific tag element data, and return a pointer to the object */
+/* (This is an internal function) */
+/* Returns NULL if error - icc->errc will contain: */
+/* 2 if not found */
+/* Returns an icmSigUnknownType object if the tag type isn't handled by a */
+/* specific object and alow_unk is NZ */
+/* NOTE: we don't handle tag duplication - you'll always get the first in the file */
+static icmBase *icc_read_tag_ix(
+ icc *p,
+ unsigned int i, /* Index from 0.. p->count-1 */
+ int alow_unk /* NZ to allow unknown tag to load */
+) {
+ icTagTypeSignature ttype; /* Tag type we will create */
+ icmBase *nob;
+ unsigned int k;
+ int j;
+
+ if (i >= p->count) {
+ sprintf(p->err,"icc_read_tag_ix: index %d is out of range",i);
+ p->errc = 2;
+ return NULL;
+ }
+
+ /* See if it's already been read */
+ if (p->data[i].objp != NULL) {
+ return p->data[i].objp; /* Just return it */
+ }
+
+ /* See if this should be a link */
+ for (k = 0; k < p->count; k++) {
+ if (i == k)
+ continue;
+ if (p->data[i].ttype == p->data[k].ttype /* Exact match and already read */
+ && p->data[i].offset == p->data[k].offset
+ && p->data[i].size == p->data[k].size
+ && p->data[k].objp != NULL)
+ break;
+ }
+ if (k < p->count) { /* Make this a link */
+ p->data[i].objp = p->data[k].objp;
+ p->data[k].objp->refcount++; /* Bump reference count */
+ return p->data[k].objp; /* Done */
+ }
+
+ /* See if we can handle this type */
+ for (j = 0; typetable[j].ttype != icMaxEnumType; j++) {
+ if (typetable[j].ttype == p->data[i].ttype)
+ break;
+ }
+
+ if (typetable[j].ttype == icMaxEnumType) {
+ if (!alow_unk) {
+ p->errc = 2;
+ return NULL;
+ }
+ ttype = icmSigUnknownType; /* Use the Unknown type to handle an unknown tag type */
+ } else {
+ ttype = p->data[i].ttype; /* We known this type */
+ }
+
+ /* Create and read in the object */
+ if (ttype == icmSigUnknownType)
+ nob = new_icmUnknown(p);
+ else
+ nob = typetable[j].new_obj(p);
+
+ if (nob == NULL)
+ return NULL;
+
+ if ((nob->read(nob, p->data[i].size, p->of + p->data[i].offset)) != 0) {
+ nob->del(nob); /* Failed, so destroy it */
+ return NULL;
+ }
+ p->data[i].objp = nob;
+ return nob;
+}
+
+/* Read the tag element data of the first matching, and return a pointer to the object */
+/* Returns NULL if error - icc->errc will contain: */
+/* 2 if not found */
+/* Doesn't read uknown type tags */
+static icmBase *icc_read_tag(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i >= p->count) {
+ sprintf(p->err,"icc_read_tag: Tag '%s' not found",string_TagSignature(sig));
+ p->errc = 2;
+ return NULL;
+ }
+
+ /* Let read_tag_ix do all the work */
+ return icc_read_tag_ix(p, i, 0);
+}
+
+/* Read the tag element data of the first matching, and return a pointer to the object */
+/* Returns NULL if error.
+/* Returns an icmSigUnknownType object if the tag type isn't handled by a specific object. */
+/* NOTE: we don't handle tag duplication - you'll always get the first in the file. */
+static icmBase *icc_read_tag_any(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i >= p->count) {
+ sprintf(p->err,"icc_read_tag: Tag '%s' not found",string_TagSignature(sig));
+ p->errc = 2;
+ return NULL;
+ }
+
+ /* Let read_tag_ix do all the work */
+ return icc_read_tag_ix(p, i, 1);
+}
+
+/* Rename a tag signature */
+static int icc_rename_tag(
+ icc *p,
+ icTagSignature sig, /* Existing Tag signature - may be unknown */
+ icTagSignature sigNew /* New Tag signature - may be unknown */
+) {
+ unsigned int k;
+ int i, j, ok = 1;
+
+ /* Search for signature */
+ for (k = 0; k < p->count; k++) {
+ if (p->data[k].sig == sig) /* Found it */
+ break;
+ }
+ if (k >= p->count) {
+ sprintf(p->err,"icc_rename_tag: Tag '%s' not found",string_TagSignature(sig));
+ return p->errc = 2;
+ }
+
+ /* Check that a known new signature has an acceptable type */
+ for (i = 0; sigtypetable[i].sig != icMaxEnumType; i++) {
+ if (sigtypetable[i].sig == sigNew) { /* recognized signature */
+ ok = 0;
+ for (j = 0; sigtypetable[i].ttypes[j] != icMaxEnumType; j++) {
+ if (sigtypetable[i].ttypes[j] == p->data[k].ttype) /* recognized type */
+ ok = 1;
+ }
+ break;
+ }
+ }
+
+ if (!ok) {
+ sprintf(p->err,"icc_rename_tag: wrong signature for tag type");
+ p->errc = 1;
+ return p->errc;
+ }
+
+ /* change its signature */
+ p->data[k].sig = sigNew;
+
+ return 0;
+}
+
+/* Unread a specific tag, and free the underlying tag type data */
+/* if this was the last reference to it. */
+/* (This is an internal function) */
+/* Returns non-zero on error: */
+/* tag not found - icc->errc will contain 2 */
+/* tag not read - icc->errc will contain 2 */
+static int icc_unread_tag_ix(
+ icc *p,
+ unsigned int i /* Index from 0.. p->count-1 */
+) {
+ if (i >= p->count) {
+ sprintf(p->err,"icc_unread_tag_ix: index %d is out of range",i);
+ return p->errc = 2;
+ }
+
+ /* See if it's been read */
+ if (p->data[i].objp == NULL) {
+ sprintf(p->err,"icc_unread_tag: Tag '%s' not currently loaded",string_TagSignature(p->data[i].sig));
+ return p->errc = 2;
+ }
+
+ if (--(p->data[i].objp->refcount) == 0) /* decrement reference count */
+ (p->data[i].objp->del)(p->data[i].objp); /* Last reference */
+ p->data[i].objp = NULL;
+
+ return 0;
+}
+
+/* Unread the tag, and free the underlying tag type */
+/* if this was the last reference to it. */
+/* Returns non-zero on error: */
+/* tag not found - icc->errc will contain 2 */
+/* tag not read - icc->errc will contain 2 */
+/* NOTE: we don't handle tag duplication - you'll always get the first in the file */
+static int icc_unread_tag(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i >= p->count) {
+ sprintf(p->err,"icc_unread_tag: Tag '%s' not found",string_TagSignature(sig));
+ return p->errc = 2;
+ }
+
+ return icc_unread_tag(p, i);
+}
+
+/* Delete the tag, and free the underlying tag type, */
+/* if this was the last reference to it. */
+/* Note this finds the first tag with a matching signature */
+/* Returns non-zero on error: */
+/* tag not found - icc->errc will contain 2 */
+static int icc_delete_tag_ix(
+ icc *p,
+ unsigned int i /* Index from 0.. p->count-1 */
+) {
+ if (i >= p->count) {
+ sprintf(p->err,"icc_delete_tag_ix: index %d of range",i);
+ return p->errc = 2;
+ }
+
+ /* If it's been read into memory, decrement the reference count */
+ if (p->data[i].objp != NULL) {
+ if (--(p->data[i].objp->refcount) == 0) /* decrement reference count */
+ (p->data[i].objp->del)(p->data[i].objp); /* Last reference */
+ p->data[i].objp = NULL;
+ }
+
+ /* Now remove it from the tag list */
+ for (; i < (p->count-1); i++)
+ p->data[i] = p->data[i+1]; /* Copy the structure down */
+
+ p->count--; /* One less tag in list */
+
+ return 0;
+}
+
+/* Delete the tag, and free the underlying tag type, */
+/* if this was the last reference to it. */
+/* Note this finds the first tag with a matching signature. */
+/* Returns non-zero on error: */
+/* tag not found - icc->errc will contain 2 */
+static int icc_delete_tag(
+ icc *p,
+ icTagSignature sig /* Tag signature - may be unknown */
+) {
+ unsigned int i;
+
+ /* Search for signature */
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].sig == sig) /* Found it */
+ break;
+ }
+ if (i >= p->count) {
+ sprintf(p->err,"icc_delete_tag: Tag '%s' not found",string_TagSignature(sig));
+ return p->errc = 2;
+ }
+
+ return icc_delete_tag_ix(p, i);
+}
+
+/* Read all the tags into memory, including unknown types. */
+/* Returns non-zero on error. */
+static int icc_read_all_tags(
+ icc *p
+) {
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++) { /* For all the tag element data */
+ if (icc_read_tag_ix(p, i, 1) == NULL)
+ return p->errc;
+ }
+ return 0;
+}
+
+
+static void icc_dump(
+ icc *p,
+ icmFile *op, /* Output to dump to */
+ int verb /* Verbosity level */
+) {
+ unsigned int i;
+ if (verb <= 0)
+ return;
+
+ op->gprintf(op,"icc:\n");
+
+ /* Dump the header */
+ if (p->header != NULL)
+ p->header->dump(p->header,op,verb);
+
+ /* Dump all the tag elements */
+ for (i = 0; i < p->count; i++) { /* For all the tag element data */
+ icmBase *ob;
+ int tr;
+ op->gprintf(op,"tag %d:\n",i);
+ op->gprintf(op," sig %s\n",tag2str(p->data[i].sig));
+ op->gprintf(op," type %s\n",tag2str(p->data[i].ttype));
+ op->gprintf(op," offset %d\n", p->data[i].offset);
+ op->gprintf(op," size %d\n", p->data[i].size);
+ tr = 0;
+ if (p->data[i].objp == NULL) {
+ /* The object is not loaded, so load it then free it */
+ if (icc_read_tag_ix(p, i, 1) == NULL)
+ op->gprintf(op,"Unable to read: %d, %s\n",p->errc,p->err);
+ tr = 1;
+ }
+ if ((ob = p->data[i].objp) != NULL) {
+ /* op->gprintf(op," refcount %d\n", ob->refcount); */
+ ob->dump(ob,op,verb-1);
+
+ if (tr != 0) { /* Cleanup if temporary */
+ icc_unread_tag_ix(p, i);
+ }
+ }
+ op->gprintf(op,"\n");
+ }
+}
+
+static void icc_delete(
+ icc *p
+) {
+ unsigned int i;
+ icmAlloc *al = p->al;
+ int del_al = p->del_al;
+
+ /* Free up the header */
+ if (p->header != NULL)
+ (p->header->del)(p->header);
+
+ /* Free up the tag data objects */
+ if (p->data != NULL) {
+ for (i = 0; i < p->count; i++) {
+ if (p->data[i].objp != NULL) {
+ if (--(p->data[i].objp->refcount) == 0) /* decrement reference count */
+ (p->data[i].objp->del)(p->data[i].objp); /* Last reference */
+ p->data[i].objp = NULL;
+ }
+ }
+ /* Free tag table */
+ al->free(al, p->data);
+ }
+
+ /* We are responsible for deleting the file object */
+ if (p->del_fp && p->fp != NULL)
+ p->fp->del(p->fp);
+
+ /* This object */
+ al->free(al, p);
+
+ if (del_al) /* We are responsible for deleting allocator */
+ al->del(al);
+}
+
+/* ================================================== */
+/* Lut Color normalizing and de-normalizing functions */
+
+/* As a rule, I am representing Lut in memory as values in machine form as real */
+/* numbers in the range 0.0 - 1.0. For many color spaces (ie. RGB, Gray, */
+/* hsv, hls, cmyk and other device coords), this is entirely appropriate. */
+/* For CIE based spaces though, this is not correct, since (I assume!) the binary */
+/* representation will be consistent with the encoding in Annex A, page 74 */
+/* of the standard. Note that the standard doesn't specify the encoding of */
+/* many color spaces (ie. Yuv, Yxy etc.), and is unclear about PCS. */
+
+/* The following functions convert to and from the CIE base spaces */
+/* and the real Lut input/output values. These are used to convert real color */
+/* space values into/out of the raw lut 0.0-1.0 representation (which subsequently */
+/* get converted to ICC integer values in the obvious way as a mapping to 0 .. 2^n-1). */
+
+/* This is used internally to support the Lut->lookup() function, */
+/* and can also be used by someone writing a Lut based profile to determine */
+/* the colorspace range that the input lut indexes cover, as well */
+/* as processing the output luts values into normalized form ready */
+/* for writing. */
+
+/* These functions should be accessed by calling icc.getNormFuncs() */
+
+/* - - - - - - - - - - - - - - - - */
+/* According to 6.5.5 and 6.5.6 of the spec., */
+/* XYZ index values are represented the same as their table */
+/* values, ie. as a u1.15 representation, with a value */
+/* range from 0.0 -> 1.999969482422 */
+
+/* Convert Lut index/value to XYZ coord. */
+static void Lut_Lut2XYZ(double *out, double *in) {
+ out[0] = in[0] * (1.0 + 32767.0/32768); /* X */
+ out[1] = in[1] * (1.0 + 32767.0/32768); /* Y */
+ out[2] = in[2] * (1.0 + 32767.0/32768); /* Z */
+}
+
+/* Convert XYZ coord to Lut index/value. */
+static void Lut_XYZ2Lut(double *out, double *in) {
+ out[0] = in[0] * (1.0/(1.0 + 32767.0/32768));
+ out[1] = in[1] * (1.0/(1.0 + 32767.0/32768));
+ out[2] = in[2] * (1.0/(1.0 + 32767.0/32768));
+}
+
+/* Convert Lut index/value to Y coord. */
+static void Lut_Lut2Y(double *out, double *in) {
+ out[0] = in[0] * (1.0 + 32767.0/32768); /* Y */
+}
+
+/* Convert Y coord to Lut index/value. */
+static void Lut_Y2Lut(double *out, double *in) {
+ out[0] = in[0] * (1.0/(1.0 + 32767.0/32768));
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Convert 8 bit Lab to Lut numbers */
+/* Annex A specifies 8 and 16 bit encoding, but is */
+/* silent on the Lut index normalization. */
+/* Following Michael Bourgoin's 1998 SIGGRAPH course comment on this, */
+/* we assume here that the index encoding is the same as the */
+/* value encoding. */
+
+/* Convert Lut8 table index/value to Lab */
+static void Lut_Lut2Lab_8(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+ out[1] = (in[1] * 255.0) - 128.0; /* a */
+ out[2] = (in[2] * 255.0) - 128.0; /* b */
+}
+
+/* Convert Lab to Lut8 table index/value */
+static void Lut_Lab2Lut_8(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+ out[1] = (in[1] + 128.0) * 1.0/255.0; /* a */
+ out[2] = (in[2] + 128.0) * 1.0/255.0; /* b */
+}
+
+/* Convert Lut8 table index/value to L */
+static void Lut_Lut2L_8(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+}
+
+/* Convert L to Lut8 table index/value */
+static void Lut_L2Lut_8(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Convert 16 bit Lab to Lut numbers, V2 */
+
+/* Convert Lut16 table index/value to Lab */
+static void Lut_Lut2LabV2_16(double *out, double *in) {
+ out[0] = in[0] * (100.0 * 65535.0)/65280.0; /* L */
+ out[1] = (in[1] * (255.0 * 65535.0)/65280) - 128.0; /* a */
+ out[2] = (in[2] * (255.0 * 65535.0)/65280) - 128.0; /* b */
+}
+
+/* Convert Lab to Lut16 table index/value */
+static void Lut_Lab2LutV2_16(double *out, double *in) {
+ out[0] = in[0] * 65280.0/(100.0 * 65535.0); /* L */
+ out[1] = (in[1] + 128.0) * 65280.0/(255.0 * 65535.0); /* a */
+ out[2] = (in[2] + 128.0) * 65280.0/(255.0 * 65535.0); /* b */
+}
+
+/* Convert Lut16 table index/value to L */
+static void Lut_Lut2LV2_16(double *out, double *in) {
+ out[0] = in[0] * (100.0 * 65535.0)/65280.0; /* L */
+}
+
+/* Convert Lab to Lut16 table index/value */
+static void Lut_L2LutV2_16(double *out, double *in) {
+ out[0] = in[0] * 65280.0/(100.0 * 65535.0); /* L */
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Convert 16 bit Lab to Lut numbers, V4 */
+
+/* Convert Lut16 table index/value to Lab */
+static void Lut_Lut2LabV4_16(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+ out[1] = (in[1] * 255.0) - 128.0; /* a */
+ out[2] = (in[2] * 255.0) - 128.0; /* b */
+}
+
+/* Convert Lab to Lut16 table index/value */
+static void Lut_Lab2LutV4_16(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+ out[1] = (in[1] + 128.0) * 1.0/255.0; /* a */
+ out[2] = (in[2] + 128.0) * 1.0/255.0; /* b */
+}
+
+/* Convert Lut16 table index/value to L */
+static void Lut_Lut2LV4_16(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+}
+
+/* Convert L to Lut16 table index/value */
+static void Lut_L2LutV4_16(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Convert Luv to Lut number */
+/* This data normalization is taken from Apples */
+/* Colorsync specification. */
+/* As per other color spaces, we assume that the index */
+/* normalization is the same as the data normalization. */
+
+/* Convert Lut table index/value to Luv */
+static void Lut_Lut2Luv(double *out, double *in) {
+ out[0] = in[0] * 100.0; /* L */
+ out[1] = (in[1] * 65535.0/256.0) - 128.0; /* u */
+ out[2] = (in[2] * 65535.0/256.0) - 128.0; /* v */
+}
+
+/* Convert Luv to Lut table index/value */
+static void Lut_Luv2Lut(double *out, double *in) {
+ out[0] = in[0] * 1.0/100.0; /* L */
+ out[1] = (in[1] + 128.0) * 256.0/65535.0; /* u */
+ out[2] = (in[2] + 128.0) * 256.0/65535.0; /* v */
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Default N component conversions */
+static void Lut_N(double *out, double *in, int nc) {
+ for (--nc; nc >= 0; nc--)
+ out[nc] = in[nc];
+}
+
+/* 1 */
+static void Lut_1(double *out, double *in) {
+ out[0] = in[0];
+}
+
+/* 2 */
+static void Lut_2(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+}
+
+/* 3 */
+static void Lut_3(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+/* 4 */
+static void Lut_4(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+}
+
+/* 5 */
+static void Lut_5(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+ out[4] = in[4];
+}
+
+/* 6 */
+static void Lut_6(double *out, double *in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+ out[4] = in[4];
+ out[5] = in[5];
+}
+
+/* 7 */
+static void Lut_7(double *out, double *in) {
+ Lut_N(out, in, 7);
+}
+
+/* 8 */
+static void Lut_8(double *out, double *in) {
+ Lut_N(out, in, 8);
+}
+
+/* 9 */
+static void Lut_9(double *out, double *in) {
+ Lut_N(out, in, 9);
+}
+
+/* 10 */
+static void Lut_10(double *out, double *in) {
+ Lut_N(out, in, 10);
+}
+
+/* 11 */
+static void Lut_11(double *out, double *in) {
+ Lut_N(out, in, 11);
+}
+
+/* 12 */
+static void Lut_12(double *out, double *in) {
+ Lut_N(out, in, 12);
+}
+
+/* 13 */
+static void Lut_13(double *out, double *in) {
+ Lut_N(out, in, 13);
+}
+
+/* 14 */
+static void Lut_14(double *out, double *in) {
+ Lut_N(out, in, 14);
+}
+
+/* 15 */
+static void Lut_15(double *out, double *in) {
+ Lut_N(out, in, 15);
+}
+
+/* Function table - match conversions to color spaces. */
+/* Anything not here, we don't know how to convert. */
+/* (ie. YCbCr) */
+static struct {
+ icColorSpaceSignature csig;
+ void (*fromLut)(double *out, double *in); /* from Lut index/entry */
+ void (*toLut)(double *out, double *in); /* to Lut index/entry */
+} colnormtable[] = {
+ {icSigXYZData, Lut_Lut2XYZ, Lut_XYZ2Lut },
+ {icmSigYData, Lut_Lut2Y, Lut_Y2Lut },
+ {icmSigLab8Data, Lut_Lut2Lab_8, Lut_Lab2Lut_8 },
+ {icmSigLabV2Data, Lut_Lut2LabV2_16, Lut_Lab2LutV2_16 },
+ {icmSigLabV4Data, Lut_Lut2LabV4_16, Lut_Lab2LutV4_16 },
+ {icmSigL8Data, Lut_Lut2L_8, Lut_L2Lut_8 },
+ {icmSigLV2Data, Lut_Lut2LV2_16, Lut_L2LutV2_16 },
+ {icmSigLV4Data, Lut_Lut2LV4_16, Lut_L2LutV4_16 },
+ {icSigLuvData, Lut_Lut2Luv, Lut_Luv2Lut },
+ {icSigYxyData, Lut_3, Lut_3 },
+ {icSigRgbData, Lut_3, Lut_3 },
+ {icSigGrayData, Lut_1, Lut_1 },
+ {icSigHsvData, Lut_3, Lut_3 },
+ {icSigHlsData, Lut_3, Lut_3 },
+ {icSigCmykData, Lut_4, Lut_4 },
+ {icSigCmyData, Lut_3, Lut_3 },
+ {icSigMch6Data, Lut_6, Lut_6 },
+ {icSig2colorData, Lut_2, Lut_2 },
+ {icSig3colorData, Lut_3, Lut_3 },
+ {icSig4colorData, Lut_4, Lut_4 },
+ {icSig5colorData, Lut_5, Lut_5 },
+ {icSig6colorData, Lut_6, Lut_6 },
+ {icSig7colorData, Lut_7, Lut_7 },
+ {icSigMch5Data, Lut_5, Lut_5 },
+ {icSigMch6Data, Lut_6, Lut_6 },
+ {icSigMch7Data, Lut_7, Lut_7 },
+ {icSigMch8Data, Lut_8, Lut_8 },
+ {icSig8colorData, Lut_8, Lut_8 },
+ {icSig9colorData, Lut_9, Lut_9 },
+ {icSig10colorData, Lut_10, Lut_10 },
+ {icSig11colorData, Lut_11, Lut_11 },
+ {icSig12colorData, Lut_12, Lut_12 },
+ {icSig13colorData, Lut_13, Lut_13 },
+ {icSig14colorData, Lut_14, Lut_14 },
+ {icSig15colorData, Lut_15, Lut_15 },
+ {icMaxEnumData, NULL, NULL }
+};
+
+/*
+ Legacy Lab encoding:
+
+ The V4 specificatins are misleading on this, since they assume in this
+ instance that all devices spaces, however labeled, have no defined
+ ICC encoding. The end result is simple enough though:
+
+ ICC V2 Lab encoding should be used in all PCS encodings in
+ a icSigLut16Type or icSigNamedColor2Type tag, and can be used
+ for Lab encoding in device spaces for these tags.
+
+ ICC V4 Lab encoding should be used in all PCS encodings in
+ all other situations, and can be used for Lab encoding in
+ device spaces for all other situtaions.
+
+ [ Since the ICC spec. doesn't cover device spaces labeled as Lab,
+ these are ripe for mis-matches between different implementations.]
+
+ This logic has yet to be fully implemented here.
+*/
+
+/* Find appropriate conversion functions from the normalised */
+/* Lut data range 0.0 - 1.0 to/from a given colorspace value, */
+/* given the color space and Lut type. */
+/* Return 0 on success, 1 on match failure. */
+/* NOTE: doesn't set error value, message etc.! */
+static int getNormFunc(
+ icc *icp,
+// icProfileClassSignature psig, /* Profile signature to use */
+ icColorSpaceSignature csig, /* Colorspace to use. */
+// int lutin, /* 0 if this is for a icSigLut16Type input, nz for output */
+// icTagSignature tagSig, /* Tag signature involved (AtoB or B2A etc.) */
+ icTagTypeSignature tagType, /* icSigLut8Type or icSigLut16Type or V4 lut */
+ icmNormFlag flag, /* icmFromLuti, icmFromLutv or icmToLuti, icmToLutv */
+ void (**nfunc)(double *out, double *in)
+) {
+ int i;
+ if (tagType == icSigLut8Type && csig == icSigLabData) {
+ csig = icmSigLab8Data;
+ }
+ if (csig == icSigLabData) {
+ if (tagType == icSigLut16Type) /* Lut16 retains legacy encoding */
+ csig = icmSigLabV2Data;
+ else { /* Other tag types use version specific encoding */
+ if (icp->ver)
+ csig = icmSigLabV4Data;
+ else
+ csig = icmSigLabV2Data;
+ }
+ }
+
+ for (i = 0; colnormtable[i].csig != icMaxEnumData; i++) {
+ if (colnormtable[i].csig == csig)
+ break; /* Found it */
+ }
+ if (colnormtable[i].csig == icMaxEnumData) { /* Oops */
+ *nfunc = NULL;
+ return 1;
+ }
+
+ if (flag == icmFromLuti || flag == icmFromLutv) { /* Table index/value decoding functions */
+ *nfunc = colnormtable[i].fromLut;
+ return 0;
+ } else if (flag == icmToLuti || flag == icmToLutv) { /* Table index/value encoding functions */
+ *nfunc = colnormtable[i].toLut;
+ return 0;
+ }
+ *nfunc = NULL;
+ return 1;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Colorspace ranges - used instead of norm/denorm by Mono, Matrix and */
+/* override PCS */
+
+/* Function table - match ranges to color spaces. */
+/* Anything not here, we don't know how to convert. */
+/* (ie. YCbCr) */
+/* Hmm. we're not handling Lab8 properly ?? ~~~8888 */
+static struct {
+ icColorSpaceSignature csig;
+ int same; /* Non zero if first entry applies to all channels */
+ double min[3]; /* Minimum value for this colorspace */
+ double max[3]; /* Maximum value for this colorspace */
+} colorrangetable[] = {
+ {icSigXYZData, 1, { 0.0 } , { 1.0 + 32767.0/32768.0 } },
+ {icmSigLab8Data, 0, { 0.0, -128.0, -128.0 }, { 100.0, 127.0, 127.0 } },
+ {icmSigLabV2Data, 0, { 0.0, -128.0, -128.0 },
+ { 100.0 + 25500.0/65280.0, 127.0 + 255.0/256.0, 127.0 + 255.0/256.0 } },
+ {icmSigLabV4Data, 0, { 0.0, -128.0, -128.0 }, { 100.0, 127.0, 127.0 } },
+ {icmSigYData, 1, { 0.0 }, { 1.0 + 32767.0/32768.0 } },
+ {icmSigL8Data, 1, { 0.0 }, { 100.0 } },
+ {icmSigLV2Data, 1, { 0.0 }, { 100.0 + 25500.0/65280.0 } },
+ {icmSigLV4Data, 1, { 0.0 }, { 100.0 } },
+ {icSigLuvData, 0, { 0.0, -128.0, -128.0 },
+ { 100.0, 127.0 + 255.0/256.0, 127.0 + 255.0/256.0 } },
+ {icSigYCbCrData, 1, { 0.0 }, { 1.0 } }, /* ??? */
+ {icSigYxyData, 1, { 0.0 }, { 1.0 } }, /* ??? */
+ {icSigRgbData, 1, { 0.0 }, { 1.0 } },
+ {icSigGrayData, 1, { 0.0 }, { 1.0 } },
+ {icSigHsvData, 1, { 0.0 }, { 1.0 } },
+ {icSigHlsData, 1, { 0.0 }, { 1.0 } },
+ {icSigCmykData, 1, { 0.0 }, { 1.0 } },
+ {icSigCmyData, 1, { 0.0 }, { 1.0 } },
+ {icSigMch6Data, 1, { 0.0 }, { 1.0 } },
+ {icSig2colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig3colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig4colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig5colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig6colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig7colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig8colorData, 1, { 0.0 }, { 1.0 } },
+ {icSigMch5Data, 1, { 0.0 }, { 1.0 } },
+ {icSigMch6Data, 1, { 0.0 }, { 1.0 } },
+ {icSigMch7Data, 1, { 0.0 }, { 1.0 } },
+ {icSigMch8Data, 1, { 0.0 }, { 1.0 } },
+ {icSig9colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig10colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig11colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig12colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig13colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig14colorData, 1, { 0.0 }, { 1.0 } },
+ {icSig15colorData, 1, { 0.0 }, { 1.0 } },
+ {icMaxEnumData, 1, { 0.0 }, { 1.0 } }
+};
+
+/* Find appropriate typical encoding ranges for a */
+/* colorspace given the color space. */
+/* Return 0 on success, 1 on match failure */
+static int getRange(
+ icc *icp,
+// icProfileClassSignature psig, /* Profile signature to use */
+ icColorSpaceSignature csig, /* Colorspace to use. */
+// int lutin, /* 0 if this is for a icSigLut16Type input, nz for output */
+// icTagSignature tagSig, /* Tag signature involved (AtoB or B2A etc.) */
+ icTagTypeSignature tagType, /* icSigLut8Type or icSigLut16Type or V4 lut */
+ double *min, double *max /* Return range values */
+) {
+ int i, e, ee;
+
+ if (tagType == icSigLut8Type && csig == icSigLabData) {
+ csig = icmSigLab8Data;
+ }
+ if (csig == icSigLabData) {
+ if (tagType == icSigLut16Type) /* Lut16 retains legacy encoding */
+ csig = icmSigLabV2Data;
+ else { /* Other tag types use version specific encoding */
+ if (icp->ver)
+ csig = icmSigLabV4Data;
+ else
+ csig = icmSigLabV2Data;
+ }
+ }
+
+ for (i = 0; colorrangetable[i].csig != icMaxEnumData; i++) {
+ if (colorrangetable[i].csig == csig)
+ break; /* Found it */
+ }
+ if (colorrangetable[i].csig == icMaxEnumData) { /* Oops */
+ return 1;
+ }
+ ee = number_ColorSpaceSignature(csig); /* Get number of components */
+
+ if (colorrangetable[i].same) { /* All channels are the same */
+ for (e = 0; e < ee; e++) {
+ if (min != NULL)
+ min[e] = colorrangetable[i].min[0];
+ if (max != NULL)
+ max[e] = colorrangetable[i].max[0];
+ }
+ } else {
+ for (e = 0; e < ee; e++) {
+ if (min != NULL)
+ min[e] = colorrangetable[i].min[e];
+ if (max != NULL)
+ max[e] = colorrangetable[i].max[e];
+ }
+ }
+ return 0;
+}
+
+/* ============================================= */
+/* Misc. support functions. */
+
+/* Clamp a 3 vector to be +ve */
+void icmClamp3(double out[3], double in[3]) {
+ int i;
+ for (i = 0; i < 3; i++)
+ out[i] = in[i] < 0.0 ? 0.0 : in[i];
+}
+
+/* Add two 3 vectors */
+void icmAdd3(double out[3], double in1[3], double in2[3]) {
+ out[0] = in1[0] + in2[0];
+ out[1] = in1[1] + in2[1];
+ out[2] = in1[2] + in2[2];
+}
+
+/* Subtract two 3 vectors, out = in1 - in2 */
+void icmSub3(double out[3], double in1[3], double in2[3]) {
+ out[0] = in1[0] - in2[0];
+ out[1] = in1[1] - in2[1];
+ out[2] = in1[2] - in2[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Set a 3x3 matrix to unity */
+void icmSetUnity3x3(double mat[3][3]) {
+ int i, j;
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++) {
+ if (i == j)
+ mat[j][i] = 1.0;
+ else
+ mat[j][i] = 0.0;
+ }
+ }
+
+}
+
+/* Copy a 3x3 transform matrix */
+void icmCpy3x3(double dst[3][3], double src[3][3]) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++)
+ dst[j][i] = src[j][i];
+ }
+}
+
+/* Scale each element of a 3x3 transform matrix */
+void icmScale3x3(double dst[3][3], double src[3][3], double scale) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++)
+ dst[j][i] = src[j][i] * scale;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/*
+
+ mat in out
+
+[ ] [] []
+[ ] * [] => []
+[ ] [] []
+
+ */
+
+/* Multiply 3 array by 3x3 transform matrix */
+void icmMulBy3x3(double out[3], double mat[3][3], double in[3]) {
+ double tt[3];
+
+ tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1] + mat[0][2] * in[2];
+ tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1] + mat[1][2] * in[2];
+ tt[2] = mat[2][0] * in[0] + mat[2][1] * in[1] + mat[2][2] * in[2];
+
+ out[0] = tt[0];
+ out[1] = tt[1];
+ out[2] = tt[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Add one 3x3 to another */
+/* dst = src1 + src2 */
+void icmAdd3x3(double dst[3][3], double src1[3][3], double src2[3][3]) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++)
+ dst[j][i] = src1[j][i] + src2[j][i];
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Tensor product. Multiply two 3 vectors to form a 3x3 matrix */
+/* src1[] forms the colums, and src2[] forms the rows in the result */
+void icmTensMul3(double dst[3][3], double src1[3], double src2[3]) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++)
+ dst[j][i] = src1[j] * src2[i];
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Multiply one 3x3 with another */
+/* dst = src * dst */
+void icmMul3x3(double dst[3][3], double src[3][3]) {
+ int i, j, k;
+ double td[3][3]; /* Temporary dest */
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++) {
+ double tt = 0.0;
+ for (k = 0; k < 3; k++)
+ tt += src[j][k] * dst[k][i];
+ td[j][i] = tt;
+ }
+ }
+
+ /* Copy result out */
+ for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ dst[j][i] = td[j][i];
+}
+
+/* Multiply one 3x3 with another #2 */
+/* dst = src1 * src2 */
+void icmMul3x3_2(double dst[3][3], double src1[3][3], double src2[3][3]) {
+ int i, j, k;
+ double td[3][3]; /* Temporary dest */
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 3; i++) {
+ double tt = 0.0;
+ for (k = 0; k < 3; k++)
+ tt += src1[j][k] * src2[k][i];
+ td[j][i] = tt;
+ }
+ }
+
+ /* Copy result out */
+ for (j = 0; j < 3; j++)
+ for (i = 0; i < 3; i++)
+ dst[j][i] = td[j][i];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ Matrix Inversion
+ by Richard Carling
+ from "Graphics Gems", Academic Press, 1990
+*/
+
+/*
+ * adjoint( original_matrix, inverse_matrix )
+ *
+ * calculate the adjoint of a 3x3 matrix
+ *
+ * Let a denote the minor determinant of matrix A obtained by
+ * ij
+ *
+ * deleting the ith row and jth column from A.
+ *
+ * i+j
+ * Let b = (-1) a
+ * ij ji
+ *
+ * The matrix B = (b ) is the adjoint of A
+ * ij
+ */
+
+#define det2x2(a, b, c, d) (a * d - b * c)
+
+static void adjoint(
+double out[3][3],
+double in[3][3]
+) {
+ double a1, a2, a3, b1, b2, b3, c1, c2, c3;
+
+ /* assign to individual variable names to aid */
+ /* selecting correct values */
+
+ a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2];
+ a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2];
+ a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2];
+
+ /* row column labeling reversed since we transpose rows & columns */
+
+ out[0][0] = det2x2(b2, b3, c2, c3);
+ out[1][0] = - det2x2(a2, a3, c2, c3);
+ out[2][0] = det2x2(a2, a3, b2, b3);
+
+ out[0][1] = - det2x2(b1, b3, c1, c3);
+ out[1][1] = det2x2(a1, a3, c1, c3);
+ out[2][1] = - det2x2(a1, a3, b1, b3);
+
+ out[0][2] = det2x2(b1, b2, c1, c2);
+ out[1][2] = - det2x2(a1, a2, c1, c2);
+ out[2][2] = det2x2(a1, a2, b1, b2);
+}
+
+/*
+ * double = icmDet3x3( a1, a2, a3, b1, b2, b3, c1, c2, c3 )
+ *
+ * calculate the determinant of a 3x3 matrix
+ * in the form
+ *
+ * | a1, b1, c1 |
+ * | a2, b2, c2 |
+ * | a3, b3, c3 |
+ */
+
+double icmDet3x3(double in[3][3]) {
+ double a1, a2, a3, b1, b2, b3, c1, c2, c3;
+ double ans;
+
+ a1 = in[0][0]; b1 = in[0][1]; c1 = in[0][2];
+ a2 = in[1][0]; b2 = in[1][1]; c2 = in[1][2];
+ a3 = in[2][0]; b3 = in[2][1]; c3 = in[2][2];
+
+ ans = a1 * det2x2(b2, b3, c2, c3)
+ - b1 * det2x2(a2, a3, c2, c3)
+ + c1 * det2x2(a2, a3, b2, b3);
+ return ans;
+}
+
+#define ICM_SMALL_NUMBER 1.e-8
+/*
+ * inverse( original_matrix, inverse_matrix )
+ *
+ * calculate the inverse of a 4x4 matrix
+ *
+ * -1
+ * A = ___1__ adjoint A
+ * det A
+ */
+
+/* Return non-zero if not invertable */
+int icmInverse3x3(
+double out[3][3],
+double in[3][3]
+) {
+ int i, j;
+ double det;
+
+ /* calculate the 3x3 determinant
+ * if the determinant is zero,
+ * then the inverse matrix is not unique.
+ */
+ det = icmDet3x3(in);
+
+ if ( fabs(det) < ICM_SMALL_NUMBER)
+ return 1;
+
+ /* calculate the adjoint matrix */
+ adjoint(out, in);
+
+ /* scale the adjoint matrix to get the inverse */
+ for (i = 0; i < 3; i++)
+ for(j = 0; j < 3; j++)
+ out[i][j] /= det;
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Transpose a 3x3 matrix */
+void icmTranspose3x3(double out[3][3], double in[3][3]) {
+ int i, j;
+ if (out != in) {
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ out[i][j] = in[j][i];
+ } else {
+ double tt[3][3];
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ tt[i][j] = in[j][i];
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ out[i][j] = tt[i][j];
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the dot product of two 3 vectors */
+double icmDot3(double in1[3], double in2[3]) {
+ return in1[0] * in2[0] + in1[1] * in2[1] + in1[2] * in2[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the cross product of two 3D vectors, out = in1 x in2 */
+void icmCross3(double out[3], double in1[3], double in2[3]) {
+ double tt[3];
+ tt[0] = (in1[1] * in2[2]) - (in1[2] * in2[1]);
+ tt[1] = (in1[2] * in2[0]) - (in1[0] * in2[2]);
+ tt[2] = (in1[0] * in2[1]) - (in1[1] * in2[0]);
+ out[0] = tt[0];
+ out[1] = tt[1];
+ out[2] = tt[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the norm (length) squared of a 3 vector */
+double icmNorm3sq(double in[3]) {
+ return in[0] * in[0] + in[1] * in[1] + in[2] * in[2];
+}
+
+/* Compute the norm (length) of a 3 vector */
+double icmNorm3(double in[3]) {
+ return sqrt(in[0] * in[0] + in[1] * in[1] + in[2] * in[2]);
+}
+
+/* Scale a 3 vector by the given ratio */
+void icmScale3(double out[3], double in[3], double rat) {
+ out[0] = in[0] * rat;
+ out[1] = in[1] * rat;
+ out[2] = in[2] * rat;
+}
+
+/* Compute a blend between in0 and in1 */
+void icmBlend3(double out[3], double in0[3], double in1[3], double bf) {
+ out[0] = (1.0 - bf) * in0[0] + bf * in1[0];
+ out[1] = (1.0 - bf) * in0[1] + bf * in1[1];
+ out[2] = (1.0 - bf) * in0[2] + bf * in1[2];
+}
+
+/* Normalise a 3 vector to the given length. Return nz if not normalisable */
+int icmNormalize3(double out[3], double in[3], double len) {
+ double tt = sqrt(in[0] * in[0] + in[1] * in[1] + in[2] * in[2]);
+
+ if (tt < ICM_SMALL_NUMBER)
+ return 1;
+ tt = len/tt;
+ out[0] = in[0] * tt;
+ out[1] = in[1] * tt;
+ out[2] = in[2] * tt;
+ return 0;
+}
+
+/* Compute the norm (length) squared of a vector define by two points */
+double icmNorm33sq(double in1[3], double in0[3]) {
+ int j;
+ double rv;
+ for (rv = 0.0, j = 0; j < 3; j++) {
+ double tt = in1[j] - in0[j];
+ rv += tt * tt;
+ }
+ return rv;
+}
+
+/* Compute the norm (length) of a vector define by two points */
+double icmNorm33(double in1[3], double in0[3]) {
+ int j;
+ double rv;
+ for (rv = 0.0, j = 0; j < 3; j++) {
+ double tt = in1[j] - in0[j];
+ rv += tt * tt;
+ }
+ return sqrt(rv);
+}
+
+/* Scale a two point vector by the given ratio. in0[] is the origin */
+void icmScale33(double out[3], double in1[3], double in0[3], double rat) {
+ out[0] = in0[0] + (in1[0] - in0[0]) * rat;
+ out[1] = in0[1] + (in1[1] - in0[1]) * rat;
+ out[2] = in0[2] + (in1[2] - in0[2]) * rat;
+}
+
+/* Normalise a two point vector to the given length. */
+/* The new location of in1[] is returned in out[]. */
+/* Return nz if not normalisable */
+int icmNormalize33(double out[3], double in1[3], double in0[3], double len) {
+ int j;
+ double vl;
+
+ for (vl = 0.0, j = 0; j < 3; j++) {
+ double tt = in1[j] - in0[j];
+ vl += tt * tt;
+ }
+ vl = sqrt(vl);
+ if (vl < ICM_SMALL_NUMBER)
+ return 1;
+
+ vl = len/vl;
+ for (j = 0; j < 3; j++) {
+ out[j] = in0[j] + (in1[j] - in0[j]) * vl;
+ }
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Given two 3D points, create a matrix that rotates */
+/* and scales one onto the other about the origin 0,0,0. */
+/* The maths is from page 52 of Tomas Moller and Eric Haines "Real-Time Rendering". */
+/* s is source vector, t is target vector. */
+/* Usage of icmRotMat: */
+/* t[0] == mat[0][0] * s[0] + mat[0][1] * s[1] + mat[0][2] * s[2]; */
+/* t[1] == mat[1][0] * s[0] + mat[1][1] * s[1] + mat[1][2] * s[2]; */
+/* t[2] == mat[2][0] * s[0] + mat[2][1] * s[1] + mat[2][2] * s[2]; */
+/* i.e. use icmMulBy3x3 */
+void icmRotMat(double m[3][3], double s[3], double t[3]) {
+ double sl, tl, sn[3], tn[3];
+ double v[3]; /* Cross product */
+ double e; /* Dot product */
+ double h; /* 1-e/Cross product dot squared */
+
+ /* Normalise input vectors */
+ /* The rotation will be about 0,0,0 */
+ sl = sqrt(s[0] * s[0] + s[1] * s[1] + s[2] * s[2]);
+ tl = sqrt(t[0] * t[0] + t[1] * t[1] + t[2] * t[2]);
+
+ if (sl < 1e-12 || tl < 1e-12) { /* Hmm. Do nothing */
+ m[0][0] = 1.0;
+ m[0][1] = 0.0;
+ m[0][2] = 0.0;
+ m[1][0] = 0.0;
+ m[1][1] = 1.0;
+ m[1][2] = 0.0;
+ m[2][0] = 0.0;
+ m[2][1] = 0.0;
+ m[2][2] = 1.0;
+ return;
+ }
+
+ sn[0] = s[0]/sl; sn[1] = s[1]/sl; sn[2] = s[2]/sl;
+ tn[0] = t[0]/tl; tn[1] = t[1]/tl; tn[2] = t[2]/tl;
+
+ /* Compute the cross product */
+ v[0] = (sn[1] * tn[2]) - (sn[2] * tn[1]);
+ v[1] = (sn[2] * tn[0]) - (sn[0] * tn[2]);
+ v[2] = (sn[0] * tn[1]) - (sn[1] * tn[0]);
+
+ /* Compute the dot product */
+ e = sn[0] * tn[0] + sn[1] * tn[1] + sn[2] * tn[2];
+
+ /* Cross product dot squared */
+ h = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
+
+ /* If the two input vectors are close to being parallel, */
+ /* then h will be close to zero. */
+ if (fabs(h) < 1e-12) {
+ m[0][0] = tl/sl;
+ m[0][1] = 0.0;
+ m[0][2] = 0.0;
+ m[1][0] = 0.0;
+ m[1][1] = tl/sl;
+ m[1][2] = 0.0;
+ m[2][0] = 0.0;
+ m[2][1] = 0.0;
+ m[2][2] = tl/sl;
+ } else {
+ /* 1-e/Cross product dot squared */
+ h = (1.0 - e) / h;
+
+ m[0][0] = tl/sl * (e + h * v[0] * v[0]);
+ m[0][1] = tl/sl * (h * v[0] * v[1] - v[2]);
+ m[0][2] = tl/sl * (h * v[0] * v[2] + v[1]);
+ m[1][0] = tl/sl * (h * v[0] * v[1] + v[2]);
+ m[1][1] = tl/sl * (e + h * v[1] * v[1]);
+ m[1][2] = tl/sl * (h * v[1] * v[2] - v[0]);
+ m[2][0] = tl/sl * (h * v[0] * v[2] - v[1]);
+ m[2][1] = tl/sl * (h * v[1] * v[2] + v[0]);
+ m[2][2] = tl/sl * (e + h * v[2] * v[2]);
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Multiply 2 array by 2x2 transform matrix */
+void icmMulBy2x2(double out[2], double mat[2][2], double in[2]) {
+ double tt[2];
+
+ tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1];
+ tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1];
+
+ out[0] = tt[0];
+ out[1] = tt[1];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Copy a 3x4 transform matrix */
+void icmCpy3x4(double dst[3][4], double src[3][4]) {
+ int i, j;
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 4; i++)
+ dst[j][i] = src[j][i];
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Multiply 3 array by 3x4 transform matrix */
+void icmMul3By3x4(double out[3], double mat[3][4], double in[3]) {
+ double tt[3];
+
+ tt[0] = mat[0][0] * in[0] + mat[0][1] * in[1] + mat[0][2] * in[2] + mat[0][3];
+ tt[1] = mat[1][0] * in[0] + mat[1][1] * in[1] + mat[1][2] * in[2] + mat[1][3];
+ tt[2] = mat[2][0] * in[0] + mat[2][1] * in[1] + mat[2][2] * in[2] + mat[2][3];
+
+ out[0] = tt[0];
+ out[1] = tt[1];
+ out[2] = tt[2];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Given two 3D vectors, create a matrix that translates, */
+/* rotates and scales one onto the other. */
+/* The maths is from page 52 of Tomas Moller and Eric Haines */
+/* "Real-Time Rendering". */
+/* s0 -> s1 is source vector, t0 -> t1 is target vector. */
+/* Usage of icmRotMat: */
+/* t[0] == mat[0][0] * s[0] + mat[0][1] * s[1] + mat[0][2] * s[2] + mat[0][3]; */
+/* t[1] == mat[1][0] * s[0] + mat[1][1] * s[1] + mat[1][2] * s[2] + mat[1][3]; */
+/* t[2] == mat[2][0] * s[0] + mat[2][1] * s[1] + mat[2][2] * s[2] + mat[2][3]; */
+/* i.e. use icmMul3By3x4 */
+void icmVecRotMat(double m[3][4], double s1[3], double s0[3], double t1[3], double t0[3]) {
+ int i, j;
+ double ss[3], tt[3], rr[3][3];
+
+ /* Create the rotation matrix: */
+ for (i = 0; i < 3; i++) {
+ ss[i] = s1[i] - s0[i];
+ tt[i] = t1[i] - t0[i];
+ }
+ icmRotMat(rr, ss, tt);
+
+ /* Create rotated version of s0: */
+ icmMulBy3x3(ss, rr, s0);
+
+ /* Create 4x4 matrix */
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < 4; i++) {
+ if (i < 3 && j < 3)
+ m[j][i] = rr[j][i];
+ else if (i == 3 && j < 3)
+ m[j][i] = t0[j] - ss[j];
+ else if (i == j)
+ m[j][i] = 1.0;
+ else
+ m[j][i] = 0.0;
+ }
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the 3D intersection of a vector and a plane */
+/* return nz if there is no intersection */
+int icmVecPlaneIsect(
+double isect[3], /* return intersection point */
+double pl_const, /* Plane equation constant */
+double pl_norm[3], /* Plane normal vector */
+double ve_1[3], /* Point on line */
+double ve_0[3] /* Second point on line */
+) {
+ double den; /* denominator */
+ double rv; /* Vector parameter value */
+ double vvec[3]; /* Vector vector */
+ double ival[3]; /* Intersection value */
+
+ /* Compute vector between the two points */
+ vvec[0] = ve_1[0] - ve_0[0];
+ vvec[1] = ve_1[1] - ve_0[1];
+ vvec[2] = ve_1[2] - ve_0[2];
+
+ /* Compute the denominator */
+ den = pl_norm[0] * vvec[0] + pl_norm[1] * vvec[1] + pl_norm[2] * vvec[2];
+
+ /* Too small to intersect ? */
+ if (fabs(den) < 1e-12) {
+ return 1;
+ }
+
+ /* Compute the parameterized intersction point */
+ rv = -(pl_norm[0] * ve_0[0] + pl_norm[1] * ve_0[1] + pl_norm[2] * ve_0[2] + pl_const)/den;
+
+ /* Compute the actual intersection point */
+ isect[0] = ve_0[0] + rv * vvec[0];
+ isect[1] = ve_0[1] + rv * vvec[1];
+ isect[2] = ve_0[2] + rv * vvec[2];
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the closest points between two lines a and b. */
+/* Return closest points and parameter values if not NULL. */
+/* Return nz if they are paralel. */
+/* The maths is from page 338 of Tomas Moller and Eric Haines "Real-Time Rendering". */
+int icmLineLineClosest(double ca[3], double cb[3], double *pa, double * pb,
+ double la0[3], double la1[3], double lb0[3], double lb1[3]) {
+ double va[3], vb[3];
+ double vvab[3], vvabns; /* Cross product of va and vb and its norm squared */
+ double vba0[3]; /* lb0 - la0 */
+ double tt[3];
+ double a, b; /* Parameter values */
+
+ icmSub3(va, la1, la0);
+ icmSub3(vb, lb1, lb0);
+ icmCross3(vvab, va, vb);
+ vvabns = icmNorm3sq(vvab);
+
+ if (vvabns < 1e-12)
+ return 1;
+
+ icmSub3(vba0, lb0, la0);
+ icmCross3(tt, vba0, vb);
+ a = icmDot3(tt, vvab) / vvabns;
+
+ icmCross3(tt, vba0, va);
+ b = icmDot3(tt, vvab) / vvabns;
+
+ if (pa != NULL)
+ *pa = a;
+
+ if (pb != NULL)
+ *pb = b;
+
+ if (ca != NULL) {
+ ca[0] = la0[0] + a * va[0];
+ ca[1] = la0[1] + a * va[1];
+ ca[2] = la0[2] + a * va[2];
+ }
+
+ if (cb != NULL) {
+ cb[0] = lb0[0] + b * vb[0];
+ cb[1] = lb0[1] + b * vb[1];
+ cb[2] = lb0[2] + b * vb[2];
+ }
+
+#ifdef NEVER /* Verify */
+ {
+ double vab[3]; /* Vector from ca to cb */
+
+ vab[0] = lb0[0] + b * vb[0] - la0[0] - a * va[0];
+ vab[1] = lb0[1] + b * vb[1] - la0[1] - a * va[1];
+ vab[2] = lb0[2] + b * vb[2] - la0[2] - a * va[2];
+
+ if (icmDot3(va, vab) > 1e-6
+ || icmDot3(vb, vab) > 1e-6)
+ warning("icmLineLineClosest verify failed\n");
+ }
+#endif
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Given 3 3D points, compute a plane equation. */
+/* The normal will be right handed given the order of the points */
+/* The plane equation will be the 3 normal components and the constant. */
+/* Return nz if any points are cooincident or co-linear */
+int icmPlaneEqn3(double eq[4], double p0[3], double p1[3], double p2[3]) {
+ double ll, v1[3], v2[3];
+
+ /* Compute vectors along edges */
+ v2[0] = p1[0] - p0[0];
+ v2[1] = p1[1] - p0[1];
+ v2[2] = p1[2] - p0[2];
+
+ v1[0] = p2[0] - p0[0];
+ v1[1] = p2[1] - p0[1];
+ v1[2] = p2[2] - p0[2];
+
+ /* Compute cross products v1 x v2, which will be the normal */
+ eq[0] = v1[1] * v2[2] - v1[2] * v2[1];
+ eq[1] = v1[2] * v2[0] - v1[0] * v2[2];
+ eq[2] = v1[0] * v2[1] - v1[1] * v2[0];
+
+ /* Normalise the equation */
+ ll = sqrt(eq[0] * eq[0] + eq[1] * eq[1] + eq[2] * eq[2]);
+ if (ll < 1e-10) {
+ return 1;
+ }
+ eq[0] /= ll;
+ eq[1] /= ll;
+ eq[2] /= ll;
+
+ /* Compute the plane equation constant */
+ eq[3] = - (eq[0] * p0[0])
+ - (eq[1] * p0[1])
+ - (eq[2] * p0[2]);
+
+ return 0;
+}
+
+/* Given a 3D point and a plane equation, return the signed */
+/* distance from the plane */
+double icmPlaneDist3(double eq[4], double p[3]) {
+ double rv;
+
+ rv = eq[0] * p[0]
+ + eq[1] * p[1]
+ + eq[2] * p[2]
+ + eq[3];
+
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* CIE Y (range 0 .. 1) to perceptual CIE 1976 L* (range 0 .. 100) */
+double
+icmY2L(double val) {
+ if (val > 0.008856451586)
+ val = pow(val,1.0/3.0);
+ else
+ val = 7.787036979 * val + 16.0/116.0;
+
+ val = (116.0 * val - 16.0);
+ return val;
+}
+
+/* Perceptual CIE 1976 L* (range 0 .. 100) to CIE Y (range 0 .. 1) */
+double
+icmL2Y(double val) {
+ val = (val + 16.0)/116.0;
+
+ if (val > 24.0/116.0)
+ val = pow(val,3.0);
+ else
+ val = (val - 16.0/116.0)/7.787036979;
+ return val;
+}
+
+/* CIE XYZ to perceptual CIE 1976 L*a*b* */
+void
+icmXYZ2Lab(icmXYZNumber *w, double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double x,y,z,fx,fy,fz;
+
+ x = X/w->X;
+ y = Y/w->Y;
+ z = Z/w->Z;
+
+ if (x > 0.008856451586)
+ fx = pow(x,1.0/3.0);
+ else
+ fx = 7.787036979 * x + 16.0/116.0;
+
+ if (y > 0.008856451586)
+ fy = pow(y,1.0/3.0);
+ else
+ fy = 7.787036979 * y + 16.0/116.0;
+
+ if (z > 0.008856451586)
+ fz = pow(z,1.0/3.0);
+ else
+ fz = 7.787036979 * z + 16.0/116.0;
+
+ out[0] = 116.0 * fy - 16.0;
+ out[1] = 500.0 * (fx - fy);
+ out[2] = 200.0 * (fy - fz);
+}
+
+/* Perceptual CIE 1976 L*a*b* to CIE XYZ */
+void
+icmLab2XYZ(icmXYZNumber *w, double *out, double *in) {
+ double L = in[0], a = in[1], b = in[2];
+ double x,y,z,fx,fy,fz;
+
+ fy = (L + 16.0)/116.0;
+ fx = a/500.0 + fy;
+ fz = fy - b/200.0;
+
+ if (fy > 24.0/116.0)
+ y = pow(fy,3.0);
+ else
+ y = (fy - 16.0/116.0)/7.787036979;
+
+ if (fx > 24.0/116.0)
+ x = pow(fx,3.0);
+ else
+ x = (fx - 16.0/116.0)/7.787036979;
+
+ if (fz > 24.0/116.0)
+ z = pow(fz,3.0);
+ else
+ z = (fz - 16.0/116.0)/7.787036979;
+
+ out[0] = x * w->X;
+ out[1] = y * w->Y;
+ out[2] = z * w->Z;
+}
+
+
+/* LCh to Lab */
+void icmLCh2Lab(double *out, double *in) {
+ double C, h;
+
+ C = in[1];
+ h = 3.14159265359/180.0 * in[2];
+
+ out[0] = in[0];
+ out[1] = C * cos(h);
+ out[2] = C * sin(h);
+}
+
+/* Lab to LCh */
+void icmLab2LCh(double *out, double *in) {
+ double C, h;
+
+ C = sqrt(in[1] * in[1] + in[2] * in[2]);
+
+ h = (180.0/3.14159265359) * atan2(in[2], in[1]);
+ h = (h < 0.0) ? h + 360.0 : h;
+
+ out[0] = in[0];
+ out[1] = C;
+ out[2] = h;
+}
+
+/* XYZ to Yxy */
+extern ICCLIB_API void icmXYZ2Yxy(double *out, double *in) {
+ double sum = in[0] + in[1] + in[2];
+ double Y, x, y;
+
+ if (sum < 1e-9) {
+ Y = 0.0;
+ y = 0.333333333;
+ x = 0.333333333;
+ } else {
+ Y = in[1];
+ x = in[0]/sum;
+ y = in[1]/sum;
+ }
+ out[0] = Y;
+ out[1] = x;
+ out[2] = y;
+}
+
+/* Yxy to XYZ */
+extern ICCLIB_API void icmYxy2XYZ(double *out, double *in) {
+ double Y = in[0];
+ double x = in[1];
+ double y = in[2];
+ double z = 1.0 - x - y;
+ double sum;
+ if (y < 1e-9) {
+ out[0] = out[1] = out[2] = 0.0;
+ } else {
+ sum = Y/y;
+ out[0] = x * sum;
+ out[1] = Y;
+ out[2] = z * sum;
+ }
+}
+
+
+/* CIE XYZ to perceptual CIE 1976 L*u*v* */
+extern ICCLIB_API void icmXYZ2Luv(icmXYZNumber *w, double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double un, vn, u, v, fl, fu, fv;
+
+ un = (4.0 * w->X) / (w->X + 15.0 * w->Y + 3.0 * w->Z);
+ vn = (9.0 * w->Y) / (w->X + 15.0 * w->Y + 3.0 * w->Z);
+ u = (4.0 * X) / (X + 15.0 * Y + 3.0 * Z);
+ v = (9.0 * Y) / (X + 15.0 * Y + 3.0 * Z);
+
+ Y /= w->Y;
+
+ if (Y > 0.008856451586)
+ fl = pow(Y,1.0/3.0);
+ else
+ fl = 7.787036979 * Y + 16.0/116.0;
+
+ fu = u - un;
+ fv = v - vn;
+
+ out[0] = 116.0 * fl - 16.0;
+ out[1] = 13.0 * out[0] * fu;
+ out[2] = 13.0 * out[0] * fv;
+}
+
+/* Perceptual CIE 1976 L*u*v* to CIE XYZ */
+extern ICCLIB_API void icmLuv2XYZ(icmXYZNumber *w, double *out, double *in) {
+ double un, vn, u, v, fl, fu, fv, sum, X, Y, Z;
+
+ fl = (in[0] + 16.0)/116.0;
+ fu = in[1] / (13.0 * in[0]);
+ fv = in[2] / (13.0 * in[0]);
+
+ un = (4.0 * w->X) / (w->X + 15.0 * w->Y + 3.0 * w->Z);
+ vn = (9.0 * w->Y) / (w->X + 15.0 * w->Y + 3.0 * w->Z);
+
+ u = fu + un;
+ v = fv + vn;
+
+ if (fl > 24.0/116.0)
+ Y = pow(fl,3.0);
+ else
+ Y = (fl - 16.0/116.0)/7.787036979;
+ Y *= w->Y;
+
+ sum = (9.0 * Y)/v;
+ X = (u * sum)/4.0;
+ Z = (sum - X - 15.0 * Y)/3.0;
+
+ out[0] = X;
+ out[1] = Y;
+ out[2] = Z;
+}
+
+/* NOTE :- none of the following seven have been protected */
+/* against arithmmetic issues (ie. for black) */
+
+/* CIE XYZ to perceptual CIE 1976 UCS diagram Yu'v'*/
+/* (Yu'v' is a better chromaticity space than Yxy) */
+extern ICCLIB_API void icmXYZ21976UCS(double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double den, u, v;
+
+ den = (X + 15.0 * Y + 3.0 * Z);
+ u = (4.0 * X) / den;
+ v = (9.0 * Y) / den;
+
+ out[0] = Y;
+ out[1] = u;
+ out[2] = v;
+}
+
+/* Perceptual CIE 1976 UCS diagram Yu'v' to CIE XYZ */
+extern ICCLIB_API void icm1976UCS2XYZ(double *out, double *in) {
+ double u, v, fl, fu, fv, sum, X, Y, Z;
+
+ Y = in[0];
+ u = in[1];
+ v = in[2];
+
+ X = ((9.0 * u * Y)/(4.0 * v));
+ Z = -(((20.0 * v + 3.0 * u - 12.0) * Y)/(4.0 * v));
+
+ out[0] = X;
+ out[1] = Y;
+ out[2] = Z;
+}
+
+/* CIE XYZ to perceptual CIE 1960 UCS */
+/* (This was obsoleted by the 1976UCS, but is still used */
+/* in computing color temperatures.) */
+extern ICCLIB_API void icmXYZ21960UCS(double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double u, v;
+
+ u = (4.0 * X) / (X + 15.0 * Y + 3.0 * Z);
+ v = (6.0 * Y) / (X + 15.0 * Y + 3.0 * Z);
+
+ out[0] = Y;
+ out[1] = u;
+ out[2] = v;
+}
+
+/* Perceptual CIE 1960 UCS to CIE XYZ */
+extern ICCLIB_API void icm1960UCS2XYZ(double *out, double *in) {
+ double u, v, fl, fu, fv, sum, X, Y, Z;
+
+ Y = in[0];
+ u = in[1];
+ v = in[2];
+
+ X = ((3.0 * u * Y)/(2.0 * v));
+ Z = -(((10.0 * v + u - 4.0) * Y)/(2.0 * v));
+
+ out[0] = X;
+ out[1] = Y;
+ out[2] = Z;
+}
+
+/* CIE XYZ to perceptual CIE 1964 WUV (U*V*W*) */
+/* (This is obsolete but still used in computing CRI) */
+extern ICCLIB_API void icmXYZ21964WUV(icmXYZNumber *w, double *out, double *in) {
+ double W, U, V;
+ double wucs[3];
+ double iucs[3];
+
+ icmXYZ2Ary(wucs, *w);
+ icmXYZ21960UCS(wucs, wucs);
+ icmXYZ21960UCS(iucs, in);
+
+ W = 25.0 * pow(iucs[0] * 100.0/wucs[0], 1.0/3.0) - 17.0;
+ U = 13.0 * W * (iucs[1] - wucs[1]);
+ V = 13.0 * W * (iucs[2] - wucs[2]);
+
+ out[0] = W;
+ out[1] = U;
+ out[2] = V;
+}
+
+/* Perceptual CIE 1964 WUV (U*V*W*) to CIE XYZ */
+extern ICCLIB_API void icm1964WUV2XYZ(icmXYZNumber *w, double *out, double *in) {
+ double W, U, V;
+ double wucs[3];
+ double iucs[3];
+
+ icmXYZ2Ary(wucs, *w);
+ icmXYZ21960UCS(wucs, wucs);
+
+ W = in[0];
+ U = in[1];
+ V = in[2];
+
+ iucs[0] = pow((W + 17.0)/25.0, 3.0) * wucs[0]/100.0;
+ iucs[1] = U / (13.0 * W) + wucs[1];
+ iucs[2] = V / (13.0 * W) + wucs[2];
+
+ icm1960UCS2XYZ(out, iucs);
+}
+
+/* CIE CIE1960 UCS to perceptual CIE 1964 WUV (U*V*W*) */
+extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double *in) {
+ double W, U, V;
+ double wucs[3];
+
+ icmXYZ2Ary(wucs, *w);
+ icmXYZ21960UCS(wucs, wucs);
+
+ W = 25.0 * pow(in[0] * 100.0/wucs[0], 1.0/3.0) - 17.0;
+ U = 13.0 * W * (in[1] - wucs[1]);
+ V = 13.0 * W * (in[2] - wucs[2]);
+
+ out[0] = W;
+ out[1] = U;
+ out[2] = V;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* available D50 Illuminant */
+icmXYZNumber icmD50 = { /* Profile illuminant - D50 */
+ 0.9642, 1.0000, 0.8249
+};
+
+icmXYZNumber icmD50_100 = { /* Profile illuminant - D50, scaled to 100 */
+ 96.42, 100.00, 82.49
+};
+
+double icmD50_ary3[3] = { /* Profile illuminant - D50 */
+ 0.9642, 1.0000, 0.8249
+};
+
+double icmD50_100_ary3[3] = { /* Profile illuminant - D50, scaled to 100 */
+ 96.42, 100.00, 82.49
+};
+
+/* available D65 Illuminant */
+icmXYZNumber icmD65 = { /* Profile illuminant - D65 */
+ 0.9505, 1.0000, 1.0890
+};
+
+icmXYZNumber icmD65_100 = { /* Profile illuminant - D65, scaled to 100 */
+ 95.05, 100.00, 108.90
+};
+
+double icmD65_ary3[3] = { /* Profile illuminant - D65 */
+ 0.9505, 1.0000, 1.0890
+};
+
+double icmD65_100_ary3[3] = { /* Profile illuminant - D65, scaled to 100 */
+ 95.05, 100.00, 108.90
+};
+
+/* Default black point */
+icmXYZNumber icmBlack = {
+ 0.0000, 0.0000, 0.0000
+};
+
+/* Return the normal Delta E given two Lab values */
+double icmLabDE(double *Lab0, double *Lab1) {
+ double rv = 0.0, tt;
+
+ tt = Lab0[0] - Lab1[0];
+ rv += tt * tt;
+ tt = Lab0[1] - Lab1[1];
+ rv += tt * tt;
+ tt = Lab0[2] - Lab1[2];
+ rv += tt * tt;
+
+ return sqrt(rv);
+}
+
+/* Return the normal Delta E squared, given two Lab values */
+double icmLabDEsq(double *Lab0, double *Lab1) {
+ double rv = 0.0, tt;
+
+ tt = Lab0[0] - Lab1[0];
+ rv += tt * tt;
+ tt = Lab0[1] - Lab1[1];
+ rv += tt * tt;
+ tt = Lab0[2] - Lab1[2];
+ rv += tt * tt;
+
+ return rv;
+}
+
+/* Return the normal Delta E given two XYZ values */
+extern ICCLIB_API double icmXYZLabDE(icmXYZNumber *w, double *in0, double *in1) {
+ double lab0[3], lab1[3], rv;
+
+ icmXYZ2Lab(w, lab0, in0);
+ icmXYZ2Lab(w, lab1, in1);
+ rv = icmLabDE(lab0, lab1);
+ return rv;
+}
+
+/* (Note that CIE94 can give odd results for very large delta E's, */
+/* when one of the two points is near the neutral axis: */
+/* ie DE(A,B + del) != DE(A,B) + DE(del) */
+#ifdef NEVER
+{
+ double w1[3] = { 99.996101, -0.058417, -0.010557 };
+ double c1[3] = { 60.267956, 98.845863, -61.163277 };
+ double w2[3] = { 100.014977, -0.138339, 0.089744 };
+ double c2[3] = { 60.294464, 98.117104, -60.843159 };
+
+ printf("DE 1 = %f, 2 = %f\n", icmLabDE(c1, w1), icmLabDE(c2, w2));
+ printf("DE94 1 = %f, 2 = %f\n", icmCIE94(c1, w1), icmCIE94(c2, w2));
+}
+#endif
+
+
+/* Return the CIE94 Delta E color difference measure, squared */
+double icmCIE94sq(double Lab0[3], double Lab1[3]) {
+ double desq, dhsq;
+ double dlsq, dcsq;
+ double c12;
+
+ {
+ double dl, da, db;
+ dl = Lab0[0] - Lab1[0];
+ dlsq = dl * dl; /* dl squared */
+ da = Lab0[1] - Lab1[1];
+ db = Lab0[2] - Lab1[2];
+
+ /* Compute normal Lab delta E squared */
+ desq = dlsq + da * da + db * db;
+ }
+
+ {
+ double c1, c2, dc;
+
+ /* Compute chromanance for the two colors */
+ c1 = sqrt(Lab0[1] * Lab0[1] + Lab0[2] * Lab0[2]);
+ c2 = sqrt(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]);
+ c12 = sqrt(c1 * c2); /* Symetric chromanance */
+
+ /* delta chromanance squared */
+ dc = c2 - c1;
+ dcsq = dc * dc;
+ }
+
+ /* Compute delta hue squared */
+ if ((dhsq = desq - dlsq - dcsq) < 0.0)
+ dhsq = 0.0;
+ {
+ double sc, sh;
+
+ /* Weighting factors for delta chromanance & delta hue */
+ sc = 1.0 + 0.048 * c12;
+ sh = 1.0 + 0.014 * c12;
+ return dlsq + dcsq/(sc * sc) + dhsq/(sh * sh);
+ }
+}
+
+/* Return the CIE94 Delta E color difference measure */
+double icmCIE94(double Lab0[3], double Lab1[3]) {
+ return sqrt(icmCIE94sq(Lab0, Lab1));
+}
+
+/* Return the CIE94 Delta E color difference measure for two XYZ values */
+extern ICCLIB_API double icmXYZCIE94(icmXYZNumber *w, double *in0, double *in1) {
+ double lab0[3], lab1[3];
+
+ icmXYZ2Lab(w, lab0, in0);
+ icmXYZ2Lab(w, lab1, in1);
+ return sqrt(icmCIE94sq(lab0, lab1));
+}
+
+
+/* From the paper "The CIEDE2000 Color-Difference Formula: Implementation Notes, */
+/* Supplementary Test Data, and Mathematical Observations", by */
+/* Gaurav Sharma, Wencheng Wu and Edul N. Dalal, */
+/* Color Res. Appl., vol. 30, no. 1, pp. 21-30, Feb. 2005. */
+
+/* Return the CIEDE2000 Delta E color difference measure squared, for two Lab values */
+double icmCIE2Ksq(double *Lab0, double *Lab1) {
+ double C1, C2;
+ double h1, h2;
+ double dL, dC, dH;
+ double dsq;
+
+ /* The trucated value of PI is needed to ensure that the */
+ /* test cases pass, as one of them lies on the edge of */
+ /* a mathematical discontinuity. The precision is still */
+ /* enough for any practical use. */
+#define RAD2DEG(xx) (180.0/3.14159265358979 * (xx))
+#define DEG2RAD(xx) (3.14159265358979/180.0 * (xx))
+
+ /* Compute Cromanance and Hue angles */
+ {
+ double C1ab, C2ab;
+ double Cab, Cab7, G;
+ double a1, a2;
+
+ C1ab = sqrt(Lab0[1] * Lab0[1] + Lab0[2] * Lab0[2]);
+ C2ab = sqrt(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]);
+ Cab = 0.5 * (C1ab + C2ab);
+ Cab7 = pow(Cab,7.0);
+ G = 0.5 * (1.0 - sqrt(Cab7/(Cab7 + 6103515625.0)));
+ a1 = (1.0 + G) * Lab0[1];
+ a2 = (1.0 + G) * Lab1[1];
+ C1 = sqrt(a1 * a1 + Lab0[2] * Lab0[2]);
+ C2 = sqrt(a2 * a2 + Lab1[2] * Lab1[2]);
+
+ if (C1 < 1e-9)
+ h1 = 0.0;
+ else {
+ h1 = RAD2DEG(atan2(Lab0[2], a1));
+ if (h1 < 0.0)
+ h1 += 360.0;
+ }
+
+ if (C2 < 1e-9)
+ h2 = 0.0;
+ else {
+ h2 = RAD2DEG(atan2(Lab1[2], a2));
+ if (h2 < 0.0)
+ h2 += 360.0;
+ }
+ }
+
+ /* Compute delta L, C and H */
+ {
+ double dh;
+
+ dL = Lab1[0] - Lab0[0];
+ dC = C2 - C1;
+ if (C1 < 1e-9 || C2 < 1e-9) {
+ dh = 0.0;
+ } else {
+ dh = h2 - h1;
+ if (dh > 180.0)
+ dh -= 360.0;
+ else if (dh < -180.0)
+ dh += 360.0;
+ }
+
+ dH = 2.0 * sqrt(C1 * C2) * sin(DEG2RAD(0.5 * dh));
+ }
+
+ {
+ double L, C, h, T;
+ double hh, ddeg;
+ double C7, RC, L50sq, SL, SC, SH, RT;
+ double dLsq, dCsq, dHsq, RCH;
+
+ L = 0.5 * (Lab0[0] + Lab1[0]);
+ C = 0.5 * (C1 + C2);
+ if (C1 < 1e-9 || C2 < 1e-9) {
+ h = h1 + h2;
+ } else {
+ h = h1 + h2;
+ if (fabs(h1 - h2) > 180.0) {
+ if (h < 360.0)
+ h += 360.0;
+ else if (h >= 360.0)
+ h -= 360.0;
+ }
+ h *= 0.5;
+ }
+ T = 1.0 - 0.17 * cos(DEG2RAD(h-30.0)) + 0.24 * cos(DEG2RAD(2.0 * h))
+ + 0.32 * cos(DEG2RAD(3.0 * h + 6.0)) - 0.2 * cos(DEG2RAD(4.0 * h - 63.0));
+ hh = (h - 275.0)/25.0;
+ ddeg = 30.0 * exp(-hh * hh);
+ C7 = pow(C,7.0);
+ RC = 2.0 * sqrt(C7/(C7 + 6103515625.0));
+ L50sq = (L - 50.0) * (L - 50.0);
+ SL = 1.0 + (0.015 * L50sq)/sqrt(20.0 + L50sq);
+ SC = 1.0 + 0.045 * C;
+ SH = 1.0 + 0.015 * C * T;
+ RT = -sin(DEG2RAD(2 * ddeg)) * RC;
+
+ dLsq = dL/SL;
+ dCsq = dC/SC;
+ dHsq = dH/SH;
+
+ RCH = RT * dCsq * dHsq;
+
+ dLsq *= dLsq;
+ dCsq *= dCsq;
+ dHsq *= dHsq;
+
+ dsq = dLsq + dCsq + dHsq + RCH;
+ }
+
+ return dsq;
+
+#undef RAD2DEG
+#undef DEG2RAD
+}
+
+/* Return the CIE2DE000 Delta E color difference measure for two Lab values */
+double icmCIE2K(double *Lab0, double *Lab1) {
+ return sqrt(icmCIE2Ksq(Lab0, Lab1));
+}
+
+/* Return the CIEDE2000 Delta E color difference measure for two XYZ values */
+extern ICCLIB_API double icmXYZCIE2K(icmXYZNumber *w, double *in0, double *in1) {
+ double lab0[3], lab1[3];
+
+ icmXYZ2Lab(w, lab0, in0);
+ icmXYZ2Lab(w, lab1, in1);
+ return sqrt(icmCIE2Ksq(lab0, lab1));
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Chromatic adaptation transform utility */
+/* Return a 3x3 chromatic adaptation matrix */
+/* Use icmMulBy3x3(dst, mat, src) */
+void icmChromAdaptMatrix(
+ int flags, /* Use bradford, Transform given matrix flags */
+ icmXYZNumber d_wp, /* Destination white point */
+ icmXYZNumber s_wp, /* Source white point */
+ double mat[3][3] /* Destination matrix */
+) {
+ double dst[3], src[3]; /* Source & destination white points */
+ double vkmat[3][3]; /* Von Kries matrix */
+#ifdef NEVER
+ static double bradford[3][3] = { /* Linear cone space matrix */
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 }
+ };
+#endif /* NEVER */
+ static double bradford[3][3] = { /* Bradford cone space matrix */
+ { 0.8951, 0.2664, -0.1614 },
+ { -0.7502, 1.7135, 0.0367 },
+ { 0.0389, -0.0685, 1.0296 }
+ };
+ static int inited = 0; /* Compute inverse bradford once */
+ static double ibradford[3][3]; /* Inverse Bradford */
+
+ /* Set initial matrix to unity */
+ if (!(flags & ICM_CAM_MULMATRIX)) {
+ icmSetUnity3x3(mat);
+ }
+
+ icmXYZ2Ary(src, s_wp);
+ icmXYZ2Ary(dst, d_wp);
+
+ if (flags & ICM_CAM_BRADFORD) {
+ icmMulBy3x3(src, bradford, src);
+ icmMulBy3x3(dst, bradford, dst);
+ }
+
+ /* Setup the Von Kries white point adaptation matrix */
+ vkmat[0][0] = dst[0]/src[0];
+ vkmat[1][1] = dst[1]/src[1];
+ vkmat[2][2] = dst[2]/src[2];
+ vkmat[0][1] = vkmat[0][2] = 0.0;
+ vkmat[1][0] = vkmat[1][2] = 0.0;
+ vkmat[2][0] = vkmat[2][1] = 0.0;
+
+ /* Transform to Bradford space if requested */
+ if (flags & ICM_CAM_BRADFORD) {
+ icmMul3x3(mat, bradford);
+ }
+
+ /* Apply chromatic adaptation */
+ icmMul3x3(mat, vkmat);
+
+ /* Transform from Bradford space */
+ if (flags & ICM_CAM_BRADFORD) {
+ if (inited == 0) {
+ icmInverse3x3(ibradford, bradford);
+ inited = 1;
+ }
+ icmMul3x3(mat, ibradford);
+ }
+
+ /* We're done */
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* RGB primaries device to RGB->XYZ transform matrix. */
+/* We assume that the device is perfectly additive, but that */
+/* there may be a scale factor applied to the channels to */
+/* match the white point at RGB = 1. */
+
+/* Return non-zero if matrix would be singular */
+int icmRGBprim2matrix(
+ icmXYZNumber white, /* White point */
+ icmXYZNumber red, /* Red colorant */
+ icmXYZNumber green, /* Green colorant */
+ icmXYZNumber blue, /* Blue colorant */
+ double mat[3][3] /* Destination matrix */
+) {
+ double tmat[3][3];
+ double t[3];
+
+ /* Assemble the colorants into a matrix */
+ tmat[0][0] = red.X;
+ tmat[0][1] = green.X;
+ tmat[0][2] = blue.X;
+ tmat[1][0] = red.Y;
+ tmat[1][1] = green.Y;
+ tmat[1][2] = blue.Y;
+ tmat[2][0] = red.Z;
+ tmat[2][1] = green.Z;
+ tmat[2][2] = blue.Z;
+
+ /* Compute the inverse */
+ if (icmInverse3x3(mat, tmat))
+ return 1;
+
+ /* Compute scale vector that maps colorants to white point */
+ t[0] = mat[0][0] * white.X
+ + mat[0][1] * white.Y
+ + mat[0][2] * white.Z;
+ t[1] = mat[1][0] * white.X
+ + mat[1][1] * white.Y
+ + mat[1][2] * white.Z;
+ t[2] = mat[2][0] * white.X
+ + mat[2][1] * white.Y
+ + mat[2][2] * white.Z;
+
+ /* Now formulate the transform matrix */
+ mat[0][0] = red.X * t[0];
+ mat[0][1] = green.X * t[1];
+ mat[0][2] = blue.X * t[2];
+ mat[1][0] = red.Y * t[0];
+ mat[1][1] = green.Y * t[1];
+ mat[1][2] = blue.Y * t[2];
+ mat[2][0] = red.Z * t[0];
+ mat[2][1] = green.Z * t[1];
+ mat[2][2] = blue.Z * t[2];
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Some PCS utility functions */
+
+/* Clip Lab, while maintaining hue angle. */
+/* Return nz if clipping occured */
+int icmClipLab(double out[3], double in[3]) {
+ double C;
+
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+
+ if (out[0] >= 0.0 && out[0] <= 100.0
+ && out[1] >= -128.0 && out[1] <= 127.0
+ && out[2] >= -128.0 && out[2] <= 127.0)
+ return 0;
+
+ /* Clip L */
+ if (out[0] < 0.0)
+ out[0] = 0.0;
+ else if (out[0] > 100.0)
+ out[0] = 100.0;
+
+ C = out[1];
+ if (fabs(out[2]) > fabs(C))
+ C = out[2];
+ if (C < -128.0 || C > 127.0) {
+ if (C < 0.0)
+ C = -128.0/C;
+ else
+ C = 127.0/C;
+ out[1] *= C;
+ out[2] *= C;
+ }
+
+ return 1;
+}
+
+/* Clip XYZ, while maintaining hue angle */
+/* Return nz if clipping occured */
+int icmClipXYZ(double out[3], double in[3]) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+
+ if (out[0] >= 0.0 && out[0] <= 1.9999
+ && out[1] >= 0.0 && out[1] <= 1.9999
+ && out[2] >= 0.0 && out[2] <= 1.9999)
+ return 0;
+
+ /* Clip Y and scale X and Z similarly */
+
+ if (out[1] > 1.9999) {
+ out[0] *= 1.9999/out[1];
+ out[2] *= 1.9999/out[1];
+ out[1] = 1.9999;
+ } else if (out[1] < 0.0) {
+ out[0] = 0.0;
+ out[1] = 0.0;
+ out[2] = 0.0;
+ }
+
+ if (out[0] < 0.0 || out[0] > 1.9999
+ || out[2] < 0.0 || out[2] > 1.9999) {
+ double D50[3] = { 0.9642, 1.0000, 0.8249 };
+ double bb = 0.0;
+
+ /* Scale the D50 so that it has the same Y value as our color */
+ D50[0] *= out[1];
+ D50[1] *= out[1];
+ D50[2] *= out[1];
+
+ /* Figure out what blend factor with Y scaled D50, brings our */
+ /* color X and Z values into range */
+
+ if (out[0] < 0.0) {
+ double b;
+ b = (0.0 - out[0])/(D50[0] - out[0]);
+ if (b > bb)
+ bb = b;
+ } else if (out[0] > 1.9999) {
+ double b;
+ b = (1.9999 - out[0])/(D50[0] - out[0]);
+ if (b > bb)
+ bb = b;
+ }
+ if (out[2] < 0.0) {
+ double b;
+ b = (0.0 - out[2])/(D50[2] - out[2]);
+ if (b > bb)
+ bb = b;
+ } else if (out[2] > 1.9999) {
+ double b;
+ b = (1.9999 - out[2])/(D50[2] - out[2]);
+ if (b > bb)
+ bb = b;
+ }
+ /* Do the desaturation */
+ out[0] = D50[0] * bb + (1.0 - bb) * out[0];
+ out[2] = D50[2] * bb + (1.0 - bb) * out[2];
+ }
+ return 1;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Object for computing RFC 1321 MD5 checksums. */
+/* Derived from Colin Plumb's 1993 public domain code. */
+
+/* Reset the checksum */
+static void icmMD5_reset(icmMD5 *p) {
+ p->tlen = 0;
+
+ p->sum[0] = 0x67452301;
+ p->sum[1] = 0xefcdab89;
+ p->sum[2] = 0x98badcfe;
+ p->sum[3] = 0x10325476;
+
+ p->fin = 0;
+}
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, pp, xtra, s) \
+ data = (pp)[0] + ((pp)[3] << 24) + ((pp)[2] << 16) + ((pp)[1] << 8); \
+ w += f(x, y, z) + data + xtra; \
+ w = (w << s) | (w >> (32-s)); \
+ w += x;
+
+/* Add another 64 bytes to the checksum */
+static void icmMD5_accume(icmMD5 *p, ORD8 *in) {
+ ORD32 data, a, b, c, d;
+
+ a = p->sum[0];
+ b = p->sum[1];
+ c = p->sum[2];
+ d = p->sum[3];
+
+ MD5STEP(F1, a, b, c, d, in + (4 * 0), 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in + (4 * 1), 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in + (4 * 2), 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in + (4 * 3), 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in + (4 * 4), 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in + (4 * 5), 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in + (4 * 6), 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in + (4 * 7), 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in + (4 * 8), 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in + (4 * 9), 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in + (4 * 10), 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in + (4 * 11), 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in + (4 * 12), 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in + (4 * 13), 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in + (4 * 14), 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in + (4 * 15), 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in + (4 * 1), 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in + (4 * 6), 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in + (4 * 11), 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in + (4 * 0), 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in + (4 * 5), 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in + (4 * 10), 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in + (4 * 15), 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in + (4 * 4), 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in + (4 * 9), 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in + (4 * 14), 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in + (4 * 3), 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in + (4 * 8), 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in + (4 * 13), 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in + (4 * 2), 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in + (4 * 7), 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in + (4 * 12), 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in + (4 * 5), 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in + (4 * 8), 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in + (4 * 11), 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in + (4 * 14), 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in + (4 * 1), 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in + (4 * 4), 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in + (4 * 7), 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in + (4 * 10), 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in + (4 * 13), 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in + (4 * 0), 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in + (4 * 3), 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in + (4 * 6), 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in + (4 * 9), 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in + (4 * 12), 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in + (4 * 15), 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in + (4 * 2), 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in + (4 * 0), 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in + (4 * 7), 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in + (4 * 14), 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in + (4 * 5), 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in + (4 * 12), 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in + (4 * 3), 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in + (4 * 10), 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in + (4 * 1), 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in + (4 * 8), 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in + (4 * 15), 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in + (4 * 6), 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in + (4 * 13), 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in + (4 * 4), 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in + (4 * 11), 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in + (4 * 2), 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in + (4 * 9), 0xeb86d391, 21);
+
+ p->sum[0] += a;
+ p->sum[1] += b;
+ p->sum[2] += c;
+ p->sum[3] += d;
+}
+
+#undef F1
+#undef F2
+#undef F3
+#undef F4
+#undef MD5STEP
+
+/* Add some bytes */
+static void icmMD5_add(icmMD5 *p, ORD8 *ibuf, unsigned int len) {
+ unsigned int bs;
+
+ if (p->fin)
+ return; /* This is actually an error */
+
+ bs = p->tlen; /* Current bytes added */
+ p->tlen = bs + len; /* Update length after adding this buffer */
+ bs &= 0x3f; /* Bytes already in buffer */
+
+ /* Deal with any existing partial bytes in p->buf */
+ if (bs) {
+ ORD8 *np = (ORD8 *)p->buf + bs; /* Next free location in partial buffer */
+
+ bs = 64 - bs; /* Free space in partial buffer */
+
+ if (len < bs) { /* Not enought new to make a full buffer */
+ memmove(np, ibuf, len);
+ return;
+ }
+
+ memmove(np, ibuf, bs); /* Now got one full buffer */
+ icmMD5_accume(p, np);
+ ibuf += bs;
+ len -= bs;
+ }
+
+ /* Deal with input data 64 bytes at a time */
+ while (len >= 64) {
+ icmMD5_accume(p, ibuf);
+ ibuf += 64;
+ len -= 64;
+ }
+
+ /* Deal with any remaining bytes */
+ memmove(p->buf, ibuf, len);
+}
+
+/* Finalise the checksum and return the result. */
+static void icmMD5_get(icmMD5 *p, ORD8 chsum[16]) {
+ int i;
+ unsigned count;
+ ORD32 bits1, bits0;
+ ORD8 *pp;
+
+ if (p->fin == 0) {
+
+ /* Compute number of bytes processed mod 64 */
+ count = p->tlen & 0x3f;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ pp = p->buf + count;
+ *pp++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64, allowing 8 bytes for length in bits. */
+ if (count < 8) { /* Not enough space for padding and length */
+
+ memset(pp, 0, count);
+ icmMD5_accume(p, p->buf);
+
+ /* Now fill the next block with 56 bytes */
+ memset(p->buf, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(pp, 0, count - 8);
+ }
+
+ /* Compute number of bits */
+ bits1 = 0x7 & (p->tlen >> (32 - 3));
+ bits0 = p->tlen << 3;
+
+ /* Append number of bits */
+ p->buf[64 - 8] = bits0 & 0xff;
+ p->buf[64 - 7] = (bits0 >> 8) & 0xff;
+ p->buf[64 - 6] = (bits0 >> 16) & 0xff;
+ p->buf[64 - 5] = (bits0 >> 24) & 0xff;
+ p->buf[64 - 4] = bits1 & 0xff;
+ p->buf[64 - 3] = (bits1 >> 8) & 0xff;
+ p->buf[64 - 2] = (bits1 >> 16) & 0xff;
+ p->buf[64 - 1] = (bits1 >> 24) & 0xff;
+
+ icmMD5_accume(p, p->buf);
+
+ p->fin = 1;
+ }
+
+ /* Return the result, lsb to msb */
+ pp = chsum;
+ for (i = 0; i < 4; i++) {
+ *pp++ = p->sum[i] & 0xff;
+ *pp++ = (p->sum[i] >> 8) & 0xff;
+ *pp++ = (p->sum[i] >> 16) & 0xff;
+ *pp++ = (p->sum[i] >> 24) & 0xff;
+ }
+}
+
+
+/* Delete the instance */
+static void icmMD5_del(icmMD5 *p) {
+ p->al->free(p->al, p);
+}
+
+/* Create a new MD5 checksumming object, with a reset checksum value */
+/* Return it or NULL if there is an error */
+icmMD5 *new_icmMD5(icmAlloc *al) {
+ icmMD5 *p;
+
+ if ((p = (icmMD5 *)al->calloc(al,1,sizeof(icmMD5))) == NULL)
+ return NULL;
+
+ p->al = al;
+ p->reset = icmMD5_reset;
+ p->add = icmMD5_add;
+ p->get = icmMD5_get;
+ p->del = icmMD5_del;
+
+ p->reset(p);
+
+ return p;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Dumy icmFile used to compute MD5 checksum on write */
+
+/* Get the size of the file (Only valid for reading file. */
+static size_t icmFileMD5_get_size(icmFile *pp) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+
+ return p->size;
+}
+
+/* Set current position to offset. Return 0 on success, nz on failure. */
+/* Seek can't be supported for MD5, so and seek must be to current location. */
+static int icmFileMD5_seek(
+icmFile *pp,
+unsigned int offset
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+
+ if (p->of != offset) {
+ p->errc = 1;
+ }
+ if (p->of > p->size)
+ p->size = p->of;
+ return 0;
+}
+
+/* Read count items of size length. Return number of items successfully read. */
+/* Read is not implemented */
+static size_t icmFileMD5_read(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ return 0;
+}
+
+/* write count items of size length. Return number of items successfully written. */
+/* Simply pass to MD5 to compute checksum */
+static size_t icmFileMD5_write(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+ size_t len = size * count;
+
+ p->md5->add(p->md5, (ORD8 *)buffer, len);
+ p->of += len;
+ if (p->of > p->size)
+ p->size = p->of;
+ return count;
+}
+
+/* do a printf */
+/* Not implemented */
+static int icmFileMD5_printf(
+icmFile *pp,
+const char *format,
+...
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+ p->errc = 2;
+ return 0;
+}
+
+/* flush all write data out to secondary storage. Return nz on failure. */
+static int icmFileMD5_flush(
+icmFile *pp
+) {
+ return 0;
+}
+
+/* we're done with the file object, return nz on failure */
+static int icmFileMD5_delete(
+icmFile *pp
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+
+ p->al->free(p->al, p); /* Free object */
+ return 0;
+}
+
+/* Return the error code. Error code will usually be set */
+/* if we did a seek to other than the current location, */
+/* or did a printf. */
+static int icmFileMD5_geterrc(
+icmFile *pp
+) {
+ icmFileMD5 *p = (icmFileMD5 *)pp;
+
+ return p->errc;
+}
+
+/* Create a checksum dump file access class with allocator */
+icmFile *new_icmFileMD5_a(
+icmMD5 *md5, /* MD5 object to use */
+icmAlloc *al /* heap allocator */
+) {
+ icmFileMD5 *p;
+
+ if ((p = (icmFileMD5 *) al->calloc(al, 1, sizeof(icmFileMD5))) == NULL) {
+ return NULL;
+ }
+ p->md5 = md5; /* MD5 compute object */
+ p->al = al; /* Heap allocator */
+ p->get_size = icmFileMD5_get_size;
+ p->seek = icmFileMD5_seek;
+ p->read = icmFileMD5_read;
+ p->write = icmFileMD5_write;
+ p->gprintf = icmFileMD5_printf;
+ p->flush = icmFileMD5_flush;
+ p->del = icmFileMD5_delete;
+ p->get_errc = icmFileMD5_geterrc;
+
+ p->of = 0;
+ p->errc = 0;
+
+ return (icmFile *)p;
+}
+
+
+/* ============================================= */
+/* Implementation of color transform lookups. */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Methods common to all transforms (icmLuBase) : */
+
+/* Return information about the native lut in/out/pcs colorspaces. */
+/* Any pointer may be NULL if value is not to be returned */
+static void
+icmLutSpaces(
+ struct _icmLuBase *p, /* This */
+ icColorSpaceSignature *ins, /* Return Native input color space */
+ int *inn, /* Return number of input components */
+ icColorSpaceSignature *outs, /* Return Native output color space */
+ int *outn, /* Return number of output components */
+ icColorSpaceSignature *pcs /* Return Native PCS color space */
+ /* (this will be the same as ins or outs */
+ /* depending on the lookup direction) */
+) {
+ if (ins != NULL)
+ *ins = p->inSpace;
+ if (inn != NULL)
+ *inn = (int)number_ColorSpaceSignature(p->inSpace);
+
+ if (outs != NULL)
+ *outs = p->outSpace;
+ if (outn != NULL)
+ *outn = (int)number_ColorSpaceSignature(p->outSpace);
+ if (pcs != NULL)
+ *pcs = p->pcs;
+}
+
+/* Return information about the effective lookup in/out colorspaces, */
+/* including allowance for PCS override. */
+/* Any pointer may be NULL if value is not to be returned */
+static void
+icmLuSpaces(
+ struct _icmLuBase *p, /* This */
+ icColorSpaceSignature *ins, /* Return effective input color space */
+ int *inn, /* Return number of input components */
+ icColorSpaceSignature *outs, /* Return effective output color space */
+ int *outn, /* Return number of output components */
+ icmLuAlgType *alg, /* Return type of lookup algorithm used */
+ icRenderingIntent *intt, /* Return the intent being implented */
+ icmLookupFunc *fnc, /* Return the profile function being implemented */
+ icColorSpaceSignature *pcs, /* Return the profile effective PCS */
+ icmLookupOrder *ord /* return the search Order */
+) {
+ if (ins != NULL)
+ *ins = p->e_inSpace;
+ if (inn != NULL)
+ *inn = (int)number_ColorSpaceSignature(p->e_inSpace);
+
+ if (outs != NULL)
+ *outs = p->e_outSpace;
+ if (outn != NULL)
+ *outn = (int)number_ColorSpaceSignature(p->e_outSpace);
+
+ if (alg != NULL)
+ *alg = p->ttype;
+
+ if (intt != NULL)
+ *intt = p->intent;
+
+ if (fnc != NULL)
+ *fnc = p->function;
+
+ if (pcs != NULL)
+ *pcs = p->e_pcs;
+
+ if (ord != NULL)
+ *ord = p->order;
+}
+
+/* Relative to Absolute for this WP in XYZ */
+static void icmLuXYZ_Rel2Abs(icmLuBase *p, double *out, double *in) {
+ icmMulBy3x3(out, p->toAbs, in);
+}
+
+/* Absolute to Relative for this WP in XYZ */
+static void icmLuXYZ_Abs2Rel(icmLuBase *p, double *out, double *in) {
+ icmMulBy3x3(out, p->fromAbs, in);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Methods common to all non-named transforms (icmLuBase) : */
+
+/* Initialise the LU white and black points from the ICC tags, */
+/* and the corresponding absolute<->relative conversion matrices */
+/* return nz on error */
+static int icmLuInit_Wh_bk(
+struct _icmLuBase *lup
+) {
+ icmXYZArray *whitePointTag, *blackPointTag;
+ icc *p = lup->icp;
+
+ if ((whitePointTag = (icmXYZArray *)p->read_tag(p, icSigMediaWhitePointTag)) == NULL
+ || whitePointTag->ttype != icSigXYZType || whitePointTag->size < 1) {
+ if (p->header->deviceClass != icSigLinkClass
+ && (lup->intent == icAbsoluteColorimetric
+ || lup->intent == icmAbsolutePerceptual
+ || lup->intent == icmAbsoluteSaturation)) {
+ sprintf(p->err,"icc_lookup: Profile is missing Media White Point Tag");
+ p->errc = 1;
+ return 1;
+ }
+ p->err[0] = '\000';
+ p->errc = 0;
+ lup->whitePoint = icmD50; /* safe value */
+ } else
+ lup->whitePoint = whitePointTag->data[0]; /* Copy structure */
+
+ if ((blackPointTag = (icmXYZArray *)p->read_tag(p, icSigMediaBlackPointTag)) == NULL
+ || blackPointTag->ttype != icSigXYZType || blackPointTag->size < 1) {
+ p->err[0] = '\000';
+ p->errc = 0;
+ lup->blackPoint = icmBlack; /* default */
+ lup->blackisassumed = 1; /* We assumed the default */
+ } else {
+ lup->blackPoint = blackPointTag->data[0]; /* Copy structure */
+ lup->blackisassumed = 0; /* The black is from the tag */
+ }
+
+ /* Create absolute <-> relative conversion matricies */
+ icmChromAdaptMatrix(ICM_CAM_BRADFORD, lup->whitePoint, icmD50, lup->toAbs);
+ icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, lup->whitePoint, lup->fromAbs);
+ DBLLL(("toAbs and fromAbs created from wp %f %f %f and D50 %f %f %f\n", lup->whitePoint.X, lup->whitePoint.Y, lup->whitePoint.Z, icmD50.X, icmD50.Y, icmD50.Z));
+ DBLLL(("toAbs = %f %f %f\n %f %f %f\n %f %f %f\n", lup->toAbs[0][0], lup->toAbs[0][1], lup->toAbs[0][2], lup->toAbs[1][0], lup->toAbs[1][1], lup->toAbs[1][2], lup->toAbs[2][0], lup->toAbs[2][1], lup->toAbs[2][2]));
+ DBLLL(("fromAbs = %f %f %f\n %f %f %f\n %f %f %f\n", lup->fromAbs[0][0], lup->fromAbs[0][1], lup->fromAbs[0][2], lup->fromAbs[1][0], lup->fromAbs[1][1], lup->fromAbs[1][2], lup->fromAbs[2][0], lup->fromAbs[2][1], lup->fromAbs[2][2]));
+
+ return 0;
+}
+
+/* Return the media white and black points in absolute XYZ space. */
+/* Note that if not in the icc, the black point will be returned as 0, 0, 0, */
+/* and the function will return nz. */
+/* Any pointer may be NULL if value is not to be returned */
+static int icmLuWh_bk_points(
+struct _icmLuBase *p,
+double *wht,
+double *blk
+) {
+ if (wht != NULL) {
+ icmXYZ2Ary(wht,p->whitePoint);
+ }
+
+ if (blk != NULL) {
+ icmXYZ2Ary(blk,p->blackPoint);
+ }
+ if (p->blackisassumed)
+ return 1;
+ return 0;
+}
+
+/* Get the LU white and black points in LU PCS space, converted to XYZ. */
+/* (ie. white and black will be relative if LU is relative intent etc.) */
+/* Return nz if the black point is being assumed to be 0,0,0 rather */
+/* than being from the tag. */ \
+static int icmLuLu_wh_bk_points(
+struct _icmLuBase *p,
+double *wht,
+double *blk
+) {
+ if (wht != NULL) {
+ icmXYZ2Ary(wht,p->whitePoint);
+ }
+
+ if (blk != NULL) {
+ icmXYZ2Ary(blk,p->blackPoint);
+ }
+ if (p->intent != icAbsoluteColorimetric
+ && p->intent != icmAbsolutePerceptual
+ && p->intent != icmAbsoluteSaturation) {
+ if (wht != NULL)
+ icmMulBy3x3(wht, p->fromAbs, wht);
+ if (blk != NULL)
+ icmMulBy3x3(blk, p->fromAbs, blk);
+ }
+ if (p->blackisassumed)
+ return 1;
+ return 0;
+}
+
+/* Get the native (internal) ranges for the Monochrome or Matrix profile */
+/* Arguments may be NULL */
+static void
+icmLu_get_lutranges (
+ struct _icmLuBase *p,
+ double *inmin, double *inmax, /* Return maximum range of inspace values */
+ double *outmin, double *outmax /* Return maximum range of outspace values */
+) {
+ icTagTypeSignature tagType;
+
+ if (p->ttype == icmLutType) {
+ icmLuLut *pp = (icmLuLut *)p;
+ tagType = pp->lut->ttype;
+ } else {
+ tagType = icMaxEnumType;
+ }
+
+ /* Hmm. we have no way of handling an error from getRange. */
+ /* It shouldn't ever return one unless there is a mismatch between */
+ /* getRange and Lu creation... */
+ getRange(p->icp, p->inSpace, tagType, inmin, inmax);
+ getRange(p->icp, p->outSpace, tagType, outmin, outmax);
+}
+
+/* Get the effective (externally visible) ranges for the all profile types */
+/* Arguments may be NULL */
+static void
+icmLu_get_ranges (
+ struct _icmLuBase *p,
+ double *inmin, double *inmax, /* Return maximum range of inspace values */
+ double *outmin, double *outmax /* Return maximum range of outspace values */
+) {
+ icTagTypeSignature tagType;
+
+ if (p->ttype == icmLutType) {
+ icmLuLut *pp = (icmLuLut *)p;
+ tagType = pp->lut->ttype;
+ } else {
+ tagType = icMaxEnumType;
+ }
+ /* Hmm. we have no way of handling an error from getRange. */
+ /* It shouldn't ever return one unless there is a mismatch between */
+ /* getRange and Lu creation... */
+ getRange(p->icp, p->e_inSpace, tagType, inmin, inmax);
+ getRange(p->icp, p->e_outSpace, tagType, outmin, outmax);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Forward and Backward Monochrome type methods: */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+
+/* Individual components of Fwd conversion: */
+
+/* Actual device to linearised device */
+static int
+icmLuMonoFwd_curve (
+icmLuMono *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+
+ /* Translate from device to PCS scale */
+ if ((rv |= p->grayCurve->lookup_fwd(p->grayCurve,&out[0],&in[0])) > 1) {
+ sprintf(icp->err,"icc_lookup: Curve->lookup_fwd() failed");
+ icp->errc = rv;
+ return 2;
+ }
+
+ return rv;
+}
+
+/* Linearised device to relative PCS */
+static int
+icmLuMonoFwd_map (
+icmLuMono *p, /* This */
+double *out, /* Vector of output values (native space) */
+double *in /* Vector of input values (native space) */
+) {
+ int rv = 0;
+ double Y = in[0]; /* In case out == in */
+
+ out[0] = p->pcswht.X;
+ out[1] = p->pcswht.Y;
+ out[2] = p->pcswht.Z;
+ if (p->pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out); /* in Lab */
+
+ /* Scale linearized device level to PCS white */
+ out[0] *= Y;
+ out[1] *= Y;
+ out[2] *= Y;
+
+ return rv;
+}
+
+/* relative PCS to absolute PCS (if required) */
+static int
+icmLuMonoFwd_abs ( /* Abs comes last in Fwd conversion */
+icmLuMono *p, /* This */
+double *out, /* Vector of output values in Effective PCS */
+double *in /* Vector of input values in Native PCS */
+) {
+ int rv = 0;
+
+ if (out != in) { /* Don't alter input values */
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ }
+
+ /* Do absolute conversion */
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+
+ if (p->pcs == icSigLabData) /* Convert L to Y */
+ icmLab2XYZ(&p->pcswht, out, out);
+
+ /* Convert from Relative to Absolute colorimetric */
+ icmMulBy3x3(out, p->toAbs, out);
+
+ if (p->e_pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+
+ } else {
+
+ /* Convert from Native to Effective output space */
+ if (p->pcs == icSigLabData && p->e_pcs == icSigXYZData)
+ icmLab2XYZ(&p->pcswht, out, out);
+ else if (p->pcs == icSigXYZData && p->e_pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+ }
+
+ return rv;
+}
+
+
+/* Overall Fwd conversion routine (Dev->PCS) */
+static int
+icmLuMonoFwd_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Input value */
+) {
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoFwd_curve(p, out, in);
+ rv |= icmLuMonoFwd_map(p, out, out);
+ rv |= icmLuMonoFwd_abs(p, out, out);
+ return rv;
+}
+
+/* Three stage conversion routines */
+static int
+icmLuMonoFwd_lookup_in(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoFwd_curve(p, out, in);
+ return rv;
+}
+
+static int
+icmLuMonoFwd_lookup_core(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoFwd_map(p, out, in);
+ rv |= icmLuMonoFwd_abs(p, out, out);
+ return rv;
+}
+
+static int
+icmLuMonoFwd_lookup_out(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ return rv;
+}
+
+
+/* - - - - - - - - - - - - - - */
+/* Individual components of Bwd conversion: */
+
+/* Convert from relative PCS to absolute PCS (if required) */
+static int
+icmLuMonoBwd_abs ( /* Abs comes first in Bwd conversion */
+icmLuMono *p, /* This */
+double *out, /* Vector of output values in Native PCS */
+double *in /* Vector of input values in Effective PCS */
+) {
+ int rv = 0;
+
+ if (out != in) { /* Don't alter input values */
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ }
+
+ /* Force to monochrome locus in correct space */
+ if (p->e_pcs == icSigLabData) {
+ double wp[3];
+
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+ wp[0] = p->whitePoint.X;
+ wp[1] = p->whitePoint.Y;
+ wp[2] = p->whitePoint.Z;
+ } else {
+ wp[0] = p->pcswht.X;
+ wp[1] = p->pcswht.Y;
+ wp[2] = p->pcswht.Z;
+ }
+ icmXYZ2Lab(&p->pcswht, wp, wp); /* Convert to Lab white point */
+ out[1] = out[0]/wp[0] * wp[1];
+ out[2] = out[0]/wp[0] * wp[2];
+
+ } else {
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+ out[0] = out[1]/p->whitePoint.Y * p->whitePoint.X;
+ out[2] = out[1]/p->whitePoint.Y * p->whitePoint.Z;
+ } else {
+ out[0] = out[1]/p->pcswht.Y * p->pcswht.X;
+ out[2] = out[1]/p->pcswht.Y * p->pcswht.Z;
+ }
+ }
+
+ /* Do absolute conversion, and conversion to effective PCS */
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+
+ if (p->e_pcs == icSigLabData)
+ icmLab2XYZ(&p->pcswht, out, out);
+
+ icmMulBy3x3(out, p->fromAbs, out);
+
+ /* Convert from Effective to Native input space */
+ if (p->pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+
+ } else {
+
+ /* Convert from Effective to Native input space */
+ if (p->e_pcs == icSigLabData && p->pcs == icSigXYZData)
+ icmLab2XYZ(&p->pcswht, out, out);
+ else if (p->e_pcs == icSigXYZData && p->pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+ }
+
+ return rv;
+}
+
+/* Map from relative PCS to linearised device */
+static int
+icmLuMonoBwd_map (
+icmLuMono *p, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values (native space) */
+) {
+ int rv = 0;
+ double pcsw[3];
+
+ pcsw[0] = p->pcswht.X;
+ pcsw[1] = p->pcswht.Y;
+ pcsw[2] = p->pcswht.Z;
+ if (p->pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, pcsw, pcsw); /* in Lab (should be 100.0!) */
+
+ /* Divide linearized device level into PCS white luminence */
+ if (p->pcs == icSigLabData)
+ out[0] = in[0]/pcsw[0];
+ else
+ out[0] = in[1]/pcsw[1];
+
+ return rv;
+}
+
+/* Map from linearised device to actual device */
+static int
+icmLuMonoBwd_curve (
+icmLuMono *p, /* This */
+double *out, /* Output value */
+double *in /* Input value */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+
+ /* Convert to device value through curve */
+ if ((rv = p->grayCurve->lookup_bwd(p->grayCurve,&out[0],&in[0])) > 1) {
+ sprintf(icp->err,"icc_lookup: Curve->lookup_bwd() failed");
+ icp->errc = rv;
+ return 2;
+ }
+
+ return rv;
+}
+
+/* Overall Bwd conversion routine (PCS->Dev) */
+static int
+icmLuMonoBwd_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ double temp[3];
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoBwd_abs(p, temp, in);
+ rv |= icmLuMonoBwd_map(p, out, temp);
+ rv |= icmLuMonoBwd_curve(p, out, out);
+ return rv;
+}
+
+/* Three stage conversion routines */
+static int
+icmLuMonoBwd_lookup_in(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ return rv;
+}
+
+static int
+icmLuMonoBwd_lookup_core(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ double temp[3];
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoBwd_abs(p, temp, in);
+ rv |= icmLuMonoBwd_map(p, out, temp);
+ return rv;
+}
+
+static int
+icmLuMonoBwd_lookup_out(
+icmLuBase *pp, /* This */
+double *out, /* Output value */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMono *p = (icmLuMono *)pp;
+ rv |= icmLuMonoBwd_curve(p, out, in);
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - */
+
+static void
+icmLuMono_delete(
+icmLuBase *p
+) {
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+static icmLuBase *
+new_icmLuMono(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func, /* Functionality requested */
+ int dir /* 0 = fwd, 1 = bwd */
+) {
+ icmLuMono *p;
+
+ if ((p = (icmLuMono *) icp->al->calloc(icp->al,1,sizeof(icmLuMono))) == NULL)
+ return NULL;
+ p->icp = icp;
+ p->del = icmLuMono_delete;
+ p->lutspaces= icmLutSpaces;
+ p->spaces = icmLuSpaces;
+ p->XYZ_Rel2Abs = icmLuXYZ_Rel2Abs;
+ p->XYZ_Abs2Rel = icmLuXYZ_Abs2Rel;
+ p->get_lutranges = icmLu_get_lutranges;
+ p->get_ranges = icmLu_get_ranges;
+ p->init_wh_bk = icmLuInit_Wh_bk;
+ p->wh_bk_points = icmLuWh_bk_points;
+ p->lu_wh_bk_points = icmLuLu_wh_bk_points;
+ p->fwd_lookup = icmLuMonoFwd_lookup;
+ p->fwd_curve = icmLuMonoFwd_curve;
+ p->fwd_map = icmLuMonoFwd_map;
+ p->fwd_abs = icmLuMonoFwd_abs;
+ p->bwd_lookup = icmLuMonoBwd_lookup;
+ p->bwd_abs = icmLuMonoFwd_abs;
+ p->bwd_map = icmLuMonoFwd_map;
+ p->bwd_curve = icmLuMonoFwd_curve;
+ if (dir) {
+ p->ttype = icmMonoBwdType;
+ p->lookup = icmLuMonoBwd_lookup;
+ p->lookup_in = icmLuMonoBwd_lookup_in;
+ p->lookup_core = icmLuMonoBwd_lookup_core;
+ p->lookup_out = icmLuMonoBwd_lookup_out;
+ p->lookup_inv_in = icmLuMonoFwd_lookup_out; /* Opposite of Bwd_lookup_in */
+ } else {
+ p->ttype = icmMonoFwdType;
+ p->lookup = icmLuMonoFwd_lookup;
+ p->lookup_in = icmLuMonoFwd_lookup_in;
+ p->lookup_core = icmLuMonoFwd_lookup_core;
+ p->lookup_out = icmLuMonoFwd_lookup_out;
+ p->lookup_inv_in = icmLuMonoBwd_lookup_out; /* Opposite of Fwd_lookup_in */
+ }
+
+ /* Lookup the white and black points */
+ if (p->init_wh_bk((icmLuBase *)p)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* See if the color spaces are appropriate for the mono type */
+ if (number_ColorSpaceSignature(icp->header->colorSpace) != 1
+ || ( icp->header->pcs != icSigXYZData && icp->header->pcs != icSigLabData)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Find the appropriate tags */
+ if ((p->grayCurve = (icmCurve *)icp->read_tag(icp, icSigGrayTRCTag)) == NULL
+ || p->grayCurve->ttype != icSigCurveType) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ p->pcswht = icp->header->illuminant;
+ p->intent = intent;
+ p->function = func;
+ p->inSpace = inSpace;
+ p->outSpace = outSpace;
+ p->pcs = pcs;
+ p->e_inSpace = e_inSpace;
+ p->e_outSpace = e_outSpace;
+ p->e_pcs = e_pcs;
+
+ return (icmLuBase *)p;
+}
+
+static icmLuBase *
+new_icmLuMonoFwd(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func /* Functionality requested */
+) {
+ return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
+ intent, func, 0);
+}
+
+
+static icmLuBase *
+new_icmLuMonoBwd(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func /* Functionality requested */
+) {
+ return new_icmLuMono(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
+ intent, func, 1);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Forward and Backward Matrix type conversion */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+
+/* Individual components of Fwd conversion: */
+static int
+icmLuMatrixFwd_curve (
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+
+ /* Curve lookups */
+ if ((rv |= p->redCurve->lookup_fwd( p->redCurve, &out[0],&in[0])) > 1
+ || (rv |= p->greenCurve->lookup_fwd(p->greenCurve,&out[1],&in[1])) > 1
+ || (rv |= p->blueCurve->lookup_fwd( p->blueCurve, &out[2],&in[2])) > 1) {
+ sprintf(icp->err,"icc_lookup: Curve->lookup_fwd() failed");
+ icp->errc = rv;
+ return 2;
+ }
+
+ return rv;
+}
+
+static int
+icmLuMatrixFwd_matrix (
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ double tt[3];
+
+ /* Matrix */
+ tt[0] = p->mx[0][0] * in[0] + p->mx[0][1] * in[1] + p->mx[0][2] * in[2];
+ tt[1] = p->mx[1][0] * in[0] + p->mx[1][1] * in[1] + p->mx[1][2] * in[2];
+ tt[2] = p->mx[2][0] * in[0] + p->mx[2][1] * in[1] + p->mx[2][2] * in[2];
+
+ out[0] = tt[0];
+ out[1] = tt[1];
+ out[2] = tt[2];
+
+ return rv;
+}
+
+static int
+icmLuMatrixFwd_abs (/* Abs comes last in Fwd conversion */
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+
+ if (out != in) { /* Don't alter input values */
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ }
+
+ /* If required, convert from Relative to Absolute colorimetric */
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+ icmMulBy3x3(out, p->toAbs, out);
+ }
+
+ /* If e_pcs is Lab, then convert XYZ to Lab */
+ if (p->e_pcs == icSigLabData)
+ icmXYZ2Lab(&p->pcswht, out, out);
+
+ return rv;
+}
+
+
+/* Overall Fwd conversion (Dev->PCS)*/
+static int
+icmLuMatrixFwd_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixFwd_curve(p, out, in);
+ rv |= icmLuMatrixFwd_matrix(p, out, out);
+ rv |= icmLuMatrixFwd_abs(p, out, out);
+ return rv;
+}
+
+/* Three stage conversion routines */
+static int
+icmLuMatrixFwd_lookup_in (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixFwd_curve(p, out, in);
+ return rv;
+}
+
+static int
+icmLuMatrixFwd_lookup_core (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixFwd_matrix(p, out, in);
+ rv |= icmLuMatrixFwd_abs(p, out, out);
+ return rv;
+}
+
+static int
+icmLuMatrixFwd_lookup_out (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - */
+/* Individual components of Bwd conversion: */
+
+static int
+icmLuMatrixBwd_abs (/* Abs comes first in Bwd conversion */
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+
+ if (out != in) { /* Don't alter input values */
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ }
+
+ /* If e_pcs is Lab, then convert Lab to XYZ */
+ if (p->e_pcs == icSigLabData)
+ icmLab2XYZ(&p->pcswht, out, out);
+
+ /* If required, convert from Absolute to Relative colorimetric */
+ if (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation) {
+ icmMulBy3x3(out, p->fromAbs, out);
+ }
+
+ return rv;
+}
+
+static int
+icmLuMatrixBwd_matrix (
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ double tt[3];
+
+ tt[0] = in[0];
+ tt[1] = in[1];
+ tt[2] = in[2];
+
+ /* Matrix */
+ out[0] = p->bmx[0][0] * tt[0] + p->bmx[0][1] * tt[1] + p->bmx[0][2] * tt[2];
+ out[1] = p->bmx[1][0] * tt[0] + p->bmx[1][1] * tt[1] + p->bmx[1][2] * tt[2];
+ out[2] = p->bmx[2][0] * tt[0] + p->bmx[2][1] * tt[1] + p->bmx[2][2] * tt[2];
+
+ return rv;
+}
+
+static int
+icmLuMatrixBwd_curve (
+icmLuMatrix *p, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ icc *icp = p->icp;
+ int rv = 0;
+
+ /* Curves */
+ if ((rv |= p->redCurve->lookup_bwd(p->redCurve,&out[0],&in[0])) > 1
+ || (rv |= p->greenCurve->lookup_bwd(p->greenCurve,&out[1],&in[1])) > 1
+ || (rv |= p->blueCurve->lookup_bwd(p->blueCurve,&out[2],&in[2])) > 1) {
+ sprintf(icp->err,"icc_lookup: Curve->lookup_bwd() failed");
+ icp->errc = rv;
+ return 2;
+ }
+ return rv;
+}
+
+/* Overall Bwd conversion (PCS->Dev) */
+static int
+icmLuMatrixBwd_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixBwd_abs(p, out, in);
+ rv |= icmLuMatrixBwd_matrix(p, out, out);
+ rv |= icmLuMatrixBwd_curve(p, out, out);
+ return rv;
+}
+
+/* Three stage conversion routines */
+static int
+icmLuMatrixBwd_lookup_in (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ return rv;
+}
+
+static int
+icmLuMatrixBwd_lookup_core (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixBwd_abs(p, out, in);
+ rv |= icmLuMatrixBwd_matrix(p, out, out);
+ return rv;
+}
+
+static int
+icmLuMatrixBwd_lookup_out (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuMatrix *p = (icmLuMatrix *)pp;
+ rv |= icmLuMatrixBwd_curve(p, out, in);
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - */
+
+static void
+icmLuMatrix_delete(
+icmLuBase *p
+) {
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+/* We setup valid fwd and bwd component conversions, */
+/* but setup only the asked for overal conversion. */
+static icmLuBase *
+new_icmLuMatrix(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func, /* Functionality requested */
+ int dir /* 0 = fwd, 1 = bwd */
+) {
+ icmLuMatrix *p;
+
+ if ((p = (icmLuMatrix *) icp->al->calloc(icp->al,1,sizeof(icmLuMatrix))) == NULL)
+ return NULL;
+ p->icp = icp;
+ p->del = icmLuMatrix_delete;
+ p->lutspaces= icmLutSpaces;
+ p->spaces = icmLuSpaces;
+ p->XYZ_Rel2Abs = icmLuXYZ_Rel2Abs;
+ p->XYZ_Abs2Rel = icmLuXYZ_Abs2Rel;
+ p->get_lutranges = icmLu_get_lutranges;
+ p->get_ranges = icmLu_get_ranges;
+ p->init_wh_bk = icmLuInit_Wh_bk;
+ p->wh_bk_points = icmLuWh_bk_points;
+ p->lu_wh_bk_points = icmLuLu_wh_bk_points;
+ p->fwd_lookup = icmLuMatrixFwd_lookup;
+ p->fwd_curve = icmLuMatrixFwd_curve;
+ p->fwd_matrix = icmLuMatrixFwd_matrix;
+ p->fwd_abs = icmLuMatrixFwd_abs;
+ p->bwd_lookup = icmLuMatrixBwd_lookup;
+ p->bwd_abs = icmLuMatrixBwd_abs;
+ p->bwd_matrix = icmLuMatrixBwd_matrix;
+ p->bwd_curve = icmLuMatrixBwd_curve;
+ if (dir) {
+ p->ttype = icmMatrixBwdType;
+ p->lookup = icmLuMatrixBwd_lookup;
+ p->lookup_in = icmLuMatrixBwd_lookup_in;
+ p->lookup_core = icmLuMatrixBwd_lookup_core;
+ p->lookup_out = icmLuMatrixBwd_lookup_out;
+ p->lookup_inv_in = icmLuMatrixFwd_lookup_out; /* Opposite of Bwd_lookup_in */
+ } else {
+ p->ttype = icmMatrixFwdType;
+ p->lookup = icmLuMatrixFwd_lookup;
+ p->lookup_in = icmLuMatrixFwd_lookup_in;
+ p->lookup_core = icmLuMatrixFwd_lookup_core;
+ p->lookup_out = icmLuMatrixFwd_lookup_out;
+ p->lookup_inv_in = icmLuMatrixBwd_lookup_out; /* Opposite of Fwd_lookup_in */
+ }
+
+ /* Lookup the white and black points */
+ if (p->init_wh_bk((icmLuBase *)p)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Note that we can use matrix type even if PCS is Lab, */
+ /* by simply converting it. */
+
+ /* Find the appropriate tags */
+ if ((p->redCurve = (icmCurve *)icp->read_tag(icp, icSigRedTRCTag)) == NULL
+ || p->redCurve->ttype != icSigCurveType
+ || (p->greenCurve = (icmCurve *)icp->read_tag(icp, icSigGreenTRCTag)) == NULL
+ || p->greenCurve->ttype != icSigCurveType
+ || (p->blueCurve = (icmCurve *)icp->read_tag(icp, icSigBlueTRCTag)) == NULL
+ || p->blueCurve->ttype != icSigCurveType
+ || (p->redColrnt = (icmXYZArray *)icp->read_tag(icp, icSigRedColorantTag)) == NULL
+ || p->redColrnt->ttype != icSigXYZType || p->redColrnt->size < 1
+ || (p->greenColrnt = (icmXYZArray *)icp->read_tag(icp, icSigGreenColorantTag)) == NULL
+ || p->greenColrnt->ttype != icSigXYZType || p->greenColrnt->size < 1
+ || (p->blueColrnt = (icmXYZArray *)icp->read_tag(icp, icSigBlueColorantTag)) == NULL
+ || p->blueColrnt->ttype != icSigXYZType || p->blueColrnt->size < 1) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Setup the matrix */
+ p->mx[0][0] = p->redColrnt->data[0].X;
+ p->mx[0][1] = p->greenColrnt->data[0].X;
+ p->mx[0][2] = p->blueColrnt->data[0].X;
+ p->mx[1][1] = p->greenColrnt->data[0].Y;
+ p->mx[1][0] = p->redColrnt->data[0].Y;
+ p->mx[1][2] = p->blueColrnt->data[0].Y;
+ p->mx[2][1] = p->greenColrnt->data[0].Z;
+ p->mx[2][0] = p->redColrnt->data[0].Z;
+ p->mx[2][2] = p->blueColrnt->data[0].Z;
+
+#ifndef ICM_STRICT
+ /* Workaround for buggy Kodak RGB profiles. Their matrix values */
+ /* may be scaled to 100 rather than 1.0, and the colorant curves */
+ /* may be scaled by 0.5 */
+ if (icp->header->cmmId == str2tag("KCMS")) {
+ int i, j, oc = 0;
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (p->mx[i][j] > 5.0)
+ oc++;
+ if (oc > 4) { /* Looks like it */
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ p->mx[i][j] /= 100.0;
+ }
+ }
+#endif /* ICM_STRICT */
+
+ if (icmInverse3x3(p->bmx, p->mx) != 0) { /* Compute inverse */
+ sprintf(icp->err,"icc_new_iccLuMatrix: Matrix wasn't invertable");
+ icp->errc = 2;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ p->pcswht = icp->header->illuminant;
+ p->intent = intent;
+ p->function = func;
+ p->inSpace = inSpace;
+ p->outSpace = outSpace;
+ p->pcs = pcs;
+ p->e_inSpace = e_inSpace;
+ p->e_outSpace = e_outSpace;
+ p->e_pcs = e_pcs;
+
+ /* Lookup the white and black points */
+ if (p->init_wh_bk((icmLuBase *)p)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ return (icmLuBase *)p;
+}
+
+static icmLuBase *
+new_icmLuMatrixFwd(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func /* Functionality requested */
+) {
+ return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
+ intent, func, 0);
+}
+
+
+static icmLuBase *
+new_icmLuMatrixBwd(
+ struct _icc *icp,
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent */
+ icmLookupFunc func /* Functionality requested */
+) {
+ return new_icmLuMatrix(icp, inSpace, outSpace, pcs, e_inSpace, e_outSpace, e_pcs,
+ intent, func, 1);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Forward and Backward Multi-Dimensional Interpolation type conversion */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+
+/* Components of overall lookup, in order */
+static int icmLuLut_in_abs(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ DBLLL(("icm in_abs: input %s\n",icmPdv(lut->inputChan, in)));
+ if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++) /* Don't alter input values */
+ out[i] = in[i];
+ }
+
+ /* If Bwd Lut, take care of Absolute color space and effective input space */
+ if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation)) {
+
+ if (p->e_inSpace == icSigLabData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm in_abs: after Lab2XYZ %s\n",icmPdv(lut->inputChan, out)));
+ }
+
+ /* Convert from Absolute to Relative colorimetric */
+ icmMulBy3x3(out, p->fromAbs, out);
+ DBLLL(("icm in_abs: after fromAbs %s\n",icmPdv(lut->inputChan, out)));
+
+ if (p->inSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm in_abs: after XYZ2Lab %s\n",icmPdv(lut->inputChan, out)));
+ }
+
+ } else {
+
+ /* Convert from Effective to Native input space */
+ if (p->e_inSpace == icSigLabData && p->inSpace == icSigXYZData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm in_abs: after Lab2XYZ %s\n",icmPdv(lut->inputChan, out)));
+ } else if (p->e_inSpace == icSigXYZData && p->inSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm in_abs: after XYZ2Lab %s\n",icmPdv(lut->inputChan, out)));
+ }
+ }
+ DBLLL(("icm in_abs: returning %s\n",icmPdv(lut->inputChan, out)));
+
+ return rv;
+}
+
+/* Possible matrix lookup */
+static int icmLuLut_matrix(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ if (p->usematrix)
+ rv |= lut->lookup_matrix(lut,out,in);
+ else if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++)
+ out[i] = in[i];
+ }
+ return rv;
+}
+
+/* Do input -> input' lookup */
+static int icmLuLut_input(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ p->in_normf(out, in); /* Normalize from input color space */
+ rv |= lut->lookup_input(lut,out,out); /* Lookup though input tables */
+ p->in_denormf(out,out); /* De-normalize to input color space */
+ return rv;
+}
+
+/* Do input'->output' lookup */
+static int icmLuLut_clut(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ double temp[MAX_CHAN];
+ int rv = 0;
+
+ p->in_normf(temp, in); /* Normalize from input color space */
+ rv |= p->lookup_clut(lut,out,temp); /* Lookup though clut tables */
+ p->out_denormf(out,out); /* De-normalize to output color space */
+ return rv;
+}
+
+/* Do output'->output lookup */
+static int icmLuLut_output(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ p->out_normf(out,in); /* Normalize from output color space */
+ rv |= lut->lookup_output(lut,out,out); /* Lookup though output tables */
+ p->out_denormf(out, out); /* De-normalize to output color space */
+ return rv;
+}
+
+static int icmLuLut_out_abs(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ DBLLL(("icm out_abs: input %s\n",icmPdv(lut->outputChan, in)));
+ if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->outputChan; i++) /* Don't alter input values */
+ out[i] = in[i];
+ }
+
+ /* If Fwd Lut, take care of Absolute color space */
+ /* and convert from native to effective out PCS */
+ if ((p->function == icmFwd || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation)) {
+
+ if (p->outSpace == icSigLabData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm out_abs: after Lab2XYZ %s\n",icmPdv(lut->outputChan, out)));
+ }
+
+ /* Convert from Relative to Absolute colorimetric XYZ */
+ icmMulBy3x3(out, p->toAbs, out);
+ DBLLL(("icm out_abs: after toAbs %s\n",icmPdv(lut->outputChan, out)));
+
+ if (p->e_outSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm out_abs: after XYZ2Lab %s\n",icmPdv(lut->outputChan, out)));
+ }
+ } else {
+
+ /* Convert from Native to Effective output space */
+ if (p->outSpace == icSigLabData && p->e_outSpace == icSigXYZData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm out_abs: after Lab2 %s\n",icmPdv(lut->outputChan, out)));
+ } else if (p->outSpace == icSigXYZData && p->e_outSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm out_abs: after XYZ2Lab %s\n",icmPdv(lut->outputChan, out)));
+ }
+ }
+ DBLLL(("icm out_abs: returning %s\n",icmPdv(lut->outputChan, out)));
+ return rv;
+}
+
+
+/* Overall lookup */
+static int
+icmLuLut_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+ double temp[MAX_CHAN];
+
+ DBGLL(("icmLuLut_lookup: in = %s\n", icmPdv(p->inputChan, in)));
+ rv |= p->in_abs(p,temp,in); /* Possible absolute conversion */
+ DBGLL(("icmLuLut_lookup: in_abs = %s\n", icmPdv(p->inputChan, temp)));
+ if (p->usematrix) {
+ rv |= lut->lookup_matrix(lut,temp,temp);/* If XYZ, multiply by non-unity matrix */
+ DBGLL(("icmLuLut_lookup: matrix = %s\n", icmPdv(p->inputChan, temp)));
+ }
+ p->in_normf(temp, temp); /* Normalize for input color space */
+ DBGLL(("icmLuLut_lookup: norm = %s\n", icmPdv(p->inputChan, temp)));
+ rv |= lut->lookup_input(lut,temp,temp); /* Lookup though input tables */
+ DBGLL(("icmLuLut_lookup: input = %s\n", icmPdv(p->inputChan, temp)));
+ rv |= p->lookup_clut(lut,out,temp); /* Lookup though clut tables */
+ DBGLL(("icmLuLut_lookup: clut = %s\n", icmPdv(p->outputChan, out)));
+ rv |= lut->lookup_output(lut,out,out); /* Lookup though output tables */
+ DBGLL(("icmLuLut_lookup: output = %s\n", icmPdv(p->outputChan, out)));
+ p->out_denormf(out,out); /* Normalize for output color space */
+ DBGLL(("icmLuLut_lookup: denorm = %s\n", icmPdv(p->outputChan, out)));
+ rv |= p->out_abs(p,out,out); /* Possible absolute conversion */
+ DBGLL(("icmLuLut_lookup: out_abse = %s\n", icmPdv(p->outputChan, out)));
+
+ return rv;
+}
+
+#ifdef NEVER /* The following should be identical in effect to the above. */
+
+/* Overall lookup */
+static int
+icmLuLut_lookup (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int i, rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+ double temp[MAX_CHAN];
+
+ rv |= p->in_abs(p,temp,in);
+ rv |= p->matrix(p,temp,temp);
+ rv |= p->input(p,temp,temp);
+ rv |= p->clut(p,out,temp);
+ rv |= p->output(p,out,out);
+ rv |= p->out_abs(p,out,out);
+
+ return rv;
+}
+
+#endif /* NEVER */
+
+/* Three stage conversion */
+static int
+icmLuLut_lookup_in (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+
+ /* If in_abs() or matrix() are active, then we can't have a per component input curve */
+ if (((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->e_inSpace != p->inSpace)
+ || (p->usematrix)) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++)
+ out[i] = in[i];
+ } else {
+ rv |= p->input(p,out,in);
+ }
+ return rv;
+}
+
+static int
+icmLuLut_lookup_core (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+
+ /* If in_abs() or matrix() are active, then we have to do the per component input curve here */
+ if (((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->e_inSpace != p->inSpace)
+ || (p->usematrix)) {
+ double temp[MAX_CHAN];
+ rv |= p->in_abs(p,temp,in);
+ rv |= p->matrix(p,temp,temp);
+ rv |= p->input(p,temp,temp);
+ rv |= p->clut(p,out,temp);
+ } else {
+ rv |= p->clut(p,out,in);
+ }
+
+ /* If out_abs() is active, then we can't have do per component out curve here */
+ if (((p->function == icmFwd || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->outSpace != p->e_outSpace)) {
+ rv |= p->output(p,out,out);
+ rv |= p->out_abs(p,out,out);
+ }
+
+ return rv;
+}
+
+static int
+icmLuLut_lookup_out (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+
+ /* If out_abs() is active, then we can't have a per component out curve */
+ if (((p->function == icmFwd || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->outSpace != p->e_outSpace)) {
+ unsigned int i;
+ for (i = 0; i < lut->outputChan; i++)
+ out[i] = in[i];
+ } else {
+ rv |= p->output(p,out,in);
+ }
+
+ return rv;
+}
+
+/* Inverse three stage conversion (partly implemented) */
+static int
+icmLuLut_lookup_inv_in (
+icmLuBase *pp, /* This */
+double *out, /* Vector of output values */
+double *in /* Vector of input values */
+) {
+ int rv = 0;
+ icmLuLut *p = (icmLuLut *)pp;
+ icmLut *lut = p->lut;
+
+ /* If in_abs() or matrix() are active, then we can't have a per component input curve */
+ if (((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation))
+ || (p->e_inSpace != p->inSpace)
+ || (p->usematrix)) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++)
+ out[i] = in[i];
+ } else {
+ rv |= p->inv_input(p,out,in);
+ }
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Some components of inverse lookup, in order */
+/* ~~ should these be in icmLut (like all the fwd transforms)? */
+
+static int icmLuLut_inv_out_abs(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ DBLLL(("icm inv_out_abs: input %s\n",icmPdv(lut->outputChan, in)));
+ if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->outputChan; i++) /* Don't alter input values */
+ out[i] = in[i];
+ }
+
+ /* If Fwd Lut, take care of Absolute color space */
+ /* and convert from effective to native inverse output PCS */
+ /* OutSpace must be PCS: XYZ or Lab */
+ if ((p->function == icmFwd || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation)) {
+
+ if (p->e_outSpace == icSigLabData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm inv_out_abs: after Lab2XYZ %s\n",icmPdv(lut->outputChan, out)));
+ }
+
+ /* Convert from Absolute to Relative colorimetric */
+ icmMulBy3x3(out, p->fromAbs, out);
+ DBLLL(("icm inv_out_abs: after fromAbs %s\n",icmPdv(lut->outputChan, out)));
+
+ if (p->outSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm inv_out_abs: after XYZ2Lab %s\n",icmPdv(lut->outputChan, out)));
+ }
+
+ } else {
+
+ /* Convert from Effective to Native output space */
+ if (p->e_outSpace == icSigLabData && p->outSpace == icSigXYZData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm inv_out_abs: after Lab2XYZ %s\n",icmPdv(lut->outputChan, out)));
+ } else if (p->e_outSpace == icSigXYZData && p->outSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm inv_out_abs: after XYZ2Lab %s\n",icmPdv(lut->outputChan, out)));
+ }
+ }
+ return rv;
+}
+
+/* Do output->output' inverse lookup */
+static int icmLuLut_inv_output(icmLuLut *p, double *out, double *in) {
+ icc *icp = p->icp;
+ icmLut *lut = p->lut;
+ int i;
+ int rv = 0;
+
+ if (lut->rot[0].inited == 0) {
+ for (i = 0; i < lut->outputChan; i++) {
+ rv = icmTable_setup_bwd(icp, &lut->rot[i], lut->outputEnt,
+ lut->outputTable + i * lut->outputEnt);
+ if (rv != 0) {
+ sprintf(icp->err,"icc_Lut_inv_input: Malloc failure in inverse lookup init.");
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ p->out_normf(out,in); /* Normalize from output color space */
+ for (i = 0; i < lut->outputChan; i++) {
+ /* Reverse lookup though output tables */
+ rv |= icmTable_lookup_bwd(&lut->rot[i], &out[i], &out[i]);
+ }
+ p->out_denormf(out, out); /* De-normalize to output color space */
+ return rv;
+}
+
+/* No output' -> input inverse lookup. */
+/* This is non-trivial ! */
+
+/* Do input' -> input inverse lookup */
+static int icmLuLut_inv_input(icmLuLut *p, double *out, double *in) {
+ icc *icp = p->icp;
+ icmLut *lut = p->lut;
+ int i;
+ int rv = 0;
+
+ if (lut->rit[0].inited == 0) {
+ for (i = 0; i < lut->inputChan; i++) {
+ rv = icmTable_setup_bwd(icp, &lut->rit[i], lut->inputEnt,
+ lut->inputTable + i * lut->inputEnt);
+ if (rv != 0) {
+ sprintf(icp->err,"icc_Lut_inv_input: Malloc failure in inverse lookup init.");
+ return icp->errc = rv;
+ }
+ }
+ }
+
+ p->in_normf(out, in); /* Normalize from input color space */
+ for (i = 0; i < lut->inputChan; i++) {
+ /* Reverse lookup though input tables */
+ rv |= icmTable_lookup_bwd(&lut->rit[i], &out[i], &out[i]);
+ }
+ p->in_denormf(out,out); /* De-normalize to input color space */
+ return rv;
+}
+
+/* Possible inverse matrix lookup */
+static int icmLuLut_inv_matrix(icmLuLut *p, double *out, double *in) {
+ icc *icp = p->icp;
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ if (p->usematrix) {
+ double tt[3];
+ if (p->imx_valid == 0) {
+ if (icmInverse3x3(p->imx, lut->e) != 0) { /* Compute inverse */
+ sprintf(icp->err,"icc_new_iccLuMatrix: Matrix wasn't invertable");
+ icp->errc = 2;
+ return 2;
+ }
+ p->imx_valid = 1;
+ }
+ /* Matrix multiply */
+ tt[0] = p->imx[0][0] * in[0] + p->imx[0][1] * in[1] + p->imx[0][2] * in[2];
+ tt[1] = p->imx[1][0] * in[0] + p->imx[1][1] * in[1] + p->imx[1][2] * in[2];
+ tt[2] = p->imx[2][0] * in[0] + p->imx[2][1] * in[1] + p->imx[2][2] * in[2];
+ out[0] = tt[0], out[1] = tt[1], out[2] = tt[2];
+ } else if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++)
+ out[i] = in[i];
+ }
+ return rv;
+}
+
+static int icmLuLut_inv_in_abs(icmLuLut *p, double *out, double *in) {
+ icmLut *lut = p->lut;
+ int rv = 0;
+
+ DBLLL(("icm inv_in_abs: input %s\n",icmPdv(lut->inputChan, in)));
+ if (out != in) {
+ unsigned int i;
+ for (i = 0; i < lut->inputChan; i++) /* Don't alter input values */
+ out[i] = in[i];
+ }
+
+ /* If Bwd Lut, take care of Absolute color space, and */
+ /* convert from native to effective input space */
+ if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview)
+ && (p->intent == icAbsoluteColorimetric
+ || p->intent == icmAbsolutePerceptual
+ || p->intent == icmAbsoluteSaturation)) {
+
+ if (p->inSpace == icSigLabData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm inv_in_abs: after Lab2XYZ %s\n",icmPdv(lut->inputChan, out)));
+ }
+
+ /* Convert from Relative to Absolute colorimetric XYZ */
+ icmMulBy3x3(out, p->toAbs, out);
+ DBLLL(("icm inv_in_abs: after toAbs %s\n",icmPdv(lut->inputChan, out)));
+
+ if (p->e_inSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm inv_in_abs: after XYZ2Lab %s\n",icmPdv(lut->inputChan, out)));
+ }
+ } else {
+
+ /* Convert from Native to Effective input space */
+ if (p->inSpace == icSigLabData && p->e_inSpace == icSigXYZData) {
+ icmLab2XYZ(&p->pcswht, out, out);
+ DBLLL(("icm inv_in_abs: after Lab2XYZ %s\n",icmPdv(lut->inputChan, out)));
+ } else if (p->inSpace == icSigXYZData && p->e_inSpace == icSigLabData) {
+ icmXYZ2Lab(&p->pcswht, out, out);
+ DBLLL(("icm inv_in_abs: after XYZ2Lab %s\n",icmPdv(lut->inputChan, out)));
+ }
+ }
+ DBLLL(("icm inv_in_abs: returning %s\n",icmPdv(lut->inputChan, out)));
+ return rv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Return LuLut information */
+static void icmLuLut_get_info(
+ icmLuLut *p, /* this */
+ icmLut **lutp, /* Pointer to icc lut type */
+ icmXYZNumber *pcswhtp, /* Pointer to profile PCS white point */
+ icmXYZNumber *whitep, /* Pointer to profile absolute white point */
+ icmXYZNumber *blackp /* Pointer to profile absolute black point */
+) {
+ if (lutp != NULL)
+ *lutp = p->lut;
+ if (pcswhtp != NULL)
+ *pcswhtp = p->pcswht;
+ if (whitep != NULL)
+ *whitep = p->whitePoint;
+ if (blackp != NULL)
+ *blackp = p->blackPoint;
+}
+
+/* Get the native ranges for the LuLut */
+/* This is computed differently to the mono & matrix types, to */
+/* accurately take into account the different range for 8 bit Lab */
+/* lut type. The range returned for the effective PCS is not so accurate. */
+static void
+icmLuLut_get_lutranges (
+ struct _icmLuBase *pp,
+ double *inmin, double *inmax, /* Return maximum range of inspace values */
+ double *outmin, double *outmax /* Return maximum range of outspace values */
+) {
+ icmLuLut *p = (icmLuLut *)pp;
+ unsigned int i;
+
+ for (i = 0; i < p->lut->inputChan; i++) {
+ inmin[i] = 0.0; /* Normalized range of input space values */
+ inmax[i] = 1.0;
+ }
+ p->in_denormf(inmin,inmin); /* Convert to real colorspace range */
+ p->in_denormf(inmax,inmax);
+
+ /* Make sure min and max are so. */
+ for (i = 0; i < p->lut->inputChan; i++) {
+ if (inmin[i] > inmax[i]) {
+ double tt;
+ tt = inmin[i];
+ inmin[i] = inmax[i];
+ inmax[i] = tt;
+ }
+ }
+
+ for (i = 0; i < p->lut->outputChan; i++) {
+ outmin[i] = 0.0; /* Normalized range of output space values */
+ outmax[i] = 1.0;
+ }
+ p->out_denormf(outmin,outmin); /* Convert to real colorspace range */
+ p->out_denormf(outmax,outmax);
+
+ /* Make sure min and max are so. */
+ for (i = 0; i < p->lut->outputChan; i++) {
+ if (outmin[i] > outmax[i]) {
+ double tt;
+ tt = outmin[i];
+ outmin[i] = outmax[i];
+ outmax[i] = tt;
+ }
+ }
+}
+
+/* Get the effective (externaly visible) ranges for the LuLut */
+/* This will be accurate if there is no override, but only */
+/* aproximate if a PCS override is in place. */
+static void
+icmLuLut_get_ranges (
+ struct _icmLuBase *pp,
+ double *inmin, double *inmax, /* Return maximum range of inspace values */
+ double *outmin, double *outmax /* Return maximum range of outspace values */
+) {
+ icmLuLut *p = (icmLuLut *)pp;
+
+ /* Get the native ranges first */
+ icmLuLut_get_lutranges(pp, inmin, inmax, outmin, outmax);
+
+ /* And replace them if the effective space is different */
+ if (p->e_inSpace != p->inSpace)
+ getRange(p->icp, p->e_inSpace, p->lut->ttype, inmin, inmax);
+
+ if (p->e_outSpace != p->outSpace)
+ getRange(p->icp, p->e_outSpace, p->lut->ttype, outmin, outmax);
+}
+
+/* Return the underlying Lut matrix */
+static void
+icmLuLut_get_matrix (
+ struct _icmLuLut *p,
+ double m[3][3]
+) {
+ int i, j;
+ icmLut *lut = p->lut;
+
+ if (p->usematrix) {
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ m[i][j] = lut->e[i][j]; /* Copy from Lut */
+
+ } else { /* return unity matrix */
+ icmSetUnity3x3(m);
+ }
+}
+
+
+static void
+icmLuLut_delete(
+icmLuBase *p
+) {
+ icc *icp = p->icp;
+
+ icp->al->free(icp->al, p);
+}
+
+icmLuBase *
+icc_new_icmLuLut(
+ icc *icp,
+ icTagSignature ttag, /* Target Lut tag */
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS (from header) */
+ icColorSpaceSignature e_inSpace, /* Effective Input color space */
+ icColorSpaceSignature e_outSpace, /* Effective Output color space */
+ icColorSpaceSignature e_pcs, /* Effective PCS */
+ icRenderingIntent intent, /* Rendering intent (For absolute) */
+ icmLookupFunc func /* Functionality requested (for icmLuSpaces()) */
+) {
+ icmLuLut *p;
+
+ if ((p = (icmLuLut *) icp->al->calloc(icp->al,1,sizeof(icmLuLut))) == NULL)
+ return NULL;
+ p->ttype = icmLutType;
+ p->icp = icp;
+ p->del = icmLuLut_delete;
+ p->lutspaces= icmLutSpaces;
+ p->spaces = icmLuSpaces;
+ p->XYZ_Rel2Abs = icmLuXYZ_Rel2Abs;
+ p->XYZ_Abs2Rel = icmLuXYZ_Abs2Rel;
+ p->init_wh_bk = icmLuInit_Wh_bk;
+ p->wh_bk_points = icmLuWh_bk_points;
+ p->lu_wh_bk_points = icmLuLu_wh_bk_points;
+
+ p->lookup = icmLuLut_lookup;
+ p->lookup_in = icmLuLut_lookup_in;
+ p->lookup_core = icmLuLut_lookup_core;
+ p->lookup_out = icmLuLut_lookup_out;
+ p->lookup_inv_in = icmLuLut_lookup_inv_in;
+
+ p->in_abs = icmLuLut_in_abs;
+ p->matrix = icmLuLut_matrix;
+ p->input = icmLuLut_input;
+ p->clut = icmLuLut_clut;
+ p->output = icmLuLut_output;
+ p->out_abs = icmLuLut_out_abs;
+
+ p->inv_in_abs = icmLuLut_inv_in_abs;
+ p->inv_matrix = icmLuLut_inv_matrix;
+ p->inv_input = icmLuLut_inv_input;
+ p->inv_output = icmLuLut_inv_output;
+ p->inv_out_abs = icmLuLut_inv_out_abs;
+
+ p->pcswht = icp->header->illuminant;
+ p->intent = intent; /* used to trigger absolute processing */
+ p->function = func;
+ p->inSpace = inSpace;
+ p->outSpace = outSpace;
+ p->pcs = pcs;
+ p->e_inSpace = e_inSpace;
+ p->e_outSpace = e_outSpace;
+ p->e_pcs = e_pcs;
+ p->get_info = icmLuLut_get_info;
+ p->get_lutranges = icmLuLut_get_lutranges;
+ p->get_ranges = icmLuLut_get_ranges;
+ p->get_matrix = icmLuLut_get_matrix;
+
+ /* Lookup the white and black points */
+ if (p->init_wh_bk((icmLuBase *)p)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Get the Lut tag, & check that it is expected type */
+ if ((p->lut = (icmLut *)icp->read_tag(icp, ttag)) == NULL
+ || (p->lut->ttype != icSigLut8Type && p->lut->ttype != icSigLut16Type)) {
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Check if matrix should be used */
+ if (inSpace == icSigXYZData && p->lut->nu_matrix(p->lut))
+ p->usematrix = 1;
+ else
+ p->usematrix = 0;
+
+ /* Lookup input color space to normalized index function */
+ if (getNormFunc(icp, inSpace, p->lut->ttype, icmToLuti, &p->in_normf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Lookup normalized index to input color space function */
+ if (getNormFunc(icp, inSpace, p->lut->ttype, icmFromLuti, &p->in_denormf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Lookup output color space to normalized Lut entry value function */
+ if (getNormFunc(icp, outSpace, p->lut->ttype, icmToLutv, &p->out_normf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Lookup normalized Lut entry value to output color space function */
+ if (getNormFunc(icp, outSpace, p->lut->ttype, icmFromLutv, &p->out_denormf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Note that the following two are only used in computing the expected */
+ /* value ranges of the effective PCS. This might not be the best way of */
+ /* doing this. */
+ /* Lookup normalized index to effective input color space function */
+ if (getNormFunc(icp, e_inSpace, p->lut->ttype, icmFromLuti, &p->e_in_denormf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown effective colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+ /* Lookup normalized Lut entry value to effective output color space function */
+ if (getNormFunc(icp, e_outSpace, p->lut->ttype, icmFromLutv, &p->e_out_denormf)) {
+ sprintf(icp->err,"icc_get_luobj: Unknown effective colorspace");
+ icp->errc = 1;
+ p->del((icmLuBase *)p);
+ return NULL;
+ }
+
+// ~~~999
+#ifndef NEVER
+ /* Standard code */
+ /* Determine appropriate clut lookup algorithm */
+ {
+ int use_sx; /* -1 = undecided, 0 = N-linear, 1 = Simplex lookup */
+ icColorSpaceSignature ins, outs; /* In and out Lut color spaces */
+ int inn, outn; /* in and out number of Lut components */
+
+ p->lutspaces((icmLuBase *)p, &ins, &inn, &outs, &outn, NULL);
+
+ /* Determine if the input space is "Device" like, */
+ /* ie. luminance will be expected to vary most strongly */
+ /* with the diagonal change in input coordinates. */
+ switch(ins) {
+
+ /* Luminence is carried by the sum of all the output channels, */
+ /* so output luminence will dominantly be in diagonal direction. */
+ case icSigXYZData: /* This seems to be appropriate ? */
+ case icSigRgbData:
+ case icSigGrayData:
+ case icSigCmykData:
+ case icSigCmyData:
+ case icSigMch6Data:
+ use_sx = 1; /* Simplex interpolation is appropriate */
+ break;
+
+ /* A single channel carries the luminence information */
+ case icSigLabData:
+ case icSigLuvData:
+ case icSigYCbCrData:
+ case icSigYxyData:
+ case icSigHlsData:
+ case icSigHsvData:
+ use_sx = 0; /* N-linear interpolation is appropriate */
+ break;
+ default:
+ use_sx = -1; /* undecided */
+ break;
+ }
+
+ /* If we couldn't figure it out from the input space, */
+ /* check output luminance variation with a diagonal input */
+ /* change. */
+ if (use_sx == -1) {
+ int lc; /* Luminance channel */
+
+ /* Determine where the luminence is carried in the output */
+ switch(outs) {
+
+ /* Luminence is carried by the sum of all the output channels */
+ case icSigRgbData:
+ case icSigGrayData:
+ case icSigCmykData:
+ case icSigCmyData:
+ case icSigMch6Data:
+ lc = -1; /* Average all channels */
+ break;
+
+ /* A single channel carries the luminence information */
+ case icSigLabData:
+ case icSigLuvData:
+ case icSigYCbCrData:
+ case icSigYxyData:
+ lc = 0;
+ break;
+
+ case icSigXYZData:
+ case icSigHlsData:
+ lc = 1;
+ break;
+
+ case icSigHsvData:
+ lc = 2;
+ break;
+
+ /* default means give up and use N-linear type lookup */
+ default:
+ lc = -2;
+ break;
+ }
+
+ /* If we know how luminance is represented in output space */
+ if (lc != -2) {
+ double tout1[MAX_CHAN]; /* Test output values */
+ double tout2[MAX_CHAN];
+ double tt, diag;
+ int n;
+
+ /* Determine input space location of min and max of */
+ /* given output channel (chan = -1 means average of all) */
+ p->lut->min_max(p->lut, tout1, tout2, lc);
+
+ /* Convert to vector and then calculate normalized */
+ /* dot product with diagonal vector (1,1,1...) */
+ for (tt = 0.0, n = 0; n < inn; n++) {
+ tout1[n] = tout2[n] - tout1[n];
+ tt += tout1[n] * tout1[n];
+ }
+ if (tt > 0.0)
+ tt = sqrt(tt); /* normalizing factor for maximum delta */
+ else
+ tt = 1.0; /* Hmm. */
+ tt *= sqrt((double)inn); /* Normalizing factor for diagonal vector */
+ for (diag = 0.0, n = 0; n < outn; n++)
+ diag += tout1[n] / tt;
+ diag = fabs(diag);
+
+ /* I'm not really convinced that this is a reliable */
+ /* indicator of whether simplex interpolation should be used ... */
+ /* It does seem to do the right thing with YCC space though. */
+ if (diag > 0.8) /* Diagonal is dominant ? */
+ use_sx = 1;
+
+ /* If we couldn't figure it out, use N-linear interpolation */
+ if (use_sx == -1)
+ use_sx = 0;
+ }
+ }
+
+ if (use_sx) {
+ p->lookup_clut = p->lut->lookup_clut_sx;
+ } else {
+ p->lookup_clut = p->lut->lookup_clut_nl;
+ }
+ }
+#else /* Development code */
+ /* Determine appropriate clut lookup algorithm, */
+ /* and set optimised simplex tables */
+ {
+ int lualg = 0; /* 0 = simplex, 1 = multi-linear, 2 = optimised simlpex */
+ icColorSpaceSignature ins, outs; /* In and out Lut color spaces */
+ int inn, outn; /* in and out number of Lut components */
+
+ p->lutspaces((icmLuBase *)p, &ins, &inn, &outs, &outn, NULL);
+
+ /* Determine which type of Lut lookup algorithm is likely to give */
+ /* minimal interpolation errors. */
+ /* To use the optimised simplex, we should ideally determine */
+ /* the simplex cell orientation that makes the simplex split diagonal */
+ /* always point towards the white or black points. */
+ switch(ins) {
+
+ case icSigXYZData: /* This seems to be appropriate ? */
+ case icSigRgbData:
+ case icSigGrayData:
+ case icSigCmykData:
+ case icSigCmyData:
+ case icSigMch6Data:
+ lualg = 0; /* Simplex interpolation is appropriate */
+ break;
+
+ case icSigLabData:
+// ~~~~9999 this isn't right! need to lookup Lab 50,0,0 through input curves then */
+/* quantize to clut nodes to figure threshold for axis flips */
+ p->lut->finfo[0].fth = 0.5; p->lut->finfo[0].bthff = 0; p->lut->finfo[0].athff = 1;
+ p->lut->finfo[1].fth = 0.5; p->lut->finfo[1].bthff = 1; p->lut->finfo[1].athff = 0;
+ p->lut->finfo[2].fth = 0.5; p->lut->finfo[2].bthff = 1; p->lut->finfo[2].athff = 0;
+ lualg = 2;
+ break;
+
+ /* !!! Should switch to optimised simplex for these too !!! */
+ case icSigLuvData:
+ case icSigYCbCrData:
+ case icSigYxyData:
+ case icSigHlsData:
+ case icSigHsvData:
+ lualg = 1; /* Multi-linear is best as a default for these ? */
+ break;
+
+ default:
+ lualg = 0; /* Simplex is best if we known nothing. */
+ break;
+ }
+
+ if (lualg == 2) {
+ p->lookup_clut = icmLut_lookup_clut_osx;
+ } else if (lualg == 1) {
+ p->lookup_clut = p->lut->lookup_clut_nl;
+ } else {
+ p->lookup_clut = p->lut->lookup_clut_sx;
+ }
+ }
+#endif
+ return (icmLuBase *)p;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Return an appropriate lookup object */
+/* Return NULL on error, and detailed error in icc */
+static icmLuBase* icc_get_luobj (
+ icc *p, /* ICC */
+ icmLookupFunc func, /* Conversion functionality */
+ icRenderingIntent intent, /* Rendering intent, including icmAbsoluteColorimetricXYZ */
+ icColorSpaceSignature pcsor,/* PCS override (0 = def) */
+ icmLookupOrder order /* Conversion representation search Order */
+) {
+ icmLuBase *luobj = NULL; /* Lookup object to return */
+ icColorSpaceSignature pcs, e_pcs; /* PCS and effective PCS */
+
+#ifdef ICM_STRICT
+ int rv;
+ /* Check that the profile is legal, since we depend on it ? */
+ if ((rv = check_icc_legal(p)) != 0)
+ return NULL;
+#endif /* ICM_STRICT */
+
+ /* Figure out the native and effective PCS */
+ e_pcs = pcs = p->header->pcs;
+ if (pcsor != icmSigDefaultData)
+ e_pcs = pcsor; /* Override */
+
+ /* How we expect to execute the request depends firstly on the type of profile */
+ switch (p->header->deviceClass) {
+ case icSigInputClass:
+ case icSigDisplayClass:
+ case icSigColorSpaceClass:
+
+ /* Look for Intent based AToBX profile + optional BToAX reverse */
+ /* or for AToB0 based profile + optional BToA0 reverse */
+ /* or three component matrix profile (reversable) */
+ /* or momochrome table profile (reversable) */
+ /* No Lut intent for ICC < V2.4, but possible for >= V2.4, */
+ /* so fall back if we can't find the chosen Lut intent.. */
+ /* Device <-> PCS */
+ /* Determine the algorithm and set its parameters */
+
+ switch (func) {
+ icRenderingIntent fbintent; /* Fallback intent */
+ icTagSignature ttag, fbtag;
+
+ case icmFwd: /* Device to PCS */
+ if (intent == icmDefaultIntent)
+ intent = icPerceptual; /* Make this the default */
+
+ switch (intent) {
+ case icAbsoluteColorimetric:
+ ttag = icSigAToB1Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = intent;
+ break;
+ case icRelativeColorimetric:
+ ttag = icSigAToB1Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icPerceptual:
+ ttag = icSigAToB0Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icSaturation:
+ ttag = icSigAToB2Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ ttag = icSigAToB0Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = intent;
+ break;
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ ttag = icSigAToB2Tag;
+ fbtag = icSigAToB0Tag;
+ fbintent = intent;
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (order != icmLuOrdRev) {
+ /* Try Lut type lookup with the chosen intent first */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try the fallback tag */
+ if ((luobj = icc_new_icmLuLut(p, fbtag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ fbintent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ } else {
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try Lut type lookup last */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try the fallback tag */
+ if ((luobj = icc_new_icmLuLut(p, fbtag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ fbintent, func)) != NULL)
+ break;
+ }
+ break;
+
+ case icmBwd: /* PCS to Device */
+ if (intent == icmDefaultIntent)
+ intent = icPerceptual; /* Make this the default */
+
+ switch (intent) {
+ case icAbsoluteColorimetric:
+ ttag = icSigBToA1Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = intent;
+ break;
+ case icRelativeColorimetric:
+ ttag = icSigBToA1Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icPerceptual:
+ ttag = icSigBToA0Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icSaturation:
+ ttag = icSigBToA2Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = icmDefaultIntent;
+ break;
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ ttag = icSigBToA0Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = intent;
+ break;
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ ttag = icSigBToA2Tag;
+ fbtag = icSigBToA0Tag;
+ fbintent = intent;
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+
+ if (order != icmLuOrdRev) {
+ /* Try Lut type lookup first */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try the fallback Lut */
+ if ((luobj = icc_new_icmLuLut(p, fbtag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ fbintent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+ } else {
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try Lut type lookup last */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try the fallback Lut */
+ if ((luobj = icc_new_icmLuLut(p, fbtag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ fbintent, func)) != NULL)
+ break;
+ }
+ break;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
+ p->errc = 1;
+ return NULL;
+ }
+ break;
+
+ case icSigOutputClass:
+ /* Expect BToA Lut and optional AToB Lut, All intents, expect gamut */
+ /* or momochrome table profile (reversable) */
+ /* Device <-> PCS */
+ /* Gamut Lut - no intent */
+ /* Optional preview links PCS <-> PCS */
+
+ /* Determine the algorithm and set its parameters */
+ switch (func) {
+ icTagSignature ttag;
+
+ case icmFwd: /* Device to PCS */
+
+ if (intent == icmDefaultIntent)
+ intent = icPerceptual; /* Make this the default */
+
+ switch (intent) {
+ case icRelativeColorimetric:
+ case icAbsoluteColorimetric:
+ ttag = icSigAToB1Tag;
+ break;
+ case icPerceptual:
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ ttag = icSigAToB0Tag;
+ break;
+ case icSaturation:
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ ttag = icSigAToB2Tag;
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (order != icmLuOrdRev) {
+ /* Try Lut type lookup first */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL) {
+ break;
+ }
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+ } else {
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixFwd(p,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try Lut type lookup last */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, e_pcs, e_pcs,
+ intent, func)) != NULL)
+ break;
+ }
+ break;
+
+ case icmBwd: /* PCS to Device */
+
+ if (intent == icmDefaultIntent)
+ intent = icPerceptual; /* Make this the default */
+
+ switch (intent) {
+ case icRelativeColorimetric:
+ case icAbsoluteColorimetric:
+ ttag = icSigBToA1Tag;
+ break;
+ case icPerceptual:
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ ttag = icSigBToA0Tag;
+ break;
+ case icSaturation:
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ ttag = icSigBToA2Tag;
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (order != icmLuOrdRev) {
+ /* Try Lut type lookup first */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+ } else {
+ /* See if it could be a monochrome lookup */
+ if ((luobj = new_icmLuMonoBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* See if it could be a matrix lookup */
+ if ((luobj = new_icmLuMatrixBwd(p,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+
+ /* Try Lut type lookup last */
+ if ((luobj = icc_new_icmLuLut(p, ttag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, p->header->colorSpace, e_pcs,
+ intent, func)) != NULL)
+ break;
+ }
+ break;
+
+ case icmGamut: /* PCS to 1D */
+
+#ifdef ICM_STRICT /* Allow only default and absolute */
+ if (intent != icmDefaultIntent
+ && intent != icAbsoluteColorimetric) {
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Gamut table");
+ p->errc = 1;
+ return NULL;
+ }
+#else /* Be more forgiving */
+ switch (intent) {
+ case icAbsoluteColorimetric:
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ break;
+ case icmDefaultIntent:
+ case icRelativeColorimetric:
+ case icPerceptual:
+ case icSaturation:
+ intent = icmDefaultIntent; /* Make all other look like default */
+ break;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent (0x%x)",intent);
+ p->errc = 1;
+ return NULL;
+ }
+
+#endif
+ /* If the target tag exists, and it is a Lut */
+ luobj = icc_new_icmLuLut(p, icSigGamutTag,
+ pcs, icSigGrayData, pcs,
+ e_pcs, icSigGrayData, e_pcs,
+ intent, func);
+ break;
+
+ case icmPreview: /* PCS to PCS */
+
+ switch (intent) {
+ case icRelativeColorimetric:
+ ttag = icSigPreview1Tag;
+ break;
+ case icPerceptual:
+ ttag = icSigPreview0Tag;
+ break;
+ case icSaturation:
+ ttag = icSigPreview2Tag;
+ break;
+ case icAbsoluteColorimetric:
+ case icmAbsolutePerceptual: /* Special icclib intent */
+ case icmAbsoluteSaturation: /* Special icclib intent */
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for preview table");
+ p->errc = 1;
+ return NULL;
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown intent");
+ p->errc = 1;
+ return NULL;
+ }
+
+ /* If the target tag exists, and it is a Lut */
+ luobj = icc_new_icmLuLut(p, ttag,
+ pcs, pcs, pcs,
+ e_pcs, e_pcs, e_pcs,
+ intent, func);
+ break;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
+ p->errc = 1;
+ return NULL;
+ }
+ break;
+
+ case icSigLinkClass:
+ /* Expect AToB0 Lut and optional BToA0 Lut, One intent in header */
+ /* Device <-> Device */
+
+ if (intent != p->header->renderingIntent
+ && intent != icmDefaultIntent) {
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Link profile");
+ p->errc = 1;
+ return NULL;
+ }
+ intent = p->header->renderingIntent;
+
+ /* Determine the algorithm and set its parameters */
+ switch (func) {
+ case icmFwd: /* Device to PCS (== Device) */
+
+ luobj = icc_new_icmLuLut(p, icSigAToB0Tag,
+ p->header->colorSpace, pcs, pcs,
+ p->header->colorSpace, pcs, pcs,
+ intent, func);
+ break;
+
+ case icmBwd: /* PCS (== Device) to Device */
+
+ luobj = icc_new_icmLuLut(p, icSigBToA0Tag,
+ pcs, p->header->colorSpace, pcs,
+ pcs, p->header->colorSpace, pcs,
+ intent, func);
+ break;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
+ p->errc = 1;
+ return NULL;
+ }
+ break;
+
+ case icSigAbstractClass:
+ /* Expect AToB0 Lut and option BToA0 Lut, with either relative or absolute intent. */
+ /* PCS <-> PCS */
+ /* Determine the algorithm and set its parameters */
+
+ if (intent != icmDefaultIntent
+ && intent != icRelativeColorimetric
+ && intent != icAbsoluteColorimetric) {
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Abstract profile");
+ p->errc = 1;
+ return NULL;
+ }
+
+ switch (func) {
+ case icmFwd: /* PCS (== Device) to PCS */
+
+ luobj = icc_new_icmLuLut(p, icSigAToB0Tag,
+ p->header->colorSpace, pcs, pcs,
+ e_pcs, e_pcs, e_pcs,
+ intent, func);
+ break;
+
+ case icmBwd: /* PCS to PCS (== Device) */
+
+ luobj = icc_new_icmLuLut(p, icSigBToA0Tag,
+ pcs, p->header->colorSpace, pcs,
+ e_pcs, e_pcs, e_pcs,
+ intent, func);
+ break;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Inaproptiate function requested");
+ p->errc = 1;
+ return NULL;
+ }
+ break;
+
+ case icSigNamedColorClass:
+ /* Expect Name -> Device, Optional PCS */
+ /* and a reverse lookup would be useful */
+ /* (ie. PCS or Device coords to closest named color) */
+ /* ~~ to be implemented ~~ */
+
+ /* ~~ Absolute intent is valid for processing of */
+ /* PCS from named Colors. Also allow for e_pcs */
+ if (intent != icmDefaultIntent
+ && intent != icRelativeColorimetric
+ && intent != icAbsoluteColorimetric) {
+ sprintf(p->err,"icc_get_luobj: Intent is inappropriate for Named Color profile");
+ p->errc = 1;
+ return NULL;
+ }
+
+ sprintf(p->err,"icc_get_luobj: Named Colors not handled yet");
+ p->errc = 1;
+ return NULL;
+
+ default:
+ sprintf(p->err,"icc_get_luobj: Unknown profile class");
+ p->errc = 1;
+ return NULL;
+ }
+
+ if (luobj == NULL) {
+ sprintf(p->err,"icc_get_luobj: Unable to locate usable conversion");
+ p->errc = 1;
+ } else {
+ luobj->order = order;
+ }
+
+ return luobj;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Returns total ink limit and channel maximums. */
+/* Returns -1.0 if not applicable for this type of profile. */
+/* Returns -1.0 for grey, additive, or any profiles < 4 channels. */
+/* This is a place holder that uses a heuristic, */
+/* until there is a private or standard tag for this information */
+static double icm_get_tac( /* return TAC */
+icc *p,
+double *chmax, /* device return channel sums. May be NULL */
+void (*calfunc)(void *cntx, double *out, double *in), /* Optional calibration func. */
+void *cntx
+) {
+ icmHeader *rh = p->header;
+ icmLuBase *luo;
+ icmLuLut *ll;
+ icmLut *lut;
+ icColorSpaceSignature outs; /* Type of output space */
+ int inn, outn; /* Number of components */
+ icmLuAlgType alg; /* Type of lookup algorithm */
+ double tac = 0.0;
+ double max[MAX_CHAN]; /* Channel maximums */
+ int i, f;
+ unsigned int uf;
+ int size; /* Lut table size */
+ double *gp; /* Pointer to grid cube base */
+
+ /* If not something that can really have a TAC */
+ if (rh->deviceClass != icSigDisplayClass
+ && rh->deviceClass != icSigOutputClass
+ && rh->deviceClass != icSigLinkClass) {
+ return -1.0;
+ }
+
+ /* If not a suitable color space */
+ switch (rh->colorSpace) {
+ /* Not applicable */
+ case icSigXYZData:
+ case icSigLabData:
+ case icSigLuvData:
+ case icSigYCbCrData:
+ case icSigYxyData:
+ case icSigHsvData:
+ case icSigHlsData:
+ return -1.0;
+
+ /* Assume no limit */
+ case icSigGrayData:
+ case icSig2colorData:
+ case icSig3colorData:
+ case icSigRgbData:
+ return -1.0;
+
+ default:
+ break;
+ }
+
+ /* Get a PCS->device colorimetric lookup */
+ if ((luo = p->get_luobj(p, icmBwd, icRelativeColorimetric, icmSigDefaultData, icmLuOrdNorm)) == NULL) {
+ if ((luo = p->get_luobj(p, icmBwd, icmDefaultIntent, icmSigDefaultData, icmLuOrdNorm)) == NULL) {
+ return -1.0;
+ }
+ }
+
+ /* Get details of conversion (Arguments may be NULL if info not needed) */
+ luo->spaces(luo, NULL, &inn, &outs, &outn, &alg, NULL, NULL, NULL, NULL);
+
+ /* Assume any non-Lut type doesn't have a TAC */
+ if (alg != icmLutType) {
+ return -1.0;
+ }
+
+ ll = (icmLuLut *)luo;
+
+ /* We have a Lut type. Search the lut for the largest values */
+ for (f = 0; f < outn; f++)
+ max[f] = 0.0;
+
+ lut = ll->lut;
+ gp = lut->clutTable; /* Base of grid array */
+ size = sat_pow(lut->clutPoints,lut->inputChan);
+ for (i = 0; i < size; i++) {
+ double tot, vv[MAX_CHAN];
+
+ lut->lookup_output(lut,vv,gp); /* Lookup though output tables */
+ ll->out_denormf(vv,vv); /* Normalize for output color space */
+
+ if (calfunc != NULL)
+ calfunc(cntx, vv, vv); /* Apply device calibration */
+
+ for (tot = 0.0, uf = 0; uf < lut->outputChan; uf++) {
+ tot += vv[uf];
+ if (vv[uf] > max[uf])
+ max[uf] = vv[uf];
+ }
+ if (tot > tac)
+ tac = tot;
+ gp += lut->outputChan;
+ }
+
+ if (chmax != NULL) {
+ for (f = 0; f < outn; f++)
+ chmax[f] = max[f];
+ }
+
+ luo->del(luo);
+
+ return tac;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Create an empty object. Return NULL on error */
+icc *new_icc_a(
+icmAlloc *al /* Memory allocator */
+) {
+ unsigned int i;
+ icc *p;
+
+ if ((p = (icc *) al->calloc(al, 1,sizeof(icc))) == NULL) {
+ return NULL;
+ }
+ p->ver = 0; /* default is V2 profile */
+
+ p->al = al; /* Heap allocator */
+
+ p->get_rfp = icc_get_rfp;
+ p->set_version = icc_set_version;
+ p->get_size = icc_get_size;
+ p->read = icc_read;
+ p->read_x = icc_read_x;
+ p->write = icc_write;
+ p->write_x = icc_write_x;
+ p->dump = icc_dump;
+ p->del = icc_delete;
+ p->add_tag = icc_add_tag;
+ p->link_tag = icc_link_tag;
+ p->find_tag = icc_find_tag;
+ p->read_tag = icc_read_tag;
+ p->read_tag_any = icc_read_tag_any;
+ p->rename_tag = icc_rename_tag;
+ p->unread_tag = icc_unread_tag;
+ p->read_all_tags = icc_read_all_tags;
+ p->delete_tag = icc_delete_tag;
+ p->check_id = icc_check_id;
+ p->get_tac = icm_get_tac;
+ p->get_luobj = icc_get_luobj;
+ p->new_clutluobj = icc_new_icmLuLut;
+
+#if defined(__IBMC__) && defined(_M_IX86)
+ _control87(EM_UNDERFLOW, EM_UNDERFLOW);
+#endif
+
+ /* Allocate a header object */
+ if ((p->header = new_icmHeader(p)) == NULL) {
+ al->free(al, p);
+ return NULL;
+ }
+
+ /* Values that must be set before writing */
+ p->header->deviceClass = icMaxEnumClass;/* Type of profile - must be set! */
+ p->header->colorSpace = icMaxEnumData; /* Clr space of data - must be set! */
+ p->header->pcs = icMaxEnumData; /* PCS: XYZ or Lab - must be set! */
+ p->header->renderingIntent = icMaxEnumIntent; /* Rendering intent - must be set ! */
+
+ /* Values that should be set before writing */
+ p->header->manufacturer = -1; /* Dev manufacturer - should be set ! */
+ p->header->model = -1; /* Dev model number - should be set ! */
+ p->header->attributes.l = 0; /* ICC Device attributes - should set ! */
+ p->header->flags = 0; /* Embedding flags - should be set ! */
+
+ /* Values that may be set before writing */
+ p->header->attributes.h = 0; /* Dev Device attributes - may be set ! */
+ p->header->creator = str2tag("argl"); /* Profile creator - Argyll - may be set ! */
+
+ /* Init default values in header */
+ p->header->cmmId = str2tag("argl"); /* CMM for profile - Argyll CMM */
+ p->header->majv = 2; /* Current version 2.2.0 */
+ p->header->minv = 2;
+ p->header->bfv = 0;
+ setcur_DateTimeNumber(&p->header->date);/* Creation Date */
+ p->header->platform = icSigMicrosoft; /* Primary Platform */
+ p->header->illuminant = icmD50; /* Profile illuminant - D50 */
+
+ /* Values that will be created automatically */
+ for (i = 0; i < 16; i++)
+ p->header->id[i] = 0;
+
+ return p;
+}
+
+
+/* ---------------------------------------------------------- */
+/* Print an int vector to a string. */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPiv(int di, int *p) {
+ static char buf[5][MAX_CHAN * 16];
+ static int ix = 0;
+ int e;
+ char *bp;
+
+ if (++ix >= 5)
+ ix = 0;
+ bp = buf[ix];
+
+ if (di > MAX_CHAN)
+ di = MAX_CHAN; /* Make sure that buf isn't overrun */
+
+ for (e = 0; e < di; e++) {
+ if (e > 0)
+ *bp++ = ' ';
+ sprintf(bp, "%d", p[e]); bp += strlen(bp);
+ }
+ return buf[ix];
+}
+
+/* Print a double color vector to a string. */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPdv(int di, double *p) {
+ static char buf[5][MAX_CHAN * 16];
+ static int ix = 0;
+ int e;
+ char *bp;
+
+ if (++ix >= 5)
+ ix = 0;
+ bp = buf[ix];
+
+ if (di > MAX_CHAN)
+ di = MAX_CHAN; /* Make sure that buf isn't overrun */
+
+ for (e = 0; e < di; e++) {
+ if (e > 0)
+ *bp++ = ' ';
+ sprintf(bp, "%f", p[e]); bp += strlen(bp);
+ }
+ return buf[ix];
+}
+
+/* Print a float color vector to a string. */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPfv(int di, float *p) {
+ static char buf[5][MAX_CHAN * 16];
+ static int ix = 0;
+ int e;
+ char *bp;
+
+ if (++ix >= 5)
+ ix = 0;
+ bp = buf[ix];
+
+ if (di > MAX_CHAN)
+ di = MAX_CHAN; /* Make sure that buf isn't overrun */
+
+ for (e = 0; e < di; e++) {
+ if (e > 0)
+ *bp++ = ' ';
+ sprintf(bp, "%f", p[e]); bp += strlen(bp);
+ }
+ return buf[ix];
+}
+
+/* Print an 0..1 range XYZ as a D50 Lab string */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPLab(double *p) {
+ static char buf[5][MAX_CHAN * 16];
+ static int ix = 0;
+ int e;
+ char *bp;
+ double lab[3];
+
+ if (++ix >= 5)
+ ix = 0;
+ bp = buf[ix];
+
+ icmXYZ2Lab(&icmD50, lab, p);
+
+ for (e = 0; e < 3; e++) {
+ if (e > 0)
+ *bp++ = ' ';
+ sprintf(bp, "%f", lab[e]); bp += strlen(bp);
+ }
+ return buf[ix];
+}
+
+
+/* ---------------------------------------------------------- */
+
diff --git a/icc/icc.h b/icc/icc.h
new file mode 100644
index 0000000..7c3a65b
--- /dev/null
+++ b/icc/icc.h
@@ -0,0 +1,1992 @@
+#ifndef ICC_H
+#define ICC_H
+
+/*
+ * International Color Consortium Format Library (icclib)
+ *
+ * Author: Graeme W. Gill
+ * Date: 1999/11/29
+ * Version: 2.15
+ *
+ * Copyright 1997 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/* We can get some subtle errors if certain headers aren't included */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <math.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Version of icclib release */
+
+#define ICCLIB_VERSION 0x020016
+#define ICCLIB_VERSION_STR "2.16"
+
+#undef ENABLE_V4 /* V4 is not fully implemented */
+
+/*
+ * Note XYZ scaling to 1.0, not 100.0
+ */
+
+#undef ICC_DEBUG_MALLOC /* Turns on partial support for filename and linenumber capture */
+
+/* Make allowance for shared library use */
+#ifdef ICCLIB_SHARED /* Compiling or Using shared library version */
+# ifdef ICCLIB_EXPORTS /* Compiling shared library */
+# ifdef NT
+# define ICCLIB_API __declspec(dllexport)
+# endif /* NT */
+# else /* Using shared library */
+# ifdef NT
+# define ICCLIB_API __declspec(dllimport)
+# ifdef ICCLIB_DEBUG
+# pragma comment (lib, "icclibd.lib")
+# else
+# pragma comment (lib, "icclib.lib")
+# endif /* DEBUG */
+# endif /* NT */
+# endif
+#else /* Using static library */
+# define ICCLIB_API /* empty */
+#endif
+
+/* =========================================================== */
+/* Platform specific primitive defines. */
+/* This really needs checking for each different platform. */
+/* Using C99 and MSC covers a lot of cases, */
+/* and the fallback default is pretty reliable with modern compilers and machines. */
+
+/* Note that the code assume that int is at least 32 bits, */
+/* and avoid using long as it is uncertain whether this is */
+/* the same size as an int or larger, opening the scope */
+/* for oveflow errors. */
+
+#if UINT_MAX < 0xffffffff
+# error "icclib: integer size is too small, must be at least 32 bit"
+#endif
+
+#ifndef ORD32 /* If not defined elsewhere */
+
+#if (__STDC_VERSION__ >= 199901L) /* C99 */
+
+#include <stdint.h>
+
+#define INR8 int8_t /* 8 bit signed */
+#define INR16 int16_t /* 16 bit signed */
+#define INR32 int32_t /* 32 bit signed */
+#define INR64 int64_t /* 64 bit signed - not used in icclib */
+#define ORD8 uint8_t /* 8 bit unsigned */
+#define ORD16 uint16_t /* 16 bit unsigned */
+#define ORD32 uint32_t /* 32 bit unsigned */
+#define ORD64 uint64_t /* 64 bit unsigned - not used in icclib */
+
+#define PNTR intptr_t
+
+/* printf format precision specifier */
+#define PF64PREC "ll"
+
+/* Constant precision specifier */
+#define CF64PREC "LL"
+
+#else /* !__STDC_VERSION__ */
+#ifdef _MSC_VER
+
+#define INR8 __int8 /* 8 bit signed */
+#define INR16 __int16 /* 16 bit signed */
+#define INR32 __int32 /* 32 bit signed */
+#define INR64 __int64 /* 64 bit signed - not used in icclib */
+#define ORD8 unsigned __int8 /* 8 bit unsigned */
+#define ORD16 unsigned __int16 /* 16 bit unsigned */
+#define ORD32 unsigned __int32 /* 32 bit unsigned */
+#define ORD64 unsigned __int64 /* 64 bit unsigned - not used in icclib */
+
+#define PNTR UINT_PTR
+
+#define vsnprintf _vsnprintf
+
+/* printf format precision specifier */
+#define PF64PREC "I64"
+
+/* Constant precision specifier */
+#define CF64PREC "LL"
+
+#else /* !_MSC_VER */
+
+/* The following works on a lot of modern systems, including */
+/* LP64 model 64 bit modes */
+
+#define INR8 signed char /* 8 bit signed */
+#define INR16 signed short /* 16 bit signed */
+#define INR32 signed int /* 32 bit signed */
+#define ORD8 unsigned char /* 8 bit unsigned */
+#define ORD16 unsigned short /* 16 bit unsigned */
+#define ORD32 unsigned int /* 32 bit unsigned */
+
+#ifdef __GNUC__
+#define INR64 long long /* 64 bit signed - not used in icclib */
+#define ORD64 unsigned long long /* 64 bit unsigned - not used in icclib */
+
+/* printf format precision specifier */
+#define PF64PREC "ll"
+
+#endif
+
+#define PNTR unsigned long
+
+#endif /* !_MSC_VER */
+#endif /* !__STDC_VERSION__ */
+#endif /* !defined(ORD32) */
+
+/* =========================================================== */
+#include "iccV42.h" /* ICC Version 4.2 definitions. */
+
+/* Note that the prefix icm is used for the native Machine */
+/* equivalents of the ICC binary file structures, and other icclib */
+/* specific definitions. */
+
+/* ---------------------------------------------- */
+/* System interface objects. The defaults can be replaced */
+/* for adaption to different system environments */
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Heap allocator class interface definition */
+#ifdef ICC_DEBUG_MALLOC
+
+#define ICM_ALLOC_BASE \
+ /* Public: */ \
+ \
+ void *(*dmalloc) (struct _icmAlloc *p, size_t size, char *file, int line); \
+ void *(*dcalloc) (struct _icmAlloc *p, size_t num, size_t size, char *file, int line); \
+ void *(*drealloc)(struct _icmAlloc *p, void *ptr, size_t size, char *file, int line); \
+ void (*dfree) (struct _icmAlloc *p, void *ptr, char *file, int line); \
+ \
+ /* we're done with the allocator object */ \
+ void (*del)(struct _icmAlloc *p); \
+
+#ifdef _ICC_C_ /* only inside icc.c */
+#define malloc( p, size ) dmalloc( p, size, __FILE__, __LINE__ )
+#define calloc( p, num, size ) dcalloc( p, num, size, __FILE__, __LINE__ )
+#define realloc( p, ptr, size ) drealloc( p, ptr, size, __FILE__, __LINE__ )
+#define free( p, ptr ) dfree( p, ptr , __FILE__, __LINE__ )
+#endif /* _ICC_C */
+
+#else /* !ICC_DEBUG_MALLOC */
+
+/* Heap allocator class interface definition */
+#define ICM_ALLOC_BASE \
+ /* Public: */ \
+ \
+ void *(*malloc) (struct _icmAlloc *p, size_t size); \
+ void *(*calloc) (struct _icmAlloc *p, size_t num, size_t size); \
+ void *(*realloc)(struct _icmAlloc *p, void *ptr, size_t size); \
+ void (*free) (struct _icmAlloc *p, void *ptr); \
+ \
+ /* we're done with the allocator object */ \
+ void (*del)(struct _icmAlloc *p); \
+
+#endif /* !ICC_DEBUG_MALLOC */
+
+/* Common heap allocator interface class */
+struct _icmAlloc {
+ ICM_ALLOC_BASE
+}; typedef struct _icmAlloc icmAlloc;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+
+/* Implementation of heap class based on standard system malloc */
+struct _icmAllocStd {
+ ICM_ALLOC_BASE
+}; typedef struct _icmAllocStd icmAllocStd;
+
+/* Create a standard alloc object */
+icmAlloc *new_icmAllocStd(void);
+
+/* File access class interface definition */
+#define ICM_FILE_BASE \
+ /* Public: */ \
+ \
+ /* Get the size of the file (Typically valid after read only) */ \
+ size_t (*get_size) (struct _icmFile *p); \
+ \
+ /* Set current position to offset. Return 0 on success, nz on failure. */ \
+ int (*seek) (struct _icmFile *p, unsigned int offset); \
+ \
+ /* Read count items of size length. Return number of items successfully read. */ \
+ size_t (*read) (struct _icmFile *p, void *buffer, size_t size, size_t count); \
+ \
+ /* write count items of size length. Return number of items successfully written. */ \
+ size_t (*write)(struct _icmFile *p, void *buffer, size_t size, size_t count); \
+ \
+ /* printf to the file */ \
+ int (*gprintf)(struct _icmFile *p, const char *format, ...); \
+ \
+ /* flush all write data out to secondary storage. Return nz on failure. */ \
+ int (*flush)(struct _icmFile *p); \
+ \
+ /* Return the memory buffer. Error if not icmFileMem */ \
+ int (*get_buf)(struct _icmFile *p, unsigned char **buf, size_t *len); \
+ \
+ /* we're done with the file object, return nz on failure */ \
+ int (*del)(struct _icmFile *p); \
+
+/* Common file interface class */
+struct _icmFile {
+ ICM_FILE_BASE
+}; typedef struct _icmFile icmFile;
+
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+
+/* These are avalailable if SEPARATE_STD is not defined: */
+
+/* Implementation of file access class based on standard file I/O */
+struct _icmFileStd {
+ ICM_FILE_BASE
+
+ /* Private: */
+ icmAlloc *al; /* Heap allocator */
+ int del_al; /* NZ if heap allocator should be deleted */
+ FILE *fp;
+ int doclose; /* nz if free should close */
+
+ /* Private: */
+ size_t size; /* Size of the file (For read) */
+
+}; typedef struct _icmFileStd icmFileStd;
+
+/* Create given a file name */
+icmFile *new_icmFileStd_name(char *name, char *mode);
+
+/* Create given a (binary) FILE* */
+icmFile *new_icmFileStd_fp(FILE *fp);
+
+/* Create given a file name with allocator */
+icmFile *new_icmFileStd_name_a(char *name, char *mode, icmAlloc *al);
+
+/* Create given a (binary) FILE* and allocator */
+icmFile *new_icmFileStd_fp_a(FILE *fp, icmAlloc *al);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Implementation of file access class based on a memory image */
+/* The buffer is assumed to be allocated with the given heap allocator */
+/* Pass base = NULL, length = 0 for no initial buffer */
+
+struct _icmFileMem {
+ ICM_FILE_BASE
+
+ /* Private: */
+ icmAlloc *al; /* Heap allocator */
+ int del_al; /* NZ if heap allocator should be deleted */
+ int del_buf; /* NZ if memory file buffer should be deleted */
+ unsigned char *start, *cur, *end, *aend;
+
+}; typedef struct _icmFileMem icmFileMem;
+
+/* Create a memory image file access class with given allocator */
+/* The buffer is not delete with the object. */
+icmFile *new_icmFileMem_a(void *base, size_t length, icmAlloc *al);
+
+/* Create a memory image file access class with given allocator */
+/* and delete buffer when icmFile is deleted. */
+icmFile *new_icmFileMem_ad(void *base, size_t length, icmAlloc *al);
+
+/* This is avalailable if SEPARATE_STD is not defined: */
+
+/* Create a memory image file access class */
+icmFile *new_icmFileMem(void *base, size_t length);
+
+/* Create a memory image file access class */
+/* and delete buffer when icmFile is deleted. */
+icmFile *new_icmFileMem_d(void *base, size_t length);
+
+/* --------------------------------- */
+/* Assumed constants */
+
+#define MAX_CHAN 15 /* Maximum number of color channels */
+
+/* --------------------------------- */
+/* tag and other compound structures */
+
+typedef int icmSig; /* Otherwise un-enumerated 4 byte signature */
+
+/* Non-standard Color Space Signatures - will be incompatible outside icclib! */
+
+/* A monochrome CIE L* space */
+#define icmSigLData ((icColorSpaceSignature) icmMakeTag('L',' ',' ',' '))
+
+/* A monochrome CIE Y space */
+#define icmSigYData ((icColorSpaceSignature) icmMakeTag('Y',' ',' ',' '))
+
+
+/* Pseudo Color Space Signatures - just used within icclib */
+
+/* Pseudo PCS colospace of profile */
+#define icmSigPCSData ((icColorSpaceSignature) icmMakeTag('P','C','S',' '))
+
+/* Pseudo PCS colospace to signal 8 bit Lab */
+#define icmSigLab8Data ((icColorSpaceSignature) icmMakeTag('L','a','b','8'))
+
+/* Pseudo PCS colospace to signal V2 16 bit Lab */
+#define icmSigLabV2Data ((icColorSpaceSignature) icmMakeTag('L','a','b','2'))
+
+/* Pseudo PCS colospace to signal V4 16 bit Lab */
+#define icmSigLabV4Data ((icColorSpaceSignature) icmMakeTag('L','a','b','4'))
+
+/* Pseudo PCS colospace to signal 8 bit L */
+#define icmSigL8Data ((icColorSpaceSignature) icmMakeTag('L',' ',' ','8'))
+
+/* Pseudo PCS colospace to signal V2 16 bit L */
+#define icmSigLV2Data ((icColorSpaceSignature) icmMakeTag('L',' ',' ','2'))
+
+/* Pseudo PCS colospace to signal V4 16 bit L */
+#define icmSigLV4Data ((icColorSpaceSignature) icmMakeTag('L',' ',' ','4'))
+
+/* Non-standard Platform Signature */
+#define icmSig_nix ((icPlatformSignature) icmMakeTag('*','n','i','x'))
+
+
+/* Alias for icSigColorantTableType found in LOGO profiles (Byte swapped clrt !) */
+#define icmSigAltColorantTableType ((icTagTypeSignature)icmMakeTag('t','r','l','c'))
+
+/* Tag Type signature, used to handle any tag that */
+/* isn't handled with a specific type object. */
+/* Also used in manufacturer & model header fields */
+/* Old: #define icmSigUnknownType ((icTagTypeSignature)icmMakeTag('?','?','?','?')) */
+#define icmSigUnknownType ((icTagTypeSignature) 0)
+
+
+typedef struct {
+ ORD32 l; /* High and low components of signed 64 bit */
+ INR32 h;
+} icmInt64;
+
+typedef struct {
+ ORD32 l,h; /* High and low components of unsigned 64 bit */
+} icmUint64;
+
+/* XYZ Number */
+typedef struct {
+ double X;
+ double Y;
+ double Z;
+} icmXYZNumber;
+
+/* Response 16 number */
+typedef struct {
+ double deviceValue; /* The device value in range 0.0 - 1.0 */
+ double measurement; /* The reading value */
+} icmResponse16Number;
+
+/*
+ * read and write method error codes:
+ * 0 = sucess
+ * 1 = file format/logistical error
+ * 2 = system error
+ */
+
+#define ICM_BASE_MEMBERS \
+ /* Private: */ \
+ icTagTypeSignature ttype; /* The tag type signature */ \
+ struct _icc *icp; /* Pointer to ICC we're a part of */ \
+ int touched; /* Flag for write bookeeping */ \
+ int refcount; /* Reference count for sharing */ \
+ unsigned int (*get_size)(struct _icmBase *p); \
+ int (*read)(struct _icmBase *p, unsigned int len, unsigned int of); \
+ int (*write)(struct _icmBase *p, unsigned int of); \
+ void (*del)(struct _icmBase *p); \
+ \
+ /* Public: */ \
+ void (*dump)(struct _icmBase *p, icmFile *op, int verb); \
+ int (*allocate)(struct _icmBase *p);
+
+/* Base tag element data object */
+struct _icmBase {
+ ICM_BASE_MEMBERS
+}; typedef struct _icmBase icmBase;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Tag type to hold an unknown tag type, */
+/* so that it can be read and copied. */
+struct _icmUnknown {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* size of data currently allocated */
+
+ /* Public: */
+ icTagTypeSignature uttype; /* The unknown tag type signature */
+ unsigned int size; /* Allocated and used size of the array */
+ unsigned char *data; /* tag data */
+}; typedef struct _icmUnknown icmUnknown;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* UInt8 Array */
+struct _icmUInt8Array {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+
+ /* Public: */
+ unsigned int size; /* Allocated and used size of the array */
+ unsigned int *data; /* Pointer to array of data */
+}; typedef struct _icmUInt8Array icmUInt8Array;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* uInt16 Array */
+struct _icmUInt16Array {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+
+ /* Public: */
+ unsigned int size; /* Allocated and used size of the array */
+ unsigned int *data; /* Pointer to array of data */
+}; typedef struct _icmUInt16Array icmUInt16Array;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* uInt32 Array */
+struct _icmUInt32Array {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+
+ /* Public: */
+ unsigned int size; /* Allocated and used size of the array */
+ unsigned int *data; /* Pointer to array of data */
+}; typedef struct _icmUInt32Array icmUInt32Array;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* UInt64 Array */
+struct _icmUInt64Array {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+
+ /* Public: */
+ unsigned int size; /* Allocated and used size of the array */
+ icmUint64 *data; /* Pointer to array of hight data */
+}; typedef struct _icmUInt64Array icmUInt64Array;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* u16Fixed16 Array */
+struct _icmU16Fixed16Array {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+
+ /* Public: */
+ unsigned int size; /* Allocated and used size of the array */
+ double *data; /* Pointer to array of hight data */
+}; typedef struct _icmU16Fixed16Array icmU16Fixed16Array;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* s15Fixed16 Array */
+struct _icmS15Fixed16Array {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+
+ /* Public: */
+ unsigned int size; /* Allocated and used size of the array */
+ double *data; /* Pointer to array of hight data */
+}; typedef struct _icmS15Fixed16Array icmS15Fixed16Array;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* XYZ Array */
+struct _icmXYZArray {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+
+ /* Public: */
+ unsigned int size; /* Allocated and used size of the array */
+ icmXYZNumber *data; /* Pointer to array of data */
+}; typedef struct _icmXYZArray icmXYZArray;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Curve */
+typedef enum {
+ icmCurveUndef = -1, /* Undefined curve */
+ icmCurveLin = 0, /* Linear transfer curve */
+ icmCurveGamma = 1, /* Gamma power transfer curve */
+ icmCurveSpec = 2 /* Specified curve */
+} icmCurveStyle;
+
+/* Curve reverse lookup information */
+typedef struct {
+ int inited; /* Flag */
+ double rmin, rmax; /* Range of reverse grid */
+ double qscale; /* Quantising scale factor */
+ int rsize; /* Number of reverse lists */
+ unsigned int **rlists; /* Array of list of fwd values that may contain output value */
+ /* Offset 0 = allocated size */
+ /* Offset 1 = next free index */
+ /* Offset 2 = first fwd index */
+ unsigned int size; /* Copy of forward table size */
+ double *data; /* Copy of forward table data */
+} icmRevTable;
+
+struct _icmCurve {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+ icmRevTable rt; /* Reverse table information */
+
+ /* Public: */
+ icmCurveStyle flag; /* Style of curve */
+ unsigned int size; /* Allocated and used size of the array */
+ double *data; /* Curve data scaled to range 0.0 - 1.0 */
+ /* or data[0] = gamma value */
+ /* Translate a value through the curve, return warning flags */
+ int (*lookup_fwd) (struct _icmCurve *p, double *out, double *in); /* Forwards */
+ int (*lookup_bwd) (struct _icmCurve *p, double *out, double *in); /* Backwards */
+
+}; typedef struct _icmCurve icmCurve;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Data */
+typedef enum {
+ icmDataUndef = -1, /* Undefined data */
+ icmDataASCII = 0, /* ASCII data */
+ icmDataBin = 1 /* Binary data */
+} icmDataStyle;
+
+struct _icmData {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+
+ /* Public: */
+ icmDataStyle flag; /* Style of data */
+ unsigned int size; /* Allocated and used size of the array (inc ascii null) */
+ unsigned char *data; /* data or string, NULL if size == 0 */
+}; typedef struct _icmData icmData;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* text */
+struct _icmText {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+
+ /* Public: */
+ unsigned int size; /* Allocated and used size of data, inc null */
+ char *data; /* ascii string (null terminated), NULL if size==0 */
+
+}; typedef struct _icmText icmText;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* The base date time number */
+struct _icmDateTimeNumber {
+ ICM_BASE_MEMBERS
+
+ /* Public: */
+ unsigned int year;
+ unsigned int month;
+ unsigned int day;
+ unsigned int hours;
+ unsigned int minutes;
+ unsigned int seconds;
+}; typedef struct _icmDateTimeNumber icmDateTimeNumber;
+
+#ifdef NEW
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* DeviceSettings */
+
+/*
+ I think this all works like this:
+
+Valid setting = ( (platform == platform1 and platform1.valid)
+ or (platform == platform2 and platform2.valid)
+ or ...
+ )
+
+where
+ platformN.valid = ( platformN.combination1.valid
+ or platformN.combination2.valid
+ or ...
+ )
+
+where
+ platformN.combinationM.valid = ( platformN.combinationM.settingstruct1.valid
+ and platformN.combinationM.settingstruct2.valid
+ and ...
+ )
+
+where
+ platformN.combinationM.settingstructP.valid = ( platformN.combinationM.settingstructP.setting1.valid
+ or platformN.combinationM.settingstructP.setting2.valid
+ or ...
+ )
+
+ */
+
+/* The Settings Structure holds an array of settings of a particular type */
+struct _icmSettingStruct {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _num; /* Size currently allocated */
+
+ /* Public: */
+ icSettingsSig settingSig; /* Setting identification */
+ unsigned int numSettings; /* number of setting values */
+ union { /* Setting values - type depends on Sig */
+ icUInt64Number *resolution;
+ icDeviceMedia *media;
+ icDeviceDither *halftone;
+ }
+}; typedef struct _icmSettingStruct icmSettingStruct;
+
+/* A Setting Combination holds all arrays of different setting types */
+struct _icmSettingComb {
+ /* Private: */
+ unsigned int _num; /* number currently allocated */
+
+ /* Public: */
+ unsigned int numStructs; /* num of setting structures */
+ icmSettingStruct *data;
+}; typedef struct _icmSettingComb icmSettingComb;
+
+/* A Platform Entry holds all setting combinations */
+struct _icmPlatformEntry {
+ /* Private: */
+ unsigned int _num; /* number currently allocated */
+
+ /* Public: */
+ icPlatformSignature platform;
+ unsigned int numCombinations; /* num of settings and allocated array size */
+ icmSettingComb *data;
+}; typedef struct _icmPlatformEntry icmPlatformEntry;
+
+/* The Device Settings holds all platform settings */
+struct _icmDeviceSettings {
+ /* Private: */
+ unsigned int _num; /* number currently allocated */
+
+ /* Public: */
+ unsigned int numPlatforms; /* num of platforms and allocated array size */
+ icmPlatformEntry *data; /* Array of pointers to platform entry data */
+}; typedef struct _icmDeviceSettings icmDeviceSettings;
+
+#endif /* NEW */
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* optimised simplex Lut lookup axis flipping structure */
+typedef struct {
+ double fth; /* Flip threshold */
+ char bthff; /* Below threshold flip flag value */
+ char athff; /* Above threshold flip flag value */
+} sx_flip_info;
+
+/* Set method flags */
+#define ICM_CLUT_SET_EXACT 0x0000 /* Set clut node values exactly from callback */
+#define ICM_CLUT_SET_APXLS 0x0001 /* Set clut node values to aproximate least squares fit */
+
+#define ICM_CLUT_SET_FILTER 0x0002 /* Post filter values (icmSetMultiLutTables() only) */
+
+
+/* lut */
+struct _icmLut {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ /* Cache appropriate normalization routines */
+ int dinc[MAX_CHAN]; /* Dimensional increment through clut (in doubles) */
+ int dcube[1 << MAX_CHAN]; /* Hyper cube offsets (in doubles) */
+ icmRevTable rit[MAX_CHAN]; /* Reverse input table information */
+ icmRevTable rot[MAX_CHAN]; /* Reverse output table information */
+ sx_flip_info finfo[MAX_CHAN]; /* Optimised simplex flip information */
+
+ unsigned int inputTable_size; /* size allocated to input table */
+ unsigned int clutTable_size; /* size allocated to clut table */
+ unsigned int outputTable_size; /* size allocated to output table */
+
+ /* Optimised simplex orientation information. oso_ffa is NZ if valid. */
+ /* Only valid if inputChan > 1 && clutPoints > 1 */
+ /* oso_ff[01] will be NULL if not valid */
+ unsigned short *oso_ffa; /* Flip flags for inputChan-1 dimensions, organised */
+ /* [inputChan 0, 0..cp-2]..[inputChan ic-2, 0..cp-2] */
+ unsigned short *oso_ffb; /* Flip flags for dimemension inputChan-1, organised */
+ /* [0..cp-2] */
+ int odinc[MAX_CHAN]; /* Dimensional increment through oso_ffa */
+
+ /* return the minimum and maximum values of the given channel in the clut */
+ void (*min_max) (struct _icmLut *pp, double *minv, double *maxv, int chan);
+
+ /* Translate color values through 3x3 matrix, input tables only, multi-dimensional lut, */
+ /* or output tables, */
+ int (*lookup_matrix) (struct _icmLut *pp, double *out, double *in);
+ int (*lookup_input) (struct _icmLut *pp, double *out, double *in);
+ int (*lookup_clut_nl) (struct _icmLut *pp, double *out, double *in);
+ int (*lookup_clut_sx) (struct _icmLut *pp, double *out, double *in);
+ int (*lookup_output) (struct _icmLut *pp, double *out, double *in);
+
+ /* Public: */
+
+ /* return non zero if matrix is non-unity */
+ int (*nu_matrix) (struct _icmLut *pp);
+
+ unsigned int inputChan; /* Num of input channels */
+ unsigned int outputChan; /* Num of output channels */
+ unsigned int clutPoints; /* Num of grid points */
+ unsigned int inputEnt; /* Num of in-table entries (must be 256 for Lut8) */
+ unsigned int outputEnt; /* Num of out-table entries (must be 256 for Lut8) */
+ double e[3][3]; /* 3 * 3 array */
+ double *inputTable; /* The in-table: [inputChan * inputEnt] */
+ double *clutTable; /* The clut: [(clutPoints ^ inputChan) * outputChan] */
+ double *outputTable; /* The out-table: [outputChan * outputEnt] */
+ /* inputTable is organized [inputChan 0..ic-1][inputEnt 0..ie-1] */
+ /* clutTable is organized [inputChan 0, 0..cp-1]..[inputChan ic-1, 0..cp-1]
+ [outputChan 0..oc-1] */
+ /* outputTable is organized [outputChan 0..oc-1][outputEnt 0..oe-1] */
+
+ /* Helper function to setup a Lut tables contents */
+ int (*set_tables) (
+ struct _icmLut *p, /* Pointer to Lut object */
+ int flags, /* Setting flags */
+ void *cbctx, /* Opaque callback context pointer value */
+ icColorSpaceSignature insig, /* Input color space */
+ icColorSpaceSignature outsig, /* Output color space */
+ void (*infunc)(void *cbctx, double *out, double *in),
+ /* Input transfer function, inspace->inspace' (NULL = default) */
+ double *inmin, double *inmax, /* Maximum range of inspace' values */
+ /* (NULL = default) */
+ void (*clutfunc)(void *cbntx, double *out, double *in),
+ /* inspace' -> outspace' transfer function */
+ double *clutmin, double *clutmax, /* Maximum range of outspace' values */
+ /* (NULL = default) */
+ void (*outfunc)(void *cbntx, double *out, double *in));
+ /* Output transfer function, outspace'->outspace (NULL = deflt) */
+
+}; typedef struct _icmLut icmLut;
+
+/* Helper function to set multiple Lut tables simultaneously. */
+/* Note that these tables all have to be compatible in */
+/* having the same configuration and resolutions, and the */
+/* same per channel input and output curves. */
+/* Set errc and return error number in underlying icc */
+/* Note that clutfunc in[] value has "index under". */
+/* If ICM_CLUT_SET_FILTER is set, the per grid node filtering radius */
+/* is returned in clutfunc out[-1], out[-2] etc for each table */
+int icmSetMultiLutTables(
+ int ntables, /* Number of tables to be set, 1..n */
+ struct _icmLut **p, /* Pointer to Lut object */
+ int flags, /* Setting flags */
+ void *cbctx, /* Opaque callback context pointer value */
+ icColorSpaceSignature insig, /* Input color space */
+ icColorSpaceSignature outsig, /* Output color space */
+ void (*infunc)(void *cbctx, double *out, double *in),
+ /* Input transfer function, inspace->inspace' (NULL = default) */
+ /* Will be called ntables times each input grid value */
+ double *inmin, double *inmax, /* Maximum range of inspace' values */
+ /* (NULL = default) */
+ void (*clutfunc)(void *cbntx, double *out, double *in),
+ /* inspace' -> outspace[ntables]' transfer function */
+ /* will be called once for each input' grid value, and */
+ /* ntables output values should be written consecutively */
+ /* to out[]. */
+ double *clutmin, double *clutmax, /* Maximum range of outspace' values */
+ /* (NULL = default) */
+ void (*outfunc)(void *cbntx, double *out, double *in)
+ /* Output transfer function, outspace'->outspace (NULL = deflt) */
+ /* Will be called ntables times on each output value */
+);
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Measurement Data */
+struct _icmMeasurement {
+ ICM_BASE_MEMBERS
+
+ /* Public: */
+ icStandardObserver observer; /* Standard observer */
+ icmXYZNumber backing; /* XYZ for backing */
+ icMeasurementGeometry geometry; /* Meas. geometry */
+ double flare; /* Measurement flare */
+ icIlluminant illuminant; /* Illuminant */
+}; typedef struct _icmMeasurement icmMeasurement;
+
+#ifdef NEW
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* MultiLocalizedUnicode */
+
+struct _icmMultiLocalizedUnicode {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ ~~~999
+
+ /* Public: */
+ ~~~999
+
+}; typedef struct _icmMultiLocalizedUnicode icmMultiLocalizedUnicode;
+
+#endif /* NEW */
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Named color */
+
+/* Structure that holds each named color data */
+typedef struct {
+ struct _icc *icp; /* Pointer to ICC we're a part of */
+ char root[32]; /* Root name for color */
+ double pcsCoords[3]; /* icmNC2: PCS coords of color */
+ double deviceCoords[MAX_CHAN]; /* Dev coords of color */
+} icmNamedColorVal;
+
+struct _icmNamedColor {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _count; /* Count currently allocated */
+
+ /* Public: */
+ unsigned int vendorFlag; /* Bottom 16 bits for IC use */
+ unsigned int count; /* Count of named colors */
+ unsigned int nDeviceCoords; /* Num of device coordinates */
+ char prefix[32]; /* Prefix for each color name (null terminated) */
+ char suffix[32]; /* Suffix for each color name (null terminated) */
+ icmNamedColorVal *data; /* Array of [count] color values */
+}; typedef struct _icmNamedColor icmNamedColor;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Colorant table */
+/* (Contribution from Piet Vandenborre, derived from NamedColor) */
+
+/* Structure that holds colorant table data */
+typedef struct {
+ struct _icc *icp; /* Pointer to ICC we're a part of */
+ char name[32]; /* Name for colorant */
+ double pcsCoords[3]; /* PCS coords of colorant */
+} icmColorantTableVal;
+
+struct _icmColorantTable {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _count; /* Count currently allocated */
+
+ /* Public: */
+ unsigned int count; /* Count of colorants */
+ icmColorantTableVal *data; /* Array of [count] colorants */
+}; typedef struct _icmColorantTable icmColorantTable;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* textDescription */
+struct _icmTextDescription {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _size; /* Size currently allocated */
+ unsigned int uc_size; /* uc Size currently allocated */
+ int (*core_read)(struct _icmTextDescription *p, char **bpp, char *end);
+ int (*core_write)(struct _icmTextDescription *p, char **bpp);
+
+ /* Public: */
+ unsigned int size; /* Allocated and used size of desc, inc null */
+ char *desc; /* ascii string (null terminated) */
+
+ unsigned int ucLangCode; /* UniCode language code */
+ unsigned int ucSize; /* Allocated and used size of ucDesc in wchars, inc null */
+ ORD16 *ucDesc; /* The UniCode description (null terminated) */
+
+ /* See <http://unicode.org/Public/MAPPINGS/VENDORS/APPLE/ReadMe.txt> */
+ ORD16 scCode; /* ScriptCode code */
+ unsigned int scSize; /* Used size of scDesc in bytes, inc null */
+ ORD8 scDesc[67]; /* ScriptCode Description (null terminated, max 67) */
+}; typedef struct _icmTextDescription icmTextDescription;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Profile sequence structure */
+struct _icmDescStruct {
+ /* Private: */
+ struct _icc *icp; /* Pointer to ICC we're a part of */
+
+ /* Public: */
+ int (*allocate)(struct _icmDescStruct *p); /* Allocate method */
+ icmSig deviceMfg; /* Dev Manufacturer */
+ unsigned int deviceModel; /* Dev Model */
+ icmUint64 attributes; /* Dev attributes */
+ icTechnologySignature technology; /* Technology sig */
+ icmTextDescription device; /* Manufacturer text (sub structure) */
+ icmTextDescription model; /* Model text (sub structure) */
+}; typedef struct _icmDescStruct icmDescStruct;
+
+/* Profile sequence description */
+struct _icmProfileSequenceDesc {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _count; /* number currently allocated */
+
+ /* Public: */
+ unsigned int count; /* Number of descriptions */
+ icmDescStruct *data; /* array of [count] descriptions */
+}; typedef struct _icmProfileSequenceDesc icmProfileSequenceDesc;
+
+#ifdef NEW
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* ResponseCurveSet16 */
+
+struct _icmResponseCurveSet16 {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ ~~~999
+
+ /* Public: */
+ ~~~999
+
+}; typedef struct _icmResponseCurveSet16 icmResponseCurveSet16;
+
+#endif /* NEW */
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* signature (only ever used for technology ??) */
+struct _icmSignature {
+ ICM_BASE_MEMBERS
+
+ /* Public: */
+ icTechnologySignature sig; /* Signature */
+}; typedef struct _icmSignature icmSignature;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Per channel Screening Data */
+typedef struct {
+ /* Public: */
+ double frequency; /* Frequency */
+ double angle; /* Screen angle */
+ icSpotShape spotShape; /* Spot Shape encodings below */
+} icmScreeningData;
+
+struct _icmScreening {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int _channels; /* number currently allocated */
+
+ /* Public: */
+ unsigned int screeningFlag; /* Screening flag */
+ unsigned int channels; /* Number of channels */
+ icmScreeningData *data; /* Array of screening data */
+}; typedef struct _icmScreening icmScreening;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Under color removal, black generation */
+struct _icmUcrBg {
+ ICM_BASE_MEMBERS
+
+ /* Private: */
+ unsigned int UCR_count; /* Currently allocated UCR count */
+ unsigned int BG_count; /* Currently allocated BG count */
+ unsigned int _size; /* Currently allocated string size */
+
+ /* Public: */
+ unsigned int UCRcount; /* Undercolor Removal Curve length */
+ double *UCRcurve; /* The array of UCR curve values, 0.0 - 1.0 */
+ /* or 0.0 - 100 % if count = 1 */
+ unsigned int BGcount; /* Black generation Curve length */
+ double *BGcurve; /* The array of BG curve values, 0.0 - 1.0 */
+ /* or 0.0 - 100 % if count = 1 */
+ unsigned int size; /* Allocated and used size of desc, inc null */
+ char *string; /* UcrBg description (null terminated) */
+}; typedef struct _icmUcrBg icmUcrBg;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* viewingConditionsType */
+struct _icmViewingConditions {
+ ICM_BASE_MEMBERS
+
+ /* Public: */
+ icmXYZNumber illuminant; /* In candelas per sq. meter */
+ icmXYZNumber surround; /* In candelas per sq. meter */
+ icIlluminant stdIlluminant; /* See icIlluminant defines */
+}; typedef struct _icmViewingConditions icmViewingConditions;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Postscript Color Rendering Dictionary names type */
+struct _icmCrdInfo {
+ ICM_BASE_MEMBERS
+ /* Private: */
+ unsigned int _ppsize; /* Currently allocated size */
+ unsigned int _crdsize[4]; /* Currently allocated sizes */
+
+ /* Public: */
+ unsigned int ppsize; /* Postscript product name size (including null) */
+ char *ppname; /* Postscript product name (null terminated) */
+ unsigned int crdsize[4]; /* Rendering intent 0-3 CRD names sizes (icluding null) */
+ char *crdname[4]; /* Rendering intent 0-3 CRD names (null terminated) */
+}; typedef struct _icmCrdInfo icmCrdInfo;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* Apple ColorSync 2.5 video card gamma type (vcgt) */
+struct _icmVideoCardGammaTable {
+ unsigned short channels; /* No of gamma channels (1 or 3) */
+ unsigned short entryCount; /* Number of entries per channel */
+ unsigned short entrySize; /* Size in bytes of each entry */
+ void *data; /* Variable size data */
+}; typedef struct _icmVideoCardGammaTable icmVideoCardGammaTable;
+
+struct _icmVideoCardGammaFormula {
+ unsigned short channels; /* No of gamma channels (always 3) */
+ double redGamma; /* must be > 0.0 */
+ double redMin; /* must be > 0.0 and < 1.0 */
+ double redMax; /* must be > 0.0 and < 1.0 */
+ double greenGamma; /* must be > 0.0 */
+ double greenMin; /* must be > 0.0 and < 1.0 */
+ double greenMax; /* must be > 0.0 and < 1.0 */
+ double blueGamma; /* must be > 0.0 */
+ double blueMin; /* must be > 0.0 and < 1.0 */
+ double blueMax; /* must be > 0.0 and < 1.0 */
+}; typedef struct _icmVideoCardGammaFormula icmVideoCardGammaFormula;
+
+typedef enum {
+ icmVideoCardGammaTableType = 0,
+ icmVideoCardGammaFormulaType = 1
+} icmVideoCardGammaTagType;
+
+struct _icmVideoCardGamma {
+ ICM_BASE_MEMBERS
+ icmVideoCardGammaTagType tagType; /* eg. table or formula, use above enum */
+ union {
+ icmVideoCardGammaTable table;
+ icmVideoCardGammaFormula formula;
+ } u;
+
+ double (*lookup)(struct _icmVideoCardGamma *p, int chan, double iv); /* Read a value */
+
+}; typedef struct _icmVideoCardGamma icmVideoCardGamma;
+
+/* ------------------------------------------------- */
+/* The Profile header */
+struct _icmHeader {
+
+ /* Private: */
+ unsigned int (*get_size)(struct _icmHeader *p);
+ int (*read)(struct _icmHeader *p, unsigned int len, unsigned int of);
+ int (*write)(struct _icmHeader *p, unsigned int of, int doid);
+ void (*del)(struct _icmHeader *p);
+ struct _icc *icp; /* Pointer to ICC we're a part of */
+ unsigned int size; /* Profile size in bytes */
+
+ /* public: */
+ void (*dump)(struct _icmHeader *p, icmFile *op, int verb);
+
+ /* Values that must be set before writing */
+ icProfileClassSignature deviceClass; /* Type of profile */
+ icColorSpaceSignature colorSpace; /* Clr space of data (Cannonical input space) */
+ icColorSpaceSignature pcs; /* PCS: XYZ or Lab (Cannonical output space) */
+ icRenderingIntent renderingIntent;/* Rendering intent */
+
+ /* Values that should be set before writing */
+ icmSig manufacturer; /* Dev manufacturer */
+ icmSig model; /* Dev model */
+ icmUint64 attributes; /* Device attributes */
+ unsigned int flags; /* Various bits */
+
+ /* Values that may optionally be set before writing */
+ /* icmUint64 attributes; Device attributes.h (see above) */
+ icmSig creator; /* Profile creator */
+
+ /* Values that are not normally set, since they have defaults */
+ icmSig cmmId; /* CMM for profile */
+ int majv, minv, bfv;/* Format version - major, minor, bug fix */
+ /* Default is majv = 2, alternate value is 4 */
+ icmDateTimeNumber date; /* Creation Date */
+ icPlatformSignature platform; /* Primary Platform */
+ icmXYZNumber illuminant; /* Profile illuminant */
+
+ /* Values that are created automatically */
+ unsigned char id[16]; /* MD5 fingerprint value, lsB to msB <V4.0+> */
+
+}; typedef struct _icmHeader icmHeader;
+
+/* ---------------------------------------------------------- */
+/* Objects for accessing lookup functions. */
+/* Note that the "effective" PCS colorspace is the one specified by the */
+/* PCS override, and visible in the overal profile conversion. */
+/* The "native" PCS colorspace is the one that the underlying tags actually */
+/* represent, and the PCS that the individual stages within each profile type handle. */
+/* The conversion between the native and effective PCS is done in the to/from_abs() */
+/* conversions. */
+
+/* Public: Parameter to get_luobj function */
+typedef enum {
+ icmFwd = 0, /* Device to PCS, or Device 1 to Last Device */
+ icmBwd = 1, /* PCS to Device, or Last Device to Device */
+ icmGamut = 2, /* PCS Gamut check */
+ icmPreview = 3 /* PCS to PCS preview */
+} icmLookupFunc;
+
+/* Public: Parameter to get_luobj function */
+typedef enum {
+ icmLuOrdNorm = 0, /* Normal profile preference: Lut, matrix, monochrome */
+ icmLuOrdRev = 1 /* Reverse profile preference: monochrome, matrix, monochrome */
+} icmLookupOrder;
+
+/* Public: Lookup algorithm object type */
+typedef enum {
+ icmMonoFwdType = 0, /* Monochrome, Forward */
+ icmMonoBwdType = 1, /* Monochrome, Backward */
+ icmMatrixFwdType = 2, /* Matrix, Forward */
+ icmMatrixBwdType = 3, /* Matrix, Backward */
+ icmLutType = 4, /* Multi-dimensional Lookup Table */
+ icmNamedType = 5 /* Named color data */
+} icmLuAlgType;
+
+/* Lookup class members common to named and non-named color types */
+#define LU_ICM_BASE_MEMBERS \
+ /* Private: */ \
+ icmLuAlgType ttype; /* The object tag */ \
+ struct _icc *icp; /* Pointer to ICC we're a part of */ \
+ icRenderingIntent intent; /* Effective (externaly visible) intent */ \
+ icmLookupFunc function; /* Functionality being used */ \
+ icmLookupOrder order; /* Conversion representation search Order */ \
+ icmXYZNumber pcswht, whitePoint, blackPoint; /* White and black point info (absolute XYZ) */ \
+ int blackisassumed; /* nz if black point tag is missing from profile */ \
+ double toAbs[3][3]; /* Matrix to convert from relative to absolute */ \
+ double fromAbs[3][3]; /* Matrix to convert from absolute to relative */ \
+ icColorSpaceSignature inSpace; /* Native Clr space of input */ \
+ icColorSpaceSignature outSpace; /* Native Clr space of output */ \
+ icColorSpaceSignature pcs; /* Native PCS */ \
+ icColorSpaceSignature e_inSpace; /* Effective Clr space of input */ \
+ icColorSpaceSignature e_outSpace; /* Effective Clr space of output */ \
+ icColorSpaceSignature e_pcs; /* Effective PCS */ \
+ \
+ /* Public: */ \
+ void (*del)(struct _icmLuBase *p); \
+ \
+ /* Internal native colorspaces: */ \
+ void (*lutspaces) (struct _icmLuBase *p, icColorSpaceSignature *ins, int *inn, \
+ icColorSpaceSignature *outs, int *outn, \
+ icColorSpaceSignature *pcs); \
+ \
+ /* External effecive colorspaces */ \
+ void (*spaces) (struct _icmLuBase *p, icColorSpaceSignature *ins, int *inn, \
+ icColorSpaceSignature *outs, int *outn, \
+ icmLuAlgType *alg, icRenderingIntent *intt, \
+ icmLookupFunc *fnc, icColorSpaceSignature *pcs, \
+ icmLookupOrder *ord); \
+ \
+ /* Relative to Absolute for this WP in XYZ */ \
+ void (*XYZ_Rel2Abs)(struct _icmLuBase *p, double *xyzout, double *xyzin); \
+ \
+ /* Absolute to Relative for this WP in XYZ */ \
+ void (*XYZ_Abs2Rel)(struct _icmLuBase *p, double *xyzout, double *xyzin); \
+
+
+/* Non-algorithm specific lookup class. Used as base class of algorithm specific class. */
+#define LU_ICM_NN_BASE_MEMBERS \
+ LU_ICM_BASE_MEMBERS \
+ \
+ /* Public: */ \
+ \
+ /* Get the native input space and output space ranges */ \
+ /* This is an accurate number of what the underlying profile can hold. */ \
+ void (*get_lutranges) (struct _icmLuBase *p, \
+ double *inmin, double *inmax, /* Range of inspace values */ \
+ double *outmin, double *outmax); /* Range of outspace values */ \
+ \
+ /* Get the effective input space and output space ranges */ \
+ /* This may not be accurate when the effective type is different to the native type */ \
+ void (*get_ranges) (struct _icmLuBase *p, \
+ double *inmin, double *inmax, /* Range of inspace values */ \
+ double *outmin, double *outmax); /* Range of outspace values */ \
+ \
+ /* Initialise the white and black points from the ICC tags */ \
+ /* and the corresponding absolute<->relative conversion matrices */ \
+ /* Return nz on error */ \
+ int (*init_wh_bk)(struct _icmLuBase *p); \
+ \
+ /* Get the LU white and black points in absolute XYZ space. */ \
+ /* Return nz if the black point is being assumed to be 0,0,0 rather */ \
+ /* than being from the tag. */ \
+ int (*wh_bk_points)(struct _icmLuBase *p, double *wht, double *blk); \
+ \
+ /* Get the LU white and black points in LU PCS space, converted to XYZ. */ \
+ /* (ie. white and black will be relative if LU is relative intent etc.) */ \
+ /* Return nz if the black point is being assumed to be 0,0,0 rather */ \
+ /* than being from the tag. */ \
+ int (*lu_wh_bk_points)(struct _icmLuBase *p, double *wht, double *blk); \
+ \
+ /* Translate color values through profile in effective in and out colorspaces, */ \
+ /* return values: */ \
+ /* 0 = success, 1 = warning: clipping occured, 2 = fatal: other error */ \
+ /* Note that clipping is not a reliable means of detecting out of gamut */ \
+ /* in the lookup(bwd) call for clut based profiles. */ \
+ int (*lookup) (struct _icmLuBase *p, double *out, double *in); \
+ \
+ \
+ /* Alternate to above, splits color conversion into three steps. */ \
+ /* Colorspace of _in and _out and _core are the effective in and out */ \
+ /* colorspaces. Note that setting colorspace overrides will probably. */ \
+ /* force _in and/or _out to be dumy (unity) transforms. */ \
+ \
+ /* Per channel input lookup (may be unity): */ \
+ int (*lookup_in) (struct _icmLuBase *p, double *out, double *in); \
+ \
+ /* Intra channel conversion: */ \
+ int (*lookup_core) (struct _icmLuBase *p, double *out, double *in); \
+ \
+ /* Per channel output lookup (may be unity): */ \
+ int (*lookup_out) (struct _icmLuBase *p, double *out, double *in); \
+ \
+ /* Inverse versions of above - partially implemented */ \
+ /* Inverse per channel input lookup (may be unity): */ \
+ int (*lookup_inv_in) (struct _icmLuBase *p, double *out, double *in); \
+ \
+
+
+/* Base lookup object */
+struct _icmLuBase {
+ LU_ICM_NN_BASE_MEMBERS
+}; typedef struct _icmLuBase icmLuBase;
+
+
+/* Algorithm specific lookup classes */
+
+/* Monochrome Fwd & Bwd type object */
+struct _icmLuMono {
+ LU_ICM_NN_BASE_MEMBERS
+ icmCurve *grayCurve;
+
+ /* Overall lookups */
+ int (*fwd_lookup) (struct _icmLuBase *p, double *out, double *in);
+ int (*bwd_lookup) (struct _icmLuBase *p, double *out, double *in);
+
+ /* Components of lookup */
+ int (*fwd_curve) (struct _icmLuMono *p, double *out, double *in);
+ int (*fwd_map) (struct _icmLuMono *p, double *out, double *in);
+ int (*fwd_abs) (struct _icmLuMono *p, double *out, double *in);
+ int (*bwd_abs) (struct _icmLuMono *p, double *out, double *in);
+ int (*bwd_map) (struct _icmLuMono *p, double *out, double *in);
+ int (*bwd_curve) (struct _icmLuMono *p, double *out, double *in);
+
+}; typedef struct _icmLuMono icmLuMono;
+
+/* 3D Matrix Fwd & Bwd type object */
+struct _icmLuMatrix {
+ LU_ICM_NN_BASE_MEMBERS
+ icmCurve *redCurve, *greenCurve, *blueCurve;
+ icmXYZArray *redColrnt, *greenColrnt, *blueColrnt;
+ double mx[3][3]; /* 3 * 3 conversion matrix */
+ double bmx[3][3]; /* 3 * 3 backwards conversion matrix */
+
+ /* Overall lookups */
+ int (*fwd_lookup) (struct _icmLuBase *p, double *out, double *in);
+ int (*bwd_lookup) (struct _icmLuBase *p, double *out, double *in);
+
+ /* Components of lookup */
+ int (*fwd_curve) (struct _icmLuMatrix *p, double *out, double *in);
+ int (*fwd_matrix) (struct _icmLuMatrix *p, double *out, double *in);
+ int (*fwd_abs) (struct _icmLuMatrix *p, double *out, double *in);
+ int (*bwd_abs) (struct _icmLuMatrix *p, double *out, double *in);
+ int (*bwd_matrix) (struct _icmLuMatrix *p, double *out, double *in);
+ int (*bwd_curve) (struct _icmLuMatrix *p, double *out, double *in);
+
+}; typedef struct _icmLuMatrix icmLuMatrix;
+
+/* Multi-D. Lut type object */
+struct _icmLuLut {
+ LU_ICM_NN_BASE_MEMBERS
+
+ /* private: */
+ icmLut *lut; /* Lut to use */
+ int usematrix; /* non-zero if matrix should be used */
+ double imx[3][3]; /* 3 * 3 inverse conversion matrix */
+ int imx_valid; /* Inverse matrix is valid */
+ void (*in_normf)(double *out, double *in); /* Lut input data normalizing function */
+ void (*in_denormf)(double *out, double *in);/* Lut input data de-normalizing function */
+ void (*out_normf)(double *out, double *in); /* Lut output data normalizing function */
+ void (*out_denormf)(double *out, double *in);/* Lut output de-normalizing function */
+ void (*e_in_denormf)(double *out, double *in);/* Effective input de-normalizing function */
+ void (*e_out_denormf)(double *out, double *in);/* Effecive output de-normalizing function */
+ /* function chosen out of lut->lookup_clut_sx and lut->lookup_clut_nl to imp. clut() */
+ int (*lookup_clut) (struct _icmLut *pp, double *out, double *in); /* clut function */
+
+ /* public: */
+
+ /* Components of lookup */
+ int (*in_abs) (struct _icmLuLut *p, double *out, double *in); /* Should be in icmLut ? */
+ int (*matrix) (struct _icmLuLut *p, double *out, double *in);
+ int (*input) (struct _icmLuLut *p, double *out, double *in);
+ int (*clut) (struct _icmLuLut *p, double *out, double *in);
+ int (*output) (struct _icmLuLut *p, double *out, double *in);
+ int (*out_abs) (struct _icmLuLut *p, double *out, double *in); /* Should be in icmLut ? */
+
+ /* Some inverse components, in reverse order */
+ /* Should be in icmLut ??? */
+ int (*inv_out_abs) (struct _icmLuLut *p, double *out, double *in);
+ int (*inv_output) (struct _icmLuLut *p, double *out, double *in);
+ /* inv_clut is beyond scope of icclib. See argyll for solution! */
+ int (*inv_input) (struct _icmLuLut *p, double *out, double *in);
+ int (*inv_matrix) (struct _icmLuLut *p, double *out, double *in);
+ int (*inv_in_abs) (struct _icmLuLut *p, double *out, double *in);
+
+ /* Get various types of information about the LuLut */
+ /* (Note that there's no reason why white/black point info */
+ /* isn't being made available for other Lu types) */
+ void (*get_info) (struct _icmLuLut *p, icmLut **lutp,
+ icmXYZNumber *pcswhtp, icmXYZNumber *whitep,
+ icmXYZNumber *blackp);
+
+ /* Get the matrix contents */
+ void (*get_matrix) (struct _icmLuLut *p, double m[3][3]);
+
+}; typedef struct _icmLuLut icmLuLut;
+
+/* Named colors lookup object */
+struct _icmLuNamed {
+ LU_ICM_BASE_MEMBERS
+
+ /* Private: */
+
+ /* Public: */
+
+ /* Get various types of information about the Named lookup */
+ void (*get_info) (struct _icmLuLut *p,
+ icmXYZNumber *pcswhtp, icmXYZNumber *whitep,
+ icmXYZNumber *blackp,
+ int *maxnamesize,
+ int *num_colors, char *prefix, char *suffix);
+
+
+ /* Lookup a name that includes prefix and suffix */
+ int (*fullname_lookup) (struct _icmLuNamed *p, double *pcs, double *dev, char *in);
+
+ /* Lookup a name that doesn't include prefix and suffix */
+ int (*name_lookup) (struct _icmLuNamed *p, double *pcs, double *dev, char *in);
+
+ /* Fill in a list with the nout closest named colors to the pcs target */
+ int (*pcs_lookup) (struct _icmLuNamed *p, char **out, int nout, double *in);
+
+ /* Fill in a list with the nout closest named colors to the device target */
+ int (*dev_lookup) (struct _icmLuNamed *p, char **out, int nout, double *in);
+
+}; typedef struct _icmLuNamed icmLuNamed;
+
+/* ---------------------------------------------------------- */
+/* A tag */
+typedef struct {
+ icTagSignature sig; /* The tag signature */
+ icTagTypeSignature ttype; /* The tag type signature */
+ unsigned int offset; /* File offset to start header */
+ unsigned int size; /* Size in bytes (not including padding) */
+ unsigned int pad; /* Padding in bytes */
+ icmBase *objp; /* In memory data structure */
+} icmTag;
+
+/* Pseudo enumerations valid as parameter to get_luobj(): */
+
+/* Special purpose Perceptual intent */
+#define icmAbsolutePerceptual ((icRenderingIntent)97)
+
+/* Special purpose Saturation intent */
+#define icmAbsoluteSaturation ((icRenderingIntent)98)
+
+/* To be specified where an intent is not appropriate */
+#define icmDefaultIntent ((icRenderingIntent)99)
+
+/* Pseudo PCS colospace used to indicate the native PCS */
+#define icmSigDefaultData ((icColorSpaceSignature) 0x0)
+
+/* Pseudo colospace used to indicate a named colorspace */
+#define icmSigNamedData ((icColorSpaceSignature) 0x1)
+
+
+/* Arguments to set a non-default creation version */
+typedef enum {
+ icmVersionDefault = 0, /* Version 2.2.0 is default */
+ icmVersion2_3 = 1, /* Version 2.3.0 - Chromaticity Tag */
+ icmVersion2_4 = 2, /* Version 2.4.0 - Display etc. have intents */
+ icmVersion4_1 = 3, /* Version 4.1.0 - General V4 features */
+} icmICCVersion;
+
+/* The ICC object */
+struct _icc {
+ /* Public: */
+ icmFile *(*get_rfp)(struct _icc *p); /* Return the current read fp (if any) */
+ int (*set_version)(struct _icc *p, icmICCVersion ver);
+ /* For creation, use ICC V4 etc. */
+ unsigned int (*get_size)(struct _icc *p); /* Return total size needed, 0 = err. */
+ int (*read)(struct _icc *p, icmFile *fp, unsigned int of); /* Returns error code */
+ int (*read_x)(struct _icc *p, icmFile *fp, unsigned int of, int take_fp);
+ int (*write)(struct _icc *p, icmFile *fp, unsigned int of);/* Returns error code */
+ int (*write_x)(struct _icc *p, icmFile *fp, unsigned int of, int take_fp);
+ void (*dump)(struct _icc *p, icmFile *op, int verb); /* Dump whole icc */
+ void (*del)(struct _icc *p); /* Free whole icc */
+ int (*find_tag)(struct _icc *p, icTagSignature sig);
+ /* Returns 0 if found, 1 if found but not readable, 2 of not found */
+ icmBase * (*read_tag)(struct _icc *p, icTagSignature sig);
+ /* Returns pointer to object */
+ icmBase * (*read_tag_any)(struct _icc *p, icTagSignature sig);
+ /* Returns pointer to object, but may be icmSigUnknownType! */
+ icmBase * (*add_tag)(struct _icc *p, icTagSignature sig, icTagTypeSignature ttype);
+ /* Returns pointer to object */
+ int (*rename_tag)(struct _icc *p, icTagSignature sig, icTagSignature sigNew);
+ /* Rename and existing tag */
+ icmBase * (*link_tag)(struct _icc *p, icTagSignature sig, icTagSignature ex_sig);
+ /* Returns pointer to object */
+ int (*unread_tag)(struct _icc *p, icTagSignature sig);
+ /* Unread a tag (free on refcount == 0 */
+ int (*read_all_tags)(struct _icc *p); /* Read all the tags, non-zero on error. */
+
+ int (*delete_tag)(struct _icc *p, icTagSignature sig);
+ /* Returns 0 if deleted OK */
+ int (*check_id)(struct _icc *p, ORD8 *id); /* Returns 0 if ID is OK, 1 if not present etc. */
+ double (*get_tac)(struct _icc *p, double *chmax, /* Returns total ink limit and channel maximums */
+ void (*calfunc)(void *cntx, double *out, double *in), void *cntx); /* optional cal. lookup */
+
+ /* Get a particular color conversion function */
+ icmLuBase * (*get_luobj) (struct _icc *p,
+ icmLookupFunc func, /* Functionality */
+ icRenderingIntent intent, /* Intent */
+ icColorSpaceSignature pcsor, /* PCS override (0 = def) */
+ icmLookupOrder order); /* Search Order */
+ /* Return appropriate lookup object */
+ /* NULL on error, check errc+err for reason */
+
+ /* Low level - load specific cLUT conversion */
+ icmLuBase * (*new_clutluobj)(struct _icc *p,
+ icTagSignature ttag, /* Target Lut tag */
+ icColorSpaceSignature inSpace, /* Native Input color space */
+ icColorSpaceSignature outSpace, /* Native Output color space */
+ icColorSpaceSignature pcs, /* Native PCS (from header) */
+ icColorSpaceSignature e_inSpace, /* Override Inpt color space */
+ icColorSpaceSignature e_outSpace, /* Override Outpt color space */
+ icColorSpaceSignature e_pcs, /* Override PCS */
+ icRenderingIntent intent, /* Rendering intent (For abs.) */
+ icmLookupFunc func); /* For icmLuSpaces() */
+ /* Return appropriate lookup object */
+ /* NULL on error, check errc+err for reason */
+
+ icmHeader *header; /* The header */
+ char err[512]; /* Error message */
+ int errc; /* Error code */
+ int warnc; /* Warning code */
+
+ /* Private: ? */
+ icmAlloc *al; /* Heap allocator */
+ int del_al; /* NZ if heap allocator should be deleted */
+ icmFile *fp; /* File associated with object */
+ int del_fp; /* NZ if File should be deleted */
+ unsigned int of; /* Offset of the profile within the file */
+ unsigned int count; /* Num tags in the profile */
+ icmTag *data; /* The tagTable and tagData */
+ icmICCVersion ver; /* Version class, see icmICCVersion enum */
+
+ }; typedef struct _icc icc;
+
+/* ========================================================== */
+/* Utility structures and declarations */
+
+/* Type of primitives that can be read or written */
+typedef enum {
+ icmUInt8Number,
+ icmUInt16Number,
+ icmUInt32Number,
+ icmUInt64Number,
+ icmU8Fixed8Number,
+ icmU16Fixed16Number,
+ icmSInt8Number,
+ icmSInt16Number,
+ icmSInt32Number,
+ icmSInt64Number,
+ icmS15Fixed16Number,
+ icmDCS8Number,
+ icmDCS16Number,
+ icmPCSNumber, /* 16 bit PCS of profile */
+ icmPCSXYZNumber, /* 16 bit XYZ */
+ icmPCSLab8Number, /* 8 bit Lab */
+ icmPCSLabNumber, /* 16 bit Lab of profile */
+ icmPCSLabV2Number, /* 16 bit Lab Version 2 */
+ icmPCSLabV4Number /* 16 bit Lab Version 4 */
+} icmPrimType;
+
+/* Structure to hold pseudo-hilbert counter info */
+struct _psh {
+ int di; /* Dimensionality */
+ unsigned int res; /* Resolution per coordinate */
+ unsigned int bits; /* Bits per coordinate */
+ unsigned int ix; /* Current binary index */
+ unsigned int tmask; /* Total 2^n count mask */
+ unsigned int count; /* Usable count */
+}; typedef struct _psh psh;
+
+/* Type of encoding to be returned as a string */
+typedef enum {
+ icmScreenEncodings,
+ icmDeviceAttributes,
+ icmProfileHeaderFlags,
+ icmAsciiOrBinaryData,
+ icmTagSignature,
+ icmTechnologySignature,
+ icmTypeSignature,
+ icmColorSpaceSignature,
+ icmProfileClassSignature,
+ icmPlatformSignature,
+ icmMeasurementFlare,
+ icmMeasurementGeometry,
+ icmRenderingIntent,
+ icmTransformLookupFunc,
+ icmSpotShape,
+ icmStandardObserver,
+ icmIlluminant,
+ icmLuAlg
+} icmEnumType;
+
+
+/* A helper object that computes MD5 checksums */
+struct _icmMD5 {
+ /* Private: */
+// icc *icp; /* ICC we're part of */
+ icmAlloc *al; /* Heap allocator */
+ int fin; /* Flag, nz if final has been called */
+ ORD32 sum[4]; /* Current/final checksum */
+ unsigned int tlen; /* Total length added in bytes */
+ ORD8 buf[64]; /* Partial buffer */
+
+ /* Public: */
+ void (*reset)(struct _icmMD5 *p); /* Reset the checksum */
+ void (*add)(struct _icmMD5 *p, ORD8 *buf, unsigned int len); /* Add some bytes */
+ void (*get)(struct _icmMD5 *p, ORD8 chsum[16]); /* Finalise and get the checksum */
+ void (*del)(struct _icmMD5 *p); /* We're done with the object */
+
+}; typedef struct _icmMD5 icmMD5;
+
+/* Create a new MD5 checksumming object, with a reset checksum value */
+/* Return it or NULL if there is an error */
+icmMD5 *new_icmMD5(icmAlloc *al);
+
+
+/* Implementation of file access class to compute an MD5 checksum */
+struct _icmFileMD5 {
+ ICM_FILE_BASE
+ int (*get_errc)(struct _icmFile *p); /* Check if there was an error */
+
+ /* Private: */
+ icmAlloc *al; /* Heap allocator */
+ icmMD5 *md5; /* MD5 object */
+ unsigned int of; /* Current offset */
+ int errc; /* Error code, 0 for OK */
+
+ /* Private: */
+ size_t size; /* Size of the file (For read) */
+
+}; typedef struct _icmFileMD5 icmFileMD5;
+
+/* Create a dumy file access class with allocator */
+icmFile *new_icmFileMD5_a(icmMD5 *md5, icmAlloc *al);
+
+
+/* ========================================================== */
+/* Public function declarations */
+/* Create an empty object. Return null on error */
+extern ICCLIB_API icc *new_icc_a(icmAlloc *al); /* With allocator class */
+
+/* If SEPARATE_STD not defined: */
+extern ICCLIB_API icc *new_icc(void); /* Default allocator */
+
+/* - - - - - - - - - - - - - */
+/* Some useful utilities: */
+
+/* Default ICC profile file extension string */
+
+#if defined (UNIX) || defined(__APPLE__)
+#define ICC_FILE_EXT ".icc"
+#define ICC_FILE_EXT_ND "icc"
+#else
+#define ICC_FILE_EXT ".icm"
+#define ICC_FILE_EXT_ND "icm"
+#endif
+
+/* Read a given primitive type. Return non-zero on error */
+extern ICCLIB_API int read_Primitive(icc *icpp, icmPrimType ptype, void *prim, char *p);
+
+/* Write a given primitive type. Return non-zero on error */
+extern ICCLIB_API int write_Primitive(icc *icp, icmPrimType ptype, char *p, void *prim);
+
+/* Return a string that represents a tag */
+extern ICCLIB_API char *tag2str(int tag);
+
+/* Return a tag created from a string */
+extern ICCLIB_API unsigned int str2tag(const char *str);
+
+/* Utility to define a non-standard tag, arguments are 4 character constants */
+#define icmMakeTag(A,B,C,D) \
+ ( (((ORD32)(ORD8)(A)) << 24) \
+ | (((ORD32)(ORD8)(B)) << 16) \
+ | (((ORD32)(ORD8)(C)) << 8) \
+ | ((ORD32)(ORD8)(D)) )
+
+/* Return a string description of the given enumeration value */
+extern ICCLIB_API const char *icm2str(icmEnumType etype, int enumval);
+
+/* Return the number of channels for the given color space. Return 0 if unknown. */
+extern ICCLIB_API unsigned int icmCSSig2nchan(icColorSpaceSignature sig);
+
+/* Return the individual channel names and number of channels give a colorspace signature. */
+/* Return 0 if it is not a colorspace that itself defines particular channels, */
+/* 1 if it is a colorant based colorspace, and 2 if it is not a colorant based space */
+extern ICCLIB_API unsigned int icmCSSig2chanNames( icColorSpaceSignature sig, char *cvals[]);
+
+
+/* Simple macro to transfer an array to an XYZ number */
+#define icmAry2XYZ(xyz, ary) ((xyz).X = (ary)[0], (xyz).Y = (ary)[1], (xyz).Z = (ary)[2])
+
+/* And the reverse */
+#define icmXYZ2Ary(ary, xyz) ((ary)[0] = (xyz).X, (ary)[1] = (xyz).Y, (ary)[2] = (xyz).Z)
+
+/* Simple macro to transfer an XYZ number to an XYZ number */
+#define icmXYZ2XYZ(d_xyz, s_xyz) ((d_xyz).X = (s_xyz).X, (d_xyz).Y = (s_xyz).Y, \
+ (d_xyz).Z = (s_xyz).Z)
+
+/* Simple macro to transfer an 3array to 3array */
+/* Hmm. Same as icmCpy3 */
+#define icmAry2Ary(d_ary, s_ary) ((d_ary)[0] = (s_ary)[0], (d_ary)[1] = (s_ary)[1], \
+ (d_ary)[2] = (s_ary)[2])
+
+/* CIE Y (range 0 .. 1) to perceptual CIE 1976 L* (range 0 .. 100) */
+double icmY2L(double val);
+
+/* Perceptual CIE 1976 L* (range 0 .. 100) to CIE Y (range 0 .. 1) */
+double icmL2Y(double val);
+
+/* CIE XYZ to perceptual Lab */
+extern ICCLIB_API void icmXYZ2Lab(icmXYZNumber *w, double *out, double *in);
+
+/* Perceptual Lab to CIE XYZ */
+extern ICCLIB_API void icmLab2XYZ(icmXYZNumber *w, double *out, double *in);
+
+/* LCh to Lab */
+extern ICCLIB_API void icmLCh2Lab(double *out, double *in);
+
+/* Lab to LCh */
+extern ICCLIB_API void icmLab2LCh(double *out, double *in);
+
+/* XYZ to Yxy */
+extern ICCLIB_API void icmXYZ2Yxy(double *out, double *in);
+
+/* Yxy to XYZ */
+extern ICCLIB_API void icmYxy2XYZ(double *out, double *in);
+
+/* CIE XYZ to perceptual Luv */
+extern ICCLIB_API void icmXYZ2Luv(icmXYZNumber *w, double *out, double *in);
+
+/* Perceptual Luv to CIE XYZ */
+extern ICCLIB_API void icmLuv2XYZ(icmXYZNumber *w, double *out, double *in);
+
+
+/* NOTE :- none of the following seven have been protected */
+/* against arithmmetic issues (ie. for black) */
+
+/* CIE XYZ to perceptual CIE 1976 UCS diagram Yu'v'*/
+/* (Yu'v' is a better chromaticity space than Yxy) */
+extern ICCLIB_API void icmXYZ21976UCS(double *out, double *in);
+
+/* Perceptual CIE 1976 UCS diagram Yu'v' to CIE XYZ */
+extern ICCLIB_API void icm1976UCS2XYZ(double *out, double *in);
+
+/* CIE XYZ to perceptual CIE 1960 UCS */
+/* (This was obsoleted by the 1976UCS, but is still used */
+/* in computing color temperatures.) */
+extern ICCLIB_API void icmXYZ21960UCS(double *out, double *in);
+
+/* Perceptual CIE 1960 UCS to CIE XYZ */
+extern ICCLIB_API void icm1960UCS2XYZ(double *out, double *in);
+
+/* CIE XYZ to perceptual CIE 1964 WUV (U*V*W*) */
+/* (This is obsolete but still used in computing CRI) */
+extern ICCLIB_API void icmXYZ21964WUV(icmXYZNumber *w, double *out, double *in);
+
+/* Perceptual CIE 1964 WUV (U*V*W*) to CIE XYZ */
+extern ICCLIB_API void icm1964WUV2XYZ(icmXYZNumber *w, double *out, double *in);
+
+/* CIE CIE1960 UCS to perceptual CIE 1964 WUV (U*V*W*) */
+extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double *in);
+
+
+/* The standard D50 illuminant value */
+extern icmXYZNumber icmD50;
+extern icmXYZNumber icmD50_100; /* Scaled to 100 */
+double icmD50_ary3[3]; /* As an array */
+double icmD50_100_ary3[3]; /* Scaled to 100 as an array */
+
+/* The standard D65 illuminant value */
+extern icmXYZNumber icmD65;
+extern icmXYZNumber icmD65_100; /* Scaled to 100 */
+double icmD65_ary3[3]; /* As an array */
+double icmD65_100_ary3[3]; /* Scaled to 100 as an array */
+
+/* The default black value */
+extern icmXYZNumber icmBlack;
+
+
+/* Initialise a pseudo-hilbert grid counter, return total usable count. */
+extern ICCLIB_API unsigned psh_init(psh *p, int di, unsigned int res, int co[]);
+
+/* Reset the counter */
+extern ICCLIB_API void psh_reset(psh *p);
+
+/* Increment pseudo-hilbert coordinates */
+/* Return non-zero if count rolls over to 0 */
+extern ICCLIB_API int psh_inc(psh *p, int co[]);
+
+
+/* RGB primaries to device to RGB->XYZ transform matrix */
+/* Return non-zero if matrix would be singular */
+int icmRGBprim2matrix(
+ icmXYZNumber white, /* White point */
+ icmXYZNumber red, /* Red colorant */
+ icmXYZNumber green, /* Green colorant */
+ icmXYZNumber blue, /* Blue colorant */
+ double mat[3][3] /* Destination matrix */
+);
+
+/* Chromatic Adaption transform utility */
+/* Return a 3x3 chromatic adaption matrix */
+/* Use icmMulBy3x3(dst, mat, src) */
+
+#define ICM_CAM_BRADFORD 0x0001 /* Use Bradford sharpened response space */
+#define ICM_CAM_MULMATRIX 0x0002 /* Transform the given matrix */
+
+void icmChromAdaptMatrix(
+ int flags, /* Flags as defined below */
+ icmXYZNumber d_wp, /* Destination white point */
+ icmXYZNumber s_wp, /* Source white point */
+ double mat[3][3] /* Destination matrix */
+);
+
+/* - - - - - - - - - - - - - - */
+/* Set a 3 vector */
+#define icmSet3(d_ary, s_val) ((d_ary)[0] = (s_val), (d_ary)[1] = (s_val), \
+ (d_ary)[2] = (s_val))
+
+/* Copy a 3 vector */
+#define icmCpy3(d_ary, s_ary) ((d_ary)[0] = (s_ary)[0], (d_ary)[1] = (s_ary)[1], \
+ (d_ary)[2] = (s_ary)[2])
+
+/* Clamp a 3 vector to be +ve */
+void icmClamp3(double out[3], double in[3]);
+
+/* Add two 3 vectors */
+void icmAdd3(double out[3], double in1[3], double in2[3]);
+
+#define ICMADD3(o, i, j) ((o)[0] = (i)[0] + (j)[0], (o)[1] = (i)[1] + (j)[1], (o)[2] = (i)[2] + (j)[2])
+
+/* Subtract two 3 vectors, out = in1 - in2 */
+void icmSub3(double out[3], double in1[3], double in2[3]);
+
+#define ICMSUB3(o, i, j) ((o)[0] = (i)[0] - (j)[0], (o)[1] = (i)[1] - (j)[1], (o)[2] = (i)[2] - (j)[2])
+
+/* Compute the dot product of two 3 vectors */
+double icmDot3(double in1[3], double in2[3]);
+
+#define ICMDOT3(o, i, j) ((o) = (i)[0] * (j)[0] + (i)[1] * (j)[1] + (i)[2] * (j)[2])
+
+/* Compute the cross product of two 3D vectors, out = in1 x in2 */
+void icmCross3(double out[3], double in1[3], double in2[3]);
+
+/* Compute the norm squared (length squared) of a 3 vector */
+double icmNorm3sq(double in[3]);
+
+#define ICMNORM3SQ(i) ((i)[0] * (i)[0] + (i)[1] * (i)[1] + (i)[2] * (i)[2])
+
+/* Compute the norm (length) of a 3 vector */
+double icmNorm3(double in[3]);
+
+#define ICMNORM3(i) sqrt((i)[0] * (i)[0] + (i)[1] * (i)[1] + (i)[2] * (i)[2])
+
+/* Scale a 3 vector by the given ratio */
+void icmScale3(double out[3], double in[3], double rat);
+
+/* Compute a blend between in0 and in1 */
+void icmBlend3(double out[3], double in0[3], double in1[3], double bf);
+
+#define ICMSCALE3(o, i, j) ((o)[0] = (i)[0] * (j), (o)[1] = (i)[1] * (j), (o)[2] = (i)[2] * (j))
+
+/* Normalise a 3 vector to the given length. Return nz if not normalisable */
+int icmNormalize3(double out[3], double in[3], double len);
+
+/* Compute the norm squared (length squared) of a vector defined by two points */
+double icmNorm33sq(double in1[3], double in0[3]);
+
+/* Compute the norm (length) of of a vector defined by two points */
+double icmNorm33(double in1[3], double in0[3]);
+
+/* Scale a two point vector by the given ratio. in0[] is the origin. */
+void icmScale33(double out[3], double in1[3], double in0[3], double rat);
+
+/* Normalise a two point vector to the given length. */
+/* The new location of in1[] is returned in out[], in0[] is the origin. */
+/* Return nz if not normalisable */
+int icmNormalize33(double out[3], double in1[3], double in0[3], double len);
+
+
+/* Set a 3x3 matrix to unity */
+void icmSetUnity3x3(double mat[3][3]);
+
+/* Copy a 3x3 transform matrix */
+void icmCpy3x3(double out[3][3], double mat[3][3]);
+
+/* Scale each element of a 3x3 transform matrix */
+void icmScale3x3(double dst[3][3], double src[3][3], double scale);
+
+/* Multiply 3 vector by 3x3 transform matrix */
+/* Organization is mat[out][in] */
+void icmMulBy3x3(double out[3], double mat[3][3], double in[3]);
+
+/* Add one 3x3 to another */
+/* dst = src1 + src2 */
+void icmAdd3x3(double dst[3][3], double src1[3][3], double src2[3][3]);
+
+/* Tensor product. Multiply two 3 vectors to form a 3x3 matrix */
+/* src1[] forms the colums, and src2[] forms the rows in the result */
+void icmTensMul3(double dst[3][3], double src1[3], double src2[3]);
+
+/* Multiply one 3x3 with another */
+/* dst = src * dst */
+void icmMul3x3(double dst[3][3], double src[3][3]);
+
+/* Multiply one 3x3 with another #2 */
+/* dst = src1 * src2 */
+void icmMul3x3_2(double dst[3][3], double src1[3][3], double src2[3][3]);
+
+/* Compute the determinant of a 3x3 matrix */
+double icmDet3x3(double in[3][3]);
+
+/* Invert a 3x3 transform matrix. Return 1 if error. */
+int icmInverse3x3(double out[3][3], double in[3][3]);
+
+/* Transpose a 3x3 matrix */
+void icmTranspose3x3(double out[3][3], double in[3][3]);
+
+/* Given two 3D points, create a matrix that rotates */
+/* and scales one onto the other about the origin 0,0,0. */
+/* Use icmMulBy3x3() to apply this to other points */
+void icmRotMat(double mat[3][3], double src[3], double targ[3]);
+
+/* Copy a 3x4 transform matrix */
+void icmCpy3x4(double out[3][4], double mat[3][4]);
+
+/* Multiply 3 array by 3x4 transform matrix */
+void icmMul3By3x4(double out[3], double mat[3][4], double in[3]);
+
+/* Given two 3D vectors, create a matrix that translates, */
+/* rotates and scales one onto the other. */
+/* Use icmMul3By3x4 to apply this to other points */
+void icmVecRotMat(double m[3][4], double s1[3], double s0[3], double t1[3], double t0[3]);
+
+
+/* Compute the intersection of a vector and a plane */
+/* return nz if there is no intersection */
+int icmVecPlaneIsect(double isect[3], double pl_const, double pl_norm[3], double ve_1[3], double ve_0[3]);
+
+/* Compute the closest points between two lines a and b. */
+/* Return closest points and parameter values if not NULL. */
+/* Return nz if they are paralel. */
+int icmLineLineClosest(double ca[3], double cb[3], double *pa, double *pb,
+ double la0[3], double la1[3], double lb0[3], double lb1[3]);
+
+/* Given 3 3D points, compute a plane equation. */
+/* The normal will be right handed given the order of the points */
+/* The plane equation will be the 3 normal components and the constant. */
+/* Return nz if any points are cooincident or co-linear */
+int icmPlaneEqn3(double eq[4], double p0[3], double p1[3], double p2[3]);
+
+/* Given a 3D point and a plane equation, return the signed */
+/* distance from the plane */
+double icmPlaneDist3(double eq[4], double p[3]);
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Multiply 2 array by 2x2 transform matrix */
+void icmMulBy2x2(double out[2], double mat[2][2], double in[2]);
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Return the normal Delta E given two Lab values */
+extern ICCLIB_API double icmLabDE(double *in0, double *in1);
+
+/* Return the normal Delta E squared, given two Lab values */
+extern ICCLIB_API double icmLabDEsq(double *in0, double *in1);
+
+/* Return the normal Delta E given two XYZ values */
+extern ICCLIB_API double icmXYZLabDE(icmXYZNumber *w, double *in0, double *in1);
+
+
+/* Return the CIE94 Delta E color difference measure for two Lab values */
+extern ICCLIB_API double icmCIE94(double *in0, double *in1);
+
+/* Return the CIE94 Delta E color difference measure squared, for two Lab values */
+extern ICCLIB_API double icmCIE94sq(double *in0, double *in1);
+
+/* Return the CIE94 Delta E color difference measure for two XYZ values */
+extern ICCLIB_API double icmXYZCIE94(icmXYZNumber *w, double *in0, double *in1);
+
+
+/* Return the CIEDE2000 Delta E color difference measure for two Lab values */
+extern ICCLIB_API double icmCIE2K(double *in0, double *in1);
+
+/* Return the CIEDE2000 Delta E color difference measure squared, for two Lab values */
+extern ICCLIB_API double icmCIE2Ksq(double *in0, double *in1);
+
+/* Return the CIEDE2000 Delta E color difference measure for two XYZ values */
+extern ICCLIB_API double icmXYZCIE2K(icmXYZNumber *w, double *in0, double *in1);
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Clip Lab, while maintaining hue angle. */
+/* Return nz if clipping occured */
+int icmClipLab(double out[3], double in[3]);
+
+/* Clip XYZ, while maintaining hue angle */
+/* Return nz if clipping occured */
+int icmClipXYZ(double out[3], double in[3]);
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Print an int vector to a string. */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPiv(int di, int *p);
+
+/* Print a double color vector to a string. */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPdv(int di, double *p);
+
+/* Print a float color vector to a string. */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPfv(int di, float *p);
+
+/* Print an 0..1 range XYZ as a D50 Lab string */
+/* Returned static buffer is re-used every 5 calls. */
+char *icmPLab(double *p);
+
+/* ---------------------------------------------------------- */
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* ICC_H */
+
diff --git a/icc/iccV42.h b/icc/iccV42.h
new file mode 100644
index 0000000..ee3fefb
--- /dev/null
+++ b/icc/iccV42.h
@@ -0,0 +1,555 @@
+
+/*
+ * This file started out as the standard ICC header provided by
+ * the ICC. Since this file no longer seems to be maintained,
+ * I've added new entries from the ICC spec., but dropped
+ * things that are not needed for icclib.
+ *
+ * This version of the header file corresponds to the profile
+ * Specification ICC.1:2003-09 (Version 4.2.0)
+ *
+ * All header file entries are pre-fixed with "ic" to help
+ * avoid name space collisions. Signatures are pre-fixed with
+ * icSig.
+ *
+ * Version numbers used within file are file format version numers,
+ * not spec. version numbers.
+ *
+ * Portions of this file are Copyright 2004 - 2005 Graeme W. Gill,
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ *
+ * Graeme Gill.
+ */
+
+/* Header file guard bands */
+#ifndef ICCV42_H
+#define ICCV42_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*****************************************************************
+ Copyright (c) 1994-1998 SunSoft, Inc.
+
+ Rights Reserved
+
+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 restrict-
+ion, 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 NON-
+INFRINGEMENT. IN NO EVENT SHALL SUNSOFT, INC. OR ITS PARENT
+COMPANY 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.
+
+Except as contained in this notice, the name of SunSoft, Inc.
+shall not be used in advertising or otherwise to promote the
+sale, use or other dealings in this Software without written
+authorization from SunSoft Inc.
+******************************************************************/
+
+/*
+ Spec name/version/file version relationship.
+
+ Spec. Name Spec. Version ICC File Version
+ ---------- ------------- ----------------
+ ICC.1:2004-10 4.2 4.2.0
+ ICC.1:2003-09 4.1 4.1.0
+ ICC.1:2001-12 4.0 4.0.0
+ ICC.1:2001-04 (3.7?) 2.4.0
+ ICC.1A:1999-04 (3.6?) 2.3.0
+ ICC.1:1998-09 (3.5?) 2.2.0
+ (Version 3.4) 3.4 2.1.0 (a)
+ (Version 3.3) 3.3 2.1.0
+ (Version 3.2) 3.2 2.0.0 (b)
+ (Version 3.01) 3.01 2.0.0 (a)
+ (Version 3.0) 3.0 2.0.0
+*/
+
+
+#define icMaxTagVal -1
+
+/*------------------------------------------------------------------------*/
+
+/*
+ * Defines used in the specification
+ */
+
+#define icMagicNumber 0x61637370L /* 'acsp' */
+#define icVersionNumber 0x02200000L /* 2.2.0, BCD */
+#define icVersionNumberV41 0x04100000L /* 4.1.0, BCD */
+
+/* Screening Encodings */
+#define icPrtrDefaultScreensFalse 0x00000000L /* Bit pos 0 */
+#define icPrtrDefaultScreensTrue 0x00000001L /* Bit pos 0 */
+#define icLinesPerInch 0x00000002L /* Bit pos 1 */
+#define icLinesPerCm 0x00000000L /* Bit pos 1 */
+
+/*
+ * Device attributes, currently defined values correspond
+ * to the least-significant 4 bytes of the 8 byte attribute
+ * quantity, see the header for their location.
+ */
+
+#define icReflective 0x00000000L /* Bit pos 0 */
+#define icTransparency 0x00000001L /* Bit pos 0 */
+#define icGlossy 0x00000000L /* Bit pos 1 */
+#define icMatte 0x00000002L /* Bit pos 1 */
+#define icPositive 0x00000000L /* Bit pos 2 */
+#define icNegative 0x00000004L /* Bit pos 2 */
+#define icColor 0x00000000L /* Bit pos 3 */
+#define icBlackAndWhite 0x00000008L /* Bit pos 3 */
+
+/*
+ * Profile header flags, the least-significant 16 bits are reserved
+ * for consortium use.
+ */
+
+#define icEmbeddedProfileFalse 0x00000000L /* Bit pos 0 */
+#define icEmbeddedProfileTrue 0x00000001L /* Bit pos 0 */
+#define icUseAnywhere 0x00000000L /* Bit pos 1 */
+#define icUseWithEmbeddedDataOnly 0x00000002L /* Bit pos 1 */
+
+/* Ascii or Binary data */
+#define icAsciiData 0x00000000L
+#define icBinaryData 0x00000001L
+
+/* Phosphor or Colorant sets */
+#define icPhColUnknown 0x0000 /* Specified */
+#define icPhColITU_R_BT_709 0x0001 /* ITU-R BT.709 */
+#define icPhColSMPTE_RP145_1994 0x0002 /* SMPTE RP145-1994 */
+#define icPhColEBU_Tech_3213_E 0x0003 /* EBU Tech.3213-E */
+#define icPhColP22 0x0004 /* P22 */
+
+/* Video card gamma formats (ColorSync 2.5 specific) */
+#define icVideoCardGammaTable 0x00000000
+#define icVideoCardGammaFormula 0x00000001
+
+
+/*------------------------------------------------------------------------*/
+/*
+ * Use this area to translate platform definitions of long
+ * etc into icXXX form. The rest of the header uses the icXXX
+ * typedefs. Signatures are 4 byte quantities.
+ */
+#ifdef ORD32 /* Formal sizes defined elsewhere */
+
+typedef INR32 icSignature;
+
+/* Unsigned integer numbers */
+typedef ORD8 icUInt8Number;
+typedef ORD16 icUInt16Number;
+typedef ORD32 icUInt32Number;
+typedef ORD32 icUInt64Number[2];
+
+/* Signed numbers */
+typedef INR8 icInt8Number;
+typedef INR16 icInt16Number;
+typedef INR32 icInt32Number;
+typedef INR32 icInt64Number[2];
+
+/* Fixed numbers */
+typedef INR32 icS15Fixed16Number;
+typedef ORD32 icU16Fixed16Number;
+
+#else /* default definitions for typical 32 bit architecture */
+
+typedef long icSignature;
+
+/* Unsigned integer numbers */
+typedef unsigned char icUInt8Number;
+typedef unsigned short icUInt16Number;
+typedef unsigned long icUInt32Number;
+typedef unsigned long icUInt64Number[2];
+
+/* Signed numbers */
+typedef char icInt8Number;
+typedef short icInt16Number;
+typedef long icInt32Number;
+typedef long icInt64Number[2];
+
+/* Fixed numbers */
+typedef long icS15Fixed16Number;
+typedef unsigned long icU16Fixed16Number;
+
+#endif /* Not formal */
+
+/*------------------------------------------------------------------------*/
+/* public tags and sizes */
+typedef enum {
+ icSigAToB0Tag = 0x41324230L, /* 'A2B0' */
+ icSigAToB1Tag = 0x41324231L, /* 'A2B1' */
+ icSigAToB2Tag = 0x41324232L, /* 'A2B2' */
+ icSigBlueMatrixColumnTag = 0x6258595AL, /* 'bXYZ' */
+ icSigBlueTRCTag = 0x62545243L, /* 'bTRC' */
+ icSigBToA0Tag = 0x42324130L, /* 'B2A0' */
+ icSigBToA1Tag = 0x42324131L, /* 'B2A1' */
+ icSigBToA2Tag = 0x42324132L, /* 'B2A2' */
+ icSigCalibrationDateTimeTag = 0x63616C74L, /* 'calt' */
+ icSigCharTargetTag = 0x74617267L, /* 'targ' */
+ icSigChromaticAdaptationTag = 0x63686164L, /* 'chad' V2.4+ */
+ icSigChromaticityTag = 0x6368726DL, /* 'chrm' V2.3+ */
+ icSigColorantOrderTag = 0x636C726FL, /* 'clro' V4.0+ */
+ icSigColorantTableTag = 0x636C7274L, /* 'clrt' V4.0+ */
+ icSigColorantTableOutTag = 0x636C6F74L, /* 'clot' V4.0+ */
+ icSigCopyrightTag = 0x63707274L, /* 'cprt' */
+ icSigCrdInfoTag = 0x63726469L, /* 'crdi' V2.1 - V4.0 */
+ icSigDataTag = 0x64617461L, /* 'data' ??? */
+ icSigDateTimeTag = 0x6474696DL, /* 'dtim' ??? */
+ icSigDeviceMfgDescTag = 0x646D6E64L, /* 'dmnd' */
+ icSigDeviceModelDescTag = 0x646D6464L, /* 'dmdd' */
+ icSigDeviceSettingsTag = 0x64657673L, /* 'devs' V2.2 - V4.0 */
+ icSigGamutTag = 0x67616D74L, /* 'gamt' */
+ icSigGrayTRCTag = 0x6b545243L, /* 'kTRC' */
+ icSigGreenMatrixColumnTag = 0x6758595AL, /* 'gXYZ' */
+ icSigGreenTRCTag = 0x67545243L, /* 'gTRC' */
+ icSigLuminanceTag = 0x6C756d69L, /* 'lumi' */
+ icSigMeasurementTag = 0x6D656173L, /* 'meas' */
+ icSigMediaBlackPointTag = 0x626B7074L, /* 'bkpt' */
+ icSigMediaWhitePointTag = 0x77747074L, /* 'wtpt' */
+ icSigNamedColorTag = 0x6E636f6CL, /* 'ncol' V2.0 */
+ icSigNamedColor2Tag = 0x6E636C32L, /* 'ncl2' V2.1+ */
+ icSigOutputResponseTag = 0x72657370L, /* 'resp' V2.2+ */
+ icSigPerceptualRenderingIntentGamutTag = 0x72696730L, /* 'rig0' ??? */
+ icSigPreview0Tag = 0x70726530L, /* 'pre0' */
+ icSigPreview1Tag = 0x70726531L, /* 'pre1' */
+ icSigPreview2Tag = 0x70726532L, /* 'pre2' */
+ icSigProfileDescriptionTag = 0x64657363L, /* 'desc' */
+ icSigProfileSequenceDescTag = 0x70736571L, /* 'pseq' */
+ icSigPs2CRD0Tag = 0x70736430L, /* 'psd0' V2.0 - V4.0 */
+ icSigPs2CRD1Tag = 0x70736431L, /* 'psd1' V2.0 - V4.0 */
+ icSigPs2CRD2Tag = 0x70736432L, /* 'psd2' V2.0 - V4.0 */
+ icSigPs2CRD3Tag = 0x70736433L, /* 'psd3' V2.0 - V4.0 */
+ icSigPs2CSATag = 0x70733273L, /* 'ps2s' V2.0 - V4.0 */
+ icSigPs2RenderingIntentTag = 0x70733269L, /* 'ps2i' V2.0 - V4.0 */
+ icSigRedMatrixColumnTag = 0x7258595AL, /* 'rXYZ' */
+ icSigRedTRCTag = 0x72545243L, /* 'rTRC' */
+ icSigSaturationRenderingIntentGamutTag = 0x72696732L, /* 'rig2' ??? */
+ icSigScreeningDescTag = 0x73637264L, /* 'scrd' V2.0 - V4.0 */
+ icSigScreeningTag = 0x7363726EL, /* 'scrn' V2.0 - V4.0 */
+ icSigTechnologyTag = 0x74656368L, /* 'tech' */
+ icSigUcrBgTag = 0x62666420L, /* 'bfd ' V2.0 - V4.0 */
+ icSigVideoCardGammaTag = 0x76636774L, /* 'vcgt' ColorSync 2.5+ */
+ icSigViewingCondDescTag = 0x76756564L, /* 'vued' */
+ icSigViewingConditionsTag = 0x76696577L, /* 'view' */
+ icMaxEnumTag = icMaxTagVal
+} icTagSignature;
+
+/* Aliases for backwards compatibility */
+#define icSigBlueColorantTag icSigBlueMatrixColumnTag /* Prior to V4.0 */
+#define icSigGreenColorantTag icSigGreenMatrixColumnTag /* Prior to V4.0 */
+#define icSigRedColorantTag icSigRedMatrixColumnTag /* Prior to V4.0 */
+
+/* technology signature descriptions */
+typedef enum {
+ icSigDigitalCamera = 0x6463616DL, /* 'dcam' */
+ icSigFilmScanner = 0x6673636EL, /* 'fscn' */
+ icSigReflectiveScanner = 0x7273636EL, /* 'rscn' */
+ icSigInkJetPrinter = 0x696A6574L, /* 'ijet' */
+ icSigThermalWaxPrinter = 0x74776178L, /* 'twax' */
+ icSigElectrophotographicPrinter = 0x6570686FL, /* 'epho' */
+ icSigElectrostaticPrinter = 0x65737461L, /* 'esta' */
+ icSigDyeSublimationPrinter = 0x64737562L, /* 'dsub' */
+ icSigPhotographicPaperPrinter = 0x7270686FL, /* 'rpho' */
+ icSigFilmWriter = 0x6670726EL, /* 'fprn' */
+ icSigVideoMonitor = 0x7669646DL, /* 'vidm' */
+ icSigVideoCamera = 0x76696463L, /* 'vidc' */
+ icSigProjectionTelevision = 0x706A7476L, /* 'pjtv' */
+ icSigCRTDisplay = 0x43525420L, /* 'CRT ' */
+ icSigPMDisplay = 0x504D4420L, /* 'PMD ' */
+ icSigAMDisplay = 0x414D4420L, /* 'AMD ' */
+ icSigPhotoCD = 0x4B504344L, /* 'KPCD' */
+ icSigPhotoImageSetter = 0x696D6773L, /* 'imgs' */
+ icSigGravure = 0x67726176L, /* 'grav' */
+ icSigOffsetLithography = 0x6F666673L, /* 'offs' */
+ icSigSilkscreen = 0x73696C6BL, /* 'silk' */
+ icSigFlexography = 0x666C6578L, /* 'flex' */
+ icMaxEnumTechnology = icMaxTagVal
+} icTechnologySignature;
+
+/* type signatures */
+typedef enum {
+ icSigChromaticityType = 0x6368726DL, /* 'chrm' */
+ icSigColorantOrderType = 0x636C726FL, /* 'clro' V4.0+ */
+ icSigColorantTableType = 0x636C7274L, /* 'clrt' V4.0+ */
+ icSigCrdInfoType = 0x63726469L, /* 'crdi' V2.1 - V4.0 */
+ icSigCurveType = 0x63757276L, /* 'curv' */
+ icSigDataType = 0x64617461L, /* 'data' */
+ icSigDateTimeType = 0x6474696DL, /* 'dtim' */
+ icSigDeviceSettingsType = 0x64657673L, /* 'devs' V2.2 - V4.0 */
+ icSigLut16Type = 0x6d667432L, /* 'mft2' */
+ icSigLut8Type = 0x6d667431L, /* 'mft1' */
+ icSigLutAtoBType = 0x6d414220L, /* 'mAB ' V4.0+ */
+ icSigLutBtoAType = 0x6d424120L, /* 'mBA ' V4.0+ */
+ icSigMeasurementType = 0x6D656173L, /* 'meas' */
+ icSigMultiLocalizedUnicodeType = 0x6D6C7563L, /* 'mluc' V4.0+ */
+ icSigNamedColorType = 0x6E636f6CL, /* 'ncol' V2.0 */
+ icSigNamedColor2Type = 0x6E636C32L, /* 'ncl2' V2.0+ */
+ icSigParametricCurveType = 0x70617261L, /* 'para' V4.0+ */
+ icSigProfileSequenceDescType = 0x70736571L, /* 'pseq' */
+ icSigResponseCurveSet16Type = 0x72637332L, /* 'rcs2' V2.2 - V4.0 */
+ icSigS15Fixed16ArrayType = 0x73663332L, /* 'sf32' */
+ icSigScreeningType = 0x7363726EL, /* 'scrn' V2.0 - V4.0 */
+ icSigSignatureType = 0x73696720L, /* 'sig ' */
+ icSigTextType = 0x74657874L, /* 'text' */
+ icSigTextDescriptionType = 0x64657363L, /* 'desc' V2.0 - V2.4 */
+ icSigU16Fixed16ArrayType = 0x75663332L, /* 'uf32' */
+ icSigUcrBgType = 0x62666420L, /* 'bfd ' V2.0 - V4.0 */
+ icSigUInt16ArrayType = 0x75693136L, /* 'ui16' */
+ icSigUInt32ArrayType = 0x75693332L, /* 'ui32' */
+ icSigUInt64ArrayType = 0x75693634L, /* 'ui64' */
+ icSigUInt8ArrayType = 0x75693038L, /* 'ui08' */
+ icSigVideoCardGammaType = 0x76636774L, /* 'vcgt' ColorSync 2.5+ */
+ icSigViewingConditionsType = 0x76696577L, /* 'view' */
+ icSigXYZType = 0x58595A20L, /* 'XYZ ' */
+ icSigXYZArrayType = 0x58595A20L, /* 'XYZ ' */
+ icMaxEnumType = icMaxTagVal
+} icTagTypeSignature;
+
+/*
+ * Color Space Signatures
+ * Note that only icSigXYZData and icSigLabData are valid
+ * Profile Connection Spaces (PCSs)
+ */
+typedef enum {
+ icSigXYZData = 0x58595A20L, /* 'XYZ ' */
+ icSigLabData = 0x4C616220L, /* 'Lab ' */
+ icSigLuvData = 0x4C757620L, /* 'Luv ' */
+ icSigYCbCrData = 0x59436272L, /* 'YCbr' */
+ icSigYxyData = 0x59787920L, /* 'Yxy ' */
+ icSigRgbData = 0x52474220L, /* 'RGB ' */
+ icSigGrayData = 0x47524159L, /* 'GRAY' */
+ icSigHsvData = 0x48535620L, /* 'HSV ' */
+ icSigHlsData = 0x484C5320L, /* 'HLS ' */
+ icSigCmykData = 0x434D594BL, /* 'CMYK' */
+ icSigCmyData = 0x434D5920L, /* 'CMY ' */
+
+ icSig2colorData = 0x32434C52L, /* '2CLR' */
+ icSig3colorData = 0x33434C52L, /* '3CLR' */
+ icSig4colorData = 0x34434C52L, /* '4CLR' */
+ icSig5colorData = 0x35434C52L, /* '5CLR' */
+ icSig6colorData = 0x36434C52L, /* '6CLR' */
+ icSig7colorData = 0x37434C52L, /* '7CLR' */
+ icSig8colorData = 0x38434C52L, /* '8CLR' */
+ icSig9colorData = 0x39434C52L, /* '9CLR' */
+ icSig10colorData = 0x41434C52L, /* 'ACLR' */
+ icSig11colorData = 0x42434C52L, /* 'BCLR' */
+ icSig12colorData = 0x43434C52L, /* 'CCLR' */
+ icSig13colorData = 0x44434C52L, /* 'DCLR' */
+ icSig14colorData = 0x45434C52L, /* 'ECLR' */
+ icSig15colorData = 0x46434C52L, /* 'FCLR' */
+
+ icSigMch5Data = 0x4D434835L, /* 'MCH5' Colorsync ? */
+ icSigMch6Data = 0x4D434836L, /* 'MCH6' Hexachrome: CMYKOG */
+ icSigMch7Data = 0x4D434837L, /* 'MCH7' Colorsync ? */
+ icSigMch8Data = 0x4D434838L, /* 'MCH8' Colorsync ? */
+ icSigNamedData = 0x6e6d636cL, /* 'nmcl' ??? */
+
+ icMaxEnumData = icMaxTagVal
+} icColorSpaceSignature;
+
+/* profileClass enumerations */
+typedef enum {
+ icSigInputClass = 0x73636E72L, /* 'scnr' */
+ icSigDisplayClass = 0x6D6E7472L, /* 'mntr' */
+ icSigOutputClass = 0x70727472L, /* 'prtr' */
+ icSigLinkClass = 0x6C696E6BL, /* 'link' */
+ icSigAbstractClass = 0x61627374L, /* 'abst' */
+ icSigColorSpaceClass = 0x73706163L, /* 'spac' */
+ icSigNamedColorClass = 0x6e6d636cL, /* 'nmcl' */
+ icMaxEnumClass = icMaxTagVal
+} icProfileClassSignature;
+
+/* Platform Signatures */
+typedef enum {
+ icSigMacintosh = 0x4150504CL, /* 'APPL' */
+ icSigMicrosoft = 0x4D534654L, /* 'MSFT' */
+ icSigSolaris = 0x53554E57L, /* 'SUNW' */
+ icSigSGI = 0x53474920L, /* 'SGI ' */
+ icSigTaligent = 0x54474E54L, /* 'TGNT' */
+ icMaxEnumPlatform = icMaxTagVal
+} icPlatformSignature;
+
+/* Rendering Intent Gamut Signatures */
+typedef enum {
+ icSigPerceptualReferenceMediumGamut = 0x70726d67L, /* 'prmg' */
+ icMaxEnumReferenceMediumGamut = icMaxTagVal
+} icReferenceMediumGamutSignature;
+
+/*------------------------------------------------------------------------*/
+/*
+ * Other enums
+ */
+
+/* Measurement Flare, used in the measurmentType tag */
+typedef enum {
+ icFlare0 = 0x00000000L, /* 0% flare */
+ icFlare100 = 0x00000001L, /* 100% flare */
+ icMaxFlare = icMaxTagVal
+} icMeasurementFlare;
+
+/* Measurement Geometry, used in the measurmentType tag */
+typedef enum {
+ icGeometryUnknown = 0x00000000L, /* Unknown */
+ icGeometry045or450 = 0x00000001L, /* 0/45, 45/0 */
+ icGeometry0dord0 = 0x00000002L, /* 0/d or d/0 */
+ icMaxGeometry = icMaxTagVal
+} icMeasurementGeometry;
+
+/* Rendering Intents, used in the profile header */
+typedef enum {
+ icPerceptual = 0,
+ icRelativeColorimetric = 1,
+ icSaturation = 2,
+ icAbsoluteColorimetric = 3,
+ icMaxEnumIntent = icMaxTagVal
+} icRenderingIntent;
+
+/* Different Spot Shapes currently defined, used for screeningType */
+typedef enum {
+ icSpotShapeUnknown = 0,
+ icSpotShapePrinterDefault = 1,
+ icSpotShapeRound = 2,
+ icSpotShapeDiamond = 3,
+ icSpotShapeEllipse = 4,
+ icSpotShapeLine = 5,
+ icSpotShapeSquare = 6,
+ icSpotShapeCross = 7,
+ icMaxEnumSpot = icMaxTagVal
+} icSpotShape;
+
+/* Standard Observer, used in the measurmentType tag */
+typedef enum {
+ icStdObsUnknown = 0x00000000L, /* Unknown */
+ icStdObs1931TwoDegrees = 0x00000001L, /* 2 deg */
+ icStdObs1964TenDegrees = 0x00000002L, /* 10 deg */
+ icMaxStdObs = icMaxTagVal
+} icStandardObserver;
+
+/* Pre-defined illuminants, used in measurement and viewing conditions type */
+typedef enum {
+ icIlluminantUnknown = 0x00000000L,
+ icIlluminantD50 = 0x00000001L,
+ icIlluminantD65 = 0x00000002L,
+ icIlluminantD93 = 0x00000003L,
+ icIlluminantF2 = 0x00000004L,
+ icIlluminantD55 = 0x00000005L,
+ icIlluminantA = 0x00000006L,
+ icIlluminantEquiPowerE = 0x00000007L,
+ icIlluminantF8 = 0x00000008L,
+ icMaxEnumIlluminant = icMaxTagVal
+} icIlluminant;
+
+/* A not so exhaustive list of language codes */
+typedef enum {
+ icLanguageCodeEnglish = 0x656E, /* 'en' */
+ icLanguageCodeGerman = 0x6465, /* 'de' */
+ icLanguageCodeItalian = 0x6974, /* 'it' */
+ icLanguageCodeDutch = 0x6E6C, /* 'nl' */
+ icLanguageCodeSweden = 0x7376, /* 'sv' */
+ icLanguageCodeSpanish = 0x6573, /* 'es' */
+ icLanguageCodeDanish = 0x6461, /* 'da' */
+ icLanguageCodeNorwegian = 0x6E6F, /* 'no' */
+ icLanguageCodeJapanese = 0x6A61, /* 'ja' */
+ icLanguageCodeFinish = 0x6669, /* 'fi' */
+ icLanguageCodeTurkish = 0x7472, /* 'tr' */
+ icLanguageCodeKorean = 0x6B6F, /* 'ko' */
+ icLanguageCodeChienese = 0x7A68, /* 'zh' */
+ icLanguageCodeFrench = 0x6672, /* 'fr' */
+ icMaxEnumLanguageCode = 0xFFFF
+} icEnumLanguageCode;
+
+/* A not so exhaustive list of region codes. */
+typedef enum {
+ icRegionCodeUSA = 0x5553, /* 'US' */
+ icRegionCodeUnitedKingdom = 0x554B, /* 'UK' */
+ icRegionCodeGermany = 0x4445, /* 'DE' */
+ icRegionCodeItaly = 0x4954, /* 'IT' */
+ icRegionCodeNetherlands = 0x4E4C, /* 'NL' */
+ icRegionCodeSpain = 0x4543, /* 'ES' */
+ icRegionCodeDenmark = 0x444B, /* 'DK' */
+ icRegionCodeNorway = 0x4E4F, /* 'ND' */
+ icRegionCodeJapan = 0x4A50, /* 'JP' */
+ icRegionCodeFinland = 0x4649, /* 'FI' */
+ icRegionCodeTurkey = 0x5452, /* 'TR' */
+ icRegionCodeKorea = 0x4B52, /* 'KR' */
+ icRegionCodeChina = 0x434E, /* 'CN' */
+ icRegionCodeTaiwan = 0x5457, /* 'TW' */
+ icRegionCodeFrance = 0x4652, /* 'FR' */
+ icMaxEnumRegionCode = 0xFFFF
+} icEnumRegionCode;
+
+/* media type for icSigDeviceSettingsTag */
+typedef enum {
+ icStandard = 1,
+ icTrans = 2, /* transparency */
+ icGloss = 3,
+ icUser1 = 256,
+ icMaxDeviceMedia = icMaxTagVal
+} icDeviceMedia;
+
+/* halftone settings for icSigDeviceSettingTag */
+typedef enum {
+ icNone = 1,
+ icCoarse = 2,
+ icFine = 3,
+ icLineArt = 4,
+ icErrorDiffusion = 5,
+ icReserved6 = 6,
+ icReserved7 = 7,
+ icReserved8 = 8,
+ icReserved9 = 9,
+ icGrayScale = 10,
+ icUser2 = 256,
+ icMaxDither = icMaxTagVal
+} icDeviceDither;
+
+/* signatures for icSigDeviceSettingsTag */
+typedef enum {
+ icSigResolution = 0x72736c6eL, /* 'rsln' */
+ icSigMedia = 0x6d747970L, /* 'mtyp' */
+ icSigHalftone = 0x6866746eL, /* 'hftn' */
+ icMaxSettings = icMaxTagVal
+} icSettingsSig;
+
+/* measurement units for the icResponseCurveSet16Type */
+typedef enum {
+ icStaA = 0x53746141L, /* 'StaA' */
+ icStaE = 0x53746145L, /* 'StaE' */
+ icStaI = 0x53746149L, /* 'StaI' */
+ icStaT = 0x53746154L, /* 'StaT' */
+ icStaM = 0x5374614dL, /* 'StaM' */
+ icDN = 0x444e2020L, /* 'DN ' */
+ icDNP = 0x444e2050L, /* 'DN P' */
+ icDNN = 0x444e4e20L, /* 'DNN ' */
+ icDNNP = 0x444e4e50L, /* 'DNNP' */
+ icMaxUnits = icMaxTagVal
+} icMeasUnitsSig;
+
+/* Parametric curve types for icSigParametricCurveType */
+typedef enum {
+ icCurveFunction1 = 0x0000,
+ icCurveFunction3 = 0x0001,
+ icCurveFunction4 = 0x0002,
+ icCurveFunction5 = 0x0003,
+ icCurveFunction7 = 0x0004
+} icParametricCurveFunctionType;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* ICCV42_H */
diff --git a/icc/iccdump.c b/icc/iccdump.c
new file mode 100644
index 0000000..aaf14f0
--- /dev/null
+++ b/icc/iccdump.c
@@ -0,0 +1,269 @@
+
+/*
+ * International Color Consortium Format Library (icclib)
+ * File dump utility.
+ *
+ * Author: Graeme W. Gill
+ * Date: 1999/11/29
+ * Version: 2.15
+ *
+ * Copyright 1997 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/*
+ * TTBD:
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include "icc.h"
+
+void error(char *fmt, ...), warning(char *fmt, ...);
+
+void usage(void) {
+ fprintf(stderr,"Dump an ICC file in human readable form, V%s\n",ICCLIB_VERSION_STR);
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: iccdump [-v level] [-t tagname] [-s] infile\n");
+ fprintf(stderr," -v level Verbose level 1-3 (default 2)\n");
+ fprintf(stderr," -t tag Dump this tag only (can be used multiple times)\n");
+ fprintf(stderr," -s Search for embedded profile\n");
+ fprintf(stderr," -i Check V4 ID value\n");
+ exit(1);
+}
+
+#define MXTGNMS 30
+
+int
+main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ char in_name[500];
+ int ntag_names = 0;
+ char tag_names[MXTGNMS][5];
+ int verb = 2;
+ int search = 0;
+ int chid = 0; /* Check V4 ID */
+ int ecount = 1; /* Embedded count */
+ int offset = 0; /* Offset to read profile from */
+ int found;
+ icmFile *fp, *op;
+ icc *icco;
+ int rv = 0;
+
+ if (argc < 2)
+ usage();
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage();
+
+ /* Verbosity */
+ else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ fa = nfa;
+ if (na == NULL) usage();
+ verb = atoi(na);
+ }
+ /* Tag name */
+ else if (argv[fa][1] == 't' || argv[fa][1] == 'T') {
+ fa = nfa;
+ if (na == NULL) usage();
+ if (ntag_names >= MXTGNMS)
+ usage();
+ strncpy(tag_names[ntag_names],na,4);
+ tag_names[ntag_names++][4] = '\000';
+ }
+ /* Search */
+ else if (argv[fa][1] == 's' || argv[fa][1] == 'S') {
+ search = 1;
+ }
+ /* Check ID */
+ else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
+ chid = 1;
+ }
+
+ else
+ usage();
+ }
+ else
+ break;
+ }
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(in_name,argv[fa]);
+
+ /* Open up the file for reading */
+ if ((fp = new_icmFileStd_name(in_name,"r")) == NULL)
+ error ("Can't open file '%s'",in_name);
+
+ if ((icco = new_icc()) == NULL)
+ error ("Creation of ICC object failed");
+
+ /* open output stream */
+ if ((op = new_icmFileStd_fp(stdout)) == NULL)
+ error ("Can't open stdout stream");
+
+ do {
+ found = 0;
+
+ /* Dumb search for magic number */
+ if (search) {
+ int fc = 0;
+ char c;
+
+ if (fp->seek(fp, offset) != 0)
+ break;
+
+ while(found == 0) {
+ if (fp->read(fp, &c, 1, 1) != 1) {
+ break;
+ }
+ offset++;
+
+ switch (fc) {
+ case 0:
+ if (c == 'a')
+ fc++;
+ else
+ fc = 0;
+ break;
+ case 1:
+ if (c == 'c')
+ fc++;
+ else
+ fc = 0;
+ break;
+ case 2:
+ if (c == 's')
+ fc++;
+ else
+ fc = 0;
+ break;
+ case 3:
+ if (c == 'p') {
+ found = 1;
+ offset -= 40;
+ }
+ else
+ fc = 0;
+ break;
+ }
+ }
+ }
+
+ if (search == 0 || found != 0) {
+ ecount++;
+
+ if (search)
+ printf("Embedded profile found at file offset %d (0x%x)\n",offset,offset);
+
+ if ((rv = icco->read(icco,fp,offset)) != 0)
+ error ("%d, %s",rv,icco->err);
+
+ if (ntag_names > 0) {
+ int i;
+ for (i = 0; i < ntag_names; i++) {
+
+ icTagSignature sig = str2tag(tag_names[i]);
+
+ /* Try and locate that particular tag */
+ if ((rv = icco->find_tag(icco,sig)) != 0 && rv != 1) {
+ warning ("icc->find_tag() can't find tag '%s' in file", tag2str(sig));
+ } else {
+ icmBase *ob;
+
+ if (rv == 1)
+ warning ("icc->find_tag() tag '%s' found but unknown type", tag_names[i]);
+ if ((ob = icco->read_tag_any(icco, sig)) == NULL) {
+ warning("Failed to read tag '%s': %d, %s",tag_names[i], icco->errc,icco->err);
+ } else {
+ ob->dump(ob, op, verb-1);
+ }
+ }
+ }
+ } else {
+ icco->dump(icco, op, verb);
+ if (chid && icco->header->majv >= 4) {
+ unsigned char id[16];
+ rv = icco->check_id(icco, id);
+ if (rv == 0)
+ printf("Id checks\n");
+ else if (rv == 1)
+ printf("Id can't be checked because it's not set\n");
+ else if (rv == 2) {
+ icmHeader *p = icco->header;
+ printf("Id check fails:\n");
+ op->gprintf(op," ID is = %02X%02X%02X%02X%02X%02X%02X%02X"
+ "%02X%02X%02X%02X%02X%02X%02X%02X\n",
+ p->id[0], p->id[1], p->id[2], p->id[3],
+ p->id[4], p->id[5], p->id[6], p->id[7],
+ p->id[8], p->id[9], p->id[10], p->id[11],
+ p->id[12], p->id[13], p->id[14], p->id[15]);
+ op->gprintf(op," ID should be = %02X%02X%02X%02X%02X%02X%02X%02X"
+ "%02X%02X%02X%02X%02X%02X%02X%02X\n",
+ id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7],
+ id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15]);
+ } else
+ warning("Failed to do ID check %d, %s",icco->errc,icco->err);
+ }
+ }
+ offset += 128;
+ }
+ } while (found != 0);
+
+ icco->del(icco);
+ op->del(op);
+ fp->del(fp);
+
+ return 0;
+}
+
+
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"iccdump: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"iccdump: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
diff --git a/icc/icclu.c b/icc/icclu.c
new file mode 100644
index 0000000..ea32da0
--- /dev/null
+++ b/icc/icclu.c
@@ -0,0 +1,424 @@
+
+/*
+ * International Color Consortium Format Library (icclib)
+ * Profile color lookup utility.
+ *
+ * Author: Graeme W. Gill
+ * Date: 1999/11/29
+ * Version: 2.15
+ *
+ * Copyright 1998 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/* TTBD:
+ *
+ */
+
+/*
+
+ This file is intended to exercise the ability
+ of the icc library to translate colors through a profile.
+ It also serves as a concise example of how to do this.
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include "icc.h"
+
+void error(char *fmt, ...), warning(char *fmt, ...);
+
+void usage(void) {
+ fprintf(stderr,"Translate colors through an ICC profile, V%s\n",ICCLIB_VERSION_STR);
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: icclu [-v level] [-f func] [-i intent] [-o order] profile\n");
+ fprintf(stderr," -v level Verbosity level 0 - 2 (default = 1)\n");
+ fprintf(stderr," -f function f = forward, b = backwards, g = gamut, p = preview\n");
+ fprintf(stderr," -i intent p = perceptual, r = relative colorimetric,\n");
+ fprintf(stderr," s = saturation, a = absolute\n");
+// fprintf(stderr," P = absolute perceptual, S = absolute saturation\n");
+ fprintf(stderr," -p oride x = XYZ_PCS, l = Lab_PCS, y = Yxy,\n");
+ fprintf(stderr," -o order n = normal (priority: lut > matrix > monochrome)\n");
+ fprintf(stderr," r = reverse (priority: monochrome > matrix > lut)\n");
+ fprintf(stderr," -s scale Scale device range 0.0 - scale rather than 0.0 - 1.0\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr," The colors to be translated should be fed into standard input,\n");
+ fprintf(stderr," one input color per line, white space separated.\n");
+ fprintf(stderr," A line starting with a # will be ignored.\n");
+ fprintf(stderr," A line not starting with a number will terminate the program.\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ char prof_name[500];
+ icmFile *fp;
+ icc *icco;
+ int verb = 1;
+ double scale = 0.0; /* Device value scale factor */
+ int rv = 0;
+ int repYxy = 0; /* Report Yxy */
+ char buf[200];
+ double oin[MAX_CHAN], in[MAX_CHAN], out[MAX_CHAN];
+
+ icmLuBase *luo;
+ icColorSpaceSignature ins, outs; /* Type of input and output spaces */
+ int inn, outn; /* Number of components */
+ icmLuAlgType alg; /* Type of lookup algorithm */
+
+ /* Lookup parameters */
+ icmLookupFunc func = icmFwd; /* Default */
+ icRenderingIntent intent = icmDefaultIntent; /* Default */
+ icColorSpaceSignature pcsor = icmSigDefaultData; /* Default */
+ icmLookupOrder order = icmLuOrdNorm; /* Default */
+
+ if (argc < 2)
+ usage();
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage();
+
+ /* Verbosity */
+ else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ fa = nfa;
+ if (na == NULL)
+ verb = 2;
+ else {
+ if (na[0] == '0')
+ verb = 0;
+ else if (na[0] == '1')
+ verb = 1;
+ else if (na[0] == '2')
+ verb = 2;
+ else
+ usage();
+ }
+ }
+
+ /* function */
+ else if (argv[fa][1] == 'f' || argv[fa][1] == 'F') {
+ fa = nfa;
+ if (na == NULL) usage();
+ switch (na[0]) {
+ case 'f':
+ case 'F':
+ func = icmFwd;
+ break;
+ case 'b':
+ case 'B':
+ func = icmBwd;
+ break;
+ case 'g':
+ case 'G':
+ func = icmGamut;
+ break;
+ case 'p':
+ case 'P':
+ func = icmPreview;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ /* Intent */
+ else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
+ fa = nfa;
+ if (na == NULL) usage();
+ switch (na[0]) {
+ case 'p':
+ intent = icPerceptual;
+ break;
+ case 'r':
+ intent = icRelativeColorimetric;
+ break;
+ case 's':
+ intent = icSaturation;
+ break;
+ case 'a':
+ intent = icAbsoluteColorimetric;
+ break;
+ /* Special function icclib intents */
+ case 'P':
+ intent = icmAbsolutePerceptual;
+ break;
+ case 'S':
+ intent = icmAbsoluteSaturation;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ /* Search order */
+ else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
+ fa = nfa;
+ if (na == NULL) usage();
+ switch (na[0]) {
+ case 'n':
+ case 'N':
+ order = icmLuOrdNorm;
+ break;
+ case 'r':
+ case 'R':
+ order = icmLuOrdRev;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ /* PCS override */
+ else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
+ fa = nfa;
+ if (na == NULL) usage();
+ switch (na[0]) {
+ case 'x':
+ case 'X':
+ pcsor = icSigXYZData;
+ repYxy = 0;
+ break;
+ case 'l':
+ case 'L':
+ pcsor = icSigLabData;
+ repYxy = 0;
+ break;
+ case 'y':
+ case 'Y':
+ pcsor = icSigXYZData;
+ repYxy = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ /* Device scale */
+ else if (argv[fa][1] == 's' || argv[fa][1] == 'S') {
+ fa = nfa;
+ if (na == NULL) usage();
+ scale = atof(na);
+ if (scale <= 0.0) usage();
+ }
+
+ else
+ usage();
+ } else
+ break;
+ }
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(prof_name,argv[fa]);
+
+ /* Open up the profile for reading */
+ if ((fp = new_icmFileStd_name(prof_name,"r")) == NULL)
+ error ("Can't open file '%s'",prof_name);
+
+ if ((icco = new_icc()) == NULL)
+ error ("Creation of ICC object failed");
+
+ if ((rv = icco->read(icco,fp,0)) != 0)
+ error ("%d, %s",rv,icco->err);
+
+ if (verb > 1) {
+ icmFile *op;
+ if ((op = new_icmFileStd_fp(stdout)) == NULL)
+ error ("Can't open stdout");
+ icco->header->dump(icco->header, op, 1);
+ op->del(op);
+ }
+
+ /* Get a conversion object */
+ if ((luo = icco->get_luobj(icco, func, intent, pcsor, order)) == NULL)
+ error ("%d, %s",icco->errc, icco->err);
+
+ /* Get details of conversion (Arguments may be NULL if info not needed) */
+ luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL, NULL);
+
+ if (repYxy) { /* report Yxy rather than XYZ */
+ if (ins == icSigXYZData)
+ ins = icSigYxyData;
+ if (outs == icSigXYZData)
+ outs = icSigYxyData;
+ }
+
+ /* Process colors to translate */
+ for (;;) {
+ int i,j;
+ char *bp, *nbp;
+
+ /* Init default input values */
+ for (i = 0; i < MAX_CHAN; i++) {
+ in[i] = oin[i] = 0.0;
+ }
+
+ /* Read in the next line */
+ if (fgets(buf, 200, stdin) == NULL)
+ break;
+ if (buf[0] == '#') {
+ if (verb > 0)
+ fprintf(stdout,"%s\n",buf);
+ continue;
+ }
+ /* For each input number */
+ for (bp = buf-1, nbp = buf, i = 0; i < MAX_CHAN; i++) {
+ bp = nbp;
+ in[i] = oin[i] = strtod(bp, &nbp);
+ if (nbp == bp)
+ break; /* Failed */
+ }
+ if (i == 0)
+ break;
+
+ /* If device data and scale */
+ if(scale > 0.0
+ && ins != icSigXYZData
+ && ins != icSigLabData
+ && ins != icSigLuvData
+ && ins != icSigYCbCrData
+ && ins != icSigYxyData
+ && ins != icSigHsvData
+ && ins != icSigHlsData) {
+ for (i = 0; i < MAX_CHAN; i++) {
+ in[i] /= scale;
+ }
+ }
+
+ if (repYxy && ins == icSigYxyData) {
+ double Y = in[0];
+ double x = in[1];
+ double y = in[2];
+ double z = 1.0 - x - y;
+ double sum;
+ if (y < 1e-6) {
+ in[0] = in[1] = in[2] = 0.0;
+ } else {
+ sum = Y/y;
+ in[0] = x * sum;
+ in[1] = Y;
+ in[2] = z * sum;
+ }
+ }
+
+ /* Do conversion */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",icco->errc,icco->err);
+
+ /* Output the results */
+ if (verb > 0) {
+ for (j = 0; j < inn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f",oin[j]);
+ else
+ fprintf(stdout,"%f",oin[j]);
+ }
+ printf(" [%s] -> %s -> ", icm2str(icmColorSpaceSignature, ins),
+ icm2str(icmLuAlg, alg));
+ }
+
+ if (repYxy && outs == icSigYxyData) {
+ double X = out[0];
+ double Y = out[1];
+ double Z = out[2];
+ double sum = X + Y + Z;
+ if (sum < 1e-6) {
+ out[0] = out[1] = out[2] = 0.0;
+ } else {
+ out[0] = Y;
+ out[1] = X/sum;
+ out[2] = Y/sum;
+ }
+ }
+
+ /* If device data and scale */
+ if(scale > 0.0
+ && outs != icSigXYZData
+ && outs != icSigLabData
+ && outs != icSigLuvData
+ && outs != icSigYCbCrData
+ && outs != icSigYxyData
+ && outs != icSigHsvData
+ && outs != icSigHlsData) {
+ for (i = 0; i < MAX_CHAN; i++) {
+ out[i] *= scale;
+ }
+ }
+
+ for (j = 0; j < outn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f",out[j]);
+ else
+ fprintf(stdout,"%f",out[j]);
+ }
+ if (verb > 0)
+ printf(" [%s]", icm2str(icmColorSpaceSignature, outs));
+
+ if (verb > 0 && rv != 0)
+ fprintf(stdout," (clip)");
+
+ fprintf(stdout,"\n");
+
+ }
+
+ /* Done with lookup object */
+ luo->del(luo);
+
+ icco->del(icco);
+
+ fp->del(fp);
+
+ return 0;
+}
+
+
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"icclu: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"icclu: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
diff --git a/icc/iccrw.c b/icc/iccrw.c
new file mode 100644
index 0000000..807c5f4
--- /dev/null
+++ b/icc/iccrw.c
@@ -0,0 +1,311 @@
+
+/*
+ * International Color Consortium Format Library (icclib)
+ * Profile read then re-write skeleton utility.
+ *
+ * Author: Graeme W. Gill
+ * Date: 1999/11/29
+ * Version: 2.15
+ *
+ * Copyright 1997 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/* TTBD:
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include "icc.h"
+
+#undef TEST_VIDGAMTAG /* Add ColorSync 2.5 VideoCardGamma tag with linear table */
+#undef TEST_SRGB_FIX /* Some test code */
+#undef WP_PATCH /* Overwrite the white point */
+#undef INVERT_GRAY /* Invert single TRC gray profile */
+#undef MOD_A2B /* 0 */ /* Modify A2B RGB tables */
+
+void error(char *fmt, ...), warning(char *fmt, ...);
+
+void usage(void) {
+ fprintf(stderr,"Read and then re-write an ICC profile V%s\n",ICCLIB_VERSION_STR);
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: iccrw readprofile writeprofile\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ char in_name[500];
+ char out_name[500];
+ icmFile *rd_fp, *wr_fp;
+ icc *icco;
+ int verb = 0;
+ int rv = 0;
+
+ if (argc < 3)
+ usage();
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage();
+
+ if (argv[fa][1] == 'v' || argv[fa][1] == 'V')
+ verb = 1;
+
+ /* No options */
+ usage();
+
+ } else
+ break;
+ }
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(in_name,argv[fa++]);
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(out_name,argv[fa]);
+
+ /* Open up the profile for reading */
+ if ((rd_fp = new_icmFileStd_name(in_name,"r")) == NULL)
+ error ("Can't open file '%s'",in_name);
+
+ if ((icco = new_icc()) == NULL)
+ error ("Creation of ICC object failed");
+
+ /* Read header etc. */
+ if ((rv = icco->read(icco,rd_fp,0)) != 0)
+ error ("%d, %s",rv,icco->err);
+
+ /* Read every tag */
+ if (icco->read_all_tags(icco) != 0) {
+ error("Unable to read all tags: %d, %s",icco->errc,icco->err);
+ }
+
+ rd_fp->del(rd_fp);
+
+ /* ======================================= */
+ /* Change profile in here. */
+
+#ifdef TEST_SRGB_FIX /* Some test code */
+ /* Try deleting the black point tag */
+ {
+ if (icco->delete_tag(icco, icSigMediaBlackPointTag) != 0) {
+ error("Unable to delete blackpoint tag: %d, %s",icco->errc,icco->err);
+ }
+ }
+
+ /* Fix up sRGB profile curve */
+ {
+ icmCurve *ro;
+ int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmCurve *)icco->read_tag(icco, icSigRedTRCTag);
+ if (ro == NULL)
+ error("Unable to read rTRC");
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigCurveType)
+ error("rTRC is not CurveType");
+
+ for (i = 0; i < ro->size; i++) {
+ double vi, vo;
+ vi = i/(double)(ro->size-1);
+
+ if (vi < 0.04045) {
+ vo = vi/12.92;
+ } else {
+ vo = pow((0.055+vi)/1.055,2.4);
+ }
+ ro->data[i] = vo;
+ }
+ }
+
+ /* Show we modified this ICC file */
+ icco->header->cmmId = str2tag("argl"); /* CMM for profile - Argyll CMM */
+#endif
+
+#ifdef TEST_VIDGAMTAG
+ /* delete video card gamma tag (c/o Neil Okamoto) */
+ {
+ if (icco->find_tag(icco, icSigVideoCardGammaTag) == 0)
+ if (icco->delete_tag(icco, icSigVideoCardGammaTag) != 0)
+ error("Unable to delete videocardgamma tag: %d, %s",icco->errc,icco->err);
+ }
+ /* Add a video card gamma table */
+ {
+ int c,i;
+ icmVideoCardGamma *wo;
+ wo = (icmVideoCardGamma *)icco->add_tag(icco, icSigVideoCardGammaTag, icSigVideoCardGammaType);
+ if (wo == NULL)
+ error ("Unable to add VideoCardGamma tag");
+
+ wo->tagType = icmVideoCardGammaTableType;
+ wo->u.table.channels = 3; /* rgb */
+ wo->u.table.entryCount = 256; /* full lut */
+ wo->u.table.entrySize = 1; /* byte */
+ wo->allocate((icmBase*)wo);
+ for (c=0; c<3; c++)
+ for (i=0; i<256; i++)
+ ((unsigned char*)wo->u.table.data)[256*c+i] = 255-i;
+ }
+
+ /* Show we modified this ICC file */
+ icco->header->cmmId = str2tag("argl"); /* CMM for profile - Argyll CMM */
+#endif
+
+#ifdef WP_PATCH /* Overwrite the white point */
+ /* delete white point tag */
+ {
+ if (icco->find_tag(icco, icSigMediaWhitePointTag) == 0)
+ if (icco->delete_tag(icco, icSigMediaWhitePointTag) != 0)
+ error("Unable to delete white point tag tag: %d, %s",icco->errc,icco->err);
+ }
+ /* Add a new white point tag */
+ {
+ double lab[3];
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)icco->add_tag(
+ icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",icco->errc, icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ lab[0] = 79.8296;
+ lab[1] = -0.004042 + 0.842312;
+ lab[2] = 3.019928 + 0.810044;
+ icmLab2XYZ(&icmD50, lab, lab);
+ icmAry2XYZ(wo->data[0], lab);
+ }
+
+ /* Show we modified this ICC file */
+ icco->header->cmmId = str2tag("argl"); /* CMM for profile - Argyll CMM */
+#endif
+#ifdef INVERT_GRAY /* Invert single TRC gray profile */
+ {
+ icmCurve *ro;
+ int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmCurve *)icco->read_tag(icco, icSigGrayTRCTag);
+ if (ro == NULL)
+ error("Unable to read GrayTRC");
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigCurveType)
+ error("GrayTRC is not CurveType");
+
+ /* Swap the curve entries from top to bottom */
+ for (i = 0; i < (ro->size/2); i++) {
+ double temp;
+ int ii = 255 - i;
+
+ temp = ro->data[ii];
+ ro->data[ii] = ro->data[i];
+ ro->data[i] = temp;
+ }
+ }
+
+#endif
+
+#ifdef MOD_A2B
+ {
+ icmLut *ro;
+ unsigned long size;
+ unsigned int i, j;
+ icTagSignature sig;
+
+ if (MOD_A2B == 0)
+ sig = icSigAToB0Tag;
+ else if (MOD_A2B == 1)
+ sig = icSigAToB1Tag;
+ else
+ sig = icSigAToB2Tag;
+
+ /* Try and read the tag from the file */
+ ro = (icmLut *)icco->read_tag(icco, sig);
+ if (ro == NULL)
+ error("Failed to read A2B tag");
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigLut16Type)
+ error("A2B is not 16 bitg");
+
+ /* Simply apply a gamma to the corresponding input curve */
+ for (i = 0; i < ro->inputChan; i++) { /* Input tables */
+ double val;
+ j = MOD_A2B;
+ val = ro->inputTable[i * ro->inputEnt + j];
+ val = pow(val, 2.0);
+ ro->inputTable[i * ro->inputEnt + j] = val;
+ }
+ }
+#endif /* MOD_A2B */
+
+ /* ======================================= */
+
+ /* Open up the other profile for writing */
+ if ((wr_fp = new_icmFileStd_name(out_name,"w")) == NULL)
+ error ("Can't open file '%s'",out_name);
+
+ if ((rv = icco->write(icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,icco->err);
+
+ icco->del(icco);
+ wr_fp->del(wr_fp);
+
+ return 0;
+}
+
+/* ------------------------------------------------ */
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"iccrw: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"iccrw: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
diff --git a/icc/iccstd.c b/icc/iccstd.c
new file mode 100644
index 0000000..742be4d
--- /dev/null
+++ b/icc/iccstd.c
@@ -0,0 +1,443 @@
+
+/*
+ * ICC library stdio and malloc utility classes.
+ *
+ * Author: Graeme W. Gill
+ * Date: 2002/10/24
+ * Version: 2.15
+ *
+ * Copyright 1997 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ *
+ * These are kept in a separate file to allow them to be
+ * selectively ommitted from the icc library.
+ *
+ */
+
+#ifndef COMBINED_STD
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <time.h>
+#ifdef __sun
+#include <unistd.h>
+#endif
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+#include "icc.h"
+
+#endif /* !COMBINED_STD */
+
+#if defined(SEPARATE_STD) || defined(COMBINED_STD)
+
+#ifdef _MSC_VER
+#define fileno _fileno
+#endif
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t)(-1))
+#endif
+
+/* ------------------------------------------------- */
+/* Standard Heap allocator icmAlloc compatible class */
+/* Just call the standard system function */
+
+#ifdef ICC_DEBUG_MALLOC
+
+/* Make sure that inline malloc #defines are turned off for this file */
+#undef was_debug_malloc
+#ifdef malloc
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+#define was_debug_malloc
+#endif /* dmalloc */
+
+static void *icmAllocStd_dmalloc(
+struct _icmAlloc *pp,
+size_t size,
+char *name,
+int line
+) {
+ return malloc(size);
+}
+
+static void *icmAllocStd_dcalloc(
+struct _icmAlloc *pp,
+size_t num,
+size_t size,
+char *name,
+int line
+) {
+ if (size != 0 && num > (SIZE_MAX/size))
+ return NULL;
+ calloc(num, size);
+}
+
+static void *icmAllocStd_drealloc(
+struct _icmAlloc *pp,
+void *ptr,
+size_t size,
+char *name,
+int line
+) {
+ return realloc(ptr, size);
+}
+
+
+static void icmAllocStd_dfree(
+struct _icmAlloc *pp,
+void *ptr,
+char *name,
+int line
+) {
+ free(ptr);
+}
+
+/* we're done with the AllocStd object */
+static void icmAllocStd_delete(
+icmAlloc *pp
+) {
+ icmAllocStd *p = (icmAllocStd *)pp;
+
+ free(p);
+}
+
+/* Create icmAllocStd */
+icmAlloc *new_icmAllocStd() {
+ icmAllocStd *p;
+ if ((p = (icmAllocStd *) calloc(1,sizeof(icmAllocStd))) == NULL)
+ return NULL;
+ p->dmalloc = icmAllocStd_dmalloc;
+ p->dcalloc = icmAllocStd_dcalloc;
+ p->drealloc = icmAllocStd_drealloc;
+ p->dfree = icmAllocStd_dfree;
+ p->del = icmAllocStd_delete;
+
+ return (icmAlloc *)p;
+}
+
+#ifdef was_debug_malloc
+#undef was_debug_malloc
+#define malloc( p, size ) dmalloc( p, size, __FILE__, __LINE__ )
+#define calloc( p, num, size ) dcalloc( p, num, size, __FILE__, __LINE__ )
+#define realloc( p, ptr, size ) drealloc( p, ptr, size, __FILE__, __LINE__ )
+#define free( p, ptr ) dfree( p, ptr , __FILE__, __LINE__ )
+#endif /* was_debug_malloc */
+
+#else /* !DEBUG_MALLOC */
+
+static void *icmAllocStd_malloc(
+struct _icmAlloc *pp,
+size_t size
+) {
+ return malloc(size);
+}
+
+static void *icmAllocStd_calloc(
+struct _icmAlloc *pp,
+size_t num,
+size_t size
+) {
+ if (size != 0 && num > (SIZE_MAX/size))
+ return NULL;
+ return calloc(num, size);
+}
+
+static void *icmAllocStd_realloc(
+struct _icmAlloc *pp,
+void *ptr,
+size_t size
+) {
+ return realloc(ptr, size);
+}
+
+
+static void icmAllocStd_free(
+struct _icmAlloc *pp,
+void *ptr
+) {
+ free(ptr);
+}
+
+/* we're done with the AllocStd object */
+static void icmAllocStd_delete(
+icmAlloc *pp
+) {
+ icmAllocStd *p = (icmAllocStd *)pp;
+
+ free(p);
+}
+
+/* Create icmAllocStd */
+icmAlloc *new_icmAllocStd() {
+ icmAllocStd *p;
+ if ((p = (icmAllocStd *) calloc(1,sizeof(icmAllocStd))) == NULL)
+ return NULL;
+ p->malloc = icmAllocStd_malloc;
+ p->calloc = icmAllocStd_calloc;
+ p->realloc = icmAllocStd_realloc;
+ p->free = icmAllocStd_free;
+ p->del = icmAllocStd_delete;
+
+ return (icmAlloc *)p;
+}
+
+#endif /* ICC_DEBUG_MALLOC */
+
+/* ------------------------------------------------- */
+/* Standard Stream file I/O icmFile compatible class */
+
+/* Get the size of the file (Only valid for reading file. */
+static size_t icmFileStd_get_size(icmFile *pp) {
+ icmFileStd *p = (icmFileStd *)pp;
+
+ return p->size;
+}
+
+/* Set current position to offset. Return 0 on success, nz on failure. */
+static int icmFileStd_seek(
+icmFile *pp,
+unsigned int offset
+) {
+ icmFileStd *p = (icmFileStd *)pp;
+
+ return fseek(p->fp, offset, SEEK_SET);
+}
+
+/* Read count items of size length. Return number of items successfully read. */
+static size_t icmFileStd_read(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ icmFileStd *p = (icmFileStd *)pp;
+
+ return fread(buffer, size, count, p->fp);
+}
+
+/* write count items of size length. Return number of items successfully written. */
+static size_t icmFileStd_write(
+icmFile *pp,
+void *buffer,
+size_t size,
+size_t count
+) {
+ icmFileStd *p = (icmFileStd *)pp;
+
+ return fwrite(buffer, size, count, p->fp);
+}
+
+
+/* do a printf */
+static int icmFileStd_printf(
+icmFile *pp,
+const char *format,
+...
+) {
+ int rv;
+ va_list args;
+ icmFileStd *p = (icmFileStd *)pp;
+
+ va_start(args, format);
+ rv = vfprintf(p->fp, format, args);
+ va_end(args);
+ return rv;
+}
+
+/* flush all write data out to secondary storage. Return nz on failure. */
+static int icmFileStd_flush(
+icmFile *pp
+) {
+ icmFileStd *p = (icmFileStd *)pp;
+
+ return fflush(p->fp);
+}
+
+/* Return the memory buffer. Error if not icmFileMem */
+static int icmFileStd_get_buf(
+icmFile *pp,
+unsigned char **buf,
+size_t *len
+) {
+ return 1;
+}
+
+/* we're done with the file object, return nz on failure */
+static int icmFileStd_delete(
+icmFile *pp
+) {
+ int rv = 0;
+ icmFileStd *p = (icmFileStd *)pp;
+ icmAlloc *al = p->al;
+ int del_al = p->del_al;
+
+ if (p->doclose != 0) {
+ if (fclose(p->fp) != 0)
+ rv = 2;
+ }
+
+ al->free(al, p); /* Free object */
+ if (del_al) /* We are responsible for deleting allocator */
+ al->del(al);
+
+ return rv;
+}
+
+/* Create icmFile given a (binary) FILE* */
+icmFile *new_icmFileStd_fp(
+FILE *fp
+) {
+ return new_icmFileStd_fp_a(fp, NULL);
+}
+
+/* Create icmFile given a (binary) FILE* and allocator */
+icmFile *new_icmFileStd_fp_a(
+FILE *fp,
+icmAlloc *al /* heap allocator, NULL for default */
+) {
+ icmFileStd *p;
+ int del_al = 0;
+ struct stat sbuf;
+
+ if (al == NULL) { /* None provided, create default */
+ if ((al = new_icmAllocStd()) == NULL)
+ return NULL;
+ del_al = 1; /* We need to delete the allocator we created */
+ }
+
+ if ((p = (icmFileStd *) al->calloc(al, 1, sizeof(icmFileStd))) == NULL) {
+ if (del_al)
+ al->del(al);
+ return NULL;
+ }
+ p->al = al; /* Heap allocator */
+ p->del_al = del_al; /* Flag noting whether we delete it */
+ p->get_size = icmFileStd_get_size;
+ p->seek = icmFileStd_seek;
+ p->read = icmFileStd_read;
+ p->write = icmFileStd_write;
+ p->gprintf = icmFileStd_printf;
+ p->flush = icmFileStd_flush;
+ p->get_buf = icmFileStd_get_buf;
+ p->del = icmFileStd_delete;
+
+ if (fstat(fileno(fp), &sbuf) == 0) {
+ p->size = sbuf.st_size;
+ } else {
+ p->size = 0;
+ }
+
+ p->fp = fp;
+ p->doclose = 0;
+
+ return (icmFile *)p;
+}
+
+/* Create icmFile given a file name */
+icmFile *new_icmFileStd_name(
+char *name,
+char *mode
+) {
+ return new_icmFileStd_name_a(name, mode, NULL);
+}
+
+/* Create given a file name and allocator */
+icmFile *new_icmFileStd_name_a(
+char *name,
+char *mode,
+icmAlloc *al /* heap allocator, NULL for default */
+) {
+ FILE *fp;
+ icmFile *p;
+ char nmode[50];
+
+ strcpy(nmode, mode);
+#if !defined(O_CREAT) && !defined(_O_CREAT)
+# error "Need to #include fcntl.h!"
+#endif
+#if defined(O_BINARY) || defined(_O_BINARY)
+ strcat(nmode, "b");
+#endif
+
+ if ((fp = fopen(name,nmode)) == NULL)
+ return NULL;
+
+ p = new_icmFileStd_fp_a(fp, al);
+
+ if (p != NULL) {
+ icmFileStd *pp = (icmFileStd *)p;
+ pp->doclose = 1;
+ }
+ return p;
+}
+
+/* ------------------------------------------------- */
+
+/* Create a memory image file access class with the std allocator */
+icmFile *new_icmFileMem(
+void *base, /* Pointer to base of memory buffer */
+size_t length /* Number of bytes in buffer */
+) {
+ icmFile *p;
+ icmAlloc *al; /* memory allocator */
+
+ if ((al = new_icmAllocStd()) == NULL)
+ return NULL;
+
+ if ((p = new_icmFileMem_a(base, length, al)) == NULL) {
+ al->del(al);
+ return NULL;
+ }
+
+ ((icmFileMem *)p)->del_al = 1; /* Get icmFileMem->del to cleanup allocator */
+ return p;
+}
+
+/* Create a memory image file access class with the std allocator */
+/* and delete buffer when icmFile is deleted. */
+icmFile *new_icmFileMem_d(
+void *base, /* Pointer to base of memory buffer */
+size_t length /* Number of bytes in buffer */
+) {
+ icmFile *fp;
+
+ if ((fp = new_icmFileMem(base, length)) != NULL) {
+ ((icmFileMem *)fp)->del_buf = 1;
+ }
+ return fp;
+}
+
+/* ------------------------------------------------- */
+
+/* Create an icc with the std allocator */
+icc *new_icc(void) {
+ icc *p;
+ icmAlloc *al; /* memory allocator */
+
+ if ((al = new_icmAllocStd()) == NULL)
+ return NULL;
+
+ if ((p = new_icc_a(al)) == NULL) {
+ al->del(al);
+ return NULL;
+ }
+
+ p->del_al = 1; /* Get icc->del to cleanup allocator */
+ return p;
+}
+
+
+#endif /* defined(SEPARATE_STD) || defined(COMBINED_STD) */
diff --git a/icc/icctest.c b/icc/icctest.c
new file mode 100644
index 0000000..97a3f62
--- /dev/null
+++ b/icc/icctest.c
@@ -0,0 +1,2393 @@
+
+/*
+ * International Color Consortium Format Library (icclib)
+ * Library Read/Write test and example code.
+ *
+ * Author: Graeme W. Gill
+ * Date: 1999/11/29
+ * Version: 2.15
+ *
+ * Copyright 1997 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/* TTBD:
+ *
+ * Fix enums to be selected randomly (ie. header)
+ *
+ * Should add test of ->delete_tag()
+ * Should add test of ->rename_tag()
+ *
+ * Add many extra comments and explanations.
+ *
+ */
+
+/*
+
+ This file is intended to serve two purposes. One
+ is to minimally test the ability of the icc library
+ to read and write all tag types. The other is as
+ a source code example of how to read and write
+ each tag type, since icc.h might otherwise take
+ some effort to understand.
+
+ Note XYZ scaling to 1.0, not 100.0
+
+ */
+
+#define NTRIALS 100
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#ifdef __sun
+#include <unistd.h>
+#endif
+#include "icc.h"
+
+void error(char *fmt, ...), warning(char *fmt, ...);
+
+/* Rounded floating test numbers */
+int rand_int(int low, int high);
+unsigned int rand_o8(void), rand_o16(void), rand_o32(void);
+double rand_8f(void), rand_L8(void), rand_ab8(void);
+double rand_16f(void), rand_XYZ16(void), rand_u8f8(void), rand_L16(void), rand_ab16(void);
+double rand_u16f16(void), rand_s15f16(void);
+int dcomp(double a, double b);
+
+/* random ICC specific values */
+unsigned int rand_ScreenEncodings(void);
+unsigned int rand_DeviceAttributes(void);
+unsigned int rand_ProfileHeaderFlags(void);
+unsigned int rand_AsciiOrBinaryData(void);
+icColorSpaceSignature rand_ColorSpaceSignature(void);
+icColorSpaceSignature rand_PCS(void);
+icTechnologySignature rand_TechnologySignature(void);
+icProfileClassSignature rand_ProfileClassSignature(void);
+icPlatformSignature rand_PlatformSignature(void);
+icMeasurementFlare rand_MeasurementFlare(void);
+icMeasurementGeometry rand_MeasurementGeometry(void);
+icRenderingIntent rand_RenderingIntent(void);
+icSpotShape rand_SpotShape(void);
+icStandardObserver rand_StandardObserver(void);
+icIlluminant rand_Illuminant(void);
+
+/* declare some test functions */
+int md5_test(void);
+
+/*
+ I've split the functionality up into two pieces.
+ The main() code does the overall file write/read,
+ while the tag type code is all in the doit() function.
+ The write/read logic is all sandwiched together (distinguished
+ by the state of the mode flag), so that the code for each
+ tag type is kept adjacent.
+
+ In a real application, one wouldn't do it this way.
+*/
+
+int doit(int mode, icc *wr_icco, icc *rd_icco);
+
+int
+main(
+ int argc,
+ char *argv[]
+) {
+ char *file_name = "xxxx.icm";
+ icmFile *wr_fp, *rd_fp;
+ icc *wr_icco, *rd_icco; /* Keep object separate */
+ int rv = 0;
+ int i;
+ unsigned int offset = 0; /* File write/read offset, 0 for standard icc */
+
+ printf("ICC library regression test, V%s\n",ICCLIB_VERSION_STR);
+
+ /* Do any internal code tests. */
+
+ if (md5_test() != 0)
+ error ("MD5 checksum routine is faulty");
+
+ /* Outer loop does a number of file write/reads, */
+ /* in order to exercise random tests, and to test file offsets. */
+
+ for (i = 0; i < NTRIALS; i++) {
+ unsigned int size; /* Expected write size */
+
+ printf(".");
+ fflush(stdout);
+
+ /* -------------------------- */
+ /* Deal with writing the file */
+
+ /* Open up the file for writing */
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags with their tag types */
+ if ((rv = doit(0, wr_icco, NULL)) != 0)
+ error ("Write tags: %d, %s",rv,wr_icco->err);
+
+ /* Write the file (including all tags) out */
+ /* The last parameter is the offset to write the */
+ /* ICC profile into the file. For a standard ICC profile, */
+ /* this needs to be 0, but it might be non-zero if you are writing */
+ /* an embedded profile. */
+
+ /* Check that get_size() is working too. */
+ if ((size = wr_icco->get_size(wr_icco)) == 0)
+ error ("Write size: %d, %s",wr_icco->errc,wr_icco->err);
+
+ if ((rv = wr_icco->write(wr_icco,wr_fp,offset)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ /* To check that get_size() is correct: */
+ {
+ icmFileStd *pp = (icmFileStd *)wr_fp; /* Cheat - Look inside icmFile */
+
+ if (fseek(pp->fp, 0, SEEK_END))
+ error ("Write: seek to EOF failed");
+ if ((unsigned int)ftell(pp->fp) != offset + size)
+ error ("Write: get_size function didn't return correct value - got %d, expected %d",
+ ftell(pp->fp),offset+size);
+ }
+
+ /*
+ Would normally call icco->free(wr_icco);
+ but leave it, so that we can verify the read.
+ */
+
+ wr_fp->del(wr_fp);
+
+ /* -------------------------- */
+ /* Deal with reading and verifying the file */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ /* The last parameter is the offset to read the */
+ /* ICC profile from the file. For a standard ICC proifile, */
+ /* this needs to be 0, but it might be non-zero if you are writing */
+ /* an embedded profile this needs to be 0, but it might be non-zero */
+ /* if you are writing an embedded profile. */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,offset)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Read and verify all the tags and their tag types */
+ if ((rv = doit(1, wr_icco, rd_icco)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* -------- */
+ /* Clean up */
+ wr_icco->del(wr_icco);
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* choose another file offset to test */
+ offset = rand_int(0,72789);
+ }
+
+ printf("\nTest completed OK\n");
+
+ return 0;
+}
+
+/* -------------------------------------------------------------- */
+/* Internal routine checks. */
+
+/* Test the MD5 function. Return nz if fail. */
+int md5_test() {
+ int rv = 0;
+ int i, j;
+ icc *icco;
+ icmMD5 *m;
+
+ unsigned char chs1[16];
+ unsigned char chs2[16];
+
+ /* Standard RFC 1321 test cases */
+ struct {
+ char *s;
+ ORD32 sum[4];
+ } tc[] = {
+ { "", { 0xd41d8cd9, 0x8f00b204, 0xe9800998, 0xecf8427e } },
+ { "a", { 0x0cc175b9, 0xc0f1b6a8, 0x31c399e2, 0x69772661 } },
+ { "abc", { 0x90015098, 0x3cd24fb0, 0xd6963f7d, 0x28e17f72 } },
+ { "message digest", { 0xf96b697d, 0x7cb7938d, 0x525a2f31, 0xaaf161d0 } },
+ { "abcdefghijklmnopqrstuvwxyz", { 0xc3fcd3d7, 0x6192e400, 0x7dfb496c, 0xca67e13b } },
+ { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ { 0xd174ab98, 0xd277d9f5, 0xa5611c2c, 0x9f419d9f } },
+ { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ { 0x57edf4a2, 0x2be3c955, 0xac49da2e, 0x2107b67a } },
+ { NULL, { 0,0,0,0 } }
+ };
+
+ if ((icco = new_icc()) == NULL)
+ error ("Creation of ICC object failed");
+
+ m = new_icmMD5(icco->al);
+
+ for (i = 0; ; i++) {
+ if (tc[i].s == NULL)
+ break;
+
+ m->reset(m);
+
+ m->add(m, (unsigned char *)tc[i].s, strlen(tc[i].s));
+ m->get(m, chs2);
+
+ /* Convert reference to a byte stream */
+ chs1[0] = (tc[i].sum[0] >> 24) & 0xff,
+ chs1[1] = (tc[i].sum[0] >> 16) & 0xff,
+ chs1[2] = (tc[i].sum[0] >> 8) & 0xff,
+ chs1[3] = tc[i].sum[0] & 0xff,
+ chs1[4] = (tc[i].sum[1] >> 24) & 0xff,
+ chs1[5] = (tc[i].sum[1] >> 16) & 0xff,
+ chs1[6] = (tc[i].sum[1] >> 8) & 0xff,
+ chs1[7] = tc[i].sum[1] & 0xff,
+ chs1[8] = (tc[i].sum[2] >> 24) & 0xff,
+ chs1[9] = (tc[i].sum[2] >> 16) & 0xff,
+ chs1[10] = (tc[i].sum[2] >> 8) & 0xff,
+ chs1[11] = tc[i].sum[2] & 0xff,
+ chs1[12] = (tc[i].sum[3] >> 24) & 0xff,
+ chs1[13] = (tc[i].sum[3] >> 16) & 0xff,
+ chs1[14] = (tc[i].sum[3] >> 8) & 0xff,
+ chs1[15] = tc[i].sum[3] & 0xff;
+
+ for (j = 0; j < 16; j++) {
+ if (chs1[j] != chs2[j]) {
+ printf("MD5 check on '%s' fails at %d with:\n",tc[i].s,j);
+ printf("Sum is %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ chs2[0], chs2[1], chs2[2], chs2[3], chs2[4], chs2[5], chs2[6], chs2[7],
+ chs2[8], chs2[9], chs2[10], chs2[11], chs2[12], chs2[13], chs2[14], chs2[15]);
+ printf("Should be %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ chs1[0], chs1[1], chs1[2], chs1[3], chs1[4], chs1[5], chs1[6], chs1[7],
+ chs1[8], chs1[9], chs1[10], chs1[11], chs1[12], chs1[13], chs1[14], chs1[15]);
+ rv = 1;
+ break;
+ }
+ }
+
+ }
+ m->del(m);
+ icco->del(icco);
+
+ return rv;
+}
+
+/* -------------------------------------------------------------- */
+/* This is the code that inits and checks the header and tag data. */
+/* Note that to undestand this, you need a copy of the ICC profile */
+/* spec., and a copy of the icc header file (icc34.h in this code) */
+/* All items that begin "icXXX" are from the ICC generic icc34.h file, */
+/* while the items that begin "icmXXX" are machine versions of structures */
+/* that are specific to this library. */
+
+int doit(
+ int mode, /* 0 - write, 1 = read/verify */
+ icc *wr_icco, /* The write icc object */
+ icc *rd_icco /* The read icc object */
+) {
+ int rv = 0;
+
+ /* ----------- */
+ /* The header: */
+ if (mode == 0) {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigAbstractClass;
+ wh->colorSpace = rand_ColorSpaceSignature();
+ wh->pcs = rand_PCS();
+ wh->renderingIntent = rand_RenderingIntent();
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst1");
+ wh->model = str2tag("1234");
+ wh->attributes.l = rand_DeviceAttributes();
+ wh->flags = rand_ProfileHeaderFlags();
+
+ /* Values that may optionally be set before writing */
+ wh->attributes.h = 0x12345678;
+ wh->creator = str2tag("tst2");
+
+ /* Values that are not normally set. Set them to non-defaults for testing */
+ wh->cmmId = str2tag("tst3");
+ wh->majv = 3; /* Default version 2.1.0 */
+ wh->minv = 2;
+ wh->bfv = 1;
+ wh->date.year = rand_int(1900,3000); /* Defaults to current date */
+ wh->date.month = rand_int(1,12);
+ wh->date.day = rand_int(1,31);
+ wh->date.hours = rand_int(0,23);
+ wh->date.minutes = rand_int(0,59);
+ wh->date.seconds = rand_int(0,59);
+ wh->platform = rand_PlatformSignature();
+ wh->illuminant.X = rand_XYZ16(); /* Defaults to D50 */
+ wh->illuminant.Y = rand_XYZ16();
+ wh->illuminant.Z = rand_XYZ16();
+ } else {
+ icmHeader *rh = rd_icco->header;
+ icmHeader *wh = wr_icco->header;
+
+ /* Check all the values */
+ rv |= (rh->deviceClass != wh->deviceClass);
+ rv |= (rh->colorSpace != wh->colorSpace);
+ rv |= (rh->pcs != wh->pcs);
+ rv |= (rh->renderingIntent != wh->renderingIntent);
+ rv |= (rh->manufacturer != wh->manufacturer);
+ rv |= (rh->model != wh->model);
+ rv |= (rh->attributes.l != wh->attributes.l);
+ rv |= (rh->attributes.h != wh->attributes.h);
+ rv |= (rh->flags != wh->flags);
+ rv |= (rh->creator != wh->creator);
+ rv |= (rh->cmmId != wh->cmmId);
+ rv |= (rh->majv != wh->majv);
+ rv |= (rh->minv != wh->minv);
+ rv |= (rh->bfv != wh->bfv);
+ rv |= (rh->date.year != wh->date.year);
+ rv |= (rh->date.month != wh->date.month);
+ rv |= (rh->date.day != wh->date.day);
+ rv |= (rh->date.hours != wh->date.hours);
+ rv |= (rh->date.minutes != wh->date.minutes);
+ rv |= (rh->date.seconds != wh->date.seconds);
+ rv |= (rh->platform != wh->platform);
+ rv |= dcomp(rh->illuminant.X, wh->illuminant.X);
+ rv |= dcomp(rh->illuminant.Y, wh->illuminant.Y);
+ rv |= dcomp(rh->illuminant.Z, wh->illuminant.Z);
+ if (rv)
+ error ("Header verify failed");
+ }
+ /* ------------- */
+ /* CrdInfo info: */
+ {
+ char *str1 = "Product Name";
+ char *str2[4] = { "Intent zero CRD Name",
+ "Intent one CRD Name",
+ "Intent two CRD Name",
+ "Intent three CRD Name" };
+ static icmCrdInfo *wo;
+ if (mode == 0) {
+ unsigned int i;
+ if ((wo = (icmCrdInfo *)wr_icco->add_tag(
+ wr_icco, icSigCrdInfoTag, icSigCrdInfoType)) == NULL)
+ return 1;
+
+ wo->ppsize = strlen(str1)+1; /* Allocated and used size of text, inc null */
+ for (i = 0; i < 4; i++)
+ wo->crdsize[i] = strlen(str2[i])+1; /* Allocated and used size of text, inc null */
+
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ /* Note we could allocate and copy as we go, rather than doing them all at once. */
+
+ strcpy(wo->ppname, str1); /* Copy the text in */
+ for (i = 0; i < 4; i++)
+ strcpy(wo->crdname[i], str2[i]); /* Copy the text in */
+ } else {
+ icmCrdInfo *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmCrdInfo *)rd_icco->read_tag(rd_icco, icSigCrdInfoTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigCrdInfoType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->ppsize != wo->ppsize)
+ for (i = 0; i < 4; i++) {
+ if (ro->crdsize[i] != wo->crdsize[i])
+ error ("CrdInfo crdsize[%d] doesn't match",i);
+ }
+
+ rv |= strcmp(ro->ppname, wo->ppname);
+ for (i = 0; i < 4; i++) {
+ rv |= strcmp(ro->crdname[i], wo->crdname[i]);
+ }
+
+ if (rv)
+ error ("CrdInfo verify failed");
+ }
+ }
+ /* ---------------------- */
+ /* Curve - Linear version */
+ {
+ static icmCurve *wo;
+ if (mode == 0) {
+ if ((wo = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigRedTRCTag, icSigCurveType)) == NULL)
+ return 1;
+
+ wo->flag = icmCurveLin; /* Linear version */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ } else {
+ icmCurve *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmCurve *)rd_icco->read_tag(rd_icco, icSigRedTRCTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigCurveType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->flag != wo->flag)
+ error ("Curve flag doesn't match for Linear");
+
+ if (ro->size != wo->size)
+ error ("Curve size doesn't match");
+
+ if (rv)
+ error ("Curve verify failed");
+ }
+ }
+ /* --------------------- */
+ /* Curve - Gamma version */
+ {
+ static icmCurve *wo;
+ if (mode == 0) {
+ if ((wo = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigGreenTRCTag, icSigCurveType)) == NULL)
+ return 1;
+
+ wo->flag = icmCurveGamma; /* Gamma version */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0] = rand_u8f8(); /* Gamma value */
+ } else {
+ icmCurve *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmCurve *)rd_icco->read_tag(rd_icco, icSigGreenTRCTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigCurveType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->flag != wo->flag)
+ error ("Curve flag doesn't match for Gamma");
+
+ if (ro->size != wo->size)
+ error ("Curve size doesn't match");
+
+ rv |= dcomp(ro->data[0], wo->data[0]);
+
+ if (rv)
+ error ("Curve verify failed");
+ }
+ }
+ /* ------------------------- */
+ /* Curve - Specified version */
+ {
+ static icmCurve *wo;
+ if (mode == 0) {
+ unsigned int i;
+ if ((wo = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigBlueTRCTag, icSigCurveType)) == NULL)
+ return 1;
+
+ wo->flag = icmCurveSpec; /* Specified version */
+ wo->size = rand_int(2,23); /* Number of entries (min must be 2!) */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ for (i = 0; i < wo->size; i++)
+ wo->data[i] = rand_16f(); /* Curve values 0.0 - 1.0 */
+ } else {
+ icmCurve *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmCurve *)rd_icco->read_tag(rd_icco, icSigBlueTRCTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigCurveType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->flag != wo->flag)
+ error ("Curve flag doesn't match for specified");
+
+ if (ro->size != wo->size)
+ error ("Curve size doesn't match");
+
+ for (i = 0; i < wo->size; i++)
+ rv |= dcomp(ro->data[i], wo->data[i]);
+
+ if (rv)
+ error ("Curve verify failed");
+ }
+ }
+ /* ------------------- */
+ /* Data - text version */
+ {
+ static icmData *wo;
+ char *ts1 = "This is a data string";
+ if (mode == 0) {
+ if ((wo = (icmData *)wr_icco->add_tag(
+ wr_icco, icSigPs2CRD0Tag, icSigDataType)) == NULL)
+ return 1;
+
+ wo->flag = icmDataASCII; /* Holding ASCII data */
+ wo->size = strlen(ts1)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy((char *)wo->data, ts1); /* Copy the text in */
+ } else {
+ icmData *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmData *)rd_icco->read_tag(rd_icco, icSigPs2CRD0Tag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigDataType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->flag != wo->flag)
+ error ("Data size doesn't match");
+
+ if (ro->size != wo->size)
+ error ("Data size doesn't match");
+
+ rv |= strcmp((char *)ro->data, (char *)wo->data);
+
+ if (rv)
+ error ("Data verify failed");
+ }
+ }
+ /* --------------------- */
+ /* Data - Binary version */
+ {
+ static icmData *wo;
+ if (mode == 0) {
+ unsigned int i;
+ if ((wo = (icmData *)wr_icco->add_tag(
+ wr_icco, icSigPs2CRD1Tag, icSigDataType)) == NULL)
+ return 1;
+
+ wo->flag = icmDataBin; /* Holding binary data */
+ wo->size = rand_int(0,43); /* Space we need for data */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ for (i = 0; i < wo->size; i ++)
+ wo->data[i] = rand_o8();
+ } else {
+ icmData *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmData *)rd_icco->read_tag(rd_icco, icSigPs2CRD1Tag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigDataType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->flag != wo->flag)
+ error ("Data size doesn't match");
+
+ if (ro->size != wo->size)
+ error ("Data size doesn't match");
+
+ for (i = 0; i < wo->size; i++)
+ rv |= (ro->data[i] != wo->data[i]);
+
+ if (rv)
+ error ("Data verify failed");
+ }
+ }
+ /* --------- */
+ /* DateTime: */
+ {
+ static icmDateTimeNumber *wo;
+ if (mode == 0) {
+ if ((wo = (icmDateTimeNumber *)wr_icco->add_tag(
+ wr_icco, icSigCalibrationDateTimeTag, icSigDateTimeType)) == NULL)
+ return 1;
+
+ wo->year = rand_int(1900, 3000);
+ wo->month = rand_int(1, 12);
+ wo->day = rand_int(1, 31);
+ wo->hours = rand_int(0, 23);
+ wo->minutes = rand_int(0, 59);
+ wo->seconds = rand_int(0, 59);
+ } else {
+ icmDateTimeNumber *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmDateTimeNumber *)rd_icco->read_tag(rd_icco, icSigCalibrationDateTimeTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigDateTimeType)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->year != wo->year);
+ rv |= (ro->month != wo->month);
+ rv |= (ro->day != wo->day);
+ rv |= (ro->hours != wo->hours);
+ rv |= (ro->minutes != wo->minutes);
+ rv |= (ro->seconds != wo->seconds);
+
+ if (rv)
+ error ("DateTime verify failed");
+ }
+ }
+ /* ----------- */
+ /* 16 bit lut: */
+ {
+ static icmLut *wo;
+ if (mode == 0) {
+ unsigned int i, j, k;
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigAToB0Tag, icSigLut16Type)) == NULL)
+ return 1;
+
+ wo->inputChan = 2;
+ wo->outputChan = 3;
+ wo->clutPoints = 5;
+ wo->inputEnt = 56;
+ wo->outputEnt = 73;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space */
+ for (i = 0; i < 3; i++) /* Matrix */
+ for (j = 0; j < 3; j++)
+ wo->e[i][j] = rand_s15f16();
+
+ /* See icc.getNormFuncs() for normalizing functions */
+ /* The input table index range is over the normalized range 0.0 - 1.0. */
+ /* The range in input color space can be determined by denormalizing */
+ /* the values 0.0 - 1.0. */
+ for (i = 0; i < wo->inputChan; i++) /* Input tables */
+ for (j = 0; j < wo->inputEnt; j++)
+ wo->inputTable[i * wo->inputEnt + j] = rand_16f();
+
+ /* Lut */
+ /* The multidimentional lut has a normalized index range */
+ /* of 0.0 - 1.0 in each dimension. Its entry values are also */
+ /* normalized values in the range 0.0 - 1.0. */
+ for (i = 0; i < wo->clutPoints; i++) /* Input chan 0 - slow changing */
+ for (j = 0; j < wo->clutPoints; j++) /* Input chan 1 - faster changing */
+ for (k = 0; k < wo->outputChan; k++) /* Output chans */
+ wo->clutTable[(i * wo->clutPoints + j) * wo->outputChan + k] = rand_16f();
+
+ /* The output color space values should be normalized to the */
+ /* range 0.0 - 1.0 for use as output table entry values. */
+ for (i = 0; i < wo->outputChan; i++) /* Output tables */
+ for (j = 0; j < wo->outputEnt; j++)
+ wo->outputTable[i * wo->outputEnt + j] = rand_16f();
+
+ } else {
+ icmLut *ro;
+ unsigned int size;
+ unsigned int i, j;
+
+ /* Try and read the tag from the file */
+ ro = (icmLut *)rd_icco->read_tag(rd_icco, icSigAToB0Tag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigLut16Type)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->inputChan != wo->inputChan);
+ rv |= (ro->outputChan != wo->outputChan);
+ rv |= (ro->clutPoints != wo->clutPoints);
+ rv |= (ro->inputEnt != wo->inputEnt);
+ rv |= (ro->outputEnt != wo->outputEnt);
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ rv |= dcomp(ro->e[i][j], wo->e[i][j]);
+
+ size = (wo->inputChan * wo->inputEnt);
+ for (i = 0; i < size; i++)
+ rv |= dcomp(ro->inputTable[i], wo->inputTable[i]);
+
+ size = wo->outputChan;
+ for (i = 0; i < wo->inputChan; i++)
+ size *= wo->clutPoints;
+ for (i = 0; i < size; i++)
+ rv |= dcomp(ro->clutTable[i], wo->clutTable[i]);
+
+ size = (wo->outputChan * wo->outputEnt);
+ for (i = 0; i < size; i++)
+ rv |= dcomp(ro->outputTable[i], wo->outputTable[i]);
+ if (rv)
+ error ("Lut16 verify failed");
+ }
+ }
+ /* ------------------ */
+ /* 16 bit lut - link: */
+ {
+ static icmLut *wo;
+ if (mode == 0) {
+ /* Just link to the existing LUT. This is often used when there */
+ /* is no distinction between intents, and saves file and memory space. */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB1Tag, icSigAToB0Tag)) == NULL)
+ return 1;
+ } else {
+ icmLut *ro;
+ unsigned int size;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmLut *)rd_icco->read_tag(rd_icco, icSigAToB1Tag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigLut16Type)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->inputChan != wo->inputChan);
+ rv |= (ro->outputChan != wo->outputChan);
+ rv |= (ro->clutPoints != wo->clutPoints);
+ rv |= (ro->inputEnt != wo->inputEnt);
+ rv |= (ro->outputEnt != wo->outputEnt);
+
+ size = (wo->inputChan * wo->inputEnt);
+ for (i = 0; i < size; i++)
+ rv |= (ro->inputTable[i] != wo->inputTable[i]);
+
+ size = wo->outputChan;
+ for (i = 0; i < wo->inputChan; i++)
+ size *= wo->clutPoints;
+ for (i = 0; i < size; i++)
+ rv |= (ro->clutTable[i] != wo->clutTable[i]);
+
+ size = (wo->outputChan * wo->outputEnt);
+ for (i = 0; i < size; i++)
+ rv |= (ro->outputTable[i] != wo->outputTable[i]);
+ if (rv)
+ error ("Lut16 link verify failed");
+ }
+ }
+ /* ---------- */
+ /* 8 bit lut: */
+ {
+ static icmLut *wo;
+ if (mode == 0) {
+ unsigned int i, j, m, k;
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigAToB2Tag, icSigLut8Type)) == NULL)
+ return 1;
+
+ wo->inputChan = 3;
+ wo->outputChan = 2;
+ wo->clutPoints = 4;
+ wo->inputEnt = 256; /* Must be 256 for Lut8 */
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ for (i = 0; i < 3; i++) /* Matrix */
+ for (j = 0; j < 3; j++)
+ wo->e[i][j] = rand_s15f16();
+
+ for (i = 0; i < wo->inputChan; i++) /* Input tables */
+ for (j = 0; j < wo->inputEnt; j++)
+ wo->inputTable[i * wo->inputEnt + j] = rand_8f();
+
+ /* Lut */
+ for (i = 0; i < wo->clutPoints; i++) /* Input chan 0 */
+ for (j = 0; j < wo->clutPoints; j++) /* Input chan 1 */
+ for (m = 0; m < wo->clutPoints; m++) /* Input chan 2 */
+ for (k = 0; k < wo->outputChan; k++) { /* Output chans */
+ int idx = ((i * wo->clutPoints + j)
+ * wo->clutPoints + m)
+ * wo->outputChan + k;
+ wo->clutTable[idx] = rand_8f();
+ }
+
+ for (i = 0; i < wo->outputChan; i++) /* Output tables */
+ for (j = 0; j < wo->outputEnt; j++)
+ wo->outputTable[i * wo->outputEnt + j] = rand_8f();
+
+ } else {
+ icmLut *ro;
+ unsigned int size;
+ unsigned int i, j;
+
+ /* Try and read the tag from the file */
+ ro = (icmLut *)rd_icco->read_tag(rd_icco, icSigAToB2Tag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigLut8Type)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->inputChan != wo->inputChan);
+ rv |= (ro->outputChan != wo->outputChan);
+ rv |= (ro->clutPoints != wo->clutPoints);
+ rv |= (ro->inputEnt != wo->inputEnt);
+ rv |= (ro->outputEnt != wo->outputEnt);
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ rv |= dcomp(ro->e[i][j], wo->e[i][j]);
+
+ size = (wo->inputChan * wo->inputEnt);
+ for (i = 0; i < size; i++)
+ rv |= dcomp(ro->inputTable[i], wo->inputTable[i]);
+
+ size = wo->outputChan;
+ for (i = 0; i < wo->inputChan; i++)
+ size *= wo->clutPoints;
+ for (i = 0; i < size; i++)
+ rv |= dcomp(ro->clutTable[i], wo->clutTable[i]);
+
+ size = (wo->outputChan * wo->outputEnt);
+ for (i = 0; i < size; i++)
+ rv |= dcomp(ro->outputTable[i], wo->outputTable[i]);
+ if (rv)
+ error ("Lut8 verify failed");
+ }
+ }
+ /* ----------------- */
+ /* Measurement: */
+ {
+ static icmMeasurement *wo;
+ if (mode == 0) {
+ if ((wo = (icmMeasurement *)wr_icco->add_tag(
+ wr_icco, icSigMeasurementTag, icSigMeasurementType)) == NULL)
+ return 1;
+
+ /* Standard observer */
+ switch(rand_int(0,2)) {
+ case 0:
+ wo->observer = icStdObsUnknown;
+ break;
+ case 1:
+ wo->observer = icStdObs1931TwoDegrees;
+ break;
+ case 2:
+ wo->observer = icStdObs1964TenDegrees;
+ break;
+ }
+
+ /* XYZ for backing color */
+ wo->backing.X = rand_XYZ16();
+ wo->backing.Y = rand_XYZ16();
+ wo->backing.Z = rand_XYZ16();
+
+ /* Measurement geometry */
+ switch(rand_int(0,2)) {
+ case 0:
+ wo->geometry = icGeometryUnknown;
+ break;
+ case 1:
+ wo->geometry = icGeometry045or450;
+ break;
+ case 2:
+ wo->geometry = icGeometry0dord0;
+ break;
+ }
+
+ /* Measurement flare */
+ wo->flare = rand_u16f16();
+
+ /* Illuminant */
+ switch(rand_int(0,8)) {
+ case 0:
+ wo->illuminant = icIlluminantUnknown;
+ break;
+ case 1:
+ wo->illuminant = icIlluminantD50;
+ break;
+ case 2:
+ wo->illuminant = icIlluminantD65;
+ break;
+ case 3:
+ wo->illuminant = icIlluminantD93;
+ break;
+ case 4:
+ wo->illuminant = icIlluminantF2;
+ break;
+ case 5:
+ wo->illuminant = icIlluminantD55;
+ break;
+ case 6:
+ wo->illuminant = icIlluminantA;
+ break;
+ case 7:
+ wo->illuminant = icIlluminantEquiPowerE;
+ break;
+ case 8:
+ wo->illuminant = icIlluminantF8;
+ break;
+ }
+ } else {
+ icmMeasurement *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmMeasurement *)rd_icco->read_tag(rd_icco, icSigMeasurementTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigMeasurementType)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->observer != wo->observer);
+ rv |= dcomp(ro->backing.X, wo->backing.X);
+ rv |= dcomp(ro->backing.Y, wo->backing.Y);
+ rv |= dcomp(ro->backing.Z, wo->backing.Z);
+ rv |= (ro->geometry != wo->geometry);
+ rv |= dcomp(ro->flare, wo->flare);
+ rv |= (ro->illuminant != wo->illuminant);
+
+ if (rv)
+ error ("Measurement verify failed");
+ }
+ }
+ /* ----------------- */
+ /* Old style NamedColor: */
+ {
+ static icmNamedColor *wo;
+ if (mode == 0) {
+ unsigned int i;
+ if ((wo = (icmNamedColor *)wr_icco->add_tag(
+ wr_icco, icSigNamedColorTag, icSigNamedColorType)) == NULL)
+ return 1;
+
+ wo->vendorFlag = rand_int(0,65535) << 16; /* Bottom 16 bits for IC use */
+ wo->count = 3; /* Count of named colors */
+ strcpy(wo->prefix,"Prefix"); /* Prefix for each color name, max 32, null terminated */
+ strcpy(wo->suffix,"Suffix"); /* Suffix for each color name, max 32, null terminated */
+
+ wo->allocate((icmBase *)wo); /* Allocate named color structures */
+
+ for (i = 0; i < wo->count; i++) {
+ unsigned int j;
+ sprintf(wo->data[i].root,"Color %d",i); /* Root name, max 32, null terminated */
+ for (j = 0; j < wo->nDeviceCoords; j++) /* nDeviceCoords defaults appropriately */
+ wo->data[i].deviceCoords[j] = rand_8f(); /* Device coords of color */
+ }
+ } else {
+ icmNamedColor *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmNamedColor *)rd_icco->read_tag(rd_icco, icSigNamedColorTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigNamedColorType)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->vendorFlag != wo->vendorFlag);
+ rv |= (ro->count != wo->count);
+ rv |= (ro->nDeviceCoords != wo->nDeviceCoords);
+ rv |= strcmp(ro->prefix, wo->prefix);
+ rv |= strcmp(ro->suffix, wo->suffix);
+
+ if (rv)
+ error ("NamedColor verify failed");
+
+ for (i = 0; i < wo->count; i++) {
+ unsigned int j;
+ rv |= strcmp(ro->data[i].root, wo->data[i].root);
+ for (j = 0; j < wo->nDeviceCoords; j++)
+ rv |= dcomp(ro->data[i].deviceCoords[j], wo->data[i].deviceCoords[j]);
+ }
+
+ if (rv)
+ error ("NamedColor verify failed");
+ }
+ }
+ /* ----------------- */
+ /* NamedColor2: */
+ {
+ static icmNamedColor *wo; /* Shares same machine specific structure */
+ if (mode == 0) {
+ unsigned int i;
+ if ((wo = (icmNamedColor *)wr_icco->add_tag(
+ wr_icco, icSigNamedColor2Tag, icSigNamedColor2Type)) == NULL)
+ return 1;
+
+ wo->vendorFlag = rand_int(0,65535) << 16; /* Bottom 16 bits for ICC use */
+ wo->count = 4; /* Count of named colors */
+ wo->nDeviceCoords = 3; /* Num of device coordinates */
+ /* Could set this different to that implied by wr_icco->header->colorSpace */
+ strcpy(wo->prefix,"Prefix-ix"); /* Prefix for each color name, max 32, null terminated */
+ strcpy(wo->suffix,"Suffix-ixix"); /* Suffix for each color name, max 32, null terminated */
+
+ wo->allocate((icmBase *)wo); /* Allocate named color structures */
+
+ for (i = 0; i < wo->count; i++) {
+ unsigned int j;
+ sprintf(wo->data[i].root,"Pigment %d",i); /* Root name, max 32, null terminated */
+ for (j = 0; j < wo->nDeviceCoords; j++)
+ wo->data[i].deviceCoords[j] = rand_8f(); /* Device coords of color */
+ switch(wo->icp->header->pcs) {
+ case icSigXYZData:
+ wo->data[i].pcsCoords[0] = rand_XYZ16();
+ wo->data[i].pcsCoords[1] = rand_XYZ16();
+ wo->data[i].pcsCoords[2] = rand_XYZ16();
+ break;
+ case icSigLabData:
+ wo->data[i].pcsCoords[0] = rand_L16();
+ wo->data[i].pcsCoords[1] = rand_ab16();
+ wo->data[i].pcsCoords[2] = rand_ab16();
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ icmNamedColor *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmNamedColor *)rd_icco->read_tag(rd_icco, icSigNamedColor2Tag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigNamedColor2Type)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->vendorFlag != wo->vendorFlag);
+ rv |= (ro->count != wo->count);
+ rv |= (ro->nDeviceCoords != wo->nDeviceCoords);
+ rv |= strcmp(ro->prefix, wo->prefix);
+ rv |= strcmp(ro->suffix, wo->suffix);
+
+ if (rv)
+ error ("NamedColor2 verify failed");
+
+ for (i = 0; i < wo->count; i++) {
+ unsigned int j;
+ rv |= strcmp(ro->data[i].root, wo->data[i].root);
+ for (j = 0; j < wo->nDeviceCoords; j++)
+ rv |= dcomp(ro->data[i].deviceCoords[j], wo->data[i].deviceCoords[j]);
+ for (j = 0; j < 3; j++) {
+ rv |= dcomp(ro->data[i].pcsCoords[j], wo->data[i].pcsCoords[j]);
+ }
+ }
+
+ if (rv)
+ error ("NamedColor2 verify failed");
+ }
+ }
+ /* ----------------- */
+ /* ColorantTable: */
+ {
+ static icmColorantTable *wo;
+ if (mode == 0) {
+ unsigned int i;
+ if ((wo = (icmColorantTable *)wr_icco->add_tag(
+ wr_icco, icSigColorantTableTag, icSigColorantTableType)) == NULL)
+ return 1;
+
+ wo->count = 4; /* Count of colorants - should be same as implied by device space */
+ wo->allocate((icmBase *)wo); /* Allocate ColorantTable structures */
+
+ for (i = 0; i < wo->count; i++) {
+ sprintf(wo->data[i].name,"Color %d",i); /* Colorant name, max 32, null terminated */
+ switch(wo->icp->header->pcs) {
+ case icSigXYZData:
+ wo->data[i].pcsCoords[0] = rand_XYZ16();
+ wo->data[i].pcsCoords[1] = rand_XYZ16();
+ wo->data[i].pcsCoords[2] = rand_XYZ16();
+ break;
+ case icSigLabData:
+ wo->data[i].pcsCoords[0] = rand_L16();
+ wo->data[i].pcsCoords[1] = rand_ab16();
+ wo->data[i].pcsCoords[2] = rand_ab16();
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ icmColorantTable *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmColorantTable *)rd_icco->read_tag(rd_icco, icSigColorantTableTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigColorantTableType)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->count != wo->count);
+
+ if (rv)
+ error ("ColorantTable verify failed");
+
+ for (i = 0; i < wo->count; i++) {
+ int j;
+ rv |= strcmp(ro->data[i].name, wo->data[i].name);
+ for (j = 0; j < 3; j++) {
+ rv |= dcomp(ro->data[i].pcsCoords[j], wo->data[i].pcsCoords[j]);
+ }
+ }
+
+ if (rv)
+ error ("ColorantTable verify failed");
+ }
+ }
+ /* ----------------- */
+ /* ProfileSequenceDescTag: */
+ {
+ unsigned short ts2a[29] = {'T','h','i','s',' ','i','s',' ','a',' ','d','e','v','i','c','e',
+ ' ','d','e','s','c','r','i','p','t','i','o','n',0x0000};
+ unsigned short ts2b[28] = {'T','h','i','s',' ','i','s',' ','a',' ','m','o','d','e','l',
+ ' ','d','e','s','c','r','i','p','t','i','o','n',0x0000};
+ char *ts3a = "This is a device description";
+ char *ts3b = "This is a model description";
+ static icmProfileSequenceDesc*wo;
+ if (mode == 0) {
+ unsigned int i;
+ if ((wo = (icmProfileSequenceDesc *)wr_icco->add_tag(
+ wr_icco, icSigProfileSequenceDescTag, icSigProfileSequenceDescType)) == NULL)
+ return 1;
+
+ wo->count = 3; /* Number of descriptions in sequence */
+ wo->allocate((icmBase *)wo); /* Allocate space for all the DescStructures */
+
+ /* Fill in each description structure in sequence */
+ for (i = 0; i < wo->count; i++) {
+ char ts1[100];
+ wo->data[i].deviceMfg = str2tag("mfg7");
+ wo->data[i].deviceModel = str2tag("2345");
+ wo->data[i].attributes.l = icTransparency | icMatte;
+ wo->data[i].attributes.h = 0x98765432;
+ wo->data[i].technology = rand_TechnologySignature();
+
+ /* device Text description */
+ sprintf(ts1,"This is device descrption %d",i);
+ wo->data[i].device.size = strlen(ts1)+1;
+ wo->data[i].allocate(&wo->data[i]); /* Allocate space */
+ strcpy(wo->data[i].device.desc, ts1); /* Copy the string in */
+
+ /* We'll fudge up the Unicode string */
+ wo->data[i].device.ucLangCode = 8765; /* UniCode language code */
+ wo->data[i].device.ucSize = 29; /* Size in chars inc null */
+ wo->data[i].allocate(&wo->data[i]); /* Allocate space */
+ memmove(wo->data[i].device.ucDesc, ts2a, 2 * 29); /* Copy string in */
+
+ wo->data[i].device.scCode = 67; /* Fudge scriptCode code */
+ wo->data[i].device.scSize = strlen(ts3a)+1; /* Used size of scDesc in bytes, inc null */
+ if (wo->data[i].device.scSize > 67)
+ error("ScriptCode string longer than 67");
+ strcpy((char *)wo->data[i].device.scDesc, ts3a); /* Copy the string in */
+
+ /* model Text description */
+ sprintf(ts1,"This is model descrption %d",i);
+ wo->data[i].model.size = strlen(ts1)+1;
+ wo->data[i].allocate(&wo->data[i]); /* Allocate space */
+ strcpy(wo->data[i].model.desc, ts1); /* Copy the string in */
+
+ /* We'll fudge up the Unicode string */
+ wo->data[i].model.ucLangCode = 7856; /* UniCode language code */
+ wo->data[i].model.ucSize = 28; /* Size in chars inc null */
+ wo->data[i].allocate(&wo->data[i]); /* Allocate space */
+ memmove(wo->data[i].model.ucDesc, ts2b, 2 * 28); /* Copy string in */
+
+ wo->data[i].model.scCode = 67; /* Fudge scriptCode code */
+ wo->data[i].model.scSize = strlen(ts3b)+1; /* Used size of scDesc in bytes, inc null */
+ if (wo->data[i].model.scSize > 67)
+ error("ScriptCode string longer than 67");
+ strcpy((char *)wo->data[i].model.scDesc, ts3b); /* Copy the string in */
+ }
+ } else {
+ icmProfileSequenceDesc *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmProfileSequenceDesc *)rd_icco->read_tag(rd_icco, icSigProfileSequenceDescTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigProfileSequenceDescType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->count != wo->count)
+ error ("ProfileSequenceDesc count doesn't match");
+
+ for (i = 0; i < wo->count; i++) {
+ rv |= (ro->data[i].deviceMfg != wo->data[i].deviceMfg);
+ rv |= (ro->data[i].deviceModel != wo->data[i].deviceModel);
+ rv |= (ro->data[i].attributes.l != wo->data[i].attributes.l);
+ rv |= (ro->data[i].attributes.h != wo->data[i].attributes.h);
+ rv |= (ro->data[i].technology != wo->data[i].technology);
+
+ /* device Text description */
+ rv |= (ro->data[i].device.size != wo->data[i].device.size);
+ rv |= strcmp(ro->data[i].device.desc, wo->data[i].device.desc);
+
+ rv |= (ro->data[i].device.ucLangCode != wo->data[i].device.ucLangCode);
+ rv |= (ro->data[i].device.ucSize != wo->data[i].device.ucSize);
+ rv |= memcmp(ro->data[i].device.ucDesc, wo->data[i].device.ucDesc, wo->data[i].device.ucSize * 2);
+
+ rv |= (ro->data[i].device.scCode != wo->data[i].device.scCode);
+ rv |= (ro->data[i].device.scSize != wo->data[i].device.scSize);
+ rv |= strcmp((char *)ro->data[i].device.scDesc, (char *)wo->data[i].device.scDesc);
+
+ /* model Text description */
+ rv |= (ro->data[i].model.size != wo->data[i].model.size);
+ rv |= strcmp(ro->data[i].model.desc, wo->data[i].model.desc);
+
+ rv |= (ro->data[i].model.ucLangCode != wo->data[i].model.ucLangCode);
+ rv |= (ro->data[i].model.ucSize != wo->data[i].model.ucSize);
+ rv |= memcmp(ro->data[i].model.ucDesc, wo->data[i].model.ucDesc, wo->data[i].model.ucSize * 2);
+
+ rv |= (ro->data[i].model.scCode != wo->data[i].model.scCode);
+ rv |= (ro->data[i].model.scSize != wo->data[i].model.scSize);
+ rv |= strcmp((char *)ro->data[i].model.scDesc, (char *)wo->data[i].model.scDesc);
+ }
+
+ if (rv)
+ error ("ProfileSequenceDesc verify failed");
+ }
+ }
+ /* ----------------- */
+ /* S15Fixed16Array: */
+ {
+ static icmS15Fixed16Array *wo;
+ if (mode == 0) {
+ unsigned int i;
+ /* There is no standard Tag that uses icSigS15Fixed16ArrayType, so use a 'custom' tag */
+ if ((wo = (icmS15Fixed16Array *)wr_icco->add_tag(
+ wr_icco, str2tag("sf32"), icSigS15Fixed16ArrayType)) == NULL)
+ return 1;
+
+ wo->size = rand_int(0,17); /* Number in array */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ for (i = 0; i < wo->size; i++) {
+ wo->data[i] = rand_s15f16(); /* Set numbers value */
+ }
+ } else {
+ icmS15Fixed16Array *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmS15Fixed16Array *)rd_icco->read_tag(rd_icco, str2tag("sf32"));
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigS15Fixed16ArrayType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->size != wo->size)
+ error ("S15Fixed16Array size doesn't match");
+
+ for (i = 0; i < wo->size; i++) {
+ rv |= dcomp(ro->data[i], wo->data[i]);
+ }
+
+ if (rv)
+ error ("S15Fixed16Array verify failed");
+ }
+ }
+ /* ----------------- */
+ /* Screening: */
+ {
+ static icmScreening *wo;
+ if (mode == 0) {
+ unsigned int i;
+ if ((wo = (icmScreening *)wr_icco->add_tag(
+ wr_icco, icSigScreeningTag, icSigScreeningType)) == NULL)
+ return 1;
+
+ wo->screeningFlag = rand_ScreenEncodings();
+ wo->channels = rand_int(1,4); /* Number of channels */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ for (i = 0; i < wo->channels; i++) {
+ wo->data[i].frequency = rand_s15f16(); /* Set screening frequency */
+ wo->data[i].angle = rand_s15f16(); /* Set screening angle */
+ wo->data[i].spotShape = rand_SpotShape(); /* Set spot shape */
+ }
+ } else {
+ icmScreening *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmScreening *)rd_icco->read_tag(rd_icco, icSigScreeningTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigScreeningType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->channels != wo->channels)
+ error ("Screening channels doesn't match");
+
+ rv |= (ro->screeningFlag != wo->screeningFlag);
+
+ for (i = 0; i < wo->channels; i++) {
+ rv |= dcomp(ro->data[i].frequency, wo->data[i].frequency);
+ rv |= dcomp(ro->data[i].angle, wo->data[i].angle);
+ rv |= (ro->data[i].spotShape != wo->data[i].spotShape);
+ }
+
+ if (rv)
+ error ("Screening verify failed");
+ }
+ }
+ /* ----------------- */
+ /* Signature: */
+ {
+ static icmSignature *wo;
+ if (mode == 0) {
+ if ((wo = (icmSignature *)wr_icco->add_tag(
+ wr_icco, icSigTechnologyTag, icSigSignatureType)) == NULL)
+ return 1;
+
+ wo->sig = rand_TechnologySignature();
+ } else {
+ icmSignature *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmSignature *)rd_icco->read_tag(rd_icco, icSigTechnologyTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigSignatureType)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->sig != wo->sig);
+
+ if (rv)
+ error ("Signature verify failed");
+ }
+ }
+ /* ----------------- */
+ /* Text Description: */
+ {
+ static icmTextDescription *wo;
+ char *ts1 = "This is a test description";
+ unsigned short ts2[27] = {'T','h','i','s',' ','i','s',' ','a',' ','t','e','s','t',
+ ' ','d','e','s','c','r','i','p','t','i','o','n',0x0000};
+ char *ts3 = "This is a test3 description";
+ if (mode == 0) {
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ return 1;
+
+ /* Data in tag type wojects is always allocated and freed by the woject */
+ wo->size = strlen(ts1)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, ts1); /* Copy the string in */
+
+ /* We'll fudge up the Unicode string */
+ wo->ucLangCode = 1234; /* UniCode language code */
+ wo->ucSize = 27; /* Allocated and used size of ucDesc in characters, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ memmove(wo->ucDesc, ts2, 2 * 27); /* Copy string in */
+
+ /* Don't really know anything about scriptCode, but fudge some values */
+ wo->scCode = 23; /* ScriptCode code */
+ wo->scSize = strlen(ts3)+1; /* Used size of scDesc in bytes, inc null */
+ /* No allocations, since this has a fixed max of 67 bytes */
+ if (wo->scSize > 67)
+ error("ScriptCode string longer than 67");
+ strcpy((char *)wo->scDesc, ts3); /* Copy the string in */
+ } else {
+ icmTextDescription *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmTextDescription *)rd_icco->read_tag(rd_icco, icSigProfileDescriptionTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ /* We could have left it icmBase, switched on ro->ttype, & then cast appropriately. */
+ if (ro->ttype != icSigTextDescriptionType)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->size != wo->size);
+ rv |= strcmp(ro->desc, wo->desc);
+
+ rv |= (ro->ucLangCode != wo->ucLangCode);
+ rv |= (ro->ucSize != wo->ucSize);
+ rv |= memcmp(ro->ucDesc, wo->ucDesc, wo->ucSize * 2);
+
+ rv |= (ro->scCode != wo->scCode);
+ rv |= (ro->scSize != wo->scSize);
+ rv |= strcmp((char *)ro->scDesc, (char *)wo->scDesc);
+ if (rv)
+ error ("Text Description verify failed4");
+ }
+ }
+ /* ----- */
+ /* Text: */
+ {
+ static icmText *wo;
+ char *ts1 = "This is Copyright by me!";
+ if (mode == 0) {
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ return 1;
+
+ wo->size = strlen(ts1)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, ts1); /* Copy the text in */
+ } else {
+ icmText *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmText *)rd_icco->read_tag(rd_icco, icSigCopyrightTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigTextType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->size != wo->size)
+ error ("Text size doesn't match");
+
+ rv |= strcmp(ro->data, wo->data);
+
+ if (rv)
+ error ("Text verify failed");
+ }
+ }
+ /* ---------------- */
+ /* U16Fixed16Array: */
+ {
+ static icmU16Fixed16Array *wo;
+ if (mode == 0) {
+ unsigned int i;
+ /* There is no standard Tag that uses icSigU16Fixed16ArrayType, so use a 'custom' tag */
+ if ((wo = (icmU16Fixed16Array *)wr_icco->add_tag(
+ wr_icco, str2tag("uf32"), icSigU16Fixed16ArrayType)) == NULL)
+ return 1;
+
+ wo->size = rand_int(0,17); /* Number in array */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ for (i = 0; i < wo->size; i++) {
+ wo->data[i] = rand_u16f16(); /* Set numbers value */
+ }
+ } else {
+ icmU16Fixed16Array *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmU16Fixed16Array *)rd_icco->read_tag(rd_icco, str2tag("uf32"));
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigU16Fixed16ArrayType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->size != wo->size)
+ error ("U16Fixed16Array size doesn't match");
+
+ for (i = 0; i < wo->size; i++) {
+ rv |= dcomp(ro->data[i], wo->data[i]);
+ }
+
+ if (rv)
+ error ("U16Fixed16Array verify failed");
+ }
+ }
+ /* ------------------- */
+ /* UcrBg - full curve: */
+ {
+ static icmUcrBg *wo;
+ char *ts1 = "UcrBg - full curve info";
+ if (mode == 0) {
+ unsigned int i;
+ if ((wo = (icmUcrBg *)wr_icco->add_tag(
+ wr_icco, icSigUcrBgTag, icSigUcrBgType)) == NULL)
+ return 1;
+
+ wo->UCRcount = rand_int(2,55); /* Number in UCR curve */
+ wo->BGcount = rand_int(2,32); /* Number in BG array */
+ wo->allocate((icmBase *)wo); /* Allocate space for both curves */
+ for (i = 0; i < wo->UCRcount; i++)
+ wo->UCRcurve[i] = rand_16f(); /* Set numbers value */
+ for (i = 0; i < wo->BGcount; i++)
+ wo->BGcurve[i] = rand_16f(); /* Set numbers value */
+ wo->size = strlen(ts1)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ strcpy(wo->string, ts1); /* Copy the text in */
+ } else {
+ icmUcrBg *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmUcrBg *)rd_icco->read_tag(rd_icco, icSigUcrBgTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigUcrBgType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->UCRcount != wo->UCRcount)
+ error ("UcrBg UCRcount doesn't match");
+
+ if (ro->BGcount != wo->BGcount)
+ error ("UcrBg BGcount doesn't match");
+
+ for (i = 0; i < wo->UCRcount; i++)
+ rv |= dcomp(ro->UCRcurve[i], wo->UCRcurve[i]);
+
+ for (i = 0; i < wo->BGcount; i++)
+ rv |= dcomp(ro->BGcurve[i], wo->BGcurve[i]);
+
+ if (ro->size != wo->size)
+ error ("Text size doesn't match");
+
+ rv |= strcmp(ro->string, wo->string);
+
+ if (rv)
+ error ("UcrBg verify failed");
+ }
+ }
+ /* ------------------- */
+ /* UcrBg - percentage: */
+ {
+ static icmUcrBg *wo;
+ char *ts1 = "UcrBg - percentage info";
+ if (mode == 0) {
+ if ((wo = (icmUcrBg *)wr_icco->add_tag(
+ wr_icco, str2tag("bfd%"), icSigUcrBgType)) == NULL)
+ return 1;
+
+ wo->UCRcount = 1; /* 1 == UCR percentage */
+ wo->BGcount = 1; /* 1 == BG percentage */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ wo->UCRcurve[0] = (double) rand_int(0,65535);
+ wo->BGcurve[0] = (double) rand_int(0,65535);
+ wo->size = strlen(ts1)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->string, ts1); /* Copy the text in */
+ } else {
+ icmUcrBg *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmUcrBg *)rd_icco->read_tag(rd_icco, str2tag("bfd%"));
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigUcrBgType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->UCRcount != wo->UCRcount)
+ error ("UcrBg UCRcount doesn't match");
+
+ if (ro->BGcount != wo->BGcount)
+ error ("UcrBg BGcount doesn't match");
+
+ rv |= (ro->UCRcurve[0] != wo->UCRcurve[0]);
+ rv |= (ro->BGcurve[0] != wo->BGcurve[0]);
+
+ if (ro->size != wo->size)
+ error ("Text size doesn't match");
+
+ rv |= strcmp(ro->string, wo->string);
+
+ if (rv)
+ error ("UcrBg verify failed");
+ }
+ }
+ /* ------------ */
+ /* UInt16Array: */
+ {
+ static icmUInt16Array *wo;
+ if (mode == 0) {
+ unsigned int i;
+ /* There is no standard Tag that uses icSigUInt16ArrayType, so use a 'custom' tag */
+ if ((wo = (icmUInt16Array *)wr_icco->add_tag(
+ wr_icco, str2tag("ui16"), icSigUInt16ArrayType)) == NULL)
+ return 1;
+
+ wo->size = rand_int(0,17); /* Number in array */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ for (i = 0; i < wo->size; i++) {
+ wo->data[i] = rand_o16(); /* Set numbers value */
+ }
+ } else {
+ icmUInt16Array *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmUInt16Array *)rd_icco->read_tag(rd_icco, str2tag("ui16"));
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigUInt16ArrayType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->size != wo->size)
+ error ("UInt16Array size doesn't match");
+
+ for (i = 0; i < wo->size; i++) {
+ rv |= (ro->data[i] != wo->data[i]);
+ }
+
+ if (rv)
+ error ("UInt16Array verify failed");
+ }
+ }
+ /* ------------ */
+ /* UInt32Array: */
+ {
+ static icmUInt32Array *wo;
+ if (mode == 0) {
+ unsigned int i;
+ /* There is no standard Tag that uses icSigUInt32ArrayType, so use a 'custom' tag */
+ if ((wo = (icmUInt32Array *)wr_icco->add_tag(
+ wr_icco, str2tag("ui32"), icSigUInt32ArrayType)) == NULL)
+ return 1;
+
+ wo->size = rand_int(0,18); /* Number in array */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ for (i = 0; i < wo->size; i++) {
+ wo->data[i] = rand_o32(); /* Set numbers value */
+ }
+ } else {
+ icmUInt32Array *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmUInt32Array *)rd_icco->read_tag(rd_icco, str2tag("ui32"));
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigUInt32ArrayType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->size != wo->size)
+ error ("UInt32Array size doesn't match");
+
+ for (i = 0; i < wo->size; i++) {
+ rv |= (ro->data[i] != wo->data[i]);
+ }
+
+ if (rv)
+ error ("UInt32Array verify failed");
+ }
+ }
+ /* ------------ */
+ /* UInt64Array: */
+ {
+ static icmUInt64Array *wo;
+ if (mode == 0) {
+ unsigned int i;
+ /* There is no standard Tag that uses icSigUInt64ArrayType, so use a 'custom' tag */
+ if ((wo = (icmUInt64Array *)wr_icco->add_tag(
+ wr_icco, str2tag("ui64"), icSigUInt64ArrayType)) == NULL)
+ return 1;
+
+ wo->size = rand_int(0,19); /* Number in array */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ for (i = 0; i < wo->size; i++) {
+ wo->data[i].l = rand_o32(); /* Set numbers value - low 32 bits */
+ wo->data[i].h = rand_o32(); /* Set numbers value - low 32 bits */
+ }
+ } else {
+ icmUInt64Array *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmUInt64Array *)rd_icco->read_tag(rd_icco, str2tag("ui64"));
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigUInt64ArrayType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->size != wo->size)
+ error ("UInt64Array size doesn't match");
+
+ for (i = 0; i < wo->size; i++) {
+ rv |= (ro->data[i].l != wo->data[i].l);
+ rv |= (ro->data[i].h != wo->data[i].h);
+ }
+
+ if (rv)
+ error ("UInt64Array verify failed");
+ }
+ }
+ /* ----------- */
+ /* UInt8Array: */
+ {
+ static icmUInt8Array *wo;
+ if (mode == 0) {
+ unsigned int i;
+ /* There is no standard Tag that uses icSigUInt8ArrayType, so use a 'custom' tag */
+ if ((wo = (icmUInt8Array *)wr_icco->add_tag(
+ wr_icco, str2tag("ui08"), icSigUInt8ArrayType)) == NULL)
+ return 1;
+
+ wo->size = rand_int(0,18); /* Number in array */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ for (i = 0; i < wo->size; i++) {
+ wo->data[i] = rand_o8(); /* Set numbers value */
+ }
+ } else {
+ icmUInt8Array *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmUInt8Array *)rd_icco->read_tag(rd_icco, str2tag("ui08"));
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigUInt8ArrayType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->size != wo->size)
+ error ("UInt8Array size doesn't match");
+
+ for (i = 0; i < wo->size; i++) {
+ rv |= (ro->data[i] != wo->data[i]);
+ }
+
+ if (rv)
+ error ("UInt8Array verify failed");
+ }
+ }
+ /* --------------- */
+ /* VideoCardGamma: (ColorSync specific) */
+ {
+ static icmVideoCardGamma *wo;
+ if (mode == 0) {
+ int i;
+ if ((wo = (icmVideoCardGamma *)wr_icco->add_tag(
+ wr_icco, icSigVideoCardGammaTag, icSigVideoCardGammaType)) == NULL)
+ return 1;
+
+ wo->tagType = icmVideoCardGammaTableType;
+ wo->u.table.channels = rand_int(1,3);
+ wo->u.table.entryCount = rand_int(2,1024);
+ wo->u.table.entrySize = rand_int(1,2);
+ wo->allocate((icmBase *)wo);
+ if (wo->u.table.entrySize == 1) {
+ unsigned char *cp = wo->u.table.data;
+ for (i=0; i<wo->u.table.channels*wo->u.table.entryCount;i++,cp++)
+ *cp = (unsigned char)rand_int(0,255);
+ } else {
+ unsigned short *sp = wo->u.table.data;
+ for (i=0; i<wo->u.table.channels*wo->u.table.entryCount;i++,sp++)
+ *sp = (unsigned short)rand_int(0,65535);
+ }
+ } else {
+ icmVideoCardGamma *ro;
+ int i;
+
+ /* Try and read tag from the file */
+ ro = (icmVideoCardGamma *)rd_icco->read_tag(rd_icco, icSigVideoCardGammaTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate */
+ if (ro->ttype != icSigVideoCardGammaType)
+ return 1;
+
+ /* Now check it out */
+ rv |= (ro->tagType != wo->tagType);
+ rv |= (ro->u.table.channels != wo->u.table.channels);
+ rv |= (ro->u.table.entryCount != wo->u.table.entryCount);
+ rv |= (ro->u.table.entrySize != wo->u.table.entrySize);
+ for (i=0; i<ro->u.table.channels*ro->u.table.entryCount*ro->u.table.entrySize; i++) {
+ rv |= (((char*)ro->u.table.data)[i] != ((char*)wo->u.table.data)[i]);
+ if (rv) break;
+ }
+ if (rv)
+ error ("VideoCardGamma verify failed");
+ }
+ }
+ /* ------------------ */
+ /* ViewingConditions: */
+ {
+ static icmViewingConditions *wo;
+ if (mode == 0) {
+ if ((wo = (icmViewingConditions *)wr_icco->add_tag(
+ wr_icco, icSigViewingConditionsTag, icSigViewingConditionsType)) == NULL)
+ return 1;
+
+ wo->illuminant.X = rand_XYZ16(); /* XYZ of illuminant in cd/m^2 */
+ wo->illuminant.Y = rand_XYZ16();
+ wo->illuminant.Z = rand_XYZ16();
+ wo->surround.X = rand_XYZ16(); /* XYZ of surround in cd/m^2 */
+ wo->surround.Y = rand_XYZ16();
+ wo->surround.Z = rand_XYZ16();
+ wo->stdIlluminant = rand_Illuminant(); /* Standard illuminent type */
+ } else {
+ icmViewingConditions *ro;
+
+ /* Try and read the tag from the file */
+ ro = (icmViewingConditions *)rd_icco->read_tag(rd_icco, icSigViewingConditionsTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigViewingConditionsType)
+ return 1;
+
+ /* Now check it out */
+ rv |= dcomp(ro->illuminant.X, wo->illuminant.X);
+ rv |= dcomp(ro->illuminant.Y, wo->illuminant.Y);
+ rv |= dcomp(ro->illuminant.Z, wo->illuminant.Z);
+ rv |= dcomp(ro->surround.X , wo->surround.X);
+ rv |= dcomp(ro->surround.Y , wo->surround.Y);
+ rv |= dcomp(ro->surround.Z , wo->surround.Z);
+ rv |= (ro->stdIlluminant != wo->stdIlluminant);
+
+ if (rv)
+ error ("ViewingConditions verify failed");
+ }
+ }
+ /* ---------- */
+ /* XYZ array: */
+ {
+ static icmXYZArray *wo;
+ if (mode == 0) {
+ unsigned int i;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ return 1;
+
+ wo->size = rand_int(1,7); /* Should be one XYZ number, but test more */
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ for (i = 0; i < wo->size; i++) {
+ wo->data[i].X = rand_XYZ16(); /* Set numbers value */
+ wo->data[i].Y = rand_XYZ16();
+ wo->data[i].Z = rand_XYZ16();
+ }
+ } else {
+ icmXYZArray *ro;
+ unsigned int i;
+
+ /* Try and read the tag from the file */
+ ro = (icmXYZArray *)rd_icco->read_tag(rd_icco, icSigMediaWhitePointTag);
+ if (ro == NULL)
+ return 1;
+
+ /* Need to check that the cast is appropriate. */
+ if (ro->ttype != icSigXYZArrayType)
+ return 1;
+
+ /* Now check it out */
+ if (ro->size != wo->size)
+ error ("XYZArray size doesn't match");
+
+ for (i = 0; i < wo->size; i++) {
+ rv |= dcomp(ro->data[i].X, wo->data[i].X);
+ rv |= dcomp(ro->data[i].Y, wo->data[i].Y);
+ rv |= dcomp(ro->data[i].Z, wo->data[i].Z);
+ }
+
+ if (rv)
+ error ("XYZArray verify failed");
+ }
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------ */
+/* Floating point random number generators */
+/* These are appropriate for the underlying integer */
+/* representations in the icc format. */
+/* This is simply as a convenience so that we can */
+/* test the full range of representation, and */
+/* get away with exact verification. */
+
+/* 32 bit pseudo random sequencer */
+static unsigned int seed = 0x12345678;
+
+/* #define PSRAND(S) ((S) * 1103515245 + 12345) */
+#define PSRAND(S) (((S) & 0x80000000) ? (((S) << 1) ^ 0xa398655d) : ((S) << 1))
+
+unsigned int rand_o8() {
+ ORD32 o32;
+ seed = PSRAND(seed);
+ o32 = seed & 0xff;
+ return o32;
+}
+
+unsigned int rand_o16() {
+ ORD32 o32;
+ seed = PSRAND(seed);
+ o32 = seed & 0xffff;
+ return o32;
+}
+
+unsigned int rand_o32() {
+ ORD32 o32;
+ o32 = seed = PSRAND(seed);
+ return o32;
+}
+
+int rand_int(int low, int high) {
+ int i;
+ seed = PSRAND(seed);
+ i = seed % (high - low + 1);
+ return i + low;
+}
+
+double rand_u8f8() {
+ ORD32 o32;
+ seed = PSRAND(seed);
+ o32 = seed & 0xffff;
+ return (double)o32/256.0;
+}
+
+double rand_u16f16() {
+ ORD32 o32;
+ seed = PSRAND(seed);
+ o32 = seed;
+ return (double)o32/65536.0;
+}
+
+double rand_s15f16() {
+ INR32 i32;
+ seed = PSRAND(seed);
+ i32 = seed;
+ return (double)i32/65536.0;
+}
+
+double rand_XYZ16() {
+ ORD32 o32;
+ seed = PSRAND(seed);
+ o32 = seed & 0xffff;
+ return (double)o32/32768.0;
+}
+
+double rand_L8() {
+ ORD32 o32;
+ seed = PSRAND(seed);
+ o32 = seed & 0xff;
+ return (double)o32/2.550;
+}
+
+double rand_ab8() {
+ ORD32 o32;
+ seed = PSRAND(seed);
+ o32 = seed & 0xff;
+ return (double)o32-128.0;
+}
+
+double rand_L16() {
+ ORD32 o32;
+ seed = PSRAND(seed);
+ o32 = seed & 0xffff;
+ return (double)o32/652.800; /* 0xff00/100.0 */
+}
+
+double rand_ab16() {
+ ORD32 o32;
+ seed = PSRAND(seed);
+ o32 = seed & 0xffff;
+ return ((double)o32/256.0)-128.0;
+}
+
+double rand_8f() {
+ unsigned int rv;
+ seed = PSRAND(seed);
+ rv = seed & 0xff;
+ return (double)rv/255.0;
+}
+
+double rand_16f() {
+ unsigned int rv;
+ seed = PSRAND(seed);
+ rv = seed & 0xffff;
+ return (double)rv/65535.0;
+}
+
+/* Random selectors for ICC flags and enumerayions */
+
+unsigned int rand_ScreenEncodings() {
+ unsigned int flags = 0;
+
+ if (rand_int(0,1) == 0)
+ flags |= icPrtrDefaultScreensTrue;
+
+ if (rand_int(0,1) == 0)
+ flags |= icLinesPerInch;
+
+ return flags;
+}
+
+/* Device attributes */
+unsigned int rand_DeviceAttributes() {
+ unsigned int flags = 0;
+
+ if (rand_int(0,1) == 0)
+ flags |= icTransparency;
+
+ if (rand_int(0,1) == 0)
+ flags |= icMatte;
+
+ return flags;
+}
+
+/* Profile header flags */
+unsigned int rand_ProfileHeaderFlags() {
+ unsigned int flags = 0;
+
+ if (rand_int(0,1) == 0)
+ flags |= icEmbeddedProfileTrue;
+
+ if (rand_int(0,1) == 0)
+ flags |= icUseWithEmbeddedDataOnly;
+
+ return flags;
+}
+
+
+unsigned int rand_AsciiOrBinaryData() {
+ unsigned int flags = 0;
+
+ if (rand_int(0,1) == 0)
+ flags |= icBinaryData;
+
+ return flags;
+}
+
+icColorSpaceSignature rand_ColorSpaceSignature() {
+ switch(rand_int(0,25)) {
+ case 0:
+ return icSigXYZData;
+ case 1:
+ return icSigLabData;
+ case 2:
+ return icSigLuvData;
+ case 3:
+ return icSigYCbCrData;
+ case 4:
+ return icSigYxyData;
+ case 5:
+ return icSigRgbData;
+ case 6:
+ return icSigGrayData;
+ case 7:
+ return icSigHsvData;
+ case 8:
+ return icSigHlsData;
+ case 9:
+ return icSigCmykData;
+ case 10:
+ return icSigCmyData;
+ case 11:
+ return icSigMch6Data;
+ case 12:
+ return icSig2colorData;
+ case 13:
+ return icSig3colorData;
+ case 14:
+ return icSig4colorData;
+ case 15:
+ return icSig5colorData;
+ case 16:
+ return icSig6colorData;
+ case 17:
+ return icSig7colorData;
+ case 18:
+ return icSig8colorData;
+ case 19:
+ return icSig9colorData;
+ case 20:
+ return icSig10colorData;
+ case 21:
+ return icSig11colorData;
+ case 22:
+ return icSig12colorData;
+ case 23:
+ return icSig13colorData;
+ case 24:
+ return icSig14colorData;
+ case 25:
+ return icSig15colorData;
+ }
+ return icMaxEnumData;
+}
+
+icColorSpaceSignature rand_PCS() {
+ switch(rand_int(0,1)) {
+ case 0:
+ return icSigXYZData;
+ case 1:
+ return icSigLabData;
+ }
+ return icMaxEnumData;
+}
+
+icTechnologySignature rand_TechnologySignature() {
+ switch(rand_int(0,21)) {
+ case 0:
+ return icSigDigitalCamera;
+ case 1:
+ return icSigFilmScanner;
+ case 2:
+ return icSigReflectiveScanner;
+ case 3:
+ return icSigInkJetPrinter;
+ case 4:
+ return icSigThermalWaxPrinter;
+ case 5:
+ return icSigElectrophotographicPrinter;
+ case 6:
+ return icSigElectrostaticPrinter;
+ case 7:
+ return icSigDyeSublimationPrinter;
+ case 8:
+ return icSigPhotographicPaperPrinter;
+ case 9:
+ return icSigFilmWriter;
+ case 10:
+ return icSigVideoMonitor;
+ case 11:
+ return icSigVideoCamera;
+ case 12:
+ return icSigProjectionTelevision;
+ case 13:
+ return icSigCRTDisplay;
+ case 14:
+ return icSigPMDisplay;
+ case 15:
+ return icSigAMDisplay;
+ case 16:
+ return icSigPhotoCD;
+ case 17:
+ return icSigPhotoImageSetter;
+ case 18:
+ return icSigGravure;
+ case 19:
+ return icSigOffsetLithography;
+ case 20:
+ return icSigSilkscreen;
+ case 21:
+ return icSigFlexography;
+ }
+ return icMaxEnumTechnology;
+}
+
+icProfileClassSignature rand_ProfileClassSignature() {
+ switch(rand_int(0,6)) {
+ case 0:
+ return icSigInputClass;
+ case 1:
+ return icSigDisplayClass;
+ case 2:
+ return icSigOutputClass;
+ case 3:
+ return icSigLinkClass;
+ case 4:
+ return icSigAbstractClass;
+ case 5:
+ return icSigColorSpaceClass;
+ case 6:
+ return icSigNamedColorClass;
+ }
+ return icMaxEnumClass;
+}
+
+icPlatformSignature rand_PlatformSignature() {
+ switch(rand_int(0,4)) {
+ case 0:
+ return icSigMacintosh;
+ case 1:
+ return icSigMicrosoft;
+ case 2:
+ return icSigSolaris;
+ case 3:
+ return icSigSGI;
+ case 4:
+ return icSigTaligent;
+ }
+ return icMaxEnumPlatform;
+}
+
+icMeasurementFlare rand_MeasurementFlare() {
+ switch(rand_int(0,1)) {
+ case 0:
+ return icFlare0;
+ case 1:
+ return icFlare100;
+ }
+ return icMaxFlare;
+}
+
+icMeasurementGeometry rand_MeasurementGeometry() {
+ switch(rand_int(0,2)) {
+ case 0:
+ return icGeometryUnknown;
+ case 1:
+ return icGeometry045or450;
+ case 2:
+ return icGeometry0dord0;
+ }
+ return icMaxGeometry;
+}
+
+icRenderingIntent rand_RenderingIntent() {
+ switch(rand_int(0,3)) {
+ case 0:
+ return icPerceptual;
+ case 1:
+ return icRelativeColorimetric;
+ case 2:
+ return icSaturation;
+ case 3:
+ return icAbsoluteColorimetric;
+ }
+ return icMaxEnumIntent;
+}
+
+icSpotShape rand_SpotShape() {
+ switch(rand_int(0,7)) {
+ case 0:
+ return icSpotShapeUnknown;
+ case 1:
+ return icSpotShapePrinterDefault;
+ case 2:
+ return icSpotShapeRound;
+ case 3:
+ return icSpotShapeDiamond;
+ case 4:
+ return icSpotShapeEllipse;
+ case 5:
+ return icSpotShapeLine;
+ case 6:
+ return icSpotShapeSquare;
+ case 7:
+ return icSpotShapeCross;
+ }
+ return icMaxEnumSpot;
+}
+
+icStandardObserver rand_StandardObserver() {
+ switch(rand_int(0,2)) {
+ case 0:
+ return icStdObsUnknown;
+ case 1:
+ return icStdObs1931TwoDegrees;
+ case 2:
+ return icStdObs1964TenDegrees;
+ }
+ return icMaxStdObs;
+}
+
+icIlluminant rand_Illuminant() {
+ switch(rand_int(0,8)) {
+ case 0:
+ return icIlluminantUnknown;
+ case 1:
+ return icIlluminantD50;
+ case 2:
+ return icIlluminantD65;
+ case 3:
+ return icIlluminantD93;
+ case 4:
+ return icIlluminantF2;
+ case 5:
+ return icIlluminantD55;
+ case 6:
+ return icIlluminantA;
+ case 7:
+ return icIlluminantEquiPowerE;
+ case 8:
+ return icIlluminantF8;
+ }
+ return icMaxEnumIlluminant;
+}
+
+/* Compare doubles with a margine to allow */
+/* for floating point handling funnies */
+int dcomp(double a, double b) {
+ double dif = fabs(a - b);
+ double mag = fabs(a) + fabs(b);
+
+ return dif > (mag * 1e-10) ? 1 : 0;
+}
+
+/* ------------------------------------------------ */
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"icctest: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"icctest: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
diff --git a/icc/lab2lab.icm b/icc/lab2lab.icm
new file mode 100644
index 0000000..fe518e6
--- /dev/null
+++ b/icc/lab2lab.icm
Binary files differ
diff --git a/icc/log.txt b/icc/log.txt
new file mode 100644
index 0000000..97435b6
--- /dev/null
+++ b/icc/log.txt
@@ -0,0 +1,173 @@
+
+ Change History: (See ArgyllCMS log.txt too)
+
+ 2.15
+ Change icc->read_tag() to only succeed if the tag type is
+ known, since the standard expectation of a non NULL
+ return type is that it is of a known type. Added new
+ method icc->read_tag_any() which will return a
+ icmSigUnknownType if the tag type is unknown.
+
+ 2.14
+ Fix potential array bounds violation in icc/icc.c for malformed
+ cLUT profiles with zero input channels.
+
+ 2.10
+
+ Added protection against crafted file integer overflows.
+
+ Many minor additions to support ArgyllCMS.
+
+ Further add V4 compatibility, with changes to PCS encoding.
+
+ 2.06
+
+ Added MD5 checksum code for profile ID support.
+
+ 2.05
+ Version in Argyll V0.51 release.
+
+ Started adding support for ICC V4. Not enabled yet.
+
+ Expanded internal/effective colorspace/ranges support
+ to be uniform between all the lookup types.
+
+ 2.04
+
+ Fixed minor casting nits picked up by some compilers
+ (thanks to Keith Trummel).
+
+ Make intent selection for Matrix profiles and Gamut tables
+ more forgiving if not ICM_STRICT.
+
+ 2.03
+ Added simple extention when dumping binary data tag to
+ dump ASCII as well if verb >= 4.
+
+ Added Makefile support for compiling under MAC OSX.
+
+ Added access to primitive read and write functions, so custom tag
+ data can be marshalled using standard encodings.
+
+ Expanded ICM_STRICT to encompass bad DateTime information in the header,
+ and also disable required tag checking on reading or lookups.
+ Changed default intent for Output profiles to perceptual from
+ colorimetric, since this would usually be expected.
+
+ Added workaround for faulty Kodak RGB matrix profiles, that
+ have their primary XYZ coordinates scaled to 100.0 rather than
+ 1.0. This is enabled if ICM_STRICT is not defined.
+
+ Replaced Lab<->XYZ functions with simpler versions.
+
+ Expand icmFile implimentation to handle icmAlloc
+
+ Do check for header Magic number first.
+
+ Add convenience function that creates an RGB to XYZ transform
+ matrix from the device primaries and the white point.
+
+ Separated stdio versions of file and alloc to allow
+ compile without these.
+
+ Non-standard (ie. Apple) 5,6,7 & 8 channel color signatures
+ weren't being handled properly thruout the library.
+
+ 2.02
+ Merged rename of [u]int64 to icm[Ui][I]nt64 (to work around
+ AIX 5.1L portability bug) from Raph Levien.
+
+ Fixed stray , in icmLookupOrder structure definition (from Dan Coby)
+
+ 2.01
+ Change TextDescription code to not barf if #undef ICM_STRICT and
+ Apple scriptcode not padded to 67 bytes.
+
+ Add get_ranges() method to all Lu types, not just LuLut.
+ Fix bug in PCS override logic that was causing
+ reverse conversions to apply the wrong conversion.
+
+ Added Delta E convenience functions icmLabDE() and
+ icmCIE94() etc.
+
+ Merged Raph Levien's cleanups, to quiet gcc warnings.
+
+ Merged another couple of warning cleanups from Jouk Jansen.
+
+ 2.00
+ Change absolute conversion to be white point only, and use
+ Bradford transform by default. (ie. we are now ignoring the
+ comment in section 6.4.22 of the 1998 spec. about the
+ media black point being used for absolute colorimetry,
+ ignoring the recommendation on page 118 in section E.5,
+ and are taking up the recommendation on page 124 in section
+ E.16 that a more sophisticated chromatic adaptation model be used.)
+
+ This is for better compatibility with other CMM's, and to
+ improve the results when using simple links between
+ profiles with non-D50 white points. Standard profiles
+ like sRGB will also be more accurate when interpreted
+ with absolute colorimetric intent.
+ This will cause some slight incompatibilty with previous
+ versions of icclib.
+
+ Added ColorSync 2.5 specific VideoCardGamma tag support
+ (from Neil Okamoto)
+
+ 1.31
+ Added file I/O class to allow substitution of alternative ICC profile
+ file access. Provide standard file class instance, and memory image
+ instance of file I/O class as default and example.
+ Added an optional new_icc_a() object creator, that takes a memory
+ allocator class instance. This allows an alternate memory heap to
+ be used with the icc class.
+ Renamed object free() methods to del() for more consistency with new().
+
+ 1.30
+ Added workaround for reading some Adobe profiles with confused header DateTime.
+ Enhanced tag allocate() methods so that they can resize allocations.
+ Enhanced icmLut_set_tables() to access grid points in a cache friendly order.
+ Fixed bug in check_icc_legal() that caused bogus errors, removed
+ uneccessary static declarations in icc.h, and fixed a bug in
+ icmTable_lookup_bwd() that effected both accuracy and speed. (Thanks to Andrei Frolov)
+ Removed icmAbsoluteColorimetricXYZ intent, and replaced it with
+ a PCS override capability. This adds a new parameter to get_luobj()
+ Added Lab translations of some XYZ "dump" strings.
+ Fix memory leak after failed tag read + rename_tag function
+ + shared library support changes. (Thanks to Carles Llopis).
+ Changed all the public 2str utility routines to a single function
+ that can be used to interpret an enumeration or tag in a human
+ readable form.
+
+ 1.23
+ Fixed important bug in Lut read/write. The matrix values had their
+ rows and columns switched. Not many profiles exercise this code.
+ Thanks to David Gillman for discovering this problem.
+ Fixup compiler complains about illegal enum values for icmCurveStyle,
+ and icmDataStyle. Malloc memory icmLut_lookup_clut_nl for gw[], so that
+ it is more friendly to systems with a limited stack. (Thanks to Dave White)
+
+ 1.22 99/11/11 Snapshot of current code.
+ Added more hooks to support inherited implementation of
+ color conversion, used in Argyll to support reversing
+ multi-dimentional table lookups.
+ Cleaned up color conversion code to make it easier to follow.
+ Adding simplex interpolation for non-Lab style input space interpolation.
+ Fix Sun misalignment and realloc problems (Thanks to Allan N. Hessenflow)
+ Fixed endian problem with Unicode on read and write.
+ Expanded icmTextDescription_dump() to do hex dump of Unicode and ScriptCode.
+ Changed over to ICC.1:1998-09 .h file.
+ Started implementing ICC.1:1998-09, but not complete yet!
+
+ 1.21 99/2/14
+ After re-reading Michael Bourgoin's 1998 SIGGRAPH notes,
+ I have consolidated the Lut input index, and table value encodings.
+ The default set_tables() scaling has been adjusted appropriately
+ for this correction of Lab encoding.
+ Trying to create an 8 bit XYZ Lut will now fail if icclib helper
+ functions are used to create it.
+
+ 1.20 99/2/7
+ Added profile color lookup functon.
+ Added set_tables() support.
+ Various bug fixes and enhancements.
diff --git a/icc/lutest.c b/icc/lutest.c
new file mode 100644
index 0000000..e43fbc5
--- /dev/null
+++ b/icc/lutest.c
@@ -0,0 +1,3506 @@
+
+/*
+ * International Color Consortium Format Library (icclib)
+ * Lookup test code, and profile creation examples.
+ *
+ * Author: Graeme W. Gill
+ * Date: 2000/6/18
+ * Version: 2.15
+ *
+ * Copyright 1998 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/* TTBD:
+ *
+ */
+
+/*
+
+ This file is intended to serve two purposes. One is to do some
+ basic regression testing of the lookup function of the icc
+ library, by creating profiles with known mapping characteristics,
+ and then checking that lookup displays those characteristics.
+ Given the huge possible number of combinations of color spaces,
+ profile variations, number of input and output channels, intents etc.
+ the tests done here are far from exaustive.
+
+ The other purpose is as a very basic source code example of how
+ to create various styles of ICC profiles from mapping information,
+ since extracting this information from the source code can take some
+ doing. The example code here is still not perfect, since
+ it doesn't cover the finer points of how to handle various intents,
+ gamut compression etc. Such things are really the domain of a
+ CMS, and would be dependent on the exact nature of the
+ profile representation that the ICC file is being created from.
+ (See examples in the Argyll CMS for these sorts of details.)
+
+ (Note XYZ scaling to 1.0, not 100.0, as per ICC)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+#include "icc.h"
+
+void error(char *fmt, ...), warning(char *fmt, ...);
+
+/* Some debuging aids */
+#define STOPONERROR /* stop if errors are excessive */
+#undef TESTLIN1 /* test linear in curves */
+#undef TESTLIN2 /* test linear clut (fails with Lab) */
+#undef TESTLIN3 /* test linear out curves */
+
+/* These two assist the accuracy of our BToA Lut tests, using our simplistic test functions */
+/* They probably shouldn't be used on any real profile. */
+#define REVLUTSCALE1 /* Define this for pre-clut gamut bounding box scaling */
+#define REVLUTSCALE2 /* Define this for post-clut gamut quantization scaling */
+
+/*
+ * We start with a mathematically defined transfer characteristic, that
+ * we create profiles from, and then check that the lookup function
+ * matches the mathematical characteristic we started with.
+ *
+ * Use a matrix style 3D to 3D XYZ transfer as the core, to be
+ * compatible with a matrix and Lut style profile.
+ */
+
+/* - - - - - - - - - - - - - */
+/* Some support functions */
+
+/* Clip value to range 0.0 to 1.0 */
+void clip(double inout[3]) {
+ int i;
+ for (i = 0; i < 3; i++) {
+ if (inout[i] < 0.0)
+ inout[i] = 0.0;
+ else if (inout[i] > 1.0)
+ inout[i] = 1.0;
+ }
+}
+
+/* Protected power function */
+double ppow(double num, double p) {
+ if (num < 0.0)
+ return -pow(-num, p);
+ else
+ return pow(num, p);
+}
+
+#define D50_X 0.9642
+#define D50_Y 1.0000
+#define D50_Z 0.8249
+
+#define D50_BX ( 0.8951 * D50_X + 0.2664 * D50_Y + -0.1614 * D50_Z)
+#define D50_BY (-0.7502 * D50_X + 1.7135 * D50_Y + 0.0367 * D50_Z)
+#define D50_BZ ( 0.0389 * D50_X + -0.0685 * D50_Y + 1.0296 * D50_Z)
+
+#define ABS_X 0.83
+#define ABS_Y 0.95
+#define ABS_Z 1.05
+
+#define ABS_BX ( 0.8951 * ABS_X + 0.2664 * ABS_Y + -0.1614 * ABS_Z)
+#define ABS_BY (-0.7502 * ABS_X + 1.7135 * ABS_Y + 0.0367 * ABS_Z)
+#define ABS_BZ ( 0.0389 * ABS_X + -0.0685 * ABS_Y + 1.0296 * ABS_Z)
+
+/* Convert from normalized to relative XYZ */
+static void to_rel(double out[3], double in[3]) {
+ /* Scale to relative white and black points */
+ out[0] = D50_X * in[0];
+ out[1] = D50_Y * in[1];
+ out[2] = D50_Z * in[2];
+}
+
+/* Convert from relative to normalized XYZ */
+static void from_rel(double out[3], double in[3]) {
+ /* Remove white and black points scale */
+ out[0] = in[0]/D50_X;
+ out[1] = in[1]/D50_Y;
+ out[2] = in[2]/D50_Z;
+}
+
+/* Convert from relative to absolute XYZ */
+static void rel_to_abs(double out[3], double in[3]) {
+ double tt[3];
+
+ /* Multiply by bradford */
+ tt[0] = 0.8951 * in[0] + 0.2664 * in[1] + -0.1614 * in[2];
+ tt[1] = -0.7502 * in[0] + 1.7135 * in[1] + 0.0367 * in[2];
+ tt[2] = 0.0389 * in[0] + -0.0685 * in[1] + 1.0296 * in[2];
+
+ /* Scale from D50 to absolute white point */
+ tt[0] = tt[0] * ABS_BX/D50_BX;
+ tt[1] = tt[1] * ABS_BY/D50_BY;
+ tt[2] = tt[2] * ABS_BZ/D50_BZ;
+
+ /* Inverse bradford */
+ out[0] = 0.986993 * tt[0] + -0.147054 * tt[1] + 0.159963 * tt[2];
+ out[1] = 0.432305 * tt[0] + 0.518360 * tt[1] + 0.049291 * tt[2];
+ out[2] = -0.008529 * tt[0] + 0.040043 * tt[1] + 0.968487 * tt[2];
+}
+
+/* Convert from normalized to absolute XYZ */
+static void to_abs(double out[3], double in[3]) {
+
+ to_rel(out, in); /* Convert to relative */
+ rel_to_abs(out, out); /* Convert to absolute */
+
+}
+
+/* Convert from absolute to relative XYZ */
+static void abs_to_rel(double out[3], double in[3]) {
+ double tt[3];
+
+ /* Multiply by bradford */
+ tt[0] = 0.8951 * in[0] + 0.2664 * in[1] + -0.1614 * in[2];
+ tt[1] = -0.7502 * in[0] + 1.7135 * in[1] + 0.0367 * in[2];
+ tt[2] = 0.0389 * in[0] + -0.0685 * in[1] + 1.0296 * in[2];
+
+ /* Scale from absolute white point to D50 */
+ tt[0] = tt[0] * D50_BX/ABS_BX;
+ tt[1] = tt[1] * D50_BY/ABS_BY;
+ tt[2] = tt[2] * D50_BZ/ABS_BZ;
+
+ /* Inverse bradford */
+ out[0] = 0.986993 * tt[0] + -0.147054 * tt[1] + 0.159963 * tt[2];
+ out[1] = 0.432305 * tt[0] + 0.518360 * tt[1] + 0.049291 * tt[2];
+ out[2] = -0.008529 * tt[0] + 0.040043 * tt[1] + 0.968487 * tt[2];
+}
+
+#ifdef NEVER /* Not currently used */
+/* Convert from absolute to normalized XYZ */
+static void from_abs(double out[3], double in[3]) {
+
+ abs_to_rel(out, in); /* Convert to relative */
+ from_rel(out, out); /* Convert to normalised */
+}
+#endif /* NEVER */
+
+/* CIE XYZ to perceptual Lab with ICC D50 white point */
+static void
+XYZ2Lab(double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double x,y,z,fx,fy,fz;
+ double L;
+
+ x = X/D50_X;
+ if (x > 0.008856451586)
+ fx = pow(x,1.0/3.0);
+ else
+ fx = 7.787036979 * x + 16.0/116.0;
+
+ y = Y/D50_Y;
+ if (y > 0.008856451586) {
+ fy = pow(y,1.0/3.0);
+ L = 116.0 * fy - 16.0;
+ } else {
+ fy = 7.787036979 * y + 16.0/116.0;
+ L = 903.2963058 * y;
+ }
+
+ z = Z/D50_Z;
+ if (z > 0.008856451586)
+ fz = pow(z,1.0/3.0);
+ else
+ fz = 7.787036979 * z + 16.0/116.0;
+
+ out[0] = L;
+ out[1] = 500.0 * (fx - fy);
+ out[2] = 200.0 * (fy - fz);
+}
+
+/* Perceptual Lab with ICC D50 white point to CIE XYZ */
+static void
+Lab2XYZ(double *out, double *in) {
+ double L = in[0], a = in[1], b = in[2];
+ double x,y,z,fx,fy,fz;
+
+ if (L > 8.0) {
+ fy = (L + 16.0)/116.0;
+ y = pow(fy,3.0);
+ } else {
+ y = L/903.2963058;
+ fy = 7.787036979 * y + 16.0/116.0;
+ }
+
+ fx = a/500.0 + fy;
+ if (fx > 24.0/116.0)
+ x = pow(fx,3.0);
+ else
+ x = (fx - 16.0/116.0)/7.787036979;
+
+ fz = fy - b/200.0;
+ if (fz > 24.0/116.0)
+ z = pow(fz,3.0);
+ else
+ z = (fz - 16.0/116.0)/7.787036979;
+
+ out[0] = x * D50_X;
+ out[1] = y * D50_Y;
+ out[2] = z * D50_Z;
+}
+
+/* - - - - - - - - - - - - - */
+
+/* Return maximum difference */
+static double maxdiff(double in1[3], double in2[3]) {
+ double tt, rv = 0.0;
+ if ((tt = fabs(in1[0] - in2[0])) > rv)
+ rv = tt;
+ if ((tt = fabs(in1[1] - in2[1])) > rv)
+ rv = tt;
+ if ((tt = fabs(in1[2] - in2[2])) > rv)
+ rv = tt;
+ return rv;
+}
+
+/* Return absolute difference */
+static double absdiff(double in1[3], double in2[3]) {
+ double tt, rv = 0.0;
+ tt = in1[0] - in2[0];
+ rv += tt * tt;
+ tt = in1[1] - in2[1];
+ rv += tt * tt;
+ tt = in1[2] - in2[2];
+ rv += tt * tt;
+ return sqrt(rv);
+}
+
+/* - - - - - - - - - - - - - - - - - */
+/* Overall Monochrome XYZ device model is */
+/* Gray -> GrayY -> XYZ */
+/* Where GrayY is assumed to directly Scale Y */
+
+/* Gray -> GrayY */
+static double Gray_GrayY(double in) {
+#ifdef TESTLIN1
+ return in;
+#else
+ return ppow(in,1.6);
+#endif
+}
+
+/* GrayY -> Gray */
+static double GrayY_Gray(double in) {
+#ifdef TESTLIN1
+ return in;
+#else
+ return ppow(in,1.0/1.6);
+#endif
+}
+
+/* Gray -> XYZ */
+static void Gray_XYZ(double out[3], double in) {
+ double temp[3];
+ temp[0] = temp[1] = temp[2] = Gray_GrayY(in);
+
+ /* Scale to relative white and black points */
+ to_rel(out, temp);
+}
+
+/* XYZ -> Device gray space */
+static double XYZ_Gray(double in[3]) {
+ double temp[3];
+
+ /* Remove relative white and black points scale */
+ from_rel(temp, in);
+
+ /* Do calculation just from Y value */
+ return GrayY_Gray(temp[1]);
+}
+
+/* Gray -> XYZ absolute */
+static void aGray_XYZ(double out[3], double in) {
+ double temp[3];
+ temp[0] = temp[1] = temp[2] = Gray_GrayY(in);
+
+ /* Scale to absolute white and black points */
+ to_abs(out, temp);
+
+}
+
+/* XYZ -> Gray absolute */
+static double aXYZ_Gray(double in[3]) {
+ double tt, temp[3];
+
+ /* Remove absolute white and black points scale */
+ abs_to_rel(temp, in); /* Convert to relative */
+
+ from_rel(temp, temp); /* Convert to normalised */
+
+ /* Do calculation just from Y value */
+ tt = GrayY_Gray(temp[1]);
+
+ return tt;
+}
+
+/* - - - - - - - - - - - */
+/* Overall Monochrome Lab device model is */
+/* Gray -> GrayL -> Lab */
+/* Where GrayL is assumed to directly scale L */
+
+
+/* Gray -> GrayL */
+static double Gray_GrayL(double in) {
+#ifdef TESTLIN1
+ return in; /* normalized L */
+#else
+ return ppow(in,1.6);
+#endif
+}
+
+/* GrayL -> Gray */
+static double GrayL_Gray(double in) {
+#ifdef TESTLIN1
+ return in;
+#else
+ return ppow(in,1.0/1.6); /* Y */
+#endif
+}
+
+/* Gray -> Lab */
+static void Gray_Lab(double out[3], double in) {
+ double tt, wp[3];
+
+ wp[0] = D50_X;
+ wp[1] = D50_Y;
+ wp[2] = D50_Z;
+ XYZ2Lab(wp, wp); /* Lab white point */
+
+ tt = Gray_GrayL(in); /* Raw L value */
+
+ /* Scale to relative Lab white point */
+ out[0] = wp[0] * tt;
+ out[1] = wp[1] * tt;
+ out[2] = wp[2] * tt;
+}
+
+/* Lab -> Gray */
+static double Lab_Gray(double in[3]) {
+ double tt, wp[3];
+
+ wp[0] = D50_X;
+ wp[1] = D50_Y;
+ wp[2] = D50_Z;
+ XYZ2Lab(wp, wp); /* Lab white point */
+
+ /* Scale from relative Lab white point */
+ tt = in[0]/wp[0];
+
+ return GrayL_Gray(tt); /* Raw L value */
+
+}
+
+/* Gray -> Lab absolute */
+static void aGray_Lab(double out[3], double in) {
+
+ /* Generate relative Lab */
+ Gray_Lab(out, in);
+
+ Lab2XYZ(out, out);
+ rel_to_abs(out, out);
+ XYZ2Lab(out, out);
+}
+
+/* Lab -> Gray absolute */
+static double aLab_Gray(double in[3]) {
+ double tt[3];
+
+ Lab2XYZ(tt, in);
+ abs_to_rel(tt, tt);
+ XYZ2Lab(tt, tt);
+
+ return Lab_Gray(tt);
+}
+
+/* - - - - - - - - - - - - - - - - - */
+/* RGB -> XYZ test transfer curves */
+/* Note that overall model is: */
+/* RBG -> RGB' -> XYZ' -> XYZ */
+
+/* Device space linearization */
+/* RGB -> RGB' */
+static void RGB_RGBp(void *cntx, double out[3], double in[3]) {
+#ifdef TESTLIN1
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ out[0] = ppow(in[0],1.6);
+ out[1] = ppow(in[1],1.5);
+ out[2] = ppow(in[2],1.4);
+#endif
+}
+
+/* RGB' -> RGB */
+static void RGBp_RGB(void *cntx, double out[3], double in[3]) {
+#ifdef TESTLIN1
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ out[0] = ppow(in[0],1.0/1.6);
+ out[1] = ppow(in[1],1.0/1.5);
+ out[2] = ppow(in[2],1.0/1.4);
+#endif
+}
+
+#ifdef TESTLIN2
+static double matrix[3][3] = {
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+};
+#else
+static double matrix[3][3] = {
+ { 0.4361, 0.3851, 0.1431 },
+ { 0.2225, 0.7169, 0.0606 },
+ { 0.0139, 0.0971, 0.7141 },
+};
+#endif
+
+/* 3x3 matrix conversion */
+/* RGB' -> XYZ' */
+static void RGBp_XYZp(void *cntx, double out[3], double in[3]) {
+ double o0,o1,o2;
+
+#ifdef TESTLIN2
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ o0 = matrix[0][0] * in[0] + matrix[0][1] * in[1] + matrix[0][2] * in[2];
+ o1 = matrix[1][0] * in[0] + matrix[1][1] * in[1] + matrix[1][2] * in[2];
+ o2 = matrix[2][0] * in[0] + matrix[2][1] * in[1] + matrix[2][2] * in[2];
+
+ out[0] = o0;
+ out[1] = o1;
+ out[2] = o2;
+#endif
+}
+
+/* XYZ' -> RGB' */
+static void XYZp_RGBp(void *cntx, double out[3], double in[3]) {
+ double o0,o1,o2;
+
+#ifdef TESTLIN2
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ o0 = 3.13360257102309000 * in[0] + -1.61682140135654430 * in[1] + -0.49074240441282400 * in[2];
+ o1 = -0.97865031588250000 * in[0] + 1.91606100412532800 * in[1] + 0.03351290204844009 * in[2];
+ o2 = 0.07207655781398956 * in[0] + -0.22906554547222160 * in[1] + 1.40535949675456500 * in[2];
+
+ out[0] = o0;
+ out[1] = o1;
+ out[2] = o2;
+#endif
+}
+
+/* Output linearization curves */
+/* XYZ' -> XYZ */
+static void XYZp_XYZ(void *cntx, double out[3], double in[3]) {
+#ifdef TESTLIN3
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ out[0] = ppow(in[0],0.9);
+ out[1] = ppow(in[1],0.8);
+ out[2] = ppow(in[2],1.1);
+#endif
+}
+
+/* XYZ -> XYZ' */
+static void XYZ_XYZp(void *cntx, double out[3], double in[3]) {
+#ifdef TESTLIN3
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+#else
+ out[0] = ppow(in[0],1.0/0.9);
+ out[1] = ppow(in[1],1.0/0.8);
+ out[2] = ppow(in[2],1.0/1.1);
+#endif
+}
+
+/* RGB -> XYZ' */
+static void RGB_XYZp(void *cntx, double out[3], double in[3]) {
+ RGB_RGBp(cntx, out, in);
+ RGBp_XYZp(cntx, out, out);
+}
+
+/* RGB -> XYZ', absolute (for matrix profile test) */
+static void aRGB_XYZp(void *cntx, double out[3], double in[3]) {
+ RGB_RGBp(cntx, out, in);
+ RGBp_XYZp(cntx, out, out);
+ from_rel(out, out);
+ to_abs(out, out);
+}
+
+/* RGB -> XYZ */
+static void RGB_XYZ(void *cntx, double out[3], double in[3]) {
+ RGB_RGBp(cntx, out, in);
+ RGBp_XYZp(cntx, out, out);
+ XYZp_XYZ(cntx, out, out);
+}
+
+/* XYZ -> RGB */
+static void XYZ_RGB(void *cntx, double out[3], double in[3]) {
+ XYZ_XYZp(cntx, out, in);
+ XYZp_RGBp(cntx, out, out);
+ RGBp_RGB(cntx, out, out);
+}
+
+/* RGB -> XYZ, absolute */
+static void aRGB_XYZ(void *cntx, double out[3], double in[3]) {
+ RGB_XYZ(cntx, out, in);
+ from_rel(out, out);
+ to_abs(out, out);
+}
+
+#ifdef NEVER /* Not currently used */
+
+/* XYZ -> RGB, absolute */
+static void aXYZ_RGB(void *cntx, double out[3], double in[3]) {
+ from_abs(out, in);
+ to_rel(out, out);
+ XYZ_RGB(cntx, out, out);
+}
+
+/* XYZ -> RGB, gamut constrained */
+static void cXYZ_RGB(void *cntx, double out[3], double in[3]) {
+ XYZ_RGB(cntx, out, in);
+ clip(out);
+}
+
+#endif /* NEVER */
+
+/* XYZ' -> distance to gamut boundary */
+static void XYZp_BDIST(void *cntx, double out[1], double in[3]) {
+ double gdst; /* Gamut error */
+ int m, mini = 0, outg;
+ double tt, mind;
+ double pcs[3]; /* PCS value of input */
+ double dev[3]; /* Device value */
+ double pgb[3]; /* PCS gamut boundary point */
+ double dgb[3]; /* device gamut boundary point */
+
+ /* Do XYZ' -> XYZ */
+ XYZp_XYZ(cntx, pcs, in);
+
+ /* Do XYZ -> RGB transform */
+ XYZ_RGB(NULL, dev, pcs);
+
+ /* Compute nearest point on gamut boundary, */
+ /* and whether it is in or out of gamut. */
+ /* This should be nearest in PCS space, but */
+ /* we'll cheat and find the nearest point in */
+ /* device space, and then compute the distance in PCS space. */
+
+ for (m = 0; m < 3; m++)
+ dgb[m] = dev[m];
+
+ for (mind = 10000.0, outg = 0, m = 0; m < 3; m++) {
+ if (dev[m] < 0.0) { /* Clip any coordinates outside device limits */
+ dgb[m] = 0.0;
+ outg = 1; /* Out of gamut */
+ } else if (dev[m] > 1.0) {
+ dgb[m] = 1.0;
+ outg = 1; /* Out of gamut */
+ } else { /* Note closest cood to boundary if within limits */
+ if (dev[m] < 0.5)
+ tt = 0.5 - dev[m];
+ else /* >= 0.5 */
+ tt = dev[m] - 0.5;
+ if (tt < mind) {
+ mind = tt;
+ mini = m;
+ }
+ }
+ }
+ if (!outg) { /* If point is within gamut, set to closest point */
+ if (dev[mini] < 0.5)
+ dgb[mini] = 0.0;
+ else
+ dgb[mini] = 1.0;
+ }
+
+ /* Do RGB -> XYZ transform on nearest gamut boundary point */
+ RGB_XYZ(NULL, pgb, dgb);
+
+ /* Distance to nearest gamut point in PCS (XYZ) space */
+ gdst = absdiff(pcs, pgb);
+ if (!outg) /* If within gamut */
+ gdst = -gdst;
+
+ /* Distance in PCS space will be roughly -0.866 -> 0.866 */
+ /* Convert so that 0.5 is on boundary, and then clip. */
+ gdst += 0.5;
+ if (gdst < 0.0)
+ gdst = 0.0;
+ else if (gdst > 1.0)
+ gdst = 1.0;
+
+ out[0] = gdst;
+}
+
+/* The output table is usually a special for the gamut table, returning */
+/* a value of 0 for all inputs <= 0.5, and then outputing between */
+/* 0.0 and 1.0 for the input range 0.5 to 1.0. This is so a graduated */
+/* "gamut boundary distance" number from the multi-d lut can be */
+/* translated into the ICC "0.0 if in gamut, > 0.0 if not" number. */
+static void BDIST_GAMMUT(void *cntx, double out[1], double in[1]) {
+ double iv, ov;
+ iv = in[0];
+ if (iv <= 0.5)
+ ov = 0.0;
+ else
+ ov = (iv - 0.5) * 2.0;
+ out[0] = ov;
+}
+
+/* - - - - - - - - - - - - - */
+/* Lab versions for Lut profile, built on top of XYZ model */
+/* The overall model is: */
+/* RBG -> RGB' -> Lab' -> Lab */
+
+/* 3x3 matrix conversion */
+/* RGB' -> Lab' */
+static void RGBp_Labp(void *cntx, double out[3], double in[3]) {
+ RGBp_XYZp(cntx, out, in);
+ XYZ2Lab(out, out);
+}
+
+/* Lab' -> RGB' */
+static void Labp_RGBp(void *cntx, double out[3], double in[3]) {
+ Lab2XYZ(out, in);
+ XYZp_RGBp(cntx, out, out);
+}
+
+/* Lab' -> Lab */
+/* (We are using linear) */
+static void Labp_Lab(void *cntx, double out[3], double in[3]) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+/* Lab -> Lab' */
+static void Lab_Labp(void *cntx, double out[3], double in[3]) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+/* RGB -> Lab */
+static void RGB_Lab(void *cntx, double out[3], double in[3]) {
+ RGB_RGBp(cntx, out, in);
+ RGBp_Labp(cntx, out, out);
+ Labp_Lab(cntx, out, out);
+}
+
+/* Lab -> RGB */
+static void Lab_RGB(void *cntx, double out[3], double in[3]) {
+ Lab_Labp(cntx, out, in);
+ Labp_RGBp(cntx, out, out);
+ RGBp_RGB(cntx, out, out);
+}
+
+/* RGB -> Lab, absolute */
+static void aRGB_Lab(void *cntx, double out[3], double in[3]) {
+ RGB_Lab(cntx, out, in);
+ Lab2XYZ(out, out);
+ from_rel(out, out);
+ to_abs(out, out);
+ XYZ2Lab(out, out);
+}
+
+#ifdef NEVER /* Not currently used */
+
+/* Lab -> RGB, absolute */
+static void aLab_RGB(void *cntx, double out[3], double in[3]) {
+ Lab2XYZ(out, in);
+ from_abs(out, out);
+ to_rel(out, out);
+ XYZ2Lab(out, out);
+ Lab_RGB(cntx, out, out);
+}
+
+/* Lab -> RGB, gamut constrained */
+static void cLab_RGB(void *cntx, double out[3], double in[3]) {
+ Lab_RGB(cntx, out, in);
+ clip(out);
+}
+
+#endif /* NEVER */
+
+/* Lab' -> distance to gamut boundary */
+static void Labp_BDIST(void *cntx, double out[1], double in[3]) {
+ double gdst; /* Gamut error */
+ int m, mini = 0, outg;
+ double tt, mind;
+ double pcs[3]; /* PCS value of input */
+ double dev[3]; /* Device value */
+ double pgb[3]; /* PCS gamut boundary point */
+ double dgb[3]; /* device gamut boundary point */
+
+ /* Do Lab' -> Lab */
+ Labp_Lab(cntx, pcs, in);
+
+ /* Do Lab -> RGB transform */
+ Lab_RGB(cntx, dev, pcs);
+
+ /* Compute nearest point on gamut boundary, */
+ /* and whether it is in or out of gamut. */
+ /* This should be nearest in PCS space, but */
+ /* we'll cheat and find the nearest point in */
+ /* device space, and then compute the distance in PCS space. */
+
+ for (m = 0; m < 3; m++)
+ dgb[m] = dev[m];
+
+ for (mind = 10000.0, outg = 0, m = 0; m < 3; m++) {
+ if (dev[m] < 0.0) { /* Clip any coordinates outside device limits */
+ dgb[m] = 0.0;
+ outg = 1; /* Out of gamut */
+ } else if (dev[m] > 1.0) {
+ dgb[m] = 1.0;
+ outg = 1; /* Out of gamut */
+ } else { /* Note closest cood to boundary if within limits */
+ if (dev[m] < 0.5)
+ tt = 0.5 - dev[m];
+ else /* >= 0.5 */
+ tt = dev[m] - 0.5;
+ if (tt < mind) {
+ mind = tt;
+ mini = m;
+ }
+ }
+ }
+ if (!outg) { /* If point is within gamut, set to closest point */
+ if (dev[mini] < 0.5)
+ dgb[mini] = 0.0;
+ else
+ dgb[mini] = 1.0;
+ }
+
+ /* Do RGB -> Lab transform on nearest gamut boundary point */
+ RGB_Lab(NULL, pgb, dgb);
+
+ /* Distance to nearest gamut point in PCS (Lab) space */
+ gdst = absdiff(pcs, pgb);
+ if (!outg) /* If within gamut */
+ gdst = -gdst;
+
+ /* Distance in PCS space will be roughly -86 -> 86 */
+ /* Convert so that 0.5 is on boundary, and then clip. */
+ gdst /= 100.0;
+ gdst += 0.5;
+ if (gdst < 0.0)
+ gdst = 0.0;
+ else if (gdst > 1.0)
+ gdst = 1.0;
+
+ out[0] = gdst;
+}
+
+/* - - - - - - - - - - - - - */
+
+#define TRES 10
+#define MON_POINTS 8101 /* Number of test points in monochrome tests */
+
+int
+main(
+ int argc,
+ char *argv[]
+) {
+ char *file_name;
+ icmFile *wr_fp, *rd_fp;
+ icc *wr_icco, *rd_icco; /* Keep object separate */
+ int rv = 0;
+
+ /* Check variables */
+ int co[3];
+ double in[3], out[3], check[3];
+
+ {
+
+#if defined(__IBMC__) && defined(_M_IX86)
+ _control87(EM_UNDERFLOW, EM_UNDERFLOW);
+#endif
+ printf("Starting lookup function test - V%s\n",ICCLIB_VERSION_STR);
+
+ /* Do a check that our reference function is reversable */
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do device -> XYZ transform */
+ RGB_XYZ(NULL, out, in);
+
+ /* Do XYZ -> Device transform */
+ XYZ_RGB(NULL, check, out);
+
+ /* Check the result */
+ mxd = maxdiff(in, check);
+ if (mxd > 0.00001)
+#ifdef STOPONERROR
+ error ("Excessive error %f > 0.00001",mxd);
+#else
+ warning ("Excessive error %f > 0.00001",mxd);
+#endif /* STOPONERROR */
+ }
+ }
+ }
+ printf("Self check complete\n");
+ }
+
+ /* ---------------------------------------- */
+ /* Create a monochrome XYZ profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_mono_XYZ.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigDisplayClass; /* Could use Output or Input too */
+ wh->colorSpace = icSigGrayData; /* It's a gray space */
+ wh->pcs = icSigXYZData; /* Test XYZ monochrome profile */
+ wh->renderingIntent = icRelativeColorimetric;
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test monochrome XYZ style Display Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* Gray Tone Reproduction Curve Tags: */
+ {
+ icmCurve *wog;
+ unsigned int i;
+ if ((wog = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigGrayTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wog->flag = icmCurveSpec; /* Specified version */
+ wog->size = 256; /* Number of entries (min must be 2!) */
+ wog->allocate((icmBase *)wog); /* Allocate space */
+ for (i = 0; i < wog->size; i++) {
+ double vv;
+ vv = i/(wog->size-1.0);
+ wog->data[i] = Gray_GrayY(vv);
+ }
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the monochrome XYZ profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ Gray_XYZ(check,in[0]);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome XYZ Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in Monochrome XYZ Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome XYZ fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function */
+ {
+ double min[3], range[3];
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Establish the range */
+ Gray_XYZ(min,0.0);
+ Gray_XYZ(range,1.0);
+ range[0] -= min[0];
+ range[1] -= min[1];
+ range[2] -= min[2];
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = range[0] * co[0]/(MON_POINTS-1.0) + min[0];
+ in[1] = range[1] * co[0]/(MON_POINTS-1.0) + min[1];
+ in[2] = range[2] * co[0]/(MON_POINTS-1.0) + min[2];
+
+ /* Do reference conversion of XYZ -> device transform */
+ check[0] = XYZ_Gray(in);
+
+ /* Do reverse lookup of XYZ -> device transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = fabs(check[0] - out[0]);
+ if (mxd > 0.00018)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome XYZ Bwd %f > 0.00018",mxd);
+#else
+ warning ("Excessive error in Monochrome XYZ Bwd %f > 0.00018",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome XYZ bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the lookup function, absolute colorimetric */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ aGray_XYZ(check,in[0]);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome XYZ Abs Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in Monochrome XYZ Abs Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome XYZ fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function, absolute colorimetric */
+ {
+ double min[3], range[3];
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Establish the range */
+ /* Establish the range */
+ aGray_XYZ(min,0.0);
+ aGray_XYZ(range,1.0);
+ range[0] -= min[0];
+ range[1] -= min[1];
+ range[2] -= min[2];
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = range[0] * co[0]/(MON_POINTS-1.0) + min[0];
+ in[1] = range[1] * co[0]/(MON_POINTS-1.0) + min[1];
+ in[2] = range[2] * co[0]/(MON_POINTS-1.0) + min[2];
+
+ /* Do reference conversion of XYZ -> device transform */
+ check[0] = aXYZ_Gray(in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = fabs(check[0] - out[0]);
+ if (mxd > 0.0002)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome XYZ Abs Bwd %f > 0.0002",mxd);
+#else
+ warning ("Excessive error in Monochrome XYZ Abs Bwd %f > 0.0002",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome XYZ bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a monochrome Lab profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_mono_Lab.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigDisplayClass; /* Could use Output or Input too */
+ wh->colorSpace = icSigGrayData; /* It's a gray space */
+ wh->pcs = icSigLabData; /* Use Lab for this monochrome profile */
+ wh->renderingIntent = icRelativeColorimetric;
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test monochrome Lab style Display Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* Gray Tone Reproduction Curve Tags: */
+ {
+ icmCurve *wog;
+ unsigned int i;
+ if ((wog = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigGrayTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wog->flag = icmCurveSpec; /* Specified version */
+ wog->size = 256; /* Number of entries (min must be 2!) */
+ wog->allocate((icmBase *)wog); /* Allocate space */
+ for (i = 0; i < wog->size; i++) {
+ double vv;
+ vv = i/(wog->size-1.0);
+ wog->data[i] = Gray_GrayL(vv);
+ }
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the monochrome Lab profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ Gray_Lab(check,in[0]);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.0025)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome Lab Fwd %f > 0.0025",mxd);
+#else
+ warning ("Excessive error in Monochrome Lab Fwd %f > 0.0025",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function */
+ {
+ double min, range;
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Establish the range */
+ Gray_Lab(out,0.0);
+ min = out[0];
+ Gray_Lab(out,1.0);
+ range = out[0] - min;
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = range * co[0]/(MON_POINTS-1.0) + min;
+
+ /* Do reference conversion of Lab -> device transform */
+ check[0] = Lab_Gray(in);
+
+ /* Do reverse lookup of Lab -> device transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = fabs(check[0] - out[0]);
+ if (mxd > 0.0002)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome Lab Bwd %f > 0.0002",mxd);
+#else
+ warning ("Excessive error in Monochrome Lab Bwd %f > 0.0002",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+#ifdef NEVER
+ /* Check the fwd/bwd accuracy */
+ {
+ double merr = 0.0;
+ icmLuBase *luof, *luob;
+
+ /* Get a fwd conversion object */
+ if ((luof = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ /* Get a bwd conversion object */
+ if ((luob = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ /* Check it out */
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luof->lookup(luof, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luob->lookup(luob, check, out)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ mxd = fabs(in[0] - check[0]);
+ if (mxd > 1e-6) {
+ printf("co %d, in %f, out %f, check %f, err %f\n",
+ co[0],in[0],out[0],check[0], fabs(in[0] - check[0]));
+ }
+
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab fwd/bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup objects */
+ luof->del(luof);
+ luob->del(luob);
+ }
+
+ /* Benchmark the routines */
+ {
+ int ii;
+ icmLuBase *luo;
+ double no_pixels = 0.0;
+ clock_t stime,ttime;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ stime = clock();
+ no_pixels = 1000.0 * 2048.0;
+
+ for (ii = 0; ii < 1000; ii++) {
+ for (co[0] = 0; co[0] < 2048; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(2048-1.0);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+ }
+ }
+ ttime = clock() - stime;
+ printf("Done - %f seconds, rate = %f Mpix/sec\n",
+ (double)ttime/CLOCKS_PER_SEC,no_pixels * CLOCKS_PER_SEC/ttime);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ {
+ int ii;
+ icmLuBase *luo;
+ double no_pixels = 0.0;
+ clock_t stime,ttime;
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ stime = clock();
+ no_pixels = 1000.0 * 2048.0;
+
+ for (ii = 0; ii < 1000; ii++) {
+ for (co[0] = 0; co[0] < 2048; co[0]++) {
+ double mxd;
+ in[0] = 100.0 * co[0]/(2048-1.0);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+ }
+ }
+ ttime = clock() - stime;
+ printf("Done - %f seconds, rate = %f Mpix/sec\n",
+ (double)ttime/CLOCKS_PER_SEC,no_pixels * CLOCKS_PER_SEC/ttime);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+#endif /* NEVER */
+
+ /* Check the lookup function, absolute colorimetric */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = co[0]/(MON_POINTS-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aGray_Lab(check,in[0]);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.003)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome Lab Fwd Abs %f > 0.003",mxd);
+#else
+ warning ("Excessive error in Monochrome Lab Fwd Abs %f > 0.003",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function, absolute colorimetric*/
+ {
+ double min, range;
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Establish the range */
+ aGray_Lab(out,0.0);
+ min = out[0];
+ aGray_Lab(out,1.0);
+ range = out[0] - min;
+
+ /* Get a bwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < MON_POINTS; co[0]++) {
+ double mxd;
+ in[0] = range * co[0]/(MON_POINTS-1.0) + min;
+ in[1] = in[2] = 0.0;
+
+ /* Do reference conversion of Lab -> device transform */
+ check[0] = aLab_Gray(in);
+
+ /* Do reverse lookup of Lab -> device transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = fabs(check[0] - out[0]);
+ if (mxd > 0.005)
+#ifdef STOPONERROR
+ error ("Excessive error in Monochrome Lab Bwd Abs %f > 0.005",mxd);
+#else
+ warning ("Excessive error in Monochrome Lab Bwd Abs %f > 0.005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ printf("Monochrome Lab bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a matrix based profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_matrix.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigDisplayClass; /* Could use Output or Input too */
+ wh->colorSpace = icSigRgbData; /* It's and RGBish space */
+ wh->pcs = icSigXYZData; /* Must be XYZ for matrix based profile */
+ wh->renderingIntent = icRelativeColorimetric;
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test matrix style Display Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* Red, Green and Blue Colorant Tags: */
+ {
+ icmXYZArray *wor, *wog, *wob;
+ if ((wor = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigRedColorantTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+ if ((wog = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigGreenColorantTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+ if ((wob = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigBlueColorantTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wor->size = wog->size = wob->size = 1;
+ wor->allocate((icmBase *)wor); /* Allocate space */
+ wog->allocate((icmBase *)wog);
+ wob->allocate((icmBase *)wob);
+ wor->data[0].X = matrix[0][0]; wor->data[0].Y = matrix[1][0]; wor->data[0].Z = matrix[2][0];
+ wog->data[0].X = matrix[0][1]; wog->data[0].Y = matrix[1][1]; wog->data[0].Z = matrix[2][1];
+ wob->data[0].X = matrix[0][2]; wob->data[0].Y = matrix[1][2]; wob->data[0].Z = matrix[2][2];
+ }
+ /* Red, Green and Blue Tone Reproduction Curve Tags: */
+ {
+ icmCurve *wor, *wog, *wob;
+ unsigned int i;
+ if ((wor = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigRedTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+ if ((wog = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigGreenTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+ if ((wob = (icmCurve *)wr_icco->add_tag(
+ wr_icco, icSigBlueTRCTag, icSigCurveType)) == NULL)
+ error("add_tag failed: %d, %s",rv,wr_icco->err);
+
+ wor->flag = wog->flag = wob->flag = icmCurveSpec; /* Specified version */
+ wor->size = wog->size = wob->size = 256; /* Number of entries (min must be 2!) */
+ wor->allocate((icmBase *)wor); /* Allocate space */
+ wog->allocate((icmBase *)wog);
+ wob->allocate((icmBase *)wob);
+ for (i = 0; i < wor->size; i++) {
+ double vv[3];
+ vv[0] = vv[1] = vv[2] = i/(wor->size-1.0);
+ RGB_RGBp(NULL, vv, vv); /* Transfer function we want */
+ wor->data[i] = vv[0]; /* Curve values 0.0 - 1.0 */
+ wog->data[i] = vv[1];
+ wob->data[i] = vv[2];
+ }
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the Matrix based profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the forward lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ RGB_XYZp(NULL, check,in);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in Matrix Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in Matrix Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Matrix fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> XYZ */
+ RGB_XYZp(NULL, check,in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.0002)
+#ifdef STOPONERROR
+ error ("Excessive error in Matrix Bwd %f > 0.0002",mxd);
+#else
+ warning ("Excessive error in Matrix Bwd %f > 0.0002",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Matrix bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the forward absolute lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> abs XYZ transform */
+ aRGB_XYZp(NULL, check,in);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Matrix Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in Abs Matrix Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Matrix fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse absolute lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> abs XYZ */
+ aRGB_XYZp(NULL, check,in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.001)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Matrix Bwd %f > 0.001",mxd);
+#else
+ warning ("Excessive error in Abs Matrix Bwd %f > 0.001",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Matrix bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a Lut16 based XYZ profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_lut16_XYZ.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigOutputClass;
+ wh->colorSpace = icSigRgbData; /* It's and RGBish space */
+ wh->pcs = icSigXYZData;
+ wh->renderingIntent = icRelativeColorimetric; /* For want of something */
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test Lut XYZ style Output Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+
+ /* 16 bit dev -> pcs lut: */
+ {
+ icmLut *wo;
+ double xyzmin[3] = {0.0, 0.0, 0.0};
+ double xyzmax[3] = {1.0, 1.0, 1.0}; /* Override default XYZ max of 1.999969482422 */
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigAToB1Tag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 4096;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* So we can't use it for this lut. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigRgbData, /* Input color space */
+ icSigXYZData, /* Output color space */
+ RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */
+ NULL, NULL, /* Use default Maximum range of RGB' values */
+ RGBp_XYZp, /* RGB' -> XYZ' transfer function */
+ xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */
+ XYZp_XYZ) != 0) /* Output transfer function, XYZ'->XYZ (NULL = deflt) */
+ error("Setting 16 bit RGB->XYZ Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 bit dev -> pcs lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB0Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 dev -> pcs bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB2Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 bit pcs -> dev lut: */
+ {
+ icmLut *wo;
+ double xyzmin[3] = {0.0, 0.0, 0.0}; /* XYZ' range */
+ double xyzmax[3] = {1.0, 1.0, 1.0}; /* Override default XYZ max of 1.999969482422 */
+ double rgbmin[3] = {0.0, 0.0, 0.0}; /* RGB' range */
+ double rgbmax[3] = {1.0, 1.0, 1.0};
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigBToA1Tag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 1024; /* (power curves are hard to represent in tables at small values) */
+ wo->outputEnt = 4096;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* (so it could be used here) */
+ /* Matrix not tested:
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ wo->e[i][j] = ??;
+ */
+
+#ifdef REVLUTSCALE1
+ {
+ /* In any any real profile, you will probably be providing a clut */
+ /* function that carefully maps out of gamut PCS values to in-gamut */
+ /* device values, so the scaling done here won't be appropriate. */
+ /* */
+ /* For this regresion test, we are interested in maximizing accuracy */
+ /* over the known gamut of the device. It is an advantage therefore */
+ /* to scale the internal lut values to this end. */
+
+ /* We'll do a really simple sampling search of the device gamut to */
+ /* establish the XYZ' bounding box. */
+
+ int co[3];
+ double in[3], out[3];
+
+ xyzmin[0] = xyzmin[1] = xyzmin[2] = 1.0;
+ xyzmax[0] = xyzmax[1] = xyzmax[2] = 0.0;
+ for (co[0] = 0; co[0] < 11; co[0]++) {
+ in[0] = co[0]/(11-1.0);
+ for (co[1] = 0; co[1] < 11; co[1]++) {
+ in[1] = co[1]/(11-1.0);
+ for (co[2] = 0; co[2] < 11; co[2]++) {
+ in[2] = co[2]/(11-1.0);
+
+ /* Do RGB -> XYZ' transform */
+ RGB_XYZp(NULL, out,in);
+ if (out[0] < xyzmin[0])
+ xyzmin[0] = out[0];
+ if (out[0] > xyzmax[0])
+ xyzmax[0] = out[0];
+ if (out[1] < xyzmin[1])
+ xyzmin[1] = out[1];
+ if (out[1] > xyzmax[1])
+ xyzmax[1] = out[1];
+ if (out[2] < xyzmin[2])
+ xyzmin[2] = out[2];
+ if (out[2] > xyzmax[2])
+ xyzmax[2] = out[2];
+ }
+ }
+ }
+ }
+#endif
+
+#ifdef REVLUTSCALE2
+ {
+ /* In any any real profile, you will probably be providing a clut */
+ /* function that carefully maps out of gamut PCS values to in-gamut */
+ /* device values, so the scaling done here won't be appropriate. */
+ /* */
+ /* For this regresion test, we are interested in maximizing accuracy */
+ /* over the known gamut of the device. */
+ /* By setting the min/max to a larger range than will actually be */
+ /* used, we can make sure that the extreme table values of the */
+ /* clut are not actually used, and therefore we won't see the */
+ /* rounding effects of these extreme values being clipped */
+ /* by the numerical limits of the ICC representation. */
+ /* Instead the extreme values will be clipped by the the */
+ /* higher resolution output table. */
+ /* */
+ /* This all assumes that the multi-d reverse transform we are trying */
+ /* to represent in the profile extrapolates beyond the legal device */
+ /* value range. */
+ /* */
+ /* The scaling was chosen by experiment to make sure that the full */
+ /* gamut is surrounded by one row of extrapolated, unclipped clut */
+ /* table entries. */
+
+ int i;
+ for (i = 0; i < 3; i++) {
+ rgbmin[i] = -0.1667; /* Magic numbers */
+ rgbmax[i] = 1.1667;
+ }
+ }
+#endif
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigXYZData, /* Input color space */
+ icSigRgbData, /* Output color space */
+ XYZ_XYZp, /* Input transfer function, XYZ->XYZ' (NULL = default) */
+ xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */
+ XYZp_RGBp, /* XYZ' -> RGB' transfer function */
+ rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */
+ RGBp_RGB) != 0) /* Output transfer function, RGB'->RGB (NULL = deflt) */
+ error("Setting 16 bit XYZ->RGB Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ }
+ /* 16 bit pcs -> dev lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA0Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 pcs -> dev bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA2Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* 16 bit pcs -> gamut lut: */
+ {
+ icmLut *wo;
+ double xyzmin[3] = {0.0, 0.0, 0.0}; /* XYZ' range */
+ double xyzmax[3] = {1.0, 1.0, 1.0}; /* Override default XYZ max of 1.999969482422 */
+
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigGamutTag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 1;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* (so it could be used here) */
+ /* Matrix not tested:
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ wo->e[i][j] = ??;
+ */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigXYZData, /* Input color space */
+ icSigGrayData, /* Output color space */
+ XYZ_XYZp, /* Input transfer function, XYZ->XYZ' (NULL = default) */
+ xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */
+ XYZp_BDIST, /* XYZ' -> Boundary Distance transfer function */
+ NULL, NULL, /* Default range from clut to output table */
+ BDIST_GAMMUT /* Boundary Distance -> Out of gamut distance */
+ ) != 0)
+ error("Setting 16 bit XYZ->Gammut Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the Lut XYZ style profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ RGB_XYZ(NULL, check, in);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in XYZ Lut Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in XYZ Lut Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut XYZ fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+ int co[3];
+ double in[3], out[3], check[3];
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of XYZ -> device transform */
+ RGB_XYZ(NULL, check, in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.002) {
+#ifdef STOPONERROR
+ error ("Excessive error in XYZ Lut Bwd %f > 0.002",mxd);
+#else
+ warning ("Excessive error in XYZ Lut Bwd %f > 0.002",mxd);
+#endif /* STOPONERROR */
+ }
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut XYZ bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> XYZ transform */
+ aRGB_XYZ(NULL, check,in);
+
+ /* Do lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 0.00005)
+#ifdef STOPONERROR
+ error ("Excessive error in XYZ Abs Lut Fwd %f > 0.00005",mxd);
+#else
+ warning ("Excessive error in XYZ Abs Lut Fwd %f > 0.00005",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut XYZ fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of XYZ -> device transform */
+ aRGB_XYZ(NULL, check,in);
+
+ /* Do reverse lookup of device -> XYZ transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.002)
+#ifdef STOPONERROR
+ error ("Excessive error in XYZ Abs Lut Bwd %f > 0.002",mxd);
+#else
+ warning ("Excessive error in XYZ Abs Lut Bwd %f > 0.002",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut XYZ bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the XYZ gamut function */
+ {
+ int ino,ono,iok,ook;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmGamut, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ ino = ono = iok = ook = 0;
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ int outgamut;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do gamut lookup of XYZ transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Do reference conversion of XYZ -> RGB */
+ XYZ_RGB(NULL, check,in);
+
+ /* Check the result */
+ outgamut = 1; /* assume on edge */
+ if (check[0] < -0.01 || check[0] > 1.01
+ || check[1] < -0.01 || check[1] > 1.01
+ || check[2] < -0.01 || check[2] > 1.01)
+ outgamut = 2; /* Definitely out of gamut */
+ if (check[0] > 0.01 && check[0] < 0.99
+ && check[1] > 0.01 && check[1] < 0.99
+ && check[2] > 0.01 && check[2] < 0.99)
+ outgamut = 0; /* Definitely in gamut */
+
+ /* Keep record of agree/disagree */
+ if (outgamut <= 1) {
+ ino++;
+ if (out[0] <= 0.01)
+ iok++;
+ } else {
+ ono++;
+ if (out[0] > 0.01)
+ ook++;
+ }
+ }
+ }
+ }
+ printf("Lut XYZ gamut check inside correct = %f%%\n",100.0 * iok/ino);
+ printf("Lut XYZ gamut check outside correct = %f%%\n",100.0 * ook/ono);
+ printf("Lut XYZ gamut check total correct = %f%%\n",100.0 * (iok+ook)/(ino+ono));
+ if (((double)iok/ino) < 0.99 || ((double)ook/ono) < 0.98)
+#ifdef STOPONERROR
+ error ("Gamut XYZ lookup has excessive error");
+#else
+ warning ("Gamut XYZ lookup has excessive error");
+#endif /* STOPONERROR */
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a Lut16 based Lab profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_lut16_Lab.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigOutputClass;
+ wh->colorSpace = icSigRgbData; /* It's and RGBish space */
+ wh->pcs = icSigLabData;
+ wh->renderingIntent = icRelativeColorimetric; /* For want of something */
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test Lut style Lab Output Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* 16 bit dev -> pcs lut: */
+ {
+ icmLut *wo;
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigAToB1Tag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256; /* I'm not going to use the output Lut */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it is not used here. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigRgbData, /* Input color space */
+ icSigLabData, /* Output color space */
+ RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */
+ NULL, NULL, /* Use default Maximum range of RGB' values */
+ RGBp_Labp, /* RGB' -> Lab' transfer function */
+ NULL, NULL, /* Use default Maximum range of Lab' values */
+ Labp_Lab /* Linear output transform Lab'->Lab */
+ ) != 0)
+ error("Setting 16 bit RGB->Lab Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 bit dev -> pcs lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB0Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 dev -> pcs bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB2Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 bit pcs -> dev lut: */
+ {
+ icmLut *wo;
+ double rgbmin[3] = {0.0, 0.0, 0.0}; /* RGB' range */
+ double rgbmax[3] = {1.0, 1.0, 1.0};
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigBToA1Tag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256; /* Not using this for Lab test */
+ wo->outputEnt = 4096;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it is not used here. */
+
+
+ /* REVLUTSCALE1 could be used here, but in this case it hardly */
+ /* makes any difference. */
+
+#ifdef REVLUTSCALE2
+ {
+ /* In any any real profile, you will probably be providing a clut */
+ /* function that carefully maps out of gamut PCS values to in-gamut */
+ /* device values, so the scaling done here won't be appropriate. */
+ /* */
+ /* For this regresion test, we are interested in maximizing accuracy */
+ /* over the known gamut of the device. */
+ /* By setting the min/max to a larger range than will actually be */
+ /* used, we can make sure that the extreme table values of the */
+ /* clut are not actually used, and therefore we won't see the */
+ /* rounding effects of these extreme values being clipped to */
+ /* by the numerical limits of the ICC representation. */
+ /* Instead the extreme values will be clipped by the the higher */
+ /* higher resolution output table. */
+ /* */
+ /* This all assumes that the multi-d reverse transform we are trying */
+ /* to represent in the profile extrapolates beyond the legal device */
+ /* value range. */
+ /* */
+ /* The scaling was chosen by experiment to make sure that the full */
+ /* gamut is surrounded by one row of extrapolated, unclipped clut */
+ /* table entries. */
+
+ int i;
+ for (i = 0; i < 3; i++) {
+ rgbmin[i] = -0.1667; /* Magic numbers */
+ rgbmax[i] = 1.1667;
+ }
+ }
+#endif
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigLabData, /* Input color space */
+ icSigRgbData, /* Output color space */
+ Lab_Labp, /* Linear input transform Lab->Lab' */
+ NULL, NULL, /* Use default Lab' range */
+ Labp_RGBp, /* Lab' -> RGB' transfer function */
+ rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */
+ RGBp_RGB) != 0) /* Output transfer function, RGB'->RGB (NULL = deflt) */
+ error("Setting 16 bit Lab->RGB Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ }
+ /* 16 bit pcs -> dev lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA0Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 16 pcs -> dev bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA2Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* 16 bit pcs -> gamut lut: */
+ {
+ icmLut *wo;
+
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigGamutTag, icSigLut16Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 1;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it can't be used here. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigLabData, /* Input color space */
+ icSigGrayData, /* Output color space */
+ Lab_Labp, /* Linear input transform Lab->Lab' */
+ NULL, NULL , /* Default Lab' range */
+ Labp_BDIST, /* Lab' -> Boundary Distance transfer function */
+ NULL, NULL, /* Default range from clut to output table */
+ BDIST_GAMMUT /* Boundary Distance -> Out of gamut distance */
+ ) != 0)
+ error("Setting 16 bit Lab->Gammut Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the Lut Lab 16bit style profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ RGB_Lab(NULL, check,in);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 1.0)
+#ifdef STOPONERROR
+ error ("Excessive error in Lab16 Lut Fwd %f",mxd);
+#else
+ warning ("Excessive error in Lab16 Lut Fwd %f",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab16 fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab */
+ RGB_Lab(NULL, check,in);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.02)
+#ifdef STOPONERROR
+ error ("Excessive error in Lab16 Lut Bwd %f > 0.02",mxd);
+#else
+ warning ("Excessive error in Lab16 Lut Bwd %f > 0.02",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab16 bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aRGB_Lab(NULL, check,in);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 1.0)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Lab16 Lut Fwd %f > 1.0",mxd);
+#else
+ warning ("Excessive error in Abs Lab16 Lut Fwd %f > 1.0",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab16 fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aRGB_Lab(NULL, check,in);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.02)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Lab16 Lut Bwd %f > 0.02",mxd);
+#else
+ warning ("Excessive error in Abs Lab16 Lut Bwd %f > 0.02",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab16 bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Lab gamut function */
+ {
+ int ino,ono,iok,ook;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmGamut, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ ino = ono = iok = ook = 0;
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = (co[0]/(TRES-1.0)) * 100.0; /* L */
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = ((co[1]/(TRES-1.0)) - 0.5) * 256.0; /* a */
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ int outgamut;
+ in[2] = ((co[2]/(TRES-1.0)) - 0.5) * 256.0; /* b */
+
+ /* Do gamut lookup of Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Do reference conversion of Lab -> RGB */
+ Lab_RGB(NULL, check,in);
+
+ /* Check the result */
+ outgamut = 1; /* assume on edge */
+ if (check[0] < -0.01 || check[0] > 1.01
+ || check[1] < -0.01 || check[1] > 1.01
+ || check[2] < -0.01 || check[2] > 1.01)
+ outgamut = 2; /* Definitely out of gamut */
+ if (check[0] > 0.01 && check[0] < 0.99
+ && check[1] > 0.01 && check[1] < 0.99
+ && check[2] > 0.01 && check[2] < 0.99)
+ outgamut = 0; /* Definitely in gamut */
+
+ /* Keep record of agree/disagree */
+ if (outgamut <= 1) {
+ ino++;
+ if (out[0] <= 0.01)
+ iok++;
+ } else {
+ ono++;
+ if (out[0] > 0.01)
+ ook++;
+ }
+ }
+ }
+ }
+ printf("Lut Lab16 gamut check inside correct = %f%%\n",100.0 * iok/ino);
+ printf("Lut Lab16 gamut check outside correct = %f%%\n",100.0 * ook/ono);
+ printf("Lut Lab16 gamut check total correct = %f%%\n",100.0 * (iok+ook)/(ino+ono));
+ if (((double)iok/ino) < 0.98 || ((double)ook/ono) < 0.98)
+#ifdef STOPONERROR
+ error ("Gamut Lab16 lookup has excessive error");
+#else
+ warning ("Gamut Lab16 lookup has excessive error");
+#endif /* STOPONERROR */
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+ /* Create a Lut8 based Lab profile to test */
+ /* ---------------------------------------- */
+
+ /* Open up the file for writing */
+ file_name = "xxxx_lut8_Lab.icm";
+ if ((wr_fp = new_icmFileStd_name(file_name,"w")) == NULL)
+ error ("Write: Can't open file '%s'",file_name);
+
+ if ((wr_icco = new_icc()) == NULL)
+ error ("Write: Creation of ICC object failed");
+
+ /* Add all the tags required */
+
+ /* The header: */
+ {
+ icmHeader *wh = wr_icco->header;
+
+ /* Values that must be set before writing */
+ wh->deviceClass = icSigOutputClass;
+ wh->colorSpace = icSigRgbData; /* It's and RGBish space */
+ wh->pcs = icSigLabData;
+ wh->renderingIntent = icRelativeColorimetric; /* For want of something */
+
+ /* Values that should be set before writing */
+ wh->manufacturer = str2tag("tst2");
+ wh->model = str2tag("test");
+ }
+ /* Profile Description Tag: */
+ {
+ icmTextDescription *wo;
+ char *dst = "This is a test Lut style Lab Output Profile";
+ if ((wo = (icmTextDescription *)wr_icco->add_tag(
+ wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Copyright Tag: */
+ {
+ icmText *wo;
+ char *crt = "Copyright 1998 Graeme Gill";
+ if ((wo = (icmText *)wr_icco->add_tag(
+ wr_icco, icSigCopyrightTag, icSigTextType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->data, crt); /* Copy the text in */
+ }
+ /* White Point Tag: */
+ {
+ icmXYZArray *wo;
+ /* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = ABS_X; /* Set some silly numbers */
+ wo->data[0].Y = ABS_Y;
+ wo->data[0].Z = ABS_Z;
+ }
+ /* Black Point Tag: */
+ {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)wr_icco->add_tag(
+ wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->size = 1;
+ wo->allocate((icmBase *)wo); /* Allocate space */
+ wo->data[0].X = 0.02; /* Doesn't take part in Absolute anymore */
+ wo->data[0].Y = 0.04;
+ wo->data[0].Z = 0.03;
+ }
+ /* 8 bit dev -> pcs lut: */
+ {
+ icmLut *wo;
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigAToB1Tag, icSigLut8Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it is not used here. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigRgbData, /* Input color space */
+ icSigLabData, /* Output color space */
+ RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */
+ NULL, NULL, /* Use default Maximum range of RGB' values */
+ RGBp_Labp, /* RGB' -> Lab' transfer function */
+ NULL, NULL, /* Use default Maximum range of Lab' values */
+ Labp_Lab /* Linear output transform Lab'->Lab */
+ ) != 0)
+ error("Setting 8 bit RGB->Lab Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 8 bit dev -> pcs lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB0Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 8 dev -> pcs bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigAToB2Tag, icSigAToB1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 8 bit pcs -> dev lut: */
+ {
+ icmLut *wo;
+ double rgbmin[3] = {0.0, 0.0, 0.0}; /* RGB' range */
+ double rgbmax[3] = {1.0, 1.0, 1.0};
+
+ /* Intent 1 = relative colorimetric */
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigBToA1Tag, icSigLut8Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 3;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it is not used here. */
+
+
+ /* REVLUTSCALE1 could be used here, but in this case it hardly */
+ /* makes any difference. */
+
+#ifdef REVLUTSCALE2
+ {
+ /* In any any real profile, you will probably be providing a clut */
+ /* function that carefully maps out of gamut PCS values to in-gamut */
+ /* device values, so the scaling done here won't be appropriate. */
+ /* */
+ /* For this regresion test, we are interested in maximizing accuracy */
+ /* over the known gamut of the device. */
+ /* By setting the min/max to a larger range than will actually be */
+ /* used, we can make sure that the extreme table values of the */
+ /* clut are not actually used, and therefore we won't see the */
+ /* rounding effects of these extreme values being clipped to */
+ /* by the numerical limits of the ICC representation. */
+ /* Instead the extreme values will be clipped by the the higher */
+ /* higher resolution output table. */
+ /* */
+ /* This all assumes that the multi-d reverse transform we are trying */
+ /* to represent in the profile extrapolates beyond the legal device */
+ /* value range. */
+ /* */
+ /* The scaling was chosen by experiment to make sure that the full */
+ /* gamut is surrounded by one row of extrapolated, unclipped clut */
+ /* table entries. */
+
+ int i;
+ for (i = 0; i < 3; i++) {
+ rgbmin[i] = -0.1667; /* Magic numbers */
+ rgbmax[i] = 1.1667;
+ }
+ }
+#endif
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigLabData, /* Input color space */
+ icSigRgbData, /* Output color space */
+ Lab_Labp, /* Linear input transform Lab->Lab' */
+ NULL, NULL, /* Use default Lab' range */
+ Labp_RGBp, /* Lab' -> RGB' transfer function */
+ rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */
+ RGBp_RGB) != 0) /* Output transfer function, RGB'->RGB (NULL = deflt) */
+ error("Setting 8 bit Lab->RGB Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ }
+ /* 8 bit pcs -> dev lut - link intent 0 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 0 = perceptual */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA0Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+ /* 8 pcs -> dev bit lut - link intent 2 to intent 1 */
+ {
+ icmLut *wo;
+ /* Intent 2 = saturation */
+ if ((wo = (icmLut *)wr_icco->link_tag(
+ wr_icco, icSigBToA2Tag, icSigBToA1Tag)) == NULL)
+ error("link_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* 8 bit pcs -> gamut lut: */
+ {
+ icmLut *wo;
+
+ if ((wo = (icmLut *)wr_icco->add_tag(
+ wr_icco, icSigGamutTag, icSigLut8Type)) == NULL)
+ error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);
+
+ wo->inputChan = 3;
+ wo->outputChan = 1;
+ wo->clutPoints = 33;
+ wo->inputEnt = 256;
+ wo->outputEnt = 256;
+ wo->allocate((icmBase *)wo);/* Allocate space */
+
+ /* The matrix is only applicable to XYZ input space, */
+ /* so it can't be used here. */
+
+ /* Use helper function to do the hard work. */
+ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL,
+ icSigLabData, /* Input color space */
+ icSigGrayData, /* Output color space */
+ Lab_Labp, /* Linear input transform Lab->Lab' */
+ NULL, NULL , /* Default Lab' range */
+ Labp_BDIST, /* Lab' -> Boundary Distance transfer function */
+ NULL, NULL, /* Default range from clut to output table */
+ BDIST_GAMMUT /* Boundary Distance -> Out of gamut distance */
+ ) != 0)
+ error("Setting 16 bit Lab->Gammut Lut failed: %d, %s",wr_icco->errc,wr_icco->err);
+ }
+
+ /* Write the file out */
+ if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
+ error ("Write file: %d, %s",rv,wr_icco->err);
+
+ wr_icco->del(wr_icco);
+ wr_fp->del(wr_fp);
+
+ /* - - - - - - - - - - - - - - */
+ /* Deal with reading and verifying the Lut Lab 8bit style profile */
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(file_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",file_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Check the Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ RGB_Lab(NULL, check,in);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 2.1)
+#ifdef STOPONERROR
+ error ("Excessive error in Lab8 Lut Fwd %f > 2.1",mxd);
+#else
+ warning ("Excessive error in Lab8 Lut Fwd %f > 2.1",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab8 fwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icRelativeColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab */
+ RGB_Lab(NULL, check,in);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.03)
+#ifdef STOPONERROR
+ error ("Excessive error in Lab8 Lut Bwd %f > 0.03",mxd);
+#else
+ warning ("Excessive error in Lab8 Lut Bwd %f > 0.03",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab8 bwd default intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aRGB_Lab(NULL, check,in);
+
+ /* Do lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(out, check);
+ if (mxd > 2.3)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Lab8 Lut Fwd %f > 2.3",mxd);
+#else
+ warning ("Excessive error in Abs Lab8 Lut Fwd %f > 2.3",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab8 fwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Absolute reverse Lut lookup function */
+ {
+ double merr = 0.0;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icAbsoluteColorimetric,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = co[0]/(TRES-1.0);
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = co[1]/(TRES-1.0);
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ double mxd;
+ in[2] = co[2]/(TRES-1.0);
+
+ /* Do reference conversion of device -> Lab transform */
+ aRGB_Lab(NULL, check,in);
+
+ /* Do reverse lookup of device -> Lab transform */
+ if ((rv = luo->lookup(luo, out, check)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Check the result */
+ mxd = maxdiff(in, out);
+ if (mxd > 0.03)
+#ifdef STOPONERROR
+ error ("Excessive error in Abs Lab8 Lut Bwd %f > 0.03",mxd);
+#else
+ warning ("Excessive error in Abs Lab8 Lut Bwd %f > 0.03",mxd);
+#endif /* STOPONERROR */
+ if (mxd > merr)
+ merr = mxd;
+ }
+ }
+ }
+ printf("Lut Lab8 bwd absolute intent check complete, peak error = %f\n",merr);
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ /* Check the Lab gamut function */
+ {
+ int ino,ono,iok,ook;
+ icmLuBase *luo;
+
+ /* Get a fwd conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmGamut, icmDefaultIntent,
+ icmSigDefaultData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+
+ ino = ono = iok = ook = 0;
+ for (co[0] = 0; co[0] < TRES; co[0]++) {
+ in[0] = (co[0]/(TRES-1.0)) * 100.0; /* L */
+ for (co[1] = 0; co[1] < TRES; co[1]++) {
+ in[1] = ((co[1]/(TRES-1.0)) - 0.5) * 256.0; /* a */
+ for (co[2] = 0; co[2] < TRES; co[2]++) {
+ int outgamut;
+ in[2] = ((co[2]/(TRES-1.0)) - 0.5) * 256.0; /* b */
+
+ /* Do gamut lookup of Lab transform */
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+ /* Do reference conversion of Lab -> RGB */
+ Lab_RGB(NULL, check,in);
+
+ /* Check the result */
+ outgamut = 1; /* assume on edge */
+ if (check[0] < -0.01 || check[0] > 1.01
+ || check[1] < -0.01 || check[1] > 1.01
+ || check[2] < -0.01 || check[2] > 1.01)
+ outgamut = 2; /* Definitely out of gamut */
+ if (check[0] > 0.01 && check[0] < 0.99
+ && check[1] > 0.01 && check[1] < 0.99
+ && check[2] > 0.01 && check[2] < 0.99)
+ outgamut = 0; /* Definitely in gamut */
+
+ /* Keep record of agree/disagree */
+ if (outgamut <= 1) {
+ ino++;
+ if (out[0] <= 0.01)
+ iok++;
+ } else {
+ ono++;
+ if (out[0] > 0.01)
+ ook++;
+ }
+ }
+ }
+ }
+ printf("Lut Lab8 gamut check inside correct = %f%%\n",100.0 * iok/ino);
+ printf("Lut Lab8 gamut check outside correct = %f%%\n",100.0 * ook/ono);
+ printf("Lut Lab8 gamut check total correct = %f%%\n",100.0 * (iok+ook)/(ino+ono));
+ if (((double)iok/ino) < 0.98 || ((double)ook/ono) < 0.98)
+#ifdef STOPONERROR
+ error ("Gamut Lab8 lookup has excessive error");
+#else
+ warning ("Gamut Lab8 lookup has excessive error");
+#endif /* STOPONERROR */
+
+ /* Done with lookup object */
+ luo->del(luo);
+ }
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ /* ---------------------------------------- */
+
+ printf("Lookup test completed OK\n");
+ return 0;
+}
+
+/* ------------------------------------------------ */
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"lutest: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"lutest: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
diff --git a/icc/makezip.ksh b/icc/makezip.ksh
new file mode 100644
index 0000000..f06efa4
--- /dev/null
+++ b/icc/makezip.ksh
@@ -0,0 +1,6 @@
+# Create icclib source distribution
+#jam
+#zip nt_iccdump.zip iccdump.exe
+#zip nt_icclu.zip icclu.exe
+rm icclib.zip
+zip -9 -ll icclib.zip Readme.txt License.txt todo.txt log.txt Jamfile Makefile Makefile.WNT Makefile.IBMNT Makefile.UNIX Makefile.OSX icc.c iccstd.c icc.h iccV42.h iccdump.c icclu.c iccrw.c icctest.c lutest.c
diff --git a/icc/mcheck.c b/icc/mcheck.c
new file mode 100644
index 0000000..9000650
--- /dev/null
+++ b/icc/mcheck.c
@@ -0,0 +1,541 @@
+
+/*
+ * International Color Consortium Format Library (icclib)
+ * Check the device chanel to PCS monotonicity.
+ *
+ * Author: Graeme W. Gill
+ * Date: 2000/12/11
+ * Version: 2.15
+ *
+ * Copyright 2000 - 2012 Graeme W. Gill
+ *
+ * This material is licensed with an "MIT" free use license:-
+ * see the License.txt file in this directory for licensing details.
+ */
+
+/* TTBD:
+ *
+ * Make general device input, not just CMYK
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include "icc.h"
+
+void error(char *fmt, ...), warning(char *fmt, ...);
+
+void usage(void) {
+ fprintf(stderr,"Check device to PCS monotonicity of a CMYK ICC file, V%s\n",ICCLIB_VERSION_STR);
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: kcheck [-v] [-w] infile\n");
+ fprintf(stderr," -v verbose\n");
+ fprintf(stderr," -c Check just Cyan monotonicity\n");
+ fprintf(stderr," -m Check just Magenta monotonicity\n");
+ fprintf(stderr," -y Check just Yellow monotonicity\n");
+ fprintf(stderr," -k Check just Black monotonicity\n");
+ fprintf(stderr," -w create VRML visualisation\n");
+ exit(1);
+}
+
+#define MGR 50 /* Maximum grid resolution handled */
+
+
+FILE *start_vrml(char *name, int doaxes);
+void start_line_set(FILE *wrl);
+void add_vertex(FILE *wrl, double pp[3]);
+void make_lines(FILE *wrl, int ppset);
+void end_vrml(FILE *wrl);
+
+int
+main(
+ int argc,
+ char *argv[]
+) {
+ int fa,nfa; /* argument we're looking at */
+ int verb = 0;
+ int cchan = -1; /* default all */
+ int dovrml = 0;
+ int doaxes = 0;
+ char in_name[500];
+ char out_name[500], *xl;
+ icmFile *rd_fp;
+ icc *wr_icco, *rd_icco; /* Keep object separate */
+ int rv = 0;
+
+ /* Check variables */
+ icmLuBase *luo;
+ icmLuLut *luluto; /* Lookup xLut type object */
+ int gres; /* Grid resolution */
+ icColorSpaceSignature ins, outs; /* Type of input and output spaces */
+ int inn; /* Number of input chanels */
+ icmLuAlgType alg;
+ FILE *wrl;
+ int dx[4]; /* Device index mapping */
+ int chan, cs, ce;
+
+ if (argc < 2)
+ usage();
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ /* Verbosity */
+ if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ verb = 1;
+ }
+ /* VRML */
+ else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') {
+ dovrml = 1;
+ }
+ /* Cyan */
+ else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
+ cchan = 0;
+ }
+ /* Magenta */
+ else if (argv[fa][1] == 'm' || argv[fa][1] == 'M') {
+ cchan = 1;
+ }
+ /* Yellow */
+ else if (argv[fa][1] == 'y' || argv[fa][1] == 'Y') {
+ cchan = 2;
+ }
+ /* Black */
+ else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
+ cchan = 3;
+ }
+ else if (argv[fa][1] == '?')
+ usage();
+ else
+ usage();
+ }
+ else
+ break;
+ }
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(in_name,argv[fa]);
+
+ strcpy(out_name, in_name);
+ if ((xl = strrchr(out_name, '.')) == NULL) /* Figure where extention is */
+ xl = out_name + strlen(out_name);
+ strcpy(xl,".wrl");
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(in_name,"r")) == NULL)
+ error ("Read: Can't open file '%s'",in_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error ("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
+ error ("Read: %d, %s",rv,rd_icco->err);
+
+ /* Get a Device to PCS conversion object */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icRelativeColorimetric, icSigLabData, icmLuOrdNorm)) == NULL) {
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm)) == NULL)
+ error ("%d, %s",rd_icco->errc, rd_icco->err);
+ }
+ /* Get details of conversion */
+ luo->spaces(luo, &ins, &inn, &outs, NULL, &alg, NULL, NULL, NULL);
+
+ if (alg != icmLutType) {
+ error("Expecting Lut based profile");
+ }
+
+ if (ins != icSigCmykData) {
+ error("Expecting CMYK device");
+ }
+
+ if (outs != icSigLabData) {
+ error("Expecting Lab PCS");
+ }
+
+ luluto = (icmLuLut *)luo; /* Lookup xLut type object */
+
+ gres = luluto->lut->clutPoints;
+ if (gres > MGR) {
+ error("Can't handle grid resolution greater than %d\n",MGR);
+ }
+
+ if (dovrml) {
+ wrl = start_vrml(out_name, doaxes);
+ start_line_set(wrl);
+ }
+
+ /* For all the device chanels chosen */
+ if (cchan < 0) {
+ cs = 0;
+ ce = inn;
+ } else {
+ cs = cchan;
+ ce = cs + 1;
+ }
+ for (chan = cs; chan < ce; chan++) {
+
+ /* Check the monotonicity of the output for a given device input */
+ int co[4];
+ if (chan == 0) {
+ dx[0] = 1;
+ dx[1] = 2;
+ dx[2] = 3;
+ dx[3] = 0; /* Cyan is variable */
+ } else if (chan == 1) {
+ dx[0] = 0;
+ dx[1] = 2;
+ dx[2] = 3;
+ dx[3] = 1; /* Magenta is variable */
+ } else if (chan == 2) {
+ dx[0] = 0;
+ dx[1] = 1;
+ dx[2] = 3;
+ dx[3] = 2; /* Yellow is variable */
+ } else if (chan == 3) {
+ dx[0] = 0;
+ dx[1] = 1;
+ dx[2] = 2;
+ dx[3] = 3; /* Black is variable */
+ }
+
+ /* Itterate throught the CMY clut grid points */
+ for (co[0] = 0; co[0] < gres; co[0]++) {
+ for (co[1] = 0; co[1] < gres; co[1]++) {
+ for (co[2] = 0; co[2] < gres; co[2]++) {
+ int j, k, ck, nm;
+ double dev[MGR][4];
+ double pcs[MGR][3];
+ double apcs[3], ss;
+
+ /* Run up the variable axis */
+ for (ck = 0; ck < gres; ck++) {
+
+ dev[ck][dx[0]] = co[0]/(gres-1.0);
+ dev[ck][dx[1]] = co[1]/(gres-1.0);
+ dev[ck][dx[2]] = co[2]/(gres-1.0);
+ dev[ck][dx[3]] = ck/(gres-1.0);
+
+ /* Device to PCS */
+ if ((rv = luluto->clut(luluto, pcs[ck], dev[ck])) > 1)
+ error ("%d, %s",rd_icco->errc,rd_icco->err);
+
+// if (dovrml)
+// add_vertex(wrl, pcs[ck]);
+ }
+
+ /* Compute average vector direction */
+ for (ss = 0.0, k = 0; k < 3; k++) {
+ double tt;
+ tt = pcs[gres-1][k] - pcs[0][k];
+ ss += tt * tt;
+ apcs[k] = tt;
+ }
+ for (k = 0; k < 3; k++)
+ apcs[k] /= ss;
+
+ /* Now compute the dot product for each vector, */
+ /* and check for reversals. */
+ j = 0;
+//printf("Checking CMYK %f %f %f %f Lab %f %f %f\n",
+// dev[j][0], dev[j][1], dev[j][2], dev[j][3],
+// pcs[j][0], pcs[j][1], pcs[j][2]);
+ for (nm = 0, j = 1; j < gres; j++) {
+ for (ss = 0.0, k = 0; k < 3; k++) /* Dot product */
+ ss += (pcs[j][k] - pcs[j-1][k]) * apcs[k];
+
+//printf("Checking %f CMYK %f %f %f %f Lab %f %f %f\n",
+// ss, dev[j][0], dev[j][1], dev[j][2], dev[j][3],
+// pcs[j][0], pcs[j][1], pcs[j][2]);
+
+ if (ss <= 0.0) {
+ nm = 1;
+ printf("NonMon %f at CMYK %f %f %f %f Lab %f %f %f\n",
+ ss, dev[j][0], dev[j][1], dev[j][2], dev[j][3],
+ pcs[j][0], pcs[j][1], pcs[j][2]);
+ }
+ }
+//printf("\n");
+
+ /* Display just the non mono threads */
+ if (nm && dovrml) {
+ for (j = 0; j < gres; j++)
+ add_vertex(wrl, pcs[j]);
+ }
+ if (verb) {
+ printf("."); fflush(stdout);
+ }
+ }
+ }
+ }
+ }
+
+ if (dovrml) {
+ make_lines(wrl, gres);
+ end_vrml(wrl);
+ }
+
+ /* Done with lookup object */
+ luo->del(luo);
+
+ rd_icco->del(rd_icco);
+ rd_fp->del(rd_fp);
+
+ return 0;
+}
+
+/* ------------------------------------------------ */
+/* Some simple functions to do basix VRML work */
+
+#define GAMUT_LCENT 50.0
+static int npoints = 0;
+static int paloc = 0;
+static struct { double pp[3]; } *pary;
+
+static void Lab2RGB(double *out, double *in);
+
+FILE *start_vrml(char *name, int doaxes) {
+ FILE *wrl;
+ struct {
+ double x, y, z;
+ double wx, wy, wz;
+ double r, g, b;
+ } axes[5] = {
+ { 0, 0, 50-GAMUT_LCENT, 2, 2, 100, .7, .7, .7 }, /* L axis */
+ { 50, 0, 0-GAMUT_LCENT, 100, 2, 2, 1, 0, 0 }, /* +a (red) axis */
+ { 0, -50, 0-GAMUT_LCENT, 2, 100, 2, 0, 0, 1 }, /* -b (blue) axis */
+ { -50, 0, 0-GAMUT_LCENT, 100, 2, 2, 0, 1, 0 }, /* -a (green) axis */
+ { 0, 50, 0-GAMUT_LCENT, 2, 100, 2, 1, 1, 0 }, /* +b (yellow) axis */
+ };
+ int i;
+
+ if ((wrl = fopen(name,"w")) == NULL)
+ error("Error opening VRML file '%s'\n",name);
+
+ npoints = 0;
+
+ fprintf(wrl,"#VRML V2.0 utf8\n");
+ fprintf(wrl,"\n");
+ fprintf(wrl,"# Created by the Argyll CMS\n");
+ fprintf(wrl,"Transform {\n");
+ fprintf(wrl,"children [\n");
+ fprintf(wrl," NavigationInfo {\n");
+ fprintf(wrl," type \"EXAMINE\" # It's an object we examine\n");
+ fprintf(wrl," } # We'll add our own light\n");
+ fprintf(wrl,"\n");
+ fprintf(wrl," DirectionalLight {\n");
+ fprintf(wrl," direction 0 0 -1 # Light illuminating the scene\n");
+ fprintf(wrl," direction 0 -1 0 # Light illuminating the scene\n");
+ fprintf(wrl," }\n");
+ fprintf(wrl,"\n");
+ fprintf(wrl," Viewpoint {\n");
+ fprintf(wrl," position 0 0 340 # Position we view from\n");
+ fprintf(wrl," }\n");
+ fprintf(wrl,"\n");
+ if (doaxes != 0) {
+ fprintf(wrl,"# Lab axes as boxes:\n");
+ for (i = 0; i < 5; i++) {
+ fprintf(wrl,"Transform { translation %f %f %f\n", axes[i].x, axes[i].y, axes[i].z);
+ fprintf(wrl,"\tchildren [\n");
+ fprintf(wrl,"\t\tShape{\n");
+ fprintf(wrl,"\t\t\tgeometry Box { size %f %f %f }\n",
+ axes[i].wx, axes[i].wy, axes[i].wz);
+ fprintf(wrl,"\t\t\tappearance Appearance { material Material ");
+ fprintf(wrl,"{ diffuseColor %f %f %f} }\n", axes[i].r, axes[i].g, axes[i].b);
+ fprintf(wrl,"\t\t}\n");
+ fprintf(wrl,"\t]\n");
+ fprintf(wrl,"}\n");
+ }
+ fprintf(wrl,"\n");
+ }
+
+ return wrl;
+}
+
+void
+start_line_set(FILE *wrl) {
+
+ fprintf(wrl,"\n");
+ fprintf(wrl,"Shape {\n");
+ fprintf(wrl," geometry IndexedLineSet { \n");
+ fprintf(wrl," coord Coordinate { \n");
+ fprintf(wrl," point [\n");
+}
+
+void add_vertex(FILE *wrl, double pp[3]) {
+
+ fprintf(wrl,"%f %f %f,\n",pp[1], pp[2], pp[0]-GAMUT_LCENT);
+
+ if (paloc < (npoints+1)) {
+ paloc = (paloc + 10) * 2;
+ if (pary == NULL)
+ pary = malloc(paloc * 3 * sizeof(double));
+ else
+ pary = realloc(pary, paloc * 3 * sizeof(double));
+
+ if (pary == NULL)
+ error ("Malloc failed");
+ }
+ pary[npoints].pp[0] = pp[0];
+ pary[npoints].pp[1] = pp[1];
+ pary[npoints].pp[2] = pp[2];
+ npoints++;
+}
+
+
+void make_lines(FILE *wrl, int ppset) {
+ int i, j;
+
+ fprintf(wrl," ]\n");
+ fprintf(wrl," }\n");
+ fprintf(wrl," coordIndex [\n");
+
+ for (i = 0; i < npoints;) {
+ for (j = 0; j < ppset; j++, i++) {
+ fprintf(wrl,"%d, ", i);
+ }
+ fprintf(wrl,"-1,\n");
+ }
+ fprintf(wrl," ]\n");
+
+ /* Color */
+ fprintf(wrl," colorPerVertex TRUE\n");
+ fprintf(wrl," color Color {\n");
+ fprintf(wrl," color [ # RGB colors of each vertex\n");
+
+ for (i = 0; i < npoints; i++) {
+ double rgb[3], Lab[3];
+ Lab[0] = pary[i].pp[0];
+ Lab[1] = pary[i].pp[1];
+ Lab[2] = pary[i].pp[2];
+ Lab2RGB(rgb, Lab);
+ fprintf(wrl," %f %f %f,\n", rgb[0], rgb[1], rgb[2]);
+ }
+ fprintf(wrl," ] \n");
+ fprintf(wrl," }\n");
+ /* End color */
+
+ fprintf(wrl," }\n");
+ fprintf(wrl,"} # end shape\n");
+
+}
+
+void end_vrml(FILE *wrl) {
+
+ fprintf(wrl,"\n");
+ fprintf(wrl," ] # end of children for world\n");
+ fprintf(wrl,"}\n");
+
+ if (fclose(wrl) != 0)
+ error("Error closing VRML file\n");
+}
+
+
+/* Convert a gamut Lab value to an RGB value for display purposes */
+static void
+Lab2RGB(double *out, double *in) {
+ double L = in[0], a = in[1], b = in[2];
+ double x,y,z,fx,fy,fz;
+ double R, G, B;
+
+ /* Scale so that black is visible */
+ L = L * (100 - 40.0)/100.0 + 40.0;
+
+ /* First convert to XYZ using D50 white point */
+ if (L > 8.0) {
+ fy = (L + 16.0)/116.0;
+ y = pow(fy,3.0);
+ } else {
+ y = L/903.2963058;
+ fy = 7.787036979 * y + 16.0/116.0;
+ }
+
+ fx = a/500.0 + fy;
+ if (fx > 24.0/116.0)
+ x = pow(fx,3.0);
+ else
+ x = (fx - 16.0/116.0)/7.787036979;
+
+ fz = fy - b/200.0;
+ if (fz > 24.0/116.0)
+ z = pow(fz,3.0);
+ else
+ z = (fz - 16.0/116.0)/7.787036979;
+
+ x *= 0.9642; /* Multiply by white point, D50 */
+ y *= 1.0;
+ z *= 0.8249;
+
+ /* Now convert to sRGB values */
+ R = x * 3.2410 + y * -1.5374 + z * -0.4986;
+ G = x * -0.9692 + y * 1.8760 + z * 0.0416;
+ B = x * 0.0556 + y * -0.2040 + z * 1.0570;
+
+ if (R < 0.0)
+ R = 0.0;
+ else if (R > 1.0)
+ R = 1.0;
+
+ if (G < 0.0)
+ G = 0.0;
+ else if (G > 1.0)
+ G = 1.0;
+
+ if (B < 0.0)
+ B = 0.0;
+ else if (B > 1.0)
+ B = 1.0;
+
+ R = pow(R, 1.0/2.2);
+ G = pow(G, 1.0/2.2);
+ B = pow(B, 1.0/2.2);
+
+ out[0] = R;
+ out[1] = G;
+ out[2] = B;
+}
+
+
+/* ------------------------------------------------ */
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"icctest: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"icctest: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
diff --git a/icc/sRGB.icm b/icc/sRGB.icm
new file mode 100644
index 0000000..db0355f
--- /dev/null
+++ b/icc/sRGB.icm
Binary files differ
diff --git a/icc/testDE2K.c b/icc/testDE2K.c
new file mode 100644
index 0000000..11a4201
--- /dev/null
+++ b/icc/testDE2K.c
@@ -0,0 +1,226 @@
+
+/* Test the CIE delta E 2000 code */
+
+#include <stdio.h>
+#include <math.h>
+#include "icc.h"
+
+
+/* Reference data */
+
+#define NTESTS 34
+
+/* From the Sharma, Wu and Dalal "Implementation Notes" etc. paper: */
+struct {
+ double Lab1[3];
+ double Lab2[3];
+ double de;
+} ref[NTESTS] = {
+ { 50.0000, 2.6772, -79.7751, 50.0000, 0.0000, -82.7485, 2.0425 },
+ { 50.0000, 3.1571, -77.2803, 50.0000, 0.0000, -82.7485, 2.8615 },
+ { 50.0000, 2.8361, -74.0200, 50.0000, 0.0000, -82.7485, 3.4412 },
+ { 50.0000, -1.3802, -84.2814, 50.0000, 0.0000, -82.7485, 1.0000 },
+ { 50.0000, -1.1848, -84.8006, 50.0000, 0.0000, -82.7485, 1.0000 },
+ { 50.0000, -0.9009, -85.5211, 50.0000, 0.0000, -82.7485, 1.0000 },
+ { 50.0000, 0.0000, 0.0000, 50.0000, -1.0000, 2.0000, 2.3669 },
+ { 50.0000, -1.0000, 2.0000, 50.0000, 0.0000, 0.0000, 2.3669 },
+ { 50.0000, 2.4900, -0.0010, 50.0000, -2.4900, 0.0009, 7.1792 },
+ { 50.0000, 2.4900, -0.0010, 50.0000, -2.4900, 0.0010, 7.1792 },
+ { 50.0000, 2.4900, -0.0010, 50.0000, -2.4900, 0.0011, 7.2195 },
+ { 50.0000, 2.4900, -0.0010, 50.0000, -2.4900, 0.0012, 7.2195 },
+ { 50.0000, -0.0010, 2.4900, 50.0000, 0.0009, -2.4900, 4.8045 },
+ { 50.0000, -0.0010, 2.4900, 50.0000, 0.0010, -2.4900, 4.8045 },
+ { 50.0000, -0.0010, 2.4900, 50.0000, 0.0011, -2.4900, 4.7461 },
+ { 50.0000, 2.5000, 0.0000, 50.0000, 0.0000, -2.5000, 4.3065 },
+ { 50.0000, 2.5000, 0.0000, 73.0000, 25.0000, -18.0000, 27.1492 },
+ { 50.0000, 2.5000, 0.0000, 61.0000, -5.0000, 29.0000, 22.8977 },
+ { 50.0000, 2.5000, 0.0000, 56.0000, -27.0000, -3.0000, 31.9030 },
+ { 50.0000, 2.5000, 0.0000, 58.0000, 24.0000, 15.0000, 19.4535 },
+ { 50.0000, 2.5000, 0.0000, 50.0000, 3.1736, 0.5854, 1.0000 },
+ { 50.0000, 2.5000, 0.0000, 50.0000, 3.2972, 0.0000, 1.0000 },
+ { 50.0000, 2.5000, 0.0000, 50.0000, 1.8634, 0.5757, 1.0000 },
+ { 50.0000, 2.5000, 0.0000, 50.0000, 3.2592, 0.3350, 1.0000 },
+ { 60.2574, -34.0099, 36.2677, 60.4626, -34.1751, 39.4387, 1.2644 },
+ { 63.0109, -31.0961, -5.8663, 62.8187, -29.7946, -4.0864, 1.2630 },
+ { 61.2901, 3.7196, -5.3901, 61.4292, 2.2480, -4.9620, 1.8731 },
+ { 35.0831, -44.1164, 3.7933, 35.0232, -40.0716, 1.5901, 1.8645 },
+ { 22.7233, 20.0904, -46.6940, 23.0331, 14.9730, -42.5619, 2.0373 },
+ { 36.4612, 47.8580, 18.3852, 36.2715, 50.5065, 21.2231, 1.4146 },
+ { 90.8027, -2.0831, 1.4410, 91.1528, -1.6435, 0.0447, 1.4441 },
+ { 90.9257, -0.5406, -0.9208, 88.6381, -0.8985, -0.7239, 1.5381 },
+ { 6.7747, -0.2908, -2.4247, 5.8714, -0.0985, -2.2286, 0.6377 },
+ { 2.0776, 0.0795, -1.1350, 0.9033, -0.0636, -0.5514, 0.9082 }
+};
+
+double icmCIE2K(double *Lab1, double *Lab2);
+
+int main(void) {
+ int rv = 0;
+ int i;
+
+ printf("Starting Test:\n");
+
+#ifdef NEVER
+ for (i = 0; i < NTESTS; i++) {
+ printf("Lab1 = %f %f %f\n", ref[i].Lab1[0], ref[i].Lab1[1], ref[i].Lab1[2]);
+ printf("Lab2 = %f %f %f\n", ref[i].Lab2[0], ref[i].Lab2[1], ref[i].Lab2[2]);
+ printf("de = %f\n",ref[i].de);
+ }
+#endif
+
+ /* Test it all out */
+ for (i = 0; i < NTESTS; i++) {
+ double de;
+
+ de = icmCIE2K(ref[i].Lab1, ref[i].Lab2);
+ if (fabs(de - ref[i].de) > 0.0001) {
+ printf("Error at index %d:\n",i);
+ printf("Lab1 = %f %f %f\n", ref[i].Lab1[0], ref[i].Lab1[1], ref[i].Lab1[2]);
+ printf("Lab2 = %f %f %f\n", ref[i].Lab2[0], ref[i].Lab2[1], ref[i].Lab2[2]);
+ printf("DeltaE is %f, should be %f\n\n",de,ref[i].de);
+ rv = 1;
+ }
+ de = icmCIE2K(ref[i].Lab2, ref[i].Lab1);
+ if (fabs(de - ref[i].de) > 0.0001) {
+ printf("Error at index %d:\n",i);
+ printf("Lab1 = %f %f %f\n", ref[i].Lab2[0], ref[i].Lab2[1], ref[i].Lab2[2]);
+ printf("Lab2 = %f %f %f\n", ref[i].Lab1[0], ref[i].Lab1[1], ref[i].Lab1[2]);
+ printf("DeltaE is %f, should be %f\n\n",de,ref[i].de);
+ rv = 1;
+ }
+ }
+
+ printf("Test Finished\n");
+ return rv;
+}
+
+#ifdef NEVER /* Test implementation in icc.c */
+
+/* From the paper "The CIEDE2000 Color-Difference Formula: Implementation Notes, */
+/* Supplementary Test Data, and Mathematical Observations", by */
+/* Gaurav Sharma, Wencheng Wu and Edul N. Dalal, */
+/* Color Res. Appl., vol. 30, no. 1, pp. 21-30, Feb. 2005. */
+
+/* Return the CIEDE2000 Delta E color difference measure squared, for two Lab values */
+double icmCIE2Ksq(double *Lab0, double *Lab1) {
+ double C1, C2;
+ double h1, h2;
+ double dL, dC, dH;
+ double dsq;
+
+ /* The trucated value of PI is needed to ensure that the */
+ /* test cases pass, as one of them lies on the edge of */
+ /* a mathematical discontinuity. The precision is still */
+ /* enough for any practical use. */
+#define RAD2DEG(xx) (180.0/3.14159265358979 * (xx))
+#define DEG2RAD(xx) (3.14159265358979/180.0 * (xx))
+
+ /* Compute Cromanance and Hue angles */
+ {
+ double C1ab, C2ab;
+ double Cab, Cab7, G;
+ double a1, a2;
+
+ C1ab = sqrt(Lab0[1] * Lab0[1] + Lab0[2] * Lab0[2]);
+ C2ab = sqrt(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]);
+ Cab = 0.5 * (C1ab + C2ab);
+ Cab7 = pow(Cab,7.0);
+ G = 0.5 * (1.0 - sqrt(Cab7/(Cab7 + 6103515625.0)));
+ a1 = (1.0 + G) * Lab0[1];
+ a2 = (1.0 + G) * Lab1[1];
+ C1 = sqrt(a1 * a1 + Lab0[2] * Lab0[2]);
+ C2 = sqrt(a2 * a2 + Lab1[2] * Lab1[2]);
+
+ if (C1 < 1e-9)
+ h1 = 0.0;
+ else {
+ h1 = RAD2DEG(atan2(Lab0[2], a1));
+ if (h1 < 0.0)
+ h1 += 360.0;
+ }
+
+ if (C2 < 1e-9)
+ h2 = 0.0;
+ else {
+ h2 = RAD2DEG(atan2(Lab1[2], a2));
+ if (h2 < 0.0)
+ h2 += 360.0;
+ }
+ }
+
+ /* Compute delta L, C and H */
+ {
+ double dh;
+
+ dL = Lab1[0] - Lab0[0];
+ dC = C2 - C1;
+ if (C1 < 1e-9 || C2 < 1e-9) {
+ dh = 0.0;
+ } else {
+ dh = h2 - h1;
+ if (dh > 180.0)
+ dh -= 360.0;
+ else if (dh < -180.0)
+ dh += 360.0;
+ }
+
+ dH = 2.0 * sqrt(C1 * C2) * sin(DEG2RAD(0.5 * dh));
+ }
+
+ {
+ double L, C, h, T;
+ double hh, ddeg;
+ double C7, RC, L50sq, SL, SC, SH, RT;
+ double dLsq, dCsq, dHsq, RCH;
+
+ L = 0.5 * (Lab0[0] + Lab1[0]);
+ C = 0.5 * (C1 + C2);
+ if (C1 < 1e-9 || C2 < 1e-9) {
+ h = h1 + h2;
+ } else {
+ h = h1 + h2;
+ if (fabs(h1 - h2) > 180.0) {
+ if (h < 360.0)
+ h += 360.0;
+ else if (h >= 360.0)
+ h -= 360.0;
+ }
+ h *= 0.5;
+ }
+ T = 1.0 - 0.17 * cos(DEG2RAD(h-30.0)) + 0.24 * cos(DEG2RAD(2.0 * h))
+ + 0.32 * cos(DEG2RAD(3.0 * h + 6.0)) - 0.2 * cos(DEG2RAD(4.0 * h - 63.0));
+ hh = (h - 275.0)/25.0;
+ ddeg = 30.0 * exp(-hh * hh);
+ C7 = pow(C,7.0);
+ RC = 2.0 * sqrt(C7/(C7 + 6103515625.0));
+ L50sq = (L - 50.0) * (L - 50.0);
+ SL = 1.0 + (0.015 * L50sq)/sqrt(20.0 + L50sq);
+ SC = 1.0 + 0.045 * C;
+ SH = 1.0 + 0.015 * C * T;
+ RT = -sin(DEG2RAD(2 * ddeg)) * RC;
+
+ dLsq = dL/SL;
+ dCsq = dC/SC;
+ dHsq = dH/SH;
+
+ RCH = RT * dCsq * dHsq;
+
+ dLsq *= dLsq;
+ dCsq *= dCsq;
+ dHsq *= dHsq;
+
+ dsq = dLsq + dCsq + dHsq + RCH;
+ }
+
+ return dsq;
+
+#undef RAD2DEG
+#undef DEG2RAD
+}
+
+/* Return the CIE2DE000 Delta E color difference measure for two Lab values */
+double icmCIE2K(double *Lab0, double *Lab1) {
+ return sqrt(icmCIE2Ksq(Lab0, Lab1));
+}
+
+#endif /* NEVER */
diff --git a/icc/todo.txt b/icc/todo.txt
new file mode 100644
index 0000000..bdbaa5e
--- /dev/null
+++ b/icc/todo.txt
@@ -0,0 +1,197 @@
+
+Notes on changes needed for V4.2
+
+--------------------------------------------
+Spec name/version/file version relationship.
+This is more complicated that it really should be:
+
+Spec. Name Spec. Version File Version
+---------- ------------- ------------
+ICC.1:2004-10 4.2 4.2.0 <-- target icclib4
+ICC.1:2003-09 4.1 4.1.0
+ICC.1:2001-12 4.0 4.0.0
+ICC.1:2001-04 (3.7?) 2.4.0
+ICC.1A:1999-04 (3.6?) 2.3.0
+ICC.1:1998-09 (3.5?) 2.2.0
+(Version 3.4) 3.4 2.1.0 (a) <-- current icclib
+(Version 3.3) 3.3 2.1.0
+(Version 3.2) 3.2 2.0.0 (b)
+(Version 3.01) 3.01 2.0.0 (a)
+(Version 3.0) 3.0 2.0.0
+
+-----------------------------
+There are other changes from V2.1 in later versions,
+such as:
+
+ 2.3.0
+ Chromaticity Tag added
+
+ 2.4.0
+ Lut tables now allowed for monochrome
+
+ Added chromaticAdaptationTag
+
+ Input, Display, Output and Colorspace LUT profiles
+ now support all 3 intents.
+
+Can this be made backwards compatible without
+messing about with specific code for other versions ??
+
+This doesn't seem possible. The profile generator code and icclib
+would seem to need to know which version they were generating,
+for assured backwards compatibility with other CMMs.
+-----------------------------
+Should add "guess ink limit" support to ease gamut finding
+from profiles. (Implemented in xicc at the moment) ?
+-----------------------------
+
+Add usage of #def icVersionNumberV41 when "V4" flag is active.
+
+Tags needed since V2.1
+
+ icSigChromaticAdaptationTag = 0x63686164L, /* 'chad' V2.4+ */
+ icSigChromaticityTag = 0x6368726DL, /* 'chrm' V2.3+ */
+ icSigColorantOrderTag = 0x636C726FL, /* 'clro' V4.0+ */
+ icSigColorantTableTag = 0x636C7274L, /* 'clrt' V4.0+ */
+ icSigColorantTableOutTag = 0x636C6F74L, /* 'clot' V4.0+ */
+ icSigDeviceSettingsTag = 0x64657673L, /* 'devs' V2.2 - V4.0 */
+ icSigOutputResponseTag = 0x72657370L, /* 'resp' V2.2+ */
+
+
+Tag types needed since V2.1
+
+ icSigDeviceSettingsType = 0x64657673L, /* 'devs' V2.2 - V4.0 */
+ icSigChromaticityTag = 0x6368726DL, /* 'chrm' V2.3+ */
+ icSigColorantOrderType = 0x636C726FL, /* 'clro' V4.0+ */
+ icSigColorantTableType = 0x636C7274L, /* 'clrt' V4.0+ */
+ icSigLutAtoBType = 0x6d414220L, /* 'mAB ' V4.0+ */
+ icSigLutBtoAType = 0x6d424120L, /* 'mBA ' V4.0+ */
+ icSigMultiLocalizedUnicodeType = 0x6D6C7563L, /* 'mluc' V4.0+ */
+ icSigParametricCurveType = 0x70617261L, /* 'para' V4.0+ */
+ icSigResponseCurveSet16Type = 0x72637332L, /* 'rcs2' V2.2 - V4.0 */
+
+Stuff to deal with in implementing ICCV4:
+-------------------
+
+Make sure DLL/SO version is compilable ??
+
+Add V4 flag in main icc struct (DONE), and some way of setting it before tags
+are added.
+
+Date & time being UTC.
+ - add flag when setting ?
+ - display both when dumping ?
+ - have both in structure when reading ?
+ - or simply not handle this ??
+
+Check all padding is zero during writing - DONE
+
+Implement MD5 generation on profile writing - DONE
+Implement MD5 check method on read profile ?
+Cross check MD5 against external V4 profiles. Cross check with cygwin md5sum!
+
+Colorant table tag needed in V4 for xCLR devices.
+colorantTableTag requirement for xCLR device link profiles.
+
+Add check that tags are unique within a profile ?
+
+Configure conformance to run as V4.2 or V2.4 on writing, and accept V4.X or V2.X on
+reading. Don't try and implement detailed minor version behaviour.
+
+Have some sort of table to embody valid tags and tag types within the two major
+version numbers (2 & 4) ??
+
+Check that non A2B0 and B2A0 intents are allowed for Input, Display & Colorspace
+profiles ??
+
+V4 mandates a chromatic adaptation tag for any profile that requires a white point
+tag & not D50. Add to V4 check, & generate chromatic adaptation tag for V2 & V4
+creation (method ?). Need to carefully read spec. to understand what chromatic
+adaptation tag is meant to represent.
+Implement absolute<->PCS object to represent functionality of chramatic adaptation,
+analogous to conversion object.
+
+
+Check mono profile black and white interpretation.
+
+
+Matrix/shaper in V4 has clipping requirements. Should these be honoured ??
+Maybe make this optional via a run time flag ?
+May be best to have multiple conformance flags (Absolute Intent etc. ?)
+
+Check icclink set header rendering intents correctly for device links.
+
+Implemenmt parametric curve type as extra icmCurveStyle(s), and use icmCurve object to
+do the work.
+
+
+Implement MultiLocalizedUnicodeType as overload of icmTextDescription, or new tagtype ??
+
+
+
+- - - - -
+Implement icSigLutAtoBType and icSigLutBtoAType as overloads of icmLut ??
+If so, add new icmLuAlgType types for AtoB and BtoA conversions (NO - more complicated)
+
+ - OR -
+
+switch to single superset Lut type ?? (YES - best solution) :-
+
+Should a more extensive general model be considered, as a superset of V4 ?? (YES)
+
+ pcs/abs -> Curves -> matrix -> curves -> lut -> curves -> matrix -> curves -> pcs/abs
+
+[ or asymetric ??: (NO)
+
+ pcs->curves->lut->3x3+3matrix->curves->pcs/abs (dev->PCS, PCS->PCS, dev->dev)
+ pcs/abs->curves->3x3+3matrix->curves->lut->curves (PCS->dev) ]
+
+Could use this as universal transform method, even for matrix profiles (don't use old style
+matrix/shaper or LUT at all internaly ?) (YES - need to accomodate V2 & V4 though).
+
+Add new methods to setup the transform stages, with current set mathods implemented
+as backwards compatible ? Have multiple methods rather than a single "set" ?
+( - how are ranges determined in latter case ?)
+
+Finish support for generic 3 stage conversion view (input per chan, cross chan, output per chan.),
+and support this in icclib4.
+
+Use bit flags to indicate which stages are valid.
+Flags effectively replace "algorithm" flag.
+Flag combination set determines how transform can be represented by a profile
+(Mono, Matrix, V2Lut, V4Lut etc.).
+
+Have flags for "separable" (i.e. channel independent) and
+"reversable".
+
+Support implementing a universal transform both forwards and
+backwards from a given set of ICC tags (backwards won't be
+valid for some tags, ie. Lut).
+
+lookup object acts as ideal abstract above exact tag representation within
+a profile. Use this model for other aspects of ICC profiles.
+Extend to simplify xicc/icc relationship - make xicc a true inheriting
+class, and add whatever hooks are needed into icclib4 to make this work.
+ICC tags -> luo -> transform
+ICC tags <- luo <- Set transform
+
+What impact does this have on memory usage though ?
+- Makes sure transform is completely independent of
+ tags or profile after setting up, so that tags and/or
+ profile can be released.
+
+- - - - - -
+
+Split icc.c into 3 levels:
+
+ 1 Basic support (I/O, primitives)
+ Toolkit for buiding ICC like file formats, but not
+ specic to the actual ICC file. Can be used to
+ create custom tags, and custom ICC like files (e.g. calibration)
+
+ 2 Specific to ICC format, tags etc.
+
+ 3 ICC color transform objects etc.
+ Allow for inheretance, so that xicc can be implemented
+ much more elegantly.
+