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