summaryrefslogtreecommitdiffstats
path: root/src/libs/lprof
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/lprof')
-rw-r--r--src/libs/lprof/Makefile.am15
-rw-r--r--src/libs/lprof/cmshull.cpp1480
-rw-r--r--src/libs/lprof/cmslm.cpp288
-rw-r--r--src/libs/lprof/cmslnr.cpp560
-rw-r--r--src/libs/lprof/cmsmatn.cpp323
-rw-r--r--src/libs/lprof/cmsmkmsh.cpp346
-rw-r--r--src/libs/lprof/cmsmntr.cpp371
-rw-r--r--src/libs/lprof/cmsoutl.cpp284
-rw-r--r--src/libs/lprof/cmspcoll.cpp1045
-rw-r--r--src/libs/lprof/cmsprf.cpp439
-rw-r--r--src/libs/lprof/cmsreg.cpp558
-rw-r--r--src/libs/lprof/cmsscn.cpp422
-rw-r--r--src/libs/lprof/cmssheet.cpp1746
-rw-r--r--src/libs/lprof/lcmsprf.h485
14 files changed, 8362 insertions, 0 deletions
diff --git a/src/libs/lprof/Makefile.am b/src/libs/lprof/Makefile.am
new file mode 100644
index 00000000..e7251d2c
--- /dev/null
+++ b/src/libs/lprof/Makefile.am
@@ -0,0 +1,15 @@
+# Gilles Caulier 12/01/06: lprof implementation is in C not C++ and do not
+# support 'nofinal' compilation option.
+KDE_OPTIONS = nofinal
+
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = liblprof.la
+
+noinst_HEADERS = lcmsprf.h
+
+liblprof_la_CXXFLAGS = -w -fomit-frame-pointer
+
+liblprof_la_SOURCES = cmshull.cpp cmslm.cpp cmslnr.cpp cmsmatn.cpp \
+ cmsmkmsh.cpp cmsmntr.cpp cmsoutl.cpp cmspcoll.cpp \
+ cmsprf.cpp cmsreg.cpp cmsscn.cpp cmssheet.cpp
diff --git a/src/libs/lprof/cmshull.cpp b/src/libs/lprof/cmshull.cpp
new file mode 100644
index 00000000..05b8dfa3
--- /dev/null
+++ b/src/libs/lprof/cmshull.cpp
@@ -0,0 +1,1480 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.09a */
+
+
+#include "lcmsprf.h"
+
+/* Convex hull management */
+
+LCMSHANDLE cdecl cmsxHullInit(void);
+void cdecl cmsxHullDone(LCMSHANDLE hHull);
+BOOL cdecl cmsxHullAddPoint(LCMSHANDLE hHull, int x, int y, int z);
+BOOL cdecl cmsxHullComputeHull(LCMSHANDLE hHull);
+char cdecl cmsxHullCheckpoint(LCMSHANDLE hHull, int x, int y, int z);
+BOOL cdecl cmsxHullDumpVRML(LCMSHANDLE hHull, const char* fname);
+
+/* --------------------------------------------------------------------- */
+
+
+
+/* This method is described in "Computational Geometry in C" Chapter 4. */
+/* */
+/* -------------------------------------------------------------------- */
+/* This code is Copyright 1998 by Joseph O'Rourke. It may be freely */
+/* redistributed in its entirety provided that this copyright notice is */
+/* not removed. */
+/* -------------------------------------------------------------------- */
+
+#define SWAP(t,x,y) { t = x; x = y; y = t; }
+
+#define XFREE(p)
+/* if (p) { free ((char *) p); p = NULL; } */
+
+
+#define ADD( head, p ) if ( head ) { \
+ p->Next = head; \
+ p->Prev = head->Prev; \
+ head->Prev = p; \
+ p->Prev->Next = p; \
+ } \
+ else { \
+ head = p; \
+ head->Next = head->Prev = p; \
+ }
+
+#define XDELETE( head, p ) if ( head ) { \
+ if ( head == head->Next ) \
+ head = NULL; \
+ else if ( p == head ) \
+ head = head->Next; \
+ p->Next->Prev = p->Prev; \
+ p->Prev->Next = p->Next; \
+ XFREE( p ); \
+ }
+
+/* Define Vertex indices. */
+#define X 0
+#define Y 1
+#define Z 2
+
+/* Define structures for vertices, edges and faces */
+
+typedef struct _vertex_struct VERTEX,FAR *LPVERTEX;
+typedef struct _edge_struct EDGE, FAR *LPEDGE;
+typedef struct _face_struct FACE, FAR *LPFACE;
+
+
+struct _edge_struct {
+
+ LPFACE AdjFace[2];
+ LPVERTEX EndPts[2];
+ LPFACE NewFace; /* pointer to incident cone face. */
+ BOOL DoDelete; /* T iff Edge should be delete. */
+
+ LPEDGE Next, Prev;
+};
+
+struct _face_struct {
+
+ LPEDGE Edge[3];
+ LPVERTEX Vertex[3];
+ BOOL Visible; /* T iff face Visible from new point. */
+
+ LPFACE Next, Prev;
+};
+
+struct _vertex_struct {
+
+ int v[3];
+ int vnum;
+ LPEDGE duplicate; /* pointer to incident cone Edge (or NULL) */
+ BOOL onhull; /* T iff point on hull. */
+ BOOL mark; /* T iff point already processed. */
+
+ LPVERTEX Next, Prev;
+};
+
+/* Define flags */
+
+#define ONHULL true
+#define REMOVED true
+#define VISIBLE true
+#define PROCESSED true
+#define SAFE 1000000 /* Range of safe coord values. */
+
+#define DIM 3 /* Dimension of points */
+typedef int VEC3I[DIM]; /* Type integer point */
+
+#define PMAX 10000 /* Max # of pts */
+
+typedef struct {
+
+ /* Global variable definitions */
+ LPVERTEX vertices;
+ LPEDGE edges;
+ LPFACE faces;
+
+ VEC3I Vertices[PMAX]; /* All the points */
+ VEC3I Faces[PMAX]; /* Each triangle face is 3 indices */
+ VEC3I Box[PMAX][2]; /* Box around each face */
+
+
+ VEC3I bmin, bmax;
+ int radius;
+ int vnumCounter;
+
+ int nfaces;
+ int nvertex;
+
+} HULL, FAR* LPHULL;
+
+/* static HULL Global; */
+
+
+/*---------------------------------------------------------------------
+MakeNullVertex: Makes a Vertex, nulls out fields.
+---------------------------------------------------------------------*/
+
+static
+LPVERTEX MakeNullVertex(LPHULL hull)
+{
+ LPVERTEX v;
+
+ v = (LPVERTEX) malloc(sizeof(VERTEX));
+ if (!v) return NULL;
+
+ v->duplicate = NULL;
+ v->onhull = !ONHULL;
+ v->mark = !PROCESSED;
+ ADD( hull->vertices, v );
+
+ return v;
+}
+
+
+
+/*---------------------------------------------------------------------
+MakeNullEdge creates a new cell and initializes all pointers to NULL
+and sets all flags to off. It returns a pointer to the empty cell.
+---------------------------------------------------------------------*/
+static
+LPEDGE MakeNullEdge(LPHULL hull)
+{
+ LPEDGE e;
+
+ e = (LPEDGE) malloc(sizeof(EDGE));
+ if (!e) return NULL;
+
+ e->AdjFace[0] = e->AdjFace[1] = e->NewFace = NULL;
+ e->EndPts[0] = e->EndPts[1] = NULL;
+ e->DoDelete = !REMOVED;
+ ADD( hull->edges, e );
+ return e;
+}
+
+/*--------------------------------------------------------------------
+MakeNullFace creates a new face structure and initializes all of its
+flags to NULL and sets all the flags to off. It returns a pointer
+to the empty cell.
+---------------------------------------------------------------------*/
+static
+LPFACE MakeNullFace(LPHULL hull)
+{
+ LPFACE f;
+ int i;
+
+ f = (LPFACE) malloc(sizeof(FACE));
+ if (!f) return NULL;
+
+ for ( i=0; i < 3; ++i ) {
+ f->Edge[i] = NULL;
+ f->Vertex[i] = NULL;
+ }
+ f->Visible = !VISIBLE;
+ ADD( hull->faces, f );
+ return f;
+}
+
+
+
+/*---------------------------------------------------------------------
+MakeFace creates a new face structure from three vertices (in ccw
+order). It returns a pointer to the face.
+---------------------------------------------------------------------*/
+static
+LPFACE MakeFace(LPHULL hull, LPVERTEX v0, LPVERTEX v1, LPVERTEX v2, LPFACE fold)
+{
+ LPFACE f;
+ LPEDGE e0, e1, e2;
+
+ /* Create edges of the initial triangle. */
+ if( !fold ) {
+ e0 = MakeNullEdge(hull);
+ e1 = MakeNullEdge(hull);
+ e2 = MakeNullEdge(hull);
+ }
+ else { /* Copy from fold, in reverse order. */
+ e0 = fold->Edge[2];
+ e1 = fold->Edge[1];
+ e2 = fold->Edge[0];
+ }
+ e0->EndPts[0] = v0; e0->EndPts[1] = v1;
+ e1->EndPts[0] = v1; e1->EndPts[1] = v2;
+ e2->EndPts[0] = v2; e2->EndPts[1] = v0;
+
+ /* Create face for triangle. */
+ f = MakeNullFace(hull);
+ f->Edge[0] = e0; f->Edge[1] = e1; f->Edge[2] = e2;
+ f->Vertex[0] = v0; f->Vertex[1] = v1; f->Vertex[2] = v2;
+
+ /* Link edges to face. */
+ e0->AdjFace[0] = e1->AdjFace[0] = e2->AdjFace[0] = f;
+
+ return f;
+}
+
+/*---------------------------------------------------------------------
+Collinear checks to see if the three points given are collinear,
+by checking to see if each element of the cross product is zero.
+---------------------------------------------------------------------*/
+static
+BOOL Collinear( LPVERTEX a, LPVERTEX b, LPVERTEX c )
+{
+ return
+ ( c->v[Z] - a->v[Z] ) * ( b->v[Y] - a->v[Y] ) -
+ ( b->v[Z] - a->v[Z] ) * ( c->v[Y] - a->v[Y] ) == 0
+ && ( b->v[Z] - a->v[Z] ) * ( c->v[X] - a->v[X] ) -
+ ( b->v[X] - a->v[X] ) * ( c->v[Z] - a->v[Z] ) == 0
+ && ( b->v[X] - a->v[X] ) * ( c->v[Y] - a->v[Y] ) -
+ ( b->v[Y] - a->v[Y] ) * ( c->v[X] - a->v[X] ) == 0 ;
+}
+
+/*---------------------------------------------------------------------
+VolumeSign returns the sign of the volume of the tetrahedron determined by f
+and p. VolumeSign is +1 iff p is on the negative side of f,
+where the positive side is determined by the rh-rule. So the volume
+is positive if the ccw normal to f points outside the tetrahedron.
+The final fewer-multiplications form is due to Bob Williamson.
+---------------------------------------------------------------------*/
+int VolumeSign( LPFACE f, LPVERTEX p )
+{
+ double vol;
+ double ax, ay, az, bx, by, bz, cx, cy, cz;
+
+ ax = f->Vertex[0]->v[X] - p->v[X];
+ ay = f->Vertex[0]->v[Y] - p->v[Y];
+ az = f->Vertex[0]->v[Z] - p->v[Z];
+ bx = f->Vertex[1]->v[X] - p->v[X];
+ by = f->Vertex[1]->v[Y] - p->v[Y];
+ bz = f->Vertex[1]->v[Z] - p->v[Z];
+ cx = f->Vertex[2]->v[X] - p->v[X];
+ cy = f->Vertex[2]->v[Y] - p->v[Y];
+ cz = f->Vertex[2]->v[Z] - p->v[Z];
+
+ vol = ax * (by*cz - bz*cy)
+ + ay * (bz*cx - bx*cz)
+ + az * (bx*cy - by*cx);
+
+
+ /* The volume should be an integer. */
+ if ( vol > 0.5 ) return 1;
+ else if ( vol < -0.5 ) return -1;
+ else return 0;
+}
+
+
+
+/*---------------------------------------------------------------------
+CleanEdges runs through the Edge list and cleans up the structure.
+If there is a NewFace then it will put that face in place of the
+Visible face and NULL out NewFace. It also deletes so marked edges.
+---------------------------------------------------------------------*/
+static
+void CleanEdges(LPHULL hull)
+{
+ LPEDGE e; /* Primary index into Edge list. */
+ LPEDGE t; /* Temporary Edge pointer. */
+
+ /* Integrate the NewFace's into the data structure. */
+ /* Check every Edge. */
+
+ e = hull ->edges;
+ do {
+ if ( e->NewFace ) {
+
+ if ( e->AdjFace[0]->Visible )
+ e->AdjFace[0] = e->NewFace;
+ else
+ e->AdjFace[1] = e->NewFace;
+
+ e->NewFace = NULL;
+ }
+
+ e = e->Next;
+
+ } while ( e != hull ->edges );
+
+ /* Delete any edges marked for deletion. */
+ while ( hull ->edges && hull ->edges->DoDelete ) {
+
+ e = hull ->edges;
+
+ XDELETE( hull ->edges, e );
+ }
+
+ e = hull ->edges->Next;
+
+ do {
+ if ( e->DoDelete ) {
+
+ t = e;
+ e = e->Next;
+ XDELETE( hull ->edges, t );
+ }
+ else e = e->Next;
+
+ } while ( e != hull ->edges );
+}
+
+/*---------------------------------------------------------------------
+CleanFaces runs through the face list and deletes any face marked Visible.
+---------------------------------------------------------------------*/
+static
+void CleanFaces(LPHULL hull)
+{
+ LPFACE f; /* Primary pointer into face list. */
+ LPFACE t; /* Temporary pointer, for deleting. */
+
+
+ while ( hull ->faces && hull ->faces->Visible ) {
+
+ f = hull ->faces;
+ XDELETE( hull ->faces, f );
+ }
+
+ f = hull ->faces->Next;
+
+ do {
+ if ( f->Visible ) {
+
+ t = f;
+ f = f->Next;
+ XDELETE( hull ->faces, t );
+ }
+ else f = f->Next;
+
+ } while ( f != hull ->faces );
+}
+
+
+
+/*---------------------------------------------------------------------
+CleanVertices runs through the Vertex list and deletes the
+vertices that are marked as processed but are not incident to any
+undeleted edges.
+---------------------------------------------------------------------*/
+static
+void CleanVertices(LPHULL hull)
+{
+ LPEDGE e;
+ LPVERTEX v, t;
+
+ /* Mark all vertices incident to some undeleted Edge as on the hull. */
+
+ e = hull ->edges;
+ do {
+ e->EndPts[0]->onhull = e->EndPts[1]->onhull = ONHULL;
+ e = e->Next;
+
+ } while (e != hull ->edges);
+
+
+ /* Delete all vertices that have been processed but
+ are not on the hull. */
+
+ while ( hull ->vertices && hull->vertices->mark && !hull ->vertices->onhull ) {
+
+ v = hull ->vertices;
+ XDELETE(hull ->vertices, v );
+ }
+
+
+ v = hull ->vertices->Next;
+ do {
+ if (v->mark && !v->onhull ) {
+ t = v;
+ v = v->Next;
+ XDELETE(hull ->vertices, t )
+ }
+ else
+ v = v->Next;
+
+ } while ( v != hull ->vertices );
+
+
+ /* Reset flags. */
+
+ v = hull ->vertices;
+ do {
+ v->duplicate = NULL;
+ v->onhull = !ONHULL;
+ v = v->Next;
+
+ } while (v != hull->vertices );
+
+}
+
+
+
+
+/*---------------------------------------------------------------------
+MakeCcw puts the vertices in the face structure in counterclock wise
+order. We want to store the vertices in the same
+order as in the Visible face. The third Vertex is always p.
+
+Although no specific ordering of the edges of a face are used
+by the code, the following condition is maintained for each face f:
+one of the two endpoints of f->Edge[i] matches f->Vertex[i].
+But note that this does not imply that f->Edge[i] is between
+f->Vertex[i] and f->Vertex[(i+1)%3]. (Thanks to Bob Williamson.)
+---------------------------------------------------------------------*/
+
+static
+void MakeCcw(LPFACE f, LPEDGE e, LPVERTEX p)
+{
+ LPFACE fv; /* The Visible face adjacent to e */
+ int i; /* Index of e->endpoint[0] in fv. */
+ LPEDGE s; /* Temporary, for swapping */
+
+ if (e->AdjFace[0]->Visible)
+
+ fv = e->AdjFace[0];
+ else
+ fv = e->AdjFace[1];
+
+ /* Set Vertex[0] & [1] of f to have the same orientation
+ as do the corresponding vertices of fv. */
+
+ for ( i=0; fv->Vertex[i] != e->EndPts[0]; ++i )
+ ;
+
+ /* Orient f the same as fv. */
+
+ if ( fv->Vertex[ (i+1) % 3 ] != e->EndPts[1] ) {
+
+ f->Vertex[0] = e->EndPts[1];
+ f->Vertex[1] = e->EndPts[0];
+ }
+ else {
+ f->Vertex[0] = e->EndPts[0];
+ f->Vertex[1] = e->EndPts[1];
+ SWAP( s, f->Edge[1], f->Edge[2] );
+ }
+
+ /* This swap is tricky. e is Edge[0]. Edge[1] is based on endpt[0],
+ Edge[2] on endpt[1]. So if e is oriented "forwards," we
+ need to move Edge[1] to follow [0], because it precedes. */
+
+ f->Vertex[2] = p;
+}
+
+/*---------------------------------------------------------------------
+MakeConeFace makes a new face and two new edges between the
+Edge and the point that are passed to it. It returns a pointer to
+the new face.
+---------------------------------------------------------------------*/
+
+static
+LPFACE MakeConeFace(LPHULL hull, LPEDGE e, LPVERTEX p)
+{
+ LPEDGE new_edge[2];
+ LPFACE new_face;
+ int i, j;
+
+ /* Make two new edges (if don't already exist). */
+
+ for ( i=0; i < 2; ++i )
+ /* If the Edge exists, copy it into new_edge. */
+ if ( !( new_edge[i] = e->EndPts[i]->duplicate) ) {
+
+ /* Otherwise (duplicate is NULL), MakeNullEdge. */
+ new_edge[i] = MakeNullEdge(hull);
+ new_edge[i]->EndPts[0] = e->EndPts[i];
+ new_edge[i]->EndPts[1] = p;
+ e->EndPts[i]->duplicate = new_edge[i];
+ }
+
+ /* Make the new face. */
+ new_face = MakeNullFace(hull);
+ new_face->Edge[0] = e;
+ new_face->Edge[1] = new_edge[0];
+ new_face->Edge[2] = new_edge[1];
+ MakeCcw( new_face, e, p );
+
+ /* Set the adjacent face pointers. */
+ for ( i=0; i < 2; ++i )
+ for ( j=0; j < 2; ++j )
+ /* Only one NULL link should be set to new_face. */
+ if ( !new_edge[i]->AdjFace[j] ) {
+ new_edge[i]->AdjFace[j] = new_face;
+ break;
+ }
+
+ return new_face;
+}
+
+
+/*---------------------------------------------------------------------
+AddOne is passed a Vertex. It first determines all faces Visible from
+that point. If none are Visible then the point is marked as not
+onhull. Next is a loop over edges. If both faces adjacent to an Edge
+are Visible, then the Edge is marked for deletion. If just one of the
+adjacent faces is Visible then a new face is constructed.
+---------------------------------------------------------------------*/
+static
+BOOL AddOne(LPHULL hull, LPVERTEX p)
+{
+ LPFACE f;
+ LPEDGE e, temp;
+ int vol;
+ BOOL vis = false;
+
+
+ /* Mark faces Visible from p. */
+ f = hull -> faces;
+
+ do {
+
+ vol = VolumeSign(f, p);
+
+ if ( vol < 0 ) {
+ f->Visible = VISIBLE;
+ vis = true;
+ }
+
+ f = f->Next;
+
+ } while ( f != hull ->faces );
+
+ /* If no faces are Visible from p, then p is inside the hull. */
+
+ if ( !vis ) {
+
+ p->onhull = !ONHULL;
+ return false;
+ }
+
+ /* Mark edges in interior of Visible region for deletion.
+ Erect a NewFace based on each border Edge. */
+
+ e = hull ->edges;
+
+ do {
+
+ temp = e->Next;
+
+ if ( e->AdjFace[0]->Visible && e->AdjFace[1]->Visible )
+ /* e interior: mark for deletion. */
+ e->DoDelete = REMOVED;
+
+ else
+ if ( e->AdjFace[0]->Visible || e->AdjFace[1]->Visible )
+ /* e border: make a new face. */
+ e->NewFace = MakeConeFace(hull, e, p );
+
+ e = temp;
+
+ } while ( e != hull ->edges );
+
+ return true;
+}
+
+
+/*---------------------------------------------------------------------
+ DoubleTriangle builds the initial double triangle. It first finds 3
+ noncollinear points and makes two faces out of them, in opposite order.
+ It then finds a fourth point that is not coplanar with that face. The
+ vertices are stored in the face structure in counterclockwise order so
+ that the volume between the face and the point is negative. Lastly, the
+ 3 newfaces to the fourth point are constructed and the data structures
+ are cleaned up.
+---------------------------------------------------------------------*/
+
+static
+BOOL DoubleTriangle(LPHULL hull)
+{
+ LPVERTEX v0, v1, v2, v3;
+ LPFACE f0, f1 = NULL;
+ int vol;
+
+ /* Find 3 noncollinear points. */
+ v0 = hull ->vertices;
+ while ( Collinear( v0, v0->Next, v0->Next->Next ) )
+ if ( ( v0 = v0->Next ) == hull->vertices )
+ return false; /* All points are Collinear! */
+
+ v1 = v0->Next;
+ v2 = v1->Next;
+
+ /* Mark the vertices as processed. */
+ v0->mark = PROCESSED;
+ v1->mark = PROCESSED;
+ v2->mark = PROCESSED;
+
+ /* Create the two "twin" faces. */
+ f0 = MakeFace(hull, v0, v1, v2, f1 );
+ f1 = MakeFace(hull, v2, v1, v0, f0 );
+
+ /* Link adjacent face fields. */
+ f0->Edge[0]->AdjFace[1] = f1;
+ f0->Edge[1]->AdjFace[1] = f1;
+ f0->Edge[2]->AdjFace[1] = f1;
+ f1->Edge[0]->AdjFace[1] = f0;
+ f1->Edge[1]->AdjFace[1] = f0;
+ f1->Edge[2]->AdjFace[1] = f0;
+
+ /* Find a fourth, noncoplanar point to form tetrahedron. */
+ v3 = v2->Next;
+ vol = VolumeSign( f0, v3 );
+
+ while ( !vol ) {
+
+ if ( ( v3 = v3->Next ) == v0 )
+ return false; /* All points are coplanar! */
+
+ vol = VolumeSign( f0, v3 );
+ }
+
+ /* Insure that v3 will be the first added. */
+ hull ->vertices = v3;
+ return true;
+}
+
+
+
+/*---------------------------------------------------------------------
+ConstructHull adds the vertices to the hull one at a time. The hull
+vertices are those in the list marked as onhull.
+---------------------------------------------------------------------*/
+static
+void ConstructHull(LPHULL hull)
+{
+ LPVERTEX v, vnext;
+ BOOL changed; /* T if addition changes hull; not used. */
+
+ v = hull->vertices;
+
+ do {
+ vnext = v->Next;
+
+ changed = false;
+
+ if (!v->mark ) {
+
+ v->mark = PROCESSED;
+ changed = AddOne(hull, v );
+
+ CleanEdges(hull);
+ CleanFaces(hull);
+ CleanVertices(hull);
+ }
+
+ v = vnext;
+
+ } while (v != hull->vertices );
+
+}
+
+
+
+/*-------------------------------------------------------------------*/
+
+
+static
+void AddVec( VEC3I q, VEC3I ray )
+{
+ int i;
+
+ for( i = 0; i < DIM; i++ )
+ ray[i] = q[i] + ray[i];
+}
+
+/*---------------------------------------------------------------------
+a - b ==> c.
+---------------------------------------------------------------------*/
+static
+void SubVec( VEC3I a, VEC3I b, VEC3I c )
+{
+ int i;
+
+ for( i = 0; i < DIM; i++ )
+ c[i] = a[i] - b[i];
+}
+
+
+/*---------------------------------------------------------------------
+Returns the dot product of the two input vectors.
+---------------------------------------------------------------------*/
+static
+double Dot( VEC3I a, LPVEC3 b )
+{
+ int i;
+ double sum = 0.0;
+
+ for( i = 0; i < DIM; i++ )
+ sum += a[i] * b->n[i];
+
+ return sum;
+}
+
+/*---------------------------------------------------------------------
+Compute the cross product of (b-a)x(c-a) and place into N.
+---------------------------------------------------------------------*/
+static
+void NormalVec( VEC3I a, VEC3I b, VEC3I c, LPVEC3 N )
+{
+ N->n[X] = ( c[Z] - a[Z] ) * ( b[Y] - a[Y] ) -
+ ( b[Z] - a[Z] ) * ( c[Y] - a[Y] );
+ N->n[Y] = ( b[Z] - a[Z] ) * ( c[X] - a[X] ) -
+ ( b[X] - a[X] ) * ( c[Z] - a[Z] );
+ N->n[Z] = ( b[X] - a[X] ) * ( c[Y] - a[Y] ) -
+ ( b[Y] - a[Y] ) * ( c[X] - a[X] );
+}
+
+
+
+
+static
+int InBox( VEC3I q, VEC3I bmin, VEC3I bmax )
+{
+
+ if( ( bmin[X] <= q[X] ) && ( q[X] <= bmax[X] ) &&
+ ( bmin[Y] <= q[Y] ) && ( q[Y] <= bmax[Y] ) &&
+ ( bmin[Z] <= q[Z] ) && ( q[Z] <= bmax[Z] ) )
+ return true;
+
+ return false;
+}
+
+
+
+/*
+ This function returns a char:
+ '0': the segment [ab] does not intersect (completely misses) the
+ bounding box surrounding the n-th triangle T. It lies
+ strictly to one side of one of the six supporting planes.
+ '?': status unknown: the segment may or may not intersect T.
+*/
+
+static
+char BoxTest(LPHULL hull, int n, VEC3I a, VEC3I b)
+{
+ int i; /* Coordinate index */
+ int w;
+
+ for ( i=0; i < DIM; i++ ) {
+
+ w = hull ->Box[ n ][0][i]; /* min: lower left */
+
+ if ( (a[i] < w) && (b[i] < w) ) return '0';
+
+ w = hull ->Box[ n ][1][i]; /* max: upper right */
+
+ if ( (a[i] > w) && (b[i] > w) ) return '0';
+ }
+
+ return '?';
+}
+
+
+
+/* Return a random ray endpoint */
+
+static
+void RandomRay( VEC3I ray, int radius )
+{
+ double x, y, z, w, t;
+
+ /* Generate a random point on a sphere of radius 1. */
+ /* the sphere is sliced at z, and a random point at angle t
+ generated on the circle of intersection. */
+
+ z = 2.0 * (double) rand() / RAND_MAX - 1.0;
+ t = 2.0 * M_PI * (double) rand() / RAND_MAX;
+ w = sqrt( 1 - z*z );
+ x = w * cos( t );
+ y = w * sin( t );
+
+ ray[X] = (int) ( radius * x );
+ ray[Y] = (int) ( radius * y );
+ ray[Z] = (int) ( radius * z );
+}
+
+
+
+static
+int ComputeBox(LPHULL hull, int F, VEC3I bmin, VEC3I bmax )
+{
+ int i, j;
+ double radius;
+
+ for( i = 0; i < F; i++ )
+ for( j = 0; j < DIM; j++ ) {
+
+ if( hull ->Vertices[i][j] < bmin[j] )
+ bmin[j] = hull ->Vertices[i][j];
+
+ if( hull ->Vertices[i][j] > bmax[j] )
+ bmax[j] = hull ->Vertices[i][j];
+ }
+
+ radius = sqrt( pow( (double)(bmax[X] - bmin[X]), 2.0 ) +
+ pow( (double)(bmax[Y] - bmin[Y]), 2.0 ) +
+ pow( (double)(bmax[Z] - bmin[Z]), 2.0 ) );
+
+ return (int)( radius +1 ) + 1;
+}
+
+
+/*---------------------------------------------------------------------
+Computes N & D and returns index m of largest component.
+---------------------------------------------------------------------*/
+static
+int PlaneCoeff(LPHULL hull, VEC3I T, LPVEC3 N, double *D )
+{
+ int i;
+ double t; /* Temp storage */
+ double biggest = 0.0; /* Largest component of normal vector. */
+ int m = 0; /* Index of largest component. */
+
+ NormalVec(hull ->Vertices[T[0]], hull ->Vertices[T[1]], hull ->Vertices[T[2]], N );
+ *D = Dot( hull ->Vertices[T[0]], N );
+
+ /* Find the largest component of N. */
+ for ( i = 0; i < DIM; i++ ) {
+ t = fabs( N->n[i] );
+ if ( t > biggest ) {
+ biggest = t;
+ m = i;
+ }
+ }
+ return m;
+}
+
+/*---------------------------------------------------------------------
+ 'p': The segment lies wholly within the plane.
+ 'q': The q endpoint is on the plane (but not 'p').
+ 'r': The r endpoint is on the plane (but not 'p').
+ '0': The segment lies strictly to one side or the other of the plane.
+ '1': The segement intersects the plane, and 'p' does not hold.
+---------------------------------------------------------------------*/
+static
+char SegPlaneInt(LPHULL hull, VEC3I T, VEC3I q, VEC3I r, LPVEC3 p, int *m)
+{
+ VEC3 N; double D;
+ VEC3I rq;
+ double num, denom, t;
+ int i;
+
+ *m = PlaneCoeff(hull, T, &N, &D );
+ num = D - Dot( q, &N );
+ SubVec( r, q, rq );
+ denom = Dot( rq, &N );
+
+ if ( denom == 0.0 ) { /* Segment is parallel to plane. */
+ if ( num == 0.0 ) /* q is on plane. */
+ return 'p';
+ else
+ return '0';
+ }
+ else
+ t = num / denom;
+
+ for( i = 0; i < DIM; i++ )
+ p->n[i] = q[i] + t * ( r[i] - q[i] );
+
+ if ( (0.0 < t) && (t < 1.0) )
+ return '1';
+ else if ( num == 0.0 ) /* t == 0 */
+ return 'q';
+ else if ( num == denom ) /* t == 1 */
+ return 'r';
+ else return '0';
+}
+
+
+
+static
+int AreaSign( VEC3I a, VEC3I b, VEC3I c )
+{
+ double area2;
+
+ area2 = ( b[0] - a[0] ) * (double)( c[1] - a[1] ) -
+ ( c[0] - a[0] ) * (double)( b[1] - a[1] );
+
+ /* The area should be an integer. */
+ if ( area2 > 0.5 ) return 1;
+ else if ( area2 < -0.5 ) return -1;
+ else return 0;
+}
+
+
+static
+char InTri2D( VEC3I Tp[3], VEC3I pp )
+{
+ int area0, area1, area2;
+
+ /* compute three AreaSign() values for pp w.r.t. each Edge of the face in 2D */
+ area0 = AreaSign( pp, Tp[0], Tp[1] );
+ area1 = AreaSign( pp, Tp[1], Tp[2] );
+ area2 = AreaSign( pp, Tp[2], Tp[0] );
+
+ if ( (( area0 == 0 ) && ( area1 > 0 ) && ( area2 > 0 )) ||
+ (( area1 == 0 ) && ( area0 > 0 ) && ( area2 > 0 )) ||
+ (( area2 == 0 ) && ( area0 > 0 ) && ( area1 > 0 )) )
+ return 'E';
+
+ if ( (( area0 == 0 ) && ( area1 < 0 ) && ( area2 < 0 )) ||
+ (( area1 == 0 ) && ( area0 < 0 ) && ( area2 < 0 )) ||
+ (( area2 == 0 ) && ( area0 < 0 ) && ( area1 < 0 )))
+ return 'E';
+
+ if ( (( area0 > 0 ) && ( area1 > 0 ) && ( area2 > 0 )) ||
+ (( area0 < 0 ) && ( area1 < 0 ) && ( area2 < 0 )))
+ return 'F';
+
+ if ( ( area0 == 0 ) && ( area1 == 0 ) && ( area2 == 0 ) )
+ return '?'; /* Error in InTriD */
+
+ if ( (( area0 == 0 ) && ( area1 == 0 )) ||
+ (( area0 == 0 ) && ( area2 == 0 )) ||
+ (( area1 == 0 ) && ( area2 == 0 )) )
+ return 'V';
+
+ else
+ return '0';
+}
+
+/* Assumption: p lies in the plane containing T.
+ Returns a char:
+ 'V': the query point p coincides with a Vertex of triangle T.
+ 'E': the query point p is in the relative interior of an Edge of triangle T.
+ 'F': the query point p is in the relative interior of a Face of triangle T.
+ '0': the query point p does not intersect (misses) triangle T.
+*/
+
+static
+char InTri3D(LPHULL hull, VEC3I T, int m, VEC3I p )
+{
+ int i; /* Index for X,Y,Z */
+ int j; /* Index for X,Y */
+ int k; /* Index for triangle Vertex */
+ VEC3I pp; /* projected p */
+ VEC3I Tp[3]; /* projected T: three new vertices */
+
+ /* Project out coordinate m in both p and the triangular face */
+ j = 0;
+ for ( i = 0; i < DIM; i++ ) {
+ if ( i != m ) { /* skip largest coordinate */
+ pp[j] = p[i];
+ for ( k = 0; k < 3; k++ )
+ Tp[k][j] = hull->Vertices[T[k]][i];
+ j++;
+ }
+ }
+ return( InTri2D( Tp, pp ) );
+}
+
+
+
+static
+int VolumeSign2( VEC3I a, VEC3I b, VEC3I c, VEC3I d )
+{
+ double vol;
+ double ax, ay, az, bx, by, bz, cx, cy, cz, dx, dy, dz;
+ double bxdx, bydy, bzdz, cxdx, cydy, czdz;
+
+ ax = a[X];
+ ay = a[Y];
+ az = a[Z];
+ bx = b[X];
+ by = b[Y];
+ bz = b[Z];
+ cx = c[X];
+ cy = c[Y];
+ cz = c[Z];
+ dx = d[X];
+ dy = d[Y];
+ dz = d[Z];
+
+ bxdx=bx-dx;
+ bydy=by-dy;
+ bzdz=bz-dz;
+ cxdx=cx-dx;
+ cydy=cy-dy;
+ czdz=cz-dz;
+ vol = (az-dz) * (bxdx*cydy - bydy*cxdx)
+ + (ay-dy) * (bzdz*cxdx - bxdx*czdz)
+ + (ax-dx) * (bydy*czdz - bzdz*cydy);
+
+
+ /* The volume should be an integer. */
+ if ( vol > 0.5 ) return 1;
+ else if ( vol < -0.5 ) return -1;
+ else return 0;
+}
+
+
+
+
+/*---------------------------------------------------------------------
+The signed volumes of three tetrahedra are computed, determined
+by the segment qr, and each Edge of the triangle.
+Returns a char:
+ 'v': the open segment includes a Vertex of T.
+ 'e': the open segment includes a point in the relative interior of an Edge
+ of T.
+ 'f': the open segment includes a point in the relative interior of a face
+ of T.
+ '0': the open segment does not intersect triangle T.
+---------------------------------------------------------------------*/
+
+static
+char SegTriCross(LPHULL hull, VEC3I T, VEC3I q, VEC3I r )
+{
+ int vol0, vol1, vol2;
+
+ vol0 = VolumeSign2( q, hull->Vertices[ T[0] ], hull->Vertices[ T[1] ], r );
+ vol1 = VolumeSign2( q, hull->Vertices[ T[1] ], hull->Vertices[ T[2] ], r );
+ vol2 = VolumeSign2( q, hull->Vertices[ T[2] ], hull->Vertices[ T[0] ], r );
+
+
+ /* Same sign: segment intersects interior of triangle. */
+ if ( ( ( vol0 > 0 ) && ( vol1 > 0 ) && ( vol2 > 0 ) ) ||
+ ( ( vol0 < 0 ) && ( vol1 < 0 ) && ( vol2 < 0 ) ) )
+ return 'f';
+
+ /* Opposite sign: no intersection between segment and triangle */
+ if ( ( ( vol0 > 0 ) || ( vol1 > 0 ) || ( vol2 > 0 ) ) &&
+ ( ( vol0 < 0 ) || ( vol1 < 0 ) || ( vol2 < 0 ) ) )
+ return '0';
+
+ else if ( ( vol0 == 0 ) && ( vol1 == 0 ) && ( vol2 == 0 ) )
+ return '?'; /* Error 1 in SegTriCross */
+
+ /* Two zeros: segment intersects Vertex. */
+ else if ( ( ( vol0 == 0 ) && ( vol1 == 0 ) ) ||
+ ( ( vol0 == 0 ) && ( vol2 == 0 ) ) ||
+ ( ( vol1 == 0 ) && ( vol2 == 0 ) ) )
+ return 'v';
+
+ /* One zero: segment intersects Edge. */
+ else if ( ( vol0 == 0 ) || ( vol1 == 0 ) || ( vol2 == 0 ) )
+ return 'e';
+
+ else
+ return '?'; /* Error 2 in SegTriCross */
+}
+
+
+
+static
+char SegTriInt(LPHULL hull, VEC3I T, VEC3I q, VEC3I r, LPVEC3 p )
+{
+ int code;
+ int m = -1;
+
+ code = SegPlaneInt(hull, T, q, r, p, &m );
+
+ if ( code == '0') return '0';
+ else if ( code == 'q') return InTri3D(hull, T, m, q );
+ else if ( code == 'r') return InTri3D(hull, T, m, r );
+ else if ( code == 'p') return 'p';
+ else if ( code == '1' ) return SegTriCross(hull, T, q, r );
+ else
+ return code; /* Error */
+}
+
+
+
+
+/*
+ This function returns a char:
+ 'i': the query point a is strictly interior to polyhedron P.
+ 'o': the query point a is strictly exterior to( or outside of) polyhedron P.
+*/
+char InPolyhedron(LPHULL hull, VEC3I q)
+{
+ int F = hull->nfaces;
+ VEC3I Ray; /* Ray endpoint. */
+ VEC3 p; /* Intersection point; not used. */
+ int f, k = 0, crossings = 0;
+ char code = '?';
+
+
+ /* If query point is outside bounding box, finished. */
+ if ( !InBox( q, hull->bmin, hull->bmax ) )
+ return 'o';
+
+ LOOP:
+ while( k++ < F ) {
+
+ crossings = 0;
+
+ RandomRay(Ray, hull->radius );
+
+ AddVec( q, Ray );
+
+
+ for ( f = 0; f < F; f++ ) { /* Begin check each face */
+
+ if ( BoxTest(hull, f, q, Ray ) == '0' ) {
+ code = '0';
+
+ }
+ else code = SegTriInt(hull, hull->Faces[f], q, Ray, &p );
+
+
+ /* If ray is degenerate, then goto outer while to generate another. */
+ if ( code == 'p' || code == 'v' || code == 'e' ) {
+
+ goto LOOP;
+ }
+
+ /* If ray hits face at interior point, increment crossings. */
+ else if ( code == 'f' ) {
+ crossings++;
+
+ }
+
+ /* If query endpoint q sits on a V/E/F, return inside. */
+ else if ( code == 'V' || code == 'E' || code == 'F' )
+ return code; /* 'i'; MM2 */
+
+ /* If ray misses triangle, do nothing. */
+ else if ( code == '0' )
+ ;
+
+ else
+ return '?'; /* Error */
+
+ }
+ /* No degeneracies encountered: ray is generic, so finished. */
+ break;
+
+ } /* End while loop */
+
+
+ /* q strictly interior to polyhedron iff an odd number of crossings. */
+ if( ( crossings % 2 ) == 1 )
+ return 'i';
+
+ else return 'o';
+}
+
+
+/*/ ---------------------------------------------------------------------------------- */
+
+
+
+
+static
+void StoreResults(LPHULL hull)
+{
+
+ int i, w;
+ LPVERTEX v;
+ LPFACE f;
+ int V = 0, F = 0;
+ int j, k;
+
+ /* Vertices */
+
+ v = hull ->vertices;
+ V = 0;
+ do {
+
+ v -> vnum = V;
+ hull ->Vertices[V][X] = v -> v[X];
+ hull ->Vertices[V][Y] = v -> v[Y];
+ hull ->Vertices[V][Z] = v -> v[Z];
+
+ v = v->Next;
+ V++;
+
+ } while ( v != hull ->vertices );
+
+ hull ->nvertex = V;
+
+ /* Faces */
+ f = hull ->faces;
+ F = 0;
+ do {
+
+ hull ->Faces[F][0] = f->Vertex[0]->vnum;
+ hull ->Faces[F][1] = f->Vertex[1]->vnum;
+ hull ->Faces[F][2] = f->Vertex[2]->vnum;
+
+ for ( j=0; j < 3; j++ ) {
+
+ hull ->Box[F][0][j] = hull ->Vertices[ hull ->Faces[F][0] ][j];
+ hull ->Box[F][1][j] = hull ->Vertices[ hull ->Faces[F][0] ][j];
+ }
+
+ /* Check k=1,2 vertices of face. */
+ for ( k=1; k < 3; k++ )
+ for ( j=0; j < 3; j++ ) {
+
+ w = hull ->Vertices[ hull ->Faces[F][k] ][j];
+ if ( w < hull ->Box[F][0][j] ) hull ->Box[F][0][j] = w;
+ if ( w > hull ->Box[F][1][j] ) hull ->Box[F][1][j] = w;
+ }
+
+
+ f = f->Next; F++;
+
+ } while ( f != hull ->faces );
+
+
+ hull ->nfaces = F;
+
+
+ /* Initialize the bounding box */
+ for ( i = 0; i < DIM; i++ )
+ hull ->bmin[i] = hull ->bmax[i] = hull ->Vertices[0][i];
+
+ hull ->radius = ComputeBox(hull, V, hull ->bmin, hull ->bmax );
+
+
+}
+
+
+LCMSHANDLE cmsxHullInit(void)
+{
+ LPHULL hull = (LPHULL) malloc(sizeof(HULL));
+
+ ZeroMemory(hull, sizeof(HULL));
+
+ hull->vnumCounter = 0;
+ hull->vertices = NULL;
+ hull->edges = NULL;
+ hull->faces = NULL;
+ hull->nfaces = 0;
+ hull->nvertex = 0;
+
+ return (LCMSHANDLE) (LPSTR) hull;
+}
+
+
+void cmsxHullDone(LCMSHANDLE hHull)
+{
+ LPHULL hull = (LPHULL) (LPSTR) hHull;
+
+ if (hull)
+ free((LPVOID) hull);
+}
+
+
+BOOL cmsxHullAddPoint(LCMSHANDLE hHull, int x, int y, int z)
+{
+ LPVERTEX v;
+ LPHULL hull = (LPHULL) (LPSTR) hHull;
+
+
+ v = MakeNullVertex(hull);
+ v->v[X] = x;
+ v->v[Y] = y;
+ v->v[Z] = z;
+ v->vnum = hull->vnumCounter++;
+
+ return true;
+}
+
+BOOL cmsxHullComputeHull(LCMSHANDLE hHull)
+{
+
+ LPHULL hull = (LPHULL) (LPSTR) hHull;
+
+ if (!DoubleTriangle(hull)) return false;
+
+ ConstructHull(hull);
+ StoreResults(hull);
+
+ return true;
+}
+
+
+char cmsxHullCheckpoint(LCMSHANDLE hHull, int x, int y, int z)
+{
+ VEC3I q;
+ LPHULL hull = (LPHULL) (LPSTR) hHull;
+
+ q[X] = x; q[Y] = y; q[Z] = z;
+
+ return InPolyhedron(hull, q ) ;
+}
+
+
+BOOL cmsxHullDumpVRML(LCMSHANDLE hHull, const char* fname)
+{
+ FILE* fp;
+ int i;
+ LPHULL hull = (LPHULL) (LPSTR) hHull;
+
+ fp = fopen (fname, "wt");
+ if (fp == NULL)
+ return false;
+
+ fprintf (fp, "#VRML V2.0 utf8\n");
+
+ /* set the viewing orientation and distance */
+ fprintf (fp, "DEF CamTest Group {\n");
+ fprintf (fp, "\tchildren [\n");
+ fprintf (fp, "\t\tDEF Cameras Group {\n");
+ fprintf (fp, "\t\t\tchildren [\n");
+ fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n");
+ fprintf (fp, "\t\t\t\t\tposition 0 0 340\n");
+ fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n");
+ fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n");
+ fprintf (fp, "\t\t\t\t}\n");
+ fprintf (fp, "\t\t\t]\n");
+ fprintf (fp, "\t\t},\n");
+ fprintf (fp, "\t]\n");
+ fprintf (fp, "}\n");
+
+ /* Output the background stuff */
+ fprintf (fp, "Background {\n");
+ fprintf (fp, "\tskyColor [\n");
+ fprintf (fp, "\t\t.5 .5 .5\n");
+ fprintf (fp, "\t]\n");
+ fprintf (fp, "}\n");
+
+ /* Output the shape stuff */
+ fprintf (fp, "Transform {\n");
+ fprintf (fp, "\tscale 8 8 8\n");
+ fprintf (fp, "\tchildren [\n");
+
+ /* Draw the axes as a shape: */
+ fprintf (fp, "\t\tShape {\n");
+ fprintf (fp, "\t\t\tappearance Appearance {\n");
+ fprintf (fp, "\t\t\t\tmaterial Material {\n");
+ fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n");
+ fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n");
+ fprintf (fp, "\t\t\t\t\tshininess 0.8\n");
+ fprintf (fp, "\t\t\t\t}\n");
+ fprintf (fp, "\t\t\t}\n");
+ fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n");
+ fprintf (fp, "\t\t\t\tcoord Coordinate {\n");
+ fprintf (fp, "\t\t\t\t\tpoint [\n");
+ fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n");
+ fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0);
+ fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0);
+ fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0);
+ fprintf (fp, "\t\t\t\t}\n");
+ fprintf (fp, "\t\t\t\tcoordIndex [\n");
+ fprintf (fp, "\t\t\t\t\t0, 1, -1\n");
+ fprintf (fp, "\t\t\t\t\t0, 2, -1\n");
+ fprintf (fp, "\t\t\t\t\t0, 3, -1]\n");
+ fprintf (fp, "\t\t\t}\n");
+ fprintf (fp, "\t\t}\n");
+
+
+ /* Draw the triangles as a shape: */
+ fprintf (fp, "\t\tShape {\n");
+ fprintf (fp, "\t\t\tappearance Appearance {\n");
+ fprintf (fp, "\t\t\t\tmaterial Material {\n");
+ fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n");
+ fprintf (fp, "\t\t\t\t\temissiveColor 0 0 0\n");
+ fprintf (fp, "\t\t\t\t\tshininess 0.8\n");
+ fprintf (fp, "\t\t\t\t}\n");
+ fprintf (fp, "\t\t\t}\n");
+ fprintf (fp, "\t\t\tgeometry IndexedFaceSet {\n");
+ fprintf (fp, "\t\t\t\tsolid false\n");
+
+ /* fill in the points here */
+ fprintf (fp, "\t\t\t\tcoord Coordinate {\n");
+ fprintf (fp, "\t\t\t\t\tpoint [\n");
+
+ for (i = 0; i < hull->nvertex; ++i)
+ {
+ fprintf (fp, "\t\t\t\t\t%g %g %g%c\n",
+ (double) hull->Vertices[i][X], (double) hull->Vertices[i][Y], (double) hull->Vertices[i][Z],
+ i == hull->nvertex-1? ']': ',');
+ }
+ fprintf (fp, "\t\t\t\t}\n");
+
+ /* fill in the Vertex indices (followed by -1) */
+
+
+ fprintf (fp, "\t\t\t\tcoordIndex [\n");
+ for (i = 0; i < hull->nfaces; ++i)
+ {
+ fprintf (fp, "\t\t\t\t\t%d, %d, %d, -1\n",
+ hull->Faces[i][0], hull->Faces[i][1], hull->Faces[i][2]);
+
+ }
+ fprintf (fp, "]\n");
+
+
+ /* fill in the face colors */
+ fprintf (fp, "\t\t\t\tcolor Color {\n");
+ fprintf (fp, "\t\t\t\t\tcolor [\n");
+ for (i = 0; i < hull->nfaces; ++i)
+ {
+ int vx, vy, vz;
+ double r, g, b;
+
+ vx = hull->Faces[i][0]; vy = hull->Faces[i][1]; vz = hull->Faces[i][2];
+ r = (double) (hull->Vertices[vx][X] + hull->Vertices[vy][X] + hull->Vertices[vz][X]) / (3* 255);
+ g = (double) (hull->Vertices[vx][Y] + hull->Vertices[vy][Y] + hull->Vertices[vz][Y]) / (3* 255);
+ b = (double) (hull->Vertices[vx][Z] + hull->Vertices[vy][Z] + hull->Vertices[vz][Z]) / (3* 255);
+
+ fprintf (fp, "\t\t\t\t\t%g %g %g%c\n", r, g, b,
+ i == hull->nfaces-1? ']': ',');
+ }
+ fprintf (fp, "\t\t\t}\n");
+
+ fprintf (fp, "\t\t\tcolorPerVertex false\n");
+
+ fprintf (fp, "\t\t\t}\n");
+ fprintf (fp, "\t\t}\n");
+ fprintf (fp, "\t]\n");
+ fprintf (fp, "}\n");
+
+ fclose (fp);
+
+ return true;
+}
diff --git a/src/libs/lprof/cmslm.cpp b/src/libs/lprof/cmslm.cpp
new file mode 100644
index 00000000..81d86ba6
--- /dev/null
+++ b/src/libs/lprof/cmslm.cpp
@@ -0,0 +1,288 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ma 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.09a */
+
+#include "lcmsprf.h"
+
+
+/* From "numerical recipes in C" */
+/* */
+/* Levenberg-Marquardt method, attempting to reduce the value X2 of a */
+/* fit between a set of data points x[1..ndata], y[1..ndata] with individual */
+/* standard deviations sig[1..ndata], and a nonlinear function dependent */
+/* on ma coefficients a[1..ma]. The input array ia[1..ma] */
+/* indicates by nonzero entries those components of a that should be */
+/* fitted for, and by zero entries those components that should be held */
+/* fixed at their input values. The program returns current best-fitt */
+/* values for the parameters a[1..ma], and chisq. The arrays */
+/* covar[1..ma][1..ma], alpha[1..ma][1..ma] are used as */
+/* working space during most iterations. Supply a routine */
+/* funcs(x, a, yfit, dyda, ma) */
+/* that evaluates the fitting function yfit, and its derivatives dyda[1..ma] */
+/* with respect to the fitting parameters a at x. On the first call provide */
+/* an initial guess for the parameters a, and set alamda<0 for initialization */
+/* (which then sets alamda=.001). If a step succeeds chisq becomes smaller */
+/* and alamda decreases by a factor of 10. If a step fails alamda grows by */
+/* a factor of 10. You must call this routine repeatedly until convergence */
+/* is achieved. Then, make one final call with alamda=0, so that */
+/* covar[1..ma][1..ma] returns the covar matrix, and alpha the */
+/* alpha matrix. (Parameters held fixed will return zero covariances.) */
+
+
+LCMSHANDLE cdecl cmsxLevenbergMarquardtInit(LPSAMPLEDCURVE x, LPSAMPLEDCURVE y, double sig,
+ double a[],
+ int ma,
+ void (*funcs)(double, double[], double*, double[], int)
+ );
+
+double cdecl cmsxLevenbergMarquardtAlamda(LCMSHANDLE hMRQ);
+double cdecl cmsxLevenbergMarquardtChiSq(LCMSHANDLE hMRQ);
+BOOL cdecl cmsxLevenbergMarquardtIterate(LCMSHANDLE hMRQ);
+BOOL cdecl cmsxLevenbergMarquardtFree(LCMSHANDLE hMRQ);
+
+/* ---------------------------------------------------------------------------- */
+
+
+
+typedef struct {
+
+ LPSAMPLEDCURVE x;
+ LPSAMPLEDCURVE y;
+ int ndata;
+ double* a;
+ int ma;
+ LPMATN covar;
+ LPMATN alpha;
+ double* atry;
+ LPMATN beta;
+ LPMATN oneda;
+ double* dyda;
+ double ochisq;
+ double sig;
+
+
+ void (*funcs)(double, double[], double*, double[], int);
+
+ double alamda;
+ double chisq;
+
+} LMRTQMIN, FAR* LPLMRTQMIN;
+
+
+
+
+static
+void mrqcof(LPLMRTQMIN pLM, double *a, LPMATN alpha, LPMATN beta, double *chisq)
+{
+ int i, j, k;
+ double ymod, wt, sig2i, dy;
+
+ for(j = 0; j < pLM->ma; j++)
+ {
+ for(k = 0; k <= j; k++)
+ alpha->Values[j][k] = 0.0;
+
+ beta->Values[j][0] = 0.0;
+ }
+
+ *chisq = 0.0;
+ sig2i = 1.0 / (pLM->sig * pLM->sig);
+
+ for(i = 0; i < pLM->ndata; i++)
+ {
+ (*(pLM->funcs))(pLM->x ->Values[i], a, &ymod, pLM->dyda, pLM->ma);
+
+ dy = pLM->y->Values[i] - ymod;
+
+ for(j = 0; j < pLM->ma; j++)
+ {
+ wt = pLM->dyda[j] * sig2i;
+
+ for(k = 0; k <= j; k++)
+ alpha->Values[j][k] += wt * pLM->dyda[k];
+
+ beta->Values[j][0] += dy * wt;
+ }
+
+ *chisq += dy * dy * sig2i;
+ }
+
+ for(j = 1; j < pLM->ma; j++) /* Fill in the symmetric side. */
+ for(k = 0; k < j; k++)
+ alpha->Values[k][j] = alpha->Values[j][k];
+}
+
+
+
+static
+void FreeStruct(LPLMRTQMIN pLM)
+{
+ if(pLM == NULL) return;
+
+ if(pLM->covar) MATNfree (pLM->covar);
+ if(pLM->alpha) MATNfree (pLM->alpha);
+ if(pLM->atry) free(pLM->atry);
+ if(pLM->beta) MATNfree (pLM->beta);
+ if(pLM->oneda) MATNfree (pLM->oneda);
+ if(pLM->dyda) free(pLM->dyda);
+ free(pLM);
+}
+
+
+
+LCMSHANDLE cmsxLevenbergMarquardtInit(LPSAMPLEDCURVE x, LPSAMPLEDCURVE y, double sig,
+ double a[],
+ int ma,
+ void (*funcs)(double, double[], double*, double[], int))
+
+{
+ int i;
+ LPLMRTQMIN pLM;
+
+ if (x ->nItems != y ->nItems) return NULL;
+
+ pLM = (LPLMRTQMIN) malloc(sizeof(LMRTQMIN));
+ if(!pLM)
+ return NULL;
+
+ ZeroMemory(pLM, sizeof(LMRTQMIN));
+
+ if((pLM->atry = (double*)malloc(ma * sizeof(double))) == NULL) goto failed;
+ if((pLM->beta = MATNalloc (ma, 1)) == NULL) goto failed;
+ if((pLM->oneda = MATNalloc (ma, 1)) == NULL) goto failed;
+
+
+
+ if((pLM->covar = MATNalloc(ma, ma)) == NULL) goto failed;
+ if((pLM->alpha = MATNalloc(ma, ma)) == NULL) goto failed;
+ if((pLM->dyda = (double*)malloc(ma * sizeof(double))) == NULL) goto failed;
+
+ pLM->alamda = 0.001;
+
+ pLM->ndata = x ->nItems;
+ pLM->x = x;
+ pLM->y = y;
+ pLM->ma = ma;
+ pLM->a = a;
+ pLM->funcs = funcs;
+ pLM->sig = sig;
+
+ mrqcof(pLM, a, pLM->alpha, pLM->beta, &pLM->chisq);
+ pLM->ochisq = (pLM->chisq);
+
+ for(i = 0; i < ma; i++) pLM->atry[i] = a[i];
+
+ return (LCMSHANDLE) pLM;
+
+failed:
+ FreeStruct(pLM);
+ return NULL;
+}
+
+
+BOOL cmsxLevenbergMarquardtFree(LCMSHANDLE hMRQ)
+{
+ LPLMRTQMIN pLM = (LPLMRTQMIN)hMRQ;
+ if(!pLM)
+ return false;
+
+ FreeStruct(pLM);
+ return true;
+}
+
+
+BOOL cmsxLevenbergMarquardtIterate(LCMSHANDLE hMRQ)
+{
+ int j, k;
+ BOOL sts;
+ LPLMRTQMIN pLM = (LPLMRTQMIN)hMRQ;
+ if(!pLM)
+ return false;
+
+ for(j = 0; j < pLM->ma; j++) /* Alter linearized fitting matrix, by augmenting diagonal elements. */
+ {
+ for(k = 0; k < pLM->ma; k++)
+ pLM->covar->Values[j][k] = pLM->alpha->Values[j][k];
+
+ pLM->covar->Values[j][j] = pLM->alpha->Values[j][j] * (1.0 + pLM ->alamda);
+ pLM->oneda->Values[j][0] = pLM->beta->Values[j][0];
+ }
+
+ if((sts = MATNsolve (pLM->covar, pLM->oneda)) != true) /* Matrix solution. */
+ return sts;
+
+ for(j = 0; j < pLM->ma; j++) /* Did the trial succeed? */
+ pLM->atry[j] = pLM->a[j] + pLM->oneda->Values[j][0];
+
+ mrqcof(pLM, pLM->atry, pLM->covar, pLM->oneda, &pLM -> chisq);
+
+ if (pLM->chisq < pLM->ochisq) { /* Success, accept the new solution. */
+
+ pLM->alamda *= 0.1;
+ pLM->ochisq = pLM->chisq;
+
+ for(j = 0; j < pLM->ma; j++)
+ {
+ for(k = 0; k < pLM->ma; k++)
+ pLM->alpha->Values[j][k] = pLM->covar->Values[j][k];
+
+ pLM->beta->Values[j][0] = pLM->oneda->Values[j][0];
+ }
+
+ for (j=0; j < pLM ->ma; j++) pLM->a[j] = pLM->atry[j];
+ }
+ else /* Failure, increase alamda and return. */
+ {
+ pLM -> alamda *= 10.0;
+ pLM->chisq = pLM->ochisq;
+ }
+
+ return true;
+}
+
+
+double cmsxLevenbergMarquardtAlamda(LCMSHANDLE hMRQ)
+{
+ LPLMRTQMIN pLM = (LPLMRTQMIN)hMRQ;
+
+ return pLM ->alamda;
+}
+
+double cmsxLevenbergMarquardtChiSq(LCMSHANDLE hMRQ)
+{
+ LPLMRTQMIN pLM = (LPLMRTQMIN)hMRQ;
+
+ return pLM ->chisq;
+}
diff --git a/src/libs/lprof/cmslnr.cpp b/src/libs/lprof/cmslnr.cpp
new file mode 100644
index 00000000..dddd8e38
--- /dev/null
+++ b/src/libs/lprof/cmslnr.cpp
@@ -0,0 +1,560 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.09a */
+
+
+#include "lcmsprf.h"
+
+
+LPGAMMATABLE cdecl cmsxEstimateGamma(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints);
+void cdecl cmsxCompleteLabOfPatches(LPMEASUREMENT m, SETOFPATCHES Valids, int Medium);
+
+void cdecl cmsxComputeLinearizationTables(LPMEASUREMENT m,
+ int ColorSpace,
+ LPGAMMATABLE Lin[3],
+ int nResultingPoints,
+ int Medium);
+
+
+void cdecl cmsxApplyLinearizationTable(double In[3],
+ LPGAMMATABLE Gamma[3],
+ double Out[3]);
+
+void cdecl cmsxApplyLinearizationGamma(WORD In[3], LPGAMMATABLE Gamma[3], WORD Out[3]);
+
+
+
+/* ------------------------------------------------------------- Implementation */
+
+
+#define EPSILON 0.00005
+#define LEVENBERG_MARQUARDT_ITERATE_MAX 150
+
+/* In order to track linearization tables, we use following procedure */
+/* */
+/* We first assume R', G' and B' does exhibit a non-linear behaviour */
+/* that can be separated for each channel as Yr(R'), Yg(G'), Yb(B') */
+/* This is the shaper step */
+/* */
+/* R = Lr(R') */
+/* G = Lg(G') */
+/* B = Lb(B') (0.0) */
+/* */
+/* After this step, RGB is converted to XYZ by a matrix multiplication */
+/* */
+/* |X| |R| */
+/* |Y| = [M]·|G| */
+/* |Z| |B| (1.0) */
+/* */
+/* In order to extract Lr,Lg,Lb tables, we are interested only on Y part */
+/* */
+/* Y = (m1 * R + m2 * G + m3 * B) (1.1) */
+/* */
+/* The total intensity for maximum RGB = (1, 1, 1) should be 1, */
+/* */
+/* 1 = m1 * 1 + m2 * 1 + m3 * 1, so */
+/* */
+/* m1 + m2 + m3 = 1.0 (1.2) */
+/* */
+/* We now impose that for neutral (gray) patches, RGB components must be equal */
+/* */
+/* R = G = B = Gray */
+/* */
+/* So, substituting in (1.1): */
+/* */
+/* Y = (m1 + m2 + m3) Gray */
+/* */
+/* and for (1.2), (m1+m2+m3) = 1, so */
+/* */
+/* Y = Gray = Lr(R') = Lg(G') = Lb(B') */
+/* */
+/* That is, after prelinearization, RGB of gray patches should give */
+/* same values for R, G and B. And this value is Y. */
+/* */
+/* */
+
+
+static
+LPSAMPLEDCURVE NormalizeTo(LPSAMPLEDCURVE X, double N, BOOL lAddEndPoint)
+{
+ int i, nItems;
+ LPSAMPLEDCURVE XNorm;
+
+ nItems = X ->nItems;
+ if (lAddEndPoint) nItems++;
+
+ XNorm = cmsAllocSampledCurve(nItems);
+
+ for (i=0; i < X ->nItems; i++) {
+
+ XNorm ->Values[i] = X ->Values[i] / N;
+ }
+
+ if (lAddEndPoint)
+ XNorm -> Values[X ->nItems] = 1.0;
+
+ return XNorm;
+}
+
+
+/* */
+/* ------------------------------------------------------------------------------ */
+/* */
+/* Our Monitor model. We assume gamma has a general expression of */
+/* */
+/* Fn(x) = (Gain * x + offset) ^ gamma | for x >= 0 */
+/* Fn(x) = 0 | for x < 0 */
+/* */
+/* First partial derivatives are */
+/* */
+/* dFn/dGamma = Fn * ln(Base) */
+/* dFn/dGain = gamma * x * ((Gain * x + Offset) ^ (gamma -1)) */
+/* dFn/dOffset = gamma * ((Gain * x + Offset) ^ (gamma -1)) */
+/* */
+
+static
+void GammaGainOffsetFn(double x, double *a, double *y, double *dyda, int na)
+{
+ double Gamma,Gain,Offset;
+ double Base;
+
+ Gamma = a[0];
+ Gain = a[1];
+ Offset = a[2];
+
+ Base = Gain * x + Offset;
+
+ if (Base < 0) {
+
+ Base = 0.0;
+ *y = 0.0;
+ dyda[0] = 0.0;
+ dyda[1] = 0.0;
+ dyda[2] = 0.0;
+
+
+ } else {
+
+
+ /* The function itself */
+ *y = pow(Base, Gamma);
+
+ /* dyda[0] is partial derivative across Gamma */
+ dyda[0] = *y * log(Base);
+
+ /* dyda[1] is partial derivative across gain */
+ dyda[1] = (x * Gamma) * pow(Base, Gamma-1.0);
+
+ /* dyda[2] is partial derivative across offset */
+ dyda[2] = Gamma * pow(Base, Gamma-1.0);
+ }
+}
+
+
+/* Fit curve to our gamma-gain-offset model. */
+
+static
+BOOL OneTry(LPSAMPLEDCURVE XNorm, LPSAMPLEDCURVE YNorm, double a[])
+{
+ LCMSHANDLE h;
+ double ChiSq, OldChiSq;
+ int i;
+ BOOL Status = true;
+
+ /* initial guesses */
+
+ a[0] = 3.0; /* gamma */
+ a[1] = 4.0; /* gain */
+ a[2] = 6.0; /* offset */
+ a[3] = 0.0; /* Thereshold */
+ a[4] = 0.0; /* Black */
+
+
+ /* Significance = 0.02 gives good results */
+
+ h = cmsxLevenbergMarquardtInit(XNorm, YNorm, 0.02, a, 3, GammaGainOffsetFn);
+ if (h == NULL) return false;
+
+
+ OldChiSq = cmsxLevenbergMarquardtChiSq(h);
+
+ for(i = 0; i < LEVENBERG_MARQUARDT_ITERATE_MAX; i++) {
+
+ if (!cmsxLevenbergMarquardtIterate(h)) {
+ Status = false;
+ break;
+ }
+
+ ChiSq = cmsxLevenbergMarquardtChiSq(h);
+
+ if(OldChiSq != ChiSq && (OldChiSq - ChiSq) < EPSILON)
+ break;
+
+ OldChiSq = ChiSq;
+ }
+
+ cmsxLevenbergMarquardtFree(h);
+
+ return Status;
+}
+
+/* Tries to fit gamma as per IEC 61966-2.1 using Levenberg-Marquardt method */
+/* */
+/* Y = (aX + b)^Gamma | X >= d */
+/* Y = cX | X < d */
+
+LPGAMMATABLE cmsxEstimateGamma(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints)
+{
+ double a[5];
+ LPSAMPLEDCURVE XNorm, YNorm;
+ double e, Max;
+
+
+ /* Coarse approximation, to find maximum. */
+ /* We have only a portion of curve. It is likely */
+ /* maximum will not fall on exactly 100. */
+
+ if (!OneTry(X, Y, a))
+ return 0;
+
+ /* Got parameters. Compute maximum. */
+ e = a[1]* 255.0 + a[2];
+ if (e < 0) return 0;
+ Max = pow(e, a[0]);
+
+
+ /* Normalize values to maximum */
+ XNorm = NormalizeTo(X, 255.0, false);
+ YNorm = NormalizeTo(Y, Max, false);
+
+ /* Do the final fitting */
+ if (!OneTry(XNorm, YNorm, a))
+ return 0;
+
+ /* Type 3 = IEC 61966-2.1 (sRGB) */
+ /* Y = (aX + b)^Gamma | X >= d */
+ /* Y = cX | X < d */
+ return cmsBuildParametricGamma(nResultingPoints, 3, a);
+}
+
+
+
+
+
+/* A dumb bubble sort */
+
+static
+void Bubble(LPSAMPLEDCURVE C, LPSAMPLEDCURVE L)
+{
+#define SWAP(a, b) { tmp = (a); (a) = (b); (b) = tmp; }
+
+ BOOL lSwapped;
+ int i, nItems;
+ double tmp;
+
+ nItems = C -> nItems;
+ do {
+ lSwapped = false;
+
+ for (i= 0; i < nItems - 1; i++) {
+
+ if (C->Values[i] > C->Values[i+1]) {
+
+ SWAP(C->Values[i], C->Values[i+1]);
+ SWAP(L->Values[i], L->Values[i+1]);
+ lSwapped = true;
+ }
+ }
+
+ } while (lSwapped);
+
+#undef SWAP
+}
+
+
+
+/* Check for monotonicity. Force it if is not the case. */
+
+static
+void CheckForMonotonicSampledCurve(LPSAMPLEDCURVE t)
+{
+ int n = t ->nItems;
+ int i;
+ double last;
+
+ last = t ->Values[n-1];
+ for (i = n-2; i >= 0; --i) {
+
+ if (t ->Values[i] > last)
+
+ t ->Values[i] = last;
+ else
+ last = t ->Values[i];
+
+ }
+
+}
+
+/* The main gamma inferer. Tries first by gamma-gain-offset, */
+/* if not proper reverts to curve guessing. */
+
+static
+LPGAMMATABLE BuildGammaTable(LPSAMPLEDCURVE C, LPSAMPLEDCURVE L, int nResultingPoints)
+{
+ LPSAMPLEDCURVE Cw, Lw, Cn, Ln;
+ LPSAMPLEDCURVE out;
+ LPGAMMATABLE Result;
+ double Lmax, Lend, Cmax;
+
+ /* Try to see if it can be fitted */
+ Result = cmsxEstimateGamma(C, L, nResultingPoints);
+ if (Result)
+ return Result;
+
+
+ /* No... build curve from scratch. Since we have not */
+ /* endpoints, a coarse linear extrapolation should be */
+ /* applied in order to get the expected maximum. */
+
+ Cw = cmsDupSampledCurve(C);
+ Lw = cmsDupSampledCurve(L);
+
+ Bubble(Cw, Lw);
+
+ /* Get endpoint */
+ Lmax = Lw->Values[Lw ->nItems - 1];
+ Cmax = Cw->Values[Cw ->nItems - 1];
+
+ /* Linearly extrapolate */
+ Lend = (255 * Lmax) / Cmax;
+
+ Ln = NormalizeTo(Lw, Lend, true);
+ Cn = NormalizeTo(Cw, 255.0, true);
+
+ cmsFreeSampledCurve(Cw);
+ cmsFreeSampledCurve(Lw);
+
+ /* Add endpoint */
+ out = cmsJoinSampledCurves(Cn, Ln, nResultingPoints);
+
+ cmsFreeSampledCurve(Cn);
+ cmsFreeSampledCurve(Ln);
+
+ CheckForMonotonicSampledCurve(out);
+
+ cmsSmoothSampledCurve(out, nResultingPoints*4.);
+ cmsClampSampledCurve(out, 0, 1.0);
+
+ Result = cmsConvertSampledCurveToGamma(out, 1.0);
+
+ cmsFreeSampledCurve(out);
+ return Result;
+}
+
+
+
+
+void cmsxCompleteLabOfPatches(LPMEASUREMENT m, SETOFPATCHES Valids, int Medium)
+{
+ LPPATCH White;
+ cmsCIEXYZ WhiteXYZ;
+ int i;
+
+ if (Medium == MEDIUM_REFLECTIVE_D50)
+ {
+ WhiteXYZ.X = D50X * 100.;
+ WhiteXYZ.Y = D50Y * 100.;
+ WhiteXYZ.Z = D50Z * 100.;
+ }
+ else {
+
+ White = cmsxPCollFindWhite(m, Valids, NULL);
+ if (!White) return;
+
+ WhiteXYZ = White ->XYZ;
+ }
+
+ /* For all patches with XYZ and without Lab, add Lab values. */
+ /* Transmissive profiles does need to locate its own white */
+ /* point for device gray. Reflective does use D50 */
+
+ for (i=0; i < m -> nPatches; i++) {
+
+ if (Valids[i]) {
+
+ LPPATCH p = m -> Patches + i;
+
+ if ((p ->dwFlags & PATCH_HAS_XYZ) &&
+ (!(p ->dwFlags & PATCH_HAS_Lab) || (Medium == MEDIUM_TRANSMISSIVE))) {
+
+ cmsXYZ2Lab(&WhiteXYZ, &p->Lab, &p->XYZ);
+ p -> dwFlags |= PATCH_HAS_Lab;
+ }
+ }
+ }
+}
+
+
+/* Compute linearization tables, trying to fit in a pure */
+/* exponential gamma. If gamma cannot be accurately infered, */
+/* then does build a smooth, monotonic curve that does the job. */
+
+void cmsxComputeLinearizationTables(LPMEASUREMENT m,
+ int ColorSpace,
+ LPGAMMATABLE Lin[3],
+ int nResultingPoints,
+ int Medium)
+
+{
+ LPSAMPLEDCURVE R, G, B, L;
+ LPGAMMATABLE gr, gg, gb;
+ SETOFPATCHES Neutrals;
+ int nGrays;
+ int i;
+
+ /* We need Lab for grays. */
+ cmsxCompleteLabOfPatches(m, m->Allowed, Medium);
+
+ /* Add neutrals, normalize to max */
+ Neutrals = cmsxPCollBuildSet(m, false);
+ cmsxPCollPatchesNearNeutral(m, m ->Allowed, 15, Neutrals);
+
+ nGrays = cmsxPCollCountSet(m, Neutrals);
+
+ R = cmsAllocSampledCurve(nGrays);
+ G = cmsAllocSampledCurve(nGrays);
+ B = cmsAllocSampledCurve(nGrays);
+ L = cmsAllocSampledCurve(nGrays);
+
+ nGrays = 0;
+
+ /* Collect patches */
+ for (i=0; i < m -> nPatches; i++) {
+
+ if (Neutrals[i]) {
+
+ LPPATCH gr = m -> Patches + i;
+
+
+ R -> Values[nGrays] = gr -> Colorant.RGB[0];
+ G -> Values[nGrays] = gr -> Colorant.RGB[1];
+ B -> Values[nGrays] = gr -> Colorant.RGB[2];
+ L -> Values[nGrays] = gr -> XYZ.Y;
+
+ nGrays++;
+ }
+
+ }
+
+
+ gr = BuildGammaTable(R, L, nResultingPoints);
+ gg = BuildGammaTable(G, L, nResultingPoints);
+ gb = BuildGammaTable(B, L, nResultingPoints);
+
+ cmsFreeSampledCurve(R);
+ cmsFreeSampledCurve(G);
+ cmsFreeSampledCurve(B);
+ cmsFreeSampledCurve(L);
+
+ if (ColorSpace == PT_Lab) {
+
+ LPGAMMATABLE Gamma3 = cmsBuildGamma(nResultingPoints, 3.0);
+
+ Lin[0] = cmsJoinGammaEx(gr, Gamma3, nResultingPoints);
+ Lin[1] = cmsJoinGammaEx(gg, Gamma3, nResultingPoints);
+ Lin[2] = cmsJoinGammaEx(gb, Gamma3, nResultingPoints);
+
+ cmsFreeGamma(gr); cmsFreeGamma(gg); cmsFreeGamma(gb);
+ cmsFreeGamma(Gamma3);
+ }
+ else {
+
+
+ LPGAMMATABLE Gamma1 = cmsBuildGamma(nResultingPoints, 1.0);
+
+ Lin[0] = cmsJoinGammaEx(gr, Gamma1, nResultingPoints);
+ Lin[1] = cmsJoinGammaEx(gg, Gamma1, nResultingPoints);
+ Lin[2] = cmsJoinGammaEx(gb, Gamma1, nResultingPoints);
+
+ cmsFreeGamma(gr); cmsFreeGamma(gg); cmsFreeGamma(gb);
+ cmsFreeGamma(Gamma1);
+
+ }
+
+}
+
+
+
+/* Apply linearization. WORD encoded version */
+
+void cmsxApplyLinearizationGamma(WORD In[3], LPGAMMATABLE Gamma[3], WORD Out[3])
+{
+ L16PARAMS Lut16;
+
+ cmsCalcL16Params(Gamma[0] -> nEntries, &Lut16);
+
+ Out[0] = cmsLinearInterpLUT16(In[0], Gamma[0] -> GammaTable, &Lut16);
+ Out[1] = cmsLinearInterpLUT16(In[1], Gamma[1] -> GammaTable, &Lut16);
+ Out[2] = cmsLinearInterpLUT16(In[2], Gamma[2] -> GammaTable, &Lut16);
+
+
+}
+
+
+
+/* Apply linearization. double version */
+
+void cmsxApplyLinearizationTable(double In[3], LPGAMMATABLE Gamma[3], double Out[3])
+{
+ WORD rw, gw, bw;
+ double rd, gd, bd;
+ L16PARAMS Lut16;
+
+
+ cmsCalcL16Params(Gamma[0] -> nEntries, &Lut16);
+
+ rw = (WORD) floor(_cmsxSaturate255To65535(In[0]) + .5);
+ gw = (WORD) floor(_cmsxSaturate255To65535(In[1]) + .5);
+ bw = (WORD) floor(_cmsxSaturate255To65535(In[2]) + .5);
+
+ rd = cmsLinearInterpLUT16(rw , Gamma[0] -> GammaTable, &Lut16);
+ gd = cmsLinearInterpLUT16(gw, Gamma[1] -> GammaTable, &Lut16);
+ bd = cmsLinearInterpLUT16(bw, Gamma[2] -> GammaTable, &Lut16);
+
+ Out[0] = _cmsxSaturate65535To255(rd); /* back to 0..255 */
+ Out[1] = _cmsxSaturate65535To255(gd);
+ Out[2] = _cmsxSaturate65535To255(bd);
+}
+
diff --git a/src/libs/lprof/cmsmatn.cpp b/src/libs/lprof/cmsmatn.cpp
new file mode 100644
index 00000000..bca52717
--- /dev/null
+++ b/src/libs/lprof/cmsmatn.cpp
@@ -0,0 +1,323 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.09a */
+
+
+#include "lcmsprf.h"
+
+
+LPMATN cdecl MATNalloc(int Rows, int Cols);
+void cdecl MATNfree (LPMATN mat);
+LPMATN cdecl MATNmult(LPMATN a1, LPMATN a2);
+double cdecl MATNcross(LPMATN a);
+void cdecl MATNscalar (LPMATN a, double scl, LPMATN b);
+LPMATN cdecl MATNtranspose (LPMATN a);
+BOOL cdecl MATNsolve(LPMATN a, LPMATN b);
+
+
+/* ------------------------------------------------------------ Implementation */
+
+/* Free matrix */
+
+void MATNfree(LPMATN mat)
+{
+ int i;
+
+ if (mat == NULL) return;
+
+ for (i = 0; i < mat->Rows; i++)
+ {
+ if (mat -> Values[i] != NULL)
+ free (mat->Values[i]);
+ }
+
+ free(mat->Values);
+ free(mat);
+}
+
+
+/* Allocate (and Zero) a new matrix */
+
+LPMATN MATNalloc(int Rows, int Cols)
+{
+ int i;
+
+ LPMATN mat = (LPMATN) malloc (sizeof (MATN));
+ if (mat == NULL) return mat;
+
+ ZeroMemory(mat, sizeof(MATN));
+
+ mat->Rows = Rows;
+ mat->Cols = Cols;
+ mat->Values = (double**) malloc(Rows * sizeof (double*));
+
+ if (mat->Values == NULL) {
+ free(mat);
+ return NULL;
+ }
+
+ ZeroMemory(mat -> Values, Rows * sizeof (double*));
+
+ for (i = 0; i < Rows; i++)
+ {
+ mat-> Values [i] = (double*) malloc(Cols * sizeof (double));
+ if (mat -> Values[i] == NULL) {
+ MATNfree(mat);
+ return NULL;
+ }
+
+ }
+
+ return mat;
+}
+
+#define DO_SWAP(a, b, tmp) { tmp = (a); (a) = (b); (b) = tmp; }
+
+/* Gauss-Jordan elimination. There is also a more */
+/* exahustive non-singular matrix checking part. */
+
+BOOL MATNsolve(LPMATN a, LPMATN b)
+{
+ BOOL status;
+ int n = a->Rows;
+ int i, iCol=0, iRow=0, j, k;
+ double fMax, fAbs, fSave, fInf, temp;
+ int* aiColIndex;
+ int* aiRowIndex=0;
+ int* aiPivoted=0;
+
+
+ if (a->Rows != a->Cols) return false;
+
+ status = false;
+ if((aiColIndex = (int*) malloc(n * sizeof(int))) == NULL)
+ goto GotError;
+
+ if((aiRowIndex = (int*) malloc(n * sizeof(int))) == NULL)
+ goto GotError;
+
+ if((aiPivoted = (int*) malloc(n * sizeof(int))) == NULL)
+ goto GotError;
+
+ ZeroMemory(aiPivoted, n * sizeof(int));
+
+
+ for(i = 0; i < n; i++) {
+
+ /* search matrix (excluding pivoted rows) for maximum absolute entry */
+
+ fMax = 0.0;
+ for (j = 0; j < n; j++)
+ if (aiPivoted[j] != 1)
+ for (k = 0; k < n; k++)
+ {
+ fAbs = fabs(a->Values[j][k]);
+ if (fAbs >= fMax) {
+
+ fMax = fAbs;
+ iRow = j;
+ iCol = k;
+ }
+ else
+ if (aiPivoted[k] > 1) {
+
+ status = false;
+ goto GotError;
+ }
+ }
+
+ aiPivoted[iCol]++;
+
+ /* swap rows so that A[iCol][iCol] contains the pivot entry */
+
+ if (iRow != iCol) {
+
+ for(j = 0; j < n; j++)
+ DO_SWAP(a->Values[iRow][j], a->Values[iCol][j], temp)
+
+ DO_SWAP(b->Values[iRow][0], b->Values[iCol][0], temp)
+ }
+
+ /* keep track of the permutations of the rows */
+
+ aiRowIndex[i] = iRow;
+ aiColIndex[i] = iCol;
+
+ if (a->Values[iCol][iCol] == 0.0)
+ {
+ status = false;
+ goto GotError;
+ }
+
+ /* scale the row so that the pivot entry is 1 */
+
+ fInf = 1.0 / a->Values[iCol][iCol];
+ a->Values[iCol][iCol] = 1.0;
+
+ for(j = 0; j < n; j++)
+ a->Values[iCol][j] *= fInf;
+
+ b->Values[iCol][0] *= fInf;
+
+ /* zero out the pivot column locations in the other rows */
+
+ for(j = 0; j < n; j++)
+ if (j != iCol) {
+
+ fSave = a->Values[j][iCol];
+ a->Values[j][iCol] = 0.0;
+
+ for(k = 0; k < n; k++)
+ a->Values[j][k] -= a->Values[iCol][k] * fSave;
+
+ b->Values[j][0] -= b->Values[iCol][0] * fSave;
+ }
+ }
+
+ /* reorder rows so that A[][] stores the inverse of the original matrix */
+
+ for(i = n - 1; i >= 0; i--) {
+
+ if(aiRowIndex[i] != aiColIndex[i])
+ for(j = 0; j < n; j++)
+ DO_SWAP(a->Values[j][aiRowIndex[i]], a->Values[j][aiColIndex[i]], temp)
+ }
+
+ status = true;
+
+GotError:
+ if(aiColIndex) free(aiColIndex);
+ if(aiRowIndex) free(aiRowIndex);
+ if(aiPivoted) free(aiPivoted);
+ return status;
+
+}
+
+#undef DO_SWAP
+
+
+LPMATN MATNmult(LPMATN a1, LPMATN a2)
+{
+ int i, j, k;
+ LPMATN b;
+
+ if (a1->Cols != a2->Rows)
+ return NULL;
+
+ b = MATNalloc (a1->Rows, a2->Cols);
+ if (b == NULL)
+ return NULL;
+
+ for (i = 0; i < b->Rows; i++) {
+
+ for (j = 0; j < b->Cols; j++) {
+
+ b->Values[i][j] = 0.0;
+
+ for (k = 0; k < a1->Cols; k++) {
+
+ b->Values[i][j] += a1->Values[i][k] * a2->Values[k][j];
+ }
+ }
+ }
+
+ return b;
+}
+
+
+double MATNcross(LPMATN a)
+{
+ int i;
+ double prod = 0.0;
+
+ for (i = 0; i < a->Rows; i++) {
+
+ prod += a->Values[i][0]*a->Values[i][0];
+ }
+ return prod;
+}
+
+
+void MATNscalar(LPMATN a, double scl, LPMATN b)
+{
+ int i, j;
+
+ if (a->Rows != b->Rows || a->Cols != b->Cols)
+ return;
+
+ for (i = 0; i < a->Rows; i++) {
+
+ for (j = 0; j < a->Cols; j++)
+ b->Values[i][j] = a->Values[i][j] * scl;
+ }
+}
+
+
+LPMATN MATNtranspose(LPMATN a)
+{
+ LPMATN b = MATNalloc(a->Cols, a->Rows);
+ if (b != NULL) {
+
+ int i, j;
+
+ for (i = 0; i < a->Rows; i++)
+ {
+ for (j = 0; j < a->Cols; j++)
+ b->Values[j][i] = a->Values [i][j];
+ }
+ }
+ return b;
+}
+
+
+
+/* Used for debug purposes */
+#ifdef DEBUG
+void MATNprintf(char* name, LPMATN mat)
+{
+ int i, j;
+
+ printf ("%s:\n", name);
+ for (i= 0; i < mat->Rows; i++) {
+
+ printf ("%3d", i);
+ for (j = 0; j < mat->Cols; j++)
+ printf (" %.5f", mat->Values[i][j]);
+ printf ("\n");
+ }
+}
+#endif
+
+
diff --git a/src/libs/lprof/cmsmkmsh.cpp b/src/libs/lprof/cmsmkmsh.cpp
new file mode 100644
index 00000000..c0e3d4c3
--- /dev/null
+++ b/src/libs/lprof/cmsmkmsh.cpp
@@ -0,0 +1,346 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.08a */
+
+
+#include "lcmsprf.h"
+
+
+BOOL cdecl cmsxComputeMatrixShaper(const char* ReferenceSheet,
+ const char* MeasurementSheet,
+ int Medium,
+ LPGAMMATABLE TransferCurves[3],
+ LPcmsCIEXYZ WhitePoint,
+ LPcmsCIEXYZ BlackPoint,
+ LPcmsCIExyYTRIPLE Primaries);
+
+
+
+/* ------------------------------------------------------------- Implementation */
+
+
+static
+void Div100(LPcmsCIEXYZ xyz)
+{
+ xyz -> X /= 100; xyz -> Y /= 100; xyz -> Z /= 100;
+}
+
+
+
+/* Compute endpoints */
+
+static
+BOOL ComputeWhiteAndBlackPoints(LPMEASUREMENT Linearized,
+ LPGAMMATABLE TransferCurves[3],
+ LPcmsCIEXYZ Black, LPcmsCIEXYZ White)
+{
+
+ double Zeroes[3], Ones[3], lmin[3], lmax[3];
+
+ SETOFPATCHES Neutrals = cmsxPCollBuildSet(Linearized, false);
+
+ cmsxPCollPatchesNearNeutral(Linearized, Linearized->Allowed,
+ 15, Neutrals);
+
+ Zeroes[0] = Zeroes[1] = Zeroes[2] = 0.0;
+ Ones[0] = Ones[1] = Ones[2] = 255.0;
+
+
+ cmsxApplyLinearizationTable(Zeroes, TransferCurves, lmin);
+ cmsxApplyLinearizationTable(Ones, TransferCurves, lmax);
+
+
+ /* Global regression to find White & Black points */
+ if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ,
+ 4,
+ true,
+ 12,
+ lmin[0], lmin[1], lmin[2],
+ Black)) return false;
+
+ if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ,
+ 4,
+ true,
+ 12,
+ lmax[0], lmax[1], lmax[2],
+ White)) return false;
+
+ _cmsxClampXYZ100(White);
+ _cmsxClampXYZ100(Black);
+
+ return true;
+
+}
+
+
+/* Study convergence of primary axis */
+
+static
+BOOL ComputePrimary(LPMEASUREMENT Linearized,
+ LPGAMMATABLE TransferCurves[3],
+ int n,
+ LPcmsCIExyY Primary)
+{
+
+ double Ones[3], lmax[3];
+ cmsCIEXYZ PrimXYZ;
+ SETOFPATCHES SetPrimary;
+ int nR;
+
+
+ /* At first, try to see if primaries are already in measurement */
+
+ SetPrimary = cmsxPCollBuildSet(Linearized, false);
+ nR = cmsxPCollPatchesNearPrimary(Linearized, Linearized->Allowed,
+ n, 32, SetPrimary);
+
+ Ones[0] = Ones[1] = Ones[2] = 0;
+ Ones[n] = 255.0;
+
+ cmsxApplyLinearizationTable(Ones, TransferCurves, lmax);
+
+ /* Do incremental regression to find primaries */
+ if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ,
+ 4,
+ false,
+ 12,
+ lmax[0], lmax[1], lmax[2],
+ &PrimXYZ)) return false;
+
+ _cmsxClampXYZ100(&PrimXYZ);
+ cmsXYZ2xyY(Primary, &PrimXYZ);
+ return true;
+
+
+}
+
+
+
+/* Does compute a matrix-shaper based on patches. */
+
+static
+double Clip(double d)
+{
+ return d > 0 ? d: 0;
+}
+
+
+BOOL cmsxComputeMatrixShaper(const char* ReferenceSheet,
+ const char* MeasurementSheet,
+ int Medium,
+ LPGAMMATABLE TransferCurves[3],
+ LPcmsCIEXYZ WhitePoint,
+ LPcmsCIEXYZ BlackPoint,
+ LPcmsCIExyYTRIPLE Primaries)
+{
+
+ MEASUREMENT Linearized;
+ cmsCIEXYZ Black, White;
+ cmsCIExyYTRIPLE PrimarySet;
+ LPPATCH PatchWhite, PatchBlack;
+ LPPATCH PatchRed, PatchGreen, PatchBlue;
+ double Distance;
+
+ /* Load sheets */
+
+ if (!cmsxPCollBuildMeasurement(&Linearized,
+ ReferenceSheet,
+ MeasurementSheet,
+ PATCH_HAS_XYZ|PATCH_HAS_RGB)) return false;
+
+
+
+ /* Any patch to deal of? */
+ if (cmsxPCollCountSet(&Linearized, Linearized.Allowed) <= 0) return false;
+
+
+ /* Try to see if proper primaries, white and black already present */
+ PatchWhite = cmsxPCollFindWhite(&Linearized, Linearized.Allowed, &Distance);
+ if (Distance != 0)
+ PatchWhite = NULL;
+
+ PatchBlack = cmsxPCollFindBlack(&Linearized, Linearized.Allowed, &Distance);
+ if (Distance != 0)
+ PatchBlack = NULL;
+
+ PatchRed = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 0, &Distance);
+ if (Distance != 0)
+ PatchRed = NULL;
+
+ PatchGreen = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 1, &Distance);
+ if (Distance != 0)
+ PatchGreen = NULL;
+
+ PatchBlue = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 2, &Distance);
+ if (Distance != 0)
+ PatchBlue= NULL;
+
+ /* If we got primaries, then we can also get prelinearization */
+ /* by Levenberg-Marquardt. This applies on monitor profiles */
+
+ if (PatchWhite && PatchRed && PatchGreen && PatchBlue) {
+
+ /* Build matrix with primaries */
+
+ MAT3 Mat, MatInv;
+ LPSAMPLEDCURVE Xr,Yr, Xg, Yg, Xb, Yb;
+ int i, nRes, cnt;
+
+ VEC3init(&Mat.v[0], PatchRed->XYZ.X, PatchGreen->XYZ.X, PatchBlue->XYZ.X);
+ VEC3init(&Mat.v[1], PatchRed->XYZ.Y, PatchGreen->XYZ.Y, PatchBlue->XYZ.Y);
+ VEC3init(&Mat.v[2], PatchRed->XYZ.Z, PatchGreen->XYZ.Z, PatchBlue->XYZ.Z);
+
+ /* Invert matrix */
+ MAT3inverse(&Mat, &MatInv);
+
+ nRes = cmsxPCollCountSet(&Linearized, Linearized.Allowed);
+
+ Xr = cmsAllocSampledCurve(nRes);
+ Yr = cmsAllocSampledCurve(nRes);
+ Xg = cmsAllocSampledCurve(nRes);
+ Yg = cmsAllocSampledCurve(nRes);
+ Xb = cmsAllocSampledCurve(nRes);
+ Yb = cmsAllocSampledCurve(nRes);
+
+ /* Convert XYZ of all patches to RGB */
+ cnt = 0;
+ for (i=0; i < Linearized.nPatches; i++) {
+
+ if (Linearized.Allowed[i]) {
+
+ VEC3 RGBprime, XYZ;
+ LPPATCH p;
+
+ p = Linearized.Patches + i;
+ XYZ.n[0] = p -> XYZ.X;
+ XYZ.n[1] = p -> XYZ.Y;
+ XYZ.n[2] = p -> XYZ.Z;
+
+ MAT3eval(&RGBprime, &MatInv, &XYZ);
+
+ Xr ->Values[cnt] = p ->Colorant.RGB[0];
+ Yr ->Values[cnt] = Clip(RGBprime.n[0]);
+
+ Xg ->Values[cnt] = p ->Colorant.RGB[1];
+ Yg ->Values[cnt] = Clip(RGBprime.n[1]);
+
+ Xb ->Values[cnt] = p ->Colorant.RGB[2];
+ Yb ->Values[cnt] = Clip(RGBprime.n[2]);
+
+ cnt++;
+
+ }
+ }
+
+ TransferCurves[0] = cmsxEstimateGamma(Xr, Yr, 1024);
+ TransferCurves[1] = cmsxEstimateGamma(Xg, Yg, 1024);
+ TransferCurves[2] = cmsxEstimateGamma(Xb, Yb, 1024);
+
+ if (WhitePoint) {
+
+ WhitePoint->X = PatchWhite->XYZ.X;
+ WhitePoint->Y= PatchWhite ->XYZ.Y;
+ WhitePoint->Z= PatchWhite ->XYZ.Z;
+ }
+
+ if (BlackPoint && PatchBlack) {
+
+ BlackPoint->X = PatchBlack ->XYZ.X;
+ BlackPoint->Y = PatchBlack ->XYZ.Y;
+ BlackPoint->Z = PatchBlack ->XYZ.Z;
+ }
+
+ if (Primaries) {
+
+ cmsXYZ2xyY(&Primaries->Red, &PatchRed ->XYZ);
+ cmsXYZ2xyY(&Primaries->Green, &PatchGreen ->XYZ);
+ cmsXYZ2xyY(&Primaries->Blue, &PatchBlue ->XYZ);
+
+ }
+
+
+ cmsFreeSampledCurve(Xr);
+ cmsFreeSampledCurve(Yr);
+ cmsFreeSampledCurve(Xg);
+ cmsFreeSampledCurve(Yg);
+ cmsFreeSampledCurve(Xb);
+ cmsFreeSampledCurve(Yb);
+
+ cmsxPCollFreeMeasurements(&Linearized);
+
+ return true;
+ }
+
+
+
+
+ /* Compute prelinearization */
+ cmsxComputeLinearizationTables(&Linearized, PT_XYZ, TransferCurves, 1024, Medium);
+
+ /* Linearize measurements */
+ cmsxPCollLinearizePatches(&Linearized, Linearized.Allowed, TransferCurves);
+
+
+ /* Endpoints */
+ ComputeWhiteAndBlackPoints(&Linearized, TransferCurves, &Black, &White);
+
+ /* Primaries */
+ ComputePrimary(&Linearized, TransferCurves, 0, &PrimarySet.Red);
+ ComputePrimary(&Linearized, TransferCurves, 1, &PrimarySet.Green);
+ ComputePrimary(&Linearized, TransferCurves, 2, &PrimarySet.Blue);
+
+
+ if (BlackPoint) {
+ *BlackPoint = Black;
+ Div100(BlackPoint);
+ }
+
+ if (WhitePoint) {
+ *WhitePoint = White;
+ Div100(WhitePoint);
+ }
+
+
+ if (Primaries) {
+ *Primaries = PrimarySet;
+ }
+
+ cmsxPCollFreeMeasurements(&Linearized);
+
+ return true;
+}
+
+
+
diff --git a/src/libs/lprof/cmsmntr.cpp b/src/libs/lprof/cmsmntr.cpp
new file mode 100644
index 00000000..ed14ed50
--- /dev/null
+++ b/src/libs/lprof/cmsmntr.cpp
@@ -0,0 +1,371 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.09a */
+
+
+#include "lcmsprf.h"
+
+
+static
+void ClampRGB(LPVEC3 RGB)
+{
+ int i;
+
+ for (i=0; i < 3; i++) {
+
+ if (RGB->n[i] > 1.0)
+ RGB->n[i] = 1.0;
+ if (RGB->n[i] < 0)
+ RGB->n[i] = 0;
+ }
+}
+
+
+static
+int RegressionSamplerA2B(WORD In[], WORD Out[], LPVOID Cargo)
+{
+ cmsCIEXYZ xyz;
+ cmsCIELab Lab;
+ VEC3 RGB, RGBlinear, vxyz;
+ LPMONITORPROFILERDATA sys = (LPMONITORPROFILERDATA) Cargo;
+
+
+ RGB.n[0] = _cmsxSaturate65535To255(In[0]);
+ RGB.n[1] = _cmsxSaturate65535To255(In[1]);
+ RGB.n[2] = _cmsxSaturate65535To255(In[2]);
+
+ cmsxApplyLinearizationTable(RGB.n, sys->PreLab, RGBlinear.n);
+ cmsxApplyLinearizationTable(RGBlinear.n, sys->Prelinearization, RGBlinear.n);
+
+ RGBlinear.n[0] /= 255.;
+ RGBlinear.n[1] /= 255.;
+ RGBlinear.n[2] /= 255.;
+
+ MAT3eval(&vxyz, &sys->PrimariesMatrix, &RGBlinear);
+
+ xyz.X = vxyz.n[0];
+ xyz.Y = vxyz.n[1];
+ xyz.Z = vxyz.n[2];
+
+ cmsxChromaticAdaptationAndNormalization(&sys ->hdr, &xyz, false);
+
+
+ /* To PCS encoding */
+
+ cmsXYZ2Lab(NULL, &Lab, &xyz);
+ cmsFloat2LabEncoded(Out, &Lab);
+
+
+ return true; /* And done witch success */
+}
+
+
+
+
+static
+int RegressionSamplerB2A(WORD In[], WORD Out[], LPVOID Cargo)
+{
+ cmsCIELab Lab;
+ cmsCIEXYZ xyz;
+ VEC3 vxyz, RGB;
+ /* cmsJCh JCh; */
+ WORD Lin[3], Llab[3];
+ LPMONITORPROFILERDATA sys = (LPMONITORPROFILERDATA) Cargo;
+ double L;
+
+
+ /* Pass L back to 0..0xff00 domain */
+
+ L = (double) (In[0] * 65280.0) / 65535.0;
+ In[0] = (WORD) floor(L + .5);
+
+
+ /* To float values */
+ cmsLabEncoded2Float(&Lab, In);
+ cmsLab2XYZ(NULL, &xyz, &Lab);
+
+
+ cmsxChromaticAdaptationAndNormalization(&sys ->hdr, &xyz, true);
+ vxyz.n[0] = xyz.X;
+ vxyz.n[1] = xyz.Y;
+ vxyz.n[2] = xyz.Z;
+
+ MAT3eval(&RGB, &sys-> PrimariesMatrixRev, &vxyz);
+
+ /* Clamp RGB */
+ ClampRGB(&RGB);
+
+ /* Encode output */
+ Lin[0] = (WORD) ((double) RGB.n[0] * 65535. + .5);
+ Lin[1] = (WORD) ((double) RGB.n[1] * 65535. + .5);
+ Lin[2] = (WORD) ((double) RGB.n[2] * 65535. + .5);
+
+ cmsxApplyLinearizationGamma(Lin, sys ->ReverseTables, Llab);
+ cmsxApplyLinearizationGamma(Llab, sys ->PreLabRev, Out);
+
+
+ return true; /* And done witch success */
+}
+
+
+BOOL cmsxMonitorProfilerInit(LPMONITORPROFILERDATA sys)
+{
+
+
+ if (sys == NULL) return false;
+ ZeroMemory(sys, sizeof(MONITORPROFILERDATA));
+
+ sys->hdr.DeviceClass = icSigDisplayClass;
+ sys->hdr.ColorSpace = icSigRgbData;
+ sys->hdr.PCSType = PT_Lab;
+ sys->hdr.Medium = MEDIUM_TRANSMISSIVE;
+
+
+ /* Default values for generation */
+
+ sys -> hdr.lUseCIECAM97s = false;
+ sys -> hdr.CLUTPoints = 16;
+
+ /* Default viewing conditions */
+
+ sys -> hdr.device.Yb = 20;
+ sys -> hdr.device.La = 20;
+ sys -> hdr.device.surround = AVG_SURROUND;
+ sys -> hdr.device.D_value = 1; /* Complete adaptation */
+
+
+ /* Viewing conditions of PCS */
+ cmsxInitPCSViewingConditions(&sys ->hdr);
+
+ strcpy(sys -> hdr.Description, "unknown monitor");
+ strcpy(sys -> hdr.Manufacturer, "little cms profiler construction set");
+ strcpy(sys -> hdr.Copyright, "No copyright, use freely");
+ strcpy(sys -> hdr.Model, "(unknown)");
+
+ sys -> hdr.ProfileVerbosityLevel = 0;
+
+ return true;
+}
+
+
+static
+void CreatePrimaryMatrices(LPMONITORPROFILERDATA sys)
+{
+ cmsCIExyY White;
+ MAT3 tmp;
+
+
+ cmsXYZ2xyY(&White, &sys->hdr.WhitePoint);
+ cmsBuildRGB2XYZtransferMatrix(&sys -> PrimariesMatrix, &White, &sys->hdr.Primaries);
+
+ CopyMemory(&tmp, &sys -> PrimariesMatrix, sizeof(MAT3));
+ MAT3inverse(&tmp, &sys->PrimariesMatrixRev);
+
+}
+
+
+static
+BOOL CreateLUTS(LPMONITORPROFILERDATA sys, LPLUT* A2B, LPLUT* B2A)
+{
+ LPLUT AToB0 = cmsAllocLUT();
+ LPLUT BToA0 = cmsAllocLUT();
+ LPGAMMATABLE LabG;
+ cmsCIExyY xyY;
+
+
+ cmsAlloc3DGrid(AToB0, sys->hdr.CLUTPoints, 3, 3);
+ cmsAlloc3DGrid(BToA0, sys->hdr.CLUTPoints, 3, 3);
+
+ /* cmsAllocLinearTable(AToB0, sys -> Prelinearization, 1); */
+
+ sys->ReverseTables[0] = cmsReverseGamma(4096, sys ->Prelinearization[0]);
+ sys->ReverseTables[1] = cmsReverseGamma(4096, sys ->Prelinearization[1]);
+ sys->ReverseTables[2] = cmsReverseGamma(4096, sys ->Prelinearization[2]);
+
+ /* Prelinearization */
+
+ LabG = cmsBuildGamma(4096, 3.0);
+
+ sys -> PreLab[0] = cmsJoinGammaEx(LabG, sys ->Prelinearization[0], 4096);
+ sys -> PreLab[1] = cmsJoinGammaEx(LabG, sys ->Prelinearization[1], 4096);
+ sys -> PreLab[2] = cmsJoinGammaEx(LabG, sys ->Prelinearization[2], 4096);
+
+ sys -> PreLabRev[0] = cmsJoinGammaEx(sys ->Prelinearization[0], LabG, 4096);
+ sys -> PreLabRev[1] = cmsJoinGammaEx(sys ->Prelinearization[1], LabG, 4096);
+ sys -> PreLabRev[2] = cmsJoinGammaEx(sys ->Prelinearization[2], LabG, 4096);
+
+
+ cmsFreeGamma(LabG);
+
+
+ cmsAllocLinearTable(AToB0, sys->PreLabRev, 1);
+ cmsAllocLinearTable(BToA0, sys->PreLab, 2);
+
+
+ /* Set CIECAM97s parameters */
+
+ sys -> hdr.device.whitePoint.X = sys -> hdr.WhitePoint.X * 100.;
+ sys -> hdr.device.whitePoint.Y = sys -> hdr.WhitePoint.Y * 100.;
+ sys -> hdr.device.whitePoint.Z = sys -> hdr.WhitePoint.Z * 100.;
+
+
+ /* Normalize White point for CIECAM97s model */
+ cmsXYZ2xyY(&xyY, &sys -> hdr.device.whitePoint);
+ xyY.Y = 100.;
+ cmsxyY2XYZ(&sys -> hdr.device.whitePoint, &xyY);
+
+
+ sys->hdr.hDevice = cmsCIECAM97sInit(&sys->hdr.device);
+ sys->hdr.hPCS = cmsCIECAM97sInit(&sys->hdr.PCS);
+
+
+ cmsSample3DGrid(AToB0, RegressionSamplerA2B, sys, 0);
+ cmsSample3DGrid(BToA0, RegressionSamplerB2A, sys, 0);
+
+ cmsCIECAM97sDone(sys->hdr.hDevice);
+ cmsCIECAM97sDone(sys->hdr.hPCS);
+
+ cmsAddTag(sys->hdr.hProfile, icSigAToB0Tag, AToB0);
+ cmsAddTag(sys->hdr.hProfile, icSigBToA0Tag, BToA0);
+
+ /* This is the 0xff00 trick to map white at lattice point */
+ BToA0 ->Matrix.v[0].n[0] = DOUBLE_TO_FIXED((65535.0 / 65280.0));
+
+ *A2B = AToB0;
+ *B2A = BToA0;
+
+ cmsFreeGammaTriple(sys->ReverseTables);
+ cmsFreeGammaTriple(sys->PreLab);
+ cmsFreeGammaTriple(sys->PreLabRev);
+ return true;
+}
+
+
+
+BOOL cmsxMonitorProfilerDo(LPMONITORPROFILERDATA sys)
+{
+
+ cmsCIExyY White;
+ LPLUT AToB0, BToA0;
+
+ AToB0 = BToA0 = NULL;
+
+ if (!*sys -> hdr.OutputProfileFile)
+ return false;
+
+
+ if (sys->hdr.ReferenceSheet[0] || sys->hdr.MeasurementSheet[0]) {
+
+ if (sys->hdr.printf) {
+
+ sys->hdr.printf("Loading sheets...");
+
+ if (sys->hdr.ReferenceSheet[0])
+ sys->hdr.printf("Reference sheet: %s", sys->hdr.ReferenceSheet);
+ if (sys->hdr.MeasurementSheet[0])
+ sys->hdr.printf("Measurement sheet: %s", sys->hdr.MeasurementSheet);
+ }
+
+
+ if (!cmsxComputeMatrixShaper(sys -> hdr.ReferenceSheet,
+ sys -> hdr.MeasurementSheet,
+ MEDIUM_TRANSMISSIVE,
+ sys -> Prelinearization,
+ &sys -> hdr.WhitePoint,
+ &sys -> hdr.BlackPoint,
+ &sys -> hdr.Primaries)) return false;
+
+ if (sys->hdr.printf) {
+
+ char Buffer[1024];
+ _cmsIdentifyWhitePoint(Buffer, &sys ->hdr.WhitePoint);
+ sys->hdr.printf("%s", Buffer);
+
+ sys->hdr.printf("Primaries: R:%1.2g, %1.2g G:%1.2g, %1.2g B:%1.2g, %1.2g",
+ sys->hdr.Primaries.Red.x,sys->hdr.Primaries.Red.y,
+ sys->hdr.Primaries.Green.x, sys->hdr.Primaries.Green.y,
+ sys->hdr.Primaries.Blue.x, sys->hdr.Primaries.Blue.y);
+ }
+
+ }
+
+
+ CreatePrimaryMatrices(sys);
+
+
+ cmsXYZ2xyY(&White, &sys->hdr.WhitePoint);
+
+ sys->hdr.hProfile = cmsCreateRGBProfile(&White,
+ &sys-> hdr.Primaries,
+ sys -> Prelinearization);
+
+ cmsSetDeviceClass(sys->hdr.hProfile, sys->hdr.DeviceClass);
+
+ if (sys -> hdr.lUseCIECAM97s)
+ sys->hdr.PCSType = PT_Lab;
+ else
+ sys->hdr.PCSType = PT_XYZ;
+
+ cmsSetPCS(sys->hdr.hProfile, _cmsICCcolorSpace(sys->hdr.PCSType));
+
+ if (sys -> hdr.lUseCIECAM97s)
+ CreateLUTS(sys, &AToB0, &BToA0);
+
+
+ cmsxEmbedTextualInfo(&sys ->hdr);
+
+ cmsAddTag(sys->hdr.hProfile, icSigMediaWhitePointTag, &sys->hdr.WhitePoint);
+ cmsAddTag(sys->hdr.hProfile, icSigMediaBlackPointTag, &sys->hdr.BlackPoint);
+
+
+ if (sys->hdr.ProfileVerbosityLevel >= 2) {
+
+ cmsxEmbedCharTarget(&sys ->hdr);
+ }
+
+
+ _cmsSaveProfile(sys->hdr.hProfile, sys->hdr.OutputProfileFile);
+ cmsCloseProfile(sys->hdr.hProfile);
+ sys->hdr.hProfile = NULL;
+
+
+ if (AToB0) cmsFreeLUT(AToB0);
+ if (BToA0) cmsFreeLUT(BToA0);
+
+ if (sys ->Prelinearization[0])
+ cmsFreeGammaTriple(sys -> Prelinearization);
+
+ return true;
+}
diff --git a/src/libs/lprof/cmsoutl.cpp b/src/libs/lprof/cmsoutl.cpp
new file mode 100644
index 00000000..248aaa04
--- /dev/null
+++ b/src/libs/lprof/cmsoutl.cpp
@@ -0,0 +1,284 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.09a */
+/* */
+/* Incremental Interpolator */
+
+#include "lcmsprf.h"
+
+
+/* Res points to a result in XYZ or Lab */
+
+BOOL cdecl cmsxRegressionInterpolatorRGB(LPMEASUREMENT m,
+ int ColorSpace,
+ int RegressionTerms,
+ BOOL lUseLocalPatches,
+ int MinPatchesToCollect,
+ double r, double g, double b,
+ void* Res);
+
+
+/* -------------------------------------------------------------- Implementation */
+
+/* #define DEBUG 1 */
+
+/* Estimate regression matrix */
+static
+void EstimateRegression(LPMEASUREMENT m, double r, double g, double b,
+ int ColorSpace,
+ LPMATN* ptfm,
+ int nterms,
+ BOOL lIncludeAllPatches,
+ int MinPatchesToCollect)
+{
+ int nCollected;
+ MLRSTATISTICS maxAns;
+ int ToCollect;
+ SETOFPATCHES collected = cmsxPCollBuildSet(m, false);
+ SETOFPATCHES allowed = cmsxPCollBuildSet(m, true);
+ BOOL rc;
+ BOOL lPatchesExhausted = false;
+
+
+ CopyMemory(allowed, m -> Allowed, m->nPatches*sizeof(BOOL));
+
+ *ptfm = NULL;
+
+ ToCollect = max(MinPatchesToCollect, (nterms + 1));
+
+ do {
+
+ if (lIncludeAllPatches) {
+
+ CopyMemory(collected, allowed, m->nPatches*sizeof(BOOL));
+ lPatchesExhausted = true;
+ ToCollect = nCollected = m->nPatches;
+ }
+ else
+ {
+
+ nCollected = cmsxPCollPatchesNearRGB(m, m -> Allowed,
+ r, g, b,
+ ToCollect, collected);
+
+ if (nCollected < ToCollect) { /* No more patches available */
+ lPatchesExhausted = true;
+ }
+ else {
+ ToCollect = nCollected + 1; /* Start from here in next iteration */
+ }
+ }
+
+ /* We are going always 3 -> 3 for now.... */
+ rc = cmsxRegressionCreateMatrix(m, collected, nterms, ColorSpace, ptfm, &maxAns);
+
+
+ /* Does fit? */
+ if ((rc == false) || maxAns.R2adj < 0.95 || maxAns.R2adj > 1.0) {
+
+ maxAns.R2adj = -100; /* No, repeat */
+ }
+
+
+ } while (!lPatchesExhausted && maxAns.R2adj < 0.95);
+
+#ifdef DEBUG
+ printf("R2adj: %g, F: %g\n", maxAns.R2adj, maxAns.F);
+#endif
+
+ free(collected);
+ free(allowed);
+}
+
+
+
+BOOL cmsxRegressionInterpolatorRGB(LPMEASUREMENT m,
+ int ColorSpace,
+ int RegressionTerms,
+ BOOL lUseLocalPatches,
+ int MinPatchesToCollect,
+ double r, double g, double b,
+ void* Res)
+{
+ LPMATN tfm = NULL;
+
+
+ EstimateRegression(m, r, g, b, ColorSpace, &tfm, RegressionTerms,
+ !lUseLocalPatches, MinPatchesToCollect);
+
+ if (tfm == NULL) return false;
+
+ switch (ColorSpace) {
+
+ case PT_Lab:
+
+ if (!cmsxRegressionRGB2Lab(r, g, b, tfm, (LPcmsCIELab) Res)) return false;
+ break;
+
+ case PT_XYZ:
+ if (!cmsxRegressionRGB2XYZ(r, g, b, tfm, (LPcmsCIEXYZ) Res)) return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ MATNfree(tfm);
+
+
+#ifdef DEBUG
+ printf("INTERPOLATED RGB %g,%g,%g Lab %g, %g, %g \n", r , g, b,
+ Lab->L, Lab->a, Lab->b);
+
+#endif
+ return true;
+}
+
+
+/* Check the results of a given regression matrix */
+
+static
+void CheckOneRegressionMatrix(LPPROFILERCOMMONDATA hdr, LPMATN Matrix,
+ double* Mean, double* Std, double* Max)
+{
+
+ cmsCIELab Lab;
+ cmsCIEXYZ XYZ;
+ double Hit, sum, sum2, n, dE;
+ int i;
+ cmsCIEXYZ D50;
+
+
+ D50.X = cmsD50_XYZ() -> X* 100.;
+ D50.Y = cmsD50_XYZ() -> Y* 100.;
+ D50.Z = cmsD50_XYZ() -> Z* 100.;
+
+ Hit = sum = sum2 = n = 0;
+ for (i=0; i < hdr -> m.nPatches; i++) {
+
+ if (hdr -> m.Allowed[i]) {
+
+ LPPATCH p = hdr -> m.Patches + i;
+
+ if (hdr -> PCSType == PT_Lab) {
+
+ WORD ProfileLabEncoded[3];
+
+ cmsxRegressionRGB2Lab(p -> Colorant.RGB[0],
+ p -> Colorant.RGB[1],
+ p -> Colorant.RGB[2],
+ Matrix, &Lab);
+
+ cmsFloat2LabEncoded(ProfileLabEncoded, &Lab);
+ cmsLabEncoded2Float(&Lab, ProfileLabEncoded);
+
+ dE = cmsDeltaE(&Lab, &p ->Lab);
+ }
+ else {
+ cmsCIELab Lab2;
+
+ cmsxRegressionRGB2XYZ(p -> Colorant.RGB[0],
+ p -> Colorant.RGB[1],
+ p -> Colorant.RGB[2],
+ Matrix, &XYZ);
+ _cmsxClampXYZ100(&XYZ);
+
+ cmsXYZ2Lab(&D50, &Lab, &XYZ);
+ cmsXYZ2Lab(&D50, &Lab2, &p ->XYZ);
+
+ dE = cmsDeltaE(&Lab, &Lab2);
+ }
+
+
+ if (dE > Hit)
+ Hit = dE;
+
+ sum += dE;
+ sum2 += dE * dE;
+ n = n + 1;
+
+ }
+ }
+
+ *Mean = sum / n;
+ *Std = sqrt((n * sum2 - sum * sum) / (n*(n-1)));
+ *Max = Hit;
+
+}
+
+
+/* Trial-and-error in order to get best number of terms. */
+
+int cmsxFindOptimumNumOfTerms(LPPROFILERCOMMONDATA hdr, int nMaxTerms, BOOL* lAllOk)
+{
+ int i, BestTerms;
+ BOOL rc;
+ LPMATN Matrix = NULL;
+ MLRSTATISTICS Stat;
+ double dEmean, dEStd, dEHit, Best;
+ BOOL lOneFound;
+
+
+ BestTerms = 4;
+ Best = 1000.;
+ lOneFound = false;
+
+ for (i=4; i <= nMaxTerms; i++) { /* 55 */
+
+ rc = cmsxRegressionCreateMatrix(&hdr -> m, hdr -> m.Allowed,
+ i, hdr -> PCSType, &Matrix, &Stat);
+
+ if (rc && Stat.R2adj < 1 && Stat.R2adj > 0.6) {
+
+ CheckOneRegressionMatrix(hdr, Matrix, &dEmean, &dEStd, &dEHit);
+
+ if (dEStd < Best && dEHit < 50.) {
+
+ Best = dEStd;
+ BestTerms = i;
+ lOneFound = true;
+ }
+
+ }
+ MATNfree(Matrix);
+ Matrix = NULL;
+ }
+
+ *lAllOk = lOneFound;
+
+ return BestTerms;
+}
+
+
diff --git a/src/libs/lprof/cmspcoll.cpp b/src/libs/lprof/cmspcoll.cpp
new file mode 100644
index 00000000..0b03fd5a
--- /dev/null
+++ b/src/libs/lprof/cmspcoll.cpp
@@ -0,0 +1,1045 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.08a */
+
+
+#include "lcmsprf.h"
+
+
+/* ----------------------------------------------------------------- Patch collections */
+
+BOOL cdecl cmsxPCollLoadFromSheet(LPMEASUREMENT m, LCMSHANDLE hSheet);
+
+BOOL cdecl cmsxPCollBuildMeasurement(LPMEASUREMENT m,
+ const char *ReferenceSheet,
+ const char *MeasurementSheet,
+ DWORD dwNeededSamplesType);
+
+LPPATCH cdecl cmsxPCollGetPatch(LPMEASUREMENT m, int n);
+
+LPPATCH cdecl cmsxPCollGetPatchByName(LPMEASUREMENT m, const char* Name, int* lpPos);
+LPPATCH cdecl cmsxPCollGetPatchByPos(LPMEASUREMENT m, int row, int col);
+LPPATCH cdecl cmsxPCollAddPatchRGB(LPMEASUREMENT m, const char *Name,
+ double r, double g, double b,
+ LPcmsCIEXYZ XYZ, LPcmsCIELab Lab);
+
+/* Sets of patches */
+
+SETOFPATCHES cdecl cmsxPCollBuildSet(LPMEASUREMENT m, BOOL lDefault);
+int cdecl cmsxPCollCountSet(LPMEASUREMENT m, SETOFPATCHES Set);
+BOOL cdecl cmsxPCollValidatePatches(LPMEASUREMENT m, DWORD dwFlags);
+
+
+/* Collect "need" patches of the specific kind, return the number of collected (that */
+/* could be less if set of patches is exhausted) */
+
+
+void cdecl cmsxPCollPatchesGS(LPMEASUREMENT m, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesNearRGB(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double r, double g, double b, int need, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesNearNeutral(LPMEASUREMENT m, SETOFPATCHES Valids,
+ int need, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesNearPrimary(LPMEASUREMENT m, SETOFPATCHES Valids,
+ int nChannel, int need, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double Lmin, double LMax, double a, double b, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesInGamutLUT(LPMEASUREMENT m, SETOFPATCHES Valids,
+ LPLUT Gamut, SETOFPATCHES Result);
+
+LPPATCH cdecl cmsxPCollFindWhite(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance);
+LPPATCH cdecl cmsxPCollFindBlack(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance);
+LPPATCH cdecl cmsxPCollFindPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, int Channel, double* Distance);
+
+
+/* ------------------------------------------------------------- Implementation */
+
+#define IS(x) EqualsTo(c, x)
+
+/* A wrapper on stricmp() */
+
+static
+BOOL EqualsTo(const char* a, const char *b)
+{
+ return (stricmp(a, b) == 0);
+}
+
+
+/* Does return a bitwise mask holding the measurements contained in a Sheet */
+
+static
+DWORD MaskOfDataSet(LCMSHANDLE hSheet)
+{
+ char** Names;
+ int i, n;
+ DWORD dwMask = 0;
+
+ n = cmsxIT8EnumDataFormat(hSheet, &Names);
+
+ for (i=0; i < n; i++) {
+
+ char *c = Names[i];
+
+ if (IS("RGB_R") || IS("RGB_G") || IS("RGB_B"))
+ dwMask |= PATCH_HAS_RGB;
+ else
+ if (IS("XYZ_X") || IS("XYZ_Y") || IS("XYZ_Z"))
+ dwMask |= PATCH_HAS_XYZ;
+ else
+ if (IS("LAB_L") || IS("LAB_A") ||IS("LAB_B"))
+ dwMask |= PATCH_HAS_Lab;
+ else
+ if (IS("STDEV_DE"))
+ dwMask |= PATCH_HAS_STD_DE;
+ }
+
+ return dwMask;
+}
+
+
+/* Addition of a patch programatically */
+
+LPPATCH cmsxPCollAddPatchRGB(LPMEASUREMENT m, const char *Name,
+ double r, double g, double b,
+ LPcmsCIEXYZ XYZ, LPcmsCIELab Lab)
+{
+ LPPATCH p;
+
+ p = m->Patches + m->nPatches++;
+
+ strcpy(p -> Name, Name);
+
+ p -> Colorant.RGB[0] = r;
+ p -> Colorant.RGB[1] = g;
+ p -> Colorant.RGB[2] = b;
+ p -> dwFlags = PATCH_HAS_RGB;
+
+ if (XYZ) {
+
+ p -> XYZ = *XYZ;
+ p -> dwFlags |= PATCH_HAS_XYZ;
+ }
+
+ if (Lab) {
+ p -> Lab = *Lab;
+ p -> dwFlags |= PATCH_HAS_Lab;
+ }
+
+
+ return p;
+}
+
+/* Some vendors does store colorant data in a non-standard way, */
+/* i.e, from 0.0..1.0 or from 0.0..100.0 This routine tries to */
+/* detect such situations */
+
+static
+void NormalizeColorant(LPMEASUREMENT m)
+{
+ int i, j;
+ double MaxColorant=0;
+ double Normalize;
+
+
+ for (i=0; i < m -> nPatches; i++) {
+
+
+ LPPATCH p = m -> Patches + i;
+
+ for (j=0; j < MAXCHANNELS; j++) {
+ if (p ->Colorant.Hexa[j] > MaxColorant)
+ MaxColorant = p ->Colorant.Hexa[j];
+ }
+ }
+
+ /* Ok, some heuristics */
+
+ if (MaxColorant < 2)
+ Normalize = 255.0; /* goes 0..1 */
+ else
+ if (MaxColorant < 102)
+ Normalize = 2.55; /* goes 0..100 */
+ else
+ if (MaxColorant > 300)
+ Normalize = (255.0 / 65535.0); /* Goes 0..65535.0 */
+ else
+ return; /* Is ok */
+
+
+ /* Rearrange patches */
+ for (i=0; i < m -> nPatches; i++) {
+
+
+ LPPATCH p = m -> Patches + i;
+ for (j=0; j < MAXCHANNELS; j++)
+ p ->Colorant.Hexa[j] *= Normalize;
+ }
+
+}
+
+
+/* Load a collection from a Sheet */
+
+BOOL cmsxPCollLoadFromSheet(LPMEASUREMENT m, LCMSHANDLE hSheet)
+{
+ int i;
+ DWORD dwMask;
+
+
+ if (m -> nPatches == 0) {
+
+ m -> nPatches = (int) cmsxIT8GetPropertyDbl(hSheet, "NUMBER_OF_SETS");
+ m -> Patches = (PATCH*)calloc(m -> nPatches, sizeof(PATCH)); // C->C++ : cast
+
+ if (m -> Patches == NULL) {
+ cmsxIT8Free(hSheet);
+ return false;
+ }
+
+ for (i=0; i < m -> nPatches; i++) {
+
+ LPPATCH p = m -> Patches + i;
+ p -> dwFlags = 0;
+ cmsxIT8GetPatchName(hSheet, i, p ->Name);
+
+ }
+
+ }
+
+
+ /* Build mask according to data format */
+
+ dwMask = MaskOfDataSet(hSheet);
+
+
+ /* Read items. Set flags accordly. */
+ for (i = 0; i < m->nPatches; i++) {
+
+ LPPATCH Patch = m -> Patches + i;
+
+ /* Fill in data according to mask */
+
+ if (dwMask & PATCH_HAS_Lab) {
+
+ if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "LAB_L", &Patch -> Lab.L) &&
+ cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "LAB_A", &Patch -> Lab.a) &&
+ cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "LAB_B", &Patch -> Lab.b))
+
+ Patch -> dwFlags |= PATCH_HAS_Lab;
+ }
+
+ if (dwMask & PATCH_HAS_XYZ) {
+
+ if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "XYZ_X", &Patch -> XYZ.X) &&
+ cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "XYZ_Y", &Patch -> XYZ.Y) &&
+ cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "XYZ_Z", &Patch -> XYZ.Z))
+
+ Patch -> dwFlags |= PATCH_HAS_XYZ;
+
+ }
+
+ if (dwMask & PATCH_HAS_RGB) {
+
+ if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "RGB_R", &Patch -> Colorant.RGB[0]) &&
+ cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "RGB_G", &Patch -> Colorant.RGB[1]) &&
+ cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "RGB_B", &Patch -> Colorant.RGB[2]))
+
+ Patch -> dwFlags |= PATCH_HAS_RGB;
+ }
+
+ if (dwMask & PATCH_HAS_STD_DE) {
+
+ if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "STDEV_DE", &Patch -> dEStd))
+
+ Patch -> dwFlags |= PATCH_HAS_STD_DE;
+
+ }
+
+ }
+
+ NormalizeColorant(m);
+ return true;
+}
+
+
+/* Does save parameters to a empty sheet */
+
+BOOL cmsxPCollSaveToSheet(LPMEASUREMENT m, LCMSHANDLE it8)
+{
+ int nNumberOfSets = cmsxPCollCountSet(m, m->Allowed);
+ int nNumberOfFields = 0;
+ DWORD dwMask = 0;
+ int i;
+
+ /* Find mask of fields */
+ for (i=0; i < m ->nPatches; i++) {
+ if (m ->Allowed[i]) {
+
+ LPPATCH p = m ->Patches + i;
+ dwMask |= p ->dwFlags;
+ }
+ }
+
+ nNumberOfFields = 1; /* SampleID */
+
+ if (dwMask & PATCH_HAS_RGB)
+ nNumberOfFields += 3;
+
+ if (dwMask & PATCH_HAS_XYZ)
+ nNumberOfFields += 3;
+
+ if (dwMask & PATCH_HAS_Lab)
+ nNumberOfFields += 3;
+
+
+ cmsxIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", nNumberOfSets);
+ cmsxIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", nNumberOfFields);
+
+ nNumberOfFields = 0;
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "SAMPLE_ID");
+
+ if (dwMask & PATCH_HAS_RGB) {
+
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "RGB_R");
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "RGB_G");
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "RGB_B");
+ }
+
+ if (dwMask & PATCH_HAS_XYZ) {
+
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "XYZ_X");
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "XYZ_Y");
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "XYZ_Z");
+
+ }
+
+
+ if (dwMask & PATCH_HAS_XYZ) {
+
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "LAB_L");
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "LAB_A");
+ cmsxIT8SetDataFormat(it8, nNumberOfFields++, "LAB_B");
+
+ }
+
+ for (i=0; i < m ->nPatches; i++) {
+ if (m ->Allowed[i]) {
+
+ LPPATCH Patch = m ->Patches + i;
+
+ cmsxIT8SetDataSet(it8, Patch->Name, "SAMPLE_ID", Patch->Name);
+
+ if (dwMask & PATCH_HAS_RGB) {
+ cmsxIT8SetDataSetDbl(it8, Patch->Name, "RGB_R", Patch ->Colorant.RGB[0]);
+ cmsxIT8SetDataSetDbl(it8, Patch->Name, "RGB_G", Patch ->Colorant.RGB[1]);
+ cmsxIT8SetDataSetDbl(it8, Patch->Name, "RGB_B", Patch ->Colorant.RGB[2]);
+ }
+
+ if (dwMask & PATCH_HAS_XYZ) {
+ cmsxIT8SetDataSetDbl(it8, Patch->Name, "XYZ_X", Patch ->XYZ.X);
+ cmsxIT8SetDataSetDbl(it8, Patch->Name, "XYZ_Y", Patch ->XYZ.Y);
+ cmsxIT8SetDataSetDbl(it8, Patch->Name, "XYZ_Z", Patch ->XYZ.Z);
+ }
+
+ if (dwMask & PATCH_HAS_Lab) {
+ cmsxIT8SetDataSetDbl(it8, Patch->Name, "LAB_L", Patch ->Lab.L);
+ cmsxIT8SetDataSetDbl(it8, Patch->Name, "LAB_A", Patch ->Lab.a);
+ cmsxIT8SetDataSetDbl(it8, Patch->Name, "LAB_B", Patch ->Lab.b);
+
+ }
+ }
+ }
+
+ return true;
+}
+
+static
+void FixLabOnly(LPMEASUREMENT m)
+{
+ int i;
+
+ for (i=0; i < m ->nPatches; i++) {
+
+ LPPATCH p = m ->Patches + i;
+ if ((p ->dwFlags & PATCH_HAS_Lab) &&
+ !(p ->dwFlags & PATCH_HAS_XYZ))
+ {
+ cmsLab2XYZ(cmsD50_XYZ(), &p->XYZ, &p ->Lab);
+
+ p ->XYZ.X *= 100.;
+ p ->XYZ.Y *= 100.;
+ p ->XYZ.Z *= 100.;
+
+ p ->dwFlags |= PATCH_HAS_XYZ;
+ }
+
+ }
+
+}
+
+
+/* Higher level function. Does merge reference and measurement sheet into */
+/* a MEASUREMENT struct. Data to keep is described in dwNeededSamplesType */
+/* mask as follows: */
+/* */
+/* PATCH_HAS_Lab 0x00000001 */
+/* PATCH_HAS_XYZ 0x00000002 */
+/* PATCH_HAS_RGB 0x00000004 */
+/* PATCH_HAS_CMY 0x00000008 */
+/* PATCH_HAS_CMYK 0x00000010 */
+/* PATCH_HAS_HEXACRM 0x00000020 */
+/* PATCH_HAS_STD_Lab 0x00010000 */
+/* PATCH_HAS_STD_XYZ 0x00020000 */
+/* PATCH_HAS_STD_RGB 0x00040000 */
+/* PATCH_HAS_STD_CMY 0x00080000 */
+/* PATCH_HAS_STD_CMYK 0x00100000 */
+/* PATCH_HAS_STD_HEXACRM 0x00100000 */
+/* PATCH_HAS_MEAN_DE 0x01000000 */
+/* PATCH_HAS_STD_DE 0x02000000 */
+/* PATCH_HAS_CHISQ 0x04000000 */
+/* */
+/* See lprof.h for further info */
+
+
+BOOL cmsxPCollBuildMeasurement(LPMEASUREMENT m,
+ const char *ReferenceSheet,
+ const char *MeasurementSheet,
+ DWORD dwNeededSamplesType)
+{
+ LCMSHANDLE hSheet;
+ BOOL rc = true;
+
+ ZeroMemory(m, sizeof(MEASUREMENT));
+
+
+ if (ReferenceSheet != NULL && *ReferenceSheet) {
+
+ hSheet = cmsxIT8LoadFromFile(ReferenceSheet);
+ if (hSheet == NULL) return false;
+
+ rc = cmsxPCollLoadFromSheet(m, hSheet);
+ cmsxIT8Free(hSheet);
+ }
+
+ if (!rc) return false;
+
+ if (MeasurementSheet != NULL && *MeasurementSheet) {
+
+ hSheet = cmsxIT8LoadFromFile(MeasurementSheet);
+ if (hSheet == NULL) return false;
+
+ rc = cmsxPCollLoadFromSheet(m, hSheet);
+ cmsxIT8Free(hSheet);
+ }
+
+ if (!rc) return false;
+
+
+ /* Fix up -- If only Lab is present, then compute */
+ /* XYZ based on D50 */
+
+ FixLabOnly(m);
+
+ cmsxPCollValidatePatches(m, dwNeededSamplesType);
+ return true;
+}
+
+
+
+void cmsxPCollFreeMeasurements(LPMEASUREMENT m)
+{
+ if (m->Patches)
+ free(m->Patches);
+
+ m->Patches = NULL;
+ m->nPatches = 0;
+
+ if (m -> Allowed)
+ free(m -> Allowed);
+
+}
+
+/* Retrieval functions */
+
+LPPATCH cmsxPCollGetPatchByName(LPMEASUREMENT m, const char* name, int* lpPos)
+{
+ int i;
+ for (i=0; i < m->nPatches; i++)
+ {
+ if (m -> Allowed)
+ if (!m -> Allowed[i])
+ continue;
+
+ if (EqualsTo(m->Patches[i].Name, name)) {
+ if (lpPos) *lpPos = i;
+ return m->Patches + i;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+
+/* -------------------------------------------------------------------- Sets */
+
+
+SETOFPATCHES cmsxPCollBuildSet(LPMEASUREMENT m, BOOL lDefault)
+{
+ SETOFPATCHES Full = (SETOFPATCHES) malloc(m -> nPatches * sizeof(BOOL));
+ int i;
+
+ for (i=0; i < m -> nPatches; i++)
+ Full[i] = lDefault;
+
+ return Full;
+}
+
+int cmsxPCollCountSet(LPMEASUREMENT m, SETOFPATCHES Set)
+{
+ int i, Count = 0;
+
+ for (i = 0; i < m -> nPatches; i++) {
+
+ if (Set[i])
+ Count++;
+ }
+
+ return Count;
+}
+
+
+/* Validate patches */
+
+BOOL cmsxPCollValidatePatches(LPMEASUREMENT m, DWORD dwFlags)
+{
+ int i, n;
+
+ if (m->Allowed)
+ free(m->Allowed);
+
+ m -> Allowed = cmsxPCollBuildSet(m, true);
+
+ /* Check for flags */
+ for (i=n=0; i < m -> nPatches; i++) {
+
+ LPPATCH p = m -> Patches + i;
+ m -> Allowed[i] = ((p -> dwFlags & dwFlags) == dwFlags);
+
+ }
+
+ return true;
+}
+
+
+/* Several filters */
+
+
+/* This filter does validate patches placed on 'radius' distance of a */
+/* device-color space. Currently only RGB is supported */
+
+static
+void PatchesByRGB(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double R, double G, double B, double radius, SETOFPATCHES Result)
+{
+ int i;
+ double ra, rmax = sqrt(radius / 255.);
+ double dR, dG, dB;
+ LPPATCH p;
+
+ for (i=0; i < m->nPatches; i++) {
+
+
+ if (Valids[i]) {
+
+ p = m->Patches + i;
+
+ dR = fabs(R - p -> Colorant.RGB[0]) / 255.;
+ dG = fabs(G - p -> Colorant.RGB[1]) / 255.;
+ dB = fabs(B - p -> Colorant.RGB[2]) / 255.;
+
+ ra = sqrt(dR*dR + dG*dG + dB*dB);
+
+ if (ra <= rmax)
+ Result[i] = true;
+ else
+ Result[i] = false;
+
+ }
+ }
+
+}
+
+
+/* This filter does validate patches placed at dEmax radius */
+/* in the device-independent side. */
+
+static
+void PatchesByLab(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double L, double a, double b, double dEmax, SETOFPATCHES Result)
+{
+ int i;
+ double dE, dEMaxSQR = sqrt(dEmax);
+ double dL, da, db;
+ LPPATCH p;
+
+
+ for (i=0; i < m->nPatches; i++) {
+
+
+ if (Valids[i]) {
+
+ p = m->Patches + i;
+
+ dL = fabs(L - p -> Lab.L);
+ da = fabs(a - p -> Lab.a);
+ db = fabs(b - p -> Lab.b);
+
+ dE = sqrt(dL*dL + da*da + db*db);
+
+ if (dE <= dEMaxSQR)
+ Result[i] = true;
+ else
+ Result[i] = false;
+ }
+ }
+}
+
+
+/* Restrict Lab in a cube of variable sides. Quick and dirty out-of-gamut */
+/* stripper used in estimations. */
+
+static
+void PatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double Lmin, double Lmax, double da, double db, SETOFPATCHES Result)
+{
+ int i;
+
+ for (i=0; i < m -> nPatches; i++) {
+
+
+ if (Valids[i]) {
+
+ LPPATCH p = m -> Patches + i;
+
+ if ((p->Lab.L >= Lmin && p->Lab.L <= Lmax) &&
+ (fabs(p -> Lab.a) < da) &&
+ (fabs(p -> Lab.b) < db))
+
+ Result[i] = true;
+ else
+ Result[i] = false;
+ }
+ }
+
+}
+
+/* Restrict to low colorfullness */
+
+static
+void PatchesOfLowC(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double Cmax, SETOFPATCHES Result)
+{
+ int i;
+ cmsCIELCh LCh;
+
+ for (i=0; i < m -> nPatches; i++) {
+
+
+ if (Valids[i]) {
+
+ LPPATCH p = m -> Patches + i;
+
+ cmsLab2LCh(&LCh, &p->Lab);
+
+
+ if (LCh.C < Cmax)
+ Result[i] = true;
+ else
+ Result[i] = false;
+ }
+ }
+
+}
+
+
+
+/* Primary can be -1 for specifying device gray. Does return patches */
+/* on device-space Colorants. dEMax is the maximum allowed ratio */
+
+static
+void PatchesPrimary(LPMEASUREMENT m, SETOFPATCHES Valids,
+ int nColorant, double dEMax, SETOFPATCHES Result)
+{
+ int i, j;
+ double n, dE;
+
+ for (i=0; i < m -> nPatches; i++) {
+
+
+ if (Valids[i]) {
+
+ LPPATCH p = m -> Patches + i;
+
+
+ if (nColorant < 0) /* device-grey? */
+ {
+ /* cross. */
+
+ double drg = fabs(p -> Colorant.RGB[0] - p -> Colorant.RGB[1]) / 255.;
+ double drb = fabs(p -> Colorant.RGB[0] - p -> Colorant.RGB[2]) / 255.;
+ double dbg = fabs(p -> Colorant.RGB[1] - p -> Colorant.RGB[2]) / 255.;
+
+ dE = (drg*drg + drb*drb + dbg*dbg);
+
+
+ }
+ else {
+ dE = 0.;
+ for (j=0; j < 3; j++) {
+
+ if (j != nColorant) {
+
+ n = p -> Colorant.RGB[j] / 255.;
+ dE += (n * n);
+
+
+ }
+ }
+ }
+
+
+
+ if (sqrt(dE) < dEMax)
+ Result[i] = true;
+ else
+ Result[i] = false;
+ }
+ }
+
+}
+
+
+/* The high level extractors ----------------------------------------------------- */
+
+int cmsxPCollPatchesNearRGB(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double r, double g, double b,
+ int need, SETOFPATCHES Result)
+{
+ double radius;
+ int nCollected;
+
+ /* Collect points inside of a sphere or radius 'radius' by RGB */
+
+ radius = 1;
+ do {
+ PatchesByRGB(m, Valids, r, g, b, radius, Result);
+
+ nCollected = cmsxPCollCountSet(m, Result);
+ if (nCollected <= need) {
+
+ radius += 1.0;
+ }
+
+ } while (nCollected <= need && radius < 256.);
+
+ return nCollected; /* Can be less than needed! */
+}
+
+
+int cmsxPCollPatchesNearNeutral(LPMEASUREMENT m, SETOFPATCHES Valids,
+ int need, SETOFPATCHES Result)
+{
+ int nGrays;
+ double Cmax;
+
+ Cmax = 1.;
+ do {
+
+
+ PatchesOfLowC(m, Valids, Cmax, Result);
+
+ nGrays = cmsxPCollCountSet(m, Result);
+ if (nGrays <= need) {
+
+ Cmax += .2;
+ }
+
+ } while (nGrays <= need && Cmax < 10.);
+
+ return nGrays;
+}
+
+
+int cmsxPCollPatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double Lmin, double Lmax, double a, double b,
+ SETOFPATCHES Result)
+
+
+{
+ PatchesInLabCube(m, Valids, Lmin, Lmax, a, b, Result);
+ return cmsxPCollCountSet(m, Result);
+}
+
+
+
+
+int cmsxPCollPatchesNearPrimary(LPMEASUREMENT m,
+ SETOFPATCHES Valids,
+ int nChannel,
+ int need,
+ SETOFPATCHES Result)
+{
+ double radius;
+ int nCollected;
+
+ /* Collect points inside of a sphere or radius 'radius' by RGB */
+
+ radius = 0.05;
+ do {
+ PatchesPrimary(m, Valids, nChannel, radius, Result);
+
+ nCollected = cmsxPCollCountSet(m, Result);
+ if (nCollected <= need) {
+
+ radius += 0.01;
+ }
+
+ } while (nCollected <= need && radius < 256.);
+
+ return nCollected;
+
+}
+
+
+static
+void AddOneGray(LPMEASUREMENT m, int n, SETOFPATCHES Grays)
+{
+ LPPATCH p;
+ char Buffer[cmsxIT8_GRAYCOLS];
+ int pos;
+
+ if (n == 0) strcpy(Buffer, "DMIN");
+ else
+ if (n == cmsxIT8_GRAYCOLS - 1) strcpy(Buffer, "DMAX");
+ else
+ sprintf(Buffer, "GS%d", n);
+
+ p = cmsxPCollGetPatchByName(m, Buffer, &pos);
+
+ if (p)
+ Grays[pos] = true;
+}
+
+
+
+void cmsxPCollPatchesGS(LPMEASUREMENT m, SETOFPATCHES Result)
+{
+
+ int i;
+
+ for (i=0; i < cmsxIT8_GRAYCOLS; i++)
+ AddOneGray(m, i, Result);
+}
+
+
+
+/* Refresh RGB of all patches after prelinearization */
+
+void cmsxPCollLinearizePatches(LPMEASUREMENT m, SETOFPATCHES Valids, LPGAMMATABLE Gamma[3])
+{
+ int i;
+
+ for (i=0; i < m -> nPatches; i++) {
+
+ if (Valids[i]) {
+
+ LPPATCH p = m -> Patches + i;
+
+ cmsxApplyLinearizationTable(p -> Colorant.RGB, Gamma, p -> Colorant.RGB);
+ }
+ }
+
+}
+
+
+int cmsxPCollPatchesInGamutLUT(LPMEASUREMENT m, SETOFPATCHES Valids,
+ LPLUT Gamut, SETOFPATCHES Result)
+{
+ int i;
+ int nCollected = 0;
+
+ for (i=0; i < m -> nPatches; i++) {
+
+ if (Valids[i]) {
+
+ LPPATCH p = m -> Patches + i;
+ WORD EncodedLab[3];
+ WORD dE;
+
+ cmsFloat2LabEncoded(EncodedLab, &p->Lab);
+ cmsEvalLUT(Gamut, EncodedLab, &dE);
+ Result[i] = (dE < 2) ? true : false;
+ if (Result[i]) nCollected++;
+ }
+ }
+
+ return nCollected;
+}
+
+LPPATCH cmsxPCollFindWhite(LPMEASUREMENT m, SETOFPATCHES Valids, double* TheDistance)
+{
+ int i;
+ LPPATCH Candidate = NULL;
+ double Distance, CandidateDistance = 255;
+ double dR, dG, dB;
+
+ Candidate = cmsxPCollGetPatchByName(m, "DMIN", NULL);
+ if (Candidate) {
+
+ if (TheDistance) *TheDistance = 0.0;
+ return Candidate;
+ }
+
+ for (i=0; i < m -> nPatches; i++) {
+
+ if (Valids[i]) {
+
+ LPPATCH p = m -> Patches + i;
+
+ dR = fabs(255.0 - p -> Colorant.RGB[0]) / 255.0;
+ dG = fabs(255.0 - p -> Colorant.RGB[1]) / 255.0;
+ dB = fabs(255.0 - p -> Colorant.RGB[2]) / 255.0;
+
+ Distance = sqrt(dR*dR + dG*dG + dB*dB);
+
+ if (Distance < CandidateDistance) {
+ Candidate = p;
+ CandidateDistance = Distance;
+ }
+ }
+ }
+
+ if (TheDistance)
+ *TheDistance = floor(CandidateDistance * 255.0 + .5);
+
+ return Candidate;
+}
+
+LPPATCH cmsxPCollFindBlack(LPMEASUREMENT m, SETOFPATCHES Valids, double* TheDistance)
+{
+ int i;
+ LPPATCH Candidate = NULL;
+ double Distance, CandidateDistance = 255;
+ double dR, dG, dB;
+
+
+ Candidate = cmsxPCollGetPatchByName(m, "DMAX", NULL);
+ if (Candidate) {
+
+ if (TheDistance) *TheDistance = 0.0;
+ return Candidate;
+ }
+
+ for (i=0; i < m -> nPatches; i++) {
+
+ if (Valids[i]) {
+
+ LPPATCH p = m -> Patches + i;
+
+ dR = (p -> Colorant.RGB[0]) / 255.0;
+ dG = (p -> Colorant.RGB[1]) / 255.0;
+ dB = (p -> Colorant.RGB[2]) / 255.0;
+
+ Distance = sqrt(dR*dR + dG*dG + dB*dB);
+
+ if (Distance < CandidateDistance) {
+ Candidate = p;
+ CandidateDistance = Distance;
+ }
+ }
+ }
+
+ if (TheDistance)
+ *TheDistance = floor(CandidateDistance * 255.0 + .5);
+
+ return Candidate;
+}
+
+
+LPPATCH cmsxPCollFindPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, int Channel, double* TheDistance)
+{
+ int i;
+ LPPATCH Candidate = NULL;
+ double Distance, CandidateDistance = 255;
+ double dR, dG, dB;
+ const struct {
+ double r, g, b;
+
+ } RGBPrimaries[3] = {
+ { 255.0, 0, 0},
+ { 0, 255.0, 0},
+ { 0, 0, 255 }};
+
+
+ for (i=0; i < m -> nPatches; i++) {
+
+ if (Valids[i]) {
+
+ LPPATCH p = m -> Patches + i;
+
+ dR = fabs(RGBPrimaries[Channel].r - p -> Colorant.RGB[0]) / 255.0;
+ dG = fabs(RGBPrimaries[Channel].g - p -> Colorant.RGB[1]) / 255.0;
+ dB = fabs(RGBPrimaries[Channel].b - p -> Colorant.RGB[2]) / 255.0;
+
+ Distance = sqrt(dR*dR + dG*dG + dB*dB);
+
+ if (Distance < CandidateDistance) {
+ Candidate = p;
+ CandidateDistance = Distance;
+ }
+ }
+ }
+
+ if (TheDistance)
+ *TheDistance = floor(CandidateDistance * 255.0 + .5);
+
+ return Candidate;
+}
diff --git a/src/libs/lprof/cmsprf.cpp b/src/libs/lprof/cmsprf.cpp
new file mode 100644
index 00000000..527fc8e8
--- /dev/null
+++ b/src/libs/lprof/cmsprf.cpp
@@ -0,0 +1,439 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* */
+/* This library is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU Lesser General Public */
+/* License as published by the Free Software Foundation; either */
+/* version 2 of the License, or (at your option) any later version. */
+/* */
+/* This library is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* Lesser General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU Lesser General Public */
+/* License along with this library; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "lcmsprf.h"
+
+
+double cdecl _cmsxSaturate65535To255(double d);
+double cdecl _cmsxSaturate255To65535(double d);
+
+
+void cdecl _cmsxClampXYZ100(LPcmsCIEXYZ xyz);
+
+
+BOOL cdecl cmsxEmbedCharTarget(LPPROFILERCOMMONDATA hdr);
+BOOL cdecl cmsxEmbedMatrixShaper(LPPROFILERCOMMONDATA hdr);
+BOOL cdecl cmsxEmbedTextualInfo(LPPROFILERCOMMONDATA hdr);
+
+/* ----------------------------------------------------------------- Implementation */
+
+
+/* Convert from 0.0..65535.0 to 0.0..255.0 */
+
+double _cmsxSaturate65535To255(double d)
+{
+ double v;
+
+ v = d / 257.0;
+
+ if (v < 0) return 0;
+ if (v > 255.0) return 255.0;
+
+ return v;
+}
+
+
+double _cmsxSaturate255To65535(double d)
+{
+ double v;
+
+ v = d * 257.0;
+
+ if (v < 0) return 0;
+ if (v > 65535.0) return 65535.0;
+
+ return v;
+}
+
+
+
+/* Cut off absurd values */
+
+void _cmsxClampXYZ100(LPcmsCIEXYZ xyz)
+{
+
+ if (xyz->X > 199.996)
+ xyz->X = 199.996;
+
+ if (xyz->Y > 199.996)
+ xyz->Y = 199.996;
+
+ if (xyz->Z > 199.996)
+ xyz->Z = 199.996;
+
+ if (xyz->Y < 0)
+ xyz->Y = 0;
+
+ if (xyz->X < 0)
+ xyz->X = 0;
+
+ if (xyz->Z < 0)
+ xyz->Z = 0;
+
+}
+
+static
+int xfilelength(int fd)
+{
+#ifdef _MSC_VER
+ return _filelength(fd);
+#else
+ struct stat sb;
+ if (fstat(fd, &sb) < 0)
+ return(-1);
+ return(sb.st_size);
+#endif
+
+
+}
+
+
+BOOL cmsxEmbedCharTarget(LPPROFILERCOMMONDATA hdr)
+{
+ LCMSHANDLE it8 = cmsxIT8Alloc();
+ LPBYTE mem;
+ size_t size, readed;
+ FILE* f;
+ BOOL lFreeOnExit = false;
+
+
+ if (!hdr->m.Patches) {
+
+ if (!hdr ->ReferenceSheet[0] && !hdr->MeasurementSheet[0]) return false;
+
+ if (cmsxPCollBuildMeasurement(&hdr ->m,
+ hdr->ReferenceSheet,
+ hdr->MeasurementSheet,
+ PATCH_HAS_RGB|PATCH_HAS_XYZ) == false) return false;
+ lFreeOnExit = true;
+
+ }
+
+ cmsxIT8SetSheetType(it8,"LCMSEMBED");
+ cmsxIT8SetProperty(it8, "ORIGINATOR", (const char *) "Little cms");
+ cmsxIT8SetProperty(it8, "DESCRIPTOR", (const char *) hdr -> Description);
+ cmsxIT8SetProperty(it8, "MANUFACTURER", (const char *) hdr ->Manufacturer);
+
+ cmsxPCollSaveToSheet(&hdr->m, it8);
+ cmsxIT8SaveToFile(it8, "TMP00.IT8");
+ cmsxIT8Free(it8);
+
+ f = fopen("TMP00.IT8", "rb");
+ size = xfilelength(fileno(f));
+ mem = (unsigned char*) malloc(size + 1); // C->C++ : fixed cast
+ readed = fread(mem, 1, size, f);
+ fclose(f);
+
+ mem[readed] = 0;
+ unlink("TMP00.IT8");
+
+ cmsAddTag(hdr->hProfile, icSigCharTargetTag, mem);
+ free(mem);
+
+ if (lFreeOnExit) {
+
+ cmsxPCollFreeMeasurements(&hdr->m);
+ }
+
+ return true;
+}
+
+
+static
+BOOL ComputeColorantMatrix(LPcmsCIEXYZTRIPLE Colorants,
+ LPcmsCIExyY WhitePoint,
+ LPcmsCIExyYTRIPLE Primaries)
+{
+ MAT3 MColorants;
+
+ if (!cmsBuildRGB2XYZtransferMatrix(&MColorants, WhitePoint, Primaries))
+ {
+ return false;
+ }
+
+
+ cmsAdaptMatrixToD50(&MColorants, WhitePoint);
+
+ Colorants->Red.X = MColorants.v[0].n[0];
+ Colorants->Red.Y = MColorants.v[1].n[0];
+ Colorants->Red.Z = MColorants.v[2].n[0];
+
+ Colorants->Green.X = MColorants.v[0].n[1];
+ Colorants->Green.Y = MColorants.v[1].n[1];
+ Colorants->Green.Z = MColorants.v[2].n[1];
+
+ Colorants->Blue.X = MColorants.v[0].n[2];
+ Colorants->Blue.Y = MColorants.v[1].n[2];
+ Colorants->Blue.Z = MColorants.v[2].n[2];
+
+ return true;
+
+}
+
+
+BOOL cmsxEmbedMatrixShaper(LPPROFILERCOMMONDATA hdr)
+{
+ cmsCIEXYZTRIPLE Colorant;
+ cmsCIExyY MediaWhite;
+
+ cmsXYZ2xyY(&MediaWhite, &hdr ->WhitePoint);
+
+ if (ComputeColorantMatrix(&Colorant, &MediaWhite, &hdr ->Primaries)) {
+
+ cmsAddTag(hdr ->hProfile, icSigRedColorantTag, &Colorant.Red);
+ cmsAddTag(hdr ->hProfile, icSigGreenColorantTag, &Colorant.Green);
+ cmsAddTag(hdr ->hProfile, icSigBlueColorantTag, &Colorant.Blue);
+ }
+
+ cmsAddTag(hdr ->hProfile, icSigRedTRCTag, hdr ->Gamma[0]);
+ cmsAddTag(hdr ->hProfile, icSigGreenTRCTag, hdr ->Gamma[1]);
+ cmsAddTag(hdr ->hProfile, icSigBlueTRCTag, hdr ->Gamma[2]);
+
+ return true;
+}
+
+
+BOOL cmsxEmbedTextualInfo(LPPROFILERCOMMONDATA hdr)
+{
+ if (*hdr ->Description)
+ cmsAddTag(hdr ->hProfile, icSigProfileDescriptionTag, hdr ->Description);
+
+ if (*hdr ->Copyright)
+ cmsAddTag(hdr ->hProfile, icSigCopyrightTag, hdr ->Copyright);
+
+ if (*hdr ->Manufacturer)
+ cmsAddTag(hdr ->hProfile, icSigDeviceMfgDescTag, hdr ->Manufacturer);
+
+ if (*hdr ->Model)
+ cmsAddTag(hdr ->hProfile, icSigDeviceModelDescTag, hdr ->Model);
+
+ return true;
+}
+
+
+
+void cmsxChromaticAdaptationAndNormalization(LPPROFILERCOMMONDATA hdr, LPcmsCIEXYZ xyz, BOOL lReverse)
+{
+
+ if (hdr->lUseCIECAM97s) {
+
+ cmsJCh JCh;
+
+ /* Let's CIECAM97s to do the adaptation to D50 */
+
+ xyz->X *= 100.;
+ xyz->Y *= 100.;
+ xyz->Z *= 100.;
+
+ _cmsxClampXYZ100(xyz);
+
+ if (lReverse) {
+ cmsCIECAM97sForward(hdr->hPCS, xyz, &JCh);
+ cmsCIECAM97sReverse(hdr->hDevice, &JCh, xyz);
+ }
+ else {
+
+ cmsCIECAM97sForward(hdr->hDevice, xyz, &JCh);
+ cmsCIECAM97sReverse(hdr->hPCS, &JCh, xyz);
+ }
+
+ _cmsxClampXYZ100(xyz);
+
+ xyz -> X /= 100.;
+ xyz -> Y /= 100.;
+ xyz -> Z /= 100.;
+
+ }
+ else {
+
+ /* Else, use Bradford */
+
+ if (lReverse) {
+ cmsAdaptToIlluminant(xyz, cmsD50_XYZ(), &hdr->WhitePoint, xyz);
+ }
+ else {
+ cmsAdaptToIlluminant(xyz, &hdr->WhitePoint, cmsD50_XYZ(), xyz);
+ }
+
+ }
+
+}
+
+
+void cmsxInitPCSViewingConditions(LPPROFILERCOMMONDATA hdr)
+{
+
+ hdr->PCS.whitePoint.X = cmsD50_XYZ()->X * 100.;
+ hdr->PCS.whitePoint.Y = cmsD50_XYZ()->Y * 100.;
+ hdr->PCS.whitePoint.Z = cmsD50_XYZ()->Z * 100.;
+
+
+ hdr->PCS.Yb = 20; /* 20% of surround */
+ hdr->PCS.La = 20; /* Adapting field luminance */
+ hdr->PCS.surround = AVG_SURROUND;
+ hdr->PCS.D_value = 1.0; /* Complete adaptation */
+
+}
+
+
+/* Build gamut hull by geometric means */
+void cmsxComputeGamutHull(LPPROFILERCOMMONDATA hdr)
+{
+ int i;
+ int x0, y0, z0;
+ int Inside, Outside, Boundaries;
+ char code;
+
+
+ hdr -> hRGBHull = cmsxHullInit();
+
+ /* For all valid patches, mark RGB knots as 0 */
+ for (i=0; i < hdr ->m.nPatches; i++) {
+
+ if (hdr ->m.Allowed[i]) {
+
+ LPPATCH p = hdr ->m.Patches + i;
+
+
+ x0 = (int) floor(p->Colorant.RGB[0] + .5);
+ y0 = (int) floor(p->Colorant.RGB[1] + .5);
+ z0 = (int) floor(p->Colorant.RGB[2] + .5);
+
+ cmsxHullAddPoint(hdr->hRGBHull, x0, y0, z0);
+ }
+ }
+
+ cmsxHullComputeHull(hdr ->hRGBHull);
+
+/* #ifdef DEBUG */
+ cmsxHullDumpVRML(hdr -> hRGBHull, "rgbhull.wrl");
+/* #endif */
+
+
+
+ /* A check */
+
+ Inside = Outside = Boundaries = 0;
+ /* For all valid patches, mark RGB knots as 0 */
+ for (i=0; i < hdr ->m.nPatches; i++) {
+
+ if (hdr ->m.Allowed[i]) {
+
+ LPPATCH p = hdr ->m.Patches + i;
+
+ x0 = (int) floor(p->Colorant.RGB[0] + .5);
+ y0 = (int) floor(p->Colorant.RGB[1] + .5);
+ z0 = (int) floor(p->Colorant.RGB[2] + .5);
+
+ code = cmsxHullCheckpoint(hdr -> hRGBHull, x0, y0, z0);
+
+ switch (code) {
+
+ case 'i': Inside++; break;
+ case 'o': Outside++; break;
+ default: Boundaries++;
+ }
+
+ }
+ }
+
+ if (hdr ->printf)
+ hdr ->printf("Gamut hull: %d inside, %d outside, %d on boundaries", Inside, Outside, Boundaries);
+
+}
+
+BOOL cmsxChoosePCS(LPPROFILERCOMMONDATA hdr)
+{
+
+ double gamma_r, gamma_g, gamma_b;
+ cmsCIExyY SourceWhite;
+
+ /* At first, compute aproximation on matrix-shaper */
+ if (!cmsxComputeMatrixShaper(hdr ->ReferenceSheet,
+ hdr ->MeasurementSheet,
+ hdr -> Medium,
+ hdr ->Gamma,
+ &hdr ->WhitePoint,
+ &hdr ->BlackPoint,
+ &hdr ->Primaries)) return false;
+
+
+
+ cmsXYZ2xyY(&SourceWhite, &hdr ->WhitePoint);
+
+ gamma_r = cmsEstimateGamma(hdr ->Gamma[0]);
+ gamma_g = cmsEstimateGamma(hdr ->Gamma[1]);
+ gamma_b = cmsEstimateGamma(hdr ->Gamma[2]);
+
+
+
+ if (gamma_r > 1.8 || gamma_g > 1.8 || gamma_b > 1.8 ||
+ gamma_r == -1 || gamma_g == -1 || gamma_b == -1) {
+
+ hdr ->PCSType = PT_Lab;
+
+ if (hdr ->printf)
+ hdr ->printf("I have chosen Lab as PCS");
+
+ }
+ else {
+
+ hdr ->PCSType = PT_XYZ;
+
+ if (hdr ->printf)
+ hdr ->printf("I have chosen XYZ as PCS");
+ }
+
+
+
+ if (hdr ->printf) {
+
+ char Buffer[256] = "Infered ";
+
+ _cmsIdentifyWhitePoint(Buffer, &hdr ->WhitePoint);
+ hdr ->printf("%s", Buffer);
+ hdr ->printf("Primaries (x-y): [Red: %2.2f, %2.2f] [Green: %2.2f, %2.2f] [Blue: %2.2f, %2.2f]",
+ hdr ->Primaries.Red.x, hdr ->Primaries.Red.y,
+ hdr ->Primaries.Green.x, hdr ->Primaries.Green.y,
+ hdr ->Primaries.Blue.x, hdr ->Primaries.Blue.y);
+
+ if ((gamma_r != -1) && (gamma_g != -1) && (gamma_b != -1)) {
+
+ hdr ->printf("Estimated gamma: [Red: %2.2f] [Green: %2.2f] [Blue: %2.2f]",
+ gamma_r, gamma_g, gamma_b);
+ }
+
+
+ }
+
+
+
+ return true;
+}
diff --git a/src/libs/lprof/cmsreg.cpp b/src/libs/lprof/cmsreg.cpp
new file mode 100644
index 00000000..4e66a9ef
--- /dev/null
+++ b/src/libs/lprof/cmsreg.cpp
@@ -0,0 +1,558 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.09a */
+
+
+#include "lcmsprf.h"
+
+
+/* There are three kinds of lies: */
+/* */
+/* * lies */
+/* * damn lies */
+/* * statistics */
+/* */
+/* -Some Wag */
+/* */
+/* */
+/* This module handles multiple linear regression stuff */
+
+
+
+/* A measurement of error
+
+typedef struct {
+
+ double SSE; // The error sum of squares
+ double MSE; // The error mean sum of squares
+ double SSR; // The regression sum of squares
+ double MSR; // The regression mean sum of squares
+ double SSTO; // Total sum of squares
+ double F; // The Fisher-F value (MSR / MSE)
+ double R2; // Proportion of variability explained by the regression
+ // (root is Pearson correlation coefficient)
+
+ double R2adj; // The adjusted coefficient of multiple determination.
+ // R2-adjusted or R2adj. This is calculated as
+ // R2adj = 1 - (1-R2)(N-n-1)/(N-1)
+ // and used as multiple correlation coefficient
+ // (really, it should be square root)
+
+ } MLRSTATISTICS, FAR* LPMLRSTATISTICS;
+
+*/
+
+
+int cdecl cmsxRegressionCreateMatrix(LPMEASUREMENT m, SETOFPATCHES Allowed, int nterms,
+ int ColorSpace,
+ LPMATN* lpMat, LPMLRSTATISTICS Stat);
+
+BOOL cdecl cmsxRegressionRGB2Lab(double r, double g, double b,
+ LPMATN tfm, LPcmsCIELab Lab);
+
+BOOL cdecl cmsxRegressionRGB2XYZ(double r, double g, double b,
+ LPMATN tfm, LPcmsCIEXYZ XYZ);
+
+
+/* -------------------------------------------------------------- Implementation */
+
+/* #define DEBUG 1 */
+
+
+/* Multiple linear regression. Also keep track of error. */
+/* Returns false if something goes wrong, or true if all Ok. */
+
+static
+BOOL MultipleLinearRegression(const LPMATN xi, /* Dependent variable */
+ const LPMATN y, /* Independent variable */
+ int nvar, /* Number of samples */
+ int npar, /* Number of parameters (terms) */
+ double* coeff, /* Returned coefficients */
+ LPMATN vcb, /* Variance-covariance array */
+ double *tvl, /* T-Values */
+ LPMLRSTATISTICS ans) /* The returned statistics */
+{
+ LPMATN bt, xt, a, xy, yt, b;
+ double sum;
+ LPMATN temp1, temp2;
+ int i;
+
+
+ /* |xt| = |xi| T */
+ xt = MATNtranspose(xi);
+ if (xt == NULL) return false;
+
+
+ /* |a| = |xt|* |xi| */
+ a = MATNmult(xt, xi);
+ if (a == NULL) return false;
+
+
+ /* |xy| = |xy| * |y| */
+ xy = MATNmult (xt, y);
+ if (xy == NULL) return false;
+
+
+ /* solve system |a|*|xy| = 0 */
+ if (!MATNsolve(a, xy)) return false;
+
+ /* b will hold coefficients */
+ b = MATNalloc (xy->Rows, 1);
+ if (b == NULL) return false;
+
+ for (i = 0; i < npar; i++)
+ b->Values[i][0] = xy->Values[i][0];
+
+ /* Store a copy for later user */
+ for (i = 0; i < npar; i++)
+ coeff[i] = b->Values[i][0];
+
+ /* Error analysis. */
+
+ /* SSE and MSE. */
+ temp1 = MATNalloc (1,1);
+ if ((temp1->Values[0][0] = MATNcross(y)) == 0) return false;
+
+ /* |bt| = |b| T */
+ bt = MATNtranspose (b);
+ if (bt == NULL) return false;
+
+ /* |yt| = |bt| * |xt| */
+ yt = MATNmult (bt, xt);
+ if (yt == NULL) return false;
+
+
+ /* |temp2| = |yt|* |y| */
+ temp2 = MATNmult (yt, y);
+ if (temp2 == NULL) return false;
+
+ /* SSE, MSE */
+ ans->SSE = temp1 -> Values[0][0] - temp2 -> Values[0][0];
+ ans->MSE = ans->SSE / (double) (nvar - npar);
+
+ /* SSTO */
+ sum = 0;
+ for (i=0; i < nvar; i++)
+ sum += y->Values[i][0];
+
+ sum *= sum / (double) nvar;
+ ans->SSTO = temp1->Values[0][0] - sum;
+
+ /* SSR, MSR, and Fisher-F */
+ ans->SSR = temp2->Values[0][0] - sum;
+ ans->MSR = ans->SSR / (double) (npar - 1);
+ ans->F = ans->MSR / ans->MSE;
+
+ /* Correlation coefficients. */
+ ans->R2 = ans->SSR/ans->SSTO;
+ ans->R2adj = 1.0 - (ans->SSE/ans->SSTO)*((nvar-1.)/(nvar-npar));
+
+ /* Variance-covariance matrix */
+ /* */
+ /* In RGB->Lab, for example: */
+ /* */
+ /* Var(R) Cov(R,G) Cov(R,B) */
+ /* |vcb| = Cov(R,G) Var(G) Cov(G,B) */
+ /* Cov(R,B) Cov(G,B) Var(B) */
+ /* */
+
+ MATNscalar(a, ans->MSE, vcb);
+
+ /* Determine the T-values */
+
+ for (i=0; i < npar; i++) {
+
+ temp1->Values[0][0] = fabs(vcb->Values[i][0]);
+ if ( temp1->Values[0][0] == 0)
+ tvl[i] = 0; /* This should never happen */
+ else
+ tvl[i] = b->Values[i][0] / sqrt(temp1->Values[0][0]);
+ }
+
+
+ /* Ok, done */
+
+ MATNfree(a); MATNfree(xy); MATNfree(yt); MATNfree(b);
+ MATNfree(temp1); MATNfree(temp2); MATNfree(bt); MATNfree(xt);
+
+
+ return true;
+}
+
+
+
+/* Does create (so, it allocates) the regression matrix, */
+/* keeping track of error as well. */
+
+static
+BOOL CreateRegressionMatrix(const LPMATN Input, const LPMATN Output,
+ LPMATN* ptrMatrix, LPMLRSTATISTICS maxErrorMeas)
+{
+ double* coef;
+ double* tval;
+ LPMATN ivar, dvar, vcov;
+ MLRSTATISTICS ErrorMeas, PeakErrorMeas;
+ int i, j, nIn, nOut, NumOfPatches;
+
+ nIn = Input -> Cols;
+ nOut = Output -> Cols;
+ NumOfPatches = Input -> Rows;
+
+ /* Checkpoint */
+ if (Output -> Rows != NumOfPatches) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "(internal) Regression matrix mismatch");
+ return false;
+ }
+
+ coef = (double*) malloc(nIn * sizeof(double));
+ if (coef == NULL) return false;
+
+ tval = (double*) malloc(nIn * sizeof(double));
+ if (tval == NULL) {
+ free(coef);
+ return false;
+ }
+
+ ivar = MATNalloc(NumOfPatches, nIn);
+ dvar = MATNalloc(NumOfPatches, 1);
+
+ /* Copy In to ivar, */
+ for (i = 0; i < NumOfPatches; i++) {
+
+ for (j = 0; j < nIn; j++)
+ ivar->Values[i][j] = Input->Values[i][j];
+ }
+
+ /* This is the (symmetric) Covariance matrix */
+ vcov = MATNalloc(nIn, nIn);
+
+ /* This is the regression matrix */
+ *ptrMatrix = MATNalloc(nIn, nOut);
+
+ PeakErrorMeas.R2adj = 0;
+ for (j = 0; j < nOut; ++j)
+ {
+ for (i = 0; i < NumOfPatches; ++i)
+ dvar->Values[i][0] = Output->Values[i][j];
+
+ if (MultipleLinearRegression(ivar, dvar, NumOfPatches, nIn, coef, vcov, tval, &ErrorMeas)) {
+
+ /* Ok so far... store values */
+ for (i = 0; i < nIn; i++)
+ (*ptrMatrix)->Values[i][j] = coef[i];
+ }
+ else {
+ /* Boo... got error. Discard whole point. */
+ MATNfree(ivar); MATNfree(dvar); MATNfree(vcov);
+ if (coef) free(coef);
+ if (tval) free(tval);
+ MATNfree(*ptrMatrix); *ptrMatrix = NULL;
+ return false;
+ }
+
+ /* Did this colorant got higer error? If so, this is */
+ /* the peak of all pixel */
+
+ if(fabs(ErrorMeas.R2adj) > fabs(PeakErrorMeas.R2adj))
+ PeakErrorMeas = ErrorMeas;
+ }
+
+ /* This is the peak error on all components */
+ *maxErrorMeas = PeakErrorMeas;
+
+
+#ifdef DEBUG
+ MATNprintf("Variance-Covariance", vcov);
+ printf("R2adj: %g, F: %g\n", PeakErrorMeas.R2adj, PeakErrorMeas.F);
+#endif
+
+ /* Free stuff. */
+ MATNfree(ivar); MATNfree(dvar); MATNfree(vcov);
+ if (coef) free(coef);
+ if (tval) free(tval);
+
+ return true;
+}
+
+
+/* Does compute the term of regression based on inputs. */
+
+static
+double Term(int n, double r, double g, double b)
+{
+
+ switch (n) {
+
+ /* 0 */
+ case 0 : return 255.0; /* 0 0 0 */
+
+ /* 1 */
+ case 1 : return r; /* 1 0 0 */
+ case 2 : return g; /* 0 1 0 */
+ case 3 : return b; /* 0 0 1 */
+
+ /* 2 */
+ case 4 : return r * g; /* 1 1 0 */
+ case 5 : return r * b; /* 1 0 1 */
+ case 6 : return g * b; /* 0 1 1 */
+ case 7 : return r * r; /* 2 0 0 */
+ case 8 : return g * g; /* 0 2 0 */
+ case 9 : return b * b; /* 0 0 2 */
+
+ /* 3 */
+ case 10: return r * g * b; /* 1 1 1 */
+ case 11: return r * r * r; /* 3 0 0 */
+ case 12: return g * g * g; /* 0 3 0 */
+ case 13: return b * b * b; /* 0 0 3 */
+ case 14: return r * g * g; /* 1 2 0 */
+ case 15: return r * r * g; /* 2 1 0 */
+ case 16: return g * g * b; /* 0 2 1 */
+ case 17: return b * r * r; /* 2 0 1 */
+ case 18: return b * b * r; /* 1 0 2 */
+
+ /* 4 */
+
+ case 19: return r * r * g * g; /* 2 2 0 */
+ case 20: return g * g * b * b; /* 0 2 2 */
+ case 21: return r * r * b * b; /* 2 0 2 */
+ case 22: return r * r * g * b; /* 2 1 1 */
+ case 23: return r * g * g * b; /* 1 2 1 */
+ case 24: return r * g * b * b; /* 1 1 2 */
+ case 25: return r * r * r * g; /* 3 1 0 */
+ case 26: return r * r * r * b; /* 3 0 1 */
+ case 27: return r * g * g * g; /* 1 3 0 */
+ case 28: return g * g * g * b; /* 0 3 1 */
+ case 29: return r * b * b * b; /* 1 0 3 */
+ case 30: return g * b * b * b; /* 0 1 3 */
+ case 31: return r * r * r * r; /* 4 0 0 */
+ case 32: return g * g * g * g; /* 0 4 0 */
+ case 33: return b * b * b * b; /* 0 0 4 */
+
+ /* 5 */
+
+ case 34: return r * r * g * g * b; /* 2 2 1 */
+ case 35: return r * g * g * b * b; /* 1 2 2 */
+ case 36: return r * r * g * b * b; /* 2 1 2 */
+ case 37: return r * r * r * g * g; /* 3 2 0 */
+ case 38: return r * r * r * g * b; /* 3 1 1 */
+ case 39: return r * r * r * b * b; /* 3 0 2 */
+ case 40: return g * g * g * b * b; /* 0 3 2 */
+ case 41: return r * r * g * g * g; /* 2 3 0 */
+ case 42: return r * g * g * g * b; /* 1 3 1 */
+ case 43: return r * r * b * b * b; /* 2 0 3 */
+ case 44: return g * g * b * b * b; /* 0 2 3 */
+ case 45: return r * g * b * b * b; /* 1 1 3 */
+ case 46: return r * r * r * r * g; /* 4 1 0 */
+ case 47: return r * r * r * r * b; /* 4 0 1 */
+ case 48: return r * g * g * g * g; /* 1 4 0 */
+ case 49: return g * g * g * g * b; /* 0 4 1 */
+ case 50: return r * b * b * b * b; /* 1 0 4 */
+ case 51: return g * b * b * b * b; /* 0 1 4 */
+ case 52: return r * r * r * r * r; /* 5 0 0 */
+ case 53: return g * g * g * g * g; /* 0 5 0 */
+ case 54: return b * b * b * b * b; /* 0 0 5 */
+
+
+ default: return 0;
+ }
+}
+
+
+
+int cmsxRegressionCreateMatrix(LPMEASUREMENT m, SETOFPATCHES Allowed, int nterms,
+ int ColorSpace,
+ LPMATN* lpMat, LPMLRSTATISTICS Stat)
+{
+ LPMATN Input, Output;
+ int nCollected = cmsxPCollCountSet(m, Allowed);
+ int i, j, n, rc;
+
+ /* We are going always 3 -> 3 for now.... */
+
+ Input = MATNalloc(nCollected, nterms);
+ Output = MATNalloc(nCollected, 3);
+
+ /* Set independent terms */
+
+ for (n = i = 0; i < m -> nPatches; i++)
+ {
+ if (Allowed[i]) {
+
+ LPPATCH p = m -> Patches + i;
+
+ for (j=0; j < nterms; j++)
+ Input -> Values[n][j] = Term(j, p -> Colorant.RGB[0], p -> Colorant.RGB[1], p->Colorant.RGB[2]);
+
+ switch (ColorSpace) {
+
+ case PT_Lab:
+
+ Output-> Values[n][0] = p -> Lab.L;
+ Output-> Values[n][1] = p -> Lab.a;
+ Output-> Values[n][2] = p -> Lab.b;
+ break;
+
+ case PT_XYZ:
+ Output-> Values[n][0] = p -> XYZ.X;
+ Output-> Values[n][1] = p -> XYZ.Y;
+ Output-> Values[n][2] = p -> XYZ.Z;
+ break;
+
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Invalid colorspace");
+ }
+
+ n++;
+ }
+ }
+
+
+ /* Apply multiple linear regression */
+
+ if (*lpMat) MATNfree(*lpMat);
+ rc = CreateRegressionMatrix(Input, Output, lpMat, Stat);
+
+ /* Free variables */
+
+ MATNfree(Input);
+ MATNfree(Output);
+
+
+#ifdef DEBUG
+ if (rc == true)
+ MATNprintf("tfm", *lpMat);
+#endif
+
+ return rc;
+}
+
+
+/* Convert a RGB triplet to Lab by using regression matrix */
+
+BOOL cmsxRegressionRGB2Lab(double r, double g, double b, LPMATN tfm, LPcmsCIELab Lab)
+{
+ LPMATN inVec, outVec;
+ int i;
+
+ inVec = MATNalloc(1, tfm->Rows);
+ if (inVec == NULL)
+ return false;
+
+ /* Put terms */
+ for (i=0; i < tfm->Rows; i++)
+ inVec -> Values[0][i] = Term(i, r, g, b);
+
+ /* Across regression matrix */
+ outVec = MATNmult(inVec, tfm);
+
+ /* Store result */
+ if (outVec != NULL) {
+
+ Lab->L = outVec->Values[0][0];
+ Lab->a = outVec->Values[0][1];
+ Lab->b = outVec->Values[0][2];
+ MATNfree(outVec);
+ }
+
+ MATNfree(inVec);
+ return true;
+}
+
+
+/* Convert a RGB triplet to XYX by using regression matrix */
+
+BOOL cmsxRegressionRGB2XYZ(double r, double g, double b, LPMATN tfm, LPcmsCIEXYZ XYZ)
+{
+ LPMATN inVec, outVec;
+ int i;
+
+ inVec = MATNalloc(1, tfm->Rows);
+ if (inVec == NULL)
+ return false;
+
+ /* Put terms */
+ for (i=0; i < tfm->Rows; i++)
+ inVec -> Values[0][i] = Term(i, r, g, b);
+
+ /* Across regression matrix */
+ outVec = MATNmult(inVec, tfm);
+
+ /* Store result */
+ if (outVec != NULL) {
+
+ XYZ->X = outVec->Values[0][0];
+ XYZ->Y = outVec->Values[0][1];
+ XYZ->Z = outVec->Values[0][2];
+ MATNfree(outVec);
+ }
+
+ MATNfree(inVec);
+ return true;
+}
+
+
+/* Convert a RGB triplet to XYX by using regression matrix */
+
+BOOL cmsxRegressionXYZ2RGB(LPcmsCIEXYZ XYZ, LPMATN tfm, double RGB[3])
+{
+ LPMATN inVec, outVec;
+ int i;
+
+ inVec = MATNalloc(1, tfm->Rows);
+ if (inVec == NULL)
+ return false;
+
+ /* Put terms */
+ for (i=0; i < tfm->Rows; i++)
+ inVec -> Values[0][i] = Term(i, XYZ->X, XYZ->Y, XYZ->Z);
+
+ /* Across regression matrix */
+ outVec = MATNmult(inVec, tfm);
+
+ /* Store result */
+ if (outVec != NULL) {
+
+ RGB[0] = outVec->Values[0][0];
+ RGB[1] = outVec->Values[0][1];
+ RGB[2] = outVec->Values[0][2];
+ MATNfree(outVec);
+ }
+
+ MATNfree(inVec);
+ return true;
+}
+
diff --git a/src/libs/lprof/cmsscn.cpp b/src/libs/lprof/cmsscn.cpp
new file mode 100644
index 00000000..b99fed87
--- /dev/null
+++ b/src/libs/lprof/cmsscn.cpp
@@ -0,0 +1,422 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.08a */
+
+
+#include "lcmsprf.h"
+#include <stdio.h>
+
+/* The scanner profiler */
+
+
+BOOL cdecl cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys);
+BOOL cdecl cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys);
+
+/* ------------------------------------------------------------ Implementation */
+
+
+
+/* Does create regression matrix */
+
+static
+void ComputeGlobalRegression(LPSCANNERPROFILERDATA sys)
+{
+ BOOL lAllOk;
+ int nTerms;
+ MLRSTATISTICS Stat;
+
+ nTerms = cmsxFindOptimumNumOfTerms(&sys ->hdr, 55, &lAllOk);
+
+
+ if (!lAllOk) {
+ if (sys -> hdr.printf)
+ sys -> hdr.printf("*** WARNING: Inconsistence found, profile may be wrong. Check the target!");
+ nTerms = 4;
+ }
+
+ /* Create high terms matrix used by interpolation */
+ cmsxRegressionCreateMatrix(&sys -> hdr.m,
+ sys -> hdr.m.Allowed,
+ nTerms,
+ sys -> hdr.PCSType,
+ &sys -> HiTerms,
+ &Stat);
+
+ if (sys -> hdr.printf)
+ sys -> hdr.printf("Global regression: %d terms, R2Adj = %g", nTerms, Stat.R2adj);
+
+ /* Create low terms matrix used by extrapolation */
+ cmsxRegressionCreateMatrix(&sys -> hdr.m,
+ sys -> hdr.m.Allowed,
+ (nTerms > 10 ? 10 : nTerms),
+ sys -> hdr.PCSType,
+ &sys -> LoTerms,
+ &Stat);
+ if (sys -> hdr.printf)
+ sys -> hdr.printf("Extrapolation: R2Adj = %g", Stat.R2adj);
+
+}
+
+
+/* Fill struct with default values */
+
+BOOL cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys)
+{
+
+
+ if (sys == NULL) return false;
+ ZeroMemory(sys, sizeof(SCANNERPROFILERDATA));
+
+ sys->hdr.DeviceClass = icSigInputClass;
+ sys->hdr.ColorSpace = icSigRgbData;
+ sys->hdr.PCSType = PT_Lab;
+ sys->hdr.Medium = MEDIUM_REFLECTIVE_D50;
+
+ /* Default values for generation */
+
+ sys -> hdr.lUseCIECAM97s = false;
+ sys -> hdr.CLUTPoints = 16;
+
+
+ /* Default viewing conditions for scanner */
+
+ sys -> hdr.device.Yb = 20;
+ sys -> hdr.device.La = 20;
+ sys -> hdr.device.surround = AVG_SURROUND;
+ sys -> hdr.device.D_value = 1.0; /* Complete adaptation */
+
+
+ /* Viewing conditions of PCS */
+ cmsxInitPCSViewingConditions(&sys -> hdr);
+
+
+ sys -> HiTerms = NULL;
+ sys -> LoTerms = NULL;
+
+ strcpy(sys -> hdr.Description, "no description");
+ strcpy(sys -> hdr.Manufacturer, "little cms profiler construction set");
+ strcpy(sys -> hdr.Copyright, "No copyright, use freely");
+ strcpy(sys -> hdr.Model, "(unknown)");
+
+ sys ->lLocalConvergenceExtrapolation = false;
+ sys ->hdr.ProfileVerbosityLevel = 0;
+
+
+ return true;
+}
+
+/* Auxiliar: take RGB and update gauge */
+static
+void GetRGB(LPPROFILERCOMMONDATA hdr, WORD In[], double* r, double* g, double* b)
+{
+ static int Count = 0, n_old = 0;
+ double R, G, B;
+ int n;
+
+
+ R = _cmsxSaturate65535To255(In[0]); /* Convert from the sheet notation */
+ G = _cmsxSaturate65535To255(In[1]); /* 0..255.0, to our notation */
+ B = _cmsxSaturate65535To255(In[2]); /* of 0..0xffff, 0xffff/255 = 257 */
+
+ if (R == 0 && G == 0 && B == 0) {
+ Count = 0; n_old = -1;
+ }
+
+ n = (int) (double) (100. * Count) / (hdr->CLUTPoints * hdr->CLUTPoints * hdr->CLUTPoints);
+ Count++;
+
+ if (n > n_old) {
+ if (hdr->Gauger) hdr->Gauger("", 0, 100, (int) n);
+ }
+
+ n_old = n;
+ *r = R; *g = G; *b = B;
+
+}
+
+
+
+
+
+/* The sampler for Lab */
+static
+int RegressionSamplerLab(WORD In[], WORD Out[], LPVOID Cargo)
+{
+ cmsCIEXYZ xyz;
+ cmsCIELab Lab;
+ double r, g, b;
+ LPSCANNERPROFILERDATA sys = (LPSCANNERPROFILERDATA) Cargo;
+ char code;
+
+
+ GetRGB(&sys->hdr, In, &r, &g, &b);
+
+
+ code = cmsxHullCheckpoint(sys->hdr.hRGBHull,
+ (int) floor(r + .5),
+ (int) floor(g + .5),
+ (int) floor(b + .5));
+
+
+ if (code == 'i') { /* Inside gamut */
+
+ if (!cmsxRegressionRGB2Lab(r, g, b, sys -> HiTerms, &Lab)) return false;
+ }
+ else
+ if (!sys -> lLocalConvergenceExtrapolation && code == 'o') { /* outside gamut */
+
+ if (!cmsxRegressionRGB2Lab(r, g, b, sys -> LoTerms, &Lab)) return false;
+ }
+ else { /* At gamut hull boundaries */
+
+ if (!cmsxRegressionInterpolatorRGB(&sys -> hdr.m,
+ PT_Lab,
+ 10,
+ true,
+ 30,
+ r, g, b,
+ &Lab)) return false;
+ }
+
+
+ /* Regression CAN deliver wrong values. Clamp these. */
+ cmsClampLab(&Lab, 127.9961, -128, 127.9961, -128);
+
+ /* Normalize */
+ cmsLab2XYZ(cmsD50_XYZ(), &xyz, &Lab);
+ cmsxChromaticAdaptationAndNormalization(&sys->hdr, &xyz, false);
+ cmsXYZ2Lab(cmsD50_XYZ(), &Lab, &xyz);
+
+ /* Clamping again, adaptation could move slightly values */
+ cmsClampLab(&Lab, 127.9961, -128, 127.9961, -128);
+
+ /* To PCS encoding */
+ cmsFloat2LabEncoded(Out, &Lab);
+
+
+ return true; /* And done with success */
+}
+
+
+
+
+
+/* The sampler for XYZ */
+static
+int RegressionSamplerXYZ(WORD In[], WORD Out[], LPVOID Cargo)
+{
+ cmsCIEXYZ xyz;
+ double r, g, b;
+ LPSCANNERPROFILERDATA sys = (LPSCANNERPROFILERDATA) Cargo;
+ char code;
+
+ GetRGB(&sys -> hdr, In, &r, &g, &b);
+
+ code = cmsxHullCheckpoint(sys ->hdr.hRGBHull,
+ (int) floor(r + .5),
+ (int) floor(g + .5),
+ (int) floor(b + .5));
+
+ if (code == 'i') { /* Inside gamut */
+
+ if (!cmsxRegressionRGB2XYZ(r, g, b, sys -> HiTerms, &xyz)) return false;
+ }
+ else
+ if (!sys -> lLocalConvergenceExtrapolation && code == 'o') { /* outside gamut */
+
+ if (!cmsxRegressionRGB2XYZ(r, g, b, sys -> LoTerms, &xyz)) return false;
+ }
+
+ else { /* At gamut hull boundaries */
+
+ if (!cmsxRegressionInterpolatorRGB(&sys -> hdr.m,
+ PT_XYZ,
+ 10,
+ true,
+ 30,
+ r, g, b,
+ &xyz)) return false;
+ }
+
+
+ xyz.X /= 100.;
+ xyz.Y /= 100.;
+ xyz.Z /= 100.;
+
+ cmsxChromaticAdaptationAndNormalization(&sys->hdr, &xyz, false);
+
+ /* To PCS encoding. It also claps bad values */
+ cmsFloat2XYZEncoded(Out, &xyz);
+
+ return true; /* And done witch success */
+}
+
+
+
+/* The main scanner profiler */
+BOOL cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys)
+{
+
+ LPLUT AToB0;
+ DWORD dwNeedSamples;
+
+
+ if (!*sys -> hdr.OutputProfileFile)
+ return false;
+
+
+ if (!cmsxChoosePCS(&sys->hdr))
+ return false;
+
+ dwNeedSamples = PATCH_HAS_RGB;
+ if (sys ->hdr.PCSType == PT_Lab)
+ dwNeedSamples |= PATCH_HAS_Lab;
+ else
+ dwNeedSamples |= PATCH_HAS_XYZ;
+
+
+ if (sys->hdr.printf) {
+
+ sys->hdr.printf("Loading sheets...");
+
+ if (sys->hdr.ReferenceSheet[0])
+ sys->hdr.printf("Reference sheet: %s", sys->hdr.ReferenceSheet);
+ if (sys->hdr.MeasurementSheet[0])
+ sys->hdr.printf("Measurement sheet: %s", sys->hdr.MeasurementSheet);
+ }
+
+
+ if (!cmsxPCollBuildMeasurement(&sys->hdr.m,
+ sys->hdr.ReferenceSheet,
+ sys->hdr.MeasurementSheet,
+ dwNeedSamples)) return false;
+
+
+
+ sys->hdr.hProfile = cmsCreateRGBProfile(NULL, NULL, NULL);
+
+
+ cmsSetDeviceClass(sys->hdr.hProfile, sys->hdr.DeviceClass);
+ cmsSetColorSpace(sys->hdr.hProfile, sys->hdr.ColorSpace);
+ cmsSetPCS(sys->hdr. hProfile, _cmsICCcolorSpace(sys->hdr.PCSType));
+
+ /* Save char target tag */
+ if (sys->hdr.ProfileVerbosityLevel >= 2) {
+
+ cmsxEmbedCharTarget(&sys ->hdr);
+ }
+
+ AToB0 = cmsAllocLUT();
+
+ cmsAlloc3DGrid(AToB0, sys->hdr.CLUTPoints, 3, 3);
+
+ cmsxComputeLinearizationTables(&sys-> hdr.m,
+ sys -> hdr.PCSType,
+ sys -> Prelinearization,
+ 1024,
+ MEDIUM_REFLECTIVE_D50);
+
+ /* Refresh RGB of all patches. This converts all regression into */
+ /* near linear RGB->Lab or XYZ */
+
+ cmsxPCollLinearizePatches(&sys->hdr.m, sys -> hdr.m.Allowed, sys -> Prelinearization);
+
+ cmsxComputeGamutHull(&sys->hdr);
+ ComputeGlobalRegression(sys);
+
+ cmsAllocLinearTable(AToB0, sys -> Prelinearization, 1);
+
+ /* Set CIECAM97s parameters */
+
+ sys -> hdr.device.whitePoint.X = sys -> hdr.WhitePoint.X * 100.;
+ sys -> hdr.device.whitePoint.Y = sys -> hdr.WhitePoint.Y * 100.;
+ sys -> hdr.device.whitePoint.Z = sys -> hdr.WhitePoint.Z * 100.;
+
+
+ sys->hdr.hDevice = cmsCIECAM97sInit(&sys->hdr.device);
+ sys->hdr.hPCS = cmsCIECAM97sInit(&sys->hdr.PCS);
+
+
+ if (sys -> hdr.PCSType == PT_Lab)
+ cmsSample3DGrid(AToB0, RegressionSamplerLab, sys, 0);
+ else
+ cmsSample3DGrid(AToB0, RegressionSamplerXYZ, sys, 0);
+
+ cmsCIECAM97sDone(sys->hdr.hDevice);
+ cmsCIECAM97sDone(sys->hdr.hPCS);
+
+ cmsAddTag(sys->hdr.hProfile, icSigAToB0Tag, AToB0);
+
+
+ cmsxEmbedTextualInfo(&sys -> hdr);
+
+ cmsAddTag(sys->hdr.hProfile, icSigMediaWhitePointTag, &sys->hdr.WhitePoint);
+ cmsAddTag(sys->hdr.hProfile, icSigMediaBlackPointTag, &sys->hdr.BlackPoint);
+
+
+ /* Save primaries & gamma curves */
+ if (sys->hdr.ProfileVerbosityLevel >= 1) {
+
+ cmsxEmbedMatrixShaper(&sys ->hdr);
+ }
+
+ _cmsSaveProfile(sys->hdr.hProfile, sys->hdr.OutputProfileFile);
+
+ cmsCloseProfile(sys->hdr.hProfile);
+ sys->hdr.hProfile = NULL;
+
+ cmsxPCollFreeMeasurements(&sys->hdr.m);
+
+ cmsFreeLUT(AToB0);
+
+ if (sys -> HiTerms)
+ MATNfree(sys -> HiTerms);
+ sys -> HiTerms = NULL;
+
+
+ if (sys -> LoTerms)
+ MATNfree(sys -> LoTerms);
+ sys -> LoTerms = NULL;
+
+
+
+ if (sys ->Prelinearization[0])
+ cmsFreeGammaTriple(sys -> Prelinearization);
+
+ if (sys ->hdr.Gamma)
+ cmsFreeGammaTriple(sys->hdr.Gamma);
+
+ return true;
+}
diff --git a/src/libs/lprof/cmssheet.cpp b/src/libs/lprof/cmssheet.cpp
new file mode 100644
index 00000000..cf8c569e
--- /dev/null
+++ b/src/libs/lprof/cmssheet.cpp
@@ -0,0 +1,1746 @@
+/* */
+/* Little cms - profiler construction set */
+/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
+/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
+/* */
+/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
+/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
+/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
+/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
+/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
+/* OF THIS SOFTWARE. */
+/* */
+/* This file is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+/* */
+/* As a special exception to the GNU General Public License, if you */
+/* distribute this file as part of a program that contains a */
+/* configuration script generated by Autoconf, you may include it under */
+/* the same distribution terms that you use for the rest of that program. */
+/* */
+/* Version 1.08a */
+
+
+#include "lcmsprf.h"
+
+/* #define _DEBUG 1 */
+
+
+
+/* IT8.7 / CGATS.17-200x handling */
+LCMSHANDLE cdecl cmsxIT8Alloc(void);
+void cdecl cmsxIT8Free(LCMSHANDLE IT8);
+
+/* Persistence */
+LCMSHANDLE cdecl cmsxIT8LoadFromFile(const char* cFileName);
+LCMSHANDLE cdecl cmsxIT8LoadFromMem(void *Ptr, size_t len);
+BOOL cdecl cmsxIT8SaveToFile(LCMSHANDLE IT8, const char* cFileName);
+
+/* Properties */
+const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8);
+BOOL cdecl cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type);
+
+BOOL cdecl cmsxIT8SetProperty(LCMSHANDLE hIT8, const char* cProp, const char *Str);
+BOOL cdecl cmsxIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val);
+
+const char* cdecl cmsxIT8GetProperty(LCMSHANDLE hIT8, const char* cProp);
+double cdecl cmsxIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp);
+int cdecl cmsxIT8EnumProperties(LCMSHANDLE IT8, char ***PropertyNames);
+
+
+/* Datasets */
+
+BOOL cdecl cmsxIT8GetDataSetByPos(LCMSHANDLE IT8, int col, int row, char* Val, int ValBufferLen);
+
+BOOL cdecl cmsxIT8GetDataSet(LCMSHANDLE IT8, const char* cPatch,
+ const char* cSample,
+ char* Val, int ValBufferLen);
+
+
+BOOL cdecl cmsxIT8GetDataSetDbl(LCMSHANDLE IT8, const char* cPatch, const char* cSample, double* d);
+
+BOOL cdecl cmsxIT8SetDataSet(LCMSHANDLE IT8, const char* cPatch,
+ const char* cSample,
+ char *Val);
+
+BOOL cdecl cmsxIT8SetDataFormat(LCMSHANDLE IT8, int n, const char *Sample);
+int cdecl cmsxIT8EnumDataFormat(LCMSHANDLE IT8, char ***SampleNames);
+
+const char *cdecl cmsxIT8GenericPatchName(int nPatch, char* buffer);
+int cdecl cmsxIT8GenericPatchNum(const char *Name);
+
+
+/* ------------------------------------------------------------- Implementation */
+
+
+#define MAXID 128 /* Max length of identifier */
+#define MAXSTR 255 /* Max length of string */
+
+#ifndef NON_WINDOWS
+#include <io.h>
+#endif
+
+/* Symbols */
+
+typedef enum { SNONE,
+ SINUM, /* Integer */
+ SDNUM, /* Real */
+ SIDENT, /* Identifier */
+ SSTRING, /* string */
+ SCOMMENT, /* comment */
+ SEOLN, /* End of line */
+ SEOF, /* End of stream */
+ SSYNERROR, /* Syntax error found on stream */
+
+ /* Keywords */
+
+ SBEGIN_DATA,
+ SBEGIN_DATA_FORMAT,
+ SEND_DATA,
+ SEND_DATA_FORMAT,
+ SKEYWORD,
+ SSTRING_SY
+
+ } SYMBOL;
+
+
+/* Linked list of variable names */
+
+typedef struct _KeyVal {
+
+ struct _KeyVal* Next;
+ char* Keyword; /* Name of variable */
+ char* Value; /* Points to value */
+
+ } KEYVALUE, FAR* LPKEYVALUE;
+
+/* Linked list of values (Memory sink) */
+
+typedef struct _OwnedMem {
+
+ struct _OwnedMem* Next;
+ void * Ptr; /* Point to value */
+
+ } OWNEDMEM, FAR* LPOWNEDMEM;
+
+
+/* This struct hold all information about an openened */
+/* IT8 handler. Only one dataset is allowed. */
+
+typedef struct {
+
+ int nSamples, nPatches; /* Rows, Cols */
+ int SampleID; /* Pos of ID */
+ LPKEYVALUE HeaderList; /* The properties */
+ char* FileBuffer; /* The ASCII stream */
+ char** DataFormat; /* The binary stream descriptor */
+ char** Data; /* The binary stream */
+ LPOWNEDMEM MemorySink; /* The storage bakend */
+
+ /* Parser state machine */
+
+ SYMBOL sy; /* Current symbol */
+ int ch; /* Current character */
+ char* Source; /* Points to loc. being parsed */
+ int inum; /* integer value */
+ double dnum; /* real value */
+ char id[MAXID]; /* identifier */
+ char str[MAXSTR]; /* string */
+
+ /* Allowed keywords & datasets */
+
+ LPKEYVALUE ValidKeywords;
+ LPKEYVALUE ValidSampleID;
+
+ char FileName[MAX_PATH];
+ int lineno; /* line counter for error reporting */
+
+ char SheetType[MAXSTR]; /* New 1.09 */
+
+ } IT8, FAR* LPIT8;
+
+
+
+/* ------------------------------------------------------ IT8 parsing routines */
+
+
+/* A keyword */
+typedef struct {
+ const char *id;
+ SYMBOL sy;
+
+ } KEYWORD;
+
+/* The keyword->symbol translation table. Sorting is required. */
+static const KEYWORD TabKeys[] = {
+
+ {"BEGIN_DATA", SBEGIN_DATA },
+ {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT },
+ {"END_DATA", SEND_DATA},
+ {"END_DATA_FORMAT", SEND_DATA_FORMAT},
+ {"KEYWORD", SKEYWORD},
+ {"STRING", SSTRING_SY}};
+
+#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
+
+/* Predefined properties */
+static char* PredefinedProperties[] = {
+
+ "NUMBER_OF_FIELDS", /* Required - NUMBER OF FIELDS */
+ "NUMBER_OF_SETS", /* Required - NUMBER OF SETS */
+ "ORIGINATOR", /* Required - Identifies the specific system, organization or individual that created the data file. */
+ "CREATED", /* Required - Indicates date of creation of the data file. */
+ "DESCRIPTOR", /* Required - Describes the purpose or contents of the data file. */
+ "DIFFUSE_GEOMETRY", /* The diffuse geometry used. Allowed values are "sphere" or "opal". */
+ "MANUFACTURER",
+ "MANUFACTURE", /* Some broken Fuji targets does store this value */
+ "PROD_DATE", /* Identifies year and month of production of the target in the form yyyy:mm. */
+ "SERIAL", /* Uniquely identifies individual physical target. */
+
+ "MATERIAL", /* Identifies the material on which the target was produced using a code */
+ /* uniquely identifying th e material. Th is is intend ed to be used for IT8.7 */
+ /* physical targets only (i.e . IT8.7/1 a nd IT8.7/2). */
+
+ "INSTRUMENTATION", /* Used to report the specific instrumentation used (manufacturer and */
+ /* model number) to generate the data reported. This data will often */
+ /* provide more information about the particular data collected than an */
+ /* extensive list of specific details. This is particularly important for */
+ /* spectral data or data derived from spectrophotometry. */
+
+ "MEASUREMENT_SOURCE", /* Illumination used for spectral measurements. This data helps provide */
+ /* a guide to the potential for issues of paper fluorescence, etc. */
+
+ "PRINT_CONDITIONS", /* Used to define the ch aracteristics of the printed sheet being reported. */
+ /* Where standard conditions have been defined (e.g., SW OP at nominal) */
+ /* named conditions may suffice. Otherwise, detailed in formation is */
+ /* needed. */
+
+ "SAMPLE_BACKING", /* Identifies the backing material used behind the sample during */
+ /* measurement. Allowed values are “black”, “white”, or "na". */
+
+ "CHISQ_DOF" /* Degrees of freedom associated with the Chi squared statistic */
+ };
+
+#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(char *))
+
+
+/* Predefined sample types on dataset */
+static char* PredefinedSampleID[] = {
+
+ "CMYK_C", /* Cyan component of CMYK data expressed as a percentage */
+ "CMYK_M", /* Magenta component of CMYK data expressed as a percentage */
+ "CMYK_Y", /* Yellow component of CMYK data expressed as a percentage */
+ "CMYK_K", /* Black component of CMYK data expressed as a percentage */
+ "D_RED", /* Red filter density */
+ "D_GREEN", /* Green filter density */
+ "D_BLUE", /* Blue filter density */
+ "D_VIS", /* Visual filter density */
+ "D_MAJOR_FILTER", /* Major filter d ensity */
+ "RGB_R", /* Red component of RGB data */
+ "RGB_G", /* Green component of RGB data */
+ "RGB_B", /* Blue com ponent of RGB data */
+ "SPECTRAL_NM", /* Wavelength of measurement expressed in nanometers */
+ "SPECTRAL_PCT", /* Percentage reflectance/transmittance */
+ "SPECTRAL_DEC", /* Reflectance/transmittance */
+ "XYZ_X", /* X component of tristimulus data */
+ "XYZ_Y", /* Y component of tristimulus data */
+ "XYZ_Z", /* Z component of tristimulus data */
+ "XYY_X" /* x component of chromaticity data */
+ "XYY_Y", /* y component of chromaticity data */
+ "XYY_CAPY", /* Y component of tristimulus data */
+ "LAB_L", /* L* component of Lab data */
+ "LAB_A", /* a* component of Lab data */
+ "LAB_B", /* b* component of Lab data */
+ "LAB_C", /* C*ab component of Lab data */
+ "LAB_H", /* hab component of Lab data */
+ "LAB_DE" /* CIE dE */
+ "LAB_DE_94", /* CIE dE using CIE 94 */
+ "LAB_DE_CMC", /* dE using CMC */
+ "LAB_DE_2000", /* CIE dE using CIE DE 2000 */
+ "MEAN_DE", /* Mean Delta E (LAB_DE) of samples compared to batch average */
+ /* (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets) */
+ "STDEV_X", /* Standard deviation of X (tristimulus data) */
+ "STDEV_Y", /* Standard deviation of Y (tristimulus data) */
+ "STDEV_Z", /* Standard deviation of Z (tristimulus data) */
+ "STDEV_L", /* Standard deviation of L* */
+ "STDEV_A" /* Standard deviation of a* */
+ "STDEV_B", /* Standard deviation of b* */
+ "STDEV_DE", /* Standard deviation of CIE dE */
+ "CHI_STQD_PAR"}; /* The average of the standard deviations of L*, a* and b*. It is */
+ /* used to derive an estimate of the chi-squared parameter which is */
+ /* recommended as the predictor of the variability of dE */
+
+#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
+
+
+/* Checks whatsever if c is a valid identifier middle char. */
+static
+BOOL isidchar(int c)
+{
+ return (isalnum(c) || c == '$' || c == '%' || c == '&' || c == '/' || c == '.' || c == '_');
+
+}
+
+/* Checks whatsever if c is a valid identifier first char. */
+static
+BOOL isfirstidchar(int c)
+{
+ return !isdigit(c) && isidchar(c);
+}
+
+/* Checks if c is a separator */
+static
+BOOL isseparator(int c)
+{
+ return (c == ' ' || c == '\t' || c == '\r');
+}
+
+/* a replacement for strupr(), just for compatibility sake */
+
+static
+void xstrupr(char *cp)
+{
+ for (;*cp;cp++)
+ if (*cp >= 'a' && *cp <= 'z')
+ *cp += 'A'-'a';
+}
+
+/* A replacement for (the nonstandard) filelength */
+
+static
+int xfilelength(int fd)
+{
+#ifdef _MSC_VER
+ return _filelength(fd);
+#else
+ struct stat sb;
+ if (fstat(fd, &sb) < 0)
+ return(-1);
+ return(sb.st_size);
+#endif
+
+
+}
+
+static
+BOOL SynError(LPIT8 it8, const char *Txt, ...)
+{
+ char Buffer[256], ErrMsg[1024];
+ va_list args;
+
+ va_start(args, Txt);
+ vsprintf(Buffer, Txt, args);
+ va_end(args);
+
+ sprintf(ErrMsg, "%s: Line %d, %s", it8->FileName, it8->lineno, Buffer);
+ it8->sy = SSYNERROR;
+ cmsSignalError(LCMS_ERRC_ABORTED, ErrMsg);
+ return false;
+}
+
+static
+BOOL Check(LPIT8 it8, SYMBOL sy, const char* Err)
+{
+ if (it8 -> sy != sy)
+ return SynError(it8, Err);
+ return true;
+}
+
+
+
+/* Read Next character from stream */
+static
+void NextCh(LPIT8 it8)
+{
+ it8->ch = *it8->Source;
+ if (it8->ch) it8->Source++;
+}
+
+
+/* Try to see if current identifier is a keyword, if so return the referred symbol */
+static
+SYMBOL BinSrchKey(const char *id)
+{
+ int l = 1;
+ int r = NUMKEYS;
+ int x, res;
+
+ while (r >= l)
+ {
+ x = (l+r)/2;
+ res = strcmp(id, TabKeys[x-1].id);
+ if (res == 0) return TabKeys[x-1].sy;
+ if (res < 0) r = x - 1;
+ else l = x + 1;
+ }
+
+ return SNONE;
+}
+
+
+/* 10 ^n */
+static
+double pow10(int n)
+{
+ return pow(10, n);
+}
+
+
+/* Reads a Real number, tries to follow from integer number */
+static
+void ReadReal(LPIT8 it8, int inum)
+{
+ it8->dnum = (double) inum;
+
+ while (isdigit(it8->ch)) {
+
+ it8->dnum = it8->dnum * 10.0 + (it8->ch - '0');
+ NextCh(it8);
+ }
+
+ if (it8->ch == '.') { /* Decimal point */
+
+ double frac = 0.0; /* fraction */
+ int prec = 0; /* precission */
+
+ NextCh(it8); /* Eats dec. point */
+
+ while (isdigit(it8->ch)) {
+
+ frac = frac * 10.0 + (it8->ch - '0');
+ prec++;
+ NextCh(it8);
+ }
+
+ it8->dnum = it8->dnum + (frac / pow10(prec));
+ }
+
+ /* Exponent, example 34.00E+20 */
+ if (toupper(it8->ch) == 'E') {
+
+ int e;
+ int sgn;
+
+ NextCh(it8); sgn = 1;
+
+ if (it8->ch == '-') {
+
+ sgn = -1; NextCh(it8);
+ }
+ else
+ if (it8->ch == '+') {
+
+ sgn = +1;
+ NextCh(it8);
+ }
+
+
+ e = 0;
+ while (isdigit(it8->ch)) {
+
+ if ((double) e * 10L < INT_MAX)
+ e = e * 10 + (it8->ch - '0');
+
+ NextCh(it8);
+ }
+
+ e = sgn*e;
+
+ it8 -> dnum = it8 -> dnum * pow10(e);
+ }
+}
+
+
+
+/* Reads next symbol */
+static
+void InSymbol(LPIT8 it8)
+{
+ char *idptr;
+ int k;
+ SYMBOL key;
+ int sng;
+
+ do {
+
+ while (isseparator(it8->ch))
+ NextCh(it8);
+
+ if (isfirstidchar(it8->ch)) { /* Identifier */
+
+
+ k = 0;
+ idptr = it8->id;
+
+ do {
+
+ if (++k < MAXID) *idptr++ = (char) it8->ch;
+
+ NextCh(it8);
+
+ } while (isidchar(it8->ch));
+
+ *idptr = '\0';
+ xstrupr(it8->id);
+
+ key = BinSrchKey(it8->id);
+ if (key == SNONE) it8->sy = SIDENT;
+ else it8->sy = key;
+
+ }
+ else /* Is a number? */
+ if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
+ {
+ int sign = 1;
+
+ if (it8->ch == '-') {
+ sign = -1;
+ NextCh(it8);
+ }
+
+ it8->inum = 0;
+ it8->sy = SINUM;
+
+ while (isdigit(it8->ch))
+ {
+ if ((long) it8->inum * 10L > (long) INT_MAX)
+ {
+ ReadReal(it8, it8->inum);
+ it8->sy = SDNUM;
+ it8->dnum *= sign;
+ return;
+ }
+
+ it8->inum = it8->inum * 10 + (it8->ch - '0');
+ NextCh(it8);
+ }
+
+ if (it8->ch == '.') {
+
+ ReadReal(it8, it8->inum);
+ it8->sy = SDNUM;
+ it8->dnum *= sign;
+ return;
+ }
+
+ it8 -> inum *= sign;
+ return;
+
+ }
+ else
+ switch ((int) it8->ch) {
+
+ case '\0':
+ case '\x1a':
+ it8->sy = SEOF;
+ break;
+
+
+
+ case '\n':
+ NextCh(it8);
+ it8->sy = SEOLN;
+ it8->lineno++;
+ break;
+
+ /* Comment */
+
+ case '#':
+ NextCh(it8);
+ while (it8->ch && it8->ch != '\n')
+ NextCh(it8);
+
+ it8->sy = SCOMMENT;
+ break;
+
+ /* String. I will support \", \n, \t and \\. */
+ /* But otherwise I hardly doubt these will be used ... */
+
+ case '\'':
+ case '\"':
+ idptr = it8->str;
+ sng = it8->ch;
+ k = 0;
+ NextCh(it8);
+
+ while (k < MAXSTR && it8->ch != sng) {
+
+ if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
+ else {
+
+ if (it8->ch == '\\')
+ {
+ NextCh(it8);
+
+ switch (it8->ch) {
+
+ case 'n': *idptr++ = '\n'; break;
+ case 'r': *idptr++ = '\r'; break;
+ case 't': *idptr++ = '\t'; break;
+ case '\\': *idptr++ = '\\'; break;
+
+ default:
+ *idptr++ = (char) it8->ch;
+ }
+
+ NextCh(it8);
+ }
+ else
+ {
+ *idptr++ = (char) it8->ch;
+ NextCh(it8);
+ }
+
+ k++;
+ }
+ }
+
+ it8->sy = SSTRING;
+ *idptr = '\0';
+ NextCh(it8);
+ break;
+
+
+ default:
+ it8->sy = SSYNERROR;
+ NextCh(it8);
+
+ }
+
+ } while (it8->sy == SCOMMENT);
+}
+
+/* Checks end of line separator */
+static
+BOOL CheckEOLN(LPIT8 it8)
+{
+ if (!Check(it8, SEOLN, "Expected separator")) return false;
+ while (it8 -> sy == SEOLN)
+ InSymbol(it8);
+ return true;
+
+}
+
+/* Skip a symbol */
+
+static
+void Skip(LPIT8 it8, SYMBOL sy)
+{
+ if (it8->sy == sy && it8->sy != SEOF)
+ InSymbol(it8);
+}
+
+/* Returns a string holding current value */
+static
+BOOL GetVal(LPIT8 it8, char* Buffer)
+{
+ switch (it8->sy) {
+
+ case SIDENT: strncpy(Buffer, it8->id, MAXID-1); break;
+ case SINUM: sprintf(Buffer, "%d", it8 -> inum); break;
+ case SDNUM: sprintf(Buffer, "%g", it8 -> dnum); break;
+ case SSTRING: strncpy(Buffer, it8->str, MAXSTR-1); break;
+
+
+ default:
+ return SynError(it8, "Sample data expected");
+ }
+
+ return true;
+}
+
+/* ---------------------------------------------------------- Memory management */
+
+
+
+/* Frees an allocator and owned memory */
+void cmsxIT8Free(LCMSHANDLE hIT8)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ if (it8 == NULL)
+ return;
+
+ if (it8->MemorySink) {
+
+ LPOWNEDMEM p;
+ LPOWNEDMEM n;
+
+ for (p = it8->MemorySink; p != NULL; p = n) {
+
+ n = p->Next;
+ if (p->Ptr) free(p->Ptr);
+ free(p);
+ }
+ }
+
+ if (it8->FileBuffer)
+ free(it8->FileBuffer);
+
+ free(it8);
+}
+
+
+/* Allocates a chunk of data, keep linked list */
+static
+void* AllocChunk(LPIT8 it8, size_t size)
+{
+ LPOWNEDMEM ptr1;
+ void* ptr = malloc(size);
+
+ if (ptr) {
+
+ ZeroMemory(ptr, size);
+ ptr1 = (LPOWNEDMEM) malloc(sizeof(OWNEDMEM));
+
+ if (ptr1 == NULL) {
+
+ free(ptr);
+ return NULL;
+ }
+
+ ZeroMemory(ptr1, sizeof(OWNEDMEM));
+
+ ptr1-> Ptr = ptr;
+ ptr1-> Next = it8 -> MemorySink;
+ it8 -> MemorySink = ptr1;
+ }
+
+ return ptr;
+}
+
+
+/* Allocates a string */
+static
+char *AllocString(LPIT8 it8, const char* str)
+{
+ int Size = strlen(str)+1;
+ char *ptr;
+ ptr = (char *) AllocChunk(it8, Size);
+ if (ptr) strncpy (ptr, str, Size);
+ return ptr;
+}
+
+/* Searches through linked list */
+
+static
+BOOL IsAvailableOnList(LPKEYVALUE p, const char* Key, LPKEYVALUE* LastPtr)
+{
+ for (; p != NULL; p = p->Next) {
+
+ if (LastPtr) *LastPtr = p;
+ if (stricmp(Key, p->Keyword) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+
+
+/* Add a property into a linked list */
+static
+BOOL AddToList(LPIT8 it8, LPKEYVALUE* Head, const char *Key, const char* Value)
+{
+ LPKEYVALUE p;
+ LPKEYVALUE last;
+
+
+ /* Check if property is already in list (this is an error) */
+
+ if (IsAvailableOnList(*Head, Key, &last)) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "duplicate key <%s>", Key);
+ return false;
+ }
+
+ /* Allocate the container */
+ p = (LPKEYVALUE) AllocChunk(it8, sizeof(KEYVALUE));
+ if (p == NULL)
+ {
+ cmsSignalError(LCMS_ERRC_ABORTED, "AddToList: out of memory");
+ return false;
+ }
+
+ /* Store name and value */
+ p->Keyword = AllocString(it8, Key);
+
+ if (Value)
+ p->Value = AllocString(it8, Value);
+ else
+ p->Value = NULL;
+
+ p->Next = NULL;
+
+ /* Keep the container in our list */
+ if (*Head == NULL)
+ *Head = p;
+ else
+ last->Next = p;
+
+ return true;
+}
+
+static
+BOOL AddAvailableProperty(LPIT8 it8, const char* Key)
+{
+ return AddToList(it8, &it8->ValidKeywords, Key, NULL);
+}
+
+
+static
+BOOL AddAvailableSampleID(LPIT8 it8, const char* Key)
+{
+ return AddToList(it8, &it8->ValidSampleID, Key, NULL);
+}
+
+
+
+/* Init an empty container */
+LCMSHANDLE cmsxIT8Alloc(void)
+{
+ LPIT8 it8;
+ int i;
+
+ it8 = (LPIT8) malloc(sizeof(IT8));
+ if (it8 == NULL) return NULL;
+
+ ZeroMemory(it8, sizeof(IT8));
+
+ it8->HeaderList = NULL;
+ it8->FileBuffer = NULL;
+ it8->DataFormat = NULL;
+ it8->Data = NULL;
+ it8->MemorySink = NULL;
+ it8->ValidKeywords = NULL;
+ it8->ValidSampleID = NULL;
+
+ it8 -> sy = SNONE;
+ it8 -> ch = ' ';
+ it8 -> Source = NULL;
+ it8 -> inum = 0;
+ it8 -> dnum = 0.0;
+
+ it8 -> lineno = 1;
+
+ strcpy(it8->SheetType, "IT8.7/2");
+
+ /* Initialize predefined properties & data */
+
+ for (i=0; i < NUMPREDEFINEDPROPS; i++)
+ AddAvailableProperty(it8, PredefinedProperties[i]);
+
+ for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
+ AddAvailableSampleID(it8, PredefinedSampleID[i]);
+
+
+ return (LCMSHANDLE) it8;
+}
+
+
+const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ return it8 ->SheetType;
+
+}
+
+BOOL cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ strncpy(it8 ->SheetType, Type, MAXSTR-1);
+ return true;
+}
+
+
+
+/* Sets a property */
+BOOL cmsxIT8SetProperty(LCMSHANDLE hIT8, const char* Key, const char *Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ if (!Val) return false;
+ if (!*Val) return false;
+
+ return AddToList(it8, &it8 -> HeaderList, Key, Val);
+}
+
+
+BOOL cmsxIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val)
+{
+ char Buffer[256];
+
+ sprintf(Buffer, "%g", Val);
+ return cmsxIT8SetProperty(hIT8, cProp, Buffer);
+}
+
+/* Gets a property */
+const char* cmsxIT8GetProperty(LCMSHANDLE hIT8, const char* Key)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ LPKEYVALUE p;
+
+ if (IsAvailableOnList(it8 -> HeaderList, Key, &p))
+ {
+ return p -> Value;
+ }
+ return NULL;
+}
+
+
+double cmsxIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp)
+{
+ const char *v = cmsxIT8GetProperty(hIT8, cProp);
+ if (v) return atof(v);
+ else return 0.0;
+}
+
+/* ----------------------------------------------------------------- Datasets */
+
+
+static
+void AllocateDataFormat(LPIT8 it8)
+{
+ if (it8 -> DataFormat) return; /* Already allocated */
+
+ it8 -> nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
+
+ if (it8 -> nSamples <= 0) {
+
+ cmsSignalError(LCMS_ERRC_WARNING, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS, assuming 10");
+ it8 -> nSamples = 10;
+ }
+
+ it8 -> DataFormat = (char**) AllocChunk (it8, (it8->nSamples + 1) * sizeof(char *));
+ if (it8->DataFormat == NULL)
+ {
+ cmsSignalError(LCMS_ERRC_ABORTED, "AllocateDataFormat: Unable to allocate dataFormat array");
+ }
+
+}
+
+static
+const char *GetDataFormat(LPIT8 it8, int n)
+{
+ if (it8->DataFormat)
+ return it8->DataFormat[n];
+
+ return NULL;
+}
+
+static
+BOOL SetDataFormat(LPIT8 it8, int n, const char *label)
+{
+ if (n > it8 -> nSamples) return false;
+
+ if (!it8->DataFormat)
+ AllocateDataFormat(it8);
+
+ if (it8->DataFormat) {
+
+ it8->DataFormat[n] = AllocString(it8, label);
+ }
+
+ return true;
+}
+
+
+BOOL cmsxIT8SetDataFormat(LCMSHANDLE h, int n, const char *Sample)
+{
+ LPIT8 it8 = (LPIT8) h;
+ return SetDataFormat(it8, n, Sample);
+}
+
+static
+void AllocateDataSet(LPIT8 it8)
+{
+ if (it8 -> Data) return; /* Already allocated */
+
+ it8-> nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
+ it8-> nPatches = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_SETS"));
+ it8-> Data = (char**)AllocChunk (it8, (it8->nSamples + 1) * (it8->nPatches + 1) *sizeof (char*));
+ if (it8->Data == NULL)
+ {
+ cmsSignalError(-1, "AllocateDataSet: Unable to allocate data array");
+ }
+
+}
+
+static
+char* GetData(LPIT8 it8, int nSet, int nField)
+{
+ int nSamples = it8 -> nSamples;
+ int nPatches = it8 -> nPatches;
+
+ if (nSet >= nPatches || nField >= nSamples)
+ return NULL;
+
+ if (!it8->Data) return NULL;
+ return it8->Data [nSet * nSamples + nField];
+}
+
+static
+BOOL SetData(LPIT8 it8, int nSet, int nField, char *Val)
+{
+ if (!it8->Data)
+ AllocateDataSet(it8);
+
+ if (!it8->Data) return false;
+
+
+ if (nSet > it8 -> nPatches) {
+
+ SynError(it8, "Patch %d out of range, there are %d datasets", nSet, it8 -> nPatches);
+ return false;
+ }
+
+ if (nField > it8 ->nSamples) {
+ SynError(it8, "Sample %d out of range, there are %d datasets", nField, it8 ->nSamples);
+ return false;
+ }
+
+
+ it8->Data [nSet * it8 -> nSamples + nField] = AllocString(it8, Val);
+ return true;
+}
+
+
+/* --------------------------------------------------------------- File I/O */
+
+
+/* Writes a string to file */
+static
+void WriteStr(FILE *f, char *str)
+{
+ if (str == NULL)
+ fwrite(" ", 1, 1, f);
+ else
+ fwrite(str, 1, strlen(str), f);
+}
+
+
+/* Writes full header */
+static
+void WriteHeader(LPIT8 it8, FILE *fp)
+{
+ LPKEYVALUE p;
+
+ WriteStr(fp, it8->SheetType);
+ WriteStr(fp, "\n");
+ for (p = it8->HeaderList; (p != NULL); p = p->Next)
+ {
+ if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL)) {
+
+ WriteStr(fp, "KEYWORD\t\"");
+ WriteStr(fp, p->Keyword);
+ WriteStr(fp, "\"\n");
+
+ }
+
+ WriteStr(fp, p->Keyword);
+ if (p->Value) {
+
+ WriteStr(fp, "\t\"");
+ WriteStr(fp, p->Value);
+ WriteStr(fp, "\"");
+ }
+ WriteStr (fp, "\n");
+ }
+
+}
+
+
+/* Writes the data format */
+static
+void WriteDataFormat(FILE *fp, LPIT8 it8)
+{
+ int i, nSamples;
+
+ if (!it8 -> DataFormat) return;
+
+ WriteStr(fp, "BEGIN_DATA_FORMAT\n");
+ nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
+
+ for (i = 0; i < nSamples; i++) {
+
+ WriteStr(fp, it8->DataFormat[i]);
+ WriteStr(fp, (char*)((i == (nSamples-1)) ? "\n" : "\t")); // C->C++ : cast
+ }
+
+ WriteStr (fp, "END_DATA_FORMAT\n");
+}
+
+
+/* Writes data array */
+static
+void WriteData(FILE *fp, LPIT8 it8)
+{
+ int i, j;
+
+ if (!it8->Data) return;
+
+ WriteStr (fp, "BEGIN_DATA\n");
+
+ it8->nPatches = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_SETS"));
+
+ for (i = 0; i < it8-> nPatches; i++) {
+
+ for (j = 0; j < it8->nSamples; j++) {
+
+ char *ptr = it8->Data[i*it8->nSamples+j];
+
+ WriteStr(fp, (char*)((ptr == NULL) ? "0.00" : ptr)); // C->C++ : cast
+ WriteStr(fp, (char*)((j == (it8->nSamples-1)) ? "\n" : "\t")); // C->C++ : cast
+ }
+ }
+ WriteStr (fp, "END_DATA\n");
+}
+
+
+
+/* Saves whole file */
+BOOL cmsxIT8SaveToFile(LCMSHANDLE hIT8, const char* cFileName)
+{
+ FILE *fp;
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ fp = fopen(cFileName, "wt");
+ if (!fp) return false;
+ WriteHeader(it8, fp);
+ WriteDataFormat(fp, it8);
+ WriteData(fp, it8);
+ fclose(fp);
+
+ return true;
+}
+
+
+/* Reads whole file in a memory block */
+static
+BOOL ReadFileInMemory(const char *cFileName, char **Buffer, size_t *Len)
+{
+ FILE *fp;
+ size_t Size;
+ char *Ptr;
+
+ fp = fopen(cFileName, "rt");
+ if (!fp) return false;
+
+ Size = xfilelength(fileno(fp));
+ if (Size <= 0) {
+ fclose(fp);
+ return false;
+ }
+
+ Ptr = (char*)malloc(Size+1); // C->C++ : cast
+
+ Size = fread(Ptr, 1, Size, fp);
+ fclose(fp);
+ Ptr[Size] = '\0';
+
+ *Buffer = Ptr;
+ *Len = Size;
+ return true;
+}
+
+
+/* -------------------------------------------------------------- Higer lever parsing */
+
+static
+BOOL DataFormatSection(LPIT8 it8)
+{
+ int iField = 0;
+ BOOL Ignoring = false;
+
+ InSymbol(it8); /* Eats "BEGIN_DATA_FORMAT" */
+ CheckEOLN(it8);
+
+ while (it8->sy != SEND_DATA_FORMAT &&
+ it8->sy != SEOLN &&
+ it8->sy != SEOF &&
+ it8->sy != SSYNERROR)
+ {
+
+ if (it8->sy != SIDENT) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Sample type expected");
+ it8->sy = SSYNERROR;
+ return false;
+ }
+
+ if (!Ignoring && iField > it8->nSamples) {
+ cmsSignalError(LCMS_ERRC_WARNING, "More than NUMBER_OF_FIELDS fields. Extra is ignored\n");
+ Ignoring = true;
+ }
+ else {
+ if (!SetDataFormat(it8, iField, it8->id)) return false;
+ iField++;
+ }
+
+ InSymbol(it8);
+ Skip(it8, SEOLN);
+ }
+
+ Skip(it8, SEOLN);
+ Skip(it8, SEND_DATA_FORMAT);
+ Skip(it8, SEOLN);
+ return true;
+}
+
+
+
+static
+BOOL DataSection (LPIT8 it8)
+{
+ int iField = 0;
+ int iSet = 0;
+ char Buffer[256];
+
+ InSymbol(it8); /* Eats "BEGIN_DATA" */
+ CheckEOLN(it8);
+
+ while (it8->sy != SEND_DATA && it8->sy != SEOF)
+ {
+ if (iField >= it8 -> nSamples) {
+ iField = 0;
+ iSet++;
+ if (!CheckEOLN(it8))
+ return false;
+ }
+
+ if (it8->sy != SEND_DATA && it8->sy != SEOF) {
+
+ if (!GetVal(it8, Buffer))
+ return false;
+
+ if (!SetData(it8, iSet, iField, Buffer))
+ return false;
+
+ iField++;
+
+ Skip(it8, SEOLN);
+ InSymbol(it8);
+ }
+ }
+
+ Skip(it8, SEOLN);
+ Skip(it8, SEND_DATA);
+ Skip(it8, SEOLN);
+ return true;
+}
+
+
+
+
+
+
+static
+BOOL HeaderSection (LPIT8 it8)
+{
+ char VarName[MAXID];
+ char Buffer[MAXSTR];
+
+ while (it8->sy != SEOF &&
+ it8->sy != SSYNERROR &&
+ it8->sy != SBEGIN_DATA_FORMAT &&
+ it8->sy != SBEGIN_DATA) {
+
+
+ switch (it8 -> sy) {
+
+ case SKEYWORD:
+ InSymbol(it8);
+ if (!Check(it8, SSTRING, "Keyword expected")) return false;
+ if (!AddAvailableProperty(it8, it8 -> str)) return false;
+ InSymbol(it8);
+ break;
+
+
+ case SIDENT:
+ strncpy(VarName, it8->id, MAXID-1);
+ if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL))
+ return SynError(it8, "Undefined keyword '%s'", VarName);
+
+ InSymbol(it8);
+ GetVal(it8, Buffer);
+ cmsxIT8SetProperty((LCMSHANDLE) it8, VarName, Buffer);
+ InSymbol(it8);
+ break;
+
+
+ case SEOLN: break;
+
+ default:
+ return SynError(it8, "expected keyword or identifier");
+ }
+
+ Skip(it8, SEOLN);
+ }
+
+ return true;
+
+}
+
+
+static
+BOOL ParseIT8(LPIT8 it8)
+{
+
+ InSymbol(it8);
+
+ if (it8->sy == SIDENT) {
+
+ strncpy(it8->SheetType, it8->id, MAXSTR-1);
+ InSymbol(it8);
+
+ /* if (!AddAvailableProperty(it8, it8 -> id)) return false; */
+ /* cmsxIT8SetProperty((LCMSHANDLE) it8, it8->id, NULL); */
+ }
+
+ Skip(it8, SEOLN);
+
+ while (it8-> sy != SEOF &&
+ it8-> sy != SSYNERROR) {
+
+ switch (it8 -> sy) {
+
+ case SBEGIN_DATA_FORMAT:
+ if (!DataFormatSection(it8)) return false;
+ break;
+
+ case SBEGIN_DATA:
+ if (!DataSection(it8)) return false;
+ break;
+
+ case SEOLN:
+ Skip(it8, SEOLN);
+ break;
+
+ default:
+ if (!HeaderSection(it8)) return false;
+ }
+
+ }
+
+ return true;
+}
+
+
+static
+void CleanPatchName(char *cell)
+{
+ char cleaned[256], Buffer[256], ident[256];
+ char *orig = cell, *id;
+ int n, lOneNum;
+
+
+ id = ident;
+ while (*cell && isalpha(*cell))
+ {
+ *id++ = (char) toupper(*cell);
+ cell++;
+ }
+ *id = 0;
+ strcpy(cleaned, ident);
+
+
+ n = 0;
+ lOneNum = false;
+ while (*cell && isdigit(*cell))
+ {
+ n = n * 10 + (*cell -'0');
+ cell++;
+ lOneNum = true;
+ }
+
+ if (lOneNum) {
+
+ sprintf(Buffer, "%d", n);
+ strcat(cleaned, Buffer);
+ }
+
+ if (strcmp(cleaned, "GS0") == 0)
+ strcpy(orig, "DMIN");
+ else
+ if (strcmp(cleaned, "GS23") == 0)
+ strcpy(orig, "DMAX");
+ else
+ strcpy(orig, cleaned);
+}
+
+
+/* Init useful pointers */
+
+static
+void CookPointers(LPIT8 it8)
+{
+ int idField, i;
+ char* Fld;
+
+ it8 -> SampleID = 0;
+ for (idField = 0; idField < it8 -> nSamples; idField++)
+ {
+ Fld = it8->DataFormat[idField];
+ if (!Fld) continue;
+
+ if (strcmp(Fld, "SAMPLE_ID") == 0) {
+ it8 -> SampleID = idField;
+
+
+ for (i=0; i < it8 -> nPatches; i++) {
+
+ char *Data = GetData(it8, i, idField);
+ if (Data) {
+ char Buffer[256];
+
+ strncpy(Buffer, Data, 255);
+ CleanPatchName(Buffer);
+
+ if (strlen(Buffer) <= strlen(Data))
+ strcpy(Data, Buffer);
+ else
+ SetData(it8, i, idField, Buffer);
+
+ }
+ }
+
+ }
+ }
+
+}
+
+
+/* ---------------------------------------------------------- Exported routines */
+
+
+LCMSHANDLE cmsxIT8LoadFromMem(void *Ptr, size_t len)
+{
+ LCMSHANDLE hIT8 = cmsxIT8Alloc();
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ if (!hIT8) return NULL;
+ it8 ->FileBuffer = (char*) malloc(len + 1);
+
+ strncpy(it8 ->FileBuffer, (const char*)Ptr, len); // C->C++ : cast
+ strncpy(it8->FileName, "", MAX_PATH-1);
+ it8-> Source = it8 -> FileBuffer;
+
+ ParseIT8(it8);
+ CookPointers(it8);
+
+ free(it8->FileBuffer);
+ it8 -> FileBuffer = NULL;
+
+ return hIT8;
+
+
+}
+
+
+LCMSHANDLE cmsxIT8LoadFromFile(const char* cFileName)
+{
+
+ LCMSHANDLE hIT8 = cmsxIT8Alloc();
+ LPIT8 it8 = (LPIT8) hIT8;
+ size_t Len;
+
+ if (!hIT8) return NULL;
+ if (!ReadFileInMemory(cFileName, &it8->FileBuffer, &Len)) return NULL;
+
+ strncpy(it8->FileName, cFileName, MAX_PATH-1);
+ it8-> Source = it8 -> FileBuffer;
+
+ ParseIT8(it8);
+ CookPointers(it8);
+
+ free(it8->FileBuffer);
+ it8 -> FileBuffer = NULL;
+
+ return hIT8;
+
+}
+
+int cmsxIT8EnumDataFormat(LCMSHANDLE hIT8, char ***SampleNames)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ *SampleNames = it8 -> DataFormat;
+ return it8 -> nSamples;
+}
+
+
+int cmsxIT8EnumProperties(LCMSHANDLE hIT8, char ***PropertyNames)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ LPKEYVALUE p;
+ int n;
+ char **Props;
+
+ /* Pass#1 - count properties */
+
+ n = 0;
+ for (p = it8 -> HeaderList; p != NULL; p = p->Next) {
+ n++;
+ }
+
+
+ Props = (char **) malloc(sizeof(char *) * n);
+
+ /* Pass#2 - Fill pointers */
+ n = 0;
+ for (p = it8 -> HeaderList; p != NULL; p = p->Next) {
+ Props[n++] = p -> Keyword;
+ }
+
+ *PropertyNames = Props;
+ return n;
+}
+
+static
+int LocatePatch(LPIT8 it8, const char* cPatch)
+{
+ int i;
+ const char *data;
+
+ for (i=0; i < it8-> nPatches; i++) {
+
+ data = GetData(it8, i, it8->SampleID);
+
+ if (data != NULL) {
+
+ if (stricmp(data, cPatch) == 0)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+static
+int LocateEmptyPatch(LPIT8 it8, const char* cPatch)
+{
+ int i;
+ const char *data;
+
+ for (i=0; i < it8-> nPatches; i++) {
+
+ data = GetData(it8, i, it8->SampleID);
+
+ if (data == NULL)
+ return i;
+
+ }
+
+ return -1;
+}
+
+static
+int LocateSample(LPIT8 it8, const char* cSample)
+{
+ int i;
+ const char *fld;
+
+ for (i=0; i < it8->nSamples; i++) {
+
+ fld = GetDataFormat(it8, i);
+ if (stricmp(fld, cSample) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+
+BOOL cmsxIT8GetDataSetByPos(LCMSHANDLE hIT8, int col, int row, char* Val, int ValBufferLen)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ const char *data = GetData(it8, row, col);
+
+ if (!data)
+ {
+ *Val = '\0';
+ return false;
+ }
+
+ strncpy(Val, data, ValBufferLen-1);
+ return true;
+}
+
+
+
+BOOL cmsxIT8GetDataSet(LCMSHANDLE hIT8, const char* cPatch,
+ const char* cSample,
+ char* Val, int ValBuffLen)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ int iField, iSet;
+
+
+ iField = LocateSample(it8, cSample);
+ if (iField < 0) {
+ /* cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find data field %s\n", cSample); */
+ return false;
+ }
+
+
+ iSet = LocatePatch(it8, cPatch);
+ if (iSet < 0) {
+
+ /* cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find patch '%s'\n", cPatch); */
+ return false;
+ }
+
+ strncpy(Val, GetData(it8, iSet, iField), ValBuffLen-1);
+ return true;
+}
+
+
+BOOL cmsxIT8GetDataSetDbl(LCMSHANDLE it8, const char* cPatch, const char* cSample, double* v)
+{
+ char Buffer[20];
+
+ if (cmsxIT8GetDataSet(it8, cPatch, cSample, Buffer, 20)) {
+
+ *v = atof(Buffer);
+ return true;
+ } else
+ return false;
+}
+
+
+
+BOOL cmsxIT8SetDataSet(LCMSHANDLE hIT8, const char* cPatch,
+ const char* cSample,
+ char *Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ int iField, iSet;
+
+
+ iField = LocateSample(it8, cSample);
+
+ if (iField < 0) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find data field %s\n", cSample);
+ return false;
+ }
+
+
+ if (it8-> nPatches == 0) {
+
+ AllocateDataFormat(it8);
+ AllocateDataSet(it8);
+ CookPointers(it8);
+ }
+
+
+ if (stricmp(cSample, "SAMPLE_ID") == 0)
+ {
+
+ iSet = LocateEmptyPatch(it8, cPatch);
+ if (iSet < 0) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't add more patches '%s'\n", cPatch);
+ return false;
+ }
+ iField = it8 -> SampleID;
+ }
+ else {
+ iSet = LocatePatch(it8, cPatch);
+ if (iSet < 0) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find patch '%s'\n", cPatch);
+ return false;
+ }
+ }
+
+ return SetData(it8, iSet, iField, Val);
+}
+
+
+BOOL cmsxIT8SetDataSetDbl(LCMSHANDLE hIT8, const char* cPatch,
+ const char* cSample,
+ double Val)
+{
+ char Buff[256];
+
+ sprintf(Buff, "%g", Val);
+ return cmsxIT8SetDataSet(hIT8, cPatch, cSample, Buff);
+
+}
+
+
+const char* cmsxIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ char* Data = GetData(it8, nPatch, it8->SampleID);
+ if (!Data) return NULL;
+
+ strcpy(buffer, Data);
+ return buffer;
+}
+
+
+const char* cmsxIT8GenericPatchName(int nPatch, char* buffer)
+{
+ int row, col;
+
+ if (nPatch >= cmsxIT8_NORMAL_PATCHES)
+ return "$CUSTOM";
+
+ if (nPatch >= (cmsxIT8_ROWS * cmsxIT8_COLS)) {
+
+ nPatch -= cmsxIT8_ROWS * cmsxIT8_COLS;
+ if (nPatch == 0)
+ return "DMIN";
+ else
+ if (nPatch == cmsxIT8_GRAYCOLS - 1)
+ return "DMAX";
+ else
+ sprintf(buffer, "GS%d", nPatch);
+ return buffer;
+ }
+
+
+ row = nPatch / cmsxIT8_COLS;
+ col = nPatch % cmsxIT8_COLS;
+
+ sprintf (buffer, "%c%d", 'A'+row, col+1);
+ return buffer;
+}
+
+
+
+
+int cmsxIT8GenericPatchNum(const char *name)
+{
+ int i;
+ char Buff[256];
+
+
+ for (i=0; i < cmsxIT8_TOTAL_PATCHES; i++)
+ if (stricmp(cmsxIT8GenericPatchName(i, Buff), name) == 0)
+ return i;
+
+ return -1;
+}
+
+
+
+
+
diff --git a/src/libs/lprof/lcmsprf.h b/src/libs/lprof/lcmsprf.h
new file mode 100644
index 00000000..00c7ac40
--- /dev/null
+++ b/src/libs/lprof/lcmsprf.h
@@ -0,0 +1,485 @@
+/*
+Little cms - profiler construction set
+Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com>
+
+THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+OF THIS SOFTWARE.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+As a special exception to the GNU General Public License, if you
+distribute this file as part of a program that contains a
+configuration script generated by Autoconf, you may include it under
+the same distribution terms that you use for the rest of that program.
+*/
+
+/* Version 1.09a */
+
+#ifndef __cmsprf_H
+
+#include <config.h>
+#include LCMS_HEADER
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef NON_WINDOWS
+# ifndef stricmp
+# define stricmp strcasecmp
+# endif
+#endif
+
+#ifndef max
+#define max(a,b) ((a) > (b)?(a):(b))
+#endif
+
+
+/* Matrix operations - arbitrary size ----------------------------------------------------- */
+
+typedef struct {
+
+ int Cols, Rows;
+ double** Values;
+
+ } MATN,FAR* LPMATN;
+
+// See B.K.O #148930: compile with lcms v.1.17
+#if (LCMS_VERSION > 116)
+typedef LCMSBOOL BOOL;
+#endif
+
+LPMATN cdecl MATNalloc(int Rows, int Cols);
+void cdecl MATNfree (LPMATN mat);
+LPMATN cdecl MATNmult(LPMATN a1, LPMATN a2);
+double cdecl MATNcross(LPMATN a);
+void cdecl MATNscalar (LPMATN a, double scl, LPMATN b);
+LPMATN cdecl MATNtranspose (LPMATN a);
+BOOL cdecl MATNsolve(LPMATN a, LPMATN b);
+
+
+/* IT8.7 / CGATS.17-200x handling -------------------------------------------------------- */
+
+#define cmsxIT8_ROWS 12
+#define cmsxIT8_COLS 22
+#define cmsxIT8_GRAYCOLS 24
+#define cmsxIT8_NORMAL_PATCHES (cmsxIT8_ROWS*cmsxIT8_COLS + cmsxIT8_GRAYCOLS)
+#define cmsxIT8_CUSTOM_PATCHES 10
+#define cmsxIT8_TOTAL_PATCHES (cmsxIT8_NORMAL_PATCHES + cmsxIT8_CUSTOM_PATCHES)
+
+
+LCMSHANDLE cdecl cmsxIT8Alloc(void);
+void cdecl cmsxIT8Free(LCMSHANDLE cmsxIT8);
+LCMSHANDLE cdecl cmsxIT8LoadFromFile(const char* cFileName);
+LCMSHANDLE cdecl cmsxIT8LoadFromMem(void *Ptr, size_t len);
+BOOL cdecl cmsxIT8SaveToFile(LCMSHANDLE cmsxIT8, const char* cFileName);
+const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8);
+BOOL cdecl cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type);
+const char* cdecl cmsxIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer);
+BOOL cdecl cmsxIT8SetProperty(LCMSHANDLE hcmsxIT8, const char* cProp, const char *Str);
+BOOL cdecl cmsxIT8SetPropertyDbl(LCMSHANDLE hcmsxIT8, const char* cProp, double Val);
+const char* cdecl cmsxIT8GetProperty(LCMSHANDLE hcmsxIT8, const char* cProp);
+double cdecl cmsxIT8GetPropertyDbl(LCMSHANDLE hcmsxIT8, const char* cProp);
+int cdecl cmsxIT8EnumProperties(LCMSHANDLE cmsxIT8, char ***PropertyNames);
+int cdecl cmsxIT8EnumDataFormat(LCMSHANDLE cmsxIT8, char ***SampleNames);
+BOOL cdecl cmsxIT8SetDataFormat(LCMSHANDLE cmsxIT8, int n, const char *Sample);
+
+BOOL cdecl cmsxIT8GetDataSetByPos(LCMSHANDLE IT8, int col, int row, char* Val, int ValBufferLen);
+
+BOOL cdecl cmsxIT8GetDataSet(LCMSHANDLE cmsxIT8, const char* cPatch,
+ const char* cSample,
+ char* Val, int ValBuffLen);
+
+BOOL cdecl cmsxIT8GetDataSetDbl(LCMSHANDLE cmsxIT8, const char* cPatch, const char* cSample, double* v);
+
+BOOL cdecl cmsxIT8SetDataSet(LCMSHANDLE cmsxIT8, const char* cPatch,
+ const char* cSample,
+ char *Val);
+
+BOOL cdecl cmsxIT8SetDataSetDbl(LCMSHANDLE cmsxIT8, const char* cPatch, const char* cSample, double Val);
+
+const char *cdecl cmsxIT8GenericPatchName(int nPatch, char* buffer);
+
+
+
+/* Patch collections (measurement lists) -------------------------------------------------- */
+
+#define PATCH_HAS_Lab 0x00000001
+#define PATCH_HAS_XYZ 0x00000002
+#define PATCH_HAS_RGB 0x00000004
+#define PATCH_HAS_CMY 0x00000008
+#define PATCH_HAS_CMYK 0x00000010
+#define PATCH_HAS_HEXACRM 0x00000020
+#define PATCH_HAS_STD_Lab 0x00010000
+#define PATCH_HAS_STD_XYZ 0x00020000
+#define PATCH_HAS_XYZ_PROOF 0x00100000
+#define PATCH_HAS_MEAN_DE 0x01000000
+#define PATCH_HAS_STD_DE 0x02000000
+#define PATCH_HAS_CHISQ 0x04000000
+
+
+#define MAXPATCHNAMELEN 20
+/* A patch in memory */
+
+typedef struct {
+
+ DWORD dwFlags; /* Is quite possible to have colorant in only */
+ /* some patches of sheet, so mark each entry with */
+ /* the values it has. */
+
+ char Name[MAXPATCHNAMELEN];
+
+ cmsCIELab Lab; /* The tristimulus values of target */
+ cmsCIEXYZ XYZ;
+
+ cmsCIEXYZ XYZProof; /* The absolute XYZ value returned by profile */
+ /* (gamut constrained to device) */
+
+ union { /* The possible colorants. Only one space is */
+ /* allowed...obviously only one set of */
+ /* device-dependent values per patch does make sense. */
+ double RGB[3];
+ double CMY[3];
+ double CMYK[4];
+ double Hexa[MAXCHANNELS];
+
+ } Colorant;
+
+ double dEStd; /* Standard deviation */
+ double ChiSq; /* Chi-square parameter (mean of STD of colorants) */
+ double dEMean; /* Mean dE */
+
+ } PATCH, FAR* LPPATCH;
+
+
+
+/* A set of patches is simply an array of bools, TRUE if the patch */
+/* belong to the set, false otherwise. */
+
+typedef BOOL* SETOFPATCHES;
+
+/* This struct holds whole Patches collection */
+
+typedef struct _measurement {
+
+ int nPatches;
+ LPPATCH Patches;
+ SETOFPATCHES Allowed;
+
+ } MEASUREMENT,FAR *LPMEASUREMENT;
+
+
+void cdecl cmsxPCollFreeMeasurements(LPMEASUREMENT m);
+SETOFPATCHES cdecl cmsxPCollBuildSet(LPMEASUREMENT m, BOOL lDefault);
+
+BOOL cdecl cmsxPCollBuildMeasurement(LPMEASUREMENT m,
+ const char *ReferenceSheet,
+ const char *MeasurementSheet,
+ DWORD dwNeededSamplesType);
+
+int cdecl cmsxPCollCountSet(LPMEASUREMENT m, SETOFPATCHES Set);
+BOOL cdecl cmsxPCollValidatePatches(LPMEASUREMENT m, DWORD dwFlags);
+
+BOOL cdecl cmsxPCollLoadFromSheet(LPMEASUREMENT m, LCMSHANDLE hSheet);
+BOOL cdecl cmsxPCollSaveToSheet(LPMEASUREMENT m, LCMSHANDLE it8);
+
+LPPATCH cdecl cmsxPCollGetPatch(LPMEASUREMENT m, int n);
+LPPATCH cdecl cmsxPCollGetPatchByName(LPMEASUREMENT m, const char* Name, int* lpPos);
+LPPATCH cdecl cmsxPCollGetPatchByPos(LPMEASUREMENT m, int row, int col);
+LPPATCH cdecl cmsxPCollAddPatchRGB(LPMEASUREMENT m, const char *Name,
+ double r, double g, double b,
+ LPcmsCIEXYZ XYZ, LPcmsCIELab Lab);
+
+void cdecl cmsxPCollLinearizePatches(LPMEASUREMENT m, SETOFPATCHES Valids,
+ LPGAMMATABLE Gamma[3]);
+
+/* Extraction utilities */
+
+/* Collect "need" patches of the specific kind, return the number of collected (that */
+/* could be less if set of patches is exhausted) */
+
+void cdecl cmsxPCollPatchesGS(LPMEASUREMENT m, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesNearRGB(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double r, double g, double b, int need, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesNearNeutral(LPMEASUREMENT m, SETOFPATCHES Valids,
+ int need, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesNearPrimary(LPMEASUREMENT m, SETOFPATCHES Valids,
+ int nChannel, int need, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids,
+ double Lmin, double LMax, double a, double b, SETOFPATCHES Result);
+
+int cdecl cmsxPCollPatchesInGamutLUT(LPMEASUREMENT m, SETOFPATCHES Valids,
+ LPLUT Gamut, SETOFPATCHES Result);
+
+/* Find important values */
+
+LPPATCH cdecl cmsxPCollFindWhite(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance);
+LPPATCH cdecl cmsxPCollFindBlack(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance);
+LPPATCH cdecl cmsxPCollFindPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, int Channel, double* Distance);
+
+/* Multiple linear regression stuff ---------------------------------------- */
+
+
+/* A measurement of error */
+
+typedef struct {
+
+ double SSE; /* The error sum of squares */
+ double MSE; /* The error mean sum of squares */
+ double SSR; /* The regression sum of squares */
+ double MSR; /* The regression mean sum of squares */
+ double SSTO; /* Total sum of squares */
+ double F; /* The Fisher-F value (MSR / MSE) */
+ double R2; /* Proportion of variability explained by the regression */
+ /* (root is Pearson correlation coefficient) */
+
+ double R2adj; /* The adjusted coefficient of multiple determination. */
+ /* R2-adjusted or R2adj. This is calculated as */
+ /* R2adj = 1 - (1-R2)(N-n-1)/(N-1) */
+ /* and used as multiple correlation coefficient */
+ /* (really, it should be square root) */
+
+ } MLRSTATISTICS, FAR* LPMLRSTATISTICS;
+
+
+int cdecl cmsxRegressionCreateMatrix(LPMEASUREMENT m, SETOFPATCHES Allowed, int nterms,
+ int ColorSpace,
+ LPMATN* lpMat, LPMLRSTATISTICS Stat);
+
+BOOL cdecl cmsxRegressionRGB2Lab(double r, double g, double b,
+ LPMATN tfm, LPcmsCIELab Lab);
+
+BOOL cdecl cmsxRegressionRGB2XYZ(double r, double g, double b,
+ LPMATN tfm, LPcmsCIEXYZ XYZ);
+
+BOOL cdecl cmsxRegressionInterpolatorRGB(LPMEASUREMENT m,
+ int ColorSpace,
+ int RegressionTerms,
+ BOOL lUseLocalPatches,
+ int MinPatchesToCollect,
+ double r, double g, double b,
+ void* Res);
+
+
+
+/* Levenberg-Marquardt ---------------------------------------------------------------------- */
+
+LCMSHANDLE cdecl cmsxLevenbergMarquardtInit(LPSAMPLEDCURVE x, LPSAMPLEDCURVE y, double sig,
+ double a[],
+ int ma,
+ void (*funcs)(double, double[], double*, double[], int)
+ );
+
+double cdecl cmsxLevenbergMarquardtAlamda(LCMSHANDLE hMRQ);
+double cdecl cmsxLevenbergMarquardtChiSq(LCMSHANDLE hMRQ);
+BOOL cdecl cmsxLevenbergMarquardtIterate(LCMSHANDLE hMRQ);
+BOOL cdecl cmsxLevenbergMarquardtFree(LCMSHANDLE hMRQ);
+
+
+/* Convex hull geometric routines ------------------------------------------------------------ */
+
+LCMSHANDLE cdecl cmsxHullInit(void);
+void cdecl cmsxHullDone(LCMSHANDLE hHull);
+BOOL cdecl cmsxHullAddPoint(LCMSHANDLE hHull, int x, int y, int z);
+BOOL cdecl cmsxHullComputeHull(LCMSHANDLE hHull);
+char cdecl cmsxHullCheckpoint(LCMSHANDLE hHull, int x, int y, int z);
+BOOL cdecl cmsxHullDumpVRML(LCMSHANDLE hHull, const char* fname);
+
+
+/* Linearization ---------------------------------------------------------------------------- */
+
+
+#define MEDIUM_REFLECTIVE_D50 0 /* Used for scanner targets */
+#define MEDIUM_TRANSMISSIVE 1 /* Used for monitors & projectors */
+
+void cdecl cmsxComputeLinearizationTables(LPMEASUREMENT m,
+ int ColorSpace,
+ LPGAMMATABLE Lin[3],
+ int nResultingPoints,
+ int Medium);
+
+
+void cdecl cmsxCompleteLabOfPatches(LPMEASUREMENT m, SETOFPATCHES Valids, int Medium);
+LPGAMMATABLE cdecl cmsxEstimateGamma(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints);
+
+void cdecl cmsxApplyLinearizationTable(double In[3], LPGAMMATABLE Gamma[3], double Out[3]);
+void cdecl cmsxApplyLinearizationGamma(WORD In[3], LPGAMMATABLE Gamma[3], WORD Out[3]);
+
+/* Support routines ---------------------------------------------------------------------- */
+
+double cdecl _cmsxSaturate65535To255(double d);
+double cdecl _cmsxSaturate255To65535(double d);
+void cdecl _cmsxClampXYZ100(LPcmsCIEXYZ xyz);
+
+/* Matrix shaper profiler API ------------------------------------------------------------- */
+
+
+BOOL cdecl cmsxComputeMatrixShaper(const char* ReferenceSheet,
+ const char* MeasurementSheet,
+ int Medium,
+ LPGAMMATABLE TransferCurves[3],
+ LPcmsCIEXYZ WhitePoint,
+ LPcmsCIEXYZ BlackPoint,
+ LPcmsCIExyYTRIPLE Primaries);
+
+
+/* Common to all profilers ------------------------------------------------------------------- */
+
+#define MAX_STR 256
+
+typedef int (* cmsxGAUGER)(const char *Label, int nMin, int nMax, int Pos);
+typedef int (* cmsxPRINTF)(const char *Frm, ...);
+
+typedef struct {
+
+ /* Files */
+ char ReferenceSheet[MAX_PATH];
+ char MeasurementSheet[MAX_PATH];
+ char OutputProfileFile[MAX_PATH];
+
+ /* Some infos */
+ char Description[MAX_STR];
+ char Manufacturer[MAX_STR];
+ char Model[MAX_STR];
+ char Copyright[MAX_STR];
+
+ /* Callbacks */
+ cmsxGAUGER Gauger;
+ cmsxPRINTF printf;
+
+ /* EndPoints */
+ cmsCIEXYZ WhitePoint; /* Black point in 0.xxx notation */
+ cmsCIEXYZ BlackPoint; /* Black point in 0.xxx notation */
+ cmsCIExyYTRIPLE Primaries; /* The primaries */
+ LPGAMMATABLE Gamma[3]; /* Gamma curves */
+
+ /* Profile */
+ cmsHPROFILE hProfile; /* handle to profile */
+
+ icProfileClassSignature DeviceClass;
+ icColorSpaceSignature ColorSpace;
+
+ int PCSType; /* PT_XYZ or PT_Lab */
+ int CLUTPoints; /* Final CLUT resolution */
+ int ProfileVerbosityLevel; /* 0=minimum, 1=additional, 2=Verbose, 3=Any suitable */
+
+
+ /* Measurement */
+ MEASUREMENT m; /* Contains list of available patches */
+ int Medium;
+
+
+ /* RGB Gamut hull */
+ LCMSHANDLE hRGBHull; /* Contains bobbin of valid RGB values */
+
+ /* CIECAM97s */
+ BOOL lUseCIECAM97s; /* Use CIECAM97s for chromatic adaptation? */
+
+ cmsViewingConditions device; /* Viewing condition of source */
+ cmsViewingConditions PCS; /* Viewing condition of PCS */
+
+ LCMSHANDLE hDevice; /* CIECAM97s models used for adaptation */
+ LCMSHANDLE hPCS; /* and viewing conditions */
+
+
+ } PROFILERCOMMONDATA,FAR* LPPROFILERCOMMONDATA;
+
+
+/* Shared routines */
+
+BOOL cdecl cmsxEmbedCharTarget(LPPROFILERCOMMONDATA hdr);
+BOOL cdecl cmsxEmbedMatrixShaper(LPPROFILERCOMMONDATA hdr);
+BOOL cdecl cmsxEmbedTextualInfo(LPPROFILERCOMMONDATA hdr);
+
+int cdecl cmsxFindOptimumNumOfTerms(LPPROFILERCOMMONDATA hdr, int nMaxTerms, BOOL* lAllOk);
+void cdecl cmsxChromaticAdaptationAndNormalization(LPPROFILERCOMMONDATA hdr, LPcmsCIEXYZ xyz, BOOL lReverse);
+void cdecl cmsxInitPCSViewingConditions(LPPROFILERCOMMONDATA hdr);
+void cdecl cmsxComputeGamutHull(LPPROFILERCOMMONDATA hdr);
+BOOL cdecl cmsxChoosePCS(LPPROFILERCOMMONDATA hdr);
+
+/* Monitor profiler API ------------------------------------------------------------------- */
+
+typedef struct {
+
+ PROFILERCOMMONDATA hdr;
+
+
+ LPGAMMATABLE Prelinearization[3]; /* Canonic gamma */
+ LPGAMMATABLE ReverseTables[3]; /* Reverse (direct) gamma */
+ LPGAMMATABLE PreLab[3];
+ LPGAMMATABLE PreLabRev[3];
+
+
+ MAT3 PrimariesMatrix;
+ MAT3 PrimariesMatrixRev;
+
+
+ } MONITORPROFILERDATA,FAR* LPMONITORPROFILERDATA;
+
+
+
+BOOL cdecl cmsxMonitorProfilerInit(LPMONITORPROFILERDATA sys);
+BOOL cdecl cmsxMonitorProfilerDo(LPMONITORPROFILERDATA sys);
+
+
+/* Scanner profiler API ------------------------------------------------------------------- */
+
+
+typedef struct {
+
+ PROFILERCOMMONDATA hdr;
+
+ LPGAMMATABLE Prelinearization[3];
+
+ LPMATN HiTerms; /* Regression matrix of many terms */
+ LPMATN LoTerms; /* Low order regression matrix used for extrapolation */
+
+ BOOL lLocalConvergenceExtrapolation;
+
+
+ } SCANNERPROFILERDATA,FAR* LPSCANNERPROFILERDATA;
+
+
+
+
+BOOL cdecl cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys);
+BOOL cdecl cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys);
+
+/* ----------------------------------------------------------- end of profilers */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#define __cmsprf_H
+#endif