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