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 "RenderNodeDrawable.h"
18 #include <SkPaintFilterCanvas.h>
19 #include "RenderNode.h"
20 #include "SkiaDisplayList.h"
21 #include "utils/TraceUtils.h"
22
23 #include <optional>
24
25 namespace android {
26 namespace uirenderer {
27 namespace skiapipeline {
28
RenderNodeDrawable(RenderNode * node,SkCanvas * canvas,bool composeLayer,bool inReorderingSection)29 RenderNodeDrawable::RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer,
30 bool inReorderingSection)
31 : mRenderNode(node)
32 , mRecordedTransform(canvas->getTotalMatrix())
33 , mComposeLayer(composeLayer)
34 , mInReorderingSection(inReorderingSection) {}
35
~RenderNodeDrawable()36 RenderNodeDrawable::~RenderNodeDrawable() {
37 // Just here to move the destructor into the cpp file where we can access RenderNode.
38
39 // TODO: Detangle the header nightmare.
40 }
41
drawBackwardsProjectedNodes(SkCanvas * canvas,const SkiaDisplayList & displayList,int nestLevel) const42 void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas,
43 const SkiaDisplayList& displayList,
44 int nestLevel) const {
45 LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver);
46 for (auto& child : displayList.mChildNodes) {
47 const RenderProperties& childProperties = child.getNodeProperties();
48
49 // immediate children cannot be projected on their parent
50 if (childProperties.getProjectBackwards() && nestLevel > 0) {
51 SkAutoCanvasRestore acr2(canvas, true);
52 // Apply recorded matrix, which is a total matrix saved at recording time to avoid
53 // replaying all DL commands.
54 canvas->concat(child.getRecordedMatrix());
55 child.drawContent(canvas);
56 }
57
58 // skip walking sub-nodes if current display list contains a receiver with exception of
59 // level 0, which is a known receiver
60 if (0 == nestLevel || !displayList.containsProjectionReceiver()) {
61 SkAutoCanvasRestore acr(canvas, true);
62 SkMatrix nodeMatrix;
63 mat4 hwuiMatrix(child.getRecordedMatrix());
64 auto childNode = child.getRenderNode();
65 childNode->applyViewPropertyTransforms(hwuiMatrix);
66 hwuiMatrix.copyTo(nodeMatrix);
67 canvas->concat(nodeMatrix);
68 SkiaDisplayList* childDisplayList = static_cast<SkiaDisplayList*>(
69 (const_cast<DisplayList*>(childNode->getDisplayList())));
70 if (childDisplayList) {
71 drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel + 1);
72 }
73 }
74 }
75 }
76
clipOutline(const Outline & outline,SkCanvas * canvas,const SkRect * pendingClip)77 static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) {
78 Rect possibleRect;
79 float radius;
80
81 /* To match the existing HWUI behavior we only supports rectangles or
82 * rounded rectangles; passing in a more complicated outline fails silently.
83 */
84 if (!outline.getAsRoundRect(&possibleRect, &radius)) {
85 if (pendingClip) {
86 canvas->clipRect(*pendingClip);
87 }
88 return;
89 }
90
91 SkRect rect = possibleRect.toSkRect();
92 if (radius != 0.0f) {
93 if (pendingClip && !pendingClip->contains(rect)) {
94 canvas->clipRect(*pendingClip);
95 }
96 canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkClipOp::kIntersect, true);
97 } else {
98 if (pendingClip) {
99 (void)rect.intersect(*pendingClip);
100 }
101 canvas->clipRect(rect);
102 }
103 }
104
getNodeProperties() const105 const RenderProperties& RenderNodeDrawable::getNodeProperties() const {
106 return mRenderNode->properties();
107 }
108
onDraw(SkCanvas * canvas)109 void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
110 // negative and positive Z order are drawn out of order, if this render node drawable is in
111 // a reordering section
112 if ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) {
113 this->forceDraw(canvas);
114 }
115 }
116
117 class MarkDraw {
118 public:
MarkDraw(SkCanvas & canvas,RenderNode & node)119 explicit MarkDraw(SkCanvas& canvas, RenderNode& node) : mCanvas(canvas), mNode(node) {
120 if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
121 mNode.markDrawStart(mCanvas);
122 }
123 }
~MarkDraw()124 ~MarkDraw() {
125 if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
126 mNode.markDrawEnd(mCanvas);
127 }
128 }
129
130 private:
131 SkCanvas& mCanvas;
132 RenderNode& mNode;
133 };
134
forceDraw(SkCanvas * canvas) const135 void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const {
136 RenderNode* renderNode = mRenderNode.get();
137 MarkDraw _marker{*canvas, *renderNode};
138
139 // We only respect the nothingToDraw check when we are composing a layer. This
140 // ensures that we paint the layer even if it is not currently visible in the
141 // event that the properties change and it becomes visible.
142 if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
143 (renderNode->nothingToDraw() && mComposeLayer)) {
144 return;
145 }
146
147 SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
148
149 SkAutoCanvasRestore acr(canvas, true);
150 const RenderProperties& properties = this->getNodeProperties();
151 // pass this outline to the children that may clip backward projected nodes
152 displayList->mProjectedOutline =
153 displayList->containsProjectionReceiver() ? &properties.getOutline() : nullptr;
154 if (!properties.getProjectBackwards()) {
155 drawContent(canvas);
156 if (mProjectedDisplayList) {
157 acr.restore(); // draw projected children using parent matrix
158 LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
159 const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
160 SkAutoCanvasRestore acr2(canvas, shouldClip);
161 canvas->setMatrix(mProjectedDisplayList->mParentMatrix);
162 if (shouldClip) {
163 canvas->clipPath(*mProjectedDisplayList->mProjectedOutline->getPath());
164 }
165 drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList);
166 }
167 }
168 displayList->mProjectedOutline = nullptr;
169 }
170
layerNeedsPaint(const LayerProperties & properties,float alphaMultiplier,SkPaint * paint)171 static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
172 SkPaint* paint) {
173 paint->setFilterQuality(kLow_SkFilterQuality);
174 if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
175 properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr) {
176 paint->setAlpha(properties.alpha() * alphaMultiplier);
177 paint->setBlendMode(properties.xferMode());
178 paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
179 return true;
180 }
181 return false;
182 }
183
184 class AlphaFilterCanvas : public SkPaintFilterCanvas {
185 public:
AlphaFilterCanvas(SkCanvas * canvas,float alpha)186 AlphaFilterCanvas(SkCanvas* canvas, float alpha) : SkPaintFilterCanvas(canvas), mAlpha(alpha) {}
187
188 protected:
onFilter(SkPaint & paint) const189 bool onFilter(SkPaint& paint) const override {
190 paint.setAlpha((uint8_t)paint.getAlpha() * mAlpha);
191 return true;
192 }
onDrawDrawable(SkDrawable * drawable,const SkMatrix * matrix)193 void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
194 // We unroll the drawable using "this" canvas, so that draw calls contained inside will
195 // get their alpha applied. The default SkPaintFilterCanvas::onDrawDrawable does not unroll.
196 drawable->draw(this, matrix);
197 }
198
199 private:
200 float mAlpha;
201 };
202
drawContent(SkCanvas * canvas) const203 void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
204 RenderNode* renderNode = mRenderNode.get();
205 float alphaMultiplier = 1.0f;
206 const RenderProperties& properties = renderNode->properties();
207
208 // If we are drawing the contents of layer, we don't want to apply any of
209 // the RenderNode's properties during this pass. Those will all be applied
210 // when the layer is composited.
211 if (mComposeLayer) {
212 setViewProperties(properties, canvas, &alphaMultiplier);
213 }
214 SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList();
215 displayList->mParentMatrix = canvas->getTotalMatrix();
216
217 // TODO should we let the bound of the drawable do this for us?
218 const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
219 bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
220 if (!quickRejected) {
221 SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
222 const LayerProperties& layerProperties = properties.layerProperties();
223 // composing a hardware layer
224 if (renderNode->getLayerSurface() && mComposeLayer) {
225 SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
226 SkPaint paint;
227 layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
228
229 // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
230 // we need to restrict the portion of the surface drawn to the size of the renderNode.
231 SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
232 SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
233
234 // If SKP recording is active save an annotation that indicates this drawImageRect
235 // could also be rendered with the commands saved at ID associated with this node.
236 if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
237 canvas->drawAnnotation(bounds, String8::format(
238 "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
239 }
240 canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds,
241 bounds, &paint);
242
243 if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
244 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
245 if (CC_UNLIKELY(Properties::debugLayersUpdates)) {
246 SkPaint layerPaint;
247 layerPaint.setColor(0x7f00ff00);
248 canvas->drawRect(bounds, layerPaint);
249 } else if (CC_UNLIKELY(Properties::debugOverdraw)) {
250 // Render transparent rect to increment overdraw for repaint area.
251 // This can be "else if" because flashing green on layer updates
252 // will also increment the overdraw if it happens to be turned on.
253 SkPaint transparentPaint;
254 transparentPaint.setColor(SK_ColorTRANSPARENT);
255 canvas->drawRect(bounds, transparentPaint);
256 }
257 }
258 } else {
259 if (alphaMultiplier < 1.0f) {
260 // Non-layer draw for a view with getHasOverlappingRendering=false, will apply
261 // the alpha to the paint of each nested draw.
262 AlphaFilterCanvas alphaCanvas(canvas, alphaMultiplier);
263 displayList->draw(&alphaCanvas);
264 } else {
265 displayList->draw(canvas);
266 }
267 }
268 }
269 }
270
setViewProperties(const RenderProperties & properties,SkCanvas * canvas,float * alphaMultiplier)271 void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
272 float* alphaMultiplier) {
273 if (properties.getLeft() != 0 || properties.getTop() != 0) {
274 canvas->translate(properties.getLeft(), properties.getTop());
275 }
276 if (properties.getStaticMatrix()) {
277 canvas->concat(*properties.getStaticMatrix());
278 } else if (properties.getAnimationMatrix()) {
279 canvas->concat(*properties.getAnimationMatrix());
280 }
281 if (properties.hasTransformMatrix()) {
282 if (properties.isTransformTranslateOnly()) {
283 canvas->translate(properties.getTranslationX(), properties.getTranslationY());
284 } else {
285 canvas->concat(*properties.getTransformMatrix());
286 }
287 }
288 const bool isLayer = properties.effectiveLayerType() != LayerType::None;
289 int clipFlags = properties.getClippingFlags();
290 if (properties.getAlpha() < 1) {
291 if (isLayer) {
292 clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
293 }
294 if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
295 *alphaMultiplier = properties.getAlpha();
296 } else {
297 // savelayer needed to create an offscreen buffer
298 Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight());
299 if (clipFlags) {
300 properties.getClippingRectForFlags(clipFlags, &layerBounds);
301 clipFlags = 0; // all clipping done by savelayer
302 }
303 SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top, layerBounds.right,
304 layerBounds.bottom);
305 canvas->saveLayerAlpha(&bounds, (int)(properties.getAlpha() * 255));
306 }
307
308 if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
309 // pretend alpha always causes savelayer to warn about
310 // performance problem affecting old versions
311 ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(),
312 properties.getHeight());
313 }
314 }
315
316 const SkRect* pendingClip = nullptr;
317 SkRect clipRect;
318
319 if (clipFlags) {
320 Rect tmpRect;
321 properties.getClippingRectForFlags(clipFlags, &tmpRect);
322 clipRect = tmpRect.toSkRect();
323 pendingClip = &clipRect;
324 }
325
326 if (properties.getRevealClip().willClip()) {
327 canvas->clipPath(*properties.getRevealClip().getPath(), SkClipOp::kIntersect, true);
328 } else if (properties.getOutline().willClip()) {
329 clipOutline(properties.getOutline(), canvas, pendingClip);
330 pendingClip = nullptr;
331 }
332
333 if (pendingClip) {
334 canvas->clipRect(*pendingClip);
335 }
336 }
337
338 } // namespace skiapipeline
339 } // namespace uirenderer
340 } // namespace android
341