Fix layoutlib to reflect recent changes in platform code

Test: Layoutlib tests
Change-Id: I81964233a9e580ccee9c9e9b0cf3525babf30bd4
diff --git a/bridge/src/android/graphics/BaseCanvas_Delegate.java b/bridge/src/android/graphics/BaseCanvas_Delegate.java
new file mode 100644
index 0000000..df85806
--- /dev/null
+++ b/bridge/src/android/graphics/BaseCanvas_Delegate.java
@@ -0,0 +1,727 @@
+/*
+ * 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;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.GcSnapshot;
+import com.android.layoutlib.bridge.impl.PorterDuffUtility;
+import com.android.ninepatch.NinePatchChunk;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.text.TextUtils;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+
+public class BaseCanvas_Delegate {
+    // ---- delegate manager ----
+    protected static DelegateManager<BaseCanvas_Delegate> sManager =
+            new DelegateManager<>(BaseCanvas_Delegate.class);
+
+    // ---- delegate helper data ----
+    private final static boolean[] sBoolOut = new boolean[1];
+
+
+    // ---- delegate data ----
+    protected Bitmap_Delegate mBitmap;
+    protected GcSnapshot mSnapshot;
+
+    // ---- Public Helper methods ----
+
+    protected BaseCanvas_Delegate(Bitmap_Delegate bitmap) {
+        mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
+    }
+
+    protected BaseCanvas_Delegate() {
+        mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
+    }
+
+    /**
+     * Disposes of the {@link Graphics2D} stack.
+     */
+    protected void dispose() {
+        mSnapshot.dispose();
+    }
+
+    /**
+     * Returns the current {@link Graphics2D} used to draw.
+     */
+    public GcSnapshot getSnapshot() {
+        return mSnapshot;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float left, float top,
+            long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity) {
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        BufferedImage image = bitmapDelegate.getImage();
+        float right = left + image.getWidth();
+        float bottom = top + image.getHeight();
+
+        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
+                0, 0, image.getWidth(), image.getHeight(),
+                (int)left, (int)top, (int)right, (int)bottom);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmap(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) {
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, (int) srcLeft, (int) srcTop,
+                (int) srcRight, (int) srcBottom, (int) dstLeft, (int) dstTop, (int) dstRight,
+                (int) dstBottom);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride,
+            final float x, final float y, int width, int height, boolean hasAlpha,
+            long nativePaintOrZero) {
+        // create a temp BufferedImage containing the content.
+        final BufferedImage image = new BufferedImage(width, height,
+                hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
+        image.setRGB(0, 0, width, height, colors, offset, stride);
+
+        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paint) -> {
+                    if (paint != null && paint.isFilterBitmap()) {
+                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+                    }
+
+                    graphics.drawImage(image, (int) x, (int) y, null);
+                });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawColor(long nativeCanvas, final int color, final int mode) {
+        // get the delegate from the native int.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        final int w = canvasDelegate.mBitmap.getImage().getWidth();
+        final int h = canvasDelegate.mBitmap.getImage().getHeight();
+        draw(nativeCanvas, (graphics, paint) -> {
+            // reset its transform just in case
+            graphics.setTransform(new AffineTransform());
+
+            // set the color
+            graphics.setColor(new java.awt.Color(color, true /*alpha*/));
+
+            Composite composite = PorterDuffUtility.getComposite(
+                    PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
+            if (composite != null) {
+                graphics.setComposite(composite);
+            }
+
+            graphics.fillRect(0, 0, w, h);
+        });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawPaint(long nativeCanvas, long paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPaint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawPoint(long nativeCanvas, float x, float y,
+            long nativePaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawPoints(long nativeCanvas, float[] pts, int offset, int count,
+            long nativePaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawLine(long nativeCanvas,
+            final float startX, final float startY, final float stopX, final float stopY,
+            long paint) {
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawLines(long nativeCanvas,
+            final float[] pts, final int offset, final int count,
+            long nativePaint) {
+        draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
+                false /*forceSrcMode*/, (graphics, paintDelegate) -> {
+                    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]);
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawRect(long nativeCanvas,
+            final float left, final float top, final float right, final float bottom, long paint) {
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> {
+                    int style = paintDelegate.getStyle();
+
+                    // draw
+                    if (style == Paint.Style.FILL.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        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) {
+                        graphics.drawRect((int)left, (int)top,
+                                (int)(right-left), (int)(bottom-top));
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawOval(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*/,
+                    (graphics, paintDelegate) -> {
+                        int style = paintDelegate.getStyle();
+
+                        // draw
+                        if (style == Paint.Style.FILL.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.fillOval((int)left, (int)top,
+                                    (int)(right - left), (int)(bottom - top));
+                        }
+
+                        if (style == Paint.Style.STROKE.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.drawOval((int)left, (int)top,
+                                    (int)(right - left), (int)(bottom - top));
+                        }
+                    });
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawCircle(long nativeCanvas,
+            float cx, float cy, float radius, long paint) {
+        nDrawOval(nativeCanvas,
+                cx - radius, cy - radius, cx + radius, cy + radius,
+                paint);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawArc(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) {
+        if (right > left && bottom > top) {
+            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                    (graphics, paintDelegate) -> {
+                        int style = paintDelegate.getStyle();
+
+                        Arc2D.Float arc = new Arc2D.Float(
+                                left, top, right - left, bottom - top,
+                                -startAngle, -sweep,
+                                useCenter ? Arc2D.PIE : Arc2D.OPEN);
+
+                        // draw
+                        if (style == Paint.Style.FILL.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.fill(arc);
+                        }
+
+                        if (style == Paint.Style.STROKE.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.draw(arc);
+                        }
+                    });
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawRoundRect(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*/,
+                (graphics, paintDelegate) -> {
+                    int style = paintDelegate.getStyle();
+
+                    // draw
+                    if (style == Paint.Style.FILL.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.fillRoundRect(
+                                (int)left, (int)top,
+                                (int)(right - left), (int)(bottom - top),
+                                2 * (int)rx, 2 * (int)ry);
+                    }
+
+                    if (style == Paint.Style.STROKE.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.drawRoundRect(
+                                (int)left, (int)top,
+                                (int)(right - left), (int)(bottom - top),
+                                2 * (int)rx, 2 * (int)ry);
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
+    public static void nDrawPath(long nativeCanvas, long path, long paint) {
+        final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> {
+                    Shape shape = pathDelegate.getJavaShape();
+                    Rectangle2D bounds = shape.getBounds2D();
+                    if (bounds.isEmpty()) {
+                        // Apple JRE 1.6 doesn't like drawing empty shapes.
+                        // http://b.android.com/178278
+
+                        if (pathDelegate.isEmpty()) {
+                            // This means that the path doesn't have any lines or curves so
+                            // nothing to draw.
+                            return;
+                        }
+
+                        // The stroke width is not consider for the size of the bounds so,
+                        // for example, a horizontal line, would be considered as an empty
+                        // rectangle.
+                        // If the strokeWidth is not 0, we use it to consider the size of the
+                        // path as well.
+                        float strokeWidth = paintDelegate.getStrokeWidth();
+                        if (strokeWidth <= 0.0f) {
+                            return;
+                        }
+                        bounds.setRect(bounds.getX(), bounds.getY(),
+                                Math.max(strokeWidth, bounds.getWidth()),
+                                Math.max(strokeWidth, bounds.getHeight()));
+                    }
+
+                    int style = paintDelegate.getStyle();
+
+                    if (style == Paint.Style.FILL.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.fill(shape);
+                    }
+
+                    if (style == Paint.Style.STROKE.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.draw(shape);
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawRegion(long nativeCanvas, long nativeRegion,
+            long nativePaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Some canvas paths may not be drawn", null, null);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawNinePatch(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) {
+
+        // get the delegate from the native int.
+        final Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        byte[] c = NinePatch_Delegate.getChunk(ninePatch);
+        if (c == null) {
+            // not a 9-patch?
+            BufferedImage image = bitmapDelegate.getImage();
+            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(),
+                    image.getHeight(), (int) dstLeft, (int) dstTop, (int) dstRight,
+                    (int) dstBottom);
+            return;
+        }
+
+        final NinePatchChunk chunkObject = NinePatch_Delegate.getChunk(c);
+        assert chunkObject != null;
+        if (chunkObject == null) {
+            return;
+        }
+
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // this one can be null
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
+
+        canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+            @Override
+            public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                chunkObject.draw(bitmapDelegate.getImage(), graphics, (int) dstLeft, (int) dstTop,
+                        (int) (dstRight - dstLeft), (int) (dstBottom - dstTop), screenDensity,
+                        bitmapDensity);
+            }
+        }, paintDelegate, true, false);
+
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmapMatrix(long nCanvas, Bitmap bitmap,
+            long nMatrix, long nPaint) {
+        // get the delegate from the native int.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the delegate from the native int, which can be null
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
+
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
+
+        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
+        if (matrixDelegate == null) {
+            return;
+        }
+
+        final AffineTransform mtx = matrixDelegate.getAffineTransform();
+
+        canvasDelegate.getSnapshot().draw((graphics, paint) -> {
+            if (paint != null && paint.isFilterBitmap()) {
+                graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+            }
+
+            //FIXME add support for canvas, screen and bitmap densities.
+            graphics.drawImage(image, mtx, null);
+        }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmapMesh(long nCanvas, Bitmap bitmap,
+            int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
+            int colorOffset, long nPaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawVertices(long nCanvas, int mode, int n,
+            float[] verts, int vertOffset,
+            float[] texs, int texOffset,
+            int[] colors, int colorOffset,
+            short[] indices, int indexOffset,
+            int indexCount, long nPaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawVertices is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawText(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 nDrawText(long nativeCanvas, String text,
+            int start, int end, float x, float y, final int flags, long paint,
+            long typeface) {
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        nDrawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawTextRun(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;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawTextRun(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 nDrawTextOnPath(long nativeCanvas,
+            char[] text, int index,
+            int count, long path,
+            float hOffset,
+            float vOffset, int bidiFlags,
+            long paint, long typeface) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawTextOnPath(long nativeCanvas,
+            String text, long path,
+            float hOffset,
+            float vOffset,
+            int bidiFlags, long paint,
+            long typeface) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /**
+     * Executes a {@link GcSnapshot.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, Paint_Delegate, int)}.
+     */
+    private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
+            GcSnapshot.Drawable drawable) {
+        // get the delegate from the native int.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the paint which can be null if nPaint is 0;
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
+
+        canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
+    }
+
+    /**
+     * Executes a {@link GcSnapshot.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, Paint_Delegate, int)}.
+     */
+    private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
+        // get the delegate from the native int.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.mSnapshot.draw(drawable);
+    }
+
+    private static void drawText(long nativeCanvas, final char[] text, final int index,
+            final int count, final float startX, final float startY, final boolean isRtl,
+            long paint, final long typeface) {
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> {
+                    // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
+                    // Any change to this method should be reflected in Paint.measureText
+
+                    // assert that the typeface passed is actually the one stored in paint.
+                    assert (typeface == paintDelegate.mNativeTypeface);
+
+                    // Paint.TextAlign indicates how the text is positioned relative to X.
+                    // LEFT is the default and there's nothing to do.
+                    float x = startX;
+                    int limit = index + count;
+                    if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
+                        RectF bounds =
+                                paintDelegate.measureText(text, index, count, null, 0, isRtl);
+                        float m = bounds.right - bounds.left;
+                        if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
+                            x -= m / 2;
+                        } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
+                            x -= m;
+                        }
+                    }
+
+                    new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x,
+                            startY).renderText(index, limit, isRtl, null, 0, true);
+                });
+    }
+
+    private static void drawBitmap(long nativeCanvas, Bitmap_Delegate bitmap,
+            long nativePaintOrZero, 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.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the paint, which could be null if the int is 0
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
+
+        final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
+
+        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
+                (graphics, paint) -> {
+                    if (paint != null && paint.isFilterBitmap()) {
+                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+                    }
+
+                    //FIXME add support for canvas, screen and bitmap densities.
+                    graphics.drawImage(image, dleft, dtop, dright, dbottom, sleft, stop, sright,
+                            sbottom, null);
+                });
+    }
+
+    /**
+     * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
+     * The image returns, through a 1-size boolean array, whether the drawing code should
+     * use a SRC composite no matter what the paint says.
+     *
+     * @param bitmap the bitmap
+     * @param paint the paint that will be used to draw
+     * @param forceSrcMode whether the composite will have to be SRC
+     * @return the image to draw
+     */
+    private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
+            boolean[] forceSrcMode) {
+        BufferedImage image = bitmap.getImage();
+        forceSrcMode[0] = false;
+
+        // if the bitmap config is alpha_8, then we erase all color value from it
+        // before drawing it.
+        if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
+            fixAlpha8Bitmap(image);
+        } else if (!bitmap.hasAlpha()) {
+            // hasAlpha is merely a rendering hint. There can in fact be alpha values
+            // in the bitmap but it should be ignored at drawing time.
+            // There is two ways to do this:
+            // - override the composite to be SRC. This can only be used if the composite
+            //   was going to be SRC or SRC_OVER in the first place
+            // - Create a different bitmap to draw in which all the alpha channel values is set
+            //   to 0xFF.
+            if (paint != null) {
+                PorterDuff.Mode mode = PorterDuff.intToMode(paint.getPorterDuffMode());
+
+                forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER || mode == PorterDuff.Mode.SRC;
+            }
+
+            // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
+            if (!forceSrcMode[0]) {
+                image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
+            }
+        }
+
+        return image;
+    }
+
+    private static void fixAlpha8Bitmap(final BufferedImage image) {
+        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);
+    }
+
+    protected int save(int saveFlags) {
+        // get the current save count
+        int count = mSnapshot.size();
+
+        mSnapshot = mSnapshot.save(saveFlags);
+
+        // return the old save count
+        return count;
+    }
+
+    protected int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
+        Paint_Delegate paint = new Paint_Delegate();
+        paint.setAlpha(alpha);
+        return saveLayer(rect, paint, saveFlags);
+    }
+
+    protected 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;
+    }
+
+    /**
+     * Restores the {@link GcSnapshot} to <var>saveCount</var>
+     * @param saveCount the saveCount
+     */
+    protected void restoreTo(int saveCount) {
+        mSnapshot = mSnapshot.restoreTo(saveCount);
+    }
+
+    /**
+     * Restores the top {@link GcSnapshot}
+     */
+    protected void restore() {
+        mSnapshot = mSnapshot.restore();
+    }
+
+    protected boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
+        return mSnapshot.clipRect(left, top, right, bottom, regionOp);
+    }
+}
diff --git a/bridge/src/android/graphics/Canvas_Delegate.java b/bridge/src/android/graphics/Canvas_Delegate.java
index 94152cd..43a0ff5 100644
--- a/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/bridge/src/android/graphics/Canvas_Delegate.java
@@ -55,40 +55,27 @@
  * @see DelegateManager
  *
  */
-public final class Canvas_Delegate {
+public final class Canvas_Delegate extends BaseCanvas_Delegate {
 
     // ---- delegate manager ----
-    private static final DelegateManager<Canvas_Delegate> sManager =
-            new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
     private static long sFinalizer = -1;
 
-
-    // ---- delegate helper data ----
-
-    private final static boolean[] sBoolOut = new boolean[1];
-
-
-    // ---- delegate data ----
-    private Bitmap_Delegate mBitmap;
-    private GcSnapshot mSnapshot;
-
     private DrawFilter_Delegate mDrawFilter = null;
 
-
     // ---- Public Helper methods ----
 
     /**
      * Returns the native delegate associated to a given {@link Canvas} object.
      */
     public static Canvas_Delegate getDelegate(Canvas canvas) {
-        return sManager.getDelegate(canvas.getNativeCanvasWrapper());
+        return (Canvas_Delegate) sManager.getDelegate(canvas.getNativeCanvasWrapper());
     }
 
     /**
      * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
      */
     public static Canvas_Delegate getDelegate(long native_canvas) {
-        return sManager.getDelegate(native_canvas);
+        return (Canvas_Delegate) sManager.getDelegate(native_canvas);
     }
 
     /**
@@ -143,7 +130,7 @@
 
     @LayoutlibDelegate
     public static void nSetBitmap(long canvas, Bitmap bitmap) {
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
         if (canvasDelegate == null || bitmapDelegate==null) {
             return;
@@ -155,7 +142,7 @@
     @LayoutlibDelegate
     public static boolean nIsOpaque(long nativeCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return false;
         }
@@ -169,7 +156,7 @@
     @LayoutlibDelegate
     public static int nGetWidth(long nativeCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -180,7 +167,7 @@
     @LayoutlibDelegate
     public static int nGetHeight(long nativeCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -191,7 +178,7 @@
     @LayoutlibDelegate
     public static int nSave(long nativeCanvas, int saveFlags) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -204,7 +191,7 @@
                                                float t, float r, float b,
                                                long paint, int layerFlags) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -223,7 +210,7 @@
                                                     float t, float r, float b,
                                                     int alpha, int layerFlags) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -235,7 +222,7 @@
     public static void nRestore(long nativeCanvas, boolean throwOnUnderflow) {
         // FIXME: implement throwOnUnderflow.
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -248,7 +235,7 @@
             boolean throwOnUnderflow) {
         // FIXME: implement throwOnUnderflow.
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -259,7 +246,7 @@
     @LayoutlibDelegate
     public static int nGetSaveCount(long nativeCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -270,7 +257,7 @@
     @LayoutlibDelegate
    public static void nTranslate(long nativeCanvas, float dx, float dy) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -281,7 +268,7 @@
     @LayoutlibDelegate
        public static void nScale(long nativeCanvas, float sx, float sy) {
             // get the delegate from the native int.
-            Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
             if (canvasDelegate == null) {
                 return;
             }
@@ -292,7 +279,7 @@
     @LayoutlibDelegate
     public static void nRotate(long nativeCanvas, float degrees) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -303,7 +290,7 @@
     @LayoutlibDelegate
    public static void nSkew(long nativeCanvas, float kx, float ky) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -327,7 +314,7 @@
     @LayoutlibDelegate
     public static void nConcat(long nCanvas, long nMatrix) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -355,7 +342,7 @@
     @LayoutlibDelegate
     public static void nSetMatrix(long nCanvas, long nMatrix) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -388,7 +375,7 @@
                                                   float right, float bottom,
                                                   int regionOp) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
         if (canvasDelegate == null) {
             return false;
         }
@@ -400,7 +387,7 @@
     public static boolean nClipPath(long nativeCanvas,
                                                   long nativePath,
                                                   int regionOp) {
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return true;
         }
@@ -417,7 +404,7 @@
     public static boolean nClipRegion(long nativeCanvas,
                                                     long nativeRegion,
                                                     int regionOp) {
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return true;
         }
@@ -432,7 +419,7 @@
 
     @LayoutlibDelegate
     public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -449,7 +436,7 @@
     public static boolean nGetClipBounds(long nativeCanvas,
                                                        Rect bounds) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return false;
         }
@@ -469,7 +456,7 @@
     @LayoutlibDelegate
     public static void nGetCTM(long canvas, long matrix) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -498,509 +485,11 @@
     }
 
     @LayoutlibDelegate
-    public static void nDrawColor(long nativeCanvas, final int color, final int mode) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        final int w = canvasDelegate.mBitmap.getImage().getWidth();
-        final int h = canvasDelegate.mBitmap.getImage().getHeight();
-        draw(nativeCanvas, new GcSnapshot.Drawable() {
-
-            @Override
-            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*/));
-
-                Composite composite = PorterDuffUtility.getComposite(
-                        PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
-                if (composite != null) {
-                    graphics.setComposite(composite);
-                }
-
-                graphics.fillRect(0, 0, w, h);
-            }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawPaint(long nativeCanvas, long paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPaint is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawPoint(long nativeCanvas, float x, float y,
-            long nativePaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPoint is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawPoints(long nativeCanvas, float[] pts, int offset, int count,
-            long nativePaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPoint is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawLine(long nativeCanvas,
-            final float startX, final float startY, final float stopX, final float stopY,
-            long paint) {
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawLines(long nativeCanvas,
-            final float[] pts, final int offset, final int count,
-            long nativePaint) {
-        draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
-                false /*forceSrcMode*/, new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        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]);
-                        }
-                    }
-                });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawRect(long nativeCanvas,
-            final float left, final float top, final float right, final float bottom, long paint) {
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        int style = paintDelegate.getStyle();
-
-                        // draw
-                        if (style == Paint.Style.FILL.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            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) {
-                            graphics.drawRect((int)left, (int)top,
-                                    (int)(right-left), (int)(bottom-top));
-                        }
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawOval(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*/,
-                    new GcSnapshot.Drawable() {
-                        @Override
-                        public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                            int style = paintDelegate.getStyle();
-
-                            // draw
-                            if (style == Paint.Style.FILL.nativeInt ||
-                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.fillOval((int)left, (int)top,
-                                        (int)(right - left), (int)(bottom - top));
-                            }
-
-                            if (style == Paint.Style.STROKE.nativeInt ||
-                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.drawOval((int)left, (int)top,
-                                        (int)(right - left), (int)(bottom - top));
-                            }
-                        }
-            });
-        }
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawCircle(long nativeCanvas,
-            float cx, float cy, float radius, long paint) {
-        nDrawOval(nativeCanvas,
-                cx - radius, cy - radius, cx + radius, cy + radius,
-                paint);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawArc(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) {
-        if (right > left && bottom > top) {
-            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                    new GcSnapshot.Drawable() {
-                        @Override
-                        public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                            int style = paintDelegate.getStyle();
-
-                            Arc2D.Float arc = new Arc2D.Float(
-                                    left, top, right - left, bottom - top,
-                                    -startAngle, -sweep,
-                                    useCenter ? Arc2D.PIE : Arc2D.OPEN);
-
-                            // draw
-                            if (style == Paint.Style.FILL.nativeInt ||
-                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.fill(arc);
-                            }
-
-                            if (style == Paint.Style.STROKE.nativeInt ||
-                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.draw(arc);
-                            }
-                        }
-            });
-        }
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawRoundRect(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*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        int style = paintDelegate.getStyle();
-
-                        // draw
-                        if (style == Paint.Style.FILL.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.fillRoundRect(
-                                    (int)left, (int)top,
-                                    (int)(right - left), (int)(bottom - top),
-                                    2 * (int)rx, 2 * (int)ry);
-                        }
-
-                        if (style == Paint.Style.STROKE.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.drawRoundRect(
-                                    (int)left, (int)top,
-                                    (int)(right - left), (int)(bottom - top),
-                                    2 * (int)rx, 2 * (int)ry);
-                        }
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawPath(long nativeCanvas, long path, long paint) {
-        final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        Shape shape = pathDelegate.getJavaShape();
-                        Rectangle2D bounds = shape.getBounds2D();
-                        if (bounds.isEmpty()) {
-                            // Apple JRE 1.6 doesn't like drawing empty shapes.
-                            // http://b.android.com/178278
-
-                            if (pathDelegate.isEmpty()) {
-                                // This means that the path doesn't have any lines or curves so
-                                // nothing to draw.
-                                return;
-                            }
-
-                            // The stroke width is not consider for the size of the bounds so,
-                            // for example, a horizontal line, would be considered as an empty
-                            // rectangle.
-                            // If the strokeWidth is not 0, we use it to consider the size of the
-                            // path as well.
-                            float strokeWidth = paintDelegate.getStrokeWidth();
-                            if (strokeWidth <= 0.0f) {
-                                return;
-                            }
-                            bounds.setRect(bounds.getX(), bounds.getY(),
-                                    Math.max(strokeWidth, bounds.getWidth()),
-                                    Math.max(strokeWidth, bounds.getHeight()));
-                        }
-
-                        int style = paintDelegate.getStyle();
-
-                        if (style == Paint.Style.FILL.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.fill(shape);
-                        }
-
-                        if (style == Paint.Style.STROKE.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.draw(shape);
-                        }
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawRegion(long nativeCanvas, long nativeRegion,
-            long nativePaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Some canvas paths may not be drawn", null, null);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawNinePatch(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) {
-
-        // get the delegate from the native int.
-        final Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmap);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        byte[] c = NinePatch_Delegate.getChunk(ninePatch);
-        if (c == null) {
-            // not a 9-patch?
-            BufferedImage image = bitmapDelegate.getImage();
-            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(),
-                    image.getHeight(), (int) dstLeft, (int) dstTop, (int) dstRight,
-                    (int) dstBottom);
-            return;
-        }
-
-        final NinePatchChunk chunkObject = NinePatch_Delegate.getChunk(c);
-        assert chunkObject != null;
-        if (chunkObject == null) {
-            return;
-        }
-
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // this one can be null
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
-        canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
-            @Override
-            public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                chunkObject.draw(bitmapDelegate.getImage(), graphics, (int) dstLeft, (int) dstTop,
-                        (int) (dstRight - dstLeft), (int) (dstBottom - dstTop), screenDensity,
-                        bitmapDensity);
-            }
-        }, paintDelegate, true, false);
-
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap,
-                                                 float left, float top,
-                                                 long nativePaintOrZero,
-                                                 int canvasDensity,
-                                                 int screenDensity,
-                                                 int bitmapDensity) {
-        // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        BufferedImage image = bitmapDelegate.getImage();
-        float right = left + image.getWidth();
-        float bottom = top + image.getHeight();
-
-        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
-                0, 0, image.getWidth(), image.getHeight(),
-                (int)left, (int)top, (int)right, (int)bottom);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawBitmap(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) {
-        // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
-                (int)srcLeft, (int)srcTop, (int)srcRight, (int)srcBottom,
-                (int)dstLeft, (int)dstTop, (int)dstRight, (int)dstBottom);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawBitmap(long nativeCanvas, int[] colors,
-                                                int offset, int stride, final float x,
-                                                 final float y, int width, int height,
-                                                 boolean hasAlpha,
-                                                 long nativePaintOrZero) {
-        // create a temp BufferedImage containing the content.
-        final BufferedImage image = new BufferedImage(width, height,
-                hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
-        image.setRGB(0, 0, width, height, colors, offset, stride);
-
-        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                        if (paint != null && paint.isFilterBitmap()) {
-                            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-                        }
-
-                        graphics.drawImage(image, (int) x, (int) y, null);
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    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);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the delegate from the native int, which can be null
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
-        // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
-
-        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
-        if (matrixDelegate == null) {
-            return;
-        }
-
-        final AffineTransform mtx = matrixDelegate.getAffineTransform();
-
-        canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
-                @Override
-                public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                    if (paint != null && paint.isFilterBitmap()) {
-                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-                    }
-
-                    //FIXME add support for canvas, screen and bitmap densities.
-                    graphics.drawImage(image, mtx, null);
-                }
-        }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nativeDrawBitmapMesh(long nCanvas, Bitmap bitmap,
-            int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
-            int colorOffset, long nPaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nativeDrawVertices(long nCanvas, int mode, int n,
-            float[] verts, int vertOffset,
-            float[] texs, int texOffset,
-            int[] colors, int colorOffset,
-            short[] indices, int indexOffset,
-            int indexCount, long nPaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawVertices is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawText(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
-    public static void nDrawText(long nativeCanvas, String text,
-            int start, int end, float x, float y, final int flags, long paint,
-            long typeface) {
-        int count = end - start;
-        char[] buffer = TemporaryBuffer.obtain(count);
-        TextUtils.getChars(text, start, end, buffer, 0);
-
-        nDrawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawTextRun(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;
-        char[] buffer = TemporaryBuffer.obtain(count);
-        TextUtils.getChars(text, start, end, buffer, 0);
-
-        drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawTextRun(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
-    public static void nDrawTextOnPath(long nativeCanvas,
-                                                     char[] text, int index,
-                                                     int count, long path,
-                                                     float hOffset,
-                                                     float vOffset, int bidiFlags,
-                                                     long paint, long typeface) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawTextOnPath(long nativeCanvas,
-                                                     String text, long path,
-                                                     float hOffset,
-                                                     float vOffset,
-                                                     int bidiFlags, long paint,
-                                                     long typeface) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long getNativeFinalizer() {
+    /*package*/ static long nGetNativeFinalizer() {
         synchronized (Canvas_Delegate.class) {
             if (sFinalizer == -1) {
                 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(nativePtr -> {
-                    Canvas_Delegate delegate = sManager.getDelegate(nativePtr);
+                    Canvas_Delegate delegate = Canvas_Delegate.getDelegate(nativePtr);
                     if (delegate != null) {
                         delegate.dispose();
                     }
@@ -1011,230 +500,12 @@
         return sFinalizer;
     }
 
-    // ---- Private delegate/helper methods ----
-
-    /**
-     * Executes a {@link GcSnapshot.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, Paint_Delegate, int)}.
-     */
-    private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
-            GcSnapshot.Drawable drawable) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the paint which can be null if nPaint is 0;
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
-        canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
-    }
-
-    /**
-     * Executes a {@link GcSnapshot.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, Paint_Delegate, int)}.
-     */
-    private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.mSnapshot.draw(drawable);
-    }
-
-    private static void drawText(long nativeCanvas, final char[] text, final int index,
-            final int count, final float startX, final float startY, final boolean isRtl,
-            long paint, final long typeface) {
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-            @Override
-            public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
-                // Any change to this method should be reflected in Paint.measureText
-
-                // assert that the typeface passed is actually the one stored in paint.
-                assert (typeface == paintDelegate.mNativeTypeface);
-
-                // Paint.TextAlign indicates how the text is positioned relative to X.
-                // LEFT is the default and there's nothing to do.
-                float x = startX;
-                int limit = index + count;
-                if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
-                    RectF bounds = paintDelegate.measureText(text, index, count, null, 0,
-                            isRtl);
-                    float m = bounds.right - bounds.left;
-                    if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
-                        x -= m / 2;
-                    } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
-                        x -= m;
-                    }
-                }
-
-                new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x, startY)
-                        .renderText(index, limit, isRtl, null, 0, true);
-            }
-        });
-    }
-
     private Canvas_Delegate(Bitmap_Delegate bitmap) {
-        mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
+        super(bitmap);
     }
 
     private Canvas_Delegate() {
-        mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
-    }
-
-    /**
-     * Disposes of the {@link Graphics2D} stack.
-     */
-    private void dispose() {
-        mSnapshot.dispose();
-    }
-
-    private int save(int saveFlags) {
-        // get the current save count
-        int count = mSnapshot.size();
-
-        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;
-    }
-
-    /**
-     * Restores the {@link GcSnapshot} to <var>saveCount</var>
-     * @param saveCount the saveCount
-     */
-    private void restoreTo(int saveCount) {
-        mSnapshot = mSnapshot.restoreTo(saveCount);
-    }
-
-    /**
-     * Restores the top {@link GcSnapshot}
-     */
-    private void restore() {
-        mSnapshot = mSnapshot.restore();
-    }
-
-    private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
-        return mSnapshot.clipRect(left, top, right, bottom, regionOp);
-    }
-
-    private static void drawBitmap(
-            long nativeCanvas,
-            Bitmap_Delegate bitmap,
-            long nativePaintOrZero,
-            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) {
-            return;
-        }
-
-        // get the paint, which could be null if the int is 0
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
-        final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
-
-        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                        if (paint != null && paint.isFilterBitmap()) {
-                            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-                        }
-
-                        //FIXME add support for canvas, screen and bitmap densities.
-                        graphics.drawImage(image, dleft, dtop, dright, dbottom,
-                                sleft, stop, sright, sbottom, null);
-                    }
-        });
-    }
-
-
-    /**
-     * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
-     * The image returns, through a 1-size boolean array, whether the drawing code should
-     * use a SRC composite no matter what the paint says.
-     *
-     * @param bitmap the bitmap
-     * @param paint the paint that will be used to draw
-     * @param forceSrcMode whether the composite will have to be SRC
-     * @return the image to draw
-     */
-    private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
-            boolean[] forceSrcMode) {
-        BufferedImage image = bitmap.getImage();
-        forceSrcMode[0] = false;
-
-        // if the bitmap config is alpha_8, then we erase all color value from it
-        // before drawing it.
-        if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
-            fixAlpha8Bitmap(image);
-        } else if (!bitmap.hasAlpha()) {
-            // hasAlpha is merely a rendering hint. There can in fact be alpha values
-            // in the bitmap but it should be ignored at drawing time.
-            // There is two ways to do this:
-            // - override the composite to be SRC. This can only be used if the composite
-            //   was going to be SRC or SRC_OVER in the first place
-            // - Create a different bitmap to draw in which all the alpha channel values is set
-            //   to 0xFF.
-            if (paint != null) {
-                Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
-                if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
-                    PorterDuff.Mode mode =
-                        ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
-
-                    forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
-                            mode == PorterDuff.Mode.SRC;
-                }
-            }
-
-            // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
-            if (!forceSrcMode[0]) {
-                image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
-            }
-        }
-
-        return image;
-    }
-
-    private static void fixAlpha8Bitmap(final BufferedImage image) {
-        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);
+        super();
     }
 }
 
