1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics; 18 19 import java.util.Arrays; 20 21 /** 22 * 4x5 matrix for transforming the color and alpha components of a Bitmap. 23 * The matrix can be passed as single array, and is treated as follows: 24 * 25 * <pre> 26 * [ a, b, c, d, e, 27 * f, g, h, i, j, 28 * k, l, m, n, o, 29 * p, q, r, s, t ]</pre> 30 * 31 * <p> 32 * When applied to a color <code>[R, G, B, A]</code>, the resulting color 33 * is computed as: 34 * </p> 35 * 36 * <pre> 37 * R’ = a*R + b*G + c*B + d*A + e; 38 * G’ = f*R + g*G + h*B + i*A + j; 39 * B’ = k*R + l*G + m*B + n*A + o; 40 * A’ = p*R + q*G + r*B + s*A + t;</pre> 41 * 42 * <p> 43 * That resulting color <code>[R’, G’, B’, A’]</code> 44 * then has each channel clamped to the <code>0</code> to <code>255</code> 45 * range. 46 * </p> 47 * 48 * <p> 49 * The sample ColorMatrix below inverts incoming colors by scaling each 50 * channel by <code>-1</code>, and then shifting the result up by 51 * <code>255</code> to remain in the standard color space. 52 * </p> 53 * 54 * <pre> 55 * [ -1, 0, 0, 0, 255, 56 * 0, -1, 0, 0, 255, 57 * 0, 0, -1, 0, 255, 58 * 0, 0, 0, 1, 0 ]</pre> 59 */ 60 @SuppressWarnings({ "MismatchedReadAndWriteOfArray", "PointlessArithmeticExpression" }) 61 public class ColorMatrix { 62 private final float[] mArray = new float[20]; 63 64 /** 65 * Create a new colormatrix initialized to identity (as if reset() had 66 * been called). 67 */ ColorMatrix()68 public ColorMatrix() { 69 reset(); 70 } 71 72 /** 73 * Create a new colormatrix initialized with the specified array of values. 74 */ ColorMatrix(float[] src)75 public ColorMatrix(float[] src) { 76 System.arraycopy(src, 0, mArray, 0, 20); 77 } 78 79 /** 80 * Create a new colormatrix initialized with the specified colormatrix. 81 */ ColorMatrix(ColorMatrix src)82 public ColorMatrix(ColorMatrix src) { 83 System.arraycopy(src.mArray, 0, mArray, 0, 20); 84 } 85 86 /** 87 * Return the array of floats representing this colormatrix. 88 */ getArray()89 public final float[] getArray() { return mArray; } 90 91 /** 92 * Set this colormatrix to identity: 93 * <pre> 94 * [ 1 0 0 0 0 - red vector 95 * 0 1 0 0 0 - green vector 96 * 0 0 1 0 0 - blue vector 97 * 0 0 0 1 0 ] - alpha vector 98 * </pre> 99 */ reset()100 public void reset() { 101 final float[] a = mArray; 102 Arrays.fill(a, 0); 103 a[0] = a[6] = a[12] = a[18] = 1; 104 } 105 106 /** 107 * Assign the src colormatrix into this matrix, copying all of its values. 108 */ set(ColorMatrix src)109 public void set(ColorMatrix src) { 110 System.arraycopy(src.mArray, 0, mArray, 0, 20); 111 } 112 113 /** 114 * Assign the array of floats into this matrix, copying all of its values. 115 */ set(float[] src)116 public void set(float[] src) { 117 System.arraycopy(src, 0, mArray, 0, 20); 118 } 119 120 /** 121 * Set this colormatrix to scale by the specified values. 122 */ setScale(float rScale, float gScale, float bScale, float aScale)123 public void setScale(float rScale, float gScale, float bScale, 124 float aScale) { 125 final float[] a = mArray; 126 127 for (int i = 19; i > 0; --i) { 128 a[i] = 0; 129 } 130 a[0] = rScale; 131 a[6] = gScale; 132 a[12] = bScale; 133 a[18] = aScale; 134 } 135 136 /** 137 * Set the rotation on a color axis by the specified values. 138 * <p> 139 * <code>axis=0</code> correspond to a rotation around the RED color 140 * <code>axis=1</code> correspond to a rotation around the GREEN color 141 * <code>axis=2</code> correspond to a rotation around the BLUE color 142 * </p> 143 */ setRotate(int axis, float degrees)144 public void setRotate(int axis, float degrees) { 145 reset(); 146 double radians = degrees * Math.PI / 180d; 147 float cosine = (float) Math.cos(radians); 148 float sine = (float) Math.sin(radians); 149 switch (axis) { 150 // Rotation around the red color 151 case 0: 152 mArray[6] = mArray[12] = cosine; 153 mArray[7] = sine; 154 mArray[11] = -sine; 155 break; 156 // Rotation around the green color 157 case 1: 158 mArray[0] = mArray[12] = cosine; 159 mArray[2] = -sine; 160 mArray[10] = sine; 161 break; 162 // Rotation around the blue color 163 case 2: 164 mArray[0] = mArray[6] = cosine; 165 mArray[1] = sine; 166 mArray[5] = -sine; 167 break; 168 default: 169 throw new RuntimeException(); 170 } 171 } 172 173 /** 174 * Set this colormatrix to the concatenation of the two specified 175 * colormatrices, such that the resulting colormatrix has the same effect 176 * as applying matB and then applying matA. 177 * <p> 178 * It is legal for either matA or matB to be the same colormatrix as this. 179 * </p> 180 */ setConcat(ColorMatrix matA, ColorMatrix matB)181 public void setConcat(ColorMatrix matA, ColorMatrix matB) { 182 float[] tmp; 183 if (matA == this || matB == this) { 184 tmp = new float[20]; 185 } else { 186 tmp = mArray; 187 } 188 189 final float[] a = matA.mArray; 190 final float[] b = matB.mArray; 191 int index = 0; 192 for (int j = 0; j < 20; j += 5) { 193 for (int i = 0; i < 4; i++) { 194 tmp[index++] = a[j + 0] * b[i + 0] + a[j + 1] * b[i + 5] + 195 a[j + 2] * b[i + 10] + a[j + 3] * b[i + 15]; 196 } 197 tmp[index++] = a[j + 0] * b[4] + a[j + 1] * b[9] + 198 a[j + 2] * b[14] + a[j + 3] * b[19] + 199 a[j + 4]; 200 } 201 202 if (tmp != mArray) { 203 System.arraycopy(tmp, 0, mArray, 0, 20); 204 } 205 } 206 207 /** 208 * Concat this colormatrix with the specified prematrix. 209 * <p> 210 * This is logically the same as calling setConcat(this, prematrix); 211 * </p> 212 */ preConcat(ColorMatrix prematrix)213 public void preConcat(ColorMatrix prematrix) { 214 setConcat(this, prematrix); 215 } 216 217 /** 218 * Concat this colormatrix with the specified postmatrix. 219 * <p> 220 * This is logically the same as calling setConcat(postmatrix, this); 221 * </p> 222 */ postConcat(ColorMatrix postmatrix)223 public void postConcat(ColorMatrix postmatrix) { 224 setConcat(postmatrix, this); 225 } 226 227 /////////////////////////////////////////////////////////////////////////// 228 229 /** 230 * Set the matrix to affect the saturation of colors. 231 * 232 * @param sat A value of 0 maps the color to gray-scale. 1 is identity. 233 */ setSaturation(float sat)234 public void setSaturation(float sat) { 235 reset(); 236 float[] m = mArray; 237 238 final float invSat = 1 - sat; 239 final float R = 0.213f * invSat; 240 final float G = 0.715f * invSat; 241 final float B = 0.072f * invSat; 242 243 m[0] = R + sat; m[1] = G; m[2] = B; 244 m[5] = R; m[6] = G + sat; m[7] = B; 245 m[10] = R; m[11] = G; m[12] = B + sat; 246 } 247 248 /** 249 * Set the matrix to convert RGB to YUV 250 */ setRGB2YUV()251 public void setRGB2YUV() { 252 reset(); 253 float[] m = mArray; 254 // these coefficients match those in libjpeg 255 m[0] = 0.299f; m[1] = 0.587f; m[2] = 0.114f; 256 m[5] = -0.16874f; m[6] = -0.33126f; m[7] = 0.5f; 257 m[10] = 0.5f; m[11] = -0.41869f; m[12] = -0.08131f; 258 } 259 260 /** 261 * Set the matrix to convert from YUV to RGB 262 */ setYUV2RGB()263 public void setYUV2RGB() { 264 reset(); 265 float[] m = mArray; 266 // these coefficients match those in libjpeg 267 m[2] = 1.402f; 268 m[5] = 1; m[6] = -0.34414f; m[7] = -0.71414f; 269 m[10] = 1; m[11] = 1.772f; m[12] = 0; 270 } 271 272 @Override equals(Object obj)273 public boolean equals(Object obj) { 274 // if (obj == this) return true; -- NaN value would mean matrix != itself 275 if (!(obj instanceof ColorMatrix)) { 276 return false; 277 } 278 279 // we don't use Arrays.equals(), since that considers NaN == NaN 280 final float[] other = ((ColorMatrix) obj).mArray; 281 for (int i = 0; i < 20; i++) { 282 if (other[i] != mArray[i]) { 283 return false; 284 } 285 } 286 return true; 287 } 288 } 289