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.annotation.Nullable;
     26 import android.graphics.Bitmap.Config;
     27 
     28 import java.awt.Graphics2D;
     29 import java.awt.Rectangle;
     30 import java.awt.geom.AffineTransform;
     31 
     32 import libcore.util.NativeAllocationRegistry_Delegate;
     33 
     34 
     35 /**
     36  * Delegate implementing the native methods of android.graphics.Canvas
     37  *
     38  * Through the layoutlib_create tool, the original native methods of Canvas have been replaced
     39  * by calls to methods of the same name in this delegate class.
     40  *
     41  * This class behaves like the original native implementation, but in Java, keeping previously
     42  * native data into its own objects and mapping them to int that are sent back and forth between
     43  * it and the original Canvas class.
     44  *
     45  * @see DelegateManager
     46  *
     47  */
     48 public final class Canvas_Delegate extends BaseCanvas_Delegate {
     49 
     50     // ---- delegate manager ----
     51     private static long sFinalizer = -1;
     52 
     53     private DrawFilter_Delegate mDrawFilter = null;
     54 
     55     // ---- Public Helper methods ----
     56 
     57     /**
     58      * Returns the native delegate associated to a given {@link Canvas} object.
     59      */
     60     public static Canvas_Delegate getDelegate(Canvas canvas) {
     61         return (Canvas_Delegate) sManager.getDelegate(canvas.getNativeCanvasWrapper());
     62     }
     63 
     64     /**
     65      * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
     66      */
     67     public static Canvas_Delegate getDelegate(long native_canvas) {
     68         return (Canvas_Delegate) sManager.getDelegate(native_canvas);
     69     }
     70 
     71     /**
     72      * Returns the current {@link Graphics2D} used to draw.
     73      */
     74     public GcSnapshot getSnapshot() {
     75         return mSnapshot;
     76     }
     77 
     78     /**
     79      * Returns the {@link DrawFilter} delegate or null if none have been set.
     80      *
     81      * @return the delegate or null.
     82      */
     83     public DrawFilter_Delegate getDrawFilter() {
     84         return mDrawFilter;
     85     }
     86 
     87     // ---- native methods ----
     88 
     89     @LayoutlibDelegate
     90     /*package*/ static void nFreeCaches() {
     91         // nothing to be done here.
     92     }
     93 
     94     @LayoutlibDelegate
     95     /*package*/ static void nFreeTextLayoutCaches() {
     96         // nothing to be done here yet.
     97     }
     98 
     99     @LayoutlibDelegate
    100     /*package*/ static long nInitRaster(long bitmapHandle) {
    101         if (bitmapHandle > 0) {
    102             // get the Bitmap from the int
    103             Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
    104 
    105             // create a new Canvas_Delegate with the given bitmap and return its new native int.
    106             Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
    107 
    108             return sManager.addNewDelegate(newDelegate);
    109         }
    110 
    111         // create a new Canvas_Delegate and return its new native int.
    112         Canvas_Delegate newDelegate = new Canvas_Delegate();
    113 
    114         return sManager.addNewDelegate(newDelegate);
    115     }
    116 
    117     @LayoutlibDelegate
    118     public static void nSetBitmap(long canvas, long bitmapHandle) {
    119         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
    120         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
    121         if (canvasDelegate == null || bitmapDelegate == null) {
    122             return;
    123         }
    124         canvasDelegate.mBitmap = bitmapDelegate;
    125         canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate);
    126     }
    127 
    128     @LayoutlibDelegate
    129     public static boolean nIsOpaque(long nativeCanvas) {
    130         // get the delegate from the native int.
    131         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    132         if (canvasDelegate == null) {
    133             return false;
    134         }
    135 
    136         return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
    137     }
    138 
    139     @LayoutlibDelegate
    140     public static int nGetWidth(long nativeCanvas) {
    141         // get the delegate from the native int.
    142         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    143         if (canvasDelegate == null) {
    144             return 0;
    145         }
    146 
    147         return canvasDelegate.mBitmap.getImage().getWidth();
    148     }
    149 
    150     @LayoutlibDelegate
    151     public static int nGetHeight(long nativeCanvas) {
    152         // get the delegate from the native int.
    153         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    154         if (canvasDelegate == null) {
    155             return 0;
    156         }
    157 
    158         return canvasDelegate.mBitmap.getImage().getHeight();
    159     }
    160 
    161     @LayoutlibDelegate
    162     public static int nSave(long nativeCanvas, int saveFlags) {
    163         // get the delegate from the native int.
    164         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    165         if (canvasDelegate == null) {
    166             return 0;
    167         }
    168 
    169         return canvasDelegate.save(saveFlags);
    170     }
    171 
    172     @LayoutlibDelegate
    173     public static int nSaveLayer(long nativeCanvas, float l,
    174                                                float t, float r, float b,
    175                                                long paint, int layerFlags) {
    176         // get the delegate from the native int.
    177         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    178         if (canvasDelegate == null) {
    179             return 0;
    180         }
    181 
    182         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
    183 
    184         return canvasDelegate.saveLayer(new RectF(l, t, r, b),
    185                 paintDelegate, layerFlags);
    186     }
    187 
    188     @LayoutlibDelegate
    189     public static int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b) {
    190         return nSaveLayer(nativeCanvas, l, t, r, b, 0, 0);
    191     }
    192 
    193     @LayoutlibDelegate
    194     public static int nSaveLayerAlpha(long nativeCanvas, float l,
    195                                                     float t, float r, float b,
    196                                                     int alpha, int layerFlags) {
    197         // get the delegate from the native int.
    198         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    199         if (canvasDelegate == null) {
    200             return 0;
    201         }
    202 
    203         return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
    204     }
    205 
    206     @LayoutlibDelegate
    207     public static boolean nRestore(long nativeCanvas) {
    208         // FIXME: implement throwOnUnderflow.
    209         // get the delegate from the native int.
    210         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    211         if (canvasDelegate == null) {
    212             return false;
    213         }
    214 
    215         canvasDelegate.restore();
    216         return true;
    217     }
    218 
    219     @LayoutlibDelegate
    220     public static void nRestoreToCount(long nativeCanvas, int saveCount) {
    221         // FIXME: implement throwOnUnderflow.
    222         // get the delegate from the native int.
    223         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    224         if (canvasDelegate == null) {
    225             return;
    226         }
    227 
    228         canvasDelegate.restoreTo(saveCount);
    229     }
    230 
    231     @LayoutlibDelegate
    232     public static int nGetSaveCount(long nativeCanvas) {
    233         // get the delegate from the native int.
    234         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    235         if (canvasDelegate == null) {
    236             return 0;
    237         }
    238 
    239         return canvasDelegate.getSnapshot().size();
    240     }
    241 
    242     @LayoutlibDelegate
    243    public static void nTranslate(long nativeCanvas, float dx, float dy) {
    244         // get the delegate from the native int.
    245         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    246         if (canvasDelegate == null) {
    247             return;
    248         }
    249 
    250         canvasDelegate.getSnapshot().translate(dx, dy);
    251     }
    252 
    253     @LayoutlibDelegate
    254        public static void nScale(long nativeCanvas, float sx, float sy) {
    255             // get the delegate from the native int.
    256             Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    257             if (canvasDelegate == null) {
    258                 return;
    259             }
    260 
    261             canvasDelegate.getSnapshot().scale(sx, sy);
    262         }
    263 
    264     @LayoutlibDelegate
    265     public static void nRotate(long nativeCanvas, float degrees) {
    266         // get the delegate from the native int.
    267         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    268         if (canvasDelegate == null) {
    269             return;
    270         }
    271 
    272         canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
    273     }
    274 
    275     @LayoutlibDelegate
    276    public static void nSkew(long nativeCanvas, float kx, float ky) {
    277         // get the delegate from the native int.
    278         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    279         if (canvasDelegate == null) {
    280             return;
    281         }
    282 
    283         // get the current top graphics2D object.
    284         GcSnapshot g = canvasDelegate.getSnapshot();
    285 
    286         // get its current matrix
    287         AffineTransform currentTx = g.getTransform();
    288         // get the AffineTransform for the given skew.
    289         float[] mtx = Matrix_Delegate.getSkew(kx, ky);
    290         AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
    291 
    292         // combine them so that the given matrix is applied after.
    293         currentTx.preConcatenate(matrixTx);
    294 
    295         // give it to the graphics2D as a new matrix replacing all previous transform
    296         g.setTransform(currentTx);
    297     }
    298 
    299     @LayoutlibDelegate
    300     public static void nConcat(long nCanvas, long nMatrix) {
    301         // get the delegate from the native int.
    302         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
    303         if (canvasDelegate == null) {
    304             return;
    305         }
    306 
    307         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
    308         if (matrixDelegate == null) {
    309             return;
    310         }
    311 
    312         // get the current top graphics2D object.
    313         GcSnapshot snapshot = canvasDelegate.getSnapshot();
    314 
    315         // get its current matrix
    316         AffineTransform currentTx = snapshot.getTransform();
    317         // get the AffineTransform of the given matrix
    318         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
    319 
    320         // combine them so that the given matrix is applied after.
    321         currentTx.concatenate(matrixTx);
    322 
    323         // give it to the graphics2D as a new matrix replacing all previous transform
    324         snapshot.setTransform(currentTx);
    325     }
    326 
    327     @LayoutlibDelegate
    328     public static void nSetMatrix(long nCanvas, long nMatrix) {
    329         // get the delegate from the native int.
    330         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
    331         if (canvasDelegate == null) {
    332             return;
    333         }
    334 
    335         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
    336         if (matrixDelegate == null) {
    337             return;
    338         }
    339 
    340         // get the current top graphics2D object.
    341         GcSnapshot snapshot = canvasDelegate.getSnapshot();
    342 
    343         // get the AffineTransform of the given matrix
    344         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
    345 
    346         // give it to the graphics2D as a new matrix replacing all previous transform
    347         snapshot.setTransform(matrixTx);
    348 
    349         if (matrixDelegate.hasPerspective()) {
    350             assert false;
    351             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE,
    352                     "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
    353                     "supports affine transformations.", null, null /*data*/);
    354         }
    355     }
    356 
    357     @LayoutlibDelegate
    358     public static boolean nClipRect(long nCanvas,
    359                                                   float left, float top,
    360                                                   float right, float bottom,
    361                                                   int regionOp) {
    362         // get the delegate from the native int.
    363         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
    364         if (canvasDelegate == null) {
    365             return false;
    366         }
    367 
    368         return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
    369     }
    370 
    371     @LayoutlibDelegate
    372     public static boolean nClipPath(long nativeCanvas,
    373                                                   long nativePath,
    374                                                   int regionOp) {
    375         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    376         if (canvasDelegate == null) {
    377             return true;
    378         }
    379 
    380         Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath);
    381         if (pathDelegate == null) {
    382             return true;
    383         }
    384 
    385         return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp);
    386     }
    387 
    388     @LayoutlibDelegate
    389     public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
    390         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    391         if (canvasDelegate == null) {
    392             return;
    393         }
    394 
    395         canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter);
    396 
    397         if (canvasDelegate.mDrawFilter != null && !canvasDelegate.mDrawFilter.isSupported()) {
    398             Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER,
    399                     canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/);
    400         }
    401     }
    402 
    403     @LayoutlibDelegate
    404     public static boolean nGetClipBounds(long nativeCanvas,
    405                                                        Rect bounds) {
    406         // get the delegate from the native int.
    407         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
    408         if (canvasDelegate == null) {
    409             return false;
    410         }
    411 
    412         Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds();
    413         if (rect != null && !rect.isEmpty()) {
    414             bounds.left = rect.x;
    415             bounds.top = rect.y;
    416             bounds.right = rect.x + rect.width;
    417             bounds.bottom = rect.y + rect.height;
    418             return true;
    419         }
    420 
    421         return false;
    422     }
    423 
    424     @LayoutlibDelegate
    425     public static void nGetMatrix(long canvas, long matrix) {
    426         // get the delegate from the native int.
    427         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
    428         if (canvasDelegate == null) {
    429             return;
    430         }
    431 
    432         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
    433         if (matrixDelegate == null) {
    434             return;
    435         }
    436 
    437         AffineTransform transform = canvasDelegate.getSnapshot().getTransform();
    438         matrixDelegate.set(Matrix_Delegate.makeValues(transform));
    439     }
    440 
    441     @LayoutlibDelegate
    442     public static boolean nQuickReject(long nativeCanvas, long path) {
    443         // FIXME properly implement quickReject
    444         return false;
    445     }
    446 
    447     @LayoutlibDelegate
    448     public static boolean nQuickReject(long nativeCanvas,
    449                                                      float left, float top,
    450                                                      float right, float bottom) {
    451         // FIXME properly implement quickReject
    452         return false;
    453     }
    454 
    455     @LayoutlibDelegate
    456     /*package*/ static long nGetNativeFinalizer() {
    457         synchronized (Canvas_Delegate.class) {
    458             if (sFinalizer == -1) {
    459                 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(nativePtr -> {
    460                     Canvas_Delegate delegate = Canvas_Delegate.getDelegate(nativePtr);
    461                     if (delegate != null) {
    462                         delegate.dispose();
    463                     }
    464                     sManager.removeJavaReferenceFor(nativePtr);
    465                 });
    466             }
    467         }
    468         return sFinalizer;
    469     }
    470 
    471     @LayoutlibDelegate
    472     /*package*/ static void nSetCompatibilityVersion(int apiLevel) {
    473         // Unsupported by layoutlib, do nothing
    474     }
    475 
    476     private Canvas_Delegate(Bitmap_Delegate bitmap) {
    477         super(bitmap);
    478     }
    479 
    480     private Canvas_Delegate() {
    481         super();
    482     }
    483 }
    484 
    485