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