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 
19 #include <SkImagePriv.h>
20 #include "CanvasTransform.h"
21 #include "Layer.h"
22 #include "LayerDrawable.h"
23 #include "NinePatchUtils.h"
24 #include "RenderNode.h"
25 #include "pipeline/skia/AnimatedDrawables.h"
26 #include "pipeline/skia/GLFunctorDrawable.h"
27 #include "pipeline/skia/VkFunctorDrawable.h"
28 #include "pipeline/skia/VkInteropFunctorDrawable.h"
29 
30 namespace android {
31 namespace uirenderer {
32 namespace skiapipeline {
33 
34 // ----------------------------------------------------------------------------
35 // Recording Canvas Setup
36 // ----------------------------------------------------------------------------
37 
initDisplayList(uirenderer::RenderNode * renderNode,int width,int height)38 void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
39                                           int height) {
40     mCurrentBarrier = nullptr;
41     SkASSERT(mDisplayList.get() == nullptr);
42 
43     if (renderNode) {
44         mDisplayList = renderNode->detachAvailableList();
45     }
46     if (!mDisplayList) {
47         mDisplayList.reset(new SkiaDisplayList());
48     }
49 
50     mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
51     SkiaCanvas::reset(&mRecorder);
52 }
53 
finishRecording()54 uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
55     // close any existing chunks if necessary
56     insertReorderBarrier(false);
57     mRecorder.restoreToCount(1);
58     return mDisplayList.release();
59 }
60 
61 // ----------------------------------------------------------------------------
62 // Recording Canvas draw operations: View System
63 // ----------------------------------------------------------------------------
64 
drawRoundRect(uirenderer::CanvasPropertyPrimitive * left,uirenderer::CanvasPropertyPrimitive * top,uirenderer::CanvasPropertyPrimitive * right,uirenderer::CanvasPropertyPrimitive * bottom,uirenderer::CanvasPropertyPrimitive * rx,uirenderer::CanvasPropertyPrimitive * ry,uirenderer::CanvasPropertyPaint * paint)65 void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
66                                         uirenderer::CanvasPropertyPrimitive* top,
67                                         uirenderer::CanvasPropertyPrimitive* right,
68                                         uirenderer::CanvasPropertyPrimitive* bottom,
69                                         uirenderer::CanvasPropertyPrimitive* rx,
70                                         uirenderer::CanvasPropertyPrimitive* ry,
71                                         uirenderer::CanvasPropertyPaint* paint) {
72     // Destructor of drawables created with allocateDrawable, will be invoked by ~LinearAllocator.
73     drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom, rx, ry,
74                                                                    paint));
75 }
76 
drawCircle(uirenderer::CanvasPropertyPrimitive * x,uirenderer::CanvasPropertyPrimitive * y,uirenderer::CanvasPropertyPrimitive * radius,uirenderer::CanvasPropertyPaint * paint)77 void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x,
78                                      uirenderer::CanvasPropertyPrimitive* y,
79                                      uirenderer::CanvasPropertyPrimitive* radius,
80                                      uirenderer::CanvasPropertyPaint* paint) {
81     drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint));
82 }
83 
insertReorderBarrier(bool enableReorder)84 void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
85     if (mCurrentBarrier && enableReorder) {
86         // Already in a re-order section, nothing to do
87         return;
88     }
89 
90     if (nullptr != mCurrentBarrier) {
91         // finish off the existing chunk
92         SkDrawable* drawable =
93                 mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(mCurrentBarrier);
94         mCurrentBarrier = nullptr;
95         drawDrawable(drawable);
96     }
97     if (enableReorder) {
98         mCurrentBarrier =
99                 mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(mDisplayList.get());
100         drawDrawable(mCurrentBarrier);
101     }
102 }
103 
drawLayer(uirenderer::DeferredLayerUpdater * layerUpdater)104 void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
105     if (layerUpdater != nullptr) {
106         // Create a ref-counted drawable, which is kept alive by sk_sp in SkLiteDL.
107         sk_sp<SkDrawable> drawable(new LayerDrawable(layerUpdater));
108         drawDrawable(drawable.get());
109     }
110 }
111 
drawRenderNode(uirenderer::RenderNode * renderNode)112 void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
113     // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared.
114     mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
115     auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
116     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
117         // Put Vulkan WebViews with non-rectangular clips in a HW layer
118         renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
119     }
120     drawDrawable(&renderNodeDrawable);
121 
122     // use staging property, since recording on UI thread
123     if (renderNode->stagingProperties().isProjectionReceiver()) {
124         mDisplayList->mProjectionReceiver = &renderNodeDrawable;
125     }
126 }
127 
callDrawGLFunction(Functor * functor,uirenderer::GlFunctorLifecycleListener * listener)128 void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
129                                              uirenderer::GlFunctorLifecycleListener* listener) {
130     FunctorDrawable* functorDrawable;
131     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
132         functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
133                 functor, listener, asSkCanvas());
134     } else {
135         functorDrawable =
136                 mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, asSkCanvas());
137     }
138     mDisplayList->mChildFunctors.push_back(functorDrawable);
139     drawDrawable(functorDrawable);
140 }
141 
drawWebViewFunctor(int functor)142 void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
143     FunctorDrawable* functorDrawable;
144     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
145         functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, asSkCanvas());
146     } else {
147         functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
148     }
149     mDisplayList->mChildFunctors.push_back(functorDrawable);
150     drawDrawable(functorDrawable);
151 }
152 
drawVectorDrawable(VectorDrawableRoot * tree)153 void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
154     mRecorder.drawVectorDrawable(tree);
155     SkMatrix mat;
156     this->getMatrix(&mat);
157     mDisplayList->appendVD(tree, mat);
158 }
159 
160 // ----------------------------------------------------------------------------
161 // Recording Canvas draw operations: Bitmaps
162 // ----------------------------------------------------------------------------
163 
filterBitmap(PaintCoW && paint)164 SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) {
165     bool fixBlending = false;
166     bool fixAA = false;
167     if (paint) {
168         // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
169         // older.
170         fixBlending = sApiLevel <= 27 && paint->getBlendMode() == SkBlendMode::kClear;
171         fixAA = paint->isAntiAlias();
172     }
173 
174     if (fixBlending || fixAA) {
175         SkPaint& tmpPaint = paint.writeable();
176 
177         if (fixBlending) {
178             tmpPaint.setBlendMode(SkBlendMode::kDstOut);
179         }
180 
181         // disabling AA on bitmap draws matches legacy HWUI behavior
182         tmpPaint.setAntiAlias(false);
183     }
184 
185     return filterPaint(std::move(paint));
186 }
187 
drawBitmap(Bitmap & bitmap,float left,float top,const SkPaint * paint)188 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
189     sk_sp<SkImage> image = bitmap.makeImage();
190     mRecorder.drawImage(image, left, top, filterBitmap(paint), bitmap.palette());
191     // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
192     // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
193     // when this function ends.
194     if (!bitmap.isImmutable() && image.get() && !image->unique()) {
195         mDisplayList->mMutableImages.push_back(image.get());
196     }
197 }
198 
drawBitmap(Bitmap & bitmap,const SkMatrix & matrix,const SkPaint * paint)199 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
200     SkAutoCanvasRestore acr(&mRecorder, true);
201     concat(matrix);
202 
203     sk_sp<SkImage> image = bitmap.makeImage();
204     mRecorder.drawImage(image, 0, 0, filterBitmap(paint), bitmap.palette());
205     if (!bitmap.isImmutable() && image.get() && !image->unique()) {
206         mDisplayList->mMutableImages.push_back(image.get());
207     }
208 }
209 
drawBitmap(Bitmap & bitmap,float srcLeft,float srcTop,float srcRight,float srcBottom,float dstLeft,float dstTop,float dstRight,float dstBottom,const SkPaint * paint)210 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
211                                      float srcBottom, float dstLeft, float dstTop, float dstRight,
212                                      float dstBottom, const SkPaint* paint) {
213     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
214     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
215 
216     sk_sp<SkImage> image = bitmap.makeImage();
217     mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint),
218                             SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
219     if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
220         !dstRect.isEmpty()) {
221         mDisplayList->mMutableImages.push_back(image.get());
222     }
223 }
224 
drawNinePatch(Bitmap & bitmap,const Res_png_9patch & chunk,float dstLeft,float dstTop,float dstRight,float dstBottom,const SkPaint * paint)225 void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
226                                         float dstTop, float dstRight, float dstBottom,
227                                         const SkPaint* paint) {
228     SkCanvas::Lattice lattice;
229     NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
230 
231     lattice.fRectTypes = nullptr;
232     lattice.fColors = nullptr;
233     int numFlags = 0;
234     if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
235         // We can expect the framework to give us a color for every distinct rect.
236         // Skia requires placeholder flags for degenerate rects.
237         numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
238     }
239 
240     SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags);
241     SkAutoSTMalloc<25, SkColor> colors(numFlags);
242     if (numFlags > 0) {
243         NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
244     }
245 
246     lattice.fBounds = nullptr;
247     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
248 
249     PaintCoW filteredPaint(paint);
250     // HWUI always draws 9-patches with bilinear filtering, regardless of what is set in the Paint.
251     if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) {
252         filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
253     }
254     sk_sp<SkImage> image = bitmap.makeImage();
255     mRecorder.drawImageLattice(image, lattice, dst, filterBitmap(std::move(filteredPaint)),
256                                bitmap.palette());
257     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
258         mDisplayList->mMutableImages.push_back(image.get());
259     }
260 }
261 
drawAnimatedImage(AnimatedImageDrawable * animatedImage)262 double SkiaRecordingCanvas::drawAnimatedImage(AnimatedImageDrawable* animatedImage) {
263     drawDrawable(animatedImage);
264     mDisplayList->mAnimatedImages.push_back(animatedImage);
265     return 0;
266 }
267 
268 }  // namespace skiapipeline
269 }  // namespace uirenderer
270 }  // namespace android
271