1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 #include "SkColorMatrix.h" 8 9 // To detect if we need to apply clamping after applying a matrix, we check if 10 // any output component might go outside of [0, 255] for any combination of 11 // input components in [0..255]. 12 // Each output component is an affine transformation of the input component, so 13 // the minimum and maximum values are for any combination of minimum or maximum 14 // values of input components (i.e. 0 or 255). 15 // E.g. if R' = x*R + y*G + z*B + w*A + t 16 // Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the 17 // minimum value will be for R=0 if x>0 or R=255 if x<0. 18 // Same goes for all components. 19 static bool component_needs_clamping(const SkScalar row[5]) { 20 SkScalar maxValue = row[4] / 255; 21 SkScalar minValue = row[4] / 255; 22 for (int i = 0; i < 4; ++i) { 23 if (row[i] > 0) 24 maxValue += row[i]; 25 else 26 minValue += row[i]; 27 } 28 return (maxValue > 1) || (minValue < 0); 29 } 30 31 bool SkColorMatrix::NeedsClamping(const SkScalar matrix[20]) { 32 return component_needs_clamping(matrix) 33 || component_needs_clamping(matrix+5) 34 || component_needs_clamping(matrix+10) 35 || component_needs_clamping(matrix+15); 36 } 37 38 void SkColorMatrix::SetConcat(SkScalar result[20], 39 const SkScalar outer[20], const SkScalar inner[20]) { 40 SkScalar tmp[20]; 41 SkScalar* target; 42 43 if (outer == result || inner == result) { 44 target = tmp; // will memcpy answer when we're done into result 45 } else { 46 target = result; 47 } 48 49 int index = 0; 50 for (int j = 0; j < 20; j += 5) { 51 for (int i = 0; i < 4; i++) { 52 target[index++] = outer[j + 0] * inner[i + 0] + 53 outer[j + 1] * inner[i + 5] + 54 outer[j + 2] * inner[i + 10] + 55 outer[j + 3] * inner[i + 15]; 56 } 57 target[index++] = outer[j + 0] * inner[4] + 58 outer[j + 1] * inner[9] + 59 outer[j + 2] * inner[14] + 60 outer[j + 3] * inner[19] + 61 outer[j + 4]; 62 } 63 64 if (target != result) { 65 memcpy(result, target, 20 * sizeof(SkScalar)); 66 } 67 } 68 69 /////////////////////////////////////////////////////////////////////////////// 70 71 void SkColorMatrix::setIdentity() { 72 memset(fMat, 0, sizeof(fMat)); 73 fMat[kR_Scale] = fMat[kG_Scale] = fMat[kB_Scale] = fMat[kA_Scale] = 1; 74 } 75 76 void SkColorMatrix::setScale(SkScalar rScale, SkScalar gScale, SkScalar bScale, 77 SkScalar aScale) { 78 memset(fMat, 0, sizeof(fMat)); 79 fMat[kR_Scale] = rScale; 80 fMat[kG_Scale] = gScale; 81 fMat[kB_Scale] = bScale; 82 fMat[kA_Scale] = aScale; 83 } 84 85 void SkColorMatrix::postTranslate(SkScalar dr, SkScalar dg, SkScalar db, 86 SkScalar da) { 87 fMat[kR_Trans] += dr; 88 fMat[kG_Trans] += dg; 89 fMat[kB_Trans] += db; 90 fMat[kA_Trans] += da; 91 } 92 93 /////////////////////////////////////////////////////////////////////////////// 94 95 void SkColorMatrix::setRotate(Axis axis, SkScalar degrees) { 96 SkScalar S, C; 97 98 S = SkScalarSinCos(SkDegreesToRadians(degrees), &C); 99 100 this->setSinCos(axis, S, C); 101 } 102 103 void SkColorMatrix::setSinCos(Axis axis, SkScalar sine, SkScalar cosine) { 104 SkASSERT((unsigned)axis < 3); 105 106 static const uint8_t gRotateIndex[] = { 107 6, 7, 11, 12, 108 0, 10, 2, 12, 109 0, 1, 5, 6, 110 }; 111 const uint8_t* index = gRotateIndex + axis * 4; 112 113 this->setIdentity(); 114 fMat[index[0]] = cosine; 115 fMat[index[1]] = sine; 116 fMat[index[2]] = -sine; 117 fMat[index[3]] = cosine; 118 } 119 120 void SkColorMatrix::preRotate(Axis axis, SkScalar degrees) { 121 SkColorMatrix tmp; 122 tmp.setRotate(axis, degrees); 123 this->preConcat(tmp); 124 } 125 126 void SkColorMatrix::postRotate(Axis axis, SkScalar degrees) { 127 SkColorMatrix tmp; 128 tmp.setRotate(axis, degrees); 129 this->postConcat(tmp); 130 } 131 132 void SkColorMatrix::setConcat(const SkColorMatrix& matA, const SkColorMatrix& matB) { 133 SetConcat(fMat, matA.fMat, matB.fMat); 134 } 135 136 /////////////////////////////////////////////////////////////////////////////// 137 138 static void setrow(SkScalar row[], SkScalar r, SkScalar g, SkScalar b) { 139 row[0] = r; 140 row[1] = g; 141 row[2] = b; 142 } 143 144 static const SkScalar kHueR = 0.213f; 145 static const SkScalar kHueG = 0.715f; 146 static const SkScalar kHueB = 0.072f; 147 148 void SkColorMatrix::setSaturation(SkScalar sat) { 149 memset(fMat, 0, sizeof(fMat)); 150 151 const SkScalar R = kHueR * (1 - sat); 152 const SkScalar G = kHueG * (1 - sat); 153 const SkScalar B = kHueB * (1 - sat); 154 155 setrow(fMat + 0, R + sat, G, B); 156 setrow(fMat + 5, R, G + sat, B); 157 setrow(fMat + 10, R, G, B + sat); 158 fMat[kA_Scale] = 1; 159 } 160 161 static const SkScalar kR2Y = 0.299f; 162 static const SkScalar kG2Y = 0.587f; 163 static const SkScalar kB2Y = 0.114f; 164 165 static const SkScalar kR2U = -0.16874f; 166 static const SkScalar kG2U = -0.33126f; 167 static const SkScalar kB2U = 0.5f; 168 169 static const SkScalar kR2V = 0.5f; 170 static const SkScalar kG2V = -0.41869f; 171 static const SkScalar kB2V = -0.08131f; 172 173 void SkColorMatrix::setRGB2YUV() { 174 memset(fMat, 0, sizeof(fMat)); 175 176 setrow(fMat + 0, kR2Y, kG2Y, kB2Y); 177 setrow(fMat + 5, kR2U, kG2U, kB2U); 178 setrow(fMat + 10, kR2V, kG2V, kB2V); 179 fMat[kA_Scale] = 1; 180 } 181 182 static const SkScalar kV2R = 1.402f; 183 static const SkScalar kU2G = -0.34414f; 184 static const SkScalar kV2G = -0.71414f; 185 static const SkScalar kU2B = 1.772f; 186 187 void SkColorMatrix::setYUV2RGB() { 188 memset(fMat, 0, sizeof(fMat)); 189 190 setrow(fMat + 0, 1, 0, kV2R); 191 setrow(fMat + 5, 1, kU2G, kV2G); 192 setrow(fMat + 10, 1, kU2B, 0); 193 fMat[kA_Scale] = 1; 194 } 195 196