diff options
Diffstat (limited to 'src/libs/lprof')
-rw-r--r-- | src/libs/lprof/Makefile.am | 15 | ||||
-rw-r--r-- | src/libs/lprof/cmshull.cpp | 1480 | ||||
-rw-r--r-- | src/libs/lprof/cmslm.cpp | 288 | ||||
-rw-r--r-- | src/libs/lprof/cmslnr.cpp | 560 | ||||
-rw-r--r-- | src/libs/lprof/cmsmatn.cpp | 323 | ||||
-rw-r--r-- | src/libs/lprof/cmsmkmsh.cpp | 346 | ||||
-rw-r--r-- | src/libs/lprof/cmsmntr.cpp | 371 | ||||
-rw-r--r-- | src/libs/lprof/cmsoutl.cpp | 284 | ||||
-rw-r--r-- | src/libs/lprof/cmspcoll.cpp | 1045 | ||||
-rw-r--r-- | src/libs/lprof/cmsprf.cpp | 439 | ||||
-rw-r--r-- | src/libs/lprof/cmsreg.cpp | 558 | ||||
-rw-r--r-- | src/libs/lprof/cmsscn.cpp | 422 | ||||
-rw-r--r-- | src/libs/lprof/cmssheet.cpp | 1746 | ||||
-rw-r--r-- | src/libs/lprof/lcmsprf.h | 485 |
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 |