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