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