RenderNodeDrawable.cpp revision 021693b967a2c5556dddd183eb0247df4079e1ad
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 "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
253