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