From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- icc/ClayRGB1998.icm | Bin 0 -> 580 bytes icc/Jamfile | 58 + icc/License.txt | 22 + icc/Makefile | 82 + icc/Makefile.IBMNT | 41 + icc/Makefile.OSX | 38 + icc/Makefile.UNIX | 38 + icc/Makefile.WNT | 38 + icc/Makefile.am | 22 + icc/Readme.txt | 150 + icc/afiles | 26 + icc/icc.c | 17838 ++++++++++++++++++++++++++++++++++++++++++++++++++ icc/icc.h | 1992 ++++++ icc/iccV42.h | 555 ++ icc/iccdump.c | 269 + icc/icclu.c | 424 ++ icc/iccrw.c | 311 + icc/iccstd.c | 443 ++ icc/icctest.c | 2393 +++++++ icc/lab2lab.icm | Bin 0 -> 500 bytes icc/log.txt | 173 + icc/lutest.c | 3506 ++++++++++ icc/makezip.ksh | 6 + icc/mcheck.c | 541 ++ icc/sRGB.icm | Bin 0 -> 3212 bytes icc/testDE2K.c | 226 + icc/todo.txt | 197 + 27 files changed, 29389 insertions(+) create mode 100644 icc/ClayRGB1998.icm create mode 100644 icc/Jamfile create mode 100644 icc/License.txt create mode 100644 icc/Makefile create mode 100644 icc/Makefile.IBMNT create mode 100644 icc/Makefile.OSX create mode 100644 icc/Makefile.UNIX create mode 100644 icc/Makefile.WNT create mode 100644 icc/Makefile.am create mode 100644 icc/Readme.txt create mode 100644 icc/afiles create mode 100644 icc/icc.c create mode 100644 icc/icc.h create mode 100644 icc/iccV42.h create mode 100644 icc/iccdump.c create mode 100644 icc/icclu.c create mode 100644 icc/iccrw.c create mode 100644 icc/iccstd.c create mode 100644 icc/icctest.c create mode 100644 icc/lab2lab.icm create mode 100644 icc/log.txt create mode 100644 icc/lutest.c create mode 100644 icc/makezip.ksh create mode 100644 icc/mcheck.c create mode 100644 icc/sRGB.icm create mode 100644 icc/testDE2K.c create mode 100644 icc/todo.txt (limited to 'icc') diff --git a/icc/ClayRGB1998.icm b/icc/ClayRGB1998.icm new file mode 100644 index 0000000..1eedf09 Binary files /dev/null and b/icc/ClayRGB1998.icm 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 + +(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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __sun +#include +#endif +#if defined(__IBMC__) && defined(_M_IX86) +#include +#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; cu.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; cu.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; cu.table.channels; c++) { + op->gprintf(op," channel #%d\n",c); + for (i=0; iu.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 = \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 +#include +#include +#include +#include +#include +#include +#include + +#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 + +#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 */ + 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 */ + +}; 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 +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __sun +#include +#endif +#if defined(__IBMC__) && defined(_M_IX86) +#include +#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 +#include +#include +#include +#include +#ifdef __sun +#include +#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; iu.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; iu.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; iu.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 Binary files /dev/null and b/icc/lab2lab.icm 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 +#include +#include +#include +#include +#include +#include +#if defined(__IBMC__) && defined(_M_IX86) +#include +#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 +#include +#include +#include +#include +#include +#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 Binary files /dev/null and b/icc/sRGB.icm 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 +#include +#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. + -- cgit v1.2.3