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      */
getDelegate(Canvas canvas)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      */
getDelegate(long native_canvas)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      */
getSnapshot()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      */
getDrawFilter()83     public DrawFilter_Delegate getDrawFilter() {
84         return mDrawFilter;
85     }
86 
87     // ---- native methods ----
88 
89     @LayoutlibDelegate
nFreeCaches()90     /*package*/ static void nFreeCaches() {
91         // nothing to be done here.
92     }
93 
94     @LayoutlibDelegate
nFreeTextLayoutCaches()95     /*package*/ static void nFreeTextLayoutCaches() {
96         // nothing to be done here yet.
97     }
98 
99     @LayoutlibDelegate
nInitRaster(long bitmapHandle)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
nSetBitmap(long canvas, long bitmapHandle)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
nIsOpaque(long nativeCanvas)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
nGetWidth(long nativeCanvas)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
nGetHeight(long nativeCanvas)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
nSave(long nativeCanvas, int saveFlags)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
nSaveLayer(long nativeCanvas, float l, float t, float r, float b, long paint, int layerFlags)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
nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b)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
nSaveLayerAlpha(long nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags)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
nRestore(long nativeCanvas)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
nRestoreToCount(long nativeCanvas, int saveCount)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
nGetSaveCount(long nativeCanvas)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
nTranslate(long nativeCanvas, float dx, float dy)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
nScale(long nativeCanvas, float sx, float sy)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
nRotate(long nativeCanvas, float degrees)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
nSkew(long nativeCanvas, float kx, float ky)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
nConcat(long nCanvas, long nMatrix)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
nSetMatrix(long nCanvas, long nMatrix)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
nClipRect(long nCanvas, float left, float top, float right, float bottom, int regionOp)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
nClipPath(long nativeCanvas, long nativePath, int regionOp)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
nSetDrawFilter(long nativeCanvas, long nativeFilter)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
nGetClipBounds(long nativeCanvas, Rect bounds)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
nGetMatrix(long canvas, long matrix)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
nQuickReject(long nativeCanvas, long path)442     public static boolean nQuickReject(long nativeCanvas, long path) {
443         // FIXME properly implement quickReject
444         return false;
445     }
446 
447     @LayoutlibDelegate
nQuickReject(long nativeCanvas, float left, float top, float right, float bottom)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
nGetNativeFinalizer()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
nSetCompatibilityVersion(int apiLevel)472     /*package*/ static void nSetCompatibilityVersion(int apiLevel) {
473         // Unsupported by layoutlib, do nothing
474     }
475 
Canvas_Delegate(Bitmap_Delegate bitmap)476     private Canvas_Delegate(Bitmap_Delegate bitmap) {
477         super(bitmap);
478     }
479 
Canvas_Delegate()480     private Canvas_Delegate() {
481         super();
482     }
483 }
484 
485