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 17package android.graphics; 18 19import android.graphics.Shader.TileMode; 20 21/** 22 * Base class for true Gradient shader delegate. 23 */ 24public 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 < 0 or > > 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