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                     RectF bounds = paintDelegate.measureText(text, index, count, isRtl);
    994                     float m = bounds.right - bounds.left;
    995                     if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
    996                         x -= m / 2;
    997                     } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
    998                         x -= m;
    999                     }
   1000                 }
   1001 
   1002                 new BidiRenderer(graphics, paintDelegate, text).renderText(
   1003                         index, limit, isRtl, null, 0, true, x, startY);
   1004             }
   1005         });
   1006     }
   1007 
   1008     @LayoutlibDelegate
   1009     /*package*/ static void native_drawText(int nativeCanvas, String text,
   1010             int start, int end, float x, float y, final int flags, int paint) {
   1011         int count = end - start;
   1012         char[] buffer = TemporaryBuffer.obtain(count);
   1013         TextUtils.getChars(text, start, end, buffer, 0);
   1014 
   1015         native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
   1016     }
   1017 
   1018     @LayoutlibDelegate
   1019     /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
   1020             int start, int end, int contextStart, int contextEnd,
   1021             float x, float y, int flags, int paint) {
   1022         int count = end - start;
   1023         char[] buffer = TemporaryBuffer.obtain(count);
   1024         TextUtils.getChars(text, start, end, buffer, 0);
   1025 
   1026         native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
   1027     }
   1028 
   1029     @LayoutlibDelegate
   1030     /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
   1031             int start, int count, int contextStart, int contextCount,
   1032             float x, float y, int flags, int paint) {
   1033         native_drawText(nativeCanvas, text, start, count, x, y, flags, paint);
   1034     }
   1035 
   1036     @LayoutlibDelegate
   1037     /*package*/ static void native_drawPosText(int nativeCanvas,
   1038                                                   char[] text, int index,
   1039                                                   int count, float[] pos,
   1040                                                   int paint) {
   1041         // FIXME
   1042         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1043                 "Canvas.drawPosText is not supported.", null, null /*data*/);
   1044     }
   1045 
   1046     @LayoutlibDelegate
   1047     /*package*/ static void native_drawPosText(int nativeCanvas,
   1048                                                   String text, float[] pos,
   1049                                                   int paint) {
   1050         // FIXME
   1051         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1052                 "Canvas.drawPosText is not supported.", null, null /*data*/);
   1053     }
   1054 
   1055     @LayoutlibDelegate
   1056     /*package*/ static void native_drawTextOnPath(int nativeCanvas,
   1057                                                      char[] text, int index,
   1058                                                      int count, int path,
   1059                                                      float hOffset,
   1060                                                      float vOffset, int bidiFlags,
   1061                                                      int paint) {
   1062         // FIXME
   1063         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1064                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
   1065     }
   1066 
   1067     @LayoutlibDelegate
   1068     /*package*/ static void native_drawTextOnPath(int nativeCanvas,
   1069                                                      String text, int path,
   1070                                                      float hOffset,
   1071                                                      float vOffset,
   1072                                                      int flags, int paint) {
   1073         // FIXME
   1074         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1075                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
   1076     }
   1077 
   1078     @LayoutlibDelegate
   1079     /*package*/ static void finalizer(int nativeCanvas) {
   1080         // get the delegate from the native int so that it can be disposed.
   1081         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
   1082         if (canvasDelegate == null) {
   1083             return;
   1084         }
   1085 
   1086         canvasDelegate.dispose();
   1087 
   1088         // remove it from the manager.
   1089         sManager.removeJavaReferenceFor(nativeCanvas);
   1090     }
   1091 
   1092     // ---- Private delegate/helper methods ----
   1093 
   1094     /**
   1095      * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
   1096      * <p>Note that the drawable may actually be executed several times if there are
   1097      * layers involved (see {@link #saveLayer(RectF, int, int)}.
   1098      */
   1099     private static void draw(int nCanvas, int nPaint, boolean compositeOnly, boolean forceSrcMode,
   1100             GcSnapshot.Drawable drawable) {
   1101         // get the delegate from the native int.
   1102         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
   1103         if (canvasDelegate == null) {
   1104             return;
   1105         }
   1106 
   1107         // get the paint which can be null if nPaint is 0;
   1108         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
   1109 
   1110         canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
   1111     }
   1112 
   1113     /**
   1114      * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
   1115      * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
   1116      * <p>Note that the drawable may actually be executed several times if there are
   1117      * layers involved (see {@link #saveLayer(RectF, int, int)}.
   1118      */
   1119     private static void draw(int nCanvas, GcSnapshot.Drawable drawable) {
   1120         // get the delegate from the native int.
   1121         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
   1122         if (canvasDelegate == null) {
   1123             return;
   1124         }
   1125 
   1126         canvasDelegate.mSnapshot.draw(drawable);
   1127     }
   1128 
   1129     private Canvas_Delegate(Bitmap_Delegate bitmap) {
   1130         mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
   1131     }
   1132 
   1133     private Canvas_Delegate() {
   1134         mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
   1135     }
   1136 
   1137     /**
   1138      * Disposes of the {@link Graphics2D} stack.
   1139      */
   1140     private void dispose() {
   1141         mSnapshot.dispose();
   1142     }
   1143 
   1144     private int save(int saveFlags) {
   1145         // get the current save count
   1146         int count = mSnapshot.size();
   1147 
   1148         mSnapshot = mSnapshot.save(saveFlags);
   1149 
   1150         // return the old save count
   1151         return count;
   1152     }
   1153 
   1154     private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
   1155         Paint_Delegate paint = new Paint_Delegate();
   1156         paint.setAlpha(alpha);
   1157         return saveLayer(rect, paint, saveFlags);
   1158     }
   1159 
   1160     private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
   1161         // get the current save count
   1162         int count = mSnapshot.size();
   1163 
   1164         mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
   1165 
   1166         // return the old save count
   1167         return count;
   1168     }
   1169 
   1170     /**
   1171      * Restores the {@link GcSnapshot} to <var>saveCount</var>
   1172      * @param saveCount the saveCount
   1173      */
   1174     private void restoreTo(int saveCount) {
   1175         mSnapshot = mSnapshot.restoreTo(saveCount);
   1176     }
   1177 
   1178     /**
   1179      * Restores the {@link GcSnapshot} to <var>saveCount</var>
   1180      * @param saveCount the saveCount
   1181      */
   1182     private void restore() {
   1183         mSnapshot = mSnapshot.restore();
   1184     }
   1185 
   1186     private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
   1187         return mSnapshot.clipRect(left, top, right, bottom, regionOp);
   1188     }
   1189 
   1190     private void setBitmap(Bitmap_Delegate bitmap) {
   1191         mBitmap = bitmap;
   1192         assert mSnapshot.size() == 1;
   1193         mSnapshot.setBitmap(mBitmap);
   1194     }
   1195 
   1196     private static void drawBitmap(
   1197             int nativeCanvas,
   1198             Bitmap_Delegate bitmap,
   1199             int nativePaintOrZero,
   1200             final int sleft, final int stop, final int sright, final int sbottom,
   1201             final int dleft, final int dtop, final int dright, final int dbottom) {
   1202         // get the delegate from the native int.
   1203         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
   1204         if (canvasDelegate == null) {
   1205             return;
   1206         }
   1207 
   1208         // get the paint, which could be null if the int is 0
   1209         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
   1210 
   1211         final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
   1212 
   1213         draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
   1214                 new GcSnapshot.Drawable() {
   1215                     @Override
   1216                     public void draw(Graphics2D graphics, Paint_Delegate paint) {
   1217                         if (paint != null && paint.isFilterBitmap()) {
   1218                             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
   1219                                     RenderingHints.VALUE_INTERPOLATION_BILINEAR);
   1220                         }
   1221 
   1222                         //FIXME add support for canvas, screen and bitmap densities.
   1223                         graphics.drawImage(image, dleft, dtop, dright, dbottom,
   1224                                 sleft, stop, sright, sbottom, null);
   1225                     }
   1226         });
   1227     }
   1228 
   1229 
   1230     /**
   1231      * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
   1232      * The image returns, through a 1-size boolean array, whether the drawing code should
   1233      * use a SRC composite no matter what the paint says.
   1234      *
   1235      * @param bitmap the bitmap
   1236      * @param paint the paint that will be used to draw
   1237      * @param forceSrcMode whether the composite will have to be SRC
   1238      * @return the image to draw
   1239      */
   1240     private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
   1241             boolean[] forceSrcMode) {
   1242         BufferedImage image = bitmap.getImage();
   1243         forceSrcMode[0] = false;
   1244 
   1245         // if the bitmap config is alpha_8, then we erase all color value from it
   1246         // before drawing it.
   1247         if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
   1248             fixAlpha8Bitmap(image);
   1249         } else if (bitmap.hasAlpha() == false) {
   1250             // hasAlpha is merely a rendering hint. There can in fact be alpha values
   1251             // in the bitmap but it should be ignored at drawing time.
   1252             // There is two ways to do this:
   1253             // - override the composite to be SRC. This can only be used if the composite
   1254             //   was going to be SRC or SRC_OVER in the first place
   1255             // - Create a different bitmap to draw in which all the alpha channel values is set
   1256             //   to 0xFF.
   1257             if (paint != null) {
   1258                 Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
   1259                 if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
   1260                     PorterDuff.Mode mode =
   1261                         ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
   1262 
   1263                     forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
   1264                             mode == PorterDuff.Mode.SRC;
   1265                 }
   1266             }
   1267 
   1268             // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
   1269             if (forceSrcMode[0] == false) {
   1270                 image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
   1271             }
   1272         }
   1273 
   1274         return image;
   1275     }
   1276 
   1277     private static void fixAlpha8Bitmap(final BufferedImage image) {
   1278         int w = image.getWidth();
   1279         int h = image.getHeight();
   1280         int[] argb = new int[w * h];
   1281         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
   1282 
   1283         final int length = argb.length;
   1284         for (int i = 0 ; i < length; i++) {
   1285             argb[i] &= 0xFF000000;
   1286         }
   1287         image.setRGB(0, 0, w, h, argb, 0, w);
   1288     }
   1289 }
   1290 
   1291