diff --git a/bridge/src/android/graphics/ComposeShader_Delegate.java b/bridge/src/android/graphics/ComposeShader_Delegate.java
index 59ddcc6..a459734 100644
--- a/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ b/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -63,16 +63,8 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static long nativeCreate1(long native_shaderA, long native_shaderB,
-            long native_mode) {
-        // FIXME not supported yet.
-        ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate2(long native_shaderA, long native_shaderB,
-            int porterDuffMode) {
+    /*package*/ static long nativeCreate(long native_shaderA, long native_shaderB,
+            int native_mode) {
         // FIXME not supported yet.
         ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
         return sManager.addNewDelegate(newDelegate);
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
index 0bbe33d..e68d8b3 100644
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ b/bridge/src/android/graphics/Paint_Delegate.java
@@ -90,10 +90,11 @@
     private int mHintingMode = Paint.HINTING_ON;
     private int mHyphenEdit;
     private float mLetterSpacing;  // not used in actual text rendering.
+    private float mWordSpacing;  // not used in actual text rendering.
     // Variant of the font. A paint's variant can only be compact or elegant.
     private FontVariant mFontVariant = FontVariant.COMPACT;
 
-    private Xfermode_Delegate mXfermode;
+    private int mPorterDuffMode = Xfermode.DEFAULT;
     private ColorFilter_Delegate mColorFilter;
     private Shader_Delegate mShader;
     private PathEffect_Delegate mPathEffect;
@@ -206,12 +207,10 @@
     }
 
     /**
-     * Returns the {@link Xfermode} delegate or null if none have been set
-     *
-     * @return the delegate or null.
+     * Returns the {@link PorterDuff.Mode} as an int
      */
-    public Xfermode_Delegate getXfermode() {
-        return mXfermode;
+    public int getPorterDuffMode() {
+        return mPorterDuffMode;
     }
 
     /**
@@ -841,16 +840,12 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static long nSetXfermode(long native_object, long xfermode) {
-        // get the delegate from the native int.
+    /*package*/ static void nSetXfermode(long native_object, int xfermode) {
         Paint_Delegate delegate = sManager.getDelegate(native_object);
         if (delegate == null) {
-            return xfermode;
+            return;
         }
-
-        delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode);
-
-        return xfermode;
+        delegate.mPorterDuffMode = xfermode;
     }
 
     @LayoutlibDelegate
@@ -998,7 +993,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(long native_object, char[] text,
+    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, char[] text,
             int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -1007,7 +1002,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(long native_object, String text,
+    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, String text,
             int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -1086,6 +1081,26 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static float nGetWordSpacing(long nativePaint) {
+        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+        return delegate.mWordSpacing;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nSetWordSpacing(long nativePaint, float wordSpacing) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
+                "Paint.setWordSpacing() not supported.", null, null);
+        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+        if (delegate == null) {
+            return;
+        }
+        delegate.mWordSpacing = wordSpacing;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static void nSetFontFeatureSettings(long nativePaint, String settings) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
                 "Paint.setFontFeatureSettings() not supported.", null, null);
@@ -1215,7 +1230,7 @@
 
         mStrokeWidth = paint.mStrokeWidth;
         mStrokeMiter = paint.mStrokeMiter;
-        mXfermode = paint.mXfermode;
+        mPorterDuffMode = paint.mPorterDuffMode;
         mColorFilter = paint.mColorFilter;
         mShader = paint.mShader;
         mPathEffect = paint.mPathEffect;
@@ -1242,7 +1257,7 @@
         mTextSize = 20.f;
         mTextScaleX = 1.f;
         mTextSkewX = 0.f;
-        mXfermode = null;
+        mPorterDuffMode = Xfermode.DEFAULT;
         mColorFilter = null;
         mShader = null;
         mPathEffect = null;
diff --git a/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
deleted file mode 100644
index 8825f84..0000000
--- a/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.PorterDuffUtility;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.PorterDuff.Mode;
-
-import java.awt.Composite;
-
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
-
-/**
- * Delegate implementing the native methods of android.graphics.PorterDuffXfermode
- *
- * Through the layoutlib_create tool, the original native methods of PorterDuffXfermode have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PorterDuffXfermode class.
- *
- * Because this extends {@link Xfermode_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
- * {@link Xfermode_Delegate}.
- *
- */
-public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
-
-    // ---- delegate data ----
-
-    private final Mode mMode;
-
-    // ---- Public Helper methods ----
-
-    public Mode getMode() {
-        return mMode;
-    }
-
-    @Override
-    public Composite getComposite(int alpha) {
-        return PorterDuffUtility.getComposite(mMode, alpha);
-    }
-
-    @Override
-    public boolean isSupported() {
-        return true;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        // no message since isSupported returns true;
-        return null;
-    }
-
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreateXfermode(int mode) {
-        PorterDuffXfermode_Delegate newDelegate = new PorterDuffXfermode_Delegate(mode);
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    private PorterDuffXfermode_Delegate(int mode) {
-        mMode = getPorterDuffMode(mode);
-    }
-
-}
diff --git a/bridge/src/android/graphics/Xfermode_Delegate.java b/bridge/src/android/graphics/Xfermode_Delegate.java
deleted file mode 100644
index 94a6d76..0000000
--- a/bridge/src/android/graphics/Xfermode_Delegate.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Composite;
-
-/**
- * Delegate implementing the native methods of android.graphics.Xfermode
- *
- * Through the layoutlib_create tool, the original native methods of Xfermode have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Xfermode class.
- *
- * This also serve as a base class for all Xfermode delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class Xfermode_Delegate {
-
-    // ---- delegate manager ----
-    protected static final DelegateManager<Xfermode_Delegate> sManager =
-            new DelegateManager<Xfermode_Delegate>(Xfermode_Delegate.class);
-
-    // ---- delegate helper data ----
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    public static Xfermode_Delegate getDelegate(long native_instance) {
-        return sManager.getDelegate(native_instance);
-    }
-
-    public abstract Composite getComposite(int alpha);
-    public abstract boolean isSupported();
-    public abstract String getSupportMessage();
-
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static void finalizer(long native_instance) {
-        sManager.removeJavaReferenceFor(native_instance);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-}
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
index 200fe3b..ad2c564 100644
--- a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
+++ b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
@@ -58,8 +58,13 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr) {
+        // TODO: implement
+    }
+    @LayoutlibDelegate
     /*package*/ static void nAddAnimator(long setPtr, long propertyValuesHolder,
-            long nativeInterpolator, long startDelay, long duration, int repeatCount) {
+            long nativeInterpolator, long startDelay, long duration, int repeatCount,
+            int repeatMode) {
         PropertySetter holder = sHolders.getDelegate(propertyValuesHolder);
         if (holder == null || holder.getValues() == null) {
             return;
@@ -72,6 +77,7 @@
         animator.setStartDelay(startDelay);
         animator.setDuration(duration);
         animator.setRepeatCount(repeatCount);
+        animator.setRepeatMode(repeatMode);
         animator.setTarget(holder);
         animator.setPropertyName(holder.getValues().getPropertyName());
 
@@ -137,6 +143,14 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static void nSetPropertyHolderData(long nativePtr, int[] data, int length) {
+        PropertySetter setter = sHolders.getDelegate(nativePtr);
+        assert setter != null;
+
+        setter.setValues(data);
+    }
+
+    @LayoutlibDelegate
     /*package*/ static void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
         AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
         assert animatorSet != null;
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
index 3d78931..fc848d9 100644
--- a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
+++ b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
@@ -18,6 +18,7 @@
 
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import android.graphics.Canvas;
 import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
 
 public class AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate {
@@ -25,4 +26,9 @@
     /*package*/ static boolean useLastSeenTarget(VectorDrawableAnimatorRT thisDrawableAnimator) {
         return true;
     }
+
+    @LayoutlibDelegate
+    /*package*/ static void onDraw(VectorDrawableAnimatorRT thisDrawableAnimator, Canvas canvas) {
+        // Do not attempt to record as we are not using a DisplayListCanvas
+    }
 }
diff --git a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index 9904263..cee679a 100644
--- a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -23,6 +23,7 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.BaseCanvas_Delegate;
 import android.graphics.Canvas_Delegate;
 import android.graphics.Color;
 import android.graphics.Matrix;
@@ -1197,7 +1198,7 @@
                     fillPaintDelegate.setColorFilter(filterPtr);
                     fillPaintDelegate.setShader(fullPath.mFillGradient);
                     Path_Delegate.native_setFillType(mRenderPath.mNativePath, fullPath.mFillType);
-                    Canvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
+                    BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
                             .getNativeInstance());
                 }
 
@@ -1228,7 +1229,7 @@
                     final float finalStrokeScale = minScale * matrixScale;
                     strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
                     strokePaintDelegate.setShader(fullPath.mStrokeGradient);
-                    Canvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
+                    BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
                             .getNativeInstance());
                 }
             }
diff --git a/bridge/src/android/os/ServiceManager.java b/bridge/src/android/os/ServiceManager.java
index 3e1ec07..c96d2ca 100644
--- a/bridge/src/android/os/ServiceManager.java
+++ b/bridge/src/android/os/ServiceManager.java
@@ -31,6 +31,13 @@
     }
 
     /**
+     * Is not supposed to return null, but that is fine for layoutlib.
+     */
+    public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
+        return null;
+    }
+
+    /**
      * Place a new @a service called @a name into the service
      * manager.
      *
diff --git a/bridge/src/android/os/SystemProperties_Delegate.java b/bridge/src/android/os/SystemProperties_Delegate.java
index af0c456..d299add 100644
--- a/bridge/src/android/os/SystemProperties_Delegate.java
+++ b/bridge/src/android/os/SystemProperties_Delegate.java
@@ -102,4 +102,9 @@
     /*package*/ static void native_add_change_callback() {
         // pass.
     }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_report_sysprop_change() {
+        // pass.
+    }
 }
diff --git a/bridge/src/android/view/RenderNode_Delegate.java b/bridge/src/android/view/RenderNode_Delegate.java
index 24f7887..a801cb0 100644
--- a/bridge/src/android/view/RenderNode_Delegate.java
+++ b/bridge/src/android/view/RenderNode_Delegate.java
@@ -21,6 +21,8 @@
 
 import android.graphics.Matrix;
 
+import libcore.util.NativeAllocationRegistry_Delegate;
+
 /**
  * Delegate implementing the native methods of {@link RenderNode}
  * <p/>
@@ -35,7 +37,7 @@
     // ---- delegate manager ----
     private static final DelegateManager<RenderNode_Delegate> sManager =
             new DelegateManager<RenderNode_Delegate>(RenderNode_Delegate.class);
-
+    private static long sFinalizer = -1;
 
     private float mLift;
     private float mTranslationX;
@@ -62,8 +64,13 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nDestroyRenderNode(long renderNode) {
-        sManager.removeJavaReferenceFor(renderNode);
+    /*package*/ static long nGetNativeFinalizer() {
+        synchronized (RenderNode_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
     }
 
     @LayoutlibDelegate