diff options
12 files changed, 907 insertions, 577 deletions
diff --git a/tests/HwAccelerationTest/res/layout/_layers.xml b/tests/HwAccelerationTest/res/layout/_layers.xml new file mode 100644 index 0000000..c2b186d --- /dev/null +++ b/tests/HwAccelerationTest/res/layout/_layers.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.android.test.hwui.LayersActivity.LayersView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> +</com.android.test.hwui.LayersActivity.LayersView> diff --git a/tests/HwAccelerationTest/res/layout/_newlayers.xml b/tests/HwAccelerationTest/res/layout/_newlayers.xml new file mode 100644 index 0000000..062a2e1 --- /dev/null +++ b/tests/HwAccelerationTest/res/layout/_newlayers.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.android.test.hwui.NewLayersActivity.LayersView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> +</com.android.test.hwui.NewLayersActivity.LayersView> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java index b705117..9d5cd28 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java @@ -33,11 +33,11 @@ public class LayersActivity extends Activity { setContentView(new LayersView(this)); } - static class LayersView extends View { + public static class LayersView extends View { private Paint mLayerPaint; private final Paint mRectPaint; - LayersView(Context c) { + public LayersView(Context c) { super(c); mLayerPaint = new Paint(); @@ -47,11 +47,11 @@ public class LayersActivity extends Activity { @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - + canvas.translate(140.0f, 100.0f); //canvas.drawRGB(255, 255, 255); - + int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint, Canvas.ALL_SAVE_FLAG); @@ -59,9 +59,9 @@ public class LayersActivity extends Activity { canvas.drawRect(0.0f, 0.0f, 200.0f, 100.0f, mRectPaint); canvas.restoreToCount(count); - + canvas.translate(0.0f, 125.0f); - + count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint, Canvas.ALL_SAVE_FLAG); @@ -75,8 +75,8 @@ public class LayersActivity extends Activity { mRectPaint.setColor(0xff0000ff); mRectPaint.setAlpha(255); - canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint); - + canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint); + mLayerPaint.setAlpha(127); mLayerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)); count = canvas.saveLayer(50.0f, 25.0f, 150.0f, 75.0f, mLayerPaint, @@ -86,13 +86,13 @@ public class LayersActivity extends Activity { canvas.drawRect(50.0f, 25.0f, 150.0f, 75.0f, mRectPaint); canvas.restoreToCount(count); - + canvas.translate(0.0f, 125.0f); mRectPaint.setColor(0xff0000ff); mRectPaint.setAlpha(255); - canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint); - + canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint); + mLayerPaint.setColor(0xffff0000); mLayerPaint.setAlpha(127); mLayerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)); @@ -103,7 +103,7 @@ public class LayersActivity extends Activity { canvas.drawRect(50.0f, 25.0f, 150.0f, 75.0f, mRectPaint); canvas.restoreToCount(count); - + mLayerPaint = new Paint(); } } diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java index d9a2893..2509d367 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java @@ -31,11 +31,11 @@ public class NewLayersActivity extends Activity { setContentView(new LayersView(this)); } - static class LayersView extends View { + public static class LayersView extends View { private Paint mLayerPaint; private final Paint mRectPaint; - LayersView(Context c) { + public LayersView(Context c) { super(c); mLayerPaint = new Paint(); @@ -57,7 +57,7 @@ public class NewLayersActivity extends Activity { canvas.translate(0.0f, 200.0f); drawStuff(canvas, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG); - + canvas.restore(); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 2720b61..df0b997 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -23,14 +23,12 @@ import com.android.layoutlib.bridge.impl.GcSnapshot; import android.graphics.Paint_Delegate.FontInfo; import android.text.TextUtils; -import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; -import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.List; @@ -57,13 +55,9 @@ public final class Canvas_Delegate { // ---- delegate helper data ---- - private interface Drawable { - void draw(Graphics2D graphics, Paint_Delegate paint); - } - // ---- delegate data ---- private BufferedImage mBufferedImage; - private GcSnapshot mSnapshot = new GcSnapshot(); + private GcSnapshot mSnapshot; // ---- Public Helper methods ---- @@ -84,7 +78,7 @@ public final class Canvas_Delegate { /** * Returns the current {@link Graphics2D} used to draw. */ - public GcSnapshot getGcSnapshot() { + public GcSnapshot getSnapshot() { return mSnapshot; } @@ -125,7 +119,7 @@ public final class Canvas_Delegate { return; } - canvasDelegate.getGcSnapshot().translate(dx, dy); + canvasDelegate.getSnapshot().translate(dx, dy); } /*package*/ static void rotate(Canvas thisCanvas, float degrees) { @@ -136,7 +130,7 @@ public final class Canvas_Delegate { return; } - canvasDelegate.getGcSnapshot().rotate(Math.toRadians(degrees)); + canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees)); } /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { @@ -147,7 +141,7 @@ public final class Canvas_Delegate { return; } - canvasDelegate.getGcSnapshot().scale(sx, sy); + canvasDelegate.getSnapshot().scale(sx, sy); } /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) { @@ -159,7 +153,7 @@ public final class Canvas_Delegate { } // get the current top graphics2D object. - GcSnapshot g = canvasDelegate.getGcSnapshot(); + GcSnapshot g = canvasDelegate.getSnapshot(); // get its current matrix AffineTransform currentTx = g.getTransform(); @@ -235,7 +229,7 @@ public final class Canvas_Delegate { return 0; } - return canvasDelegate.getGcSnapshot().size(); + return canvasDelegate.getSnapshot().size(); } /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) { @@ -260,33 +254,18 @@ public final class Canvas_Delegate { throw new UnsupportedOperationException(); } - /*package*/ static void drawLines(Canvas thisCanvas, float[] pts, int offset, int count, + /*package*/ static void drawLines(Canvas thisCanvas, + final float[] pts, final int offset, final int count, Paint paint) { - // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); - if (canvasDelegate == null) { - assert false; - return; - } - - Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint.mNativePaint); - if (paintDelegate == null) { - assert false; - return; - } - - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); - - try { - for (int i = 0 ; i < count ; i += 4) { - g.drawLine((int)pts[i + offset], (int)pts[i + offset + 1], - (int)pts[i + offset + 2], (int)pts[i + offset + 3]); - } - } finally { - // dispose Graphics2D object - g.dispose(); - } + draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/, + new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + for (int i = 0 ; i < count ; i += 4) { + graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1], + (int)pts[i + offset + 2], (int)pts[i + offset + 3]); + } + } + }); } /*package*/ static void freeCaches() { @@ -331,29 +310,66 @@ public final class Canvas_Delegate { /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds, int paint, int layerFlags) { - // FIXME - throw new UnsupportedOperationException(); + // get the delegate from the native int. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); + if (canvasDelegate == null) { + assert false; + return 0; + } + + Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); + if (paintDelegate == null) { + assert false; + return 0; + } + + return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags); } /*package*/ static int native_saveLayer(int nativeCanvas, float l, float t, float r, float b, int paint, int layerFlags) { - // FIXME - throw new UnsupportedOperationException(); + // get the delegate from the native int. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); + if (canvasDelegate == null) { + assert false; + return 0; + } + + Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); + if (paintDelegate == null) { + assert false; + return 0; + } + + return canvasDelegate.saveLayer(new RectF(l, t, r, b), + paintDelegate, layerFlags); } /*package*/ static int native_saveLayerAlpha(int nativeCanvas, RectF bounds, int alpha, int layerFlags) { - // FIXME - throw new UnsupportedOperationException(); + // get the delegate from the native int. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); + if (canvasDelegate == null) { + assert false; + return 0; + } + + return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags); } /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags) { - // FIXME - throw new UnsupportedOperationException(); + // get the delegate from the native int. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); + if (canvasDelegate == null) { + assert false; + return 0; + } + + return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags); } @@ -372,7 +388,7 @@ public final class Canvas_Delegate { } // get the current top graphics2D object. - GcSnapshot snapshot = canvasDelegate.getGcSnapshot(); + GcSnapshot snapshot = canvasDelegate.getSnapshot(); // get its current matrix AffineTransform currentTx = snapshot.getTransform(); @@ -399,7 +415,7 @@ public final class Canvas_Delegate { } // get the current top graphics2D object. - GcSnapshot snapshot = canvasDelegate.getGcSnapshot(); + GcSnapshot snapshot = canvasDelegate.getSnapshot(); // get the AffineTransform of the given matrix AffineTransform matrixTx = matrixDelegate.getAffineTransform(); @@ -458,7 +474,7 @@ public final class Canvas_Delegate { return false; } - Rectangle rect = canvasDelegate.getGcSnapshot().getClip().getBounds(); + Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds(); if (rect != null) { bounds.left = rect.x; bounds.top = rect.y; @@ -511,7 +527,7 @@ public final class Canvas_Delegate { native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt); } - /*package*/ static void native_drawColor(int nativeCanvas, int color, int mode) { + /*package*/ static void native_drawColor(int nativeCanvas, final int color, final int mode) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -519,27 +535,26 @@ public final class Canvas_Delegate { return; } - // get a new graphics context. - Graphics2D graphics = (Graphics2D)canvasDelegate.getGcSnapshot().create(); - try { - // reset its transform just in case - graphics.setTransform(new AffineTransform()); + final int w = canvasDelegate.mBufferedImage.getWidth(); + final int h = canvasDelegate.mBufferedImage.getHeight(); + draw(nativeCanvas, new GcSnapshot.Drawable() { + + public void draw(Graphics2D graphics, Paint_Delegate paint) { + // reset its transform just in case + graphics.setTransform(new AffineTransform()); - // set the color - graphics.setColor(new Color(color, true /*alpha*/)); + // set the color + graphics.setColor(new Color(color, true /*alpha*/)); - Composite composite = PorterDuffXfermode_Delegate.getComposite( - PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF); - if (composite != null) { - graphics.setComposite(composite); - } + Composite composite = PorterDuffXfermode_Delegate.getComposite( + PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF); + if (composite != null) { + graphics.setComposite(composite); + } - graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(), - canvasDelegate.mBufferedImage.getHeight()); - } finally { - // dispose Graphics2D object - graphics.dispose(); - } + graphics.fillRect(0, 0, w, h); + } + }); } /*package*/ static void native_drawPaint(int nativeCanvas, int paint) { @@ -547,32 +562,15 @@ public final class Canvas_Delegate { throw new UnsupportedOperationException(); } - /*package*/ static void native_drawLine(int nativeCanvas, float startX, - float startY, float stopX, - float stopY, int paint) { - // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); - if (canvasDelegate == null) { - assert false; - return; - } - - // get the delegate from the native int. - Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); - if (paintDelegate == null) { - assert false; - return; - } - - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); + /*package*/ static void native_drawLine(int nativeCanvas, + final float startX, final float startY, final float stopX, final float stopY, + int paint) { - try { - g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); - } finally { - // dispose Graphics2D object - g.dispose(); - } + draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); + } + }); } /*package*/ static void native_drawRect(int nativeCanvas, RectF rect, @@ -580,82 +578,47 @@ public final class Canvas_Delegate { native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint); } - /*package*/ static void native_drawRect(int nativeCanvas, float left, - float top, float right, - float bottom, int paint) { - // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); - if (canvasDelegate == null) { - assert false; - return; - } - - // get the delegate from the native int. - Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); - if (paintDelegate == null) { - assert false; - return; - } - - if (right > left && bottom > top) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); + /*package*/ static void native_drawRect(int nativeCanvas, + final float left, final float top, final float right, final float bottom, int paint) { - try { - int style = paintDelegate.getStyle(); + draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + int style = paint.getStyle(); // draw if (style == Paint.Style.FILL.nativeInt || style == Paint.Style.FILL_AND_STROKE.nativeInt) { - g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); + graphics.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); } if (style == Paint.Style.STROKE.nativeInt || style == Paint.Style.FILL_AND_STROKE.nativeInt) { - g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); + graphics.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); } - } finally { - // dispose Graphics2D object - g.dispose(); } - } + }); } - /*package*/ static void native_drawOval(int nativeCanvas, RectF oval, - int paint) { - // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); - if (canvasDelegate == null) { - assert false; - return; - } - - // get the delegate from the native int. - Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); - if (paintDelegate == null) { - assert false; - return; - } - + /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) { if (oval.right > oval.left && oval.bottom > oval.top) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); - - int style = paintDelegate.getStyle(); - - // draw - if (style == Paint.Style.FILL.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height()); - } - - if (style == Paint.Style.STROKE.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height()); - } + draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + int style = paint.getStyle(); + + // draw + if (style == Paint.Style.FILL.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.fillOval((int)oval.left, (int)oval.top, + (int)oval.width(), (int)oval.height()); + } - // dispose Graphics2D object - g.dispose(); + if (style == Paint.Style.STROKE.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.drawOval((int)oval.left, (int)oval.top, + (int)oval.width(), (int)oval.height()); + } + } + }); } } @@ -675,46 +638,29 @@ public final class Canvas_Delegate { } /*package*/ static void native_drawRoundRect(int nativeCanvas, - RectF rect, float rx, - float ry, int paint) { - // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); - if (canvasDelegate == null) { - assert false; - return; - } - - // get the delegate from the native int. - Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); - if (paintDelegate == null) { - assert false; - return; - } + final RectF rect, final float rx, final float ry, int paint) { - if (rect.right > rect.left && rect.bottom > rect.top) { - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); + draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { - int style = paintDelegate.getStyle(); + public void draw(Graphics2D graphics, Paint_Delegate paint) { + int style = paint.getStyle(); - // draw - if (style == Paint.Style.FILL.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - g.fillRoundRect( - (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), - (int)rx, (int)ry); - } + // draw + if (style == Paint.Style.FILL.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.fillRoundRect( + (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), + (int)rx, (int)ry); + } - if (style == Paint.Style.STROKE.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - g.drawRoundRect( - (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), - (int)rx, (int)ry); + if (style == Paint.Style.STROKE.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + graphics.drawRoundRect( + (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), + (int)rx, (int)ry); + } } - - // dispose Graphics2D object - g.dispose(); - } + }); } /*package*/ static void native_drawPath(int nativeCanvas, int path, @@ -725,7 +671,7 @@ public final class Canvas_Delegate { return; } - draw(nativeCanvas, paint, new Drawable() { + draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { public void draw(Graphics2D graphics, Paint_Delegate paint) { Shape shape = pathDelegate.getJavaShape(); int style = paint.getStyle(); @@ -846,116 +792,103 @@ public final class Canvas_Delegate { throw new UnsupportedOperationException(); } - /*package*/ static void native_drawText(int nativeCanvas, char[] text, - int index, int count, float x, - float y, int flags, int paint) { - // WARNING: the logic in this method is similar to Paint.measureText. - // Any change to this method should be reflected in Paint.measureText - - // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); - if (canvasDelegate == null) { - assert false; - return; - } + /*package*/ static void native_drawText(int nativeCanvas, + final char[] text, final int index, final int count, + final float startX, final float startY, int flags, int paint) { + draw(nativeCanvas, paint, false /*compositeOnly*/, new GcSnapshot.Drawable() { - // get the delegate from the native int. - Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); - if (paintDelegate == null) { - assert false; - return; - } - - Graphics2D g = (Graphics2D) canvasDelegate.createCustomGraphics(paintDelegate); - try { - // Paint.TextAlign indicates how the text is positioned relative to X. - // LEFT is the default and there's nothing to do. - if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) { - float m = paintDelegate.measureText(text, index, count); - if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) { - x -= m / 2; - } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) { - x -= m; + public void draw(Graphics2D graphics, Paint_Delegate paint) { + // WARNING: the logic in this method is similar to Paint.measureText. + // Any change to this method should be reflected in Paint.measureText + // Paint.TextAlign indicates how the text is positioned relative to X. + // LEFT is the default and there's nothing to do. + float x = startX; + float y = startY; + if (paint.getTextAlign() != Paint.Align.LEFT.nativeInt) { + float m = paint.measureText(text, index, count); + if (paint.getTextAlign() == Paint.Align.CENTER.nativeInt) { + x -= m / 2; + } else if (paint.getTextAlign() == Paint.Align.RIGHT.nativeInt) { + x -= m; + } } - } - List<FontInfo> fonts = paintDelegate.getFonts(); - - if (fonts.size() > 0) { - FontInfo mainFont = fonts.get(0); - int i = index; - int lastIndex = index + count; - while (i < lastIndex) { - // always start with the main font. - int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); - if (upTo == -1) { - // draw all the rest and exit. - g.setFont(mainFont.mFont); - g.drawChars(text, i, lastIndex - i, (int)x, (int)y); - return; - } else if (upTo > 0) { - // draw what's possible - g.setFont(mainFont.mFont); - g.drawChars(text, i, upTo - i, (int)x, (int)y); - - // compute the width that was drawn to increase x - x += mainFont.mMetrics.charsWidth(text, i, upTo - i); - - // move index to the first non displayed char. - i = upTo; - - // don't call continue at this point. Since it is certain the main font - // cannot display the font a index upTo (now ==i), we move on to the - // fallback fonts directly. - } + List<FontInfo> fonts = paint.getFonts(); - // no char supported, attempt to read the next char(s) with the - // fallback font. In this case we only test the first character - // and then go back to test with the main font. - // Special test for 2-char characters. - boolean foundFont = false; - for (int f = 1 ; f < fonts.size() ; f++) { - FontInfo fontInfo = fonts.get(f); - - // need to check that the font can display the character. We test - // differently if the char is a high surrogate. - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; - upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); + if (fonts.size() > 0) { + FontInfo mainFont = fonts.get(0); + int i = index; + int lastIndex = index + count; + while (i < lastIndex) { + // always start with the main font. + int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { - // draw that char - g.setFont(fontInfo.mFont); - g.drawChars(text, i, charCount, (int)x, (int)y); - - // update x - x += fontInfo.mMetrics.charsWidth(text, i, charCount); - - // update the index in the text, and move on - i += charCount; - foundFont = true; - break; + // draw all the rest and exit. + graphics.setFont(mainFont.mFont); + graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y); + return; + } else if (upTo > 0) { + // draw what's possible + graphics.setFont(mainFont.mFont); + graphics.drawChars(text, i, upTo - i, (int)x, (int)y); + + // compute the width that was drawn to increase x + x += mainFont.mMetrics.charsWidth(text, i, upTo - i); + + // move index to the first non displayed char. + i = upTo; + + // don't call continue at this point. Since it is certain the main font + // cannot display the font a index upTo (now ==i), we move on to the + // fallback fonts directly. + } + // no char supported, attempt to read the next char(s) with the + // fallback font. In this case we only test the first character + // and then go back to test with the main font. + // Special test for 2-char characters. + boolean foundFont = false; + for (int f = 1 ; f < fonts.size() ; f++) { + FontInfo fontInfo = fonts.get(f); + + // need to check that the font can display the character. We test + // differently if the char is a high surrogate. + int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; + upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); + if (upTo == -1) { + // draw that char + graphics.setFont(fontInfo.mFont); + graphics.drawChars(text, i, charCount, (int)x, (int)y); + + // update x + x += fontInfo.mMetrics.charsWidth(text, i, charCount); + + // update the index in the text, and move on + i += charCount; + foundFont = true; + break; + + } } - } - // in case no font can display the char, display it with the main font. - // (it'll put a square probably) - if (foundFont == false) { - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; + // in case no font can display the char, display it with the main font. + // (it'll put a square probably) + if (foundFont == false) { + int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; - g.setFont(mainFont.mFont); - g.drawChars(text, i, charCount, (int)x, (int)y); + graphics.setFont(mainFont.mFont); + graphics.drawChars(text, i, charCount, (int)x, (int)y); - // measure it to advance x - x += mainFont.mMetrics.charsWidth(text, i, charCount); + // measure it to advance x + x += mainFont.mMetrics.charsWidth(text, i, charCount); - // and move to the next chars. - i += charCount; + // and move to the next chars. + i += charCount; + } } } } - } finally { - g.dispose(); - } + }); } /*package*/ static void native_drawText(int nativeCanvas, String text, @@ -1042,8 +975,11 @@ public final class Canvas_Delegate { /** * Executes a {@link Drawable} with a given canvas and paint. + * <p>Note that the drawable may actually be executed several times if there are + * layers involved (see {@link #saveLayer(RectF, int, int)}. */ - private static void draw(int nCanvas, int nPaint, Drawable drawable) { + private static void draw(int nCanvas, int nPaint, boolean compositeOnly, + GcSnapshot.Drawable drawable) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); if (canvasDelegate == null) { @@ -1051,28 +987,42 @@ public final class Canvas_Delegate { return; } - Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint); - if (paintDelegate == null) { - assert false; - return; + // paint could be 0 meaning no paint + Paint_Delegate paintDelegate = null; + if (nPaint > 0) { + paintDelegate = Paint_Delegate.getDelegate(nPaint); + if (paintDelegate == null) { + assert false; + return; + } } - // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); + canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly); + } - try { - drawable.draw(g, paintDelegate); - } finally { - // dispose Graphics2D object - g.dispose(); + /** + * Executes a {@link Drawable} with a given canvas. No paint object will be provided + * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}. + * <p>Note that the drawable may actually be executed several times if there are + * layers involved (see {@link #saveLayer(RectF, int, int)}. + */ + private static void draw(int nCanvas, GcSnapshot.Drawable drawable) { + // get the delegate from the native int. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); + if (canvasDelegate == null) { + assert false; + return; } + + canvasDelegate.mSnapshot.draw(drawable); } private Canvas_Delegate(BufferedImage image) { - setBitmap(image); + mSnapshot = GcSnapshot.createDefaultSnapshot(mBufferedImage = image); } private Canvas_Delegate() { + mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/); } /** @@ -1086,8 +1036,23 @@ public final class Canvas_Delegate { // get the current save count int count = mSnapshot.size(); - // create a new snapshot and add it to the stack - mSnapshot = new GcSnapshot(mSnapshot, saveFlags); + mSnapshot = mSnapshot.save(saveFlags); + + // return the old save count + return count; + } + + private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) { + Paint_Delegate paint = new Paint_Delegate(); + paint.setAlpha(alpha); + return saveLayer(rect, paint, saveFlags); + } + + private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) { + // get the current save count + int count = mSnapshot.size(); + + mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags); // return the old save count return count; @@ -1116,126 +1081,16 @@ public final class Canvas_Delegate { private void setBitmap(BufferedImage image) { mBufferedImage = image; assert mSnapshot.size() == 1; - mSnapshot.setGraphics2D(mBufferedImage.createGraphics()); - } - - /** - * Creates a new {@link Graphics2D} based on the {@link Paint} parameters. - * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used. - */ - /*package*/ Graphics2D createCustomGraphics(Paint_Delegate paint) { - // make new one - Graphics2D g = getGcSnapshot().create(); - - // configure it - - if (paint.isAntiAliased()) { - g.setRenderingHint( - RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setRenderingHint( - RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - } - - // get the shader first, as it'll replace the color if it can be used it. - boolean customShader = false; - int nativeShader = paint.getShader(); - if (nativeShader > 0) { - Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(nativeShader); - assert shaderDelegate != null; - if (shaderDelegate != null) { - if (shaderDelegate.isSupported()) { - java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint(); - assert shaderPaint != null; - if (shaderPaint != null) { - g.setPaint(shaderPaint); - customShader = true; - } - } else { - Bridge.getLog().fidelityWarning(null, - shaderDelegate.getSupportMessage(), - null); - } - } - } - - // if no shader, use the paint color - if (customShader == false) { - g.setColor(new Color(paint.getColor(), true /*hasAlpha*/)); - } - - boolean customStroke = false; - int pathEffect = paint.getPathEffect(); - if (pathEffect > 0) { - PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(pathEffect); - assert effectDelegate != null; - if (effectDelegate != null) { - if (effectDelegate.isSupported()) { - Stroke stroke = effectDelegate.getStroke(paint); - assert stroke != null; - if (stroke != null) { - g.setStroke(stroke); - customStroke = true; - } - } else { - Bridge.getLog().fidelityWarning(null, - effectDelegate.getSupportMessage(), - null); - } - } - } - - // if no custom stroke as been set, set the default one. - if (customStroke == false) { - g.setStroke(new BasicStroke( - paint.getStrokeWidth(), - paint.getJavaCap(), - paint.getJavaJoin(), - paint.getJavaStrokeMiter())); - } - - // the alpha for the composite. Always opaque if the normal paint color is used since - // it contains the alpha - int alpha = customShader ? paint.getAlpha() : 0xFF; - - boolean customXfermode = false; - int xfermode = paint.getXfermode(); - if (xfermode > 0) { - Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode()); - assert xfermodeDelegate != null; - if (xfermodeDelegate != null) { - if (xfermodeDelegate.isSupported()) { - Composite composite = xfermodeDelegate.getComposite(alpha); - assert composite != null; - if (composite != null) { - g.setComposite(composite); - customXfermode = true; - } - } else { - Bridge.getLog().fidelityWarning(null, - xfermodeDelegate.getSupportMessage(), - null); - } - } - } - - // if there was no custom xfermode, but we have alpha (due to a shader and a non - // opaque alpha channel in the paint color), then we create an AlphaComposite anyway - // that will handle the alpha. - if (customXfermode == false && alpha != 0xFF) { - g.setComposite(PorterDuffXfermode_Delegate.getComposite( - PorterDuff.Mode.SRC_OVER, alpha)); - } - - return g; + mSnapshot.setImage(mBufferedImage); } private static void drawBitmap( int nativeCanvas, - BufferedImage image, - Bitmap.Config mBitmapConfig, + final BufferedImage image, + final Bitmap.Config mBitmapConfig, int nativePaintOrZero, - int sleft, int stop, int sright, int sbottom, - int dleft, int dtop, int dright, int dbottom) { + final int sleft, final int stop, final int sright, final int sbottom, + final int dleft, final int dtop, final int dright, final int dbottom) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -1253,47 +1108,35 @@ public final class Canvas_Delegate { } } - drawBitmap(canvasDelegate, image, mBitmapConfig, paintDelegate, - sleft, stop, sright, sbottom, - dleft, dtop, dright, dbottom); - } - - private static void drawBitmap( - Canvas_Delegate canvasDelegate, - BufferedImage image, - Bitmap.Config mBitmapConfig, - Paint_Delegate paintDelegate, - int sleft, int stop, int sright, int sbottom, - int dleft, int dtop, int dright, int dbottom) { - //FIXME add support for canvas, screen and bitmap densities. - - Graphics2D g = canvasDelegate.getGcSnapshot().create(); - try { - if (paintDelegate != null && paintDelegate.isFilterBitmap()) { - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); + // if the bitmap config is alpha_8, then we erase all color value from it + // before drawing it. + if (mBitmapConfig == Bitmap.Config.ALPHA_8) { + int w = image.getWidth(); + int h = image.getHeight(); + int[] argb = new int[w*h]; + image.getRGB(0, 0, image.getWidth(), image.getHeight(), + argb, 0, image.getWidth()); + + final int length = argb.length; + for (int i = 0 ; i < length; i++) { + argb[i] &= 0xFF000000; } + image.setRGB(0, 0, w, h, argb, 0, w); + } - // if the bitmap config is alpha_8, then we erase all color value from it - // before drawing it. - if (mBitmapConfig == Bitmap.Config.ALPHA_8) { - int w = image.getWidth(); - int h = image.getHeight(); - int[] argb = new int[w*h]; - image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth()); - - final int length = argb.length; - for (int i = 0 ; i < length; i++) { - argb[i] &= 0xFF000000; - } - image.setRGB(0, 0, w, h, argb, 0, w); - } + draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, + new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + if (paint != null && paint.isFilterBitmap()) { + graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + } - g.drawImage(image, dleft, dtop, dright, dbottom, - sleft, stop, sright, sbottom, null); - } finally { - g.dispose(); - } + //FIXME add support for canvas, screen and bitmap densities. + graphics.drawImage(image, dleft, dtop, dright, dbottom, + sleft, stop, sright, sbottom, null); + } + }); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java index 954c658..a6844d4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java @@ -55,7 +55,7 @@ public class ComposePathEffect_Delegate extends PathEffect_Delegate { @Override public String getSupportMessage() { - return "Composte Path Effects are not supported in Layout Preview mode."; + return "Compose Path Effects are not supported in Layout Preview mode."; } // ---- native methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java index 2853222..b4baa6f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java @@ -55,7 +55,7 @@ public class ComposeShader_Delegate extends Shader_Delegate { @Override public String getSupportMessage() { - return "Compose Shader are not supported in Layout Preview mode."; + return "Compose Shaders are not supported in Layout Preview mode."; } diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index f6cee5e..463f4e9 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -106,6 +106,45 @@ public final class Matrix_Delegate { return true; } + public static Matrix_Delegate make(AffineTransform matrix) { + float[] values = new float[MATRIX_SIZE]; + values[0] = (float) matrix.getScaleX(); + values[1] = (float) matrix.getShearX(); + values[2] = (float) matrix.getTranslateX(); + values[3] = (float) matrix.getShearY(); + values[4] = (float) matrix.getScaleY(); + values[5] = (float) matrix.getTranslateY(); + values[6] = 0.f; + values[7] = 0.f; + values[8] = 1.f; + + return new Matrix_Delegate(values); + } + + public boolean mapRect(RectF dst, RectF src) { + // array with 4 corners + float[] corners = new float[] { + src.left, src.top, + src.right, src.top, + src.right, src.bottom, + src.left, src.bottom, + }; + + // apply the transform to them. + mapPoints(corners); + + // now put the result in the rect. We take the min/max of Xs and min/max of Ys + dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6])); + dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6])); + + dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7])); + dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7])); + + + return (computeTypeMask() & kRectStaysRect_Mask) != 0; + } + + /** * Returns an {@link AffineTransform} matching the matrix. */ @@ -655,26 +694,7 @@ public final class Matrix_Delegate { return false; } - // array with 4 corners - float[] corners = new float[] { - src.left, src.top, - src.right, src.top, - src.right, src.bottom, - src.left, src.bottom, - }; - - // apply the transform to them. - d.mapPoints(corners); - - // now put the result in the rect. We take the min/max of Xs and min/max of Ys - dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6])); - dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6])); - - dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7])); - dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7])); - - - return (d.computeTypeMask() & kRectStaysRect_Mask) != 0; + return d.mapRect(dst, src); } /*package*/ static float native_mapRadius(int native_object, float radius) { @@ -740,7 +760,6 @@ public final class Matrix_Delegate { matrix[4], matrix[2], matrix[5]); } - /** * Reset a matrix to the identity */ @@ -830,6 +849,10 @@ public final class Matrix_Delegate { reset(); } + private Matrix_Delegate(float[] values) { + System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE); + } + /** * Adds the given transformation to the current Matrix * <p/>This in effect does this = this*matrix diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java index b8f76a6..e64a69a 100644 --- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java @@ -18,6 +18,7 @@ package android.graphics; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.layoutlib.bridge.impl.GcSnapshot; import com.android.ninepatch.NinePatchChunk; import android.graphics.drawable.NinePatchDrawable; @@ -123,11 +124,11 @@ public final class NinePatch_Delegate { } private static void draw(int canvas_instance, - int left, int top, int right, int bottom, + final int left, final int top, final int right, final int bottom, int bitmap_instance, byte[] c, int paint_instance_or_null, - int destDensity, int srcDensity) { + final int destDensity, final int srcDensity) { // get the delegate from the native int. - Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance); + final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance); if (bitmap_delegate == null) { assert false; return; @@ -143,7 +144,7 @@ public final class NinePatch_Delegate { return; } - NinePatchChunk chunkObject = getChunk(c); + final NinePatchChunk chunkObject = getChunk(c); assert chunkObject != null; if (chunkObject == null) { return; @@ -158,19 +159,13 @@ public final class NinePatch_Delegate { // this one can be null Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null); - Graphics2D graphics; - if (paint_delegate != null) { - graphics = canvas_delegate.createCustomGraphics(paint_delegate); - } else { - graphics = canvas_delegate.getGcSnapshot().create(); - } + canvas_delegate.getSnapshot().draw(new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + chunkObject.draw(bitmap_delegate.getImage(), graphics, + left, top, right - left, bottom - top, destDensity, srcDensity); + } + }, paint_delegate, true /*compositeOnly*/); - try { - chunkObject.draw(bitmap_delegate.getImage(), graphics, - left, top, right - left, bottom - top, destDensity, srcDensity); - } finally { - graphics.dispose(); - } } /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) { diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 0a597ca..7cb30dd 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -118,6 +118,10 @@ public class Paint_Delegate { return mColor >>> 24; } + public void setAlpha(int alpha) { + mColor = (alpha << 24) | (mColor & 0x00FFFFFF); + } + public int getTextAlign() { return mTextAlign; } @@ -130,7 +134,11 @@ public class Paint_Delegate { * returns the value of stroke miter needed by the java api. */ public float getJavaStrokeMiter() { - return mStrokeMiter * mStrokeWidth; + float miter = mStrokeMiter * mStrokeWidth; + if (miter < 1.f) { + miter = 1.f; + } + return miter; } public int getJavaCap() { @@ -274,7 +282,7 @@ public class Paint_Delegate { return; } - delegate.mColor = (a << 24) | (delegate.mColor & 0x00FFFFFF); + delegate.setAlpha(a); } /*package*/ static float getStrokeWidth(Paint thisPaint) { @@ -835,7 +843,7 @@ public class Paint_Delegate { // ---- Private delegate/helper methods ---- - private Paint_Delegate() { + /*package*/ Paint_Delegate() { reset(); } @@ -866,14 +874,14 @@ public class Paint_Delegate { private void reset() { mFlags = Paint.DEFAULT_PAINT_FLAGS; - mColor = 0; + mColor = 0xFF000000; mStyle = Paint.Style.FILL.nativeInt; mCap = Paint.Cap.BUTT.nativeInt; mJoin = Paint.Join.MITER.nativeInt; mTextAlign = 0; mTypeface = Typeface.sDefaults[0].native_instance; mStrokeWidth = 1.f; - mStrokeMiter = 2.f; + mStrokeMiter = 4.f; mTextSize = 20.f; mTextScaleX = 1.f; mTextSkewX = 0.f; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java index a09524c..6ebc56c 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java @@ -107,7 +107,8 @@ public class BridgeRenderSession extends RenderSession { Bridge.prepareThread(); mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT); if (mLastResult.isSuccess()) { - mLastResult = mSession.insertChild((ViewGroup) parentView, childXml, index, listener); + mLastResult = mSession.insertChild((ViewGroup) parentView, childXml, index, + listener); } } finally { mSession.release(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java index 8c6b1be..103e262 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java @@ -16,20 +16,42 @@ package com.android.layoutlib.bridge.impl; +import com.android.layoutlib.bridge.Bridge; + import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint_Delegate; +import android.graphics.PathEffect_Delegate; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode_Delegate; +import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Region; +import android.graphics.Shader_Delegate; +import android.graphics.Xfermode_Delegate; +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; import java.awt.Graphics2D; +import java.awt.RenderingHints; import java.awt.Shape; +import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.ArrayList; /** * Class representing a graphics context snapshot, as well as a context stack as a linked list. * <p> * This is based on top of {@link Graphics2D} but can operate independently if none are available * yet when setting transforms and clip information. + * <p> + * This allows for drawing through {@link #draw(Drawable, Paint_Delegate)} and + * {@link #draw(Drawable, Paint_Delegate)} * */ public class GcSnapshot { @@ -37,38 +59,209 @@ public class GcSnapshot { private final GcSnapshot mPrevious; private final int mFlags; - private Graphics2D mGraphics2D = null; + /** list of layers. */ + private final ArrayList<Layer> mLayers = new ArrayList<Layer>(); + /** temp transform in case transformation are set before a Graphics2D exists */ private AffineTransform mTransform = null; /** temp clip in case clipping is set before a Graphics2D exists */ private Area mClip = null; + // local layer data + private final Layer mLocalLayer; + private final Paint_Delegate mLocalLayerPaint; + private Rect mLocalLayerRegion; + + public interface Drawable { + void draw(Graphics2D graphics, Paint_Delegate paint); + } + + /** + * class containing information about a layer. + */ + private static class Layer { + private final Graphics2D mGraphics; + private final BufferedImage mImage; + private BufferedImage mOriginalCopy; + + Layer(Graphics2D graphics, BufferedImage image) { + mGraphics = graphics; + mImage = image; + } + + /** The Graphics2D, guaranteed to be non null */ + Graphics2D getGraphics() { + return mGraphics; + } + + /** The BufferedImage, guaranteed to be non null */ + BufferedImage getImage() { + return mImage; + } + + Layer makeCopy() { + return new Layer((Graphics2D) mGraphics.create(), mImage); + } + + /** sets an optional copy of the original content to be used during restore */ + void setOriginalCopy(BufferedImage image) { + mOriginalCopy = image; + } + + BufferedImage getOriginalCopy() { + return mOriginalCopy; + } + } + /** - * Creates a new {@link GcSnapshot} on top of another one. + * Creates the root snapshot associating it with a given image. + * <p> + * If <var>image</var> is null, then {@link GcSnapshot#setImage(BufferedImage)} must be + * called before the snapshot can be used to draw. Transform and clip operations are permitted + * before. + * + * @param image the image to associate to the snapshot or null. + * @return the root snapshot + */ + public static GcSnapshot createDefaultSnapshot(BufferedImage image) { + GcSnapshot snapshot = new GcSnapshot(); + if (image != null) { + snapshot.setImage(image); + } + + return snapshot; + } + + /** + * Saves the current state according to the given flags and returns the new current snapshot. * <p/> - * This is basically the equivalent of {@link Canvas#save(int)} - * @param previous the previous snapshot head. - * @param flags the flags regarding what should be saved. + * This is the equivalent of {@link Canvas#save(int)} + * + * @param flags the save flags. + * @return the new snapshot + * + * @see Canvas#save(int) */ - public GcSnapshot(GcSnapshot previous, int flags) { - assert previous != null; - mPrevious = previous; - mFlags = flags; - mGraphics2D = (Graphics2D) previous.mGraphics2D.create(); + public GcSnapshot save(int flags) { + return new GcSnapshot(this, null /*layerbounds*/, null /*paint*/, flags); + } + + /** + * Saves the current state and creates a new layer, and returns the new current snapshot. + * <p/> + * This is the equivalent of {@link Canvas#saveLayer(RectF, Paint, int)} + * + * @param layerBounds the layer bounds + * @param paint the Paint information used to blit the layer back into the layers underneath + * upon restore + * @param flags the save flags. + * @return the new snapshot + * + * @see Canvas#saveLayer(RectF, Paint, int) + */ + public GcSnapshot saveLayer(RectF layerBounds, Paint_Delegate paint, int flags) { + return new GcSnapshot(this, layerBounds, paint, flags); } /** * Creates the root snapshot. * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible. */ - public GcSnapshot() { + private GcSnapshot() { mPrevious = null; mFlags = 0; + mLocalLayer = null; + mLocalLayerPaint = null; + } + + /** + * Creates a new {@link GcSnapshot} on top of another one, with a layer data to be restored + * into the main graphics when {@link #restore()} is called. + * + * @param previous the previous snapshot head. + * @param layerBounds the region of the layer. Optional, if null, this is a normal save() + * @param paint the Paint information used to blit the layer back into the layers underneath + * upon restore + * @param flags the flags regarding what should be saved. + */ + private GcSnapshot(GcSnapshot previous, RectF layerBounds, Paint_Delegate paint, int flags) { + assert previous != null; + mPrevious = previous; + mFlags = flags; + + // make a copy of the current layers before adding the new one. + // This keeps the same BufferedImage reference but creates new Graphics2D for this + // snapshot. + // It does not copy whatever original copy the layers have, as they will be done + // only if the new layer doesn't clip drawing to itself. + for (Layer layer : mPrevious.mLayers) { + mLayers.add(layer.makeCopy()); + } + + if (layerBounds != null) { + // get the current transform + AffineTransform matrix = mLayers.get(0).getGraphics().getTransform(); + + // transform the layerBounds and puts it into a int rect + RectF rect2 = new RectF(); + mapRect(matrix, rect2, layerBounds); + mLocalLayerRegion = new Rect(); + rect2.round(mLocalLayerRegion); + + // get the base layer (always at index 0) + Layer baseLayer = mLayers.get(0); + + // create the image for the layer + BufferedImage layerImage = new BufferedImage( + baseLayer.getImage().getWidth(), + baseLayer.getImage().getHeight(), + BufferedImage.TYPE_INT_ARGB); + + // create a graphics for it so that drawing can be done. + Graphics2D layerGraphics = layerImage.createGraphics(); + + // because this layer inherits the current context for transform and clip, + // set them to one from the base layer. + AffineTransform currentMtx = baseLayer.getGraphics().getTransform(); + layerGraphics.setTransform(currentMtx); + + Shape currentClip = baseLayer.getGraphics().getClip(); + layerGraphics.setClip(currentClip); + + // create a new layer for this new layer and add it to the list at the end. + mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage)); + + // if the drawing is not clipped to the local layer only, we save the current content + // of all other layers. We are only interested in the part that will actually + // be drawn, so we create as small bitmaps as we can. + // This is so that we can erase the drawing that goes in the layers below that will + // be coming from the layer itself. + if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) { + int w = mLocalLayerRegion.width(); + int h = mLocalLayerRegion.height(); + for (int i = 0 ; i < mLayers.size() - 1 ; i++) { + Layer layer = mLayers.get(i); + BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = image.createGraphics(); + graphics.drawImage(layer.getImage(), + 0, 0, w, h, + mLocalLayerRegion.left, mLocalLayerRegion.top, + mLocalLayerRegion.right, mLocalLayerRegion.bottom, + null); + graphics.dispose(); + layer.setOriginalCopy(image); + } + } + } else { + mLocalLayer = null; + } + + mLocalLayerPaint = paint; } public void dispose() { - if (mGraphics2D != null) { - mGraphics2D.dispose(); + for (Layer layer : mLayers) { + layer.getGraphics().dispose(); } if (mPrevious != null) { @@ -102,35 +295,35 @@ public class GcSnapshot { } /** - * Sets the Graphics2D object for this snapshot if it was created through {@link #GcSnapshot()}. + * Link the snapshot to a BufferedImage. + * <p/> + * This is only for the case where the snapshot was created with a null image when calling + * {@link #createDefaultSnapshot(BufferedImage)}, and is therefore not yet linked to + * a previous snapshot. + * <p/> * If any transform or clip information was set before, they are put into the Graphics object. - * @param graphics2D the graphics object to set. + * @param image the buffered image to link to. */ - public void setGraphics2D(Graphics2D graphics2D) { - mGraphics2D = graphics2D; + public void setImage(BufferedImage image) { + assert mLayers.size() == 0; + Graphics2D graphics2D = image.createGraphics(); + mLayers.add(new Layer(graphics2D, image)); if (mTransform != null) { - mGraphics2D.setTransform(mTransform); + graphics2D.setTransform(mTransform); mTransform = null; } if (mClip != null) { - mGraphics2D.setClip(mClip); + graphics2D.setClip(mClip); mClip = null; } } - /** - * Creates and return a copy of the current {@link Graphics2D}. - * @return a new {@link Graphics2D}. - */ - public Graphics2D create() { - assert mGraphics2D != null; - return (Graphics2D) mGraphics2D.create(); - } - public void translate(float dx, float dy) { - if (mGraphics2D != null) { - mGraphics2D.translate(dx, dy); + if (mLayers.size() > 0) { + for (Layer layer : mLayers) { + layer.getGraphics().translate(dx, dy); + } } else { if (mTransform == null) { mTransform = new AffineTransform(); @@ -140,8 +333,10 @@ public class GcSnapshot { } public void rotate(double radians) { - if (mGraphics2D != null) { - mGraphics2D.rotate(radians); + if (mLayers.size() > 0) { + for (Layer layer : mLayers) { + layer.getGraphics().rotate(radians); + } } else { if (mTransform == null) { mTransform = new AffineTransform(); @@ -151,8 +346,10 @@ public class GcSnapshot { } public void scale(float sx, float sy) { - if (mGraphics2D != null) { - mGraphics2D.scale(sx, sy); + if (mLayers.size() > 0) { + for (Layer layer : mLayers) { + layer.getGraphics().scale(sx, sy); + } } else { if (mTransform == null) { mTransform = new AffineTransform(); @@ -162,8 +359,9 @@ public class GcSnapshot { } public AffineTransform getTransform() { - if (mGraphics2D != null) { - return mGraphics2D.getTransform(); + if (mLayers.size() > 0) { + // all graphics2D in the list have the same transform + return mLayers.get(0).getGraphics().getTransform(); } else { if (mTransform == null) { mTransform = new AffineTransform(); @@ -173,8 +371,10 @@ public class GcSnapshot { } public void setTransform(AffineTransform transform) { - if (mGraphics2D != null) { - mGraphics2D.setTransform(transform); + if (mLayers.size() > 0) { + for (Layer layer : mLayers) { + layer.getGraphics().setTransform(transform); + } } else { if (mTransform == null) { mTransform = new AffineTransform(); @@ -184,40 +384,52 @@ public class GcSnapshot { } public boolean clipRect(float left, float top, float right, float bottom, int regionOp) { - if (mGraphics2D != null) { + if (mLayers.size() > 0) { + Shape clip = null; if (regionOp == Region.Op.DIFFERENCE.nativeInt) { - Area newClip = new Area(mGraphics2D.getClip()); + Area newClip = new Area(getClip()); newClip.subtract(new Area( new Rectangle2D.Float(left, top, right - left, bottom - top))); - mGraphics2D.setClip(newClip); + clip = newClip; } else if (regionOp == Region.Op.INTERSECT.nativeInt) { - mGraphics2D.clipRect((int) left, (int) top, - (int) (right - left), (int) (bottom - top)); + for (Layer layer : mLayers) { + layer.getGraphics().clipRect( + (int) left, (int) top, (int) (right - left), (int) (bottom - top)); + } } else if (regionOp == Region.Op.UNION.nativeInt) { - Area newClip = new Area(mGraphics2D.getClip()); + Area newClip = new Area(getClip()); newClip.add(new Area( new Rectangle2D.Float(left, top, right - left, bottom - top))); - mGraphics2D.setClip(newClip); + clip = newClip; } else if (regionOp == Region.Op.XOR.nativeInt) { - Area newClip = new Area(mGraphics2D.getClip()); + Area newClip = new Area(getClip()); newClip.exclusiveOr(new Area( new Rectangle2D.Float(left, top, right - left, bottom - top))); - mGraphics2D.setClip(newClip); + clip = newClip; } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) { Area newClip = new Area( new Rectangle2D.Float(left, top, right - left, bottom - top)); - newClip.subtract(new Area(mGraphics2D.getClip())); - mGraphics2D.setClip(newClip); + newClip.subtract(new Area(getClip())); + clip = newClip; + } else if (regionOp == Region.Op.REPLACE.nativeInt) { - mGraphics2D.setClip((int) left, (int) top, - (int) (right - left), (int) (bottom - top)); + for (Layer layer : mLayers) { + layer.getGraphics().setClip( + (int) left, (int) top, (int) (right - left), (int) (bottom - top)); + } } - return mGraphics2D.getClip().getBounds().isEmpty() == false; + if (clip != null) { + for (Layer layer : mLayers) { + layer.getGraphics().setClip(clip); + } + } + + return getClip().getBounds().isEmpty() == false; } else { if (mClip == null) { mClip = new Area(); @@ -238,8 +450,9 @@ public class GcSnapshot { } public Shape getClip() { - if (mGraphics2D != null) { - return mGraphics2D.getClip(); + if (mLayers.size() > 0) { + // they all have the same clip + return mLayers.get(0).getGraphics().getClip(); } else { if (mClip == null) { mClip = new Area(); @@ -263,26 +476,261 @@ public class GcSnapshot { } } + /** + * Executes the Drawable's draw method, with a null paint delegate. + * <p/> + * Note that the method can be called several times if there are more than one active layer. + * @param drawable + */ + public void draw(Drawable drawable) { + draw(drawable, null, false /*compositeOnly*/); + } + + /** + * Executes the Drawable's draw method. + * <p/> + * Note that the method can be called several times if there are more than one active layer. + * @param drawable + * @param paint + * @param compositeOnly whether the paint is used for composite only. This is typically + * the case for bitmaps. + */ + public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly) { + // if we clip to the layer, then we only draw in the layer + if (mLocalLayer != null && (mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) != 0) { + drawInLayer(mLocalLayer, drawable, paint, compositeOnly); + } else { + // draw in all the layers + for (Layer layer : mLayers) { + drawInLayer(layer, drawable, paint, compositeOnly); + } + } + } + + private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint, + boolean compositeOnly) { + Graphics2D originalGraphics = layer.getGraphics(); + // get a Graphics2D object configured with the drawing parameters. + Graphics2D configuredGraphics2D = + paint != null ? + createCustomGraphics(originalGraphics, paint, compositeOnly) : + (Graphics2D) originalGraphics.create(); + + try { + drawable.draw(configuredGraphics2D, paint); + } finally { + // dispose Graphics2D object + configuredGraphics2D.dispose(); + } + } + private GcSnapshot doRestore() { - // if this snapshot does not save everything, then set the previous snapshot - // to this snapshot content if (mPrevious != null) { + boolean forceAllSave = false; + if (mLocalLayer != null) { + forceAllSave = true; // layers always save both clip and transform + + // prepare to draw the current layer in the previous layers, ie all layers but + // the last one, since the last one is the local layer + for (int i = 0 ; i < mLayers.size() - 1 ; i++) { + Layer layer = mLayers.get(i); + + Graphics2D baseGfx = layer.getImage().createGraphics(); + + // if the layer contains an original copy this means the flags + // didn't restrict drawing to the local layer and we need to make sure the + // layer bounds in the layer beneath didn't receive any drawing. + // so we use the originalCopy to erase the new drawings in there. + BufferedImage originalCopy = layer.getOriginalCopy(); + if (originalCopy != null) { + Graphics2D g = (Graphics2D) baseGfx.create(); + g.setComposite(AlphaComposite.Src); + + g.drawImage(originalCopy, + mLocalLayerRegion.left, mLocalLayerRegion.top, + mLocalLayerRegion.right, mLocalLayerRegion.bottom, + 0, 0, mLocalLayerRegion.width(), mLocalLayerRegion.height(), + null); + g.dispose(); + } + + // now draw put the content of the local layer onto the layer, using the paint + // information + Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint, + true /*alphaOnly*/); + + g.drawImage(mLocalLayer.getImage(), + mLocalLayerRegion.left, mLocalLayerRegion.top, + mLocalLayerRegion.right, mLocalLayerRegion.bottom, + mLocalLayerRegion.left, mLocalLayerRegion.top, + mLocalLayerRegion.right, mLocalLayerRegion.bottom, + null); + g.dispose(); + + baseGfx.dispose(); + } + } + + // if this snapshot does not save everything, then set the previous snapshot + // to this snapshot content + // didn't save the matrix? set the current matrix on the previous snapshot - if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) { - mPrevious.mGraphics2D.setTransform(getTransform()); + if (forceAllSave == false && (mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) { + AffineTransform mtx = getTransform(); + for (Layer layer : mPrevious.mLayers) { + layer.getGraphics().setTransform(mtx); + } } // didn't save the clip? set the current clip on the previous snapshot - if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) { - mPrevious.mGraphics2D.setClip(mGraphics2D.getClip()); + if (forceAllSave == false && (mFlags & Canvas.CLIP_SAVE_FLAG) == 0) { + Shape clip = getClip(); + for (Layer layer : mPrevious.mLayers) { + layer.getGraphics().setClip(clip); + } } } - if (mGraphics2D != null) { - mGraphics2D.dispose(); + for (Layer layer : mLayers) { + layer.getGraphics().dispose(); } return mPrevious; } + /** + * Creates a new {@link Graphics2D} based on the {@link Paint} parameters. + * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used. + */ + private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint, + boolean compositeOnly) { + // make new one graphics + Graphics2D g = (Graphics2D) original.create(); + + // configure it + + if (paint.isAntiAliased()) { + g.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint( + RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } + + boolean customShader = false; + + // get the shader first, as it'll replace the color if it can be used it. + if (compositeOnly == false) { + int nativeShader = paint.getShader(); + if (nativeShader > 0) { + Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(nativeShader); + assert shaderDelegate != null; + if (shaderDelegate != null) { + if (shaderDelegate.isSupported()) { + java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint(); + assert shaderPaint != null; + if (shaderPaint != null) { + g.setPaint(shaderPaint); + customShader = true; + } + } else { + Bridge.getLog().fidelityWarning(null, + shaderDelegate.getSupportMessage(), + null); + } + } + } + + // if no shader, use the paint color + if (customShader == false) { + g.setColor(new Color(paint.getColor(), true /*hasAlpha*/)); + } + + boolean customStroke = false; + int pathEffect = paint.getPathEffect(); + if (pathEffect > 0) { + PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(pathEffect); + assert effectDelegate != null; + if (effectDelegate != null) { + if (effectDelegate.isSupported()) { + Stroke stroke = effectDelegate.getStroke(paint); + assert stroke != null; + if (stroke != null) { + g.setStroke(stroke); + customStroke = true; + } + } else { + Bridge.getLog().fidelityWarning(null, + effectDelegate.getSupportMessage(), + null); + } + } + } + + // if no custom stroke as been set, set the default one. + if (customStroke == false) { + g.setStroke(new BasicStroke( + paint.getStrokeWidth(), + paint.getJavaCap(), + paint.getJavaJoin(), + paint.getJavaStrokeMiter())); + } + } + + // the alpha for the composite. Always opaque if the normal paint color is used since + // it contains the alpha + int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF; + + boolean customXfermode = false; + int xfermode = paint.getXfermode(); + if (xfermode > 0) { + Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode()); + assert xfermodeDelegate != null; + if (xfermodeDelegate != null) { + if (xfermodeDelegate.isSupported()) { + Composite composite = xfermodeDelegate.getComposite(alpha); + assert composite != null; + if (composite != null) { + g.setComposite(composite); + customXfermode = true; + } + } else { + Bridge.getLog().fidelityWarning(null, + xfermodeDelegate.getSupportMessage(), + null); + } + } + } + + // if there was no custom xfermode, but we have alpha (due to a shader and a non + // opaque alpha channel in the paint color), then we create an AlphaComposite anyway + // that will handle the alpha. + if (customXfermode == false && alpha != 0xFF) { + g.setComposite(PorterDuffXfermode_Delegate.getComposite( + PorterDuff.Mode.SRC_OVER, alpha)); + } + + return g; + } + + + private void mapRect(AffineTransform matrix, RectF dst, RectF src) { + // array with 4 corners + float[] corners = new float[] { + src.left, src.top, + src.right, src.top, + src.right, src.bottom, + src.left, src.bottom, + }; + + // apply the transform to them. + matrix.transform(corners, 0, corners, 0, 4); + + // now put the result in the rect. We take the min/max of Xs and min/max of Ys + dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6])); + dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6])); + + dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7])); + dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7])); + } + } |