More implementation of the layoutlib Paint/Canvas delegates.

Change-Id: I0c0029b9a679af4ae0178488f70b2a90292ea42d
diff --git a/bridge/src/android/graphics/Canvas_Delegate.java b/bridge/src/android/graphics/Canvas_Delegate.java
index 6627d37..2b54711 100644
--- a/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/bridge/src/android/graphics/Canvas_Delegate.java
@@ -19,10 +19,22 @@
 import com.android.layoutlib.api.ILayoutLog;
 import com.android.layoutlib.bridge.DelegateManager;
 
+import android.graphics.Paint_Delegate.FontInfo;
+import android.text.TextUtils;
+
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Composite;
 import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
 import java.awt.image.BufferedImage;
+import java.util.List;
 import java.util.Stack;
 
+
 /**
  * Delegate implementing the native methods of android.graphics.Canvas
  *
@@ -95,80 +107,177 @@
     }
 
     /*package*/ static int getWidth(Canvas thisCanvas) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return canvasDelegate.mBufferedImage.getWidth();
     }
 
     /*package*/ static int getHeight(Canvas thisCanvas) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return canvasDelegate.mBufferedImage.getHeight();
     }
 
     /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.getGraphics2d().translate(dx, dy);
     }
 
     /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.getGraphics2d().rotate(Math.toRadians(degrees));
     }
 
     /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.getGraphics2d().scale(sx, sy);
     }
 
-    /*package*/ static void skew(Canvas thisCanvas, float sx, float sy) {
-        // FIXME
-        throw new UnsupportedOperationException();
+    /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the current top graphics2D object.
+        Graphics2D g = canvasDelegate.getGraphics2d();
+
+        // get its current matrix
+        AffineTransform currentTx = g.getTransform();
+        // get the AffineTransform for the given skew.
+        float[] mtx = Matrix_Delegate.getSkew(kx, ky);
+        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
+
+        // combine them so that the given matrix is applied after.
+        currentTx.preConcatenate(matrixTx);
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        g.setTransform(currentTx);
     }
 
     /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
     }
 
     /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
     }
 
     /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
             float bottom) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return false;
+        }
+
+        canvasDelegate.getGraphics2d().clipRect((int)left, (int)top, (int)(right-left),
+                (int)(bottom-top));
+        return true;
     }
 
     /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
             int bottom) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return false;
+        }
+
+        canvasDelegate.getGraphics2d().clipRect(left, top, right - left, bottom - top);
+        return true;
     }
 
     /*package*/ static int save(Canvas thisCanvas) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return 0;
+        }
+
+        // get the current save count
+        int count = canvasDelegate.mGraphicsStack.size();
+
+        // create a new graphics and add it to the stack
+        Graphics2D g = (Graphics2D)canvasDelegate.getGraphics2d().create();
+        canvasDelegate.mGraphicsStack.push(g);
+
+        // return the old save count
+        return count;
+
     }
 
     /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // FIXME implement save(flags)
+        return save(thisCanvas);
     }
 
     /*package*/ static void restore(Canvas thisCanvas) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.mGraphicsStack.pop();
     }
 
     /*package*/ static int getSaveCount(Canvas thisCanvas) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return canvasDelegate.mGraphicsStack.size();
     }
 
     /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        while (canvasDelegate.mGraphicsStack.size() > saveCount) {
+            canvasDelegate.mGraphicsStack.pop();
+        }
     }
 
     /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
@@ -296,8 +405,22 @@
 
     /*package*/ static boolean native_getClipBounds(int nativeCanvas,
                                                        Rect bounds) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return false;
