Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2010 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 
     20 /**
     21  * Base class for Gradient shader. This is not a standard android class and is just used
     22  * as a base class for the re-implemented gradient classes.
     23  *
     24  * It also provides a base class to handle common code between the different shaders'
     25  * implementations of {@link java.awt.Paint}.
     26  *
     27  * @see LinearGradient
     28  * @see RadialGradient
     29  * @see SweepGradient
     30  */
     31 public abstract class GradientShader extends Shader {
     32 
     33     protected final int[] mColors;
     34     protected final float[] mPositions;
     35 
     36     /**
     37      * Creates the base shader and do some basic test on the parameters.
     38      *
     39      * @param colors The colors to be distributed along the gradient line
     40      * @param positions May be null. The relative positions [0..1] of each
     41      *            corresponding color in the colors array. If this is null, the
     42      *            the colors are distributed evenly along the gradient line.
     43      */
     44     protected GradientShader(int colors[], float positions[]) {
     45         if (colors.length < 2) {
     46             throw new IllegalArgumentException("needs >= 2 number of colors");
     47         }
     48         if (positions != null && colors.length != positions.length) {
     49             throw new IllegalArgumentException("color and position arrays must be of equal length");
     50         }
     51 
     52         if (positions == null) {
     53             float spacing = 1.f / (colors.length - 1);
     54             positions = new float[colors.length];
     55             positions[0] = 0.f;
     56             positions[colors.length-1] = 1.f;
     57             for (int i = 1; i < colors.length - 1 ; i++) {
     58                 positions[i] = spacing * i;
     59             }
     60         }
     61 
     62         mColors = colors;
     63         mPositions = positions;
     64     }
     65 
     66     /**
     67      * Base class for (Java) Gradient Paints. This handles computing the gradient colors based
     68      * on the color and position lists, as well as the {@link TileMode}
     69      *
     70      */
     71     protected abstract static class GradientPaint implements java.awt.Paint {
     72         private final static int GRADIENT_SIZE = 100;
     73 
     74         private final int[] mColors;
     75         private final float[] mPositions;
     76         private final TileMode mTileMode;
     77         private int[] mGradient;
     78 
     79         protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) {
     80             mColors = colors;
     81             mPositions = positions;
     82             mTileMode = tileMode;
     83         }
     84 
     85         public int getTransparency() {
     86             return java.awt.Paint.TRANSLUCENT;
     87         }
     88 
     89         /**
     90          * Pre-computes the colors for the gradient. This must be called once before any call
     91          * to {@link #getGradientColor(float)}
     92          */
     93         protected synchronized void precomputeGradientColors() {
     94             if (mGradient == null) {
     95                 // actually create an array with an extra size, so that we can really go
     96                 // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
     97                 mGradient = new int[GRADIENT_SIZE+1];
     98 
     99                 int prevPos = 0;
    100                 int nextPos = 1;
    101                 for (int i  = 0 ; i <= GRADIENT_SIZE ; i++) {
    102                     // compute current position
    103                     float currentPos = (float)i/GRADIENT_SIZE;
    104                     while (currentPos > mPositions[nextPos]) {
    105                         prevPos = nextPos++;
    106                     }
    107 
    108                     float percent = (currentPos - mPositions[prevPos]) /
    109                             (mPositions[nextPos] - mPositions[prevPos]);
    110 
    111                     mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
    112                 }
    113             }
    114         }
    115 
    116         /**
    117          * Returns the color based on the position in the gradient.
    118          * <var>pos</var> can be anything, even &lt; 0 or &gt; > 1, as the gradient
    119          * will use {@link TileMode} value to convert it into a [0,1] value.
    120          */
    121         protected int getGradientColor(float pos) {
    122             if (pos < 0.f) {
    123                 if (mTileMode != null) {
    124                     switch (mTileMode) {
    125                         case CLAMP:
    126                             pos = 0.f;
    127                             break;
    128                         case REPEAT:
    129                             // remove the integer part to stay in the [0,1] range
    130                             // careful: this is a negative value, so use ceil instead of floor
    131                             pos = pos - (float)Math.ceil(pos);
    132                             break;
    133                         case MIRROR:
    134                             // get the integer and the decimal part
    135                             // careful: this is a negative value, so use ceil instead of floor
    136                             int intPart = (int)Math.ceil(pos);
    137                             pos = pos - intPart;
    138                             // 0  -> -1 : mirrored order
    139                             // -1 -> -2: normal order
    140                             // etc..
    141                             // this means if the intpart is even we invert
    142                             if ((intPart % 2) == 0) {
    143                                 pos = 1.f - pos;
    144                             }
    145                             break;
    146                     }
    147                 } else {
    148                     pos = 0.0f;
    149                 }
    150             } else if (pos > 1f) {
    151                 if (mTileMode != null) {
    152                     switch (mTileMode) {
    153                         case CLAMP:
    154                             pos = 1.f;
    155                             break;
    156                         case REPEAT:
    157                             // remove the integer part to stay in the [0,1] range
    158                             pos = pos - (float)Math.floor(pos);
    159                             break;
    160                         case MIRROR:
    161                             // get the integer and the decimal part
    162                             int intPart = (int)Math.floor(pos);
    163                             pos = pos - intPart;
    164                             // 0 -> 1 : normal order
    165                             // 1 -> 2: mirrored
    166                             // etc..
    167                             // this means if the intpart is odd we invert
    168                             if ((intPart % 2) == 1) {
    169                                 pos = 1.f - pos;
    170                             }
    171                             break;
    172                     }
    173                 } else {
    174                     pos = 1.0f;
    175                 }
    176             }
    177 
    178             int index = (int)((pos * GRADIENT_SIZE) + .5);
    179 
    180             return mGradient[index];
    181         }
    182 
    183         /**
    184          * Returns the color between c1, and c2, based on the percent of the distance
    185          * between c1 and c2.
    186          */
    187         private int computeColor(int c1, int c2, float percent) {
    188             int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
    189             int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
    190             int g = computeChannel((c1 >>  8) & 0xFF, (c2 >>  8) & 0xFF, percent);
    191             int b = computeChannel((c1      ) & 0xFF, (c2      ) & 0xFF, percent);
    192             return a << 24 | r << 16 | g << 8 | b;
    193         }
    194 
    195         /**
    196          * Returns the channel value between 2 values based on the percent of the distance between
    197          * the 2 values..
    198          */
    199         private int computeChannel(int c1, int c2, float percent) {
    200             return c1 + (int)((percent * (c2-c1)) + .5);
    201         }
    202 
    203 
    204     }
    205 }
    206