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