Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/lcms2/src/cmswtpnt.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 //--------------------------------------------------------------------------------- | |
| 2 // | |
| 3 // Little Color Management System | |
| 4 // Copyright (c) 1998-2023 Marti Maria Saguer | |
| 5 // | |
| 6 // Permission is hereby granted, free of charge, to any person obtaining | |
| 7 // a copy of this software and associated documentation files (the "Software"), | |
| 8 // to deal in the Software without restriction, including without limitation | |
| 9 // the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 // and/or sell copies of the Software, and to permit persons to whom the Software | |
| 11 // is furnished to do so, subject to the following conditions: | |
| 12 // | |
| 13 // The above copyright notice and this permission notice shall be included in | |
| 14 // all copies or substantial portions of the Software. | |
| 15 // | |
| 16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO | |
| 18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| 19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
| 20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
| 21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
| 22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 23 // | |
| 24 //--------------------------------------------------------------------------------- | |
| 25 // | |
| 26 | |
| 27 #include "lcms2_internal.h" | |
| 28 | |
| 29 | |
| 30 // D50 - Widely used | |
| 31 const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(cmsContext ContextID) | |
| 32 { | |
| 33 static cmsCIEXYZ D50XYZ = {cmsD50X, cmsD50Y, cmsD50Z}; | |
| 34 cmsUNUSED_PARAMETER(ContextID); | |
| 35 | |
| 36 return &D50XYZ; | |
| 37 } | |
| 38 | |
| 39 const cmsCIExyY* CMSEXPORT cmsD50_xyY(cmsContext ContextID) | |
| 40 { | |
| 41 static cmsCIExyY D50xyY; | |
| 42 | |
| 43 cmsXYZ2xyY(ContextID, &D50xyY, cmsD50_XYZ(ContextID)); | |
| 44 | |
| 45 return &D50xyY; | |
| 46 } | |
| 47 | |
| 48 // Obtains WhitePoint from Temperature | |
| 49 cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsContext ContextID, cmsCIExyY* WhitePoint, cmsFloat64Number TempK) | |
| 50 { | |
| 51 cmsFloat64Number x, y; | |
| 52 cmsFloat64Number T, T2, T3; | |
| 53 // cmsFloat64Number M1, M2; | |
| 54 cmsUNUSED_PARAMETER(ContextID); | |
| 55 | |
| 56 _cmsAssert(WhitePoint != NULL); | |
| 57 | |
| 58 T = TempK; | |
| 59 T2 = T*T; // Square | |
| 60 T3 = T2*T; // Cube | |
| 61 | |
| 62 // For correlated color temperature (T) between 4000K and 7000K: | |
| 63 | |
| 64 if (T >= 4000. && T <= 7000.) | |
| 65 { | |
| 66 x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; | |
| 67 } | |
| 68 else | |
| 69 // or for correlated color temperature (T) between 7000K and 25000K: | |
| 70 | |
| 71 if (T > 7000.0 && T <= 25000.0) | |
| 72 { | |
| 73 x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; | |
| 74 } | |
| 75 else { | |
| 76 cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp"); | |
| 77 return FALSE; | |
| 78 } | |
| 79 | |
| 80 // Obtain y(x) | |
| 81 y = -3.000*(x*x) + 2.870*x - 0.275; | |
| 82 | |
| 83 // wave factors (not used, but here for futures extensions) | |
| 84 | |
| 85 // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); | |
| 86 // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); | |
| 87 | |
| 88 WhitePoint -> x = x; | |
| 89 WhitePoint -> y = y; | |
| 90 WhitePoint -> Y = 1.0; | |
| 91 | |
| 92 return TRUE; | |
| 93 } | |
| 94 | |
| 95 | |
| 96 | |
| 97 typedef struct { | |
| 98 | |
| 99 cmsFloat64Number mirek; // temp (in microreciprocal kelvin) | |
| 100 cmsFloat64Number ut; // u coord of intersection w/ blackbody locus | |
| 101 cmsFloat64Number vt; // v coord of intersection w/ blackbody locus | |
| 102 cmsFloat64Number tt; // slope of ISOTEMPERATURE. line | |
| 103 | |
| 104 } ISOTEMPERATURE; | |
| 105 | |
| 106 static const ISOTEMPERATURE isotempdata[] = { | |
| 107 // {Mirek, Ut, Vt, Tt } | |
| 108 {0, 0.18006, 0.26352, -0.24341}, | |
| 109 {10, 0.18066, 0.26589, -0.25479}, | |
| 110 {20, 0.18133, 0.26846, -0.26876}, | |
| 111 {30, 0.18208, 0.27119, -0.28539}, | |
| 112 {40, 0.18293, 0.27407, -0.30470}, | |
| 113 {50, 0.18388, 0.27709, -0.32675}, | |
| 114 {60, 0.18494, 0.28021, -0.35156}, | |
| 115 {70, 0.18611, 0.28342, -0.37915}, | |
| 116 {80, 0.18740, 0.28668, -0.40955}, | |
| 117 {90, 0.18880, 0.28997, -0.44278}, | |
| 118 {100, 0.19032, 0.29326, -0.47888}, | |
| 119 {125, 0.19462, 0.30141, -0.58204}, | |
| 120 {150, 0.19962, 0.30921, -0.70471}, | |
| 121 {175, 0.20525, 0.31647, -0.84901}, | |
| 122 {200, 0.21142, 0.32312, -1.0182 }, | |
| 123 {225, 0.21807, 0.32909, -1.2168 }, | |
| 124 {250, 0.22511, 0.33439, -1.4512 }, | |
| 125 {275, 0.23247, 0.33904, -1.7298 }, | |
| 126 {300, 0.24010, 0.34308, -2.0637 }, | |
| 127 {325, 0.24702, 0.34655, -2.4681 }, | |
| 128 {350, 0.25591, 0.34951, -2.9641 }, | |
| 129 {375, 0.26400, 0.35200, -3.5814 }, | |
| 130 {400, 0.27218, 0.35407, -4.3633 }, | |
| 131 {425, 0.28039, 0.35577, -5.3762 }, | |
| 132 {450, 0.28863, 0.35714, -6.7262 }, | |
| 133 {475, 0.29685, 0.35823, -8.5955 }, | |
| 134 {500, 0.30505, 0.35907, -11.324 }, | |
| 135 {525, 0.31320, 0.35968, -15.628 }, | |
| 136 {550, 0.32129, 0.36011, -23.325 }, | |
| 137 {575, 0.32931, 0.36038, -40.770 }, | |
| 138 {600, 0.33724, 0.36051, -116.45 } | |
| 139 }; | |
| 140 | |
| 141 #define NISO sizeof(isotempdata)/sizeof(ISOTEMPERATURE) | |
| 142 | |
| 143 | |
| 144 // Robertson's method | |
| 145 cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsContext ContextID, cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint) | |
| 146 { | |
| 147 cmsUInt32Number j; | |
| 148 cmsFloat64Number us,vs; | |
| 149 cmsFloat64Number uj,vj,tj,di,dj,mi,mj; | |
| 150 cmsFloat64Number xs, ys; | |
| 151 cmsUNUSED_PARAMETER(ContextID); | |
| 152 | |
| 153 _cmsAssert(WhitePoint != NULL); | |
| 154 _cmsAssert(TempK != NULL); | |
| 155 | |
| 156 di = mi = 0; | |
| 157 xs = WhitePoint -> x; | |
| 158 ys = WhitePoint -> y; | |
| 159 | |
| 160 // convert (x,y) to CIE 1960 (u,WhitePoint) | |
| 161 | |
| 162 us = (2*xs) / (-xs + 6*ys + 1.5); | |
| 163 vs = (3*ys) / (-xs + 6*ys + 1.5); | |
| 164 | |
| 165 | |
| 166 for (j=0; j < NISO; j++) { | |
| 167 | |
| 168 uj = isotempdata[j].ut; | |
| 169 vj = isotempdata[j].vt; | |
| 170 tj = isotempdata[j].tt; | |
| 171 mj = isotempdata[j].mirek; | |
| 172 | |
| 173 dj = ((vs - vj) - tj * (us - uj)) / sqrt(1.0 + tj * tj); | |
| 174 | |
| 175 if ((j != 0) && (di/dj < 0.0)) { | |
| 176 | |
| 177 // Found a match | |
| 178 *TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi)); | |
| 179 return TRUE; | |
| 180 } | |
| 181 | |
| 182 di = dj; | |
| 183 mi = mj; | |
| 184 } | |
| 185 | |
| 186 // Not found | |
| 187 return FALSE; | |
| 188 } | |
| 189 | |
| 190 | |
| 191 // Compute chromatic adaptation matrix using Chad as cone matrix | |
| 192 | |
| 193 static | |
| 194 cmsBool ComputeChromaticAdaptation(cmsContext ContextID, cmsMAT3* Conversion, | |
| 195 const cmsCIEXYZ* SourceWhitePoint, | |
| 196 const cmsCIEXYZ* DestWhitePoint, | |
| 197 const cmsMAT3* Chad) | |
| 198 | |
| 199 { | |
| 200 | |
| 201 cmsMAT3 Chad_Inv; | |
| 202 cmsVEC3 ConeSourceXYZ, ConeSourceRGB; | |
| 203 cmsVEC3 ConeDestXYZ, ConeDestRGB; | |
| 204 cmsMAT3 Cone, Tmp; | |
| 205 | |
| 206 | |
| 207 Tmp = *Chad; | |
| 208 if (!_cmsMAT3inverse(ContextID, &Tmp, &Chad_Inv)) return FALSE; | |
| 209 | |
| 210 _cmsVEC3init(ContextID, &ConeSourceXYZ, SourceWhitePoint -> X, | |
| 211 SourceWhitePoint -> Y, | |
| 212 SourceWhitePoint -> Z); | |
| 213 | |
| 214 _cmsVEC3init(ContextID, &ConeDestXYZ, DestWhitePoint -> X, | |
| 215 DestWhitePoint -> Y, | |
| 216 DestWhitePoint -> Z); | |
| 217 | |
| 218 _cmsMAT3eval(ContextID, &ConeSourceRGB, Chad, &ConeSourceXYZ); | |
| 219 _cmsMAT3eval(ContextID, &ConeDestRGB, Chad, &ConeDestXYZ); | |
| 220 | |
| 221 if ((fabs(ConeSourceRGB.n[0]) < MATRIX_DET_TOLERANCE) || | |
| 222 (fabs(ConeSourceRGB.n[1]) < MATRIX_DET_TOLERANCE) || | |
| 223 (fabs(ConeSourceRGB.n[2]) < MATRIX_DET_TOLERANCE)) return FALSE; | |
| 224 | |
| 225 // Build matrix | |
| 226 _cmsVEC3init(ContextID, &Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0); | |
| 227 _cmsVEC3init(ContextID, &Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0); | |
| 228 _cmsVEC3init(ContextID, &Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]); | |
| 229 | |
| 230 // Normalize | |
| 231 _cmsMAT3per(ContextID, &Tmp, &Cone, Chad); | |
| 232 _cmsMAT3per(ContextID, Conversion, &Chad_Inv, &Tmp); | |
| 233 | |
| 234 return TRUE; | |
| 235 } | |
| 236 | |
| 237 // Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll | |
| 238 // The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed | |
| 239 cmsBool _cmsAdaptationMatrix(cmsContext ContextID, cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll) | |
| 240 { | |
| 241 cmsMAT3 LamRigg = {{ // Bradford matrix | |
| 242 {{ 0.8951, 0.2664, -0.1614 }}, | |
| 243 {{ -0.7502, 1.7135, 0.0367 }}, | |
| 244 {{ 0.0389, -0.0685, 1.0296 }} | |
| 245 }}; | |
| 246 | |
| 247 if (ConeMatrix == NULL) | |
| 248 ConeMatrix = &LamRigg; | |
| 249 | |
| 250 return ComputeChromaticAdaptation(ContextID, r, FromIll, ToIll, ConeMatrix); | |
| 251 } | |
| 252 | |
| 253 // Same as anterior, but assuming D50 destination. White point is given in xyY | |
| 254 static | |
| 255 cmsBool _cmsAdaptMatrixToD50(cmsContext ContextID, cmsMAT3* r, const cmsCIExyY* SourceWhitePt) | |
| 256 { | |
| 257 cmsCIEXYZ Dn; | |
| 258 cmsMAT3 Bradford; | |
| 259 cmsMAT3 Tmp; | |
| 260 | |
| 261 cmsxyY2XYZ(ContextID, &Dn, SourceWhitePt); | |
| 262 | |
| 263 if (!_cmsAdaptationMatrix(ContextID, &Bradford, NULL, &Dn, cmsD50_XYZ(ContextID))) return FALSE; | |
| 264 | |
| 265 Tmp = *r; | |
| 266 _cmsMAT3per(ContextID, r, &Bradford, &Tmp); | |
| 267 | |
| 268 return TRUE; | |
| 269 } | |
| 270 | |
| 271 // Build a White point, primary chromas transfer matrix from RGB to CIE XYZ | |
| 272 // This is just an approximation, I am not handling all the non-linear | |
| 273 // aspects of the RGB to XYZ process, and assuming that the gamma correction | |
| 274 // has transitive property in the transformation chain. | |
| 275 // | |
| 276 // the algorithm: | |
| 277 // | |
| 278 // - First I build the absolute conversion matrix using | |
| 279 // primaries in XYZ. This matrix is next inverted | |
| 280 // - Then I eval the source white point across this matrix | |
| 281 // obtaining the coefficients of the transformation | |
| 282 // - Then, I apply these coefficients to the original matrix | |
| 283 // | |
| 284 cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsContext ContextID, cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs) | |
| 285 { | |
| 286 cmsVEC3 WhitePoint, Coef; | |
| 287 cmsMAT3 Result, Primaries; | |
| 288 cmsFloat64Number xn, yn; | |
| 289 cmsFloat64Number xr, yr; | |
| 290 cmsFloat64Number xg, yg; | |
| 291 cmsFloat64Number xb, yb; | |
| 292 | |
| 293 xn = WhitePt -> x; | |
| 294 yn = WhitePt -> y; | |
| 295 xr = Primrs -> Red.x; | |
| 296 yr = Primrs -> Red.y; | |
| 297 xg = Primrs -> Green.x; | |
| 298 yg = Primrs -> Green.y; | |
| 299 xb = Primrs -> Blue.x; | |
| 300 yb = Primrs -> Blue.y; | |
| 301 | |
| 302 // Build Primaries matrix | |
| 303 _cmsVEC3init(ContextID, &Primaries.v[0], xr, xg, xb); | |
| 304 _cmsVEC3init(ContextID, &Primaries.v[1], yr, yg, yb); | |
| 305 _cmsVEC3init(ContextID, &Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb)); | |
| 306 | |
| 307 | |
| 308 // Result = Primaries ^ (-1) inverse matrix | |
| 309 if (!_cmsMAT3inverse(ContextID, &Primaries, &Result)) | |
| 310 return FALSE; | |
| 311 | |
| 312 | |
| 313 _cmsVEC3init(ContextID, &WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn); | |
| 314 | |
| 315 // Across inverse primaries ... | |
| 316 _cmsMAT3eval(ContextID, &Coef, &Result, &WhitePoint); | |
| 317 | |
| 318 // Give us the Coefs, then I build transformation matrix | |
| 319 _cmsVEC3init(ContextID, &r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb); | |
| 320 _cmsVEC3init(ContextID, &r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb); | |
| 321 _cmsVEC3init(ContextID, &r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb)); | |
| 322 | |
| 323 | |
| 324 return _cmsAdaptMatrixToD50(ContextID, r, WhitePt); | |
| 325 | |
| 326 } | |
| 327 | |
| 328 | |
| 329 // Adapts a color to a given illuminant. Original color is expected to have | |
| 330 // a SourceWhitePt white point. | |
| 331 cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsContext ContextID, cmsCIEXYZ* Result, | |
| 332 const cmsCIEXYZ* SourceWhitePt, | |
| 333 const cmsCIEXYZ* Illuminant, | |
| 334 const cmsCIEXYZ* Value) | |
| 335 { | |
| 336 cmsMAT3 Bradford; | |
| 337 cmsVEC3 In, Out; | |
| 338 | |
| 339 _cmsAssert(Result != NULL); | |
| 340 _cmsAssert(SourceWhitePt != NULL); | |
| 341 _cmsAssert(Illuminant != NULL); | |
| 342 _cmsAssert(Value != NULL); | |
| 343 | |
| 344 if (!_cmsAdaptationMatrix(ContextID, &Bradford, NULL, SourceWhitePt, Illuminant)) return FALSE; | |
| 345 | |
| 346 _cmsVEC3init(ContextID, &In, Value -> X, Value -> Y, Value -> Z); | |
| 347 _cmsMAT3eval(ContextID, &Out, &Bradford, &In); | |
| 348 | |
| 349 Result -> X = Out.n[0]; | |
| 350 Result -> Y = Out.n[1]; | |
| 351 Result -> Z = Out.n[2]; | |
| 352 | |
| 353 return TRUE; | |
| 354 } | |
| 355 | |
| 356 |
