RenderNodeDrawable.cpp revision 79756be175dea78ee9d51bb22abba7621bd9b5cc
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(sk_ref_sp(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
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            }
175        // 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
262