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