| diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c | |
| index 5ea1b4c85..8a1171820 100644 | |
| --- a/third_party/lcms/src/cmsopt.c | |
| +++ b/third_party/lcms/src/cmsopt.c | |
| @@ -104,6 +104,39 @@ typedef struct { | |
| // Simple optimizations ---------------------------------------------------------------------------------------------------------- | |
| +// Clamp a fixed point integer to signed 28 bits to avoid overflow in | |
| +// calculations. Clamp is intended for use with colorants, requiring one bit | |
| +// for a colorant and another two bits to avoid overflow when combining the | |
| +// colors. | |
| +cmsINLINE cmsS1Fixed14Number _FixedClamp(cmsS1Fixed14Number n) { | |
| + const cmsS1Fixed14Number max_positive = 268435455; // 0x0FFFFFFF; | |
| + const cmsS1Fixed14Number max_negative = -268435456; // 0xF0000000; | |
| + // Normally expect the provided number to be in the range [0..1] (but in | |
| + // fixed 1.14 format), so can perform a quick check for this typical case | |
| + // to reduce number of compares. | |
| + const cmsS1Fixed14Number typical_range_mask = 0xFFFF8000; | |
| + | |
| + if (!(n & typical_range_mask)) | |
| + return n; | |
| + if (n < max_negative) | |
| + return max_negative; | |
| + if (n > max_positive) | |
| + return max_positive; | |
| + return n; | |
| +} | |
| + | |
| +// Perform one row of matrix multiply with translation for MatShaperEval16(). | |
| +cmsINLINE cmsInt64Number _MatShaperEvaluateRow(cmsS1Fixed14Number* mat, | |
| + cmsS1Fixed14Number off, | |
| + cmsS1Fixed14Number r, | |
| + cmsS1Fixed14Number g, | |
| + cmsS1Fixed14Number b) { | |
| + return ((cmsInt64Number)mat[0] * r + | |
| + (cmsInt64Number)mat[1] * g + | |
| + (cmsInt64Number)mat[2] * b + | |
| + off + 0x2000) >> 14; | |
| +} | |
| + | |
| // Remove an element in linked chain | |
| static | |
| void _RemoveElement(cmsStage** head) | |
| @@ -1527,7 +1560,8 @@ void MatShaperEval16(register const cmsUInt16Number In[], | |
| register const void* D) | |
| { | |
| MatShaper8Data* p = (MatShaper8Data*) D; | |
| - cmsS1Fixed14Number l1, l2, l3, r, g, b; | |
| + cmsS1Fixed14Number r, g, b; | |
| + cmsInt64Number l1, l2, l3; | |
| cmsUInt32Number ri, gi, bi; | |
| // In this case (and only in this case!) we can use this simplification since | |
| @@ -1537,14 +1571,14 @@ void MatShaperEval16(register const cmsUInt16Number In[], | |
| bi = In[2] & 0xFFU; | |
| // Across first shaper, which also converts to 1.14 fixed point | |
| - r = p->Shaper1R[ri]; | |
| - g = p->Shaper1G[gi]; | |
| - b = p->Shaper1B[bi]; | |
| + r = _FixedClamp(p->Shaper1R[ri]); | |
| + g = _FixedClamp(p->Shaper1G[gi]); | |
| + b = _FixedClamp(p->Shaper1B[bi]); | |
| // Evaluate the matrix in 1.14 fixed point | |
| - l1 = (p->Mat[0][0] * r + p->Mat[0][1] * g + p->Mat[0][2] * b + p->Off[0] + 0x2000) >> 14; | |
| - l2 = (p->Mat[1][0] * r + p->Mat[1][1] * g + p->Mat[1][2] * b + p->Off[1] + 0x2000) >> 14; | |
| - l3 = (p->Mat[2][0] * r + p->Mat[2][1] * g + p->Mat[2][2] * b + p->Off[2] + 0x2000) >> 14; | |
| + l1 = _MatShaperEvaluateRow(p->Mat[0], p->Off[0], r, g, b); | |
| + l2 = _MatShaperEvaluateRow(p->Mat[1], p->Off[1], r, g, b); | |
| + l3 = _MatShaperEvaluateRow(p->Mat[2], p->Off[2], r, g, b); | |
| // Now we have to clip to 0..1.0 range | |
| ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384U : (cmsUInt32Number) l1); |