diff options
author | Xavier Ducrohet <xav@android.com> | 2010-01-14 15:15:35 -0800 |
---|---|---|
committer | Xavier Ducrohet <xav@android.com> | 2010-01-14 15:15:35 -0800 |
commit | 374abf0b95ca94d34f94edd385fb37a8d5cb123b (patch) | |
tree | 60dcb0c54cac2ce1d49ddb318184bfe3e5e103df /tools/layoutlib | |
parent | 5e083024269e7c5bf904301a98fa6eade6fec094 (diff) | |
download | frameworks_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.java | 249 |
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 < 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]; + } } } |