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