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.getBlendMode() == SkBlendMode::kClear) {
191 paint.setBlendMode(SkBlendMode::kDstOut);
192 }
193 }
194
Paint_to_filter(const SkPaint & paint)195 static SkFilterMode Paint_to_filter(const SkPaint& paint) {
196 return paint.getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
197 : SkFilterMode::kNearest;
198 }
199
Paint_to_sampling(const SkPaint & paint)200 static SkSamplingOptions Paint_to_sampling(const SkPaint& paint) {
201 // Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics
202 return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone);
203 }
204
drawBitmap(Bitmap & bitmap,float left,float top,const Paint * paint)205 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
206 sk_sp<SkImage> image = bitmap.makeImage();
207
208 applyLooper(
209 paint,
210 [&](const SkPaint& p) {
211 mRecorder.drawImage(image, left, top, Paint_to_sampling(p), &p, bitmap.palette());
212 },
213 FilterForImage);
214
215 // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
216 // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
217 // when this function ends.
218 if (!bitmap.isImmutable() && image.get() && !image->unique()) {
219 mDisplayList->mMutableImages.push_back(image.get());
220 }
221 }
222
drawBitmap(Bitmap & bitmap,const SkMatrix & matrix,const Paint * paint)223 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) {
224 SkAutoCanvasRestore acr(&mRecorder, true);
225 concat(matrix);
226
227 sk_sp<SkImage> image = bitmap.makeImage();
228
229 applyLooper(
230 paint,
231 [&](const SkPaint& p) {
232 mRecorder.drawImage(image, 0, 0, Paint_to_sampling(p), &p, bitmap.palette());
233 },
234 FilterForImage);
235
236 if (!bitmap.isImmutable() && image.get() && !image->unique()) {
237 mDisplayList->mMutableImages.push_back(image.get());
238 }
239 }
240
drawBitmap(Bitmap & bitmap,float srcLeft,float srcTop,float srcRight,float srcBottom,float dstLeft,float dstTop,float dstRight,float dstBottom,const Paint * paint)241 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
242 float srcBottom, float dstLeft, float dstTop, float dstRight,
243 float dstBottom, const Paint* paint) {
244 SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
245 SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
246
247 sk_sp<SkImage> image = bitmap.makeImage();
248
249 applyLooper(
250 paint,
251 [&](const SkPaint& p) {
252 mRecorder.drawImageRect(image, srcRect, dstRect, Paint_to_sampling(p), &p,
253 SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
254 },
255 FilterForImage);
256
257 if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
258 !dstRect.isEmpty()) {
259 mDisplayList->mMutableImages.push_back(image.get());
260 }
261 }
262
drawNinePatch(Bitmap & bitmap,const Res_png_9patch & chunk,float dstLeft,float dstTop,float dstRight,float dstBottom,const Paint * paint)263 void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
264 float dstTop, float dstRight, float dstBottom,
265 const Paint* paint) {
266 SkCanvas::Lattice lattice;
267 NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
268
269 lattice.fRectTypes = nullptr;
270 lattice.fColors = nullptr;
271 int numFlags = 0;
272 if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
273 // We can expect the framework to give us a color for every distinct rect.
274 // Skia requires placeholder flags for degenerate rects.
275 numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
276 }
277
278 SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags);
279 SkAutoSTMalloc<25, SkColor> colors(numFlags);
280 if (numFlags > 0) {
281 NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
282 }
283
284 lattice.fBounds = nullptr;
285 SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
286 sk_sp<SkImage> image = bitmap.makeImage();
287
288 // HWUI always draws 9-patches with linear filtering, regardless of the Paint.
289 const SkFilterMode filter = SkFilterMode::kLinear;
290
291 applyLooper(
292 paint,
293 [&](const SkPaint& p) {
294 mRecorder.drawImageLattice(image, lattice, dst, filter, &p, bitmap.palette());
295 },
296 FilterForImage);
297
298 if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
299 mDisplayList->mMutableImages.push_back(image.get());
300 }
301 }
302
drawAnimatedImage(AnimatedImageDrawable * animatedImage)303 double SkiaRecordingCanvas::drawAnimatedImage(AnimatedImageDrawable* animatedImage) {
304 drawDrawable(animatedImage);
305 mDisplayList->mAnimatedImages.push_back(animatedImage);
306 return 0;
307 }
308
309 } // namespace skiapipeline
310 } // namespace uirenderer
311 } // namespace android
312