summaryrefslogtreecommitdiffstats
path: root/tools/layoutlib
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2010-01-14 15:15:35 -0800
committerXavier Ducrohet <xav@android.com>2010-01-14 15:15:35 -0800
commit374abf0b95ca94d34f94edd385fb37a8d5cb123b (patch)
tree60dcb0c54cac2ce1d49ddb318184bfe3e5e103df /tools/layoutlib
parent5e083024269e7c5bf904301a98fa6eade6fec094 (diff)
downloadframeworks_base-374abf0b95ca94d34f94edd385fb37a8d5cb123b.zip
frameworks_base-374abf0b95ca94d34f94edd385fb37a8d5cb123b.tar.gz
frameworks_base-374abf0b95ca94d34f94edd385fb37a8d5cb123b.tar.bz2
ADT/Layout: support for 3+ color in linear gradients
Change-Id: I14c6a5a1de41470c6f1c66d490492ecc727302f2
Diffstat (limited to 'tools/layoutlib')
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/LinearGradient.java249
1 files changed, 243 insertions, 6 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
index 7cb8f26..945a539 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
@@ -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 &lt; 0 or &gt; > 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];
+ }
}
}