0,0 → 1,1045 |
// |
// 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., 59 Temple Place - Suite 330, 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 = calloc(m -> nPatches, sizeof(PATCH)); |
|
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; |
} |
Property changes: |
Added: svn:eol-style |
## -0,0 +1 ## |
+native |
\ No newline at end of property |
Added: svn:keywords |
## -0,0 +1 ## |
+Author Date Id Revision |
\ No newline at end of property |