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