| 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); |