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; |
} |
+ |