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 android.annotation.ColorInt; 20 import android.annotation.NonNull; 21 import android.os.SystemProperties; 22 import android.util.Pools.SynchronizedPool; 23 24 import dalvik.annotation.optimization.CriticalNative; 25 26 /** 27 * A Canvas implementation that records view system drawing operations for deferred rendering. 28 * This is used in combination with RenderNode. This class keeps a list of all the Paint and 29 * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being released while 30 * the RecordingCanvas is still holding a native reference to the memory. 31 * 32 * This is obtained by calling {@link RenderNode#beginRecording()} and is valid until the matching 33 * {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is 34 * internally reused. 35 */ 36 @android.ravenwood.annotation.RavenwoodKeepWholeClass 37 public final class RecordingCanvas extends BaseRecordingCanvas { 38 // The recording canvas pool should be large enough to handle a deeply nested 39 // view hierarchy because display lists are generated recursively. 40 private static final int POOL_LIMIT = 25; 41 42 /** @hide */ getPanelFrameSize()43 private static int getPanelFrameSize() { 44 final int DefaultSize = 150 * 1024 * 1024; // 150 MB; 45 return Math.max(SystemProperties.getInt("ro.hwui.max_texture_allocation_size", DefaultSize), 46 DefaultSize); 47 } 48 49 /** @hide */ 50 public static final int MAX_BITMAP_SIZE = getPanelFrameSize(); 51 52 private static final SynchronizedPool<RecordingCanvas> sPool = 53 new SynchronizedPool<>(POOL_LIMIT); 54 55 /** 56 * TODO: Temporarily exposed for RenderNodeAnimator(Set) 57 * @hide */ 58 public RenderNode mNode; 59 private int mWidth; 60 private int mHeight; 61 62 /*package*/ obtain(@onNull RenderNode node, int width, int height)63 static RecordingCanvas obtain(@NonNull RenderNode node, int width, int height) { 64 if (node == null) throw new IllegalArgumentException("node cannot be null"); 65 RecordingCanvas canvas = sPool.acquire(); 66 if (canvas == null) { 67 canvas = new RecordingCanvas(node, width, height); 68 } else { 69 nResetDisplayListCanvas(canvas.mNativeCanvasWrapper, node.mNativeRenderNode, 70 width, height); 71 } 72 canvas.mNode = node; 73 canvas.mWidth = width; 74 canvas.mHeight = height; 75 return canvas; 76 } 77 78 /*package*/ recycle()79 void recycle() { 80 mNode = null; 81 sPool.release(this); 82 } 83 84 /*package*/ finishRecording(RenderNode node)85 void finishRecording(RenderNode node) { 86 nFinishRecording(mNativeCanvasWrapper, node.mNativeRenderNode); 87 } 88 89 /////////////////////////////////////////////////////////////////////////// 90 // Constructors 91 /////////////////////////////////////////////////////////////////////////// 92 RecordingCanvas(@onNull RenderNode node, int width, int height)93 private RecordingCanvas(@NonNull RenderNode node, int width, int height) { 94 super(nCreateDisplayListCanvas(node.mNativeRenderNode, width, height)); 95 mDensity = 0; // disable bitmap density scaling 96 } 97 98 /////////////////////////////////////////////////////////////////////////// 99 // Canvas management 100 /////////////////////////////////////////////////////////////////////////// 101 102 103 @Override setDensity(int density)104 public void setDensity(int density) { 105 // drop silently, since RecordingCanvas doesn't perform density scaling 106 } 107 108 @Override isHardwareAccelerated()109 public boolean isHardwareAccelerated() { 110 return true; 111 } 112 113 @Override setBitmap(Bitmap bitmap)114 public void setBitmap(Bitmap bitmap) { 115 throw new UnsupportedOperationException(); 116 } 117 118 @Override isOpaque()119 public boolean isOpaque() { 120 return false; 121 } 122 123 @Override getWidth()124 public int getWidth() { 125 return mWidth; 126 } 127 128 @Override getHeight()129 public int getHeight() { 130 return mHeight; 131 } 132 133 @Override getMaximumBitmapWidth()134 public int getMaximumBitmapWidth() { 135 return nGetMaximumTextureWidth(); 136 } 137 138 @Override getMaximumBitmapHeight()139 public int getMaximumBitmapHeight() { 140 return nGetMaximumTextureHeight(); 141 } 142 143 /////////////////////////////////////////////////////////////////////////// 144 // Setup 145 /////////////////////////////////////////////////////////////////////////// 146 147 @Override enableZ()148 public void enableZ() { 149 nEnableZ(mNativeCanvasWrapper, true); 150 } 151 152 @Override disableZ()153 public void disableZ() { 154 nEnableZ(mNativeCanvasWrapper, false); 155 } 156 157 /////////////////////////////////////////////////////////////////////////// 158 // WebView 159 /////////////////////////////////////////////////////////////////////////// 160 161 /** 162 * Calls the provided functor that was created via WebViewFunctor_create() 163 * @hide 164 */ drawWebViewFunctor(int functor)165 public void drawWebViewFunctor(int functor) { 166 nDrawWebViewFunctor(mNativeCanvasWrapper, functor); 167 } 168 169 /////////////////////////////////////////////////////////////////////////// 170 // Display list 171 /////////////////////////////////////////////////////////////////////////// 172 173 /** 174 * Draws the specified display list onto this canvas. 175 * 176 * @param renderNode The RenderNode to draw. 177 */ 178 @Override drawRenderNode(@onNull RenderNode renderNode)179 public void drawRenderNode(@NonNull RenderNode renderNode) { 180 nDrawRenderNode(mNativeCanvasWrapper, renderNode.mNativeRenderNode); 181 } 182 183 /////////////////////////////////////////////////////////////////////////// 184 // Hardware layer 185 /////////////////////////////////////////////////////////////////////////// 186 187 /** 188 * Draws the specified layer onto this canvas. 189 * 190 * @param layer The layer to composite on this canvas 191 * @hide TODO: Make this a SystemApi for b/155905258 192 */ drawTextureLayer(@onNull TextureLayer layer)193 public void drawTextureLayer(@NonNull TextureLayer layer) { 194 nDrawTextureLayer(mNativeCanvasWrapper, layer.getLayerHandle()); 195 } 196 197 /////////////////////////////////////////////////////////////////////////// 198 // Drawing 199 /////////////////////////////////////////////////////////////////////////// 200 201 /** 202 * Draws a circle 203 * 204 * @param cx 205 * @param cy 206 * @param radius 207 * @param paint 208 * 209 * @hide 210 */ drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy, CanvasProperty<Float> radius, CanvasProperty<Paint> paint)211 public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy, 212 CanvasProperty<Float> radius, CanvasProperty<Paint> paint) { 213 nDrawCircle(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(), 214 radius.getNativeContainer(), paint.getNativeContainer()); 215 } 216 217 /** 218 * Draws a ripple 219 * 220 * @param cx 221 * @param cy 222 * @param radius 223 * @param paint 224 * @param progress 225 * @param shader 226 * 227 * @hide 228 */ drawRipple(CanvasProperty<Float> cx, CanvasProperty<Float> cy, CanvasProperty<Float> radius, CanvasProperty<Paint> paint, CanvasProperty<Float> progress, CanvasProperty<Float> turbulencePhase, @ColorInt int color, RuntimeShader shader)229 public void drawRipple(CanvasProperty<Float> cx, CanvasProperty<Float> cy, 230 CanvasProperty<Float> radius, CanvasProperty<Paint> paint, 231 CanvasProperty<Float> progress, CanvasProperty<Float> turbulencePhase, 232 @ColorInt int color, RuntimeShader shader) { 233 nDrawRipple(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(), 234 radius.getNativeContainer(), paint.getNativeContainer(), 235 progress.getNativeContainer(), turbulencePhase.getNativeContainer(), 236 color, shader.getNativeShaderBuilder()); 237 } 238 239 /** 240 * Draws a round rect 241 * 242 * @param left 243 * @param top 244 * @param right 245 * @param bottom 246 * @param rx 247 * @param ry 248 * @param paint 249 * 250 * @hide 251 */ drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top, CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx, CanvasProperty<Float> ry, CanvasProperty<Paint> paint)252 public void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top, 253 CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx, 254 CanvasProperty<Float> ry, CanvasProperty<Paint> paint) { 255 nDrawRoundRect(mNativeCanvasWrapper, left.getNativeContainer(), top.getNativeContainer(), 256 right.getNativeContainer(), bottom.getNativeContainer(), 257 rx.getNativeContainer(), ry.getNativeContainer(), 258 paint.getNativeContainer()); 259 } 260 261 /** @hide */ 262 @Override throwIfCannotDraw(Bitmap bitmap)263 protected void throwIfCannotDraw(Bitmap bitmap) { 264 super.throwIfCannotDraw(bitmap); 265 int bitmapSize = bitmap.getByteCount(); 266 if (bitmap.getConfig() != Bitmap.Config.HARDWARE && bitmapSize > MAX_BITMAP_SIZE) { 267 throw new RuntimeException( 268 "Canvas: trying to draw too large(" + bitmapSize + "bytes) bitmap."); 269 } 270 } 271 272 273 // ------------------ Critical JNI ------------------------ 274 275 @CriticalNative nCreateDisplayListCanvas(long node, int width, int height)276 private static native long nCreateDisplayListCanvas(long node, int width, int height); 277 @CriticalNative nResetDisplayListCanvas(long canvas, long node, int width, int height)278 private static native void nResetDisplayListCanvas(long canvas, long node, 279 int width, int height); nGetMaximumTextureWidth()280 private static native int nGetMaximumTextureWidth(); nGetMaximumTextureHeight()281 private static native int nGetMaximumTextureHeight(); 282 @CriticalNative nEnableZ(long renderer, boolean enableZ)283 private static native void nEnableZ(long renderer, boolean enableZ); 284 @CriticalNative nFinishRecording(long renderer, long renderNode)285 private static native void nFinishRecording(long renderer, long renderNode); 286 @CriticalNative nDrawRenderNode(long renderer, long renderNode)287 private static native void nDrawRenderNode(long renderer, long renderNode); 288 @CriticalNative nDrawTextureLayer(long renderer, long layer)289 private static native void nDrawTextureLayer(long renderer, long layer); 290 @CriticalNative nDrawCircle(long renderer, long propCx, long propCy, long propRadius, long propPaint)291 private static native void nDrawCircle(long renderer, long propCx, 292 long propCy, long propRadius, long propPaint); 293 @CriticalNative nDrawRipple(long renderer, long propCx, long propCy, long propRadius, long propPaint, long propProgress, long turbulencePhase, int color, long runtimeEffect)294 private static native void nDrawRipple(long renderer, long propCx, long propCy, long propRadius, 295 long propPaint, long propProgress, long turbulencePhase, int color, long runtimeEffect); 296 @CriticalNative nDrawRoundRect(long renderer, long propLeft, long propTop, long propRight, long propBottom, long propRx, long propRy, long propPaint)297 private static native void nDrawRoundRect(long renderer, long propLeft, long propTop, 298 long propRight, long propBottom, long propRx, long propRy, long propPaint); 299 @CriticalNative nDrawWebViewFunctor(long canvas, int functor)300 private static native void nDrawWebViewFunctor(long canvas, int functor); 301 } 302