• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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