You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/libs/lprof/cmspcoll.cpp

1046 lines
26 KiB

/* */
/* 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;
}