RenderNodeDrawable.cpp revision 2f06e8ad1a1c4d0866bb66854d2759e275898635
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 "SkiaPipeline.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, if this render node drawable is in
53    //a reordering section
54    if ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) {
55        this->forceDraw(canvas);
56    }
57}
58
59void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
60    RenderNode* renderNode = mRenderNode.get();
61    if (SkiaPipeline::skpCaptureEnabled()) {
62        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());
134        paint->setColorFilter(properties.colorFilter());
135        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
254