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