summaryrefslogtreecommitdiffstats
path: root/digikam/libs/lprof/cmspcoll.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'digikam/libs/lprof/cmspcoll.cpp')
-rw-r--r--digikam/libs/lprof/cmspcoll.cpp1045
1 files changed, 1045 insertions, 0 deletions
diff --git a/digikam/libs/lprof/cmspcoll.cpp b/digikam/libs/lprof/cmspcoll.cpp
new file mode 100644
index 0000000..3177e6b
--- /dev/null
+++ b/digikam/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 02111-1307, 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;
+}