• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
28 namespace android {
29 namespace uirenderer {
30 namespace skiapipeline {
31 
StartReorderBarrierDrawable(SkiaDisplayList * data)32 StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
33         : mEndChildIndex(0)
34         , mBeginChildIndex(data->mChildNodes.size())
35         , mDisplayList(data) {
36 }
37 
onDraw(SkCanvas * canvas)38 void 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 
EndReorderBarrierDrawable(StartReorderBarrierDrawable * startBarrier)68 EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
69         : mStartBarrier(startBarrier) {
70     mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
71 }
72 
73 #define SHADOW_DELTA 0.1f
74 
onDraw(SkCanvas * canvas)75 void 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
drawShadow(SkCanvas * canvas,RenderNodeDrawable * caster)116 void 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