diff options
author | Xavier Ducrohet <xav@android.com> | 2010-12-20 08:22:47 -0800 |
---|---|---|
committer | Xavier Ducrohet <xav@android.com> | 2010-12-21 10:43:18 -0800 |
commit | d348b6eaa98e23cb38d90906df109aaa2d20ea7f (patch) | |
tree | c9195fa9628e78108424cbae0dd2a86a12d2faeb /tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java | |
parent | 19a021038f2f4683dddef651543d7298f5bd7218 (diff) | |
download | frameworks_base-d348b6eaa98e23cb38d90906df109aaa2d20ea7f.zip frameworks_base-d348b6eaa98e23cb38d90906df109aaa2d20ea7f.tar.gz frameworks_base-d348b6eaa98e23cb38d90906df109aaa2d20ea7f.tar.bz2 |
LayoutLib: support for Path and BitmapShader using delegates.
Also created delegates for all missing shader, xfermode
and patheffect classes. Moved the logic of the xfermode,
and patheffects that was in Canvas_Delegate into the
xfermode/patheffect classes, and added support (in all
3 clases) for knowing if the shader/xfermode/patheffect
is actually supported or not. Make use of fidelityWarning
in LayoutLog if they are not.
Diffstat (limited to 'tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java')
-rw-r--r-- | tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java new file mode 100644 index 0000000..811f0f6 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -0,0 +1,748 @@ +/* + * 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 com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.impl.DelegateManager; + +import android.graphics.Path.Direction; +import android.graphics.Path.FillType; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Area; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * Delegate implementing the native methods of android.graphics.Path + * + * Through the layoutlib_create tool, the original native methods of Path have been replaced + * by calls to methods of the same name in this delegate class. + * + * This class behaves like the original native implementation, but in Java, keeping previously + * native data into its own objects and mapping them to int that are sent back and forth between + * it and the original Path class. + * + * @see DelegateManager + * + */ +public final class Path_Delegate { + + // ---- delegate manager ---- + private static final DelegateManager<Path_Delegate> sManager = + new DelegateManager<Path_Delegate>(); + + // ---- delegate data ---- + private FillType mFillType = FillType.WINDING; + private GeneralPath mPath = new GeneralPath(); + + private float mLastX = 0; + private float mLastY = 0; + + // ---- Public Helper methods ---- + + public static Path_Delegate getDelegate(int nPath) { + return sManager.getDelegate(nPath); + } + + public Shape getJavaShape() { + return mPath; + } + + // ---- native methods ---- + + /*package*/ static int init1() { + // create the delegate + Path_Delegate newDelegate = new Path_Delegate(); + + return sManager.addDelegate(newDelegate); + } + + /*package*/ static int init2(int nPath) { + // create the delegate + Path_Delegate newDelegate = new Path_Delegate(); + + // get the delegate to copy + if (nPath > 0) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return 0; + } + + newDelegate.set(pathDelegate); + } + + return sManager.addDelegate(newDelegate); + } + + /*package*/ static void native_reset(int nPath) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.mPath.reset(); + } + + /*package*/ static void native_rewind(int nPath) { + // call out to reset since there's nothing to optimize in + // terms of data structs. + native_reset(nPath); + } + + /*package*/ static void native_set(int native_dst, int native_src) { + Path_Delegate pathDstDelegate = sManager.getDelegate(native_dst); + if (pathDstDelegate == null) { + assert false; + return; + } + + Path_Delegate pathSrcDelegate = sManager.getDelegate(native_src); + if (pathSrcDelegate == null) { + assert false; + return; + } + + pathDstDelegate.set(pathSrcDelegate); + } + + /*package*/ static int native_getFillType(int nPath) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return 0; + } + + return pathDelegate.mFillType.nativeInt; + } + + /*package*/ static void native_setFillType(int nPath, int ft) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.mFillType = Path.sFillTypeArray[ft]; + } + + /*package*/ static boolean native_isEmpty(int nPath) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return true; + } + + return pathDelegate.isEmpty(); + } + + /*package*/ static boolean native_isRect(int nPath, RectF rect) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return false; + } + + // create an Area that can test if the path is a rect + Area area = new Area(pathDelegate.mPath); + if (area.isRectangular()) { + if (rect != null) { + pathDelegate.fillBounds(rect); + } + + return true; + } + + return false; + } + + /*package*/ static void native_computeBounds(int nPath, RectF bounds) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.fillBounds(bounds); + } + + /*package*/ static void native_incReserve(int nPath, int extraPtCount) { + // since we use a java2D path, there's no way to pre-allocate new points, + // so we do nothing. + } + + /*package*/ static void native_moveTo(int nPath, float x, float y) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.moveTo(x, y); + } + + /*package*/ static void native_rMoveTo(int nPath, float dx, float dy) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.rMoveTo(dx, dy); + } + + /*package*/ static void native_lineTo(int nPath, float x, float y) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.lineTo(x, y); + } + + /*package*/ static void native_rLineTo(int nPath, float dx, float dy) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.rLineTo(dx, dy); + } + + /*package*/ static void native_quadTo(int nPath, float x1, float y1, float x2, float y2) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.quadTo(x1, y1, x2, y2); + } + + /*package*/ static void native_rQuadTo(int nPath, float dx1, float dy1, + float dx2, float dy2) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.rQuadTo(dx1, dy1, dx2, dy2); + } + + /*package*/ static void native_cubicTo(int nPath, float x1, float y1, + float x2, float y2, float x3, float y3) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.cubicTo(x1, y1, x2, y2, x3, y3); + } + + /*package*/ static void native_rCubicTo(int nPath, float x1, float y1, + float x2, float y2, float x3, float y3) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.rCubicTo(x1, y1, x2, y2, x3, y3); + } + + /*package*/ static void native_arcTo(int nPath, RectF oval, + float startAngle, float sweepAngle, boolean forceMoveTo) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.arcTo(oval, startAngle, sweepAngle, forceMoveTo); + } + + /*package*/ static void native_close(int nPath) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.close(); + } + + /*package*/ static void native_addRect(int nPath, RectF rect, int dir) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.addRect(rect.left, rect.top, rect.right, rect.bottom, dir); + } + + /*package*/ static void native_addRect(int nPath, float left, float top, + float right, float bottom, int dir) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.addRect(left, top, right, bottom, dir); + } + + /*package*/ static void native_addOval(int nPath, RectF oval, int dir) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_addCircle(int nPath, float x, float y, + float radius, int dir) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_addArc(int nPath, RectF oval, + float startAngle, float sweepAngle) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_addRoundRect(int nPath, RectF rect, + float rx, float ry, int dir) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_addRoundRect(int nPath, RectF r, + float[] radii, int dir) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_addPath(int nPath, int src, float dx, + float dy) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_addPath(int nPath, int src) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_addPath(int nPath, int src, int matrix) { + // FIXME + throw new UnsupportedOperationException(); + } + + /*package*/ static void native_offset(int nPath, float dx, float dy, + int dst_path) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + Path_Delegate dstDelegate = null; + if (dst_path > 0) { + dstDelegate = sManager.getDelegate(dst_path); + if (dstDelegate == null) { + assert false; + return; + } + } + + pathDelegate.offset(dx, dy, dstDelegate); + } + + /*package*/ static void native_offset(int nPath, float dx, float dy) { + native_offset(nPath, dx, dy, 0); + } + + /*package*/ static void native_setLastPoint(int nPath, float dx, float dy) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + pathDelegate.mLastX = dx; + pathDelegate.mLastY = dy; + } + + /*package*/ static void native_transform(int nPath, int matrix, + int dst_path) { + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + assert false; + return; + } + + Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix); + if (matrixDelegate == null) { + assert false; + return; + } + + Path_Delegate dstDelegate = null; + if (dst_path > 0) { + dstDelegate = sManager.getDelegate(dst_path); + if (dstDelegate == null) { + assert false; + return; + } + } + + pathDelegate.transform(matrixDelegate, dstDelegate); + } + + /*package*/ static void native_transform(int nPath, int matrix) { + native_transform(nPath, matrix, 0); + } + + /*package*/ static void finalizer(int nPath) { + sManager.removeDelegate(nPath); + } + + + // ---- Private helper methods ---- + + private void set(Path_Delegate delegate) { + mPath.reset(); + setFillType(delegate.mFillType); + mPath.append(delegate.mPath, false /*connect*/); + } + + private void setFillType(FillType fillType) { + mFillType = fillType; + mPath.setWindingRule(getWindingRule(fillType)); + } + + /** + * Returns the Java2D winding rules matching a given Android {@link FillType}. + * @param type the android fill type + * @return the matching java2d winding rule. + */ + private static int getWindingRule(FillType type) { + switch (type) { + case WINDING: + case INVERSE_WINDING: + return GeneralPath.WIND_NON_ZERO; + case EVEN_ODD: + case INVERSE_EVEN_ODD: + return GeneralPath.WIND_EVEN_ODD; + } + + assert false; + throw new IllegalArgumentException(); + } + + private static Direction getDirection(int direction) { + for (Direction d : Direction.values()) { + if (direction == d.nativeInt) { + return d; + } + } + + assert false; + return null; + } + + /** + * Returns whether the path is empty. + * @return true if the path is empty. + */ + private boolean isEmpty() { + return mPath.getCurrentPoint() == null; + } + + /** + * Fills the given {@link RectF} with the path bounds. + * @param bounds the RectF to be filled. + */ + private void fillBounds(RectF bounds) { + Rectangle2D rect = mPath.getBounds2D(); + bounds.left = (float)rect.getMinX(); + bounds.right = (float)rect.getMaxX(); + bounds.top = (float)rect.getMinY(); + bounds.bottom = (float)rect.getMaxY(); + } + + /** + * Set the beginning of the next contour to the point (x,y). + * + * @param x The x-coordinate of the start of a new contour + * @param y The y-coordinate of the start of a new contour + */ + private void moveTo(float x, float y) { + mPath.moveTo(mLastX = x, mLastY = y); + } + + /** + * Set the beginning of the next contour relative to the last point on the + * previous contour. If there is no previous contour, this is treated the + * same as moveTo(). + * + * @param dx The amount to add to the x-coordinate of the end of the + * previous contour, to specify the start of a new contour + * @param dy The amount to add to the y-coordinate of the end of the + * previous contour, to specify the start of a new contour + */ + private void rMoveTo(float dx, float dy) { + dx += mLastX; + dy += mLastY; + mPath.moveTo(mLastX = dx, mLastY = dy); + } + + /** + * Add a line from the last point to the specified point (x,y). + * If no moveTo() call has been made for this contour, the first point is + * automatically set to (0,0). + * + * @param x The x-coordinate of the end of a line + * @param y The y-coordinate of the end of a line + */ + private void lineTo(float x, float y) { + mPath.lineTo(mLastX = x, mLastY = y); + } + + /** + * Same as lineTo, but the coordinates are considered relative to the last + * point on this contour. If there is no previous point, then a moveTo(0,0) + * is inserted automatically. + * + * @param dx The amount to add to the x-coordinate of the previous point on + * this contour, to specify a line + * @param dy The amount to add to the y-coordinate of the previous point on + * this contour, to specify a line + */ + private void rLineTo(float dx, float dy) { + if (isEmpty()) { + mPath.moveTo(mLastX = 0, mLastY = 0); + } + dx += mLastX; + dy += mLastY; + mPath.lineTo(mLastX = dx, mLastY = dy); + } + + /** + * Add a quadratic bezier from the last point, approaching control point + * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for + * this contour, the first point is automatically set to (0,0). + * + * @param x1 The x-coordinate of the control point on a quadratic curve + * @param y1 The y-coordinate of the control point on a quadratic curve + * @param x2 The x-coordinate of the end point on a quadratic curve + * @param y2 The y-coordinate of the end point on a quadratic curve + */ + private void quadTo(float x1, float y1, float x2, float y2) { + mPath.quadTo(x1, y1, mLastX = x2, mLastY = y2); + } + + /** + * Same as quadTo, but the coordinates are considered relative to the last + * point on this contour. If there is no previous point, then a moveTo(0,0) + * is inserted automatically. + * + * @param dx1 The amount to add to the x-coordinate of the last point on + * this contour, for the control point of a quadratic curve + * @param dy1 The amount to add to the y-coordinate of the last point on + * this contour, for the control point of a quadratic curve + * @param dx2 The amount to add to the x-coordinate of the last point on + * this contour, for the end point of a quadratic curve + * @param dy2 The amount to add to the y-coordinate of the last point on + * this contour, for the end point of a quadratic curve + */ + private void rQuadTo(float dx1, float dy1, float dx2, float dy2) { + if (isEmpty()) { + mPath.moveTo(mLastX = 0, mLastY = 0); + } + dx1 += mLastX; + dy1 += mLastY; + dx2 += mLastX; + dy2 += mLastY; + mPath.quadTo(dx1, dy1, mLastX = dx2, mLastY = dy2); + } + + /** + * Add a cubic bezier from the last point, approaching control points + * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been + * made for this contour, the first point is automatically set to (0,0). + * + * @param x1 The x-coordinate of the 1st control point on a cubic curve + * @param y1 The y-coordinate of the 1st control point on a cubic curve + * @param x2 The x-coordinate of the 2nd control point on a cubic curve + * @param y2 The y-coordinate of the 2nd control point on a cubic curve + * @param x3 The x-coordinate of the end point on a cubic curve + * @param y3 The y-coordinate of the end point on a cubic curve + */ + private void cubicTo(float x1, float y1, float x2, float y2, + float x3, float y3) { + mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3); + } + + /** + * Same as cubicTo, but the coordinates are considered relative to the + * current point on this contour. If there is no previous point, then a + * moveTo(0,0) is inserted automatically. + */ + private void rCubicTo(float dx1, float dy1, float dx2, float dy2, + float dx3, float dy3) { + if (isEmpty()) { + mPath.moveTo(mLastX = 0, mLastY = 0); + } + dx1 += mLastX; + dy1 += mLastY; + dx2 += mLastX; + dy2 += mLastY; + dx3 += mLastX; + dy3 += mLastY; + mPath.curveTo(dx1, dy1, dx2, dy2, mLastX = dx3, mLastY = dy3); + } + + /** + * Append the specified arc to the path as a new contour. If the start of + * the path is different from the path's current last point, then an + * automatic lineTo() is added to connect the current contour to the + * start of the arc. However, if the path is empty, then we call moveTo() + * with the first point of the arc. The sweep angle is tread mod 360. + * + * @param oval The bounds of oval defining shape and size of the arc + * @param startAngle Starting angle (in degrees) where the arc begins + * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated + * mod 360. + * @param forceMoveTo If true, always begin a new contour with the arc + */ + private void arcTo(RectF oval, float startAngle, float sweepAngle, + boolean forceMoveTo) { + Arc2D arc = new Arc2D.Float(oval.left, oval.top, oval.width(), oval.height(), startAngle, + sweepAngle, Arc2D.OPEN); + mPath.append(arc, true /*connect*/); + + resetLastPointFromPath(); + } + + /** + * Close the current contour. If the current point is not equal to the + * first point of the contour, a line segment is automatically added. + */ + private void close() { + mPath.closePath(); + } + + private void resetLastPointFromPath() { + Point2D last = mPath.getCurrentPoint(); + mLastX = (float) last.getX(); + mLastY = (float) last.getY(); + } + + /** + * Add a closed rectangle contour to the path + * + * @param left The left side of a rectangle to add to the path + * @param top The top of a rectangle to add to the path + * @param right The right side of a rectangle to add to the path + * @param bottom The bottom of a rectangle to add to the path + * @param dir The direction to wind the rectangle's contour + */ + private void addRect(float left, float top, float right, float bottom, + int dir) { + moveTo(left, top); + + Direction direction = getDirection(dir); + + switch (direction) { + case CW: + lineTo(right, top); + lineTo(right, bottom); + lineTo(left, bottom); + break; + case CCW: + lineTo(left, bottom); + lineTo(right, bottom); + lineTo(right, top); + break; + } + + close(); + + resetLastPointFromPath(); + } + + /** + * Offset the path by (dx,dy), returning true on success + * + * @param dx The amount in the X direction to offset the entire path + * @param dy The amount in the Y direction to offset the entire path + * @param dst The translated path is written here. If this is null, then + * the original path is modified. + */ + public void offset(float dx, float dy, Path_Delegate dst) { + GeneralPath newPath = new GeneralPath(); + + PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy)); + + newPath.append(iterator, false /*connect*/); + + if (dst != null) { + dst.mPath = newPath; + } else { + mPath = newPath; + } + } + + /** + * Transform the points in this path by matrix, and write the answer + * into dst. If dst is null, then the the original path is modified. + * + * @param matrix The matrix to apply to the path + * @param dst The transformed path is written here. If dst is null, + * then the the original path is modified + */ + public void transform(Matrix_Delegate matrix, Path_Delegate dst) { + if (matrix.hasPerspective()) { + assert false; + Bridge.getLog().fidelityWarning(null, + "android.graphics.Path#transform() only " + + "supports affine transformations in the Layout Preview.", null); + } + + GeneralPath newPath = new GeneralPath(); + + PathIterator iterator = mPath.getPathIterator(matrix.getAffineTransform()); + + newPath.append(iterator, false /*connect*/); + + if (dst != null) { + dst.mPath = newPath; + } else { + mPath = newPath; + } + } +} |