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