+        }
+
+        Rectangle rect = canvasDelegate.getGraphics2d().getClipBounds();
+        if (rect != null) {
+            bounds.left = rect.x;
+            bounds.top = rect.y;
+            bounds.right = rect.x + rect.width;
+            bounds.bottom = rect.y + rect.height;
+            return true;
+        }
+        return false;
     }
 
     /*package*/ static void native_getCTM(int canvas, int matrix) {
@@ -309,14 +432,14 @@
                                                      RectF rect,
                                                      int native_edgeType) {
         // FIXME
-        throw new UnsupportedOperationException();
+        return false;
     }
 
     /*package*/ static boolean native_quickReject(int nativeCanvas,
                                                      int path,
                                                      int native_edgeType) {
         // FIXME
-        throw new UnsupportedOperationException();
+        return false;
     }
 
     /*package*/ static boolean native_quickReject(int nativeCanvas,
@@ -324,7 +447,7 @@
                                                      float right, float bottom,
                                                      int native_edgeType) {
         // FIXME
-        throw new UnsupportedOperationException();
+        return false;
     }
 
     /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g,
@@ -371,8 +494,41 @@
     /*package*/ static void native_drawRect(int nativeCanvas, float left,
                                                float top, float right,
                                                float bottom, int paint) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+        if (paintDelegate == null) {
+            assert false;
+            return;
+        }
+
+        if (right > left && bottom > top) {
+            // get a Graphics2D object configured with the drawing parameters.
+            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+
+            int style = paintDelegate.getStyle();
+
+            // draw
+            if (style == Paint.Style.FILL.nativeInt ||
+                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
+            }
+
+            if (style == Paint.Style.STROKE.nativeInt ||
+                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
+            }
+
+            // dispose Graphics2D object
+            g.dispose();
+        }
+
     }
 
     /*package*/ static void native_drawOval(int nativeCanvas, RectF oval,
@@ -423,8 +579,24 @@
                                                  int nativePaintOrZero,
                                                  int screenDensity,
                                                  int bitmapDensity) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            assert false;
+            return;
+        }
+
+        BufferedImage image = bitmapDelegate.getImage();
+
+        if (src == null) {
+            drawBitmap(nativeCanvas, image, nativePaintOrZero,
+                    0, 0, image.getWidth(), image.getHeight(),
+                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
+        } else {
+            drawBitmap(nativeCanvas, image, nativePaintOrZero,
+                    src.left, src.top, src.width(), src.height(),
+                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
+        }
     }
 
     /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
@@ -432,8 +604,24 @@
                                                  int nativePaintOrZero,
                                                  int screenDensity,
                                                  int bitmapDensity) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            assert false;
+            return;
+        }
+
+        BufferedImage image = bitmapDelegate.getImage();
+
+        if (src == null) {
+            drawBitmap(nativeCanvas, image, nativePaintOrZero,
+                    0, 0, image.getWidth(), image.getHeight(),
+                    dst.left, dst.top, dst.right, dst.bottom);
+        } else {
+            drawBitmap(nativeCanvas, image, nativePaintOrZero,
+                    src.left, src.top, src.width(), src.height(),
+                    dst.left, dst.top, dst.right, dst.bottom);
+        }
     }
 
     /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
@@ -471,15 +659,134 @@
     /*package*/ static void native_drawText(int nativeCanvas, char[] text,
                                                int index, int count, float x,
                                                float y, int flags, int paint) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // WARNING: the logic in this method is similar to Paint.measureText.
