am b847fbf2: Merge "ADT/Layout: support for 3+ color in linear gradients" into eclair
Merge commit 'b847fbf2098acc8c5854bbbfa1453431128c720e' into eclair-plus-aosp * commit 'b847fbf2098acc8c5854bbbfa1453431128c720e': ADT/Layout: support for 3+ color in linear gradients
This commit is contained in:
@@ -19,10 +19,20 @@ package android.graphics;
|
||||
import java.awt.GradientPaint;
|
||||
import java.awt.Color;
|
||||
import java.awt.Paint;
|
||||
import java.awt.PaintContext;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.SampleModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
public class LinearGradient extends Shader {
|
||||
|
||||
private GradientPaint mGradientPaint;
|
||||
private Paint mJavaPaint;
|
||||
|
||||
/**
|
||||
* Create a shader that draws a linear gradient along a line.
|
||||
@@ -46,12 +56,13 @@ public class LinearGradient extends Shader {
|
||||
throw new IllegalArgumentException("color and position arrays must be of equal length");
|
||||
}
|
||||
|
||||
// FIXME implement multi color linear gradient
|
||||
if (colors.length == 2) {
|
||||
if (colors.length == 2) { // for 2 colors: use the Java implementation
|
||||
// The hasAlpha flag in Color() is only used to enforce alpha to 0xFF if false.
|
||||
// If true the alpha is read from the int.
|
||||
mGradientPaint = new GradientPaint(x0, y0, new Color(colors[0], true /* hasalpha */),
|
||||
mJavaPaint = new GradientPaint(x0, y0, new Color(colors[0], true /* hasalpha */),
|
||||
x1, y1, new Color(colors[1], true /* hasalpha */), tile != TileMode.CLAMP);
|
||||
} else {
|
||||
mJavaPaint = new MultiPointLinearGradientPaint(x0, y0, x1, y1, colors, positions, tile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +81,7 @@ public class LinearGradient extends Shader {
|
||||
TileMode tile) {
|
||||
// The hasAlpha flag in Color() is only used to enforce alpha to 0xFF if false.
|
||||
// If true the alpha is read from the int.
|
||||
mGradientPaint = new GradientPaint(x0, y0, new Color(color0, true /* hasalpha */), x1, y1,
|
||||
mJavaPaint = new GradientPaint(x0, y0, new Color(color0, true /* hasalpha */), x1, y1,
|
||||
new Color(color1, true /* hasalpha */), tile != TileMode.CLAMP);
|
||||
}
|
||||
|
||||
@@ -78,6 +89,232 @@ public class LinearGradient extends Shader {
|
||||
|
||||
@Override
|
||||
public Paint getJavaPaint() {
|
||||
return mGradientPaint;
|
||||
return mJavaPaint;
|
||||
}
|
||||
|
||||
private static class MultiPointLinearGradientPaint implements Paint {
|
||||
private final static int GRADIENT_SIZE = 100;
|
||||
|
||||
private final float mX0;
|
||||
private final float mY0;
|
||||
private final float mDx;
|
||||
private final float mDy;
|
||||
private final float mDSize2;
|
||||
private final int[] mColors;
|
||||
private final float[] mPositions;
|
||||
private final TileMode mTile;
|
||||
private int[] mGradient;
|
||||
|
||||
public MultiPointLinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
|
||||
float positions[], TileMode tile) {
|
||||
mX0 = x0;
|
||||
mY0 = y0;
|
||||
mDx = x1 - x0;
|
||||
mDy = y1 - y0;
|
||||
mDSize2 = mDx * mDx + mDy * mDy;
|
||||
|
||||
mColors = colors;
|
||||
mPositions = positions;
|
||||
mTile = tile;
|
||||
}
|
||||
|
||||
public PaintContext createContext(ColorModel cm, Rectangle deviceBounds,
|
||||
Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
|
||||
prepareColors();
|
||||
return new MultiPointLinearGradientPaintContext(cm, deviceBounds,
|
||||
userBounds, xform, hints);
|
||||
}
|
||||
|
||||
public int getTransparency() {
|
||||
return TRANSLUCENT;
|
||||
}
|
||||
|
||||
private synchronized void prepareColors() {
|
||||
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] = getColor(mColors[prevPos], mColors[nextPos], percent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color between c1, and c2, based on the percent of the distance
|
||||
* between c1 and c2.
|
||||
*/
|
||||
private int getColor(int c1, int c2, float percent) {
|
||||
int a = getChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
|
||||
int r = getChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
|
||||
int g = getChannel((c1 >> 8) & 0xFF, (c2 >> 8) & 0xFF, percent);
|
||||
int b = getChannel((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 getChannel(int c1, int c2, float percent) {
|
||||
return c1 + (int)((percent * (c2-c1)) + .5);
|
||||
}
|
||||
|
||||
private class MultiPointLinearGradientPaintContext implements PaintContext {
|
||||
|
||||
private ColorModel mColorModel;
|
||||
private final Rectangle mDeviceBounds;
|
||||
private final Rectangle2D mUserBounds;
|
||||
private final AffineTransform mXform;
|
||||
private final RenderingHints mHints;
|
||||
|
||||
public MultiPointLinearGradientPaintContext(ColorModel cm, Rectangle deviceBounds,
|
||||
Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
|
||||
mColorModel = cm;
|
||||
// FIXME: so far all this is always the same rect gotten in getRaster with an indentity matrix?
|
||||
mDeviceBounds = deviceBounds;
|
||||
mUserBounds = userBounds;
|
||||
mXform = xform;
|
||||
mHints = hints;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public ColorModel getColorModel() {
|
||||
return mColorModel;
|
||||
}
|
||||
|
||||
public Raster getRaster(int x, int y, int w, int h) {
|
||||
SampleModel sampleModel = mColorModel.createCompatibleSampleModel(w, h);
|
||||
WritableRaster raster = Raster.createWritableRaster(sampleModel,
|
||||
new java.awt.Point(x, y));
|
||||
|
||||
DataBuffer data = raster.getDataBuffer();
|
||||
|
||||
if (mDx == 0) { // vertical gradient
|
||||
// compute first column and copy to all other columns
|
||||
int index = 0;
|
||||
for (int iy = 0 ; iy < h ; iy++) {
|
||||
int color = getColor(iy + y, mY0, mDy);
|
||||
for (int ix = 0 ; ix < w ; ix++) {
|
||||
data.setElem(index++, color);
|
||||
}
|
||||
}
|
||||
} else if (mDy == 0) { // horizontal
|
||||
// compute first line in a tmp array and copy to all lines
|
||||
int[] line = new int[w];
|
||||
for (int ix = 0 ; ix < w ; ix++) {
|
||||
line[ix] = getColor(ix + x, mX0, mDx);
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (int iy = 0 ; iy < h ; iy++) {
|
||||
for (int ix = 0 ; ix < w ; ix++) {
|
||||
data.setElem(index++, line[ix]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int index = 0;
|
||||
for (int iy = 0 ; iy < h ; iy++) {
|
||||
for (int ix = 0 ; ix < w ; ix++) {
|
||||
data.setElem(index++, getColor(ix + x, iy + y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return raster;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a color for the easy vertical/horizontal mode */
|
||||
private int getColor(float absPos, float refPos, float refSize) {
|
||||
float pos = (absPos - refPos) / refSize;
|
||||
|
||||
return getIndexFromPos(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color for an arbitrary point.
|
||||
*/
|
||||
private int getColor(float x, float y) {
|
||||
// find the x position on the gradient vector.
|
||||
float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
|
||||
// from it get the position relative to the vector
|
||||
float pos = (float) ((_x - mX0) / mDx);
|
||||
|
||||
return getIndexFromPos(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color based on the position in the gradient.
|
||||
* <var>pos</var> can be anything, even < 0 or > > 1, as the gradient
|
||||
* will use {@link TileMode} value to convert it into a [0,1] value.
|
||||
*/
|
||||
private int getIndexFromPos(float pos) {
|
||||
if (pos < 0.f) {
|
||||
switch (mTile) {
|
||||
case CLAMP:
|
||||
pos = 0.f;
|
||||
break;
|
||||
case REPEAT:
|
||||
// remove the integer part to stay in the [0,1] range
|
||||
// careful: this is a negative value, so use ceil instead of floor
|
||||
pos = pos - (float)Math.ceil(pos);
|
||||
break;
|
||||
case MIRROR:
|
||||
// get the integer and the decimal part
|
||||
// careful: this is a negative value, so use ceil instead of floor
|
||||
int intPart = (int)Math.ceil(pos);
|
||||
pos = pos - intPart;
|
||||
// 0 -> -1 : mirrored order
|
||||
// -1 -> -2: normal order
|
||||
// etc..
|
||||
// this means if the intpart is even we invert
|
||||
if ((intPart % 2) == 0) {
|
||||
pos = 1.f - pos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (pos > 1f) {
|
||||
switch (mTile) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int index = (int)((pos * GRADIENT_SIZE) + .5);
|
||||
|
||||
return mGradient[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user