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