+        // Any change to this method should be reflected in Paint.measureText
+
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+        if (paintDelegate == null) {
+            assert false;
+            return;
+        }
+
+        Graphics2D g = canvasDelegate.getGraphics2d();
+
+        g = (Graphics2D)g.create();
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+        // set the color. because this only handles RGB, the alpha channel is handled
+        // as a composite.
+        g.setColor(new Color(paintDelegate.getColor()));
+        int alpha = paintDelegate.getAlpha();
+        float falpha = alpha / 255.f;
+        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
+
+
+        // Paint.TextAlign indicates how the text is positioned relative to X.
+        // LEFT is the default and there's nothing to do.
+        if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
+            float m = paintDelegate.measureText(text, index, count);
+            if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
+                x -= m / 2;
+            } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
+                x -= m;
+            }
+        }
+
+        List<FontInfo> fonts = paintDelegate.getFonts();
+        try {
+            if (fonts.size() > 0) {
+                FontInfo mainFont = fonts.get(0);
+                int i = index;
+                int lastIndex = index + count;
+                while (i < lastIndex) {
+                    // always start with the main font.
+                    int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
+                    if (upTo == -1) {
+                        // draw all the rest and exit.
+                        g.setFont(mainFont.mFont);
+                        g.drawChars(text, i, lastIndex - i, (int)x, (int)y);
+                        return;
+                    } else if (upTo > 0) {
+                        // draw what's possible
+                        g.setFont(mainFont.mFont);
+                        g.drawChars(text, i, upTo - i, (int)x, (int)y);
+
+                        // compute the width that was drawn to increase x
+                        x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
+
+                        // move index to the first non displayed char.
+                        i = upTo;
+
+                        // don't call continue at this point. Since it is certain the main font
+                        // cannot display the font a index upTo (now ==i), we move on to the
+                        // fallback fonts directly.
+                    }
+
+                    // no char supported, attempt to read the next char(s) with the
+                    // fallback font. In this case we only test the first character
+                    // and then go back to test with the main font.
+                    // Special test for 2-char characters.
+                    boolean foundFont = false;
+                    for (int f = 1 ; f < fonts.size() ; f++) {
+                        FontInfo fontInfo = fonts.get(f);
+
+                        // need to check that the font can display the character. We test
+                        // differently if the char is a high surrogate.
+                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                        upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
+                        if (upTo == -1) {
+                            // draw that char
+                            g.setFont(fontInfo.mFont);
+                            g.drawChars(text, i, charCount, (int)x, (int)y);
+
+                            // update x
+                            x += fontInfo.mMetrics.charsWidth(text, i, charCount);
+
+                            // update the index in the text, and move on
+                            i += charCount;
+                            foundFont = true;
+                            break;
+
+                        }
+                    }
+
+                    // in case no font can display the char, display it with the main font.
+                    // (it'll put a square probably)
+                    if (foundFont == false) {
+                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+
+                        g.setFont(mainFont.mFont);
+                        g.drawChars(text, i, charCount, (int)x, (int)y);
+
+                        // measure it to advance x
+                        x += mainFont.mMetrics.charsWidth(text, i, charCount);
+
+                        // and move to the next chars.
+                        i += charCount;
+                    }
+                }
+            }
+        } finally {
+            g.dispose();
+        }
     }
 
     /*package*/ static void native_drawText(int nativeCanvas, String text,
                                                int start, int end, float x,
                                                float y, int flags, int paint) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
     }
 
 
@@ -556,4 +863,141 @@
         mBufferedImage = image;
         mGraphicsStack.push(mBufferedImage.createGraphics());
     }
