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