Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.graphics;
     18 
     19 import com.android.ide.common.rendering.api.LayoutLog;
     20 import com.android.layoutlib.bridge.Bridge;
     21 import com.android.layoutlib.bridge.impl.DelegateManager;
     22 import com.android.layoutlib.bridge.impl.GcSnapshot;
     23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
     24 
     25 import android.graphics.Bitmap.Config;
     26 import android.graphics.Paint_Delegate.FontInfo;
     27 import android.text.TextUtils;
     28 
     29 import java.awt.Color;
     30 import java.awt.Composite;
     31 import java.awt.Graphics2D;
     32 import java.awt.Rectangle;
     33 import java.awt.RenderingHints;
     34 import java.awt.Shape;
     35 import java.awt.geom.AffineTransform;
     36 import java.awt.geom.Arc2D;
     37 import java.awt.image.BufferedImage;
     38 import java.util.List;
     39 
     40 
     41 /**
     42  * Delegate implementing the native methods of android.graphics.Canvas
     43  *
     44  * Through the layoutlib_create tool, the original native methods of Canvas have been replaced
     45  * by calls to methods of the same name in this delegate class.
     46  *
     47  * This class behaves like the original native implementation, but in Java, keeping previously
     48  * native data into its own objects and mapping them to int that are sent back and forth between
     49  * it and the original Canvas class.
     50  *
     51  * @see DelegateManager
     52  *
     53  */
     54 public final class Canvas_Delegate {
     55 
     56     // ---- delegate manager ----
     57     private static final DelegateManager<Canvas_Delegate> sManager =
     58             new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
     59 
     60     // ---- delegate helper data ----
     61 
     62     private final static boolean[] sBoolOut = new boolean[1];
     63 
     64     // ---- delegate data ----
     65     private Bitmap_Delegate mBitmap;
     66     private GcSnapshot mSnapshot;
     67 
     68     private DrawFilter_Delegate mDrawFilter = null;
     69 
     70     // ---- Public Helper methods ----
     71 
     72     /**
     73      * Returns the native delegate associated to a given {@link Canvas} object.
     74      */
     75     public static Canvas_Delegate getDelegate(Canvas canvas) {
     76         return sManager.getDelegate(canvas.mNativeCanvas);
     77     }
     78 
     79     /**
     80      * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
     81      */
     82     public static Canvas_Delegate getDelegate(int native_canvas) {
     83         return sManager.getDelegate(native_canvas);
     84     }
     85 
     86     /**
     87      * Returns the current {@link Graphics2D} used to draw.
     88      */
     89     public GcSnapshot getSnapshot() {
     90         return mSnapshot;
     91     }
     92 
     93     /**
     94      * Returns the {@link DrawFilter} delegate or null if none have been set.
     95      *
     96      * @return the delegate or null.
     97      */
     98     public DrawFilter_Delegate getDrawFilter() {
     99         return mDrawFilter;
    100     }
    101 
    102     // ---- native methods ----
    103 
    104     @LayoutlibDelegate
    105     /*package*/ static boolean isOpaque(Canvas thisCanvas) {
    106         // get the delegate from the native int.
    107         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    108         if (canvasDelegate == null) {
    109             return false;
    110         }
    111 
    112         return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
    113     }
    114 
    115     @LayoutlibDelegate
    116     /*package*/ static int getWidth(Canvas thisCanvas) {
    117         // get the delegate from the native int.
    118         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    119         if (canvasDelegate == null) {
    120             return 0;
    121         }
    122 
    123         return canvasDelegate.mBitmap.getImage().getWidth();
    124     }
    125 
    126     @LayoutlibDelegate
    127     /*package*/ static int getHeight(Canvas thisCanvas) {
    128         // get the delegate from the native int.
    129         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    130         if (canvasDelegate == null) {
    131             return 0;
    132         }
    133 
    134         return canvasDelegate.mBitmap.getImage().getHeight();
    135     }
    136 
    137     @LayoutlibDelegate
    138    /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
    139         // get the delegate from the native int.
    140         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    141         if (canvasDelegate == null) {
    142             return;
    143         }
    144 
    145         canvasDelegate.getSnapshot().translate(dx, dy);
    146     }
    147 
    148     @LayoutlibDelegate
    149     /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
    150         // get the delegate from the native int.
    151         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    152         if (canvasDelegate == null) {
    153             return;
    154         }
    155 
    156         canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
    157     }
    158 
    159     @LayoutlibDelegate
    160    /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
    161         // get the delegate from the native int.
    162         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    163         if (canvasDelegate == null) {
    164             return;
    165         }
    166 
    167         canvasDelegate.getSnapshot().scale(sx, sy);
    168     }
    169 
    170     @LayoutlibDelegate
    171    /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
    172         // get the delegate from the native int.
    173         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    174         if (canvasDelegate == null) {
    175             return;
    176         }
    177 
    178         // get the current top graphics2D object.
    179         GcSnapshot g = canvasDelegate.getSnapshot();
    180 
    181         // get its current matrix
    182         AffineTransform currentTx = g.getTransform();
    183         // get the AffineTransform for the given skew.
    184         float[] mtx = Matrix_Delegate.getSkew(kx, ky);
    185         AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
    186 
    187         // combine them so that the given matrix is applied after.
    188         currentTx.preConcatenate(matrixTx);
    189 
    190         // give it to the graphics2D as a new matrix replacing all previous transform
    191         g.setTransform(currentTx);
    192     }
    193 
    194     @LayoutlibDelegate
    195     /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
    196         return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
    197     }
    198 
    199     @LayoutlibDelegate
    200     /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
    201         return clipRect(thisCanvas, (float) rect.left, (float) rect.top,
    202                 (float) rect.right, (float) rect.bottom);
    203     }
    204 
    205     @LayoutlibDelegate
    206     /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
    207             float bottom) {
    208         // get the delegate from the native int.
    209         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    210         if (canvasDelegate == null) {
    211             return false;
    212         }
    213 
    214         return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
    215     }
    216 
    217     @LayoutlibDelegate
    218     /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
    219             int bottom) {
    220 
    221         return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom);
    222     }
    223 
    224     @LayoutlibDelegate
    225     /*package*/ static int save(Canvas thisCanvas) {
    226         return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG);
    227     }
    228 
    229     @LayoutlibDelegate
    230     /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
    231         // get the delegate from the native int.
    232         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    233         if (canvasDelegate == null) {
    234             return 0;
    235         }
    236 
    237         return canvasDelegate.save(saveFlags);
    238     }
    239 
    240     @LayoutlibDelegate
    241     /*package*/ static void restore(Canvas thisCanvas) {
    242         // get the delegate from the native int.
    243         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    244         if (canvasDelegate == null) {
    245             return;
    246         }
    247 
    248         canvasDelegate.restore();
    249     }
    250 
    251     @LayoutlibDelegate
    252     /*package*/ static int getSaveCount(Canvas thisCanvas) {
    253         // get the delegate from the native int.
    254         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    255         if (canvasDelegate == null) {
    256             return 0;
    257         }
    258 
    259         return canvasDelegate.getSnapshot().size();
    260     }
    261 
    262     @LayoutlibDelegate
    263     /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
    264         // get the delegate from the native int.
    265         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
    266         if (canvasDelegate == null) {
    267             return;
    268         }
    269 
    270         canvasDelegate.restoreTo(saveCount);
    271     }
    272 
    273     @LayoutlibDelegate
    274     /*package*/ static void drawText(Canvas thisCanvas,
    275             String text, float x, float y, Paint paint) {
    276         native_drawText(thisCanvas.mNativeCanvas, text, 0, text.length(), x, y,
    277                 paint.mNativePaint);
    278     }
    279 
    280     @LayoutlibDelegate
    281     /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
    282             Paint paint) {
    283         // FIXME
    284         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    285                 "Canvas.drawPoint is not supported.", null, null /*data*/);
    286     }
    287 
    288     @LayoutlibDelegate
    289     /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
    290         // FIXME
    291         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    292                 "Canvas.drawPoint is not supported.", null, null /*data*/);
    293     }
    294 
    295     @LayoutlibDelegate
    296     /*package*/ static void drawLines(Canvas thisCanvas,
    297             final float[] pts, final int offset, final int count,
    298             Paint paint) {
    299         draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/,
    300                 false /*forceSrcMode*/, new GcSnapshot.Drawable() {
    301                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
    302                         for (int i = 0 ; i < count ; i += 4) {
    303                             graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
    304                                     (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
    305                         }
    306                     }
    307                 });
    308     }
    309 
    310     @LayoutlibDelegate
    311     /*package*/ static void freeCaches() {
    312         // nothing to be done here.
    313     }
    314 
    315     @LayoutlibDelegate
    316     /*package*/ static int initRaster(int nativeBitmapOrZero) {
    317         if (nativeBitmapOrZero > 0) {
    318             // get the Bitmap from the int
    319             Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
    320 
    321             // create a new Canvas_Delegate with the given bitmap and return its new native int.
    322             Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
    323 
    324             return sManager.addNewDelegate(newDelegate);
    325         }
    326 
    327         // create a new Canvas_Delegate and return its new native int.
    328         Canvas_Delegate newDelegate = new Canvas_Delegate();
    329 
    330         return sManager.addNewDelegate(newDelegate);
    331     }
    332 
    333     @LayoutlibDelegate
    334     /*package*/ static int initGL() {
    335         // not supported.
    336         return 0;
    337     }
    338 
    339     @LayoutlibDelegate
    340     /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) {
    341         // get the delegate from the native int.
    342         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    343         if (canvasDelegate == null) {
    344             return;
    345         }
    346 
    347         // get the delegate from the native int.
    348         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
    349         if (bitmapDelegate == null) {
    350             return;
    351         }
    352 
    353         canvasDelegate.setBitmap(bitmapDelegate);
    354     }
    355 
    356     @LayoutlibDelegate
    357     /*package*/ static void nativeSetViewport(int nCanvas, int w, int h) {
    358         // only useful in GL which is not supported, so no need to do anything.
    359     }
    360 
    361     @LayoutlibDelegate
    362     /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds,
    363                                                int paint, int layerFlags) {
    364         // get the delegate from the native int.
    365         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    366         if (canvasDelegate == null) {
    367             return 0;
    368         }
    369 
    370         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
    371         if (paintDelegate == null) {
    372             return 0;
    373         }
    374 
    375         return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags);
    376     }
    377 
    378     @LayoutlibDelegate
    379     /*package*/ static int native_saveLayer(int nativeCanvas, float l,
    380                                                float t, float r, float b,
    381                                                int paint, int layerFlags) {
    382         // get the delegate from the native int.
    383         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    384         if (canvasDelegate == null) {
    385             return 0;
    386         }
    387 
    388         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
    389         if (paintDelegate == null) {
    390             return 0;
    391         }
    392 
    393         return canvasDelegate.saveLayer(new RectF(l, t, r, b),
    394                 paintDelegate, layerFlags);
    395     }
    396 
    397     @LayoutlibDelegate
    398     /*package*/ static int native_saveLayerAlpha(int nativeCanvas,
    399                                                     RectF bounds, int alpha,
    400                                                     int layerFlags) {
    401         // get the delegate from the native int.
    402         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    403         if (canvasDelegate == null) {
    404             return 0;
    405         }
    406 
    407         return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags);
    408     }
    409 
    410     @LayoutlibDelegate
    411     /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l,
    412                                                     float t, float r, float b,
    413                                                     int alpha, int layerFlags) {
    414         // get the delegate from the native int.
    415         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    416         if (canvasDelegate == null) {
    417             return 0;
    418         }
    419 
    420         return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
    421     }
    422 
    423 
    424     @LayoutlibDelegate
    425     /*package*/ static void native_concat(int nCanvas, int nMatrix) {
    426         // get the delegate from the native int.
    427         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
    428         if (canvasDelegate == null) {
    429             return;
    430         }
    431 
    432         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
    433         if (matrixDelegate == null) {
    434             return;
    435         }
    436 
    437         // get the current top graphics2D object.
    438         GcSnapshot snapshot = canvasDelegate.getSnapshot();
    439 
    440         // get its current matrix
    441         AffineTransform currentTx = snapshot.getTransform();
    442         // get the AffineTransform of the given matrix
    443         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
    444 
    445         // combine them so that the given matrix is applied after.
    446         currentTx.concatenate(matrixTx);
    447 
    448         // give it to the graphics2D as a new matrix replacing all previous transform
    449         snapshot.setTransform(currentTx);
    450     }
    451 
    452     @LayoutlibDelegate
    453     /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
    454         // get the delegate from the native int.
    455         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
    456         if (canvasDelegate == null) {
    457             return;
    458         }
    459 
    460         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
    461         if (matrixDelegate == null) {
    462             return;
    463         }
    464 
    465         // get the current top graphics2D object.
    466         GcSnapshot snapshot = canvasDelegate.getSnapshot();
    467 
    468         // get the AffineTransform of the given matrix
    469         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
    470 
    471         // give it to the graphics2D as a new matrix replacing all previous transform
    472         snapshot.setTransform(matrixTx);
    473 
    474         if (matrixDelegate.hasPerspective()) {
    475             assert false;
    476             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE,
    477                     "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
    478                     "supports affine transformations.", null, null /*data*/);
    479         }
    480     }
    481 
    482     @LayoutlibDelegate
    483     /*package*/ static boolean native_clipRect(int nCanvas,
    484                                                   float left, float top,
    485                                                   float right, float bottom,
    486                                                   int regionOp) {
    487 
    488         // get the delegate from the native int.
    489         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
    490         if (canvasDelegate == null) {
    491             return false;
    492         }
    493 
    494         return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
    495     }
    496 
    497     @LayoutlibDelegate
    498     /*package*/ static boolean native_clipPath(int nativeCanvas,
    499                                                   int nativePath,
    500                                                   int regionOp) {
    501         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    502         if (canvasDelegate == null) {
    503             return true;
    504         }
    505 
    506         Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath);
    507         if (pathDelegate == null) {
    508             return true;
    509         }
    510 
    511         return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp);
    512     }
    513 
    514     @LayoutlibDelegate
    515     /*package*/ static boolean native_clipRegion(int nativeCanvas,
    516                                                     int nativeRegion,
    517                                                     int regionOp) {
    518         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    519         if (canvasDelegate == null) {
    520             return true;
    521         }
    522 
    523         Region_Delegate region = Region_Delegate.getDelegate(nativeRegion);
    524         if (region == null) {
    525             return true;
    526         }
    527 
    528         return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp);
    529     }
    530 
    531     @LayoutlibDelegate
    532     /*package*/ static void nativeSetDrawFilter(int nativeCanvas, int nativeFilter) {
    533         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    534         if (canvasDelegate == null) {
    535             return;
    536         }
    537 
    538         canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter);
    539 
    540         if (canvasDelegate.mDrawFilter != null &&
    541                 canvasDelegate.mDrawFilter.isSupported() == false) {
    542             Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER,
    543                     canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/);
    544         }
    545     }
    546 
    547     @LayoutlibDelegate
    548     /*package*/ static boolean native_getClipBounds(int nativeCanvas,
    549                                                        Rect bounds) {
    550         // get the delegate from the native int.
    551         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    552         if (canvasDelegate == null) {
    553             return false;
    554         }
    555 
    556         Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds();
    557         if (rect != null && rect.isEmpty() == false) {
    558             bounds.left = rect.x;
    559             bounds.top = rect.y;
    560             bounds.right = rect.x + rect.width;
    561             bounds.bottom = rect.y + rect.height;
    562             return true;
    563         }
    564 
    565         return false;
    566     }
    567 
    568     @LayoutlibDelegate
    569     /*package*/ static void native_getCTM(int canvas, int matrix) {
    570         // get the delegate from the native int.
    571         Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
    572         if (canvasDelegate == null) {
    573             return;
    574         }
    575 
    576         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
    577         if (matrixDelegate == null) {
    578             return;
    579         }
    580 
    581         AffineTransform transform = canvasDelegate.getSnapshot().getTransform();
    582         matrixDelegate.set(Matrix_Delegate.makeValues(transform));
    583     }
    584 
    585     @LayoutlibDelegate
    586     /*package*/ static boolean native_quickReject(int nativeCanvas,
    587                                                      RectF rect,
    588                                                      int native_edgeType) {
    589         // FIXME properly implement quickReject
    590         return false;
    591     }
    592 
    593     @LayoutlibDelegate
    594     /*package*/ static boolean native_quickReject(int nativeCanvas,
    595                                                      int path,
    596                                                      int native_edgeType) {
    597         // FIXME properly implement quickReject
    598         return false;
    599     }
    600 
    601     @LayoutlibDelegate
    602     /*package*/ static boolean native_quickReject(int nativeCanvas,
    603                                                      float left, float top,
    604                                                      float right, float bottom,
    605                                                      int native_edgeType) {
    606         // FIXME properly implement quickReject
    607         return false;
    608     }
    609 
    610     @LayoutlibDelegate
    611     /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, int b) {
    612         native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF),
    613                 PorterDuff.Mode.SRC_OVER.nativeInt);
    614 
    615     }
    616 
    617     @LayoutlibDelegate
    618     /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, int g, int b) {
    619         native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF),
    620                 PorterDuff.Mode.SRC_OVER.nativeInt);
    621     }
    622 
    623     @LayoutlibDelegate
    624     /*package*/ static void native_drawColor(int nativeCanvas, int color) {
    625         native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt);
    626     }
    627 
    628     @LayoutlibDelegate
    629     /*package*/ static void native_drawColor(int nativeCanvas, final int color, final int mode) {
    630         // get the delegate from the native int.
    631         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
    632         if (canvasDelegate == null) {
    633             return;
    634         }
    635 
    636         final int w = canvasDelegate.mBitmap.getImage().getWidth();
    637         final int h = canvasDelegate.mBitmap.getImage().getHeight();
    638         draw(nativeCanvas, new GcSnapshot.Drawable() {
    639 
    640             public void draw(Graphics2D graphics, Paint_Delegate paint) {
    641                 // reset its transform just in case
    642                 graphics.setTransform(new AffineTransform());
    643 
    644                 // set the color
    645                 graphics.setColor(new Color(color, true /*alpha*/));
    646 
    647                 Composite composite = PorterDuffXfermode_Delegate.getComposite(
    648                         PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
    649                 if (composite != null) {
    650                     graphics.setComposite(composite);
    651                 }
    652 
    653                 graphics.fillRect(0, 0, w, h);
    654             }
    655         });
    656     }
    657 
    658     @LayoutlibDelegate
    659     /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
    660         // FIXME
    661         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    662                 "Canvas.drawPaint is not supported.", null, null /*data*/);
    663     }
    664 
    665     @LayoutlibDelegate
    666     /*package*/ static void native_drawLine(int nativeCanvas,
    667             final float startX, final float startY, final float stopX, final float stopY,
    668             int paint) {
    669 
    670         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
    671                 new GcSnapshot.Drawable() {
    672                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
    673                         graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
    674                     }
    675         });
    676     }
    677 
    678     @LayoutlibDelegate
    679     /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
    680                                                int paint) {
    681         native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
    682     }
    683 
    684     @LayoutlibDelegate
    685     /*package*/ static void native_drawRect(int nativeCanvas,
    686             final float left, final float top, final float right, final float bottom, int paint) {
    687 
    688         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
    689                 new GcSnapshot.Drawable() {
    690                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
    691                         int style = paintDelegate.getStyle();
    692 
    693                         // draw
    694                         if (style == Paint.Style.FILL.nativeInt ||
    695                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    696                             graphics.fillRect((int)left, (int)top,
    697                                     (int)(right-left), (int)(bottom-top));
    698                         }
    699 
    700                         if (style == Paint.Style.STROKE.nativeInt ||
    701                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    702                             graphics.drawRect((int)left, (int)top,
    703                                     (int)(right-left), (int)(bottom-top));
    704                         }
    705                     }
    706         });
    707     }
    708 
    709     @LayoutlibDelegate
    710     /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) {
    711         if (oval.right > oval.left && oval.bottom > oval.top) {
    712             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
    713                     new GcSnapshot.Drawable() {
    714                         public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
    715                             int style = paintDelegate.getStyle();
    716 
    717                             // draw
    718                             if (style == Paint.Style.FILL.nativeInt ||
    719                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    720                                 graphics.fillOval((int)oval.left, (int)oval.top,
    721                                         (int)oval.width(), (int)oval.height());
    722                             }
    723 
    724                             if (style == Paint.Style.STROKE.nativeInt ||
    725                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    726                                 graphics.drawOval((int)oval.left, (int)oval.top,
    727                                         (int)oval.width(), (int)oval.height());
    728                             }
    729                         }
    730             });
    731         }
    732     }
    733 
    734     @LayoutlibDelegate
    735     /*package*/ static void native_drawCircle(int nativeCanvas,
    736             float cx, float cy, float radius, int paint) {
    737         native_drawOval(nativeCanvas,
    738                 new RectF(cx - radius, cy - radius, radius, radius),
    739                 paint);
    740     }
    741 
    742     @LayoutlibDelegate
    743     /*package*/ static void native_drawArc(int nativeCanvas,
    744             final RectF oval, final float startAngle, final float sweep,
    745             final boolean useCenter, int paint) {
    746         if (oval.right > oval.left && oval.bottom > oval.top) {
    747             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
    748                     new GcSnapshot.Drawable() {
    749                         public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
    750                             int style = paintDelegate.getStyle();
    751 
    752                             Arc2D.Float arc = new Arc2D.Float(
    753                                     oval.left, oval.top, oval.width(), oval.height(),
    754                                     -startAngle, -sweep,
    755                                     useCenter ? Arc2D.PIE : Arc2D.OPEN);
    756 
    757                             // draw
    758                             if (style == Paint.Style.FILL.nativeInt ||
    759                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    760                                 graphics.fill(arc);
    761                             }
    762 
    763                             if (style == Paint.Style.STROKE.nativeInt ||
    764                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    765                                 graphics.draw(arc);
    766                             }
    767                         }
    768             });
    769         }
    770     }
    771 
    772     @LayoutlibDelegate
    773     /*package*/ static void native_drawRoundRect(int nativeCanvas,
    774             final RectF rect, final float rx, final float ry, int paint) {
    775 
    776         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
    777                 new GcSnapshot.Drawable() {
    778                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
    779                         int style = paintDelegate.getStyle();
    780 
    781                         // draw
    782                         if (style == Paint.Style.FILL.nativeInt ||
    783                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    784                             graphics.fillRoundRect(
    785                                     (int)rect.left, (int)rect.top,
    786                                     (int)rect.width(), (int)rect.height(),
    787                                     (int)rx, (int)ry);
    788                         }
    789 
    790                         if (style == Paint.Style.STROKE.nativeInt ||
    791                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    792                             graphics.drawRoundRect(
    793                                     (int)rect.left, (int)rect.top,
    794                                     (int)rect.width(), (int)rect.height(),
    795                                     (int)rx, (int)ry);
    796                         }
    797                     }
    798         });
    799     }
    800 
    801     @LayoutlibDelegate
    802     /*package*/ static void native_drawPath(int nativeCanvas, int path, int paint) {
    803         final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
    804         if (pathDelegate == null) {
    805             return;
    806         }
    807 
    808         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
    809                 new GcSnapshot.Drawable() {
    810                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
    811                         Shape shape = pathDelegate.getJavaShape();
    812                         int style = paintDelegate.getStyle();
    813 
    814                         if (style == Paint.Style.FILL.nativeInt ||
    815                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    816                             graphics.fill(shape);
    817                         }
    818 
    819                         if (style == Paint.Style.STROKE.nativeInt ||
    820                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
    821                             graphics.draw(shape);
    822                         }
    823                     }
    824         });
    825     }
    826 
    827     @LayoutlibDelegate
    828     /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
    829                                                  float left, float top,
    830                                                  int nativePaintOrZero,
    831                                                  int canvasDensity,
    832                                                  int screenDensity,
    833                                                  int bitmapDensity) {
    834         // get the delegate from the native int.
    835         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
    836         if (bitmapDelegate == null) {
    837             return;
    838         }
    839 
    840         BufferedImage image = bitmapDelegate.getImage();
    841         float right = left + image.getWidth();
    842         float bottom = top + image.getHeight();
    843 
    844         drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
    845                 0, 0, image.getWidth(), image.getHeight(),
    846                 (int)left, (int)top, (int)right, (int)bottom);
    847     }
    848 
    849     @LayoutlibDelegate
    850     /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
    851                                                  Rect src, RectF dst,
    852                                                  int nativePaintOrZero,
    853                                                  int screenDensity,
    854                                                  int bitmapDensity) {
    855         // get the delegate from the native int.
    856         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
    857         if (bitmapDelegate == null) {
    858             return;
    859         }
    860 
    861         BufferedImage image = bitmapDelegate.getImage();
    862 
    863         if (src == null) {
    864             drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
    865                     0, 0, image.getWidth(), image.getHeight(),
    866                     (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
    867         } else {
    868             drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
    869                     src.left, src.top, src.width(), src.height(),
    870                     (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
    871         }
    872     }
    873 
    874     @LayoutlibDelegate
    875     /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
    876                                                  Rect src, Rect dst,
    877                                                  int nativePaintOrZero,
    878                                                  int screenDensity,
    879                                                  int bitmapDensity) {
    880         // get the delegate from the native int.
    881         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
    882         if (bitmapDelegate == null) {
    883             return;
    884         }
    885 
    886         BufferedImage image = bitmapDelegate.getImage();
    887 
    888         if (src == null) {
    889             drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
    890                     0, 0, image.getWidth(), image.getHeight(),
    891                     dst.left, dst.top, dst.right, dst.bottom);
    892         } else {
    893             drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
    894                     src.left, src.top, src.width(), src.height(),
    895                     dst.left, dst.top, dst.right, dst.bottom);
    896         }
    897     }
    898 
    899     @LayoutlibDelegate
    900     /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
    901                                                 int offset, int stride, final float x,
    902                                                  final float y, int width, int height,
    903                                                  boolean hasAlpha,
    904                                                  int nativePaintOrZero) {
    905 
    906         // create a temp BufferedImage containing the content.
    907         final BufferedImage image = new BufferedImage(width, height,
    908                 hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
    909         image.setRGB(0, 0, width, height, colors, offset, stride);
    910 
    911         draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
    912                 new GcSnapshot.Drawable() {
    913                     public void draw(Graphics2D graphics, Paint_Delegate paint) {
    914                         if (paint != null && paint.isFilterBitmap()) {
    915                             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    916                                     RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    917                         }
    918 
    919                         graphics.drawImage(image, (int) x, (int) y, null);
    920                     }
    921         });
    922     }
    923 
    924     @LayoutlibDelegate
    925     /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
    926                                                       int nMatrix, int nPaint) {
    927         // get the delegate from the native int.
    928         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
    929         if (canvasDelegate == null) {
    930             return;
    931         }
    932 
    933         // get the delegate from the native int, which can be null
    934         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
    935 
    936         // get the delegate from the native int.
    937         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap);
    938         if (bitmapDelegate == null) {
    939             return;
    940         }
    941 
    942         final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
    943 
    944         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
    945         if (matrixDelegate == null) {
    946             return;
    947         }
    948 
    949         final AffineTransform mtx = matrixDelegate.getAffineTransform();
    950 
    951         canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
    952                 public void draw(Graphics2D graphics, Paint_Delegate paint) {
    953                     if (paint != null && paint.isFilterBitmap()) {
    954                         graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    955                                 RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    956                     }
    957 
    958                     //FIXME add support for canvas, screen and bitmap densities.
    959                     graphics.drawImage(image, mtx, null);
    960                 }
    961         }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
    962     }
    963 
    964     @LayoutlibDelegate
    965     /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
    966             int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
    967             int colorOffset, int nPaint) {
    968         // FIXME
    969         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    970                 "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
    971     }
    972 
    973     @LayoutlibDelegate
    974     /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
    975             float[] verts, int vertOffset,
    976             float[] texs, int texOffset,
    977             int[] colors, int colorOffset,
    978             short[] indices, int indexOffset,
    979             int indexCount, int nPaint) {
    980         // FIXME
    981         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    982                 "Canvas.drawVertices is not supported.", null, null /*data*/);
    983     }
    984 
    985     @LayoutlibDelegate
    986     /*package*/ static void native_drawText(int nativeCanvas,
    987             final char[] text, final int index, final int count,
    988             final float startX, final float startY, int paint) {
    989         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
    990                 new GcSnapshot.Drawable() {
    991             public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
    992                 // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
    993                 // Any change to this method should be reflected in Paint.measureText
    994                 // Paint.TextAlign indicates how the text is positioned relative to X.
    995                 // LEFT is the default and there's nothing to do.
    996                 float x = startX;
    997                 float y = startY;
    998                 if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
    999                     float m = paintDelegate.measureText(text, index, count);
   1000                     if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
   1001                         x -= m / 2;
   1002                     } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
   1003                         x -= m;
   1004                     }
   1005                 }
   1006 
   1007                 List<FontInfo> fonts = paintDelegate.getFonts();
   1008 
   1009                 if (fonts.size() > 0) {
   1010                     FontInfo mainFont = fonts.get(0);
   1011                     int i = index;
   1012                     int lastIndex = index + count;
   1013                     while (i < lastIndex) {
   1014                         // always start with the main font.
   1015                         int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
   1016                         if (upTo == -1) {
   1017                             // draw all the rest and exit.
   1018                             graphics.setFont(mainFont.mFont);
   1019                             graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y);
   1020                             return;
   1021                         } else if (upTo > 0) {
   1022                             // draw what's possible
   1023                             graphics.setFont(mainFont.mFont);
   1024                             graphics.drawChars(text, i, upTo - i, (int)x, (int)y);
   1025 
   1026                             // compute the width that was drawn to increase x
   1027                             x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
   1028 
   1029                             // move index to the first non displayed char.
   1030                             i = upTo;
   1031 
   1032                             // don't call continue at this point. Since it is certain the main font
   1033                             // cannot display the font a index upTo (now ==i), we move on to the
   1034                             // fallback fonts directly.
   1035                         }
   1036 
   1037                         // no char supported, attempt to read the next char(s) with the
   1038                         // fallback font. In this case we only test the first character
   1039                         // and then go back to test with the main font.
   1040                         // Special test for 2-char characters.
   1041                         boolean foundFont = false;
   1042                         for (int f = 1 ; f < fonts.size() ; f++) {
   1043                             FontInfo fontInfo = fonts.get(f);
   1044 
   1045                             // need to check that the font can display the character. We test
   1046                             // differently if the char is a high surrogate.
   1047                             int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
   1048                             upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
   1049                             if (upTo == -1) {
   1050                                 // draw that char
   1051                                 graphics.setFont(fontInfo.mFont);
   1052                                 graphics.drawChars(text, i, charCount, (int)x, (int)y);
   1053 
   1054                                 // update x
   1055                                 x += fontInfo.mMetrics.charsWidth(text, i, charCount);
   1056 
   1057                                 // update the index in the text, and move on
   1058                                 i += charCount;
   1059                                 foundFont = true;
   1060                                 break;
   1061 
   1062                             }
   1063                         }
   1064 
   1065                         // in case no font can display the char, display it with the main font.
   1066                         // (it'll put a square probably)
   1067                         if (foundFont == false) {
   1068                             int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
   1069 
   1070                             graphics.setFont(mainFont.mFont);
   1071                             graphics.drawChars(text, i, charCount, (int)x, (int)y);
   1072 
   1073                             // measure it to advance x
   1074                             x += mainFont.mMetrics.charsWidth(text, i, charCount);
   1075 
   1076                             // and move to the next chars.
   1077                             i += charCount;
   1078                         }
   1079                     }
   1080                 }
   1081             }
   1082         });
   1083     }
   1084 
   1085     @LayoutlibDelegate
   1086     /*package*/ static void native_drawText(int nativeCanvas, String text,
   1087                                                int start, int end, float x,
   1088                                                float y, int paint) {
   1089         int count = end - start;
   1090         char[] buffer = TemporaryBuffer.obtain(count);
   1091         TextUtils.getChars(text, start, end, buffer, 0);
   1092 
   1093         native_drawText(nativeCanvas, buffer, 0, count, x, y, paint);
   1094     }
   1095 
   1096     @LayoutlibDelegate
   1097     /*package*/ static void native_drawPosText(int nativeCanvas,
   1098                                                   char[] text, int index,
   1099                                                   int count, float[] pos,
   1100                                                   int paint) {
   1101         // FIXME
   1102         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1103                 "Canvas.drawPosText is not supported.", null, null /*data*/);
   1104     }
   1105 
   1106     @LayoutlibDelegate
   1107     /*package*/ static void native_drawPosText(int nativeCanvas,
   1108                                                   String text, float[] pos,
   1109                                                   int paint) {
   1110         // FIXME
   1111         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1112                 "Canvas.drawPosText is not supported.", null, null /*data*/);
   1113     }
   1114 
   1115     @LayoutlibDelegate
   1116     /*package*/ static void native_drawTextOnPath(int nativeCanvas,
   1117                                                      char[] text, int index,
   1118                                                      int count, int path,
   1119                                                      float hOffset,
   1120                                                      float vOffset,
   1121                                                      int paint) {
   1122         // FIXME
   1123         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1124                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
   1125     }
   1126 
   1127     @LayoutlibDelegate
   1128     /*package*/ static void native_drawTextOnPath(int nativeCanvas,
   1129                                                      String text, int path,
   1130                                                      float hOffset,
   1131                                                      float vOffset,
   1132                                                      int paint) {
   1133         // FIXME
   1134         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1135                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
   1136     }
   1137 
   1138     @LayoutlibDelegate
   1139     /*package*/ static void native_drawPicture(int nativeCanvas,
   1140                                                   int nativePicture) {
   1141         // FIXME
   1142         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1143                 "Canvas.drawPicture is not supported.", null, null /*data*/);
   1144     }
   1145 
   1146     @LayoutlibDelegate
   1147     /*package*/ static void finalizer(int nativeCanvas) {
   1148         // get the delegate from the native int so that it can be disposed.
   1149         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
   1150         if (canvasDelegate == null) {
   1151             return;
   1152         }
   1153 
   1154         canvasDelegate.dispose();
   1155 
   1156         // remove it from the manager.
   1157         sManager.removeJavaReferenceFor(nativeCanvas);
   1158     }
   1159 
   1160     // ---- Private delegate/helper methods ----
   1161 
   1162     /**
   1163      * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
   1164      * <p>Note that the drawable may actually be executed several times if there are
   1165      * layers involved (see {@link #saveLayer(RectF, int, int)}.
   1166      */
   1167     private static void draw(int nCanvas, int nPaint, boolean compositeOnly, boolean forceSrcMode,
   1168             GcSnapshot.Drawable drawable) {
   1169         // get the delegate from the native int.
   1170         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
   1171         if (canvasDelegate == null) {
   1172             return;
   1173         }
   1174 
   1175         // get the paint which can be null if nPaint is 0;
   1176         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
   1177 
   1178         canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
   1179     }
   1180 
   1181     /**
   1182      * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
   1183      * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
   1184      * <p>Note that the drawable may actually be executed several times if there are
   1185      * layers involved (see {@link #saveLayer(RectF, int, int)}.
   1186      */
   1187     private static void draw(int nCanvas, GcSnapshot.Drawable drawable) {
   1188         // get the delegate from the native int.
   1189         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
   1190         if (canvasDelegate == null) {
   1191             return;
   1192         }
   1193 
   1194         canvasDelegate.mSnapshot.draw(drawable);
   1195     }
   1196 
   1197     private Canvas_Delegate(Bitmap_Delegate bitmap) {
   1198         mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
   1199     }
   1200 
   1201     private Canvas_Delegate() {
   1202         mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
   1203     }
   1204 
   1205     /**
   1206      * Disposes of the {@link Graphics2D} stack.
   1207      */
   1208     private void dispose() {
   1209         mSnapshot.dispose();
   1210     }
   1211 
   1212     private int save(int saveFlags) {
   1213         // get the current save count
   1214         int count = mSnapshot.size();
   1215 
   1216         mSnapshot = mSnapshot.save(saveFlags);
   1217 
   1218         // return the old save count
   1219         return count;
   1220     }
   1221 
   1222     private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
   1223         Paint_Delegate paint = new Paint_Delegate();
   1224         paint.setAlpha(alpha);
   1225         return saveLayer(rect, paint, saveFlags);
   1226     }
   1227 
   1228     private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
   1229         // get the current save count
   1230         int count = mSnapshot.size();
   1231 
   1232         mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
   1233 
   1234         // return the old save count
   1235         return count;
   1236     }
   1237 
   1238     /**
   1239      * Restores the {@link GcSnapshot} to <var>saveCount</var>
   1240      * @param saveCount the saveCount
   1241      */
   1242     private void restoreTo(int saveCount) {
   1243         mSnapshot = mSnapshot.restoreTo(saveCount);
   1244     }
   1245 
   1246     /**
   1247      * Restores the {@link GcSnapshot} to <var>saveCount</var>
   1248      * @param saveCount the saveCount
   1249      */
   1250     private void restore() {
   1251         mSnapshot = mSnapshot.restore();
   1252     }
   1253 
   1254     private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
   1255         return mSnapshot.clipRect(left, top, right, bottom, regionOp);
   1256     }
   1257 
   1258     private void setBitmap(Bitmap_Delegate bitmap) {
   1259         mBitmap = bitmap;
   1260         assert mSnapshot.size() == 1;
   1261         mSnapshot.setBitmap(mBitmap);
   1262     }
   1263 
   1264     private static void drawBitmap(
   1265             int nativeCanvas,
   1266             Bitmap_Delegate bitmap,
   1267             int nativePaintOrZero,
   1268             final int sleft, final int stop, final int sright, final int sbottom,
   1269             final int dleft, final int dtop, final int dright, final int dbottom) {
   1270         // get the delegate from the native int.
   1271         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
   1272         if (canvasDelegate == null) {
   1273             return;
   1274         }
   1275 
   1276         // get the paint, which could be null if the int is 0
   1277         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
   1278 
   1279         final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
   1280 
   1281         draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
   1282                 new GcSnapshot.Drawable() {
   1283                     public void draw(Graphics2D graphics, Paint_Delegate paint) {
   1284                         if (paint != null && paint.isFilterBitmap()) {
   1285                             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
   1286                                     RenderingHints.VALUE_INTERPOLATION_BILINEAR);
   1287                         }
   1288 
   1289                         //FIXME add support for canvas, screen and bitmap densities.
   1290                         graphics.drawImage(image, dleft, dtop, dright, dbottom,
   1291                                 sleft, stop, sright, sbottom, null);
   1292                     }
   1293         });
   1294     }
   1295 
   1296 
   1297     /**
   1298      * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
   1299      * The image returns, through a 1-size boolean array, whether the drawing code should
   1300      * use a SRC composite no matter what the paint says.
   1301      *
   1302      * @param bitmap the bitmap
   1303      * @param paint the paint that will be used to draw
   1304      * @param forceSrcMode whether the composite will have to be SRC
   1305      * @return the image to draw
   1306      */
   1307     private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
   1308             boolean[] forceSrcMode) {
   1309         BufferedImage image = bitmap.getImage();
   1310         forceSrcMode[0] = false;
   1311 
   1312         // if the bitmap config is alpha_8, then we erase all color value from it
   1313         // before drawing it.
   1314         if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
   1315             fixAlpha8Bitmap(image);
   1316         } else if (bitmap.hasAlpha() == false) {
   1317             // hasAlpha is merely a rendering hint. There can in fact be alpha values
   1318             // in the bitmap but it should be ignored at drawing time.
   1319             // There is two ways to do this:
   1320             // - override the composite to be SRC. This can only be used if the composite
   1321             //   was going to be SRC or SRC_OVER in the first place
   1322             // - Create a different bitmap to draw in which all the alpha channel values is set
   1323             //   to 0xFF.
   1324             if (paint != null) {
   1325                 Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
   1326                 if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
   1327                     PorterDuff.Mode mode =
   1328                         ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
   1329 
   1330                     forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
   1331                             mode == PorterDuff.Mode.SRC;
   1332                 }
   1333             }
   1334 
   1335             // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
   1336             if (forceSrcMode[0] == false) {
   1337                 image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
   1338             }
   1339         }
   1340 
   1341         return image;
   1342     }
   1343 
   1344     private static void fixAlpha8Bitmap(final BufferedImage image) {
   1345         int w = image.getWidth();
   1346         int h = image.getHeight();
   1347         int[] argb = new int[w * h];
   1348         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
   1349 
   1350         final int length = argb.length;
   1351         for (int i = 0 ; i < length; i++) {
   1352             argb[i] &= 0xFF000000;
   1353         }
   1354         image.setRGB(0, 0, w, h, argb, 0, w);
   1355     }
   1356 }
   1357 
   1358