/* */ /* Little cms - profiler construction set */ /* Copyright (C) 1998-2001 Marti Maria */ /* */ /* 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; }