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