diff options
8 files changed, 1474 insertions, 227 deletions
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index db4c6dc68b77..ae42ba6660fd 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java @@ -305,6 +305,12 @@ public final class BridgeTypedArray extends TypedArray { return defValue; } + @Override + public ComplexColor getComplexColor(int index) { + // TODO: Support GradientColor + return getColorStateList(index); + } + /** * Retrieve the ColorStateList for the attribute at <var>index</var>. * The value may be either a single solid color or a reference to diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index c4fbd562812a..fa880f0710c4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -142,7 +142,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_setBitmap(long canvas, Bitmap bitmap) { + public static void native_setBitmap(long canvas, Bitmap bitmap) { Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas); Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); if (canvasDelegate == null || bitmapDelegate==null) { @@ -153,7 +153,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static boolean native_isOpaque(long nativeCanvas) { + public static boolean native_isOpaque(long nativeCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -164,10 +164,10 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_setHighContrastText(long nativeCanvas, boolean highContrastText){} + public static void native_setHighContrastText(long nativeCanvas, boolean highContrastText){} @LayoutlibDelegate - /*package*/ static int native_getWidth(long nativeCanvas) { + public static int native_getWidth(long nativeCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -178,7 +178,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static int native_getHeight(long nativeCanvas) { + public static int native_getHeight(long nativeCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -189,7 +189,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static int native_save(long nativeCanvas, int saveFlags) { + public static int native_save(long nativeCanvas, int saveFlags) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -200,7 +200,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static int native_saveLayer(long nativeCanvas, float l, + public static int native_saveLayer(long nativeCanvas, float l, float t, float r, float b, long paint, int layerFlags) { // get the delegate from the native int. @@ -219,7 +219,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static int native_saveLayerAlpha(long nativeCanvas, float l, + public static int native_saveLayerAlpha(long nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags) { // get the delegate from the native int. @@ -232,7 +232,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_restore(long nativeCanvas, boolean throwOnUnderflow) { + public static void native_restore(long nativeCanvas, boolean throwOnUnderflow) { // FIXME: implement throwOnUnderflow. // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); @@ -244,7 +244,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_restoreToCount(long nativeCanvas, int saveCount, + public static void native_restoreToCount(long nativeCanvas, int saveCount, boolean throwOnUnderflow) { // FIXME: implement throwOnUnderflow. // get the delegate from the native int. @@ -257,7 +257,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static int native_getSaveCount(long nativeCanvas) { + public static int native_getSaveCount(long nativeCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -268,7 +268,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_translate(long nativeCanvas, float dx, float dy) { + public static void native_translate(long nativeCanvas, float dx, float dy) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -279,7 +279,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_scale(long nativeCanvas, float sx, float sy) { + public static void native_scale(long nativeCanvas, float sx, float sy) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -290,7 +290,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_rotate(long nativeCanvas, float degrees) { + public static void native_rotate(long nativeCanvas, float degrees) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -301,7 +301,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_skew(long nativeCanvas, float kx, float ky) { + public static void native_skew(long nativeCanvas, float kx, float ky) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -325,7 +325,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_concat(long nCanvas, long nMatrix) { + public static void native_concat(long nCanvas, long nMatrix) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); if (canvasDelegate == null) { @@ -353,7 +353,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_setMatrix(long nCanvas, long nMatrix) { + public static void native_setMatrix(long nCanvas, long nMatrix) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); if (canvasDelegate == null) { @@ -383,7 +383,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static boolean native_clipRect(long nCanvas, + public static boolean native_clipRect(long nCanvas, float left, float top, float right, float bottom, int regionOp) { @@ -397,7 +397,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static boolean native_clipPath(long nativeCanvas, + public static boolean native_clipPath(long nativeCanvas, long nativePath, int regionOp) { Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); @@ -414,7 +414,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static boolean native_clipRegion(long nativeCanvas, + public static boolean native_clipRegion(long nativeCanvas, long nativeRegion, int regionOp) { Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); @@ -431,7 +431,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeSetDrawFilter(long nativeCanvas, long nativeFilter) { + public static void nativeSetDrawFilter(long nativeCanvas, long nativeFilter) { Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { return; @@ -446,7 +446,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static boolean native_getClipBounds(long nativeCanvas, + public static boolean native_getClipBounds(long nativeCanvas, Rect bounds) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); @@ -467,7 +467,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_getCTM(long canvas, long matrix) { + public static void native_getCTM(long canvas, long matrix) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas); if (canvasDelegate == null) { @@ -484,13 +484,13 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static boolean native_quickReject(long nativeCanvas, long path) { + public static boolean native_quickReject(long nativeCanvas, long path) { // FIXME properly implement quickReject return false; } @LayoutlibDelegate - /*package*/ static boolean native_quickReject(long nativeCanvas, + public static boolean native_quickReject(long nativeCanvas, float left, float top, float right, float bottom) { // FIXME properly implement quickReject @@ -498,7 +498,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawColor(long nativeCanvas, final int color, final int mode) { + public static void native_drawColor(long nativeCanvas, final int color, final int mode) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -529,14 +529,14 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawPaint(long nativeCanvas, long paint) { + public static void native_drawPaint(long nativeCanvas, long paint) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "Canvas.drawPaint is not supported.", null, null /*data*/); } @LayoutlibDelegate - /*package*/ static void native_drawPoint(long nativeCanvas, float x, float y, + public static void native_drawPoint(long nativeCanvas, float x, float y, long nativePaint) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, @@ -544,7 +544,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawPoints(long nativeCanvas, float[] pts, int offset, int count, + public static void native_drawPoints(long nativeCanvas, float[] pts, int offset, int count, long nativePaint) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, @@ -552,7 +552,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawLine(long nativeCanvas, + public static void native_drawLine(long nativeCanvas, final float startX, final float startY, final float stopX, final float stopY, long paint) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, @@ -565,7 +565,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawLines(long nativeCanvas, + public static void native_drawLines(long nativeCanvas, final float[] pts, final int offset, final int count, long nativePaint) { draw(nativeCanvas, nativePaint, false /*compositeOnly*/, @@ -581,7 +581,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawRect(long nativeCanvas, + public static void native_drawRect(long nativeCanvas, final float left, final float top, final float right, final float bottom, long paint) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, @@ -607,7 +607,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawOval(long nativeCanvas, final float left, + public static void native_drawOval(long nativeCanvas, final float left, final float top, final float right, final float bottom, long paint) { if (right > left && bottom > top) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, @@ -634,7 +634,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawCircle(long nativeCanvas, + public static void native_drawCircle(long nativeCanvas, float cx, float cy, float radius, long paint) { native_drawOval(nativeCanvas, cx - radius, cy - radius, cx + radius, cy + radius, @@ -642,7 +642,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawArc(long nativeCanvas, + public static void native_drawArc(long nativeCanvas, final float left, final float top, final float right, final float bottom, final float startAngle, final float sweep, final boolean useCenter, long paint) { @@ -674,7 +674,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawRoundRect(long nativeCanvas, + public static void native_drawRoundRect(long nativeCanvas, final float left, final float top, final float right, final float bottom, final float rx, final float ry, long paint) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, @@ -704,7 +704,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawPath(long nativeCanvas, long path, long paint) { + public static void native_drawPath(long nativeCanvas, long path, long paint) { final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path); if (pathDelegate == null) { return; @@ -756,7 +756,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawRegion(long nativeCanvas, long nativeRegion, + public static void native_drawRegion(long nativeCanvas, long nativeRegion, long nativePaint) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, @@ -764,7 +764,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawNinePatch(Canvas thisCanvas, long nativeCanvas, + public static void native_drawNinePatch(Canvas thisCanvas, long nativeCanvas, long nativeBitmap, long ninePatch, final float dstLeft, final float dstTop, final float dstRight, final float dstBottom, long nativePaintOrZero, final int screenDensity, final int bitmapDensity) { @@ -811,7 +811,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap, + public static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap, float left, float top, long nativePaintOrZero, int canvasDensity, @@ -833,7 +833,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap, + public static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity) { @@ -849,7 +849,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawBitmap(long nativeCanvas, int[] colors, + public static void native_drawBitmap(long nativeCanvas, int[] colors, int offset, int stride, final float x, final float y, int width, int height, boolean hasAlpha, @@ -874,7 +874,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDrawBitmapMatrix(long nCanvas, Bitmap bitmap, + public static void nativeDrawBitmapMatrix(long nCanvas, Bitmap bitmap, long nMatrix, long nPaint) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); @@ -915,7 +915,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDrawBitmapMesh(long nCanvas, Bitmap bitmap, + public static void nativeDrawBitmapMesh(long nCanvas, Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, long nPaint) { // FIXME @@ -924,7 +924,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDrawVertices(long nCanvas, int mode, int n, + public static void nativeDrawVertices(long nCanvas, int mode, int n, float[] verts, int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset, @@ -936,14 +936,14 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawText(long nativeCanvas, char[] text, int index, int count, + public static void native_drawText(long nativeCanvas, char[] text, int index, int count, float startX, float startY, int flags, long paint, long typeface) { drawText(nativeCanvas, text, index, count, startX, startY, (flags & 1) != 0, paint, typeface); } @LayoutlibDelegate - /*package*/ static void native_drawText(long nativeCanvas, String text, + public static void native_drawText(long nativeCanvas, String text, int start, int end, float x, float y, final int flags, long paint, long typeface) { int count = end - start; @@ -954,7 +954,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawTextRun(long nativeCanvas, String text, + public static void native_drawTextRun(long nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, long paint, long typeface) { int count = end - start; @@ -965,14 +965,14 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawTextRun(long nativeCanvas, char[] text, + public static void native_drawTextRun(long nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, float x, float y, boolean isRtl, long paint, long typeface) { drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface); } @LayoutlibDelegate - /*package*/ static void native_drawTextOnPath(long nativeCanvas, + public static void native_drawTextOnPath(long nativeCanvas, char[] text, int index, int count, long path, float hOffset, @@ -984,7 +984,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawTextOnPath(long nativeCanvas, + public static void native_drawTextOnPath(long nativeCanvas, String text, long path, float hOffset, float vOffset, diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 514d7852bd05..839c1823dad8 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -223,6 +223,10 @@ public class Paint_Delegate { return mColorFilter; } + public void setColorFilter(long colorFilterPtr) { + mColorFilter = ColorFilter_Delegate.getDelegate(colorFilterPtr); + } + /** * Returns the {@link Shader} delegate or null if none have been set * diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index e1da27ba3497..08f0cb491882 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -572,7 +572,7 @@ public final class Path_Delegate { return null; } - private static void addPath(long destPath, long srcPath, AffineTransform transform) { + public static void addPath(long destPath, long srcPath, AffineTransform transform) { Path_Delegate destPathDelegate = sManager.getDelegate(destPath); if (destPathDelegate == null) { return; @@ -630,7 +630,7 @@ public final class Path_Delegate { * Fills the given {@link RectF} with the path bounds. * @param bounds the RectF to be filled. */ - private void fillBounds(RectF bounds) { + public void fillBounds(RectF bounds) { Rectangle2D rect = mPath.getBounds2D(); bounds.left = (float)rect.getMinX(); bounds.right = (float)rect.getMaxX(); @@ -644,7 +644,7 @@ public final class Path_Delegate { * @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) { + public void moveTo(float x, float y) { mPath.moveTo(mLastX = x, mLastY = y); } @@ -658,7 +658,7 @@ public final class Path_Delegate { * @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) { + public void rMoveTo(float dx, float dy) { dx += mLastX; dy += mLastY; mPath.moveTo(mLastX = dx, mLastY = dy); @@ -672,7 +672,7 @@ public final class Path_Delegate { * @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) { + public void lineTo(float x, float y) { if (!hasPoints()) { mPath.moveTo(mLastX = 0, mLastY = 0); } @@ -689,7 +689,7 @@ public final class Path_Delegate { * @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) { + public void rLineTo(float dx, float dy) { if (!hasPoints()) { mPath.moveTo(mLastX = 0, mLastY = 0); } @@ -714,7 +714,7 @@ public final class Path_Delegate { * @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) { + public void quadTo(float x1, float y1, float x2, float y2) { mPath.quadTo(x1, y1, mLastX = x2, mLastY = y2); } @@ -732,7 +732,7 @@ public final class Path_Delegate { * @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) { + public void rQuadTo(float dx1, float dy1, float dx2, float dy2) { if (!hasPoints()) { mPath.moveTo(mLastX = 0, mLastY = 0); } @@ -755,7 +755,7 @@ public final class Path_Delegate { * @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, + public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) { if (!hasPoints()) { mPath.moveTo(0, 0); @@ -768,7 +768,7 @@ public final class Path_Delegate { * 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, + public void rCubicTo(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) { if (!hasPoints()) { mPath.moveTo(mLastX = 0, mLastY = 0); @@ -798,7 +798,7 @@ public final class Path_Delegate { * mod 360. * @param forceMoveTo If true, always begin a new contour with the arc */ - private void arcTo(float left, float top, float right, float bottom, float startAngle, + public void arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) { Arc2D arc = new Arc2D.Float(left, top, right - left, bottom - top, -startAngle, @@ -812,7 +812,7 @@ public final class Path_Delegate { * 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() { + public void close() { mPath.closePath(); } @@ -831,7 +831,7 @@ public final class Path_Delegate { * @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, + public void addRect(float left, float top, float right, float bottom, int dir) { moveTo(left, top); diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java new file mode 100644 index 000000000000..22d15e148ba4 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java @@ -0,0 +1,1131 @@ +/* + * Copyright (C) 2016 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.drawable; + +import com.android.internal.R; +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.graphics.Canvas_Delegate; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Paint.Cap; +import android.graphics.Paint.Join; +import android.graphics.Paint_Delegate; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.graphics.Path_Delegate; +import android.graphics.Rect; +import android.graphics.Region.Op; +import android.util.ArrayMap; +import android.util.AttributeSet; +import android.util.Log; +import android.util.MathUtils; +import android.util.PathParser_Delegate; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.ArrayList; + +import static android.graphics.Canvas.CLIP_SAVE_FLAG; +import static android.graphics.Canvas.MATRIX_SAVE_FLAG; +import static android.graphics.Paint.Cap.BUTT; +import static android.graphics.Paint.Cap.ROUND; +import static android.graphics.Paint.Cap.SQUARE; +import static android.graphics.Paint.Join.BEVEL; +import static android.graphics.Paint.Join.MITER; +import static android.graphics.Paint.Style; + +/** + * Delegate used to provide new implementation of a select few methods of {@link VectorDrawable} + * <p> + * Through the layoutlib_create tool, the original methods of VectorDrawable have been replaced by + * calls to methods of the same name in this delegate class. + */ +@SuppressWarnings("unused") +public class VectorDrawable_Delegate { + private static final String LOGTAG = VectorDrawable_Delegate.class.getSimpleName(); + private static final boolean DBG_VECTOR_DRAWABLE = false; + + private static final DelegateManager<VNativeObject> sPathManager = + new DelegateManager<>(VNativeObject.class); + + private static <T> T getDelegate(long nativePtr) { + //noinspection unchecked + T object = (T) sPathManager.getDelegate(nativePtr); + assert object != null; + + return object; + } + + /** + * Obtains styled attributes from the theme, if available, or unstyled resources if the theme is + * null. + */ + private static TypedArray obtainAttributes( + Resources res, Theme theme, AttributeSet set, int[] attrs) { + if (theme == null) { + return res.obtainAttributes(set, attrs); + } + return theme.obtainStyledAttributes(set, attrs, 0, 0); + } + + private static int applyAlpha(int color, float alpha) { + int alphaBytes = Color.alpha(color); + color &= 0x00FFFFFF; + color |= ((int) (alphaBytes * alpha)) << 24; + return color; + } + + @LayoutlibDelegate + static long nCreateRenderer(long rootGroupPtr) { + VGroup_Delegate rootGroup = getDelegate(rootGroupPtr); + return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rootGroup)); + } + + @LayoutlibDelegate + static void nSetRendererViewportSize(long rendererPtr, float viewportWidth, + float viewportHeight) { + VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr); + nativePathRenderer.mViewportWidth = viewportWidth; + nativePathRenderer.mViewportHeight = viewportHeight; + } + + @LayoutlibDelegate + static boolean nSetRootAlpha(long rendererPtr, float alpha) { + VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr); + nativePathRenderer.setRootAlpha(alpha); + + return true; + } + + @LayoutlibDelegate + static float nGetRootAlpha(long rendererPtr) { + VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr); + + return nativePathRenderer.getRootAlpha(); + } + + @LayoutlibDelegate + static void nSetAllowCaching(long rendererPtr, boolean allowCaching) { + // ignored + } + + @LayoutlibDelegate + static void nDraw(long rendererPtr, long canvasWrapperPtr, + long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache) { + VPathRenderer_Delegate nativePathRenderer = + getDelegate(rendererPtr); + + nativePathRenderer.draw(canvasWrapperPtr, colorFilterPtr, bounds.width(), bounds.height()); + } + + @LayoutlibDelegate + static long nCreateFullPath() { + return sPathManager.addNewDelegate(new VFullPath_Delegate()); + } + + @LayoutlibDelegate + static long nCreateFullPath(long nativeFullPathPtr) { + VFullPath_Delegate original = getDelegate(nativeFullPathPtr); + + return sPathManager.addNewDelegate(new VFullPath_Delegate(original)); + } + + @LayoutlibDelegate + static boolean nGetFullPathProperties(long pathPtr, byte[] propertiesData, + int length) { + VFullPath_Delegate path = getDelegate(pathPtr); + + ByteBuffer properties = ByteBuffer.wrap(propertiesData); + properties.order(ByteOrder.nativeOrder()); + + properties.putFloat(VFullPath_Delegate.STROKE_WIDTH_INDEX * 4, path.getStrokeWidth()); + properties.putInt(VFullPath_Delegate.STROKE_COLOR_INDEX * 4, path.getStrokeColor()); + properties.putFloat(VFullPath_Delegate.STROKE_ALPHA_INDEX * 4, path.getStrokeAlpha()); + properties.putInt(VFullPath_Delegate.FILL_COLOR_INDEX * 4, path.getFillColor()); + properties.putFloat(VFullPath_Delegate.FILL_ALPHA_INDEX * 4, path.getStrokeAlpha()); + properties.putFloat(VFullPath_Delegate.TRIM_PATH_START_INDEX * 4, path.getTrimPathStart()); + properties.putFloat(VFullPath_Delegate.TRIM_PATH_END_INDEX * 4, path.getTrimPathEnd()); + properties.putFloat(VFullPath_Delegate.TRIM_PATH_OFFSET_INDEX * 4, + path.getTrimPathOffset()); + properties.putInt(VFullPath_Delegate.STROKE_LINE_CAP_INDEX * 4, path.getStrokeLineCap()); + properties.putInt(VFullPath_Delegate.STROKE_LINE_JOIN_INDEX * 4, path.getStrokeLineJoin()); + properties.putFloat(VFullPath_Delegate.STROKE_MITER_LIMIT_INDEX * 4, + path.getStrokeMiterlimit()); + + return true; + } + + @LayoutlibDelegate + static void nUpdateFullPathProperties(long pathPtr, float strokeWidth, + int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart, + float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap, + int strokeLineJoin) { + VFullPath_Delegate path = getDelegate(pathPtr); + + path.setStrokeWidth(strokeWidth); + path.setStrokeColor(strokeColor); + path.setStrokeAlpha(strokeAlpha); + path.setFillColor(fillColor); + path.setFillAlpha(fillAlpha); + path.setTrimPathStart(trimPathStart); + path.setTrimPathEnd(trimPathEnd); + path.setTrimPathOffset(trimPathOffset); + path.setStrokeMiterlimit(strokeMiterLimit); + path.setStrokeLineCap(strokeLineCap); + path.setStrokeLineJoin(strokeLineJoin); + } + + @LayoutlibDelegate + static void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr) { + + } + + @LayoutlibDelegate + static void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr) { + + } + + @LayoutlibDelegate + static long nCreateClipPath() { + return sPathManager.addNewDelegate(new VClipPath_Delegate()); + } + + @LayoutlibDelegate + static long nCreateClipPath(long clipPathPtr) { + VClipPath_Delegate original = getDelegate(clipPathPtr); + return sPathManager.addNewDelegate(new VClipPath_Delegate(original)); + } + + @LayoutlibDelegate + static long nCreateGroup() { + return sPathManager.addNewDelegate(new VGroup_Delegate()); + } + + @LayoutlibDelegate + static long nCreateGroup(long groupPtr) { + VGroup_Delegate original = getDelegate(groupPtr); + return sPathManager.addNewDelegate( + new VGroup_Delegate(original, new ArrayMap<String, Object>())); + } + + @LayoutlibDelegate + static void nSetName(long nodePtr, String name) { + VNativeObject group = getDelegate(nodePtr); + group.setName(name); + } + + @LayoutlibDelegate + static boolean nGetGroupProperties(long groupPtr, float[] propertiesData, + int length) { + VGroup_Delegate group = getDelegate(groupPtr); + + FloatBuffer properties = FloatBuffer.wrap(propertiesData); + + properties.put(VGroup_Delegate.ROTATE_INDEX, group.getRotation()); + properties.put(VGroup_Delegate.PIVOT_X_INDEX, group.getPivotX()); + properties.put(VGroup_Delegate.PIVOT_Y_INDEX, group.getPivotY()); + properties.put(VGroup_Delegate.SCALE_X_INDEX, group.getScaleX()); + properties.put(VGroup_Delegate.SCALE_Y_INDEX, group.getScaleY()); + properties.put(VGroup_Delegate.TRANSLATE_X_INDEX, group.getTranslateX()); + properties.put(VGroup_Delegate.TRANSLATE_Y_INDEX, group.getTranslateY()); + + return true; + } + @LayoutlibDelegate + static void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX, + float pivotY, float scaleX, float scaleY, float translateX, float translateY) { + VGroup_Delegate group = getDelegate(groupPtr); + + group.setRotation(rotate); + group.setPivotX(pivotX); + group.setPivotY(pivotY); + group.setScaleX(scaleX); + group.setScaleY(scaleY); + group.setTranslateX(translateX); + group.setTranslateY(translateY); + } + + @LayoutlibDelegate + static void nAddChild(long groupPtr, long nodePtr) { + VGroup_Delegate group = getDelegate(groupPtr); + group.mChildren.add(getDelegate(nodePtr)); + } + + @LayoutlibDelegate + static void nSetPathString(long pathPtr, String pathString, int length) { + VPath_Delegate path = getDelegate(pathPtr); + path.setPathData(PathParser_Delegate.createNodesFromPathData(pathString)); + } + + /** + * The setters and getters below for paths and groups are here temporarily, and will be removed + * once the animation in AVD is replaced with RenderNodeAnimator, in which case the animation + * will modify these properties in native. By then no JNI hopping would be necessary for VD + * during animation, and these setters and getters will be obsolete. + */ + // Setters and getters during animation. + @LayoutlibDelegate + static float nGetRotation(long groupPtr) { + VGroup_Delegate group = getDelegate(groupPtr); + return group.getRotation(); + } + + @LayoutlibDelegate + static void nSetRotation(long groupPtr, float rotation) { + VGroup_Delegate group = getDelegate(groupPtr); + group.setRotation(rotation); + } + + @LayoutlibDelegate + static float nGetPivotX(long groupPtr) { + VGroup_Delegate group = getDelegate(groupPtr); + return group.getPivotX(); + } + + @LayoutlibDelegate + static void nSetPivotX(long groupPtr, float pivotX) { + VGroup_Delegate group = getDelegate(groupPtr); + group.setPivotX(pivotX); + } + + @LayoutlibDelegate + static float nGetPivotY(long groupPtr) { + VGroup_Delegate group = getDelegate(groupPtr); + return group.getPivotY(); + } + + @LayoutlibDelegate + static void nSetPivotY(long groupPtr, float pivotY) { + VGroup_Delegate group = getDelegate(groupPtr); + group.setPivotY(pivotY); + } + + @LayoutlibDelegate + static float nGetScaleX(long groupPtr) { + VGroup_Delegate group = getDelegate(groupPtr); + return group.getScaleX(); + } + + @LayoutlibDelegate + static void nSetScaleX(long groupPtr, float scaleX) { + VGroup_Delegate group = getDelegate(groupPtr); + group.setScaleX(scaleX); + } + + @LayoutlibDelegate + static float nGetScaleY(long groupPtr) { + VGroup_Delegate group = getDelegate(groupPtr); + return group.getScaleY(); + } + + @LayoutlibDelegate + static void nSetScaleY(long groupPtr, float scaleY) { + VGroup_Delegate group = getDelegate(groupPtr); + group.setScaleY(scaleY); + } + + @LayoutlibDelegate + static float nGetTranslateX(long groupPtr) { + VGroup_Delegate group = getDelegate(groupPtr); + return group.getTranslateX(); + } + + @LayoutlibDelegate + static void nSetTranslateX(long groupPtr, float translateX) { + VGroup_Delegate group = getDelegate(groupPtr); + group.setTranslateX(translateX); + } + + @LayoutlibDelegate + static float nGetTranslateY(long groupPtr) { + VGroup_Delegate group = getDelegate(groupPtr); + return group.getTranslateY(); + } + + @LayoutlibDelegate + static void nSetTranslateY(long groupPtr, float translateY) { + VGroup_Delegate group = getDelegate(groupPtr); + group.setTranslateY(translateY); + } + + @LayoutlibDelegate + static void nSetPathData(long pathPtr, long pathDataPtr) { + VPath_Delegate path = getDelegate(pathPtr); + path.setPathData(PathParser_Delegate.getDelegate(pathDataPtr).getPathDataNodes()); + } + + @LayoutlibDelegate + static float nGetStrokeWidth(long pathPtr) { + VFullPath_Delegate path = getDelegate(pathPtr); + return path.getStrokeWidth(); + } + + @LayoutlibDelegate + static void nSetStrokeWidth(long pathPtr, float width) { + VFullPath_Delegate path = getDelegate(pathPtr); + path.setStrokeWidth(width); + } + + @LayoutlibDelegate + static int nGetStrokeColor(long pathPtr) { + VFullPath_Delegate path = getDelegate(pathPtr); + return path.getStrokeColor(); + } + + @LayoutlibDelegate + static void nSetStrokeColor(long pathPtr, int strokeColor) { + VFullPath_Delegate path = getDelegate(pathPtr); + path.setStrokeColor(strokeColor); + } + + @LayoutlibDelegate + static float nGetStrokeAlpha(long pathPtr) { + VFullPath_Delegate path = getDelegate(pathPtr); + return path.getStrokeAlpha(); + } + + @LayoutlibDelegate + static void nSetStrokeAlpha(long pathPtr, float alpha) { + VFullPath_Delegate path = getDelegate(pathPtr); + path.setStrokeAlpha(alpha); + } + + @LayoutlibDelegate + static int nGetFillColor(long pathPtr) { + VFullPath_Delegate path = getDelegate(pathPtr); + return path.getFillColor(); + } + + @LayoutlibDelegate + static void nSetFillColor(long pathPtr, int fillColor) { + VFullPath_Delegate path = getDelegate(pathPtr); + path.setFillColor(fillColor); + } + + @LayoutlibDelegate + static float nGetFillAlpha(long pathPtr) { + VFullPath_Delegate path = getDelegate(pathPtr); + return path.getFillAlpha(); + } + + @LayoutlibDelegate + static void nSetFillAlpha(long pathPtr, float fillAlpha) { + VFullPath_Delegate path = getDelegate(pathPtr); + path.setFillAlpha(fillAlpha); + } + + @LayoutlibDelegate + static float nGetTrimPathStart(long pathPtr) { + VFullPath_Delegate path = getDelegate(pathPtr); + return path.getTrimPathStart(); + } + + @LayoutlibDelegate + static void nSetTrimPathStart(long pathPtr, float trimPathStart) { + VFullPath_Delegate path = getDelegate(pathPtr); + path.setTrimPathStart(trimPathStart); + } + + @LayoutlibDelegate + static float nGetTrimPathEnd(long pathPtr) { + VFullPath_Delegate path = getDelegate(pathPtr); + return path.getTrimPathEnd(); + } + + @LayoutlibDelegate + static void nSetTrimPathEnd(long pathPtr, float trimPathEnd) { + VFullPath_Delegate path = getDelegate(pathPtr); + path.setTrimPathEnd(trimPathEnd); + } + + @LayoutlibDelegate + static float nGetTrimPathOffset(long pathPtr) { + VFullPath_Delegate path = getDelegate(pathPtr); + return path.getTrimPathOffset(); + } + + @LayoutlibDelegate + static void nSetTrimPathOffset(long pathPtr, float trimPathOffset) { + VFullPath_Delegate path = getDelegate(pathPtr); + path.setTrimPathOffset(trimPathOffset); + } + + /** + * Base class for all the internal Delegates that does two functions: + * <ol> + * <li>Serves as base class to store all the delegates in one {@link DelegateManager} + * <li>Provides setName for all the classes. {@link VPathRenderer_Delegate} does actually + * not need it + * </ol> + */ + private interface VNativeObject { + void setName(String name); + } + + private static class VClipPath_Delegate extends VPath_Delegate { + private VClipPath_Delegate() { + // Empty constructor. + } + + private VClipPath_Delegate(VClipPath_Delegate copy) { + super(copy); + } + + public void inflate(Resources r, AttributeSet attrs, Theme theme) { + final TypedArray a = obtainAttributes(r, theme, attrs, + R.styleable.VectorDrawableClipPath); + updateStateFromTypedArray(a); + a.recycle(); + } + + private void updateStateFromTypedArray(TypedArray a) { + // Account for any configuration changes. + mChangingConfigurations |= a.getChangingConfigurations(); + + final String pathName = a.getString(R.styleable.VectorDrawableClipPath_name); + if (pathName != null) { + mPathName = pathName; + } + + final String pathData = a.getString(R.styleable.VectorDrawableClipPath_pathData); + if (pathData != null) { + mNodes = PathParser_Delegate.createNodesFromPathData(pathData); + } + } + + @Override + public boolean isClipPath() { + return true; + } + } + + private static class VFullPath_Delegate extends VPath_Delegate { + // These constants need to be kept in sync with their values in VectorDrawable.VFullPath + private static final int STROKE_WIDTH_INDEX = 0; + private static final int STROKE_COLOR_INDEX = 1; + private static final int STROKE_ALPHA_INDEX = 2; + private static final int FILL_COLOR_INDEX = 3; + private static final int FILL_ALPHA_INDEX = 4; + private static final int TRIM_PATH_START_INDEX = 5; + private static final int TRIM_PATH_END_INDEX = 6; + private static final int TRIM_PATH_OFFSET_INDEX = 7; + private static final int STROKE_LINE_CAP_INDEX = 8; + private static final int STROKE_LINE_JOIN_INDEX = 9; + private static final int STROKE_MITER_LIMIT_INDEX = 10; + + private static final int LINECAP_BUTT = 0; + private static final int LINECAP_ROUND = 1; + private static final int LINECAP_SQUARE = 2; + + private static final int LINEJOIN_MITER = 0; + private static final int LINEJOIN_ROUND = 1; + private static final int LINEJOIN_BEVEL = 2; + + ///////////////////////////////////////////////////// + // Variables below need to be copied (deep copy if applicable) for mutation. + + int mStrokeColor = Color.TRANSPARENT; + float mStrokeWidth = 0; + + int mFillColor = Color.TRANSPARENT; + float mStrokeAlpha = 1.0f; + float mFillAlpha = 1.0f; + float mTrimPathStart = 0; + float mTrimPathEnd = 1; + float mTrimPathOffset = 0; + + Cap mStrokeLineCap = BUTT; + Join mStrokeLineJoin = MITER; + float mStrokeMiterlimit = 4; + + private VFullPath_Delegate() { + // Empty constructor. + } + + private VFullPath_Delegate(VFullPath_Delegate copy) { + super(copy); + + mStrokeColor = copy.mStrokeColor; + mStrokeWidth = copy.mStrokeWidth; + mStrokeAlpha = copy.mStrokeAlpha; + mFillColor = copy.mFillColor; + mFillAlpha = copy.mFillAlpha; + mTrimPathStart = copy.mTrimPathStart; + mTrimPathEnd = copy.mTrimPathEnd; + mTrimPathOffset = copy.mTrimPathOffset; + + mStrokeLineCap = copy.mStrokeLineCap; + mStrokeLineJoin = copy.mStrokeLineJoin; + mStrokeMiterlimit = copy.mStrokeMiterlimit; + } + + private int getStrokeLineCap() { + switch (mStrokeLineCap) { + case BUTT: + return LINECAP_BUTT; + case ROUND: + return LINECAP_ROUND; + case SQUARE: + return LINECAP_SQUARE; + default: + assert false; + } + + return -1; + } + + private void setStrokeLineCap(int cap) { + switch (cap) { + case LINECAP_BUTT: + mStrokeLineCap = BUTT; + break; + case LINECAP_ROUND: + mStrokeLineCap = ROUND; + break; + case LINECAP_SQUARE: + mStrokeLineCap = SQUARE; + break; + default: + assert false; + } + } + + private int getStrokeLineJoin() { + switch (mStrokeLineJoin) { + case MITER: + return LINEJOIN_MITER; + case ROUND: + return LINEJOIN_ROUND; + case BEVEL: + return LINEJOIN_BEVEL; + default: + assert false; + } + + return -1; + } + + private void setStrokeLineJoin(int join) { + switch (join) { + case LINEJOIN_BEVEL: + mStrokeLineJoin = BEVEL; + break; + case LINEJOIN_MITER: + mStrokeLineJoin = MITER; + break; + case LINEJOIN_ROUND: + mStrokeLineJoin = Join.ROUND; + break; + default: + assert false; + } + } + + private int getStrokeColor() { + return mStrokeColor; + } + + private void setStrokeColor(int strokeColor) { + mStrokeColor = strokeColor; + } + + private float getStrokeWidth() { + return mStrokeWidth; + } + + private void setStrokeWidth(float strokeWidth) { + mStrokeWidth = strokeWidth; + } + + private float getStrokeAlpha() { + return mStrokeAlpha; + } + + private void setStrokeAlpha(float strokeAlpha) { + mStrokeAlpha = strokeAlpha; + } + + private int getFillColor() { + return mFillColor; + } + + private void setFillColor(int fillColor) { + mFillColor = fillColor; + } + + private float getFillAlpha() { + return mFillAlpha; + } + + private void setFillAlpha(float fillAlpha) { + mFillAlpha = fillAlpha; + } + + private float getTrimPathStart() { + return mTrimPathStart; + } + + private void setTrimPathStart(float trimPathStart) { + mTrimPathStart = trimPathStart; + } + + private float getTrimPathEnd() { + return mTrimPathEnd; + } + + private void setTrimPathEnd(float trimPathEnd) { + mTrimPathEnd = trimPathEnd; + } + + private float getTrimPathOffset() { + return mTrimPathOffset; + } + + private void setTrimPathOffset(float trimPathOffset) { + mTrimPathOffset = trimPathOffset; + } + + private void setStrokeMiterlimit(float limit) { + mStrokeMiterlimit = limit; + } + + private float getStrokeMiterlimit() { + return mStrokeMiterlimit; + } + } + + private static class VGroup_Delegate implements VNativeObject { + // This constants need to be kept in sync with their definitions in VectorDrawable.Group + private static final int ROTATE_INDEX = 0; + private static final int PIVOT_X_INDEX = 1; + private static final int PIVOT_Y_INDEX = 2; + private static final int SCALE_X_INDEX = 3; + private static final int SCALE_Y_INDEX = 4; + private static final int TRANSLATE_X_INDEX = 5; + private static final int TRANSLATE_Y_INDEX = 6; + + ///////////////////////////////////////////////////// + // Variables below need to be copied (deep copy if applicable) for mutation. + final ArrayList<Object> mChildren = new ArrayList<>(); + // mStackedMatrix is only used temporarily when drawing, it combines all + // the parents' local matrices with the current one. + private final Matrix mStackedMatrix = new Matrix(); + // mLocalMatrix is updated based on the update of transformation information, + // either parsed from the XML or by animation. + private final Matrix mLocalMatrix = new Matrix(); + private float mRotate = 0; + private float mPivotX = 0; + private float mPivotY = 0; + private float mScaleX = 1; + private float mScaleY = 1; + private float mTranslateX = 0; + private float mTranslateY = 0; + private int mChangingConfigurations; + private String mGroupName = null; + + private VGroup_Delegate(VGroup_Delegate copy, ArrayMap<String, Object> targetsMap) { + mRotate = copy.mRotate; + mPivotX = copy.mPivotX; + mPivotY = copy.mPivotY; + mScaleX = copy.mScaleX; + mScaleY = copy.mScaleY; + mTranslateX = copy.mTranslateX; + mTranslateY = copy.mTranslateY; + mGroupName = copy.mGroupName; + mChangingConfigurations = copy.mChangingConfigurations; + if (mGroupName != null) { + targetsMap.put(mGroupName, this); + } + + mLocalMatrix.set(copy.mLocalMatrix); + + final ArrayList<Object> children = copy.mChildren; + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < children.size(); i++) { + Object copyChild = children.get(i); + if (copyChild instanceof VGroup_Delegate) { + VGroup_Delegate copyGroup = (VGroup_Delegate) copyChild; + mChildren.add(new VGroup_Delegate(copyGroup, targetsMap)); + } else { + VPath_Delegate newPath; + if (copyChild instanceof VFullPath_Delegate) { + newPath = new VFullPath_Delegate((VFullPath_Delegate) copyChild); + } else if (copyChild instanceof VClipPath_Delegate) { + newPath = new VClipPath_Delegate((VClipPath_Delegate) copyChild); + } else { + throw new IllegalStateException("Unknown object in the tree!"); + } + mChildren.add(newPath); + if (newPath.mPathName != null) { + targetsMap.put(newPath.mPathName, newPath); + } + } + } + } + + private VGroup_Delegate() { + } + + private void updateLocalMatrix() { + // The order we apply is the same as the + // RenderNode.cpp::applyViewPropertyTransforms(). + mLocalMatrix.reset(); + mLocalMatrix.postTranslate(-mPivotX, -mPivotY); + mLocalMatrix.postScale(mScaleX, mScaleY); + mLocalMatrix.postRotate(mRotate, 0, 0); + mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY); + } + + /* Setters and Getters, used by animator from AnimatedVectorDrawable. */ + private float getRotation() { + return mRotate; + } + + private void setRotation(float rotation) { + if (rotation != mRotate) { + mRotate = rotation; + updateLocalMatrix(); + } + } + + private float getPivotX() { + return mPivotX; + } + + private void setPivotX(float pivotX) { + if (pivotX != mPivotX) { + mPivotX = pivotX; + updateLocalMatrix(); + } + } + + private float getPivotY() { + return mPivotY; + } + + private void setPivotY(float pivotY) { + if (pivotY != mPivotY) { + mPivotY = pivotY; + updateLocalMatrix(); + } + } + + private float getScaleX() { + return mScaleX; + } + + private void setScaleX(float scaleX) { + if (scaleX != mScaleX) { + mScaleX = scaleX; + updateLocalMatrix(); + } + } + + private float getScaleY() { + return mScaleY; + } + + private void setScaleY(float scaleY) { + if (scaleY != mScaleY) { + mScaleY = scaleY; + updateLocalMatrix(); + } + } + + private float getTranslateX() { + return mTranslateX; + } + + private void setTranslateX(float translateX) { + if (translateX != mTranslateX) { + mTranslateX = translateX; + updateLocalMatrix(); + } + } + + private float getTranslateY() { + return mTranslateY; + } + + private void setTranslateY(float translateY) { + if (translateY != mTranslateY) { + mTranslateY = translateY; + updateLocalMatrix(); + } + } + + @Override + public void setName(String name) { + mGroupName = name; + } + } + + public static class VPath_Delegate implements VNativeObject { + protected PathParser_Delegate.PathDataNode[] mNodes = null; + String mPathName; + int mChangingConfigurations; + + public VPath_Delegate() { + // Empty constructor. + } + + public VPath_Delegate(VPath_Delegate copy) { + mPathName = copy.mPathName; + mChangingConfigurations = copy.mChangingConfigurations; + mNodes = PathParser_Delegate.deepCopyNodes(copy.mNodes); + } + + public void toPath(Path path) { + path.reset(); + if (mNodes != null) { + PathParser_Delegate.PathDataNode.nodesToPath(mNodes, + Path_Delegate.getDelegate(path.mNativePath)); + } + } + + @Override + public void setName(String name) { + mPathName = name; + } + + public boolean isClipPath() { + return false; + } + + private void setPathData(PathParser_Delegate.PathDataNode[] nodes) { + if (!PathParser_Delegate.canMorph(mNodes, nodes)) { + // This should not happen in the middle of animation. + mNodes = PathParser_Delegate.deepCopyNodes(nodes); + } else { + PathParser_Delegate.updateNodes(mNodes, nodes); + } + } + } + + private static class VPathRenderer_Delegate implements VNativeObject { + /* Right now the internal data structure is organized as a tree. + * Each node can be a group node, or a path. + * A group node can have groups or paths as children, but a path node has + * no children. + * One example can be: + * Root Group + * / | \ + * Group Path Group + * / \ | + * Path Path Path + * + */ + // Variables that only used temporarily inside the draw() call, so there + // is no need for deep copying. + private final Path mPath; + private final Path mRenderPath; + private final Matrix mFinalPathMatrix = new Matrix(); + private final VGroup_Delegate mRootGroup; + private float mViewportWidth = 0; + private float mViewportHeight = 0; + private float mRootAlpha = 1.0f; + private Paint mStrokePaint; + private Paint mFillPaint; + private PathMeasure mPathMeasure; + + private VPathRenderer_Delegate(VGroup_Delegate rootGroup) { + mRootGroup = rootGroup; + mPath = new Path(); + mRenderPath = new Path(); + } + + private float getRootAlpha() { + return mRootAlpha; + } + + private void setRootAlpha(float alpha) { + mRootAlpha = alpha; + } + + private void drawGroupTree(VGroup_Delegate currentGroup, Matrix currentMatrix, + long canvasPtr, int w, int h, long filterPtr) { + // Calculate current group's matrix by preConcat the parent's and + // and the current one on the top of the stack. + // Basically the Mfinal = Mviewport * M0 * M1 * M2; + // Mi the local matrix at level i of the group tree. + currentGroup.mStackedMatrix.set(currentMatrix); + currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix); + + // Save the current clip information, which is local to this group. + Canvas_Delegate.native_save(canvasPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG); + // Draw the group tree in the same order as the XML file. + for (int i = 0; i < currentGroup.mChildren.size(); i++) { + Object child = currentGroup.mChildren.get(i); + if (child instanceof VGroup_Delegate) { + VGroup_Delegate childGroup = (VGroup_Delegate) child; + drawGroupTree(childGroup, currentGroup.mStackedMatrix, + canvasPtr, w, h, filterPtr); + } else if (child instanceof VPath_Delegate) { + VPath_Delegate childPath = (VPath_Delegate) child; + drawPath(currentGroup, childPath, canvasPtr, w, h, filterPtr); + } + } + Canvas_Delegate.native_restore(canvasPtr, true); + } + + public void draw(long canvasPtr, long filterPtr, int w, int h) { + // Traverse the tree in pre-order to draw. + drawGroupTree(mRootGroup, Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr); + } + + private void drawPath(VGroup_Delegate VGroup, VPath_Delegate VPath, long canvasPtr, + int w, + int h, + long filterPtr) { + final float scaleX = w / mViewportWidth; + final float scaleY = h / mViewportHeight; + final float minScale = Math.min(scaleX, scaleY); + final Matrix groupStackedMatrix = VGroup.mStackedMatrix; + + mFinalPathMatrix.set(groupStackedMatrix); + mFinalPathMatrix.postScale(scaleX, scaleY); + + final float matrixScale = getMatrixScale(groupStackedMatrix); + if (matrixScale == 0) { + // When either x or y is scaled to 0, we don't need to draw anything. + return; + } + VPath.toPath(mPath); + final Path path = mPath; + + mRenderPath.reset(); + + if (VPath.isClipPath()) { + mRenderPath.addPath(path, mFinalPathMatrix); + Canvas_Delegate.native_clipPath(canvasPtr, mRenderPath.mNativePath, Op + .INTERSECT.nativeInt); + } else { + VFullPath_Delegate fullPath = (VFullPath_Delegate) VPath; + if (fullPath.mTrimPathStart != 0.0f || fullPath.mTrimPathEnd != 1.0f) { + float start = (fullPath.mTrimPathStart + fullPath.mTrimPathOffset) % 1.0f; + float end = (fullPath.mTrimPathEnd + fullPath.mTrimPathOffset) % 1.0f; + + if (mPathMeasure == null) { + mPathMeasure = new PathMeasure(); + } + mPathMeasure.setPath(mPath, false); + + float len = mPathMeasure.getLength(); + start = start * len; + end = end * len; + path.reset(); + if (start > end) { + mPathMeasure.getSegment(start, len, path, true); + mPathMeasure.getSegment(0f, end, path, true); + } else { + mPathMeasure.getSegment(start, end, path, true); + } + path.rLineTo(0, 0); // fix bug in measure + } + mRenderPath.addPath(path, mFinalPathMatrix); + + if (fullPath.mFillColor != Color.TRANSPARENT) { + if (mFillPaint == null) { + mFillPaint = new Paint(); + mFillPaint.setStyle(Style.FILL); + mFillPaint.setAntiAlias(true); + } + + final Paint fillPaint = mFillPaint; + fillPaint.setColor(applyAlpha(fullPath.mFillColor, fullPath.mFillAlpha)); + Paint_Delegate fillPaintDelegate = Paint_Delegate.getDelegate(fillPaint + .getNativeInstance + ()); + // mFillPaint can not be null at this point so we will have a delegate + assert fillPaintDelegate != null; + fillPaintDelegate.setColorFilter(filterPtr); + Canvas_Delegate.native_drawPath(canvasPtr, mRenderPath.mNativePath, fillPaint + .getNativeInstance()); + } + + if (fullPath.mStrokeColor != Color.TRANSPARENT) { + if (mStrokePaint == null) { + mStrokePaint = new Paint(); + mStrokePaint.setStyle(Style.STROKE); + mStrokePaint.setAntiAlias(true); + } + + final Paint strokePaint = mStrokePaint; + if (fullPath.mStrokeLineJoin != null) { + strokePaint.setStrokeJoin(fullPath.mStrokeLineJoin); + } + + if (fullPath.mStrokeLineCap != null) { + strokePaint.setStrokeCap(fullPath.mStrokeLineCap); + } + + strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit); + strokePaint.setColor(applyAlpha(fullPath.mStrokeColor, fullPath.mStrokeAlpha)); + Paint_Delegate strokePaintDelegate = Paint_Delegate.getDelegate(strokePaint + .getNativeInstance()); + // mStrokePaint can not be null at this point so we will have a delegate + assert strokePaintDelegate != null; + strokePaintDelegate.setColorFilter(filterPtr); + final float finalStrokeScale = minScale * matrixScale; + strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale); + Canvas_Delegate.native_drawPath(canvasPtr, mRenderPath.mNativePath, strokePaint + .getNativeInstance()); + } + } + } + + private float getMatrixScale(Matrix groupStackedMatrix) { + // Given unit vectors A = (0, 1) and B = (1, 0). + // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'. + // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)), + // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|); + // If max (|A'|, |B'|) = 0, that means either x or y has a scale of 0. + // + // For non-skew case, which is most of the cases, matrix scale is computing exactly the + // scale on x and y axis, and take the minimal of these two. + // For skew case, an unit square will mapped to a parallelogram. And this function will + // return the minimal height of the 2 bases. + float[] unitVectors = new float[]{0, 1, 1, 0}; + groupStackedMatrix.mapVectors(unitVectors); + float scaleX = MathUtils.mag(unitVectors[0], unitVectors[1]); + float scaleY = MathUtils.mag(unitVectors[2], unitVectors[3]); + float crossProduct = MathUtils.cross(unitVectors[0], unitVectors[1], + unitVectors[2], unitVectors[3]); + float maxScale = MathUtils.max(scaleX, scaleY); + + float matrixScale = 0; + if (maxScale > 0) { + matrixScale = MathUtils.abs(crossProduct) / maxScale; + } + if (DBG_VECTOR_DRAWABLE) { + Log.d(LOGTAG, "Scale x " + scaleX + " y " + scaleY + " final " + matrixScale); + } + return matrixScale; + } + + @Override + public void setName(String name) { + } + } +} diff --git a/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java b/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java index d3af837ffd29..6c34c70b7609 100644 --- a/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java +++ b/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java @@ -24,7 +24,6 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.annotation.NonNull; import android.graphics.Path_Delegate; -import java.awt.geom.Path2D; import java.util.ArrayList; import java.util.Arrays; import java.util.logging.Level; @@ -52,10 +51,18 @@ public class PathParser_Delegate { @NonNull private PathDataNode[] mPathDataNodes; + public static PathParser_Delegate getDelegate(long nativePtr) { + return sManager.getDelegate(nativePtr); + } + private PathParser_Delegate(@NonNull PathDataNode[] nodes) { mPathDataNodes = nodes; } + public PathDataNode[] getPathDataNodes() { + return mPathDataNodes; + } + @LayoutlibDelegate /*package*/ static boolean nParseStringForPath(long pathPtr, @NonNull String pathString, int stringLength) { @@ -64,7 +71,7 @@ public class PathParser_Delegate { return false; } assert pathString.length() == stringLength; - PathDataNode.nodesToPath(createNodesFromPathData(pathString), path_delegate.getJavaShape()); + PathDataNode.nodesToPath(createNodesFromPathData(pathString), path_delegate); return true; } @@ -75,7 +82,7 @@ public class PathParser_Delegate { if (source == null || path_delegate == null) { return; } - PathDataNode.nodesToPath(source.mPathDataNodes, path_delegate.getJavaShape()); + PathDataNode.nodesToPath(source.mPathDataNodes, path_delegate); } @LayoutlibDelegate @@ -124,8 +131,11 @@ public class PathParser_Delegate { out.mPathDataNodes = new PathDataNode[length]; } for (int i = 0; i < length; i++) { + if (out.mPathDataNodes[i] == null) { + out.mPathDataNodes[i] = new PathDataNode(from.mPathDataNodes[i]); + } out.mPathDataNodes[i].interpolatePathDataNode(from.mPathDataNodes[i], - to.mPathDataNodes[i], fraction); + to.mPathDataNodes[i], fraction); } return true; } @@ -137,9 +147,13 @@ public class PathParser_Delegate { @LayoutlibDelegate /*package*/ static boolean nCanMorph(long fromDataPtr, long toDataPtr) { - Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "morphing path data isn't " + - "supported", null, null); - return false; + PathParser_Delegate fromPath = PathParser_Delegate.getDelegate(fromDataPtr); + PathParser_Delegate toPath = PathParser_Delegate.getDelegate(toDataPtr); + if (fromPath == null || toPath == null || fromPath.getPathDataNodes() == null || toPath + .getPathDataNodes() == null) { + return true; + } + return PathParser_Delegate.canMorph(fromPath.getPathDataNodes(), toPath.getPathDataNodes()); } @LayoutlibDelegate @@ -158,7 +172,7 @@ public class PathParser_Delegate { * @return an array of the PathDataNode. */ @NonNull - private static PathDataNode[] createNodesFromPathData(@NonNull String pathData) { + public static PathDataNode[] createNodesFromPathData(@NonNull String pathData) { int start = 0; int end = 1; @@ -186,7 +200,7 @@ public class PathParser_Delegate { * @return a deep copy of the <code>source</code>. */ @NonNull - private static PathDataNode[] deepCopyNodes(@NonNull PathDataNode[] source) { + public static PathDataNode[] deepCopyNodes(@NonNull PathDataNode[] source) { PathDataNode[] copy = new PathDataNode[source.length]; for (int i = 0; i < source.length; i++) { copy[i] = new PathDataNode(source[i]); @@ -194,6 +208,45 @@ public class PathParser_Delegate { return copy; } + /** + * @param nodesFrom The source path represented in an array of PathDataNode + * @param nodesTo The target path represented in an array of PathDataNode + * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code> + */ + public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) { + if (nodesFrom == null || nodesTo == null) { + return false; + } + + if (nodesFrom.length != nodesTo.length) { + return false; + } + + for (int i = 0; i < nodesFrom.length; i ++) { + if (nodesFrom[i].mType != nodesTo[i].mType + || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) { + return false; + } + } + return true; + } + + /** + * Update the target's data to match the source. + * Before calling this, make sure canMorph(target, source) is true. + * + * @param target The target path represented in an array of PathDataNode + * @param source The source path represented in an array of PathDataNode + */ + public static void updateNodes(PathDataNode[] target, PathDataNode[] source) { + for (int i = 0; i < source.length; i ++) { + target[i].mType = source[i].mType; + for (int j = 0; j < source[i].mParams.length; j ++) { + target[i].mParams[j] = source[i].mParams[j]; + } + } + } + private static int nextStart(@NonNull String s, int end) { char c; @@ -330,7 +383,7 @@ public class PathParser_Delegate { * Each PathDataNode represents one command in the "d" attribute of the svg file. An array of * PathDataNode can represent the whole "d" attribute. */ - private static class PathDataNode { + public static class PathDataNode { private char mType; @NonNull private float[] mParams; @@ -355,12 +408,13 @@ public class PathParser_Delegate { } /** - * Convert an array of PathDataNode to Path. + * Convert an array of PathDataNode to Path. Reset the passed path as needed before + * calling this method. * * @param node The source array of PathDataNode. * @param path The target Path object. */ - private static void nodesToPath(@NonNull PathDataNode[] node, @NonNull Path2D path) { + public static void nodesToPath(@NonNull PathDataNode[] node, @NonNull Path_Delegate path) { float[] current = new float[6]; char previousCommand = 'm'; //noinspection ForLoopReplaceableByForEach @@ -387,24 +441,32 @@ public class PathParser_Delegate { } @SuppressWarnings("PointlessArithmeticExpression") - private static void addCommand(@NonNull Path2D path, float[] current, char cmd, - char lastCmd, @NonNull float[] val) { + private static void addCommand(@NonNull Path_Delegate path, float[] current, + char previousCmd, char cmd, @NonNull float[] val) { int incr = 2; - - float cx = current[0]; - float cy = current[1]; - float cpx = current[2]; - float cpy = current[3]; - float loopX = current[4]; - float loopY = current[5]; + float currentX = current[0]; + float currentY = current[1]; + float ctrlPointX = current[2]; + float ctrlPointY = current[3]; + float currentSegmentStartX = current[4]; + float currentSegmentStartY = current[5]; + float reflectiveCtrlPointX; + float reflectiveCtrlPointY; switch (cmd) { case 'z': case 'Z': - path.closePath(); - cx = loopX; - cy = loopY; + path.close(); + // Path is closed here, but we need to move the pen to the + // closed position. So we cache the segment's starting position, + // and restore it here. + currentX = currentSegmentStartX; + currentY = currentSegmentStartY; + ctrlPointX = currentSegmentStartX; + ctrlPointY = currentSegmentStartY; + path.moveTo(currentX, currentY); + break; case 'm': case 'M': case 'l': @@ -432,185 +494,206 @@ public class PathParser_Delegate { case 'a': case 'A': incr = 7; + break; } for (int k = 0; k < val.length; k += incr) { - boolean reflectCtrl; - float tempReflectedX, tempReflectedY; - switch (cmd) { - case 'm': - cx += val[k + 0]; - cy += val[k + 1]; + case 'm': // moveto - Start a new sub-path (relative) + currentX += val[k + 0]; + currentY += val[k + 1]; + if (k > 0) { // According to the spec, if a moveto is followed by multiple // pairs of coordinates, the subsequent pairs are treated as // implicit lineto commands. - path.lineTo(cx, cy); + path.rLineTo(val[k + 0], val[k + 1]); } else { - path.moveTo(cx, cy); - loopX = cx; - loopY = cy; + path.rMoveTo(val[k + 0], val[k + 1]); + currentSegmentStartX = currentX; + currentSegmentStartY = currentY; } break; - case 'M': - cx = val[k + 0]; - cy = val[k + 1]; + case 'M': // moveto - Start a new sub-path + currentX = val[k + 0]; + currentY = val[k + 1]; + if (k > 0) { // According to the spec, if a moveto is followed by multiple // pairs of coordinates, the subsequent pairs are treated as // implicit lineto commands. - path.lineTo(cx, cy); + path.lineTo(val[k + 0], val[k + 1]); } else { - path.moveTo(cx, cy); - loopX = cx; - loopY = cy; + path.moveTo(val[k + 0], val[k + 1]); + currentSegmentStartX = currentX; + currentSegmentStartY = currentY; } break; - case 'l': - cx += val[k + 0]; - cy += val[k + 1]; - path.lineTo(cx, cy); - break; - case 'L': - cx = val[k + 0]; - cy = val[k + 1]; - path.lineTo(cx, cy); + case 'l': // lineto - Draw a line from the current point (relative) + path.rLineTo(val[k + 0], val[k + 1]); + currentX += val[k + 0]; + currentY += val[k + 1]; break; - case 'z': - case 'Z': - path.closePath(); - cx = loopX; - cy = loopY; + case 'L': // lineto - Draw a line from the current point + path.lineTo(val[k + 0], val[k + 1]); + currentX = val[k + 0]; + currentY = val[k + 1]; break; - case 'h': - cx += val[k + 0]; - path.lineTo(cx, cy); + case 'h': // horizontal lineto - Draws a horizontal line (relative) + path.rLineTo(val[k + 0], 0); + currentX += val[k + 0]; break; - case 'H': - path.lineTo(val[k + 0], cy); - cx = val[k + 0]; + case 'H': // horizontal lineto - Draws a horizontal line + path.lineTo(val[k + 0], currentY); + currentX = val[k + 0]; break; - case 'v': - cy += val[k + 0]; - path.lineTo(cx, cy); + case 'v': // vertical lineto - Draws a vertical line from the current point (r) + path.rLineTo(0, val[k + 0]); + currentY += val[k + 0]; break; - case 'V': - path.lineTo(cx, val[k + 0]); - cy = val[k + 0]; + case 'V': // vertical lineto - Draws a vertical line from the current point + path.lineTo(currentX, val[k + 0]); + currentY = val[k + 0]; break; - case 'c': - path.curveTo(cx + val[k + 0], cy + val[k + 1], cx + val[k + 2], - cy + val[k + 3], cx + val[k + 4], cy + val[k + 5]); - cpx = cx + val[k + 2]; - cpy = cy + val[k + 3]; - cx += val[k + 4]; - cy += val[k + 5]; + case 'c': // curveto - Draws a cubic Bézier curve (relative) + path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], + val[k + 4], val[k + 5]); + + ctrlPointX = currentX + val[k + 2]; + ctrlPointY = currentY + val[k + 3]; + currentX += val[k + 4]; + currentY += val[k + 5]; + break; - case 'C': - path.curveTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], + case 'C': // curveto - Draws a cubic Bézier curve + path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], val[k + 4], val[k + 5]); - cx = val[k + 4]; - cy = val[k + 5]; - cpx = val[k + 2]; - cpy = val[k + 3]; + currentX = val[k + 4]; + currentY = val[k + 5]; + ctrlPointX = val[k + 2]; + ctrlPointY = val[k + 3]; break; - case 's': - reflectCtrl = (lastCmd == 'c' || lastCmd == 's' || lastCmd == 'C' || - lastCmd == 'S'); - path.curveTo(reflectCtrl ? 2 * cx - cpx : cx, reflectCtrl ? 2 - * cy - cpy : cy, cx + val[k + 0], cy + val[k + 1], cx - + val[k + 2], cy + val[k + 3]); - - cpx = cx + val[k + 0]; - cpy = cy + val[k + 1]; - cx += val[k + 2]; - cy += val[k + 3]; + case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp) + reflectiveCtrlPointX = 0; + reflectiveCtrlPointY = 0; + if (previousCmd == 'c' || previousCmd == 's' + || previousCmd == 'C' || previousCmd == 'S') { + reflectiveCtrlPointX = currentX - ctrlPointX; + reflectiveCtrlPointY = currentY - ctrlPointY; + } + path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, + val[k + 0], val[k + 1], + val[k + 2], val[k + 3]); + + ctrlPointX = currentX + val[k + 0]; + ctrlPointY = currentY + val[k + 1]; + currentX += val[k + 2]; + currentY += val[k + 3]; break; - case 'S': - reflectCtrl = (lastCmd == 'c' || lastCmd == 's' || lastCmd == 'C' || - lastCmd == 'S'); - path.curveTo(reflectCtrl ? 2 * cx - cpx : cx, reflectCtrl ? 2 - * cy - cpy : cy, val[k + 0], val[k + 1], val[k + 2], - val[k + 3]); - cpx = (val[k + 0]); - cpy = (val[k + 1]); - cx = val[k + 2]; - cy = val[k + 3]; + case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp) + reflectiveCtrlPointX = currentX; + reflectiveCtrlPointY = currentY; + if (previousCmd == 'c' || previousCmd == 's' + || previousCmd == 'C' || previousCmd == 'S') { + reflectiveCtrlPointX = 2 * currentX - ctrlPointX; + reflectiveCtrlPointY = 2 * currentY - ctrlPointY; + } + path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, + val[k + 0], val[k + 1], val[k + 2], val[k + 3]); + ctrlPointX = val[k + 0]; + ctrlPointY = val[k + 1]; + currentX = val[k + 2]; + currentY = val[k + 3]; break; - case 'q': - path.quadTo(cx + val[k + 0], cy + val[k + 1], cx + val[k + 2], - cy + val[k + 3]); - cpx = cx + val[k + 0]; - cpy = cy + val[k + 1]; - // Note that we have to update cpx first, since cx will be updated here. - cx += val[k + 2]; - cy += val[k + 3]; + case 'q': // Draws a quadratic Bézier (relative) + path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); + ctrlPointX = currentX + val[k + 0]; + ctrlPointY = currentY + val[k + 1]; + currentX += val[k + 2]; + currentY += val[k + 3]; break; - case 'Q': + case 'Q': // Draws a quadratic Bézier path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); - cx = val[k + 2]; - cy = val[k + 3]; - cpx = val[k + 0]; - cpy = val[k + 1]; + ctrlPointX = val[k + 0]; + ctrlPointY = val[k + 1]; + currentX = val[k + 2]; + currentY = val[k + 3]; break; - case 't': - reflectCtrl = (lastCmd == 'q' || lastCmd == 't' || lastCmd == 'Q' || - lastCmd == 'T'); - tempReflectedX = reflectCtrl ? 2 * cx - cpx : cx; - tempReflectedY = reflectCtrl ? 2 * cy - cpy : cy; - path.quadTo(tempReflectedX, tempReflectedY, cx + val[k + 0], - cy + val[k + 1]); - cpx = tempReflectedX; - cpy = tempReflectedY; - cx += val[k + 0]; - cy += val[k + 1]; + case 't': // Draws a quadratic Bézier curve(reflective control point)(relative) + reflectiveCtrlPointX = 0; + reflectiveCtrlPointY = 0; + if (previousCmd == 'q' || previousCmd == 't' + || previousCmd == 'Q' || previousCmd == 'T') { + reflectiveCtrlPointX = currentX - ctrlPointX; + reflectiveCtrlPointY = currentY - ctrlPointY; + } + path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, + val[k + 0], val[k + 1]); + ctrlPointX = currentX + reflectiveCtrlPointX; + ctrlPointY = currentY + reflectiveCtrlPointY; + currentX += val[k + 0]; + currentY += val[k + 1]; break; - case 'T': - reflectCtrl = (lastCmd == 'q' || lastCmd == 't' || lastCmd == 'Q' || - lastCmd == 'T'); - tempReflectedX = reflectCtrl ? 2 * cx - cpx : cx; - tempReflectedY = reflectCtrl ? 2 * cy - cpy : cy; - path.quadTo(tempReflectedX, tempReflectedY, val[k + 0], val[k + 1]); - cx = val[k + 0]; - cy = val[k + 1]; - cpx = tempReflectedX; - cpy = tempReflectedY; + case 'T': // Draws a quadratic Bézier curve (reflective control point) + reflectiveCtrlPointX = currentX; + reflectiveCtrlPointY = currentY; + if (previousCmd == 'q' || previousCmd == 't' + || previousCmd == 'Q' || previousCmd == 'T') { + reflectiveCtrlPointX = 2 * currentX - ctrlPointX; + reflectiveCtrlPointY = 2 * currentY - ctrlPointY; + } + path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, + val[k + 0], val[k + 1]); + ctrlPointX = reflectiveCtrlPointX; + ctrlPointY = reflectiveCtrlPointY; + currentX = val[k + 0]; + currentY = val[k + 1]; break; - case 'a': + case 'a': // Draws an elliptical arc // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) - drawArc(path, cx, cy, val[k + 5] + cx, val[k + 6] + cy, - val[k + 0], val[k + 1], val[k + 2], val[k + 3] != 0, + drawArc(path, + currentX, + currentY, + val[k + 5] + currentX, + val[k + 6] + currentY, + val[k + 0], + val[k + 1], + val[k + 2], + val[k + 3] != 0, val[k + 4] != 0); - cx += val[k + 5]; - cy += val[k + 6]; - cpx = cx; - cpy = cy; - + currentX += val[k + 5]; + currentY += val[k + 6]; + ctrlPointX = currentX; + ctrlPointY = currentY; break; - case 'A': - drawArc(path, cx, cy, val[k + 5], val[k + 6], val[k + 0], - val[k + 1], val[k + 2], val[k + 3] != 0, + case 'A': // Draws an elliptical arc + drawArc(path, + currentX, + currentY, + val[k + 5], + val[k + 6], + val[k + 0], + val[k + 1], + val[k + 2], + val[k + 3] != 0, val[k + 4] != 0); - cx = val[k + 5]; - cy = val[k + 6]; - cpx = cx; - cpy = cy; + currentX = val[k + 5]; + currentY = val[k + 6]; + ctrlPointX = currentX; + ctrlPointY = currentY; break; - } - lastCmd = cmd; + previousCmd = cmd; } - current[0] = cx; - current[1] = cy; - current[2] = cpx; - current[3] = cpy; - current[4] = loopX; - current[5] = loopY; - + current[0] = currentX; + current[1] = currentY; + current[2] = ctrlPointX; + current[3] = ctrlPointY; + current[4] = currentSegmentStartX; + current[5] = currentSegmentStartY; } - private static void drawArc(@NonNull Path2D p, float x0, float y0, float x1, + private static void drawArc(@NonNull Path_Delegate p, float x0, float y0, float x1, float y1, float a, float b, float theta, boolean isMoreThanHalf, boolean isPositiveArc) { @@ -707,7 +790,7 @@ public class PathParser_Delegate { * @param start The start angle of the arc on the ellipse * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse */ - private static void arcToBezier(@NonNull Path2D p, double cx, double cy, double a, + private static void arcToBezier(@NonNull Path_Delegate p, double cx, double cy, double a, double b, double e1x, double e1y, double theta, double start, double sweep) { // Taken from equations at: @@ -744,8 +827,12 @@ public class PathParser_Delegate { double q2x = e2x - alpha * ep2x; double q2y = e2y - alpha * ep2y; - p.curveTo((float) q1x, (float) q1y, (float) q2x, (float) q2y, - (float) e2x, (float) e2y); + p.cubicTo((float) q1x, + (float) q1y, + (float) q2x, + (float) q2y, + (float) e2x, + (float) e2y); eta1 = eta2; e1x = e2x; e1y = e2y; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 99af226e4c4c..53f1912a833b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -60,6 +60,7 @@ import android.app.Fragment_Delegate; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; import android.graphics.Canvas; +import android.os.Looper; import android.preference.Preference_Delegate; import android.view.AttachInfo_Accessor; import android.view.BridgeInflater; @@ -1398,6 +1399,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } public void dispose() { + boolean createdLooper = false; + if (Looper.myLooper() == null) { + // Detaching the root view from the window will try to stop any running animations. + // The stop method checks that it can run in the looper so, if there is no current + // looper, we create a temporary one to complete the shutdown. + Bridge.prepareThread(); + createdLooper = true; + } AttachInfo_Accessor.detachFromWindow(mViewRoot); if (mCanvas != null) { mCanvas.release(); @@ -1412,5 +1421,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { mImage = null; mViewRoot = null; mContentRoot = null; + + if (createdLooper) { + Bridge.cleanupThread(); + } } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 8a23e4b9d3da..8c3bd2f40b53 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -269,6 +269,7 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.SweepGradient", "android.graphics.Typeface", "android.graphics.Xfermode", + "android.graphics.drawable.VectorDrawable", "android.os.SystemClock", "android.os.SystemProperties", "android.text.AndroidBidi", @@ -321,7 +322,12 @@ public final class CreateInfo implements ICreateInfo { "org.kxml2.io.KXmlParser" }; + /** + * List of fields for which we will update the visibility to be public. This is sometimes + * needed when access from the delegate classes is needed. + */ private final static String[] PROMOTED_FIELDS = new String[] { + "android.graphics.drawable.VectorDrawable#mVectorState", "android.view.Choreographer#mLastFrameTimeNanos" }; |