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