| Index: src/effects/SkColorMatrix.cpp | 
| diff --git a/src/effects/SkColorMatrix.cpp b/src/effects/SkColorMatrix.cpp | 
| index 3842285bf404c2fdb9b3ab2e69790681552439d7..d99d51e1de529ce166e824fc991a365898b5f8f8 100644 | 
| --- a/src/effects/SkColorMatrix.cpp | 
| +++ b/src/effects/SkColorMatrix.cpp | 
| @@ -6,6 +6,68 @@ | 
| */ | 
| #include "SkColorMatrix.h" | 
|  | 
| +// To detect if we need to apply clamping after applying a matrix, we check if | 
| +// any output component might go outside of [0, 255] for any combination of | 
| +// input components in [0..255]. | 
| +// Each output component is an affine transformation of the input component, so | 
| +// the minimum and maximum values are for any combination of minimum or maximum | 
| +// values of input components (i.e. 0 or 255). | 
| +// E.g. if R' = x*R + y*G + z*B + w*A + t | 
| +// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the | 
| +// minimum value will be for R=0 if x>0 or R=255 if x<0. | 
| +// Same goes for all components. | 
| +static bool component_needs_clamping(const SkScalar row[5]) { | 
| +    SkScalar maxValue = row[4] / 255; | 
| +    SkScalar minValue = row[4] / 255; | 
| +    for (int i = 0; i < 4; ++i) { | 
| +        if (row[i] > 0) | 
| +            maxValue += row[i]; | 
| +        else | 
| +            minValue += row[i]; | 
| +    } | 
| +    return (maxValue > 1) || (minValue < 0); | 
| +} | 
| + | 
| +bool SkColorMatrix::NeedsClamping(const SkScalar matrix[20]) { | 
| +    return component_needs_clamping(matrix) | 
| +        || component_needs_clamping(matrix+5) | 
| +        || component_needs_clamping(matrix+10) | 
| +        || component_needs_clamping(matrix+15); | 
| +} | 
| + | 
| +void SkColorMatrix::SetConcat(SkScalar result[20], | 
| +                              const SkScalar outer[20], const SkScalar inner[20]) { | 
| +    SkScalar    tmp[20]; | 
| +    SkScalar*   target; | 
| + | 
| +    if (outer == result || inner == result) { | 
| +        target = tmp;   // will memcpy answer when we're done into result | 
| +    } else { | 
| +        target = result; | 
| +    } | 
| + | 
| +    int index = 0; | 
| +    for (int j = 0; j < 20; j += 5) { | 
| +        for (int i = 0; i < 4; i++) { | 
| +            target[index++] =   outer[j + 0] * inner[i + 0] + | 
| +                                outer[j + 1] * inner[i + 5] + | 
| +                                outer[j + 2] * inner[i + 10] + | 
| +                                outer[j + 3] * inner[i + 15]; | 
| +        } | 
| +        target[index++] =   outer[j + 0] * inner[4] + | 
| +                            outer[j + 1] * inner[9] + | 
| +                            outer[j + 2] * inner[14] + | 
| +                            outer[j + 3] * inner[19] + | 
| +                            outer[j + 4]; | 
| +    } | 
| + | 
| +    if (target != result) { | 
| +        memcpy(result, target, 20 * sizeof(SkScalar)); | 
| +    } | 
| +} | 
| + | 
| +/////////////////////////////////////////////////////////////////////////////// | 
| + | 
| void SkColorMatrix::setIdentity() { | 
| memset(fMat, 0, sizeof(fMat)); | 
| fMat[kR_Scale] = fMat[kG_Scale] = fMat[kB_Scale] = fMat[kA_Scale] = 1; | 
| @@ -67,38 +129,8 @@ void SkColorMatrix::postRotate(Axis axis, SkScalar degrees) { | 
| this->postConcat(tmp); | 
| } | 
|  | 
| -/////////////////////////////////////////////////////////////////////////////// | 
| - | 
| -void SkColorMatrix::setConcat(const SkColorMatrix& matA, | 
| -                              const SkColorMatrix& matB) { | 
| -    SkScalar    tmp[20]; | 
| -    SkScalar*   result = fMat; | 
| - | 
| -    if (&matA == this || &matB == this) { | 
| -        result = tmp; | 
| -    } | 
| - | 
| -    const SkScalar* a = matA.fMat; | 
| -    const SkScalar* b = matB.fMat; | 
| - | 
| -    int index = 0; | 
| -    for (int j = 0; j < 20; j += 5) { | 
| -        for (int i = 0; i < 4; i++) { | 
| -            result[index++] =   SkScalarMul(a[j + 0], b[i + 0]) + | 
| -                                SkScalarMul(a[j + 1], b[i + 5]) + | 
| -                                SkScalarMul(a[j + 2], b[i + 10]) + | 
| -                                SkScalarMul(a[j + 3], b[i + 15]); | 
| -        } | 
| -        result[index++] =   SkScalarMul(a[j + 0], b[4]) + | 
| -                            SkScalarMul(a[j + 1], b[9]) + | 
| -                            SkScalarMul(a[j + 2], b[14]) + | 
| -                            SkScalarMul(a[j + 3], b[19]) + | 
| -                            a[j + 4]; | 
| -    } | 
| - | 
| -    if (fMat != result) { | 
| -        memcpy(fMat, result, sizeof(fMat)); | 
| -    } | 
| +void SkColorMatrix::setConcat(const SkColorMatrix& matA, const SkColorMatrix& matB) { | 
| +    SetConcat(fMat, matA.fMat, matB.fMat); | 
| } | 
|  | 
| /////////////////////////////////////////////////////////////////////////////// | 
| @@ -160,3 +192,4 @@ void SkColorMatrix::setYUV2RGB() { | 
| setrow(fMat + 10, 1, kU2B, 0); | 
| fMat[kA_Scale] = 1; | 
| } | 
| + | 
|  |