static void ComputeChromaticAdaptation(LPMAT3 Conversion, LPcmsCIEXYZ SourceWhitePoint, LPcmsCIEXYZ DestWhitePoint, LPMAT3 Chad) { MAT3 Chad_Inv; VEC3 ConeSourceXYZ, ConeSourceRGB; VEC3 ConeDestXYZ, ConeDestRGB; MAT3 Cone, Tmp; Tmp = *Chad; MAT3inverse(&Tmp, &Chad_Inv); VEC3init(&ConeSourceXYZ, SourceWhitePoint -> X, SourceWhitePoint -> Y, SourceWhitePoint -> Z); VEC3init(&ConeDestXYZ, DestWhitePoint -> X, DestWhitePoint -> Y, DestWhitePoint -> Z); MAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ); MAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ); // Build matrix VEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0); VEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0); VEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]); // Normalize MAT3per(&Tmp, &Cone, Chad); MAT3per(Conversion, &Chad_Inv, &Tmp); }
static void ComputeBlackPointCompensationFactors(LPcmsCIEXYZ BlackPointIn, LPcmsCIEXYZ WhitePointIn, LPcmsCIEXYZ IlluminantIn, LPcmsCIEXYZ BlackPointOut, LPcmsCIEXYZ WhitePointOut, LPcmsCIEXYZ IlluminantOut, LPMAT3 m, LPVEC3 of) { cmsCIEXYZ RelativeBlackPointIn, RelativeBlackPointOut; double ax, ay, az, bx, by, bz, tx, ty, tz; // At first, convert both black points to relative. cmsAdaptToIlluminant(&RelativeBlackPointIn, WhitePointIn, IlluminantIn, BlackPointIn); cmsAdaptToIlluminant(&RelativeBlackPointOut, WhitePointOut, IlluminantOut, BlackPointOut); // Now we need to compute a matrix plus an offset m and of such of // [m]*bpin + off = bpout // [m]*D50 + off = D50 // // This is a linear scaling in the form ax+b, where // a = (bpout - D50) / (bpin - D50) // b = - D50* (bpout - bpin) / (bpin - D50) tx = RelativeBlackPointIn.X - IlluminantIn ->X; ty = RelativeBlackPointIn.Y - IlluminantIn ->Y; tz = RelativeBlackPointIn.Z - IlluminantIn ->Z; ax = (RelativeBlackPointOut.X - IlluminantOut ->X) / tx; ay = (RelativeBlackPointOut.Y - IlluminantOut ->Y) / ty; az = (RelativeBlackPointOut.Z - IlluminantOut ->Z) / tz; bx = - IlluminantOut -> X * (RelativeBlackPointOut.X - RelativeBlackPointIn.X) / tx; by = - IlluminantOut -> Y * (RelativeBlackPointOut.Y - RelativeBlackPointIn.Y) / ty; bz = - IlluminantOut -> Z * (RelativeBlackPointOut.Z - RelativeBlackPointIn.Z) / tz; MAT3identity(m); m->v[VX].n[0] = ax; m->v[VY].n[1] = ay; m->v[VZ].n[2] = az; VEC3init(of, bx, by, bz); }
BOOL LCMSEXPORT cmsBuildRGB2XYZtransferMatrix(LPMAT3 r, LPcmsCIExyY WhitePt, LPcmsCIExyYTRIPLE Primrs) { VEC3 WhitePoint, Coef; MAT3 Result, Primaries; double xn, yn; double xr, yr; double xg, yg; double xb, yb; xn = WhitePt -> x; yn = WhitePt -> y; xr = Primrs -> Red.x; yr = Primrs -> Red.y; xg = Primrs -> Green.x; yg = Primrs -> Green.y; xb = Primrs -> Blue.x; yb = Primrs -> Blue.y; // Build Primaries matrix VEC3init(&Primaries.v[0], xr, xg, xb); VEC3init(&Primaries.v[1], yr, yg, yb); VEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb)); // Result = Primaries ^ (-1) inverse matrix if (!MAT3inverse(&Primaries, &Result)) return FALSE; VEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn); // Across inverse primaries ... MAT3eval(&Coef, &Result, &WhitePoint); // Give us the Coefs, then I build transformation matrix VEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb); VEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb); VEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb)); return TRUE; }
static void Rel2RelStepAbsCoefs(double AdaptationState, LPcmsCIEXYZ BlackPointIn, LPcmsCIEXYZ WhitePointIn, LPcmsCIEXYZ IlluminantIn, LPMAT3 ChromaticAdaptationMatrixIn, LPcmsCIEXYZ BlackPointOut, LPcmsCIEXYZ WhitePointOut, LPcmsCIEXYZ IlluminantOut, LPMAT3 ChromaticAdaptationMatrixOut, LPMAT3 m, LPVEC3 of) { VEC3 WtPtIn, WtPtInAdapted; VEC3 WtPtOut, WtPtOutAdapted; MAT3 Scale, m1, m2, m3; VEC3init(&WtPtIn, WhitePointIn->X, WhitePointIn->Y, WhitePointIn->Z); MAT3eval(&WtPtInAdapted, ChromaticAdaptationMatrixIn, &WtPtIn); VEC3init(&WtPtOut, WhitePointOut->X, WhitePointOut->Y, WhitePointOut->Z); MAT3eval(&WtPtOutAdapted, ChromaticAdaptationMatrixOut, &WtPtOut); VEC3init(&Scale.v[0], WtPtInAdapted.n[0] / WtPtOutAdapted.n[0], 0, 0); VEC3init(&Scale.v[1], 0, WtPtInAdapted.n[1] / WtPtOutAdapted.n[1], 0); VEC3init(&Scale.v[2], 0, 0, WtPtInAdapted.n[2] / WtPtOutAdapted.n[2]); // Adaptation state if (AdaptationState == 1.0) { // Observer is fully adapted. Keep chromatic adaptation CopyMemory(m, &Scale, sizeof(MAT3)); } else { // Observer is not adapted, undo the chromatic adaptation m1 = *ChromaticAdaptationMatrixIn; MAT3inverse(&m1, &m2); MAT3per(&m3, &m2, &Scale); MAT3per(m, &m3, ChromaticAdaptationMatrixOut); } VEC3init(of, 0.0, 0.0, 0.0); }
BOOL LCMSEXPORT cmsAdaptToIlluminant(LPcmsCIEXYZ Result, LPcmsCIEXYZ SourceWhitePt, LPcmsCIEXYZ Illuminant, LPcmsCIEXYZ Value) { MAT3 Bradford; VEC3 In, Out; // BradfordLamRiggChromaticAdaptation(&Bradford, SourceWhitePt, Illuminant); cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant); VEC3init(&In, Value -> X, Value -> Y, Value -> Z); MAT3eval(&Out, &Bradford, &In); Result -> X = Out.n[0]; Result -> Y = Out.n[1]; Result -> Z = Out.n[2]; return TRUE; }
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; }
int cmsChooseCnvrt(int Absolute, int Phase1, LPcmsCIEXYZ BlackPointIn, LPcmsCIEXYZ WhitePointIn, LPcmsCIEXYZ IlluminantIn, LPMAT3 ChromaticAdaptationMatrixIn, int Phase2, LPcmsCIEXYZ BlackPointOut, LPcmsCIEXYZ WhitePointOut, LPcmsCIEXYZ IlluminantOut, LPMAT3 ChromaticAdaptationMatrixOut, int DoBlackPointCompensation, double AdaptationState, _cmsADJFN *fn1, LPWMAT3 wm, LPWVEC3 wof) { int rc; MAT3 m; VEC3 of; MAT3identity(&m); VEC3init(&of, 0, 0, 0); switch (Phase1) { // Input LUT is giving XYZ relative values. case XYZRel: rc = FromXYZRelLUT(Absolute, BlackPointIn, WhitePointIn, IlluminantIn, ChromaticAdaptationMatrixIn, Phase2, BlackPointOut, WhitePointOut, IlluminantOut, ChromaticAdaptationMatrixOut, DoBlackPointCompensation, AdaptationState, fn1, &m, &of); break; // Input LUT is giving Lab relative values case LabRel: rc = FromLabRelLUT(Absolute, BlackPointIn, WhitePointIn, IlluminantIn, ChromaticAdaptationMatrixIn, Phase2, BlackPointOut, WhitePointOut, IlluminantOut, ChromaticAdaptationMatrixOut, DoBlackPointCompensation, AdaptationState, fn1, &m, &of); break; // Unrecognized combination default: cmsSignalError(LCMS_ERRC_ABORTED, "(internal) Phase error"); return FALSE; } MAT3toFix(wm, &m); VEC3toFix(wof, &of); // Do some optimization -- discard conversion if identity parameters. if (*fn1 == XYZ2XYZ || *fn1 == Lab2XYZ2Lab) { if (IdentityParameters(wm, wof)) *fn1 = NULL; } return rc; }
static int FromXYZRelLUT(int Absolute, LPcmsCIEXYZ BlackPointIn, LPcmsCIEXYZ WhitePointIn, LPcmsCIEXYZ IlluminantIn, LPMAT3 ChromaticAdaptationMatrixIn, int Phase2, LPcmsCIEXYZ BlackPointOut, LPcmsCIEXYZ WhitePointOut, LPcmsCIEXYZ IlluminantOut, LPMAT3 ChromaticAdaptationMatrixOut, int DoBlackPointCompensation, double AdaptationState, _cmsADJFN *fn1, LPMAT3 m, LPVEC3 of) { switch (Phase2) { // From relative XYZ to Relative XYZ. case XYZRel: if (Absolute) { // From input relative to absolute, and then // back to output relative Rel2RelStepAbsCoefs(AdaptationState, BlackPointIn, WhitePointIn, IlluminantIn, ChromaticAdaptationMatrixIn, BlackPointOut, WhitePointOut, IlluminantOut, ChromaticAdaptationMatrixOut, m, of); *fn1 = XYZ2XYZ; } else { // XYZ Relative to XYZ relative, no op required *fn1 = NULL; if (DoBlackPointCompensation) { *fn1 = XYZ2XYZ; ComputeBlackPointCompensationFactors(BlackPointIn, WhitePointIn, IlluminantIn, BlackPointOut, WhitePointOut, IlluminantOut, m, of); } } break; // From relative XYZ to Relative Lab case LabRel: // First pass XYZ to absolute, then to relative and // finally to Lab. I use here D50 for output in order // to prepare the "to Lab" conversion. if (Absolute) { Rel2RelStepAbsCoefs(AdaptationState, BlackPointIn, WhitePointIn, IlluminantIn, ChromaticAdaptationMatrixIn, BlackPointOut, WhitePointOut, IlluminantOut, ChromaticAdaptationMatrixOut, m, of); *fn1 = XYZ2Lab; } else { // Just Convert to Lab MAT3identity(m); VEC3init(of, 0, 0, 0); *fn1 = XYZ2Lab; if (DoBlackPointCompensation) { ComputeBlackPointCompensationFactors(BlackPointIn, WhitePointIn, IlluminantIn, BlackPointOut, WhitePointOut, IlluminantOut, m, of); } } break; default: return FALSE; } return TRUE; }