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.
347 lines
11 KiB
347 lines
11 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"
|
|
|
|
|
|
BOOL cdecl cmsxComputeMatrixShaper(const char* ReferenceSheet,
|
|
const char* MeasurementSheet,
|
|
int Medium,
|
|
LPGAMMATABLE TransferCurves[3],
|
|
LPcmsCIEXYZ WhitePoint,
|
|
LPcmsCIEXYZ BlackPoint,
|
|
LPcmsCIExyYTRIPLE Primaries);
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- Implementation */
|
|
|
|
|
|
static
|
|
void Div100(LPcmsCIEXYZ xyz)
|
|
{
|
|
xyz -> X /= 100; xyz -> Y /= 100; xyz -> Z /= 100;
|
|
}
|
|
|
|
|
|
|
|
/* Compute endpoints */
|
|
|
|
static
|
|
BOOL ComputeWhiteAndBlackPoints(LPMEASUREMENT Linearized,
|
|
LPGAMMATABLE TransferCurves[3],
|
|
LPcmsCIEXYZ Black, LPcmsCIEXYZ White)
|
|
{
|
|
|
|
double Zeroes[3], Ones[3], lmin[3], lmax[3];
|
|
|
|
SETOFPATCHES Neutrals = cmsxPCollBuildSet(Linearized, false);
|
|
|
|
cmsxPCollPatchesNearNeutral(Linearized, Linearized->Allowed,
|
|
15, Neutrals);
|
|
|
|
Zeroes[0] = Zeroes[1] = Zeroes[2] = 0.0;
|
|
Ones[0] = Ones[1] = Ones[2] = 255.0;
|
|
|
|
|
|
cmsxApplyLinearizationTable(Zeroes, TransferCurves, lmin);
|
|
cmsxApplyLinearizationTable(Ones, TransferCurves, lmax);
|
|
|
|
|
|
/* Global regression to find White & Black points */
|
|
if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ,
|
|
4,
|
|
true,
|
|
12,
|
|
lmin[0], lmin[1], lmin[2],
|
|
Black)) return false;
|
|
|
|
if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ,
|
|
4,
|
|
true,
|
|
12,
|
|
lmax[0], lmax[1], lmax[2],
|
|
White)) return false;
|
|
|
|
_cmsxClampXYZ100(White);
|
|
_cmsxClampXYZ100(Black);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
/* Study convergence of primary axis */
|
|
|
|
static
|
|
BOOL ComputePrimary(LPMEASUREMENT Linearized,
|
|
LPGAMMATABLE TransferCurves[3],
|
|
int n,
|
|
LPcmsCIExyY Primary)
|
|
{
|
|
|
|
double Ones[3], lmax[3];
|
|
cmsCIEXYZ PrimXYZ;
|
|
SETOFPATCHES SetPrimary;
|
|
int nR;
|
|
|
|
|
|
/* At first, try to see if primaries are already in measurement */
|
|
|
|
SetPrimary = cmsxPCollBuildSet(Linearized, false);
|
|
nR = cmsxPCollPatchesNearPrimary(Linearized, Linearized->Allowed,
|
|
n, 32, SetPrimary);
|
|
|
|
Ones[0] = Ones[1] = Ones[2] = 0;
|
|
Ones[n] = 255.0;
|
|
|
|
cmsxApplyLinearizationTable(Ones, TransferCurves, lmax);
|
|
|
|
/* Do incremental regression to find primaries */
|
|
if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ,
|
|
4,
|
|
false,
|
|
12,
|
|
lmax[0], lmax[1], lmax[2],
|
|
&PrimXYZ)) return false;
|
|
|
|
_cmsxClampXYZ100(&PrimXYZ);
|
|
cmsXYZ2xyY(Primary, &PrimXYZ);
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Does compute a matrix-shaper based on patches. */
|
|
|
|
static
|
|
double Clip(double d)
|
|
{
|
|
return d > 0 ? d: 0;
|
|
}
|
|
|
|
|
|
BOOL cmsxComputeMatrixShaper(const char* ReferenceSheet,
|
|
const char* MeasurementSheet,
|
|
int Medium,
|
|
LPGAMMATABLE TransferCurves[3],
|
|
LPcmsCIEXYZ WhitePoint,
|
|
LPcmsCIEXYZ BlackPoint,
|
|
LPcmsCIExyYTRIPLE Primaries)
|
|
{
|
|
|
|
MEASUREMENT Linearized;
|
|
cmsCIEXYZ Black, White;
|
|
cmsCIExyYTRIPLE PrimarySet;
|
|
LPPATCH PatchWhite, PatchBlack;
|
|
LPPATCH PatchRed, PatchGreen, PatchBlue;
|
|
double Distance;
|
|
|
|
/* Load sheets */
|
|
|
|
if (!cmsxPCollBuildMeasurement(&Linearized,
|
|
ReferenceSheet,
|
|
MeasurementSheet,
|
|
PATCH_HAS_XYZ|PATCH_HAS_RGB)) return false;
|
|
|
|
|
|
|
|
/* Any patch to deal of? */
|
|
if (cmsxPCollCountSet(&Linearized, Linearized.Allowed) <= 0) return false;
|
|
|
|
|
|
/* Try to see if proper primaries, white and black already present */
|
|
PatchWhite = cmsxPCollFindWhite(&Linearized, Linearized.Allowed, &Distance);
|
|
if (Distance != 0)
|
|
PatchWhite = NULL;
|
|
|
|
PatchBlack = cmsxPCollFindBlack(&Linearized, Linearized.Allowed, &Distance);
|
|
if (Distance != 0)
|
|
PatchBlack = NULL;
|
|
|
|
PatchRed = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 0, &Distance);
|
|
if (Distance != 0)
|
|
PatchRed = NULL;
|
|
|
|
PatchGreen = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 1, &Distance);
|
|
if (Distance != 0)
|
|
PatchGreen = NULL;
|
|
|
|
PatchBlue = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 2, &Distance);
|
|
if (Distance != 0)
|
|
PatchBlue= NULL;
|
|
|
|
/* If we got primaries, then we can also get prelinearization */
|
|
/* by Levenberg-Marquardt. This applies on monitor profiles */
|
|
|
|
if (PatchWhite && PatchRed && PatchGreen && PatchBlue) {
|
|
|
|
/* Build matrix with primaries */
|
|
|
|
MAT3 Mat, MatInv;
|
|
LPSAMPLEDCURVE Xr,Yr, Xg, Yg, Xb, Yb;
|
|
int i, nRes, cnt;
|
|
|
|
VEC3init(&Mat.v[0], PatchRed->XYZ.X, PatchGreen->XYZ.X, PatchBlue->XYZ.X);
|
|
VEC3init(&Mat.v[1], PatchRed->XYZ.Y, PatchGreen->XYZ.Y, PatchBlue->XYZ.Y);
|
|
VEC3init(&Mat.v[2], PatchRed->XYZ.Z, PatchGreen->XYZ.Z, PatchBlue->XYZ.Z);
|
|
|
|
/* Invert matrix */
|
|
MAT3inverse(&Mat, &MatInv);
|
|
|
|
nRes = cmsxPCollCountSet(&Linearized, Linearized.Allowed);
|
|
|
|
Xr = cmsAllocSampledCurve(nRes);
|
|
Yr = cmsAllocSampledCurve(nRes);
|
|
Xg = cmsAllocSampledCurve(nRes);
|
|
Yg = cmsAllocSampledCurve(nRes);
|
|
Xb = cmsAllocSampledCurve(nRes);
|
|
Yb = cmsAllocSampledCurve(nRes);
|
|
|
|
/* Convert XYZ of all patches to RGB */
|
|
cnt = 0;
|
|
for (i=0; i < Linearized.nPatches; i++) {
|
|
|
|
if (Linearized.Allowed[i]) {
|
|
|
|
VEC3 RGBprime, XYZ;
|
|
LPPATCH p;
|
|
|
|
p = Linearized.Patches + i;
|
|
XYZ.n[0] = p -> XYZ.X;
|
|
XYZ.n[1] = p -> XYZ.Y;
|
|
XYZ.n[2] = p -> XYZ.Z;
|
|
|
|
MAT3eval(&RGBprime, &MatInv, &XYZ);
|
|
|
|
Xr ->Values[cnt] = p ->Colorant.RGB[0];
|
|
Yr ->Values[cnt] = Clip(RGBprime.n[0]);
|
|
|
|
Xg ->Values[cnt] = p ->Colorant.RGB[1];
|
|
Yg ->Values[cnt] = Clip(RGBprime.n[1]);
|
|
|
|
Xb ->Values[cnt] = p ->Colorant.RGB[2];
|
|
Yb ->Values[cnt] = Clip(RGBprime.n[2]);
|
|
|
|
cnt++;
|
|
|
|
}
|
|
}
|
|
|
|
TransferCurves[0] = cmsxEstimateGamma(Xr, Yr, 1024);
|
|
TransferCurves[1] = cmsxEstimateGamma(Xg, Yg, 1024);
|
|
TransferCurves[2] = cmsxEstimateGamma(Xb, Yb, 1024);
|
|
|
|
if (WhitePoint) {
|
|
|
|
WhitePoint->X = PatchWhite->XYZ.X;
|
|
WhitePoint->Y= PatchWhite ->XYZ.Y;
|
|
WhitePoint->Z= PatchWhite ->XYZ.Z;
|
|
}
|
|
|
|
if (BlackPoint && PatchBlack) {
|
|
|
|
BlackPoint->X = PatchBlack ->XYZ.X;
|
|
BlackPoint->Y = PatchBlack ->XYZ.Y;
|
|
BlackPoint->Z = PatchBlack ->XYZ.Z;
|
|
}
|
|
|
|
if (Primaries) {
|
|
|
|
cmsXYZ2xyY(&Primaries->Red, &PatchRed ->XYZ);
|
|
cmsXYZ2xyY(&Primaries->Green, &PatchGreen ->XYZ);
|
|
cmsXYZ2xyY(&Primaries->Blue, &PatchBlue ->XYZ);
|
|
|
|
}
|
|
|
|
|
|
cmsFreeSampledCurve(Xr);
|
|
cmsFreeSampledCurve(Yr);
|
|
cmsFreeSampledCurve(Xg);
|
|
cmsFreeSampledCurve(Yg);
|
|
cmsFreeSampledCurve(Xb);
|
|
cmsFreeSampledCurve(Yb);
|
|
|
|
cmsxPCollFreeMeasurements(&Linearized);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compute prelinearization */
|
|
cmsxComputeLinearizationTables(&Linearized, PT_XYZ, TransferCurves, 1024, Medium);
|
|
|
|
/* Linearize measurements */
|
|
cmsxPCollLinearizePatches(&Linearized, Linearized.Allowed, TransferCurves);
|
|
|
|
|
|
/* Endpoints */
|
|
ComputeWhiteAndBlackPoints(&Linearized, TransferCurves, &Black, &White);
|
|
|
|
/* Primaries */
|
|
ComputePrimary(&Linearized, TransferCurves, 0, &PrimarySet.Red);
|
|
ComputePrimary(&Linearized, TransferCurves, 1, &PrimarySet.Green);
|
|
ComputePrimary(&Linearized, TransferCurves, 2, &PrimarySet.Blue);
|
|
|
|
|
|
if (BlackPoint) {
|
|
*BlackPoint = Black;
|
|
Div100(BlackPoint);
|
|
}
|
|
|
|
if (WhitePoint) {
|
|
*WhitePoint = White;
|
|
Div100(WhitePoint);
|
|
}
|
|
|
|
|
|
if (Primaries) {
|
|
*Primaries = PrimarySet;
|
|
}
|
|
|
|
cmsxPCollFreeMeasurements(&Linearized);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|