blob: 7dcbbd059e888cd27ff7aaa738f67a266dbd52ed [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);
Matt Sarett79756be2016-11-09 16:13:54 -0500167
168 if (CC_UNLIKELY(Properties::debugLayersUpdates
169 && !renderNode->getSkiaLayer()->hasRenderedSinceRepaint)) {
170 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
171 SkPaint layerPaint;
172 layerPaint.setColor(0x7f00ff00);
173 canvas->drawRect(bounds, layerPaint);
174 }
Stan Iliev021693b2016-10-17 16:26:15 -0400175 // composing a software layer with alpha
176 } else if (properties.effectiveLayerType() == LayerType::Software) {
177 SkPaint paint;
178 bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
179 if (needsLayer) {
180 canvas->saveLayer(bounds, &paint);
181 }
182 canvas->drawDrawable(displayList->mDrawable.get());
183 if (needsLayer) {
184 canvas->restore();
185 }
186 } else {
187 canvas->drawDrawable(displayList->mDrawable.get());
188 }
189 }
190}
191
192void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
193 float* alphaMultiplier) {
194 if (properties.getLeft() != 0 || properties.getTop() != 0) {
195 canvas->translate(properties.getLeft(), properties.getTop());
196 }
197 if (properties.getStaticMatrix()) {
198 canvas->concat(*properties.getStaticMatrix());
199 } else if (properties.getAnimationMatrix()) {
200 canvas->concat(*properties.getAnimationMatrix());
201 }
202 if (properties.hasTransformMatrix()) {
203 if (properties.isTransformTranslateOnly()) {
204 canvas->translate(properties.getTranslationX(), properties.getTranslationY());
205 } else {
206 canvas->concat(*properties.getTransformMatrix());
207 }
208 }
209 const bool isLayer = properties.effectiveLayerType() != LayerType::None;
210 int clipFlags = properties.getClippingFlags();
211 if (properties.getAlpha() < 1) {
212 if (isLayer) {
213 clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
214 }
215 if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
216 *alphaMultiplier = properties.getAlpha();
217 } else {
218 // savelayer needed to create an offscreen buffer
219 Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight());
220 if (clipFlags) {
221 properties.getClippingRectForFlags(clipFlags, &layerBounds);
222 clipFlags = 0; // all clipping done by savelayer
223 }
224 SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top,
225 layerBounds.right, layerBounds.bottom);
226 canvas->saveLayerAlpha(&bounds, (int) (properties.getAlpha() * 255));
227 }
228
229 if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
230 // pretend alpha always causes savelayer to warn about
231 // performance problem affecting old versions
232 ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(),
233 properties.getHeight());
234 }
235 }
236
237 const SkRect* pendingClip = nullptr;
238 SkRect clipRect;
239
240 if (clipFlags) {
241 Rect tmpRect;
242 properties.getClippingRectForFlags(clipFlags, &tmpRect);
243 clipRect = tmpRect.toSkRect();
244 pendingClip = &clipRect;
245 }
246
247 if (properties.getRevealClip().willClip()) {
248 canvas->clipPath(*properties.getRevealClip().getPath(), SkRegion::kIntersect_Op, true);
249 } else if (properties.getOutline().willClip()) {
250 clipOutline(properties.getOutline(), canvas, pendingClip);
251 pendingClip = nullptr;
252 }
253
254 if (pendingClip) {
255 canvas->clipRect(*pendingClip);
256 }
257}
258
259}; // namespace skiapipeline
260}; // namespace uirenderer
261}; // namespace android