blob: 3b8caeb3aab120ca3fee656497feb46562f10c05 [file] [log] [blame]
Stan Iliev021693b2016-10-17 16:26:15 -04001/*
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"
Fedor Kudasov90df0562019-06-19 11:41:34 +010020#include "LightingInfo.h"
Stan Iliev021693b2016-10-17 16:26:15 -040021
Stan Iliev021693b2016-10-17 16:26:15 -040022#include <SkPathOps.h>
Stan Iliev30a75de2017-01-24 17:24:27 -050023#include <SkShadowUtils.h>
Stan Iliev021693b2016-10-17 16:26:15 -040024
25namespace android {
26namespace uirenderer {
27namespace skiapipeline {
28
29StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
Fedor Kudasovc5317432019-06-24 09:07:03 +010030 : mEndChildIndex(-1), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {}
Stan Iliev021693b2016-10-17 16:26:15 -040031
32void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
33 if (mChildren.empty()) {
John Reck1bcacfd2017-11-03 10:12:19 -070034 // mChildren is allocated and initialized only the first time onDraw is called and cached
35 // for
36 // subsequent calls
Stan Iliev021693b2016-10-17 16:26:15 -040037 mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1);
Stan Iliev347691f2016-12-01 12:25:07 -050038 for (int i = mBeginChildIndex; i <= mEndChildIndex; i++) {
Stan Iliev021693b2016-10-17 16:26:15 -040039 mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i]));
40 }
41 }
42 std::stable_sort(mChildren.begin(), mChildren.end(),
John Reck1bcacfd2017-11-03 10:12:19 -070043 [](RenderNodeDrawable* a, RenderNodeDrawable* b) {
44 const float aZValue = a->getNodeProperties().getZ();
45 const float bZValue = b->getNodeProperties().getZ();
46 return aZValue < bZValue;
47 });
Stan Iliev021693b2016-10-17 16:26:15 -040048
Stan Iliev021693b2016-10-17 16:26:15 -040049 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();
John Reck1bcacfd2017-11-03 10:12:19 -070055 if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z
Stan Iliev021693b2016-10-17 16:26:15 -040056 return;
57 }
Stan Iliev54d70322018-06-14 18:00:10 -040058 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());
Stan Iliev021693b2016-10-17 16:26:15 -040063 childNode->forceDraw(canvas);
64 drawIndex++;
65 }
66}
67
68EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
69 : mStartBarrier(startBarrier) {
70 mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
71}
72
73#define SHADOW_DELTA 0.1f
74
75void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
76 auto& zChildren = mStartBarrier->mChildren;
Stan Iliev021693b2016-10-17 16:26:15 -040077
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();
John Reck1bcacfd2017-11-03 10:12:19 -070088 while (drawIndex < endIndex // draw only children with positive Z
89 && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON)
90 drawIndex++;
Stan Iliev021693b2016-10-17 16:26:15 -040091 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]);
John Reck1bcacfd2017-11-03 10:12:19 -0700102 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
Stan Iliev021693b2016-10-17 16:26:15 -0400103 shadowIndex++;
104 continue;
105 }
106 }
107
108 RenderNodeDrawable* childNode = zChildren[drawIndex];
109 SkASSERT(childNode);
Stan Iliev54d70322018-06-14 18:00:10 -0400110 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());
Stan Iliev021693b2016-10-17 16:26:15 -0400115 childNode->forceDraw(canvas);
116
117 drawIndex++;
118 }
119}
120
John Reckd8be4a02017-11-17 15:06:24 -0800121static SkColor multiplyAlpha(SkColor color, float alpha) {
122 return SkColorSetA(color, alpha * SkColorGetA(color));
123}
124
Stan Iliev021693b2016-10-17 16:26:15 -0400125// copied from FrameBuilder::deferShadow
126void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
127 const RenderProperties& casterProperties = caster->getNodeProperties();
128
John Reck1bcacfd2017-11-03 10:12:19 -0700129 if (casterProperties.getAlpha() <= 0.0f || casterProperties.getOutline().getAlpha() <= 0.0f ||
130 !casterProperties.getOutline().getPath() || casterProperties.getScaleX() == 0 ||
131 casterProperties.getScaleY() == 0) {
Stan Iliev021693b2016-10-17 16:26:15 -0400132 // no shadow to draw
133 return;
134 }
135
John Reck1bcacfd2017-11-03 10:12:19 -0700136 const SkScalar casterAlpha =
137 casterProperties.getAlpha() * casterProperties.getOutline().getAlpha();
Stan Iliev021693b2016-10-17 16:26:15 -0400138 if (casterAlpha <= 0.0f) {
139 return;
140 }
141
Fedor Kudasov90df0562019-06-19 11:41:34 +0100142 float ambientAlpha = (LightingInfo::getAmbientShadowAlpha() / 255.f) * casterAlpha;
143 float spotAlpha = (LightingInfo::getSpotShadowAlpha() / 255.f) * casterAlpha;
Stan Iliev021693b2016-10-17 16:26:15 -0400144
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
Derek Sollenbergerf87da672016-11-02 11:34:27 -0400154 SkRect casterClipRect = SkRect::MakeEmpty();
Stan Iliev021693b2016-10-17 16:26:15 -0400155 if (clippedToBounds) {
156 Rect clipBounds;
157 casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
158 casterClipRect = clipBounds.toSkRect();
Derek Sollenbergerf87da672016-11-02 11:34:27 -0400159 if (casterClipRect.isEmpty()) {
160 // An empty clip rect means nothing is drawn
161 return;
162 }
Stan Iliev021693b2016-10-17 16:26:15 -0400163 }
164
165 SkAutoCanvasRestore acr(canvas, true);
Stan Iliev54d70322018-06-14 18:00:10 -0400166 // 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);
Stan Iliev021693b2016-10-17 16:26:15 -0400169
170 SkMatrix shadowMatrix;
Stan Iliev54d70322018-06-14 18:00:10 -0400171 mat4 hwuiMatrix(caster->getRecordedMatrix());
Stan Iliev021693b2016-10-17 16:26:15 -0400172 // TODO we don't pass the optional boolean to treat it as a 4x4 matrix
Stan Iliev54d70322018-06-14 18:00:10 -0400173 // applyViewPropertyTransforms gets the same matrix, which render nodes apply with
174 // RenderNodeDrawable::setViewProperties as a part if their draw.
Stan Iliev021693b2016-10-17 16:26:15 -0400175 caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
176 hwuiMatrix.copyTo(shadowMatrix);
177 canvas->concat(shadowMatrix);
178
Derek Sollenberger579317d2017-08-29 16:33:49 -0400179 // default the shadow-casting path to the outline of the caster
180 const SkPath* casterPath = casterProperties.getOutline().getPath();
Stan Iliev021693b2016-10-17 16:26:15 -0400181
Derek Sollenberger579317d2017-08-29 16:33:49 -0400182 // 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
Stan Iliev021693b2016-10-17 16:26:15 -0400187 // intersect the shadow-casting path with the reveal, if present
John Reck1bcacfd2017-11-03 10:12:19 -0700188 SkPath tmpPath; // holds temporary SkPath to store the result of intersections
Stan Iliev021693b2016-10-17 16:26:15 -0400189 if (revealClipPath) {
190 Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
Jim Van Verth749b93d2017-05-12 10:03:03 -0400191 tmpPath.setIsVolatile(true);
Stan Iliev021693b2016-10-17 16:26:15 -0400192 casterPath = &tmpPath;
193 }
194
Fedor Kudasov90df0562019-06-19 11:41:34 +0100195 const Vector3 lightPos = LightingInfo::getLightCenter();
Stan Iliev30a75de2017-01-24 17:24:27 -0500196 SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z);
Jim Van Verth749b93d2017-05-12 10:03:03 -0400197 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]);
Jim Van Verth5745a0a2017-04-12 14:08:46 -0400203 } else {
Jim Van Verth749b93d2017-05-12 10:03:03 -0400204 zParams = SkPoint3::Make(0, 0, casterProperties.getZ());
Jim Van Verth5745a0a2017-04-12 14:08:46 -0400205 }
John Reckd8be4a02017-11-17 15:06:24 -0800206 SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha);
207 SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha);
John Reck1bcacfd2017-11-03 10:12:19 -0700208 SkShadowUtils::DrawShadow(
Fedor Kudasov90df0562019-06-19 11:41:34 +0100209 canvas, *casterPath, zParams, skiaLightPos, LightingInfo::getLightRadius(),
John Reckd8be4a02017-11-17 15:06:24 -0800210 ambientColor, spotColor,
John Reck1bcacfd2017-11-03 10:12:19 -0700211 casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
Stan Iliev021693b2016-10-17 16:26:15 -0400212}
213
Chris Blume7b8a8082018-11-30 15:51:58 -0800214} // namespace skiapipeline
215} // namespace uirenderer
216} // namespace android