Home | History | Annotate | Download | only in graphics
      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&rsquo; = a*R + b*G + c*B + d*A + e;
     38  *   G&rsquo; = f*R + g*G + h*B + i*A + j;
     39  *   B&rsquo; = k*R + l*G + m*B + n*A + o;
     40  *   A&rsquo; = p*R + q*G + r*B + s*A + t;</pre>
     41  *
     42  * <p>
     43  * That resulting color <code>[R&rsquo;, G&rsquo;, B&rsquo;, A&rsquo;]</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      */
     68     public ColorMatrix() {
     69         reset();
     70     }
     71 
     72     /**
     73      * Create a new colormatrix initialized with the specified array of values.
     74      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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