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         @Override
     91         public int getTransparency() {
     92             return java.awt.Paint.TRANSLUCENT;
     93         }
     94 
     95         /**
     96          * Pre-computes the colors for the gradient. This must be called once before any call
     97          * to {@link #getGradientColor(float)}
     98          */
     99         protected void precomputeGradientColors() {
    100             if (mGradient == null) {
    101                 // actually create an array with an extra size, so that we can really go
    102                 // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
    103                 mGradient = new int[GRADIENT_SIZE+1];
    104 
    105                 int prevPos = 0;
    106                 int nextPos = 1;
    107                 for (int i  = 0 ; i <= GRADIENT_SIZE ; i++) {
    108                     // compute current position
    109                     float currentPos = (float)i/GRADIENT_SIZE;
    110                     while (currentPos > mPositions[nextPos]) {
    111                         prevPos = nextPos++;
    112                     }
    113 
    114                     float percent = (currentPos - mPositions[prevPos]) /
    115                             (mPositions[nextPos] - mPositions[prevPos]);
    116 
    117                     mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
    118                 }
    119             }
    120         }
    121 
    122         /**
    123          * Returns the color based on the position in the gradient.
    124          * <var>pos</var> can be anything, even &lt; 0 or &gt; > 1, as the gradient
    125          * will use {@link TileMode} value to convert it into a [0,1] value.
    126          */
    127         protected int getGradientColor(float pos) {
    128             if (pos < 0.f) {
    129                 if (mTileMode != null) {
    130                     switch (mTileMode) {
    131                         case CLAMP:
    132                             pos = 0.f;
    133                             break;
    134                         case REPEAT:
    135                             // remove the integer part to stay in the [0,1] range.
    136                             // we also need to invert the value from [-1,0] to [0, 1]
    137                             pos = pos - (float)Math.floor(pos);
    138                             break;
    139                         case MIRROR:
    140                             // this is the same as the positive side, just make the value positive
    141                             // first.
    142                             pos = Math.abs(pos);
    143 
    144                             // get the integer and the decimal part
    145                             int intPart = (int)Math.floor(pos);
    146                             pos = pos - intPart;
    147                             // 0 -> 1 : normal order
    148                             // 1 -> 2: mirrored
    149                             // etc..
    150                             // this means if the intpart is odd we invert
    151                             if ((intPart % 2) == 1) {
    152                                 pos = 1.f - pos;
    153                             }
    154                             break;
    155                     }
    156                 } else {
    157                     pos = 0.0f;
    158                 }
    159             } else if (pos > 1f) {
    160                 if (mTileMode != null) {
    161                     switch (mTileMode) {
    162                         case CLAMP:
    163                             pos = 1.f;
    164                             break;
    165                         case REPEAT:
    166                             // remove the integer part to stay in the [0,1] range
    167                             pos = pos - (float)Math.floor(pos);
    168                             break;
    169                         case MIRROR:
    170                             // get the integer and the decimal part
    171                             int intPart = (int)Math.floor(pos);
    172                             pos = pos - intPart;
    173                             // 0 -> 1 : normal order
    174                             // 1 -> 2: mirrored
    175                             // etc..
    176                             // this means if the intpart is odd we invert
    177                             if ((intPart % 2) == 1) {
    178                                 pos = 1.f - pos;
    179                             }
    180                             break;
    181                     }
    182                 } else {
    183                     pos = 1.0f;
    184                 }
    185             }
    186 
    187             int index = (int)((pos * GRADIENT_SIZE) + .5);
    188 
    189             return mGradient[index];
    190         }
    191 
    192         /**
    193          * Returns the color between c1, and c2, based on the percent of the distance
    194          * between c1 and c2.
    195          */
    196         private int computeColor(int c1, int c2, float percent) {
    197             int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
    198             int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
    199             int g = computeChannel((c1 >>  8) & 0xFF, (c2 >>  8) & 0xFF, percent);
    200             int b = computeChannel((c1      ) & 0xFF, (c2      ) & 0xFF, percent);
    201             return a << 24 | r << 16 | g << 8 | b;
    202         }
    203 
    204         /**
    205          * Returns the channel value between 2 values based on the percent of the distance between
    206          * the 2 values..
    207          */
    208         private int computeChannel(int c1, int c2, float percent) {
    209             return c1 + (int)((percent * (c2-c1)) + .5);
    210         }
    211     }
    212 }
    213