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.annotation.Nullable; 21import android.graphics.Bitmap; 22import android.graphics.Canvas; 23import android.graphics.CanvasProperty; 24import android.graphics.Paint; 25import android.util.Pools.SynchronizedPool; 26 27/** 28 * An implementation of a GL canvas that records drawing operations. 29 * This is intended for use with a DisplayList. This class keeps a list of all the Paint and 30 * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while 31 * the DisplayList is still holding a native reference to the memory. 32 * 33 * @hide 34 */ 35public class DisplayListCanvas extends Canvas { 36 // The recording canvas pool should be large enough to handle a deeply nested 37 // view hierarchy because display lists are generated recursively. 38 private static final int POOL_LIMIT = 25; 39 40 private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB 41 42 private static final SynchronizedPool<DisplayListCanvas> sPool = 43 new SynchronizedPool<DisplayListCanvas>(POOL_LIMIT); 44 45 RenderNode mNode; 46 private int mWidth; 47 private int mHeight; 48 49 static DisplayListCanvas obtain(@NonNull RenderNode node, int width, int height) { 50 if (node == null) throw new IllegalArgumentException("node cannot be null"); 51 DisplayListCanvas canvas = sPool.acquire(); 52 if (canvas == null) { 53 canvas = new DisplayListCanvas(width, height); 54 } else { 55 nResetDisplayListCanvas(canvas.mNativeCanvasWrapper, width, height); 56 } 57 canvas.mNode = node; 58 canvas.mWidth = width; 59 canvas.mHeight = height; 60 return canvas; 61 } 62 63 void recycle() { 64 mNode = null; 65 sPool.release(this); 66 } 67 68 long finishRecording() { 69 return nFinishRecording(mNativeCanvasWrapper); 70 } 71 72 @Override 73 public boolean isRecordingFor(Object o) { 74 return o == mNode; 75 } 76 77 /////////////////////////////////////////////////////////////////////////// 78 // JNI 79 /////////////////////////////////////////////////////////////////////////// 80 81 private static native boolean nIsAvailable(); 82 private static boolean sIsAvailable = nIsAvailable(); 83 84 static boolean isAvailable() { 85 return sIsAvailable; 86 } 87 88 /////////////////////////////////////////////////////////////////////////// 89 // Constructors 90 /////////////////////////////////////////////////////////////////////////// 91 92 private DisplayListCanvas(int width, int height) { 93 super(nCreateDisplayListCanvas(width, height)); 94 mDensity = 0; // disable bitmap density scaling 95 } 96 97 private static native long nCreateDisplayListCanvas(int width, int height); 98 private static native void nResetDisplayListCanvas(long canvas, int width, int height); 99 100 /////////////////////////////////////////////////////////////////////////// 101 // Canvas management 102 /////////////////////////////////////////////////////////////////////////// 103 104 105 @Override 106 public void setDensity(int density) { 107 // drop silently, since DisplayListCanvas doesn't perform density scaling 108 } 109 110 @Override 111 public boolean isHardwareAccelerated() { 112 return true; 113 } 114 115 @Override 116 public void setBitmap(Bitmap bitmap) { 117 throw new UnsupportedOperationException(); 118 } 119 120 @Override 121 public boolean isOpaque() { 122 return false; 123 } 124 125 @Override 126 public int getWidth() { 127 return mWidth; 128 } 129 130 @Override 131 public int getHeight() { 132 return mHeight; 133 } 134 135 @Override 136 public int getMaximumBitmapWidth() { 137 return nGetMaximumTextureWidth(); 138 } 139 140 @Override 141 public int getMaximumBitmapHeight() { 142 return nGetMaximumTextureHeight(); 143 } 144 145 private static native int nGetMaximumTextureWidth(); 146 private static native int nGetMaximumTextureHeight(); 147 148 /////////////////////////////////////////////////////////////////////////// 149 // Setup 150 /////////////////////////////////////////////////////////////////////////// 151 152 @Override 153 public void insertReorderBarrier() { 154 nInsertReorderBarrier(mNativeCanvasWrapper, true); 155 } 156 157 @Override 158 public void insertInorderBarrier() { 159 nInsertReorderBarrier(mNativeCanvasWrapper, false); 160 } 161 162 private static native void nInsertReorderBarrier(long renderer, boolean enableReorder); 163 164 /////////////////////////////////////////////////////////////////////////// 165 // Functor 166 /////////////////////////////////////////////////////////////////////////// 167 168 /** 169 * Records the functor specified with the drawGLFunction function pointer. This is 170 * functionality used by webview for calling into their renderer from our display lists. 171 * 172 * @param drawGLFunction A native function pointer 173 */ 174 public void callDrawGLFunction2(long drawGLFunction) { 175 nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction, null); 176 } 177 178 /** 179 * Records the functor specified with the drawGLFunction function pointer. This is 180 * functionality used by webview for calling into their renderer from our display lists. 181 * 182 * @param drawGLFunction A native function pointer 183 * @param releasedCallback Called when the display list is destroyed, and thus 184 * the functor is no longer referenced by this canvas's display list. 185 * 186 * NOTE: The callback does *not* necessarily mean that there are no longer 187 * any references to the functor, just that the reference from this specific 188 * canvas's display list has been released. 189 */ 190 public void drawGLFunctor2(long drawGLFunctor, @Nullable Runnable releasedCallback) { 191 nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback); 192 } 193 194 private static native void nCallDrawGLFunction(long renderer, 195 long drawGLFunction, Runnable releasedCallback); 196 197 /////////////////////////////////////////////////////////////////////////// 198 // Display list 199 /////////////////////////////////////////////////////////////////////////// 200 201 protected static native long nFinishRecording(long renderer); 202 203 /** 204 * Draws the specified display list onto this canvas. The display list can only 205 * be drawn if {@link android.view.RenderNode#isValid()} returns true. 206 * 207 * @param renderNode The RenderNode to draw. 208 */ 209 public void drawRenderNode(RenderNode renderNode) { 210 nDrawRenderNode(mNativeCanvasWrapper, renderNode.getNativeDisplayList()); 211 } 212 213 private static native void nDrawRenderNode(long renderer, long renderNode); 214 215 /////////////////////////////////////////////////////////////////////////// 216 // Hardware layer 217 /////////////////////////////////////////////////////////////////////////// 218 219 /** 220 * Draws the specified layer onto this canvas. 221 * 222 * @param layer The layer to composite on this canvas 223 */ 224 void drawHardwareLayer(HardwareLayer layer) { 225 nDrawLayer(mNativeCanvasWrapper, layer.getLayerHandle()); 226 } 227 228 private static native void nDrawLayer(long renderer, long layer); 229 230 /////////////////////////////////////////////////////////////////////////// 231 // Drawing 232 /////////////////////////////////////////////////////////////////////////// 233 234 public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy, 235 CanvasProperty<Float> radius, CanvasProperty<Paint> paint) { 236 nDrawCircle(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(), 237 radius.getNativeContainer(), paint.getNativeContainer()); 238 } 239 240 private static native void nDrawCircle(long renderer, long propCx, 241 long propCy, long propRadius, long propPaint); 242 243 public void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top, 244 CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx, 245 CanvasProperty<Float> ry, CanvasProperty<Paint> paint) { 246 nDrawRoundRect(mNativeCanvasWrapper, left.getNativeContainer(), top.getNativeContainer(), 247 right.getNativeContainer(), bottom.getNativeContainer(), 248 rx.getNativeContainer(), ry.getNativeContainer(), 249 paint.getNativeContainer()); 250 } 251 252 private static native void nDrawRoundRect(long renderer, long propLeft, long propTop, 253 long propRight, long propBottom, long propRx, long propRy, long propPaint); 254 255 @Override 256 protected void throwIfCannotDraw(Bitmap bitmap) { 257 super.throwIfCannotDraw(bitmap); 258 int bitmapSize = bitmap.getByteCount(); 259 if (bitmapSize > MAX_BITMAP_SIZE) { 260 throw new RuntimeException( 261 "Canvas: trying to draw too large(" + bitmapSize + "bytes) bitmap."); 262 } 263 } 264} 265