Gradient_Delegate.java revision 251d2e99245095369b52d891a660b2ed270f02e0
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 /** 30 * Creates the base shader and do some basic test on the parameters. 31 * 32 * @param colors The colors to be distributed along the gradient line 33 * @param positions May be null. The relative positions [0..1] of each 34 * corresponding color in the colors array. If this is null, the 35 * the colors are distributed evenly along the gradient line. 36 */ 37 protected Gradient_Delegate(int colors[], float positions[]) { 38 if (colors.length < 2) { 39 throw new IllegalArgumentException("needs >= 2 number of colors"); 40 } 41 if (positions != null && colors.length != positions.length) { 42 throw new IllegalArgumentException("color and position arrays must be of equal length"); 43 } 44 45 if (positions == null) { 46 float spacing = 1.f / (colors.length - 1); 47 positions = new float[colors.length]; 48 positions[0] = 0.f; 49 positions[colors.length-1] = 1.f; 50 for (int i = 1; i < colors.length - 1 ; i++) { 51 positions[i] = spacing * i; 52 } 53 } 54 55 mColors = colors; 56 mPositions = positions; 57 } 58 59 /** 60 * Base class for (Java) Gradient Paints. This handles computing the gradient colors based 61 * on the color and position lists, as well as the {@link TileMode} 62 * 63 */ 64 protected abstract static class GradientPaint implements java.awt.Paint { 65 private final static int GRADIENT_SIZE = 100; 66 67 private final int[] mColors; 68 private final float[] mPositions; 69 private final TileMode mTileMode; 70 private int[] mGradient; 71 72 protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) { 73 mColors = colors; 74 mPositions = positions; 75 mTileMode = tileMode; 76 } 77 78 public int getTransparency() { 79 return java.awt.Paint.TRANSLUCENT; 80 } 81 82 /** 83 * Pre-computes the colors for the gradient. This must be called once before any call 84 * to {@link #getGradientColor(float)} 85 */ 86 protected synchronized void precomputeGradientColors() { 87 if (mGradient == null) { 88 // actually create an array with an extra size, so that we can really go 89 // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0 90 mGradient = new int[GRADIENT_SIZE+1]; 91 92 int prevPos = 0; 93 int nextPos = 1; 94 for (int i = 0 ; i <= GRADIENT_SIZE ; i++) { 95 // compute current position 96 float currentPos = (float)i/GRADIENT_SIZE; 97 while (currentPos > mPositions[nextPos]) { 98 prevPos = nextPos++; 99 } 100 101 float percent = (currentPos - mPositions[prevPos]) / 102 (mPositions[nextPos] - mPositions[prevPos]); 103 104 mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent); 105 } 106 } 107 } 108 109 /** 110 * Returns the color based on the position in the gradient. 111 * <var>pos</var> can be anything, even < 0 or > > 1, as the gradient 112 * will use {@link TileMode} value to convert it into a [0,1] value. 113 */ 114 protected int getGradientColor(float pos) { 115 if (pos < 0.f) { 116 if (mTileMode != null) { 117 switch (mTileMode) { 118 case CLAMP: 119 pos = 0.f; 120 break; 121 case REPEAT: 122 // remove the integer part to stay in the [0,1] range 123 // careful: this is a negative value, so use ceil instead of floor 124 pos = pos - (float)Math.ceil(pos); 125 break; 126 case MIRROR: 127 // get the integer and the decimal part 128 // careful: this is a negative value, so use ceil instead of floor 129 int intPart = (int)Math.ceil(pos); 130 pos = pos - intPart; 131 // 0 -> -1 : mirrored order 132 // -1 -> -2: normal order 133 // etc.. 134 // this means if the intpart is even we invert 135 if ((intPart % 2) == 0) { 136 pos = 1.f - pos; 137 } 138 break; 139 } 140 } else { 141 pos = 0.0f; 142 } 143 } else if (pos > 1f) { 144 if (mTileMode != null) { 145 switch (mTileMode) { 146 case CLAMP: 147 pos = 1.f; 148 break; 149 case REPEAT: 150 // remove the integer part to stay in the [0,1] range 151 pos = pos - (float)Math.floor(pos); 152 break; 153 case MIRROR: 154 // get the integer and the decimal part 155 int intPart = (int)Math.floor(pos); 156 pos = pos - intPart; 157 // 0 -> 1 : normal order 158 // 1 -> 2: mirrored 159 // etc.. 160 // this means if the intpart is odd we invert 161 if ((intPart % 2) == 1) { 162 pos = 1.f - pos; 163 } 164 break; 165 } 166 } else { 167 pos = 1.0f; 168 } 169 } 170 171 int index = (int)((pos * GRADIENT_SIZE) + .5); 172 173 return mGradient[index]; 174 } 175 176 /** 177 * Returns the color between c1, and c2, based on the percent of the distance 178 * between c1 and c2. 179 */ 180 private int computeColor(int c1, int c2, float percent) { 181 int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent); 182 int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent); 183 int g = computeChannel((c1 >> 8) & 0xFF, (c2 >> 8) & 0xFF, percent); 184 int b = computeChannel((c1 ) & 0xFF, (c2 ) & 0xFF, percent); 185 return a << 24 | r << 16 | g << 8 | b; 186 } 187 188 /** 189 * Returns the channel value between 2 values based on the percent of the distance between 190 * the 2 values.. 191 */ 192 private int computeChannel(int c1, int c2, float percent) { 193 return c1 + (int)((percent * (c2-c1)) + .5); 194 } 195 } 196} 197