/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics; import android.graphics.Shader.TileMode; /** * Base class for true Gradient shader delegate. */ public abstract class Gradient_Delegate extends Shader_Delegate { protected final int[] mColors; protected final float[] mPositions; @Override public boolean isSupported() { // all gradient shaders are supported. return true; } @Override public String getSupportMessage() { // all gradient shaders are supported, no need for a gradient support return null; } /** * Creates the base shader and do some basic test on the parameters. * * @param nativeMatrix reference to the shader's native transformation matrix * @param colors The colors to be distributed along the gradient line * @param positions May be null. The relative positions [0..1] of each * corresponding color in the colors array. If this is null, the * the colors are distributed evenly along the gradient line. */ protected Gradient_Delegate(long nativeMatrix, int colors[], float positions[]) { super(nativeMatrix); assert colors.length >= 2 : "needs >= 2 number of colors"; if (positions == null) { float spacing = 1.f / (colors.length - 1); positions = new float[colors.length]; positions[0] = 0.f; positions[colors.length - 1] = 1.f; for (int i = 1; i < colors.length - 1; i++) { positions[i] = spacing * i; } } else { assert colors.length == positions.length : "color and position " + "arrays must be of equal length"; } mColors = colors; mPositions = positions; } /** * Base class for (Java) Gradient Paints. This handles computing the gradient colors based * on the color and position lists, as well as the {@link TileMode} * */ protected abstract static class GradientPaint implements java.awt.Paint { private final static int GRADIENT_SIZE = 100; private final int[] mColors; private final float[] mPositions; private final TileMode mTileMode; private int[] mGradient; protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) { mColors = colors; mPositions = positions; mTileMode = tileMode; } @Override public int getTransparency() { return java.awt.Paint.TRANSLUCENT; } /** * Pre-computes the colors for the gradient. This must be called once before any call * to {@link #getGradientColor(float)} */ protected void precomputeGradientColors() { if (mGradient == null) { // actually create an array with an extra size, so that we can really go // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0 mGradient = new int[GRADIENT_SIZE+1]; int prevPos = 0; int nextPos = 1; for (int i = 0 ; i <= GRADIENT_SIZE ; i++) { // compute current position float currentPos = (float)i/GRADIENT_SIZE; while (currentPos > mPositions[nextPos]) { prevPos = nextPos++; } float percent = (currentPos - mPositions[prevPos]) / (mPositions[nextPos] - mPositions[prevPos]); mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent); } } } /** * Returns the color based on the position in the gradient. * pos can be anything, even < 0 or > > 1, as the gradient * will use {@link TileMode} value to convert it into a [0,1] value. */ protected int getGradientColor(float pos) { if (pos < 0.f) { if (mTileMode != null) { switch (mTileMode) { case CLAMP: pos = 0.f; break; case REPEAT: // remove the integer part to stay in the [0,1] range. // we also need to invert the value from [-1,0] to [0, 1] pos = pos - (float)Math.floor(pos); break; case MIRROR: // this is the same as the positive side, just make the value positive // first. pos = Math.abs(pos); // get the integer and the decimal part int intPart = (int)Math.floor(pos); pos = pos - intPart; // 0 -> 1 : normal order // 1 -> 2: mirrored // etc.. // this means if the intpart is odd we invert if ((intPart % 2) == 1) { pos = 1.f - pos; } break; } } else { pos = 0.0f; } } else if (pos > 1f) { if (mTileMode != null) { switch (mTileMode) { case CLAMP: pos = 1.f; break; case REPEAT: // remove the integer part to stay in the [0,1] range pos = pos - (float)Math.floor(pos); break; case MIRROR: // get the integer and the decimal part int intPart = (int)Math.floor(pos); pos = pos - intPart; // 0 -> 1 : normal order // 1 -> 2: mirrored // etc.. // this means if the intpart is odd we invert if ((intPart % 2) == 1) { pos = 1.f - pos; } break; } } else { pos = 1.0f; } } int index = (int)((pos * GRADIENT_SIZE) + .5); return mGradient[index]; } /** * Returns the color between c1, and c2, based on the percent of the distance * between c1 and c2. */ private int computeColor(int c1, int c2, float percent) { int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent); int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent); int g = computeChannel((c1 >> 8) & 0xFF, (c2 >> 8) & 0xFF, percent); int b = computeChannel((c1 ) & 0xFF, (c2 ) & 0xFF, percent); return a << 24 | r << 16 | g << 8 | b; } /** * Returns the channel value between 2 values based on the percent of the distance between * the 2 values.. */ private int computeChannel(int c1, int c2, float percent) { return c1 + (int)((percent * (c2-c1)) + .5); } } }