1 /*
2 * Copyright (C) 2016 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 #include "SkiaRecordingCanvas.h"
18 #include "hwui/Paint.h"
19 #include <SkImagePriv.h>
20 #include "CanvasTransform.h"
21 #ifdef __ANDROID__ // Layoutlib does not support Layers
22 #include "Layer.h"
23 #include "LayerDrawable.h"
24 #endif
25 #include "NinePatchUtils.h"
26 #include "RenderNode.h"
27 #include "pipeline/skia/AnimatedDrawables.h"
28 #ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
29 #include "pipeline/skia/GLFunctorDrawable.h"
30 #include "pipeline/skia/VkFunctorDrawable.h"
31 #include "pipeline/skia/VkInteropFunctorDrawable.h"
32 #endif
33
34 namespace android {
35 namespace uirenderer {
36 namespace skiapipeline {
37
38 // ----------------------------------------------------------------------------
39 // Recording Canvas Setup
40 // ----------------------------------------------------------------------------
41
initDisplayList(uirenderer::RenderNode * renderNode,int width,int height)42 void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
43 int height) {
44 mCurrentBarrier = nullptr;
45 SkASSERT(mDisplayList.get() == nullptr);
46
47 if (renderNode) {
48 mDisplayList = renderNode->detachAvailableList();
49 }
50 if (!mDisplayList) {
51 mDisplayList.reset(new SkiaDisplayList());
52 }
53
54 mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
55 SkiaCanvas::reset(&mRecorder);
56 mDisplayList->setHasHolePunches(false);
57 }
58
punchHole(const SkRRect & rect)59 void SkiaRecordingCanvas::punchHole(const SkRRect& rect) {
60 // Add the marker annotation to allow HWUI to determine where the current
61 // clip/transformation should be applied
62 SkVector vector = rect.getSimpleRadii();
63 float data[2];
64 data[0] = vector.x();
65 data[1] = vector.y();
66 mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(),
67 SkData::MakeWithCopy(data, 2 * sizeof(float)));
68
69 // Clear the current rect within the layer itself
70 SkPaint paint = SkPaint();
71 paint.setColor(0);
72 paint.setBlendMode(SkBlendMode::kClear);
73 mRecorder.drawRRect(rect, paint);
74
75 mDisplayList->setHasHolePunches(true);
76 }
77
finishRecording()78 std::unique_ptr<SkiaDisplayList> SkiaRecordingCanvas::finishRecording() {
79 // close any existing chunks if necessary
80 enableZ(false);
81 mRecorder.restoreToCount(1);
82 return std::move(mDisplayList);
83 }
84
finishRecording(uirenderer::RenderNode * destination)85 void SkiaRecordingCanvas::finishRecording(uirenderer::RenderNode* destination) {
86 destination->setStagingDisplayList(uirenderer::DisplayList(finishRecording()));
87 }
88
89 // ----------------------------------------------------------------------------
90 // Recording Canvas draw operations: View System
91 // ----------------------------------------------------------------------------
92
drawRoundRect(uirenderer::CanvasPropertyPrimitive * left,uirenderer::CanvasPropertyPrimitive * top,uirenderer::CanvasPropertyPrimitive * right,uirenderer::CanvasPropertyPrimitive * bottom,uirenderer::CanvasPropertyPrimitive * rx,uirenderer::CanvasPropertyPrimitive * ry,uirenderer::CanvasPropertyPaint * paint)93 void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
94 uirenderer::CanvasPropertyPrimitive* top,
95 uirenderer::CanvasPropertyPrimitive* right,
96 uirenderer::CanvasPropertyPrimitive* bottom,
97 uirenderer::CanvasPropertyPrimitive* rx,
98 uirenderer::CanvasPropertyPrimitive* ry,
99 uirenderer::CanvasPropertyPaint* paint) {
100 // Destructor of drawables created with allocateDrawable, will be invoked by ~LinearAllocator.
101 drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom, rx, ry,
102 paint));
103 }
104
drawCircle(uirenderer::CanvasPropertyPrimitive * x,uirenderer::CanvasPropertyPrimitive * y,uirenderer::CanvasPropertyPrimitive * radius,uirenderer::CanvasPropertyPaint * paint)105 void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x,
106 uirenderer::CanvasPropertyPrimitive* y,
107 uirenderer::CanvasPropertyPrimitive* radius,
108 uirenderer::CanvasPropertyPaint* paint) {
109 drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint));
110 }
111
drawRipple(const skiapipeline::RippleDrawableParams & params)112 void SkiaRecordingCanvas::drawRipple(const skiapipeline::RippleDrawableParams& params) {
113 mRecorder.drawRippleDrawable(params);
114 }
115
enableZ(bool enableZ)116 void SkiaRecordingCanvas::enableZ(bool enableZ) {
117 if (mCurrentBarrier && enableZ) {
118 // Already in a re-order section, nothing to do
119 return;
120 }
121
122 if (nullptr != mCurrentBarrier) {
123 // finish off the existing chunk
124 SkDrawable* drawable =
125 mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(mCurrentBarrier);
126 mCurrentBarrier = nullptr;
127 drawDrawable(drawable);
128 }
129 if (enableZ) {
130 mCurrentBarrier =
131 mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(mDisplayList.get());
132 drawDrawable(mCurrentBarrier);
133 }
134 }
135
drawLayer(uirenderer::DeferredLayerUpdater * layerUpdater)136 void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
137 #ifdef __ANDROID__ // Layoutlib does not support Layers
138 if (layerUpdater != nullptr) {
139 // Create a ref-counted drawable, which is kept alive by sk_sp in SkLiteDL.
140 sk_sp<SkDrawable> drawable(new LayerDrawable(layerUpdater));
141 drawDrawable(drawable.get());
142 }
143 #endif
144 }
145
146
drawRenderNode(uirenderer::RenderNode * renderNode)147 void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
148 // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared.
149 mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
150 auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
151 if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
152 // Put Vulkan WebViews with non-rectangular clips in a HW layer
153 renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
154 }
155 drawDrawable(&renderNodeDrawable);
156
157 // use staging property, since recording on UI thread
158 if (renderNode->stagingProperties().isProjectionReceiver()) {
159 mDisplayList->mProjectionReceiver = &renderNodeDrawable;
160 }
161 }
162
drawWebViewFunctor(int functor)163 void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
164 #ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
165 FunctorDrawable* functorDrawable;
166 if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
167 functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, asSkCanvas());
168 } else {
169 functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
170 }
171 mDisplayList->mChildFunctors.push_back(functorDrawable);
172 mRecorder.drawWebView(functorDrawable);
173 #endif
174 }
175
drawVectorDrawable(VectorDrawableRoot * tree)176 void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
177 mRecorder.drawVectorDrawable(tree);
178 SkMatrix mat;
179 this->getMatrix(&mat);
180 mDisplayList->appendVD(tree, mat);
181 }
182
183 // ----------------------------------------------------------------------------
184 // Recording Canvas draw operations: Bitmaps
185 // ----------------------------------------------------------------------------
186
FilterForImage(SkPaint & paint)187 void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) {
188 // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
189 // older.
190 if (sApiLevel <= 27 && paint.asBlendMode() == SkBlendMode::kClear) {
191 paint.setBlendMode(SkBlendMode::kDstOut);
192 }
193 }
194
drawBitmap(Bitmap & bitmap,float left,float top,const Paint * paint)195 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
196 sk_sp<SkImage> image = bitmap.makeImage();
197
198 applyLooper(
199 paint,
200 [&](const Paint& p) {
201 mRecorder.drawImage(image, left, top, p.sampling(), &p, bitmap.palette());
202 },
203 FilterForImage);
204
205 // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
206 // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
207 // when this function ends.
208 if (!bitmap.isImmutable() && image.get() && !image->unique()) {
209 mDisplayList->mMutableImages.push_back(image.get());
210 }
211 }
212
drawBitmap(Bitmap & bitmap,const SkMatrix & matrix,const Paint * paint)213 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) {
214 SkAutoCanvasRestore acr(&mRecorder, true);
215 concat(matrix);
216
217 sk_sp<SkImage> image = bitmap.makeImage();
218
219 applyLooper(
220 paint,
221 [&](const Paint& p) {
222 mRecorder.drawImage(image, 0, 0, p.sampling(), &p, bitmap.palette());
223 },
224 FilterForImage);
225
226 if (!bitmap.isImmutable() && image.get() && !image->unique()) {
227 mDisplayList->mMutableImages.push_back(image.get());
228 }
229 }
230
drawBitmap(Bitmap & bitmap,float srcLeft,float srcTop,float srcRight,float srcBottom,float dstLeft,float dstTop,float dstRight,float dstBottom,const Paint * paint)231 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
232 float srcBottom, float dstLeft, float dstTop, float dstRight,
233 float dstBottom, const Paint* paint) {
234 SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
235 SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
236
237 sk_sp<SkImage> image = bitmap.makeImage();
238
239 applyLooper(
240 paint,
241 [&](const Paint& p) {
242 mRecorder.drawImageRect(image, srcRect, dstRect, p.sampling(), &p,
243 SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
244 },
245 FilterForImage);
246
247 if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
248 !dstRect.isEmpty()) {
249 mDisplayList->mMutableImages.push_back(image.get());
250 }
251 }
252
drawNinePatch(Bitmap & bitmap,const Res_png_9patch & chunk,float dstLeft,float dstTop,float dstRight,float dstBottom,const Paint * paint)253 void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
254 float dstTop, float dstRight, float dstBottom,
255 const Paint* paint) {
256 SkCanvas::Lattice lattice;
257 NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
258
259 lattice.fRectTypes = nullptr;
260 lattice.fColors = nullptr;
261 int numFlags = 0;
262 if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
263 // We can expect the framework to give us a color for every distinct rect.
264 // Skia requires placeholder flags for degenerate rects.
265 numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
266 }
267
268 SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags);
269 SkAutoSTMalloc<25, SkColor> colors(numFlags);
270 if (numFlags > 0) {
271 NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
272 }
273
274 lattice.fBounds = nullptr;
275 SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
276 sk_sp<SkImage> image = bitmap.makeImage();
277
278 // HWUI always draws 9-patches with linear filtering, regardless of the Paint.
279 const SkFilterMode filter = SkFilterMode::kLinear;
280
281 applyLooper(
282 paint,
283 [&](const SkPaint& p) {
284 mRecorder.drawImageLattice(image, lattice, dst, filter, &p, bitmap.palette());
285 },
286 FilterForImage);
287
288 if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
289 mDisplayList->mMutableImages.push_back(image.get());
290 }
291 }
292
drawAnimatedImage(AnimatedImageDrawable * animatedImage)293 double SkiaRecordingCanvas::drawAnimatedImage(AnimatedImageDrawable* animatedImage) {
294 drawDrawable(animatedImage);
295 mDisplayList->mAnimatedImages.push_back(animatedImage);
296 return 0;
297 }
298
299 } // namespace skiapipeline
300 } // namespace uirenderer
301 } // namespace android
302