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