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 < 0 or > > 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