diff options
author | Xavier Ducrohet <xav@android.com> | 2010-01-19 10:20:43 -0800 |
---|---|---|
committer | Xavier Ducrohet <xav@android.com> | 2010-01-19 14:03:31 -0800 |
commit | 45a7c285985976c23d818665694addbb25e02565 (patch) | |
tree | 19af466f6639bfe9d00b745b5fffdbd59e5bc7d9 /tools/layoutlib/bridge | |
parent | f167c4bfca57b5467f40f6cf25e10fb12183a9f3 (diff) | |
download | frameworks_base-45a7c285985976c23d818665694addbb25e02565.zip frameworks_base-45a7c285985976c23d818665694addbb25e02565.tar.gz frameworks_base-45a7c285985976c23d818665694addbb25e02565.tar.bz2 |
ADT/Layoutlib: implement radial gradient.
Also refactored some parts of LinearGradient to reuse them
in the radial gradient
Change-Id: I2ec69bd60190bd014217d989177dcc7269188dea
Diffstat (limited to 'tools/layoutlib/bridge')
3 files changed, 338 insertions, 219 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/GradientShader.java b/tools/layoutlib/bridge/src/android/graphics/GradientShader.java new file mode 100644 index 0000000..40e5df2 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/GradientShader.java @@ -0,0 +1,197 @@ +/* + * 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; + + +/** + * Base class for Gradient shader. This is not a standard android class and is just used + * as a base class for the re-implemented gradient classes. + * + * It also provides a base class to handle common code between the different shaders' + * implementations of {@link java.awt.Paint}. + * + * @see LinearGradient + * @see RadialGradient + * @see SweepGradient + */ +public abstract class GradientShader extends Shader { + + protected final int[] mColors; + protected final float[] mPositions; + + /** + * Creates the base shader and do some basic test on the parameters. + * + * @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 GradientShader(int colors[], float positions[]) { + if (colors.length < 2) { + throw new IllegalArgumentException("needs >= 2 number of colors"); + } + if (positions != null && colors.length != positions.length) { + throw new IllegalArgumentException("color and position arrays must be of equal length"); + } + + 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; + } + } + + 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; + } + + 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 synchronized 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. + * <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. + */ + protected int getGradientColor(float pos) { + if (pos < 0.f) { + switch (mTileMode) { + 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 (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; + } + } + + 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); + } + + + } +} diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java index 38ffed3..10c4a5e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java +++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java @@ -16,19 +16,9 @@ package android.graphics; -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.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.Raster; +public class LinearGradient extends GradientShader { -public class LinearGradient extends Shader { - - private Paint mJavaPaint; + private java.awt.Paint mJavaPaint; /** * Create a shader that draws a linear gradient along a line. @@ -45,24 +35,8 @@ public class LinearGradient extends Shader { */ public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[], TileMode tile) { - if (colors.length < 2) { - throw new IllegalArgumentException("needs >= 2 number of colors"); - } - if (positions != null && colors.length != positions.length) { - throw new IllegalArgumentException("color and position arrays must be of equal length"); - } - - 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; - } - } - - mJavaPaint = new MultiPointLinearGradientPaint(x0, y0, x1, y1, colors, positions, tile); + super(colors, positions); + mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile); } /** @@ -84,117 +58,62 @@ public class LinearGradient extends Shader { // ---------- Custom Methods @Override - public Paint getJavaPaint() { + java.awt.Paint getJavaPaint() { return mJavaPaint; } - private static class MultiPointLinearGradientPaint implements Paint { - private final static int GRADIENT_SIZE = 100; + /** + * Linear Gradient (Java) Paint able to handle more than 2 points, as + * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile + * modes. + */ + private static class LinearGradientPaint extends GradientPaint { 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[], + public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[], float positions[], TileMode tile) { + super(colors, positions, 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 java.awt.PaintContext createContext( + java.awt.image.ColorModel colorModel, + java.awt.Rectangle deviceBounds, + java.awt.geom.Rectangle2D userBounds, + java.awt.geom.AffineTransform xform, + java.awt.RenderingHints hints) { + precomputeGradientColors(); + return new LinearGradientPaintContext(colorModel); } - 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]); + private class LinearGradientPaintContext implements java.awt.PaintContext { - mGradient[i] = getColor(mColors[prevPos], mColors[nextPos], percent); - } - } - } + private final java.awt.image.ColorModel mColorModel; - /** - * 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; + public LinearGradientPaintContext(java.awt.image.ColorModel colorModel) { + mColorModel = colorModel; // 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() { + public java.awt.image.ColorModel getColorModel() { return mColorModel; } - public Raster getRaster(int x, int y, int w, int h) { - BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + public java.awt.image.Raster getRaster(int x, int y, int w, int h) { + java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h, + java.awt.image.BufferedImage.TYPE_INT_ARGB); int[] data = new int[w*h]; @@ -236,7 +155,7 @@ public class LinearGradient extends Shader { private int getColor(float absPos, float refPos, float refSize) { float pos = (absPos - refPos) / refSize; - return getIndexFromPos(pos); + return getGradientColor(pos); } /** @@ -248,66 +167,7 @@ public class LinearGradient extends Shader { // 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]; + return getGradientColor(pos); } } } diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java index 13848c5..db8dff2 100644 --- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java +++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java @@ -16,55 +16,117 @@ package android.graphics; -import java.awt.Paint; - -public class RadialGradient extends Shader { - - /** Create a shader that draws a radial gradient given the center and radius. - @param x The x-coordinate of the center of the radius - @param y The y-coordinate of the center of the radius - @param radius Must be positive. The radius of the circle for this gradient - @param colors The colors to be distributed between the center and edge of the circle - @param positions May be NULL. The relative position of - each corresponding color in the colors array. If this is NULL, - the the colors are distributed evenly between the center and edge of the circle. - @param tile The Shader tiling mode - */ - public RadialGradient(float x, float y, float radius, - int colors[], float positions[], TileMode tile) { - if (radius <= 0) { - throw new IllegalArgumentException("radius must be > 0"); - } - if (colors.length < 2) { - throw new IllegalArgumentException("needs >= 2 number of colors"); - } - if (positions != null && colors.length != positions.length) { - throw new IllegalArgumentException("color and position arrays must be of equal length"); - } - - // FIXME Implement shader - } - - /** Create a shader that draws a radial gradient given the center and radius. - @param x The x-coordinate of the center of the radius - @param y The y-coordinate of the center of the radius - @param radius Must be positive. The radius of the circle for this gradient - @param color0 The color at the center of the circle. - @param color1 The color at the edge of the circle. - @param tile The Shader tiling mode - */ - public RadialGradient(float x, float y, float radius, - int color0, int color1, TileMode tile) { - if (radius <= 0) { - throw new IllegalArgumentException("radius must be > 0"); - } - // FIXME Implement shader - } +public class RadialGradient extends GradientShader { + + private RadialGradientPaint mPaint; + + /** + * Create a shader that draws a radial gradient given the center and radius. + * + * @param x The x-coordinate of the center of the radius + * @param y The y-coordinate of the center of the radius + * @param radius Must be positive. The radius of the circle for this + * gradient + * @param colors The colors to be distributed between the center and edge of + * the circle + * @param positions May be NULL. The relative position of each corresponding + * color in the colors array. If this is NULL, the the colors are + * distributed evenly between the center and edge of the circle. + * @param tile The Shader tiling mode + */ + public RadialGradient(float x, float y, float radius, int colors[], float positions[], + TileMode tile) { + super(colors, positions); + if (radius <= 0) { + throw new IllegalArgumentException("radius must be > 0"); + } + + mPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile); + } + + /** + * Create a shader that draws a radial gradient given the center and radius. + * + * @param x The x-coordinate of the center of the radius + * @param y The y-coordinate of the center of the radius + * @param radius Must be positive. The radius of the circle for this + * gradient + * @param color0 The color at the center of the circle. + * @param color1 The color at the edge of the circle. + * @param tile The Shader tiling mode + */ + public RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile) { + this(x, y, radius, new int[] { color0, color1 }, null /* positions */, tile); + } @Override - Paint getJavaPaint() { - // TODO Auto-generated method stub - return null; + java.awt.Paint getJavaPaint() { + return mPaint; } -} + private static class RadialGradientPaint extends GradientPaint { + + private final float mX; + private final float mY; + private final float mRadius; + + public RadialGradientPaint(float x, float y, float radius, int[] colors, float[] positions, TileMode mode) { + super(colors, positions, mode); + mX = x; + mY = y; + mRadius = radius; + } + + public java.awt.PaintContext createContext( + java.awt.image.ColorModel colorModel, + java.awt.Rectangle deviceBounds, + java.awt.geom.Rectangle2D userBounds, + java.awt.geom.AffineTransform xform, + java.awt.RenderingHints hints) { + precomputeGradientColors(); + return new RadialGradientPaintContext(colorModel); + } + + private class RadialGradientPaintContext implements java.awt.PaintContext { + + private final java.awt.image.ColorModel mColorModel; + + public RadialGradientPaintContext(java.awt.image.ColorModel colorModel) { + mColorModel = colorModel; + } + + public void dispose() { + } + + public java.awt.image.ColorModel getColorModel() { + return mColorModel; + } + + public java.awt.image.Raster getRaster(int x, int y, int w, int h) { + java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h, + java.awt.image.BufferedImage.TYPE_INT_ARGB); + + int[] data = new int[w*h]; + + // compute distance from each point to the center, and figure out the distance from + // it. + int index = 0; + for (int iy = 0 ; iy < h ; iy++) { + for (int ix = 0 ; ix < w ; ix++) { + float _x = x + ix - mX; + float _y = y + iy - mY; + float distance = (float) Math.sqrt(_x * _x + _y * _y); + + data[index++] = getGradientColor(distance / mRadius); + } + } + + image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/); + + return image.getRaster(); + } + + } + } + +} |