Canvas_Delegate.java revision dcc3e373c444b488f84a97654ef14667d183a423
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
17package android.graphics;
18
19import com.android.ide.common.rendering.api.LayoutLog;
20import com.android.layoutlib.bridge.Bridge;
21import com.android.layoutlib.bridge.impl.DelegateManager;
22import com.android.layoutlib.bridge.impl.GcSnapshot;
23import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
24
25import android.annotation.Nullable;
26import android.graphics.Bitmap.Config;
27
28import java.awt.Graphics2D;
29import java.awt.Rectangle;
30import java.awt.geom.AffineTransform;
31
32import 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 */
48public 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(@Nullable Bitmap bitmap) {
101        long nativeBitmapOrZero = 0;
102        if (bitmap != null) {
103            nativeBitmapOrZero = bitmap.getNativeInstance();
104        }
105        if (nativeBitmapOrZero > 0) {
106            // get the Bitmap from the int
107            Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
108
109            // create a new Canvas_Delegate with the given bitmap and return its new native int.
110            Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
111
112            return sManager.addNewDelegate(newDelegate);
113        }
114
115        // create a new Canvas_Delegate and return its new native int.
116        Canvas_Delegate newDelegate = new Canvas_Delegate();
117
118        return sManager.addNewDelegate(newDelegate);
119    }
120
121    @LayoutlibDelegate
122    public static void nSetBitmap(long canvas, Bitmap bitmap) {
123        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
124        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
125        if (canvasDelegate == null || bitmapDelegate==null) {
126            return;
127        }
128        canvasDelegate.mBitmap = bitmapDelegate;
129        canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate);
130    }
131
132    @LayoutlibDelegate
133    public static boolean nIsOpaque(long nativeCanvas) {
134        // get the delegate from the native int.
135        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
136        if (canvasDelegate == null) {
137            return false;
138        }
139
140        return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
141    }
142
143    @LayoutlibDelegate
144    public static int nGetWidth(long nativeCanvas) {
145        // get the delegate from the native int.
146        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
147        if (canvasDelegate == null) {
148            return 0;
149        }
150
151        return canvasDelegate.mBitmap.getImage().getWidth();
152    }
153
154    @LayoutlibDelegate
155    public static int nGetHeight(long nativeCanvas) {
156        // get the delegate from the native int.
157        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
158        if (canvasDelegate == null) {
159            return 0;
160        }
161
162        return canvasDelegate.mBitmap.getImage().getHeight();
163    }
164
165    @LayoutlibDelegate
166    public static int nSave(long nativeCanvas, int saveFlags) {
167        // get the delegate from the native int.
168        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
169        if (canvasDelegate == null) {
170            return 0;
171        }
172
173        return canvasDelegate.save(saveFlags);
174    }
175
176    @LayoutlibDelegate
177    public static int nSaveLayer(long nativeCanvas, float l,
178                                               float t, float r, float b,
179                                               long paint, int layerFlags) {
180        // get the delegate from the native int.
181        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
182        if (canvasDelegate == null) {
183            return 0;
184        }
185
186        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
187        if (paintDelegate == null) {
188            return 0;
189        }
190
191        return canvasDelegate.saveLayer(new RectF(l, t, r, b),
192                paintDelegate, layerFlags);
193    }
194
195    @LayoutlibDelegate
196    public static int nSaveLayerAlpha(long nativeCanvas, float l,
197                                                    float t, float r, float b,
198                                                    int alpha, int layerFlags) {
199        // get the delegate from the native int.
200        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
201        if (canvasDelegate == null) {
202            return 0;
203        }
204
205        return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
206    }
207
208    @LayoutlibDelegate
209    public static boolean nRestore(long nativeCanvas) {
210        // FIXME: implement throwOnUnderflow.
211        // get the delegate from the native int.
212        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
213        if (canvasDelegate == null) {
214            return false;
215        }
216
217        canvasDelegate.restore();
218        return true;
219    }
220
221    @LayoutlibDelegate
222    public static void nRestoreToCount(long nativeCanvas, int saveCount) {
223        // FIXME: implement throwOnUnderflow.
224        // get the delegate from the native int.
225        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
226        if (canvasDelegate == null) {
227            return;
228        }
229
230        canvasDelegate.restoreTo(saveCount);
231    }
232
233    @LayoutlibDelegate
234    public static int nGetSaveCount(long nativeCanvas) {
235        // get the delegate from the native int.
236        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
237        if (canvasDelegate == null) {
238            return 0;
239        }
240
241        return canvasDelegate.getSnapshot().size();
242    }
243
244    @LayoutlibDelegate
245   public static void nTranslate(long nativeCanvas, float dx, float dy) {
246        // get the delegate from the native int.
247        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
248        if (canvasDelegate == null) {
249            return;
250        }
251
252        canvasDelegate.getSnapshot().translate(dx, dy);
253    }
254
255    @LayoutlibDelegate
256       public static void nScale(long nativeCanvas, float sx, float sy) {
257            // get the delegate from the native int.
258            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
259            if (canvasDelegate == null) {
260                return;
261            }
262
263            canvasDelegate.getSnapshot().scale(sx, sy);
264        }
265
266    @LayoutlibDelegate
267    public static void nRotate(long nativeCanvas, float degrees) {
268        // get the delegate from the native int.
269        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
270        if (canvasDelegate == null) {
271            return;
272        }
273
274        canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
275    }
276
277    @LayoutlibDelegate
278   public static void nSkew(long nativeCanvas, float kx, float ky) {
279        // get the delegate from the native int.
280        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
281        if (canvasDelegate == null) {
282            return;
283        }
284
285        // get the current top graphics2D object.
286        GcSnapshot g = canvasDelegate.getSnapshot();
287
288        // get its current matrix
289        AffineTransform currentTx = g.getTransform();
290        // get the AffineTransform for the given skew.
291        float[] mtx = Matrix_Delegate.getSkew(kx, ky);
292        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
293
294        // combine them so that the given matrix is applied after.
295        currentTx.preConcatenate(matrixTx);
296
297        // give it to the graphics2D as a new matrix replacing all previous transform
298        g.setTransform(currentTx);
299    }
300
301    @LayoutlibDelegate
302    public static void nConcat(long nCanvas, long nMatrix) {
303        // get the delegate from the native int.
304        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
305        if (canvasDelegate == null) {
306            return;
307        }
308
309        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
310        if (matrixDelegate == null) {
311            return;
312        }
313
314        // get the current top graphics2D object.
315        GcSnapshot snapshot = canvasDelegate.getSnapshot();
316
317        // get its current matrix
318        AffineTransform currentTx = snapshot.getTransform();
319        // get the AffineTransform of the given matrix
320        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
321
322        // combine them so that the given matrix is applied after.
323        currentTx.concatenate(matrixTx);
324
325        // give it to the graphics2D as a new matrix replacing all previous transform
326        snapshot.setTransform(currentTx);
327    }
328
329    @LayoutlibDelegate
330    public static void nSetMatrix(long nCanvas, long nMatrix) {
331        // get the delegate from the native int.
332        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
333        if (canvasDelegate == null) {
334            return;
335        }
336
337        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
338        if (matrixDelegate == null) {
339            return;
340        }
341
342        // get the current top graphics2D object.
343        GcSnapshot snapshot = canvasDelegate.getSnapshot();
344
345        // get the AffineTransform of the given matrix
346        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
347
348        // give it to the graphics2D as a new matrix replacing all previous transform
349        snapshot.setTransform(matrixTx);
350
351        if (matrixDelegate.hasPerspective()) {
352            assert false;
353            Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE,
354                    "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
355                    "supports affine transformations.", null, null /*data*/);
356        }
357    }
358
359    @LayoutlibDelegate
360    public static boolean nClipRect(long nCanvas,
361                                                  float left, float top,
362                                                  float right, float bottom,
363                                                  int regionOp) {
364        // get the delegate from the native int.
365        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
366        if (canvasDelegate == null) {
367            return false;
368        }
369
370        return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
371    }
372
373    @LayoutlibDelegate
374    public static boolean nClipPath(long nativeCanvas,
375                                                  long nativePath,
376                                                  int regionOp) {
377        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
378        if (canvasDelegate == null) {
379            return true;
380        }
381
382        Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath);
383        if (pathDelegate == null) {
384            return true;
385        }
386
387        return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp);
388    }
389
390    @LayoutlibDelegate
391    public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
392        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
393        if (canvasDelegate == null) {
394            return;
395        }
396
397        canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter);
398
399        if (canvasDelegate.mDrawFilter != null && !canvasDelegate.mDrawFilter.isSupported()) {
400            Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER,
401                    canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/);
402        }
403    }
404
405    @LayoutlibDelegate
406    public static boolean nGetClipBounds(long nativeCanvas,
407                                                       Rect bounds) {
408        // get the delegate from the native int.
409        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
410        if (canvasDelegate == null) {
411            return false;
412        }
413
414        Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds();
415        if (rect != null && !rect.isEmpty()) {
416            bounds.left = rect.x;
417            bounds.top = rect.y;
418            bounds.right = rect.x + rect.width;
419            bounds.bottom = rect.y + rect.height;
420            return true;
421        }
422
423        return false;
424    }
425
426    @LayoutlibDelegate
427    public static void nGetMatrix(long canvas, long matrix) {
428        // get the delegate from the native int.
429        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
430        if (canvasDelegate == null) {
431            return;
432        }
433
434        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
435        if (matrixDelegate == null) {
436            return;
437        }
438
439        AffineTransform transform = canvasDelegate.getSnapshot().getTransform();
440        matrixDelegate.set(Matrix_Delegate.makeValues(transform));
441    }
442
443    @LayoutlibDelegate
444    public static boolean nQuickReject(long nativeCanvas, long path) {
445        // FIXME properly implement quickReject
446        return false;
447    }
448
449    @LayoutlibDelegate
450    public static boolean nQuickReject(long nativeCanvas,
451                                                     float left, float top,
452                                                     float right, float bottom) {
453        // FIXME properly implement quickReject
454        return false;
455    }
456
457    @LayoutlibDelegate
458    /*package*/ static long nGetNativeFinalizer() {
459        synchronized (Canvas_Delegate.class) {
460            if (sFinalizer == -1) {
461                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(nativePtr -> {
462                    Canvas_Delegate delegate = Canvas_Delegate.getDelegate(nativePtr);
463                    if (delegate != null) {
464                        delegate.dispose();
465                    }
466                    sManager.removeJavaReferenceFor(nativePtr);
467                });
468            }
469        }
470        return sFinalizer;
471    }
472
473    private Canvas_Delegate(Bitmap_Delegate bitmap) {
474        super(bitmap);
475    }
476
477    private Canvas_Delegate() {
478        super();
479    }
480}
481
482