+
+    /**
+     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
+     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
+     */
+    private Graphics2D getCustomGraphics(Paint_Delegate paint) {
+        // make new one
+        Graphics2D g = getGraphics2d();
+        g = (Graphics2D)g.create();
+
+        // configure it
+        g.setColor(new Color(paint.getColor()));
+        int alpha = paint.getAlpha();
+        float falpha = alpha / 255.f;
+
+        int style = paint.getStyle();
+        if (style == Paint.Style.STROKE.nativeInt ||
+                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+            /* FIXME
+            PathEffect e = paint.getPathEffect();
+            if (e instanceof DashPathEffect) {
+                DashPathEffect dpe = (DashPathEffect)e;
+                g.setStroke(new BasicStroke(
+                        paint.getStrokeWidth(),
+                        paint.getStrokeCap().getJavaCap(),
+                        paint.getStrokeJoin().getJavaJoin(),
+                        paint.getStrokeMiter(),
+                        dpe.getIntervals(),
+                        dpe.getPhase()));
+            } else {*/
+                g.setStroke(new BasicStroke(
+                        paint.getStrokeWidth(),
+                        paint.getJavaCap(),
+                        paint.getJavaJoin(),
+                        paint.getStrokeMiter()));
+          /*  }*/
+        }
+/*
+        Xfermode xfermode = paint.getXfermode();
+        if (xfermode instanceof PorterDuffXfermode) {
+            PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode();
+
+            setModeInGraphics(mode, g, falpha);
+        } else {
+            if (mLogger != null && xfermode != null) {
+                mLogger.warning(String.format(
+                        "Xfermode '%1$s' is not supported in the Layout Editor.",
+                        xfermode.getClass().getCanonicalName()));
+            }
+            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
+        }
+
+        Shader shader = paint.getShader();
+        if (shader != null) {
+            java.awt.Paint shaderPaint = shader.getJavaPaint();
+            if (shaderPaint != null) {
+                g.setPaint(shaderPaint);
+            } else {
+                if (mLogger != null) {
+                    mLogger.warning(String.format(
+                            "Shader '%1$s' is not supported in the Layout Editor.",
+                            shader.getClass().getCanonicalName()));
+                }
+            }
+        }
+*/
+        return g;
+    }
+
+
+    private static void drawBitmap(
+            int nativeCanvas,
+            BufferedImage image,
+            int nativePaintOrZero,
+            int sleft, int stop, int sright, int sbottom,
+            int dleft, int dtop, int dright, int dbottom) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate paintDelegate = null;
+        if (nativePaintOrZero > 0) {
+            paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
+            if (paintDelegate == null) {
+                assert false;
+                return;
+            }
+        }
+
+        drawBitmap(canvasDelegate, image, paintDelegate,
+                sleft, stop, sright, sbottom,
+                dleft, dtop, dright, dbottom);
+    }
+
+    private static void drawBitmap(
+            Canvas_Delegate canvasDelegate,
+            BufferedImage image,
+            Paint_Delegate paintDelegate,
+            int sleft, int stop, int sright, int sbottom,
+            int dleft, int dtop, int dright, int dbottom) {
+
+        Graphics2D g = canvasDelegate.getGraphics2d();
+
+        Composite c = null;
+
+        if (paintDelegate != null) {
+            if (paintDelegate.isFilterBitmap()) {
+                g = (Graphics2D)g.create();
+                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+            }
+
+            if (paintDelegate.getAlpha() != 0xFF) {
+                c = g.getComposite();
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+                        paintDelegate.getAlpha()/255.f));
+            }
+        }
+
+        g.drawImage(image, dleft, dtop, dright, dbottom,
+                sleft, stop, sright, sbottom, null);
+
+        if (paintDelegate != null) {
+            if (paintDelegate.isFilterBitmap()) {
+                g.dispose();
+            }
+            if (c != null) {
+                g.setComposite(c);
+            }
+        }
+    }
+
 }
+
diff --git a/bridge/src/android/graphics/Matrix_Delegate.java b/bridge/src/android/graphics/Matrix_Delegate.java
index cc4a80c..713f784 100644
--- a/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/bridge/src/android/graphics/Matrix_Delegate.java
@@ -689,13 +689,17 @@
     // ---- Private helper methods ----
 
     private static AffineTransform getAffineTransform(Matrix_Delegate d) {
+        return getAffineTransform(d.mValues);
+    }
+
+    /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
         // the AffineTransform constructor takes the value in a different order
         // for a matrix [ 0 1 2 ]
         //              [ 3 4 5 ]
         // the order is 0, 3, 1, 4, 2, 5...
         return new AffineTransform(
-                d.mValues[0], d.mValues[3], d.mValues[1],
-                d.mValues[4], d.mValues[2], d.mValues[5]);
+                matrix[0], matrix[3], matrix[1],
+                matrix[4], matrix[2], matrix[5]);
     }
 
 
@@ -862,7 +866,7 @@
      * <p/>This in effect does dest = a*b
      * dest cannot be the same as a or b.
      */
-    private static void multiply(float dest[], float[] a, float[] b) {
+     /*package*/ static void multiply(float dest[], float[] a, float[] b) {
         // first row
         dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
         dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
@@ -885,11 +889,11 @@
      * @param dy
      * @return
      */
-    private static float[] getTranslate(float dx, float dy) {
+    /*package*/ static float[] getTranslate(float dx, float dy) {
         return setTranslate(new float[9], dx, dy);
     }
 
-    private static float[] setTranslate(float[] dest, float dx, float dy) {
+    /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
         dest[0] = 1;
         dest[1] = 0;
         dest[2] = dx;
@@ -902,7 +906,7 @@
         return dest;
     }
 
-    private static float[] getScale(float sx, float sy) {
+    /*package*/ static float[] getScale(float sx, float sy) {
         return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
     }
 
@@ -913,7 +917,7 @@
      * @param px
      * @param py
      */
-    private static float[] getScale(float sx, float sy, float px, float py) {
+    /*package*/ static float[] getScale(float sx, float sy, float px, float py) {
         float[] tmp = new float[9];
         float[] tmp2 = new float[9];
 
@@ -932,7 +936,7 @@
     }
 
 
-    private static float[] getRotate(float degrees) {
+    /*package*/ static float[] getRotate(float degrees) {
         double rad = Math.toRadians(degrees);
         float sin = (float)Math.sin(rad);
         float cos = (float)Math.cos(rad);
@@ -940,11 +944,11 @@
         return getRotate(sin, cos);
     }
 
-    private static float[] getRotate(float sin, float cos) {
+    /*package*/ static float[] getRotate(float sin, float cos) {
         return setRotate(new float[9], sin, cos);
     }
 
-    private static float[] setRotate(float[] dest, float degrees) {
+    /*package*/ static float[] setRotate(float[] dest, float degrees) {
         double rad = Math.toRadians(degrees);
         float sin = (float)Math.sin(rad);
         float cos = (float)Math.cos(rad);
@@ -952,7 +956,7 @@
         return setRotate(dest, sin, cos);
     }
 
-    private static float[] setRotate(float[] dest, float sin, float cos) {
+    /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
         dest[0] = cos;
         dest[1] = -sin;
         dest[2] = 0;
@@ -965,7 +969,7 @@
         return dest;
     }
 
-    private static float[] getRotate(float degrees, float px, float py) {
+    /*package*/ static float[] getRotate(float degrees, float px, float py) {
         float[] tmp = new float[9];
         float[] tmp2 = new float[9];
 
@@ -986,11 +990,11 @@
         return tmp;
     }
 
-    private static float[] getSkew(float kx, float ky) {
+    /*package*/ static float[] getSkew(float kx, float ky) {
         return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
     }
 
-    private static float[] getSkew(float kx, float ky, float px, float py) {
+    /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
         float[] tmp = new float[9];
         float[] tmp2 = new float[9];
 
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
index e8079ed..f2602b5 100644
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ b/bridge/src/android/graphics/Paint_Delegate.java
@@ -20,7 +20,9 @@
 
 import android.graphics.Paint.FontMetrics;
 import android.graphics.Paint.FontMetricsInt;
+import android.text.TextUtils;
 
+import java.awt.BasicStroke;
 import java.awt.Font;
 import java.awt.Toolkit;
 import java.awt.font.FontRenderContext;
@@ -47,7 +49,7 @@
     /**
      * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
      */
-    public static final class FontInfo {
+    /*package*/ static final class FontInfo {
         Font mFont;
         java.awt.FontMetrics mMetrics;
     }
@@ -67,7 +69,7 @@
     private int mStyle;
     private int mCap;
     private int mJoin;
-    private int mAlign;
+    private int mTextAlign;
     private int mTypeface;
     private float mStrokeWidth;
     private float mStrokeMiter;
@@ -78,6 +80,10 @@
 
     // ---- Public Helper methods ----
 
+    public static Paint_Delegate getDelegate(int native_paint) {
+        return sManager.getDelegate(native_paint);
+    }
+
     /**
      * Returns the list of {@link Font} objects. The first item is the main font, the rest
      * are fall backs for characters not present in the main font.
@@ -86,6 +92,57 @@
         return mFonts;
     }
 
+    public boolean isFilterBitmap() {
+        return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
+    }
+
+    public int getStyle() {
+        return mStyle;
+    }
+
+    public int getColor() {
+        return mColor;
+    }
+
+    public int getAlpha() {
+        return mColor >>> 24;
+    }
+
+    public int getTextAlign() {
+        return mTextAlign;
+    }
+
+    public float getStrokeWidth() {
+        return mStrokeWidth;
+    }
+
+    public float getStrokeMiter() {
+        return mStrokeMiter;
+    }
+
+    public int getJavaCap() {
+        switch (Paint.sCapArray[mCap]) {
+            case BUTT:
+                return BasicStroke.CAP_BUTT;
+            case ROUND:
+                return BasicStroke.CAP_ROUND;
+            default:
+            case SQUARE:
+                return BasicStroke.CAP_SQUARE;
+        }
+    }
+
+    public int getJavaJoin() {
+        switch (Paint.sJoinArray[mJoin]) {
+            default:
+            case MITER:
+                return BasicStroke.JOIN_MITER;
+            case ROUND:
+                return BasicStroke.JOIN_ROUND;
+            case BEVEL:
+                return BasicStroke.JOIN_BEVEL;
+        }
+    }
 
     // ---- native methods ----
 
@@ -112,8 +169,7 @@
     }
 
     /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter);
     }
 
     /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
@@ -174,7 +230,7 @@
             return 0;
         }
 
-        return delegate.mColor >>> 24;
+        return delegate.getAlpha();
     }
 
     /*package*/ static void setAlpha(Paint thisPaint, int a) {
@@ -315,13 +371,53 @@
     }
 
     /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+            if (metrics != null) {
+                // Android expects negative ascent so we invert the value from Java.
+                metrics.top = - javaMetrics.getMaxAscent();
+                metrics.ascent = - javaMetrics.getAscent();
+                metrics.descent = javaMetrics.getDescent();
+                metrics.bottom = javaMetrics.getMaxDescent();
+                metrics.leading = javaMetrics.getLeading();
+            }
+
+            return javaMetrics.getHeight();
+        }
+
+        return 0;
     }
 
     /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+            if (fmi != null) {
+                // Android expects negative ascent so we invert the value from Java.
+                fmi.top = - javaMetrics.getMaxAscent();
+                fmi.ascent = - javaMetrics.getAscent();
+                fmi.descent = javaMetrics.getDescent();
+                fmi.bottom = javaMetrics.getMaxDescent();
+                fmi.leading = javaMetrics.getLeading();
+            }
+
+            return javaMetrics.getHeight();
+        }
+
+        return 0;
     }
 
     /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
@@ -336,56 +432,7 @@
             return 0;
         }
 
-        if (delegate.mFonts.size() > 0) {
-            FontInfo mainFont = delegate.mFonts.get(0);
-            int i = index;
-            int lastIndex = index + count;
-            float total = 0f;
-            while (i < lastIndex) {
-                // always start with the main font.
-                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
-                if (upTo == -1) {
-                    // shortcut to exit
-                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
-                } else if (upTo > 0) {
-                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-                    i = upTo;
-                    // don't call continue at this point. Since it is certain the main font
-                    // cannot display the font a index upTo (now ==i), we move on to the
-                    // fallback fonts directly.
-                }
-
-                // no char supported, attempt to read the next char(s) with the
-                // fallback font. In this case we only test the first character
-                // and then go back to test with the main font.
-                // Special test for 2-char characters.
-                boolean foundFont = false;
-                for (int f = 1 ; f < delegate.mFonts.size() ; f++) {
-                    FontInfo fontInfo = delegate.mFonts.get(f);
-
-                    // need to check that the font can display the character. We test
-                    // differently if the char is a high surrogate.
-                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
-                    if (upTo == -1) {
-                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
-                        i += charCount;
-                        foundFont = true;
-                        break;
-
-                    }
-                }
-
-                // in case no font can display the char, measure it with the main font.
-                if (foundFont == false) {
-                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    total += mainFont.mMetrics.charsWidth(text, i, size);
-                    i += size;
-                }
-            }
-        }
-
-        return 0;
+        return delegate.measureText(text, index, count);
     }
 
     /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
@@ -576,7 +623,7 @@
             return 0;
         }
 
-        return delegate.mAlign;
+        return delegate.mTextAlign;
     }
 
     /*package*/ static void native_setTextAlign(int native_object, int align) {
@@ -587,7 +634,7 @@
             return;
         }
 
-        delegate.mAlign = align;
+        delegate.mTextAlign = align;
     }
 
     /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) {
@@ -610,15 +657,58 @@
     /*package*/ static float native_getTextRunAdvances(int native_object,
             char[] text, int index, int count, int contextIndex, int contextCount,
             int flags, float[] advances, int advancesIndex) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0.f;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            // FIXME: handle multi-char characters.
+            // see measureText.
+            float totalAdvance = 0;
+            for (int i = 0; i < count; i++) {
+                char c = text[i + index];
+                boolean found = false;
+                for (FontInfo info : delegate.mFonts) {
+                    if (info.mFont.canDisplay(c)) {
+                        float adv = info.mMetrics.charWidth(c);
+                        totalAdvance += adv;
+                        if (advances != null) {
+                            advances[i] = adv;
+                        }
+
+                        found = true;
+                        break;
+                    }
+                }
+
+                if (found == false) {
+                    // no advance for this char.
+                    if (advances != null) {
+                        advances[i] = 0.f;
+                    }
+                }
+            }
+
+            return totalAdvance;
+        }
+
+        return 0;
+
     }
 
     /*package*/ static float native_getTextRunAdvances(int native_object,
             String text, int start, int end, int contextStart, int contextEnd,
             int flags, float[] advances, int advancesIndex) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // FIXME: support contextStart, contextEnd and direction flag
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
+                contextEnd - contextStart, flags, advances, advancesIndex);
     }
 
     /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
@@ -681,7 +771,7 @@
         mStyle = paint.mStyle;
         mCap = paint.mCap;
         mJoin = paint.mJoin;
-        mAlign = paint.mAlign;
+        mTextAlign = paint.mTextAlign;
         mTypeface = paint.mTypeface;
         mStrokeWidth = paint.mStrokeWidth;
         mStrokeMiter = paint.mStrokeMiter;
@@ -696,7 +786,7 @@
         mStyle = 0;
         mCap = 0;
         mJoin = 0;
-        mAlign = 0;
+        mTextAlign = 0;
         mTypeface = 0;
         mStrokeWidth = 1.f;
         mStrokeMiter = 2.f;
@@ -733,6 +823,61 @@
         }
     }
 
+    /*package*/ float measureText(char[] text, int index, int count) {
+        if (mFonts.size() > 0) {
+            FontInfo mainFont = mFonts.get(0);
+            int i = index;
+            int lastIndex = index + count;
+            float total = 0f;
+            while (i < lastIndex) {
+                // always start with the main font.
+                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
+                if (upTo == -1) {
+                    // shortcut to exit
+                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
+                } else if (upTo > 0) {
+                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
+                    i = upTo;
+                    // don't call continue at this point. Since it is certain the main font
+                    // cannot display the font a index upTo (now ==i), we move on to the
+                    // fallback fonts directly.
+                }
+
+                // no char supported, attempt to read the next char(s) with the
+                // fallback font. In this case we only test the first character
+                // and then go back to test with the main font.
+                // Special test for 2-char characters.
+                boolean foundFont = false;
+                for (int f = 1 ; f < mFonts.size() ; f++) {
+                    FontInfo fontInfo = mFonts.get(f);
+
+                    // need to check that the font can display the character. We test
+                    // differently if the char is a high surrogate.
+                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
+                    if (upTo == -1) {
+                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
+                        i += charCount;
+                        foundFont = true;
+                        break;
+
+                    }
+                }
+
+                // in case no font can display the char, measure it with the main font.
+                if (foundFont == false) {
+                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                    total += mainFont.mMetrics.charsWidth(text, i, size);
+                    i += size;
+                }
+            }
+        }
+
+        return 0;
+
+    }
+
+
     private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);