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 
29 namespace android {
30 namespace uirenderer {
31 namespace skiapipeline {
32 
StartReorderBarrierDrawable(SkiaDisplayList * data)33 StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
34         : mEndChildIndex(0)
35         , mBeginChildIndex(data->mChildNodes.size())
36         , mDisplayList(data) {
37 }
38 
onDraw(SkCanvas * canvas)39 void 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 
EndReorderBarrierDrawable(StartReorderBarrierDrawable * startBarrier)71 EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
72         : mStartBarrier(startBarrier) {
73     mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
74 }
75 
76 #define SHADOW_DELTA 0.1f
77 
onDraw(SkCanvas * canvas)78 void 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
drawShadow(SkCanvas * canvas,RenderNodeDrawable * caster)120 void 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