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