ReorderBarrierDrawables.cpp revision 5745a0a37fa8b09e6ce538ab8ef58684cc00604e
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 "ReorderBarrierDrawables.h"
18#include "RenderNode.h"
19#include "SkiaDisplayList.h"
20#include "SkiaPipeline.h"
21
22#include <SkBlurMask.h>
23#include <SkBlurMaskFilter.h>
24#include <SkGaussianEdgeShader.h>
25#include <SkPathOps.h>
26#include <SkRRectsGaussianEdgeMaskFilter.h>
27#include <SkShadowUtils.h>
28
29namespace android {
30namespace uirenderer {
31namespace skiapipeline {
32
33StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
34        : mEndChildIndex(0)
35        , mBeginChildIndex(data->mChildNodes.size())
36        , mDisplayList(data) {
37}
38
39void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
40    if (mChildren.empty()) {
41        //mChildren is allocated and initialized only the first time onDraw is called and cached for
42        //subsequent calls
43        mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1);
44        for (int i = mBeginChildIndex; i <= mEndChildIndex; i++) {
45            mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i]));
46        }
47    }
48    std::stable_sort(mChildren.begin(), mChildren.end(),
49        [](RenderNodeDrawable* a, RenderNodeDrawable* b) {
50            const float aZValue = a->getNodeProperties().getZ();
51            const float bZValue = b->getNodeProperties().getZ();
52            return aZValue < bZValue;
53        });
54
55    SkASSERT(!mChildren.empty());
56
57    size_t drawIndex = 0;
58    const size_t endIndex = mChildren.size();
59    while (drawIndex < endIndex) {
60        RenderNodeDrawable* childNode = mChildren[drawIndex];
61        SkASSERT(childNode);
62        const float casterZ = childNode->getNodeProperties().getZ();
63        if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z
64            return;
65        }
66        childNode->forceDraw(canvas);
67        drawIndex++;
68    }
69}
70
71EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
72        : mStartBarrier(startBarrier) {
73    mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
74}
75
76#define SHADOW_DELTA 0.1f
77
78void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
79    auto& zChildren = mStartBarrier->mChildren;
80    SkASSERT(!zChildren.empty());
81
82    /**
83     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
84     * with very similar Z heights to draw together.
85     *
86     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
87     * underneath both, and neither's shadow is drawn on top of the other.
88     */
89    size_t drawIndex = 0;
90
91    const size_t endIndex = zChildren.size();
92    while (drawIndex < endIndex     //draw only children with positive Z
93            && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) drawIndex++;
94    size_t shadowIndex = drawIndex;
95
96    float lastCasterZ = 0.0f;
97    while (shadowIndex < endIndex || drawIndex < endIndex) {
98        if (shadowIndex < endIndex) {
99            const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ();
100
101            // attempt to render the shadow if the caster about to be drawn is its caster,
102            // OR if its caster's Z value is similar to the previous potential caster
103            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
104                this->drawShadow(canvas, zChildren[shadowIndex]);
105                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
106                shadowIndex++;
107                continue;
108            }
109        }
110
111        RenderNodeDrawable* childNode = zChildren[drawIndex];
112        SkASSERT(childNode);
113        childNode->forceDraw(canvas);
114
115        drawIndex++;
116    }
117}
118
119// copied from FrameBuilder::deferShadow
120void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
121    const RenderProperties& casterProperties = caster->getNodeProperties();
122
123    if (casterProperties.getAlpha() <= 0.0f
124            || casterProperties.getOutline().getAlpha() <= 0.0f
125            || !casterProperties.getOutline().getPath()
126            || casterProperties.getScaleX() == 0
127            || casterProperties.getScaleY() == 0) {
128        // no shadow to draw
129        return;
130    }
131
132    const SkScalar casterAlpha = casterProperties.getAlpha()
133            * casterProperties.getOutline().getAlpha();
134    if (casterAlpha <= 0.0f) {
135        return;
136    }
137
138    float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha()/255.f)*casterAlpha;
139    float spotAlpha = (SkiaPipeline::getSpotShadowAlpha()/255.f)*casterAlpha;
140    const float casterZValue = casterProperties.getZ();
141
142    const RevealClip& revealClip = casterProperties.getRevealClip();
143    const SkPath* revealClipPath = revealClip.getPath();
144    if (revealClipPath && revealClipPath->isEmpty()) {
145        // An empty reveal clip means nothing is drawn
146        return;
147    }
148
149    bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS;
150
151    SkRect casterClipRect = SkRect::MakeEmpty();
152    if (clippedToBounds) {
153        Rect clipBounds;
154        casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
155        casterClipRect = clipBounds.toSkRect();
156        if (casterClipRect.isEmpty()) {
157            // An empty clip rect means nothing is drawn
158            return;
159        }
160    }
161
162    SkAutoCanvasRestore acr(canvas, true);
163
164    SkMatrix shadowMatrix;
165    mat4 hwuiMatrix;
166    // TODO we don't pass the optional boolean to treat it as a 4x4 matrix
167    caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
168    hwuiMatrix.copyTo(shadowMatrix);
169    canvas->concat(shadowMatrix);
170
171    const SkPath* casterOutlinePath = casterProperties.getOutline().getPath();
172    // holds temporary SkPath to store the result of intersections
173    SkPath tmpPath;
174    const SkPath* casterPath = casterOutlinePath;
175
176    // TODO: In to following course of code that calculates the final shape, is there an optimal
177    //       of doing the Op calculations?
178    // intersect the shadow-casting path with the reveal, if present
179    if (revealClipPath) {
180        Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
181        casterPath = &tmpPath;
182    }
183
184    // intersect the shadow-casting path with the clipBounds, if present
185    if (clippedToBounds) {
186        SkPath clipBoundsPath;
187        clipBoundsPath.addRect(casterClipRect);
188        Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath);
189        casterPath = &tmpPath;
190    }
191    const Vector3 lightPos = SkiaPipeline::getLightCenter();
192    SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z);
193    if (shadowMatrix.hasPerspective() || revealClipPath || clippedToBounds) {
194        std::function<SkScalar(SkScalar, SkScalar)> casterHeightFunc;
195        if (shadowMatrix.hasPerspective()) {
196            // get the matrix with the full 3D transform
197            mat4 zMatrix;
198            caster->getRenderNode()->applyViewPropertyTransforms(zMatrix, true);
199            SkScalar A = zMatrix[2];
200            SkScalar B = zMatrix[6];
201            SkScalar C = zMatrix[mat4::kTranslateZ];
202            casterHeightFunc = [A, B, C](SkScalar x, SkScalar y) {
203                return A*x + B*y + C;  // casterZValue already baked into C
204            };
205        } else {
206            casterHeightFunc = [casterZValue] (SkScalar, SkScalar) {
207                return casterZValue;
208            };
209        }
210
211        SkShadowUtils::DrawUncachedShadow(canvas, *casterPath, casterHeightFunc, skiaLightPos,
212            SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK,
213            casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
214    } else {
215        SkShadowUtils::DrawShadow(canvas, *casterPath, casterZValue, skiaLightPos,
216            SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK,
217            casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
218    }
219}
220
221}; // namespace skiapipeline
222}; // namespace uirenderer
223}; // namespace android
224