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