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