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.view;
18
19import android.annotation.NonNull;
20import android.graphics.Bitmap;
21import android.graphics.Canvas;
22import android.graphics.CanvasProperty;
23import android.graphics.NinePatch;
24import android.graphics.Paint;
25import android.graphics.Path;
26import android.graphics.Picture;
27import android.graphics.Rect;
28import android.graphics.RectF;
29import android.util.Pools.SynchronizedPool;
30
31/**
32 * An implementation of a GL canvas that records drawing operations.
33 * This is intended for use with a DisplayList. This class keeps a list of all the Paint and
34 * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
35 * the DisplayList is still holding a native reference to the memory.
36 *
37 * @hide
38 */
39public class DisplayListCanvas extends Canvas {
40    // The recording canvas pool should be large enough to handle a deeply nested
41    // view hierarchy because display lists are generated recursively.
42    private static final int POOL_LIMIT = 25;
43
44    private static final SynchronizedPool<DisplayListCanvas> sPool =
45            new SynchronizedPool<DisplayListCanvas>(POOL_LIMIT);
46
47    RenderNode mNode;
48    private int mWidth;
49    private int mHeight;
50
51    static DisplayListCanvas obtain(@NonNull RenderNode node) {
52        if (node == null) throw new IllegalArgumentException("node cannot be null");
53        DisplayListCanvas canvas = sPool.acquire();
54        if (canvas == null) {
55            canvas = new DisplayListCanvas();
56        }
57        canvas.mNode = node;
58        return canvas;
59    }
60
61    void recycle() {
62        mNode = null;
63        sPool.release(this);
64    }
65
66    long finishRecording() {
67        return nFinishRecording(mNativeCanvasWrapper);
68    }
69
70    @Override
71    public boolean isRecordingFor(Object o) {
72        return o == mNode;
73    }
74
75    ///////////////////////////////////////////////////////////////////////////
76    // JNI
77    ///////////////////////////////////////////////////////////////////////////
78
79    private static native boolean nIsAvailable();
80    private static boolean sIsAvailable = nIsAvailable();
81
82    static boolean isAvailable() {
83        return sIsAvailable;
84    }
85
86    ///////////////////////////////////////////////////////////////////////////
87    // Constructors
88    ///////////////////////////////////////////////////////////////////////////
89
90    private DisplayListCanvas() {
91        super(nCreateDisplayListCanvas());
92        mDensity = 0; // disable bitmap density scaling
93    }
94
95    private static native long nCreateDisplayListCanvas();
96
97    ///////////////////////////////////////////////////////////////////////////
98    // Canvas management
99    ///////////////////////////////////////////////////////////////////////////
100
101
102    @Override
103    public void setDensity(int density) {
104        // drop silently, since DisplayListCanvas doesn't perform density scaling
105    }
106
107    @Override
108    public boolean isHardwareAccelerated() {
109        return true;
110    }
111
112    @Override
113    public void setBitmap(Bitmap bitmap) {
114        throw new UnsupportedOperationException();
115    }
116
117    @Override
118    public boolean isOpaque() {
119        return false;
120    }
121
122    @Override
123    public int getWidth() {
124        return mWidth;
125    }
126
127    @Override
128    public int getHeight() {
129        return mHeight;
130    }
131
132    @Override
133    public int getMaximumBitmapWidth() {
134        return nGetMaximumTextureWidth();
135    }
136
137    @Override
138    public int getMaximumBitmapHeight() {
139        return nGetMaximumTextureHeight();
140    }
141
142    private static native int nGetMaximumTextureWidth();
143    private static native int nGetMaximumTextureHeight();
144
145    /**
146     * Returns the native OpenGLRenderer object.
147     */
148    long getRenderer() {
149        return mNativeCanvasWrapper;
150    }
151
152    ///////////////////////////////////////////////////////////////////////////
153    // Setup
154    ///////////////////////////////////////////////////////////////////////////
155
156    @Override
157    public void setViewport(int width, int height) {
158        mWidth = width;
159        mHeight = height;
160
161        nSetViewport(mNativeCanvasWrapper, width, height);
162    }
163
164    private static native void nSetViewport(long renderer,
165            int width, int height);
166
167    @Override
168    public void setHighContrastText(boolean highContrastText) {
169        nSetHighContrastText(mNativeCanvasWrapper, highContrastText);
170    }
171
172    private static native void nSetHighContrastText(long renderer, boolean highContrastText);
173
174    @Override
175    public void insertReorderBarrier() {
176        nInsertReorderBarrier(mNativeCanvasWrapper, true);
177    }
178
179    @Override
180    public void insertInorderBarrier() {
181        nInsertReorderBarrier(mNativeCanvasWrapper, false);
182    }
183
184    private static native void nInsertReorderBarrier(long renderer, boolean enableReorder);
185
186    /**
187     * Invoked before any drawing operation is performed in this canvas.
188     *
189     * @param dirty The dirty rectangle to update, can be null.
190     */
191    public void onPreDraw(Rect dirty) {
192        if (dirty != null) {
193            nPrepareDirty(mNativeCanvasWrapper, dirty.left, dirty.top, dirty.right, dirty.bottom);
194        } else {
195            nPrepare(mNativeCanvasWrapper);
196        }
197    }
198
199    private static native void nPrepare(long renderer);
200    private static native void nPrepareDirty(long renderer, int left, int top, int right, int bottom);
201
202    /**
203     * Invoked after all drawing operation have been performed.
204     */
205    public void onPostDraw() {
206        nFinish(mNativeCanvasWrapper);
207    }
208
209    private static native void nFinish(long renderer);
210
211    ///////////////////////////////////////////////////////////////////////////
212    // Functor
213    ///////////////////////////////////////////////////////////////////////////
214
215    /**
216     * Calls the function specified with the drawGLFunction function pointer. This is
217     * functionality used by webkit for calling into their renderer from our display lists.
218     * This function may return true if an invalidation is needed after the call.
219     *
220     * @param drawGLFunction A native function pointer
221     */
222    public void callDrawGLFunction2(long drawGLFunction) {
223        nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction);
224    }
225
226    private static native void nCallDrawGLFunction(long renderer, long drawGLFunction);
227
228    ///////////////////////////////////////////////////////////////////////////
229    // Display list
230    ///////////////////////////////////////////////////////////////////////////
231
232    protected static native long nFinishRecording(long renderer);
233
234    /**
235     * Draws the specified display list onto this canvas. The display list can only
236     * be drawn if {@link android.view.RenderNode#isValid()} returns true.
237     *
238     * @param renderNode The RenderNode to draw.
239     */
240    public void drawRenderNode(RenderNode renderNode) {
241        nDrawRenderNode(mNativeCanvasWrapper, renderNode.getNativeDisplayList());
242    }
243
244    private static native void nDrawRenderNode(long renderer, long renderNode);
245
246    ///////////////////////////////////////////////////////////////////////////
247    // Hardware layer
248    ///////////////////////////////////////////////////////////////////////////
249
250    /**
251     * Draws the specified layer onto this canvas.
252     *
253     * @param layer The layer to composite on this canvas
254     * @param x The left coordinate of the layer
255     * @param y The top coordinate of the layer
256     * @param paint The paint used to draw the layer
257     */
258    void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
259        layer.setLayerPaint(paint);
260        nDrawLayer(mNativeCanvasWrapper, layer.getLayerHandle(), x, y);
261    }
262
263    private static native void nDrawLayer(long renderer, long layer, float x, float y);
264
265    ///////////////////////////////////////////////////////////////////////////
266    // Drawing
267    ///////////////////////////////////////////////////////////////////////////
268
269    // TODO: move to Canvas.java
270    @Override
271    public void drawPatch(NinePatch patch, Rect dst, Paint paint) {
272        Bitmap bitmap = patch.getBitmap();
273        throwIfCannotDraw(bitmap);
274        final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
275        nDrawPatch(mNativeCanvasWrapper, bitmap, patch.mNativeChunk,
276                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
277    }
278
279    // TODO: move to Canvas.java
280    @Override
281    public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
282        Bitmap bitmap = patch.getBitmap();
283        throwIfCannotDraw(bitmap);
284        final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
285        nDrawPatch(mNativeCanvasWrapper, bitmap, patch.mNativeChunk,
286                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
287    }
288
289    private static native void nDrawPatch(long renderer, Bitmap bitmap, long chunk,
290            float left, float top, float right, float bottom, long paint);
291
292    public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
293            CanvasProperty<Float> radius, CanvasProperty<Paint> paint) {
294        nDrawCircle(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(),
295                radius.getNativeContainer(), paint.getNativeContainer());
296    }
297
298    private static native void nDrawCircle(long renderer, long propCx,
299            long propCy, long propRadius, long propPaint);
300
301    public void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
302            CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx,
303            CanvasProperty<Float> ry, CanvasProperty<Paint> paint) {
304        nDrawRoundRect(mNativeCanvasWrapper, left.getNativeContainer(), top.getNativeContainer(),
305                right.getNativeContainer(), bottom.getNativeContainer(),
306                rx.getNativeContainer(), ry.getNativeContainer(),
307                paint.getNativeContainer());
308    }
309
310    private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
311            long propRight, long propBottom, long propRx, long propRy, long propPaint);
312
313    // TODO: move this optimization to Canvas.java
314    @Override
315    public void drawPath(Path path, Paint paint) {
316        if (path.isSimplePath) {
317            if (path.rects != null) {
318                nDrawRects(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
319            }
320        } else {
321            super.drawPath(path, paint);
322        }
323    }
324
325    private static native void nDrawRects(long renderer, long region, long paint);
326}
327