// This file is available under and governed by the GNU General Public // License version 2 only, as published by the Free Software Foundation. // However, the following notice accompanied the original version of this // file: // //--------------------------------------------------------------------------------- // // Little Color Management System // Copyright (c) 1998-2022 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software // is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // //--------------------------------------------------------------------------------- //
a = sp ->r * sin_theta * sin_alpha;
b = sp ->r * sin_theta * cos_alpha;
L = sp ->r * cos_theta;
v ->n[VX] = L;
v ->n[VY] = a;
v ->n[VZ] = b;
}
// Quantize sector of a spherical coordinate. Saturate 360, 180 to last sector // The limits are the centers of each sector, so static void QuantizeToSector(const cmsSpherical* sp, int* alpha, int* theta)
{
*alpha = (int) floor(((sp->alpha * (SECTORS)) / 360.0) );
*theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) );
if (*alpha >= SECTORS)
*alpha = SECTORS-1; if (*theta >= SECTORS)
*theta = SECTORS-1;
}
// Line determined by 2 points static void LineOf2Points(cmsLine* line, cmsVEC3* a, cmsVEC3* b)
{
_cmsVEC3init(&line ->a, a ->n[VX], a ->n[VY], a ->n[VZ]);
_cmsVEC3init(&line ->u, b ->n[VX] - a ->n[VX],
b ->n[VY] - a ->n[VY],
b ->n[VZ] - a ->n[VZ]);
}
// Evaluate parametric line static void GetPointOfLine(cmsVEC3* p, const cmsLine* line, cmsFloat64Number t)
{
p ->n[VX] = line ->a.n[VX] + t * line->u.n[VX];
p ->n[VY] = line ->a.n[VY] + t * line->u.n[VY];
p ->n[VZ] = line ->a.n[VZ] + t * line->u.n[VZ];
}
static
cmsBool ClosestLineToLine(cmsVEC3* r, const cmsLine* line1, const cmsLine* line2)
{
cmsFloat64Number a, b, c, d, e, D;
cmsFloat64Number sc, sN, sD; //cmsFloat64Number tc; // left for future use
cmsFloat64Number tN, tD;
cmsVEC3 w0;
_cmsVEC3minus(&w0, &line1 ->a, &line2 ->a);
a = _cmsVEC3dot(&line1 ->u, &line1 ->u);
b = _cmsVEC3dot(&line1 ->u, &line2 ->u);
c = _cmsVEC3dot(&line2 ->u, &line2 ->u);
d = _cmsVEC3dot(&line1 ->u, &w0);
e = _cmsVEC3dot(&line2 ->u, &w0);
D = a*c - b * b; // Denominator
sD = tD = D; // default sD = D >= 0
if (D < MATRIX_DET_TOLERANCE) { // the lines are almost parallel
sN = 0.0; // force using point P0 on segment S1
sD = 1.0; // to prevent possible division by 0.0 later
tN = e;
tD = c;
} else { // get the closest points on the infinite lines
sN = (b*e - c*d);
tN = (a*e - b*d);
if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
sN = 0.0;
tN = e;
tD = c;
} elseif (sN > sD) { // sc > 1 => the s=1 edge is visible
sN = sD;
tN = e + b;
tD = c;
}
}
if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
tN = 0.0; // recompute sc for this edge if (-d < 0.0)
sN = 0.0; elseif (-d > a)
sN = sD; else {
sN = -d;
sD = a;
}
} elseif (tN > tD) { // tc > 1 => the t=1 edge is visible
tN = tD;
// recompute sc for this edge if ((-d + b) < 0.0)
sN = 0; elseif ((-d + b) > a)
sN = sD; else {
sN = (-d + b);
sD = a;
}
} // finally do the division to get sc and tc
sc = (fabs(sN) < MATRIX_DET_TOLERANCE ? 0.0 : sN / sD); //tc = (fabs(tN) < MATRIX_DET_TOLERANCE ? 0.0 : tN / tD); // left for future use.
// Auxiliary to retrieve a pointer to the segmentr containing the Lab value static
cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp)
{
cmsVEC3 v; int alpha, theta;
// Center L* by subtracting half of its domain, that's 50
_cmsVEC3init(&v, Lab ->L - 50.0, Lab ->a, Lab ->b);
// Convert to spherical coordinates
ToSpherical(sp, &v);
if (sp ->r < 0 || sp ->alpha < 0 || sp->theta < 0) {
cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, "spherical value out of range"); return NULL;
}
// On which sector it falls?
QuantizeToSector(sp, &alpha, &theta);
if (alpha < 0 || theta < 0 || alpha >= SECTORS || theta >= SECTORS) {
cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, " quadrant out of range"); return NULL;
}
// Get pointer to the sector return &gbd ->Gamut[theta][alpha];
}
// Add a point to gamut descriptor. Point to add is in Lab color space. // GBD is centered on a=b=0 and L*=50
cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab)
{
cmsGDB* gbd = (cmsGDB*) hGBD;
cmsGDBPoint* ptr;
cmsSpherical sp;
// Get pointer to the sector
ptr = GetPoint(gbd, Lab, &sp); if (ptr == NULL) returnFALSE;
// If no samples at this sector, add it if (ptr ->Type == GP_EMPTY) {
ptr -> Type = GP_SPECIFIED;
ptr -> p = sp;
} else {
// Substitute only if radius is greater if (sp.r > ptr -> p.r) {
ptr -> Type = GP_SPECIFIED;
ptr -> p = sp;
}
}
returnTRUE;
}
// Check if a given point falls inside gamut
cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab)
{
cmsGDB* gbd = (cmsGDB*) hGBD;
cmsGDBPoint* ptr;
cmsSpherical sp;
// Get pointer to the sector
ptr = GetPoint(gbd, Lab, &sp); if (ptr == NULL) returnFALSE;
// If no samples at this sector, return no data if (ptr ->Type == GP_EMPTY) returnFALSE;
static int FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[])
{ int nSectors = 0; int a, t;
cmsUInt32Number i;
cmsGDBPoint* pt;
for (i=0; i < NSTEPS; i++) {
a = alpha + Spiral[i].AdvX;
t = theta + Spiral[i].AdvY;
// Cycle at the end
a %= SECTORS;
t %= SECTORS;
// Cycle at the begin if (a < 0) a = SECTORS + a; if (t < 0) t = SECTORS + t;
pt = &gbd ->Gamut[t][a];
if (pt -> Type != GP_EMPTY) {
Close[nSectors++] = pt;
}
}
return nSectors;
}
// Interpolate a missing sector. Method identifies whatever this is top, bottom or mid static
cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta)
{
cmsSpherical sp;
cmsVEC3 Lab;
cmsVEC3 Centre;
cmsLine ray; int nCloseSectors;
cmsGDBPoint* Close[NSTEPS + 1];
cmsSpherical closel, templ;
cmsLine edge; int k, m;
// Is that point already specified? if (gbd ->Gamut[theta][alpha].Type != GP_EMPTY) returnTRUE;
// Fill close points
nCloseSectors = FindNearSectors(gbd, alpha, theta, Close);
// Find a central point on the sector
sp.alpha = (cmsFloat64Number) ((alpha + 0.5) * 360.0) / (SECTORS);
sp.theta = (cmsFloat64Number) ((theta + 0.5) * 180.0) / (SECTORS);
sp.r = 50.0;
// Convert to Cartesian
ToCartesian(&Lab, &sp);
// Create a ray line from centre to this point
_cmsVEC3init(&Centre, 50.0, 0, 0);
LineOf2Points(&ray, &Lab, &Centre);
// For all close sectors
closel.r = 0.0;
closel.alpha = 0;
closel.theta = 0;
for (k=0; k < nCloseSectors; k++) {
for(m = k+1; m < nCloseSectors; m++) {
cmsVEC3 temp, a1, a2;
// A line from sector to sector
ToCartesian(&a1, &Close[k]->p);
ToCartesian(&a2, &Close[m]->p);
LineOf2Points(&edge, &a1, &a2);
// Find a line
ClosestLineToLine(&temp, &ray, &edge);
// Convert to spherical
ToSpherical(&templ, &temp);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.