blob: accbabda5a2fa0000bbc8b84d8c4cd98130da6c6 [file] [log] [blame]
Stan Iliev021693b2016-10-17 16:26:15 -04001/*
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 "RenderNode.h"
19#include "SkiaDisplayList.h"
Stan Iliev500a0c32016-10-26 10:30:09 -040020#include "SkiaPipeline.h"
Stan Iliev021693b2016-10-17 16:26:15 -040021#include "utils/TraceUtils.h"
22
23namespace android {
24namespace uirenderer {
25namespace skiapipeline {
26
27static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) {
28 SkASSERT(outline.willClip());
29 Rect possibleRect;
30 float radius;
31 LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius),
32 "clipping outlines should be at most roundedRects");
33 SkRect rect = possibleRect.toSkRect();
34 if (radius != 0.0f) {
35 if (pendingClip && !pendingClip->contains(rect)) {
36 canvas->clipRect(*pendingClip);
37 }
38 canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkRegion::kIntersect_Op, true);
39 } else {
40 if (pendingClip) {
41 (void)rect.intersect(*pendingClip);
42 }
43 canvas->clipRect(rect);
44 }
45}
46
47const RenderProperties& RenderNodeDrawable::getNodeProperties() const {
48 return mRenderNode->properties();
49}
50
51void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
Stan Iliev2f06e8a2016-11-02 15:29:03 -040052 //negative and positive Z order are drawn out of order, if this render node drawable is in
53 //a reordering section
54 if ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) {
Stan Iliev021693b2016-10-17 16:26:15 -040055 this->forceDraw(canvas);
56 }
57}
58
59void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
60 RenderNode* renderNode = mRenderNode.get();
Stan Iliev500a0c32016-10-26 10:30:09 -040061 if (SkiaPipeline::skpCaptureEnabled()) {
Stan Iliev021693b2016-10-17 16:26:15 -040062 SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
63 canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
64 }
65
66 // We only respect the nothingToDraw check when we are composing a layer. This
67 // ensures that we paint the layer even if it is not currently visible in the
68 // event that the properties change and it becomes visible.
69 if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) {
70 return;
71 }
72
73 SkASSERT(renderNode->getDisplayList()->isSkiaDL());
74 SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
75
76 SkAutoCanvasRestore acr(canvas, true);
77
78 const RenderProperties& properties = this->getNodeProperties();
79 if (displayList->mIsProjectionReceiver) {
80 // this node is a projection receiver. We will gather the projected nodes as we draw our
81 // children, and then draw them on top of this node's content.
82 std::vector<ProjectedChild> newList;
83 for (auto& child : displayList->mChildNodes) {
84 // our direct children are not supposed to project into us (nodes project to, at the
85 // nearest, their grandparents). So we "delay" the list's activation one level by
86 // passing it into mNextProjectedChildrenTarget rather than mProjectedChildrenTarget.
87 child.mProjectedChildrenTarget = mNextProjectedChildrenTarget;
88 child.mNextProjectedChildrenTarget = &newList;
89 }
90 // draw ourselves and our children. As a side effect, this will add projected nodes to
91 // newList.
92 this->drawContent(canvas);
93 bool willClip = properties.getOutline().willClip();
94 if (willClip) {
95 canvas->save();
96 clipOutline(properties.getOutline(), canvas, nullptr);
97 }
98 // draw the collected projected nodes
99 for (auto& projectedChild : newList) {
100 canvas->setMatrix(projectedChild.matrix);
101 projectedChild.node->drawContent(canvas);
102 }
103 if (willClip) {
104 canvas->restore();
105 }
106 } else {
107 if (properties.getProjectBackwards() && mProjectedChildrenTarget) {
108 // We are supposed to project this node, so add it to the list and do not actually draw
109 // yet. It will be drawn by its projection receiver.
110 mProjectedChildrenTarget->push_back({ this, canvas->getTotalMatrix() });
111 return;
112 }
113 for (auto& child : displayList->mChildNodes) {
114 // storing these values in the nodes themselves is a bit ugly; they should "really" be
115 // function parameters, but we have to go through the preexisting draw() method and
116 // therefore cannot add additional parameters to it
117 child.mProjectedChildrenTarget = mNextProjectedChildrenTarget;
118 child.mNextProjectedChildrenTarget = mNextProjectedChildrenTarget;
119 }
120 this->drawContent(canvas);
121 }
122 mProjectedChildrenTarget = nullptr;
123 mNextProjectedChildrenTarget = nullptr;
124}
125
126static bool layerNeedsPaint(const LayerProperties& properties,
127 float alphaMultiplier, SkPaint* paint) {
128 if (alphaMultiplier < 1.0f
129 || properties.alpha() < 255
130 || properties.xferMode() != SkBlendMode::kSrcOver
131 || properties.colorFilter() != nullptr) {
132 paint->setAlpha(properties.alpha() * alphaMultiplier);
133 paint->setBlendMode(properties.xferMode());
Derek Sollenbergerf87da672016-11-02 11:34:27 -0400134 paint->setColorFilter(sk_ref_sp(properties.colorFilter()));
Stan Iliev021693b2016-10-17 16:26:15 -0400135 return true;
136 }
137 return false;
138}
139
140void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
141 RenderNode* renderNode = mRenderNode.get();
142 float alphaMultiplier = 1.0f;
143 const RenderProperties& properties = renderNode->properties();
144
145 // If we are drawing the contents of layer, we don't want to apply any of
146 // the RenderNode's properties during this pass. Those will all be applied
147 // when the layer is composited.
148 if (mComposeLayer) {
149 setViewProperties(properties, canvas, &alphaMultiplier);
150 }
151
152 //TODO should we let the bound of the drawable do this for us?
153 const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
154 bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
155 if (!quickRejected) {
156 SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
157 const LayerProperties& layerProperties = properties.layerProperties();
158 // composing a hardware layer
159 if (renderNode->getLayerSurface() && mComposeLayer) {
160 SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
161 SkPaint* paint = nullptr;
162 SkPaint tmpPaint;
163 if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
164 paint = &tmpPaint;
165 }
166 renderNode->getLayerSurface()->draw(canvas, 0, 0, paint);
167 // composing a software layer with alpha
168 } else if (properties.effectiveLayerType() == LayerType::Software) {
169 SkPaint paint;
170 bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
171 if (needsLayer) {
172 canvas->saveLayer(bounds, &paint);
173 }
174 canvas->drawDrawable(displayList->mDrawable.get());
175 if (needsLayer) {
176 canvas->restore();
177 }
178 } else {
179 canvas->drawDrawable(displayList->mDrawable.get());
180 }
181 }
182}
183
184void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
185 float* alphaMultiplier) {
186 if (properties.getLeft() != 0 || properties.getTop() != 0) {
187 canvas->translate(properties.getLeft(), properties.getTop());
188 }
189 if (properties.getStaticMatrix()) {
190 canvas->concat(*properties.getStaticMatrix());
191 } else if (properties.getAnimationMatrix()) {
192 canvas->concat(*properties.getAnimationMatrix());
193 }
194 if (properties.hasTransformMatrix()) {
195 if (properties.isTransformTranslateOnly()) {
196 canvas->translate(properties.getTranslationX(), properties.getTranslationY());
197 } else {
198 canvas->concat(*properties.getTransformMatrix());
199 }
200 }
201 const bool isLayer = properties.effectiveLayerType() != LayerType::None;
202 int clipFlags = properties.getClippingFlags();
203 if (properties.getAlpha() < 1) {
204 if (isLayer) {
205 clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
206 }
207 if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
208 *alphaMultiplier = properties.getAlpha();
209 } else {
210 // savelayer needed to create an offscreen buffer
211 Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight());
212 if (clipFlags) {
213 properties.getClippingRectForFlags(clipFlags, &layerBounds);
214 clipFlags = 0; // all clipping done by savelayer
215 }
216 SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top,
217 layerBounds.right, layerBounds.bottom);
218 canvas->saveLayerAlpha(&bounds, (int) (properties.getAlpha() * 255));
219 }
220
221 if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
222 // pretend alpha always causes savelayer to warn about
223 // performance problem affecting old versions
224 ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(),
225 properties.getHeight());
226 }
227 }
228
229 const SkRect* pendingClip = nullptr;
230 SkRect clipRect;
231
232 if (clipFlags) {
233 Rect tmpRect;
234 properties.getClippingRectForFlags(clipFlags, &tmpRect);
235 clipRect = tmpRect.toSkRect();
236 pendingClip = &clipRect;
237 }
238
239 if (properties.getRevealClip().willClip()) {
240 canvas->clipPath(*properties.getRevealClip().getPath(), SkRegion::kIntersect_Op, true);
241 } else if (properties.getOutline().willClip()) {
242 clipOutline(properties.getOutline(), canvas, pendingClip);
243 pendingClip = nullptr;
244 }
245
246 if (pendingClip) {
247 canvas->clipRect(*pendingClip);
248 }
249}
250
251}; // namespace skiapipeline
252}; // namespace uirenderer
253}; // namespace android