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