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 isSupported()32 public boolean isSupported() { 33 // all gradient shaders are supported. 34 return true; 35 } 36 37 @Override getSupportMessage()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 */ Gradient_Delegate(long nativeMatrix, long[] colors, float[] positions)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 GradientPaint(int[] colors, float[] positions, TileMode tileMode)90 protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) { 91 mColors = colors; 92 mPositions = positions; 93 mTileMode = tileMode; 94 } 95 96 @Override getTransparency()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 */ precomputeGradientColors()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 < 0 or > > 1, as the gradient 141 * will use {@link TileMode} value to convert it into a [0,1] value. 142 */ getGradientColor(float pos)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 */ computeColor(int c1, int c2, float percent)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 */ computeChannel(int c1, int c2, float percent)224 private int computeChannel(int c1, int c2, float percent) { 225 return c1 + (int)((percent * (c2-c1)) + .5); 226 } 227 } 228 } 229