blob: 25c51f2716e62c3314c7ade235df05afc9b1e65e [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"
Stan Iliev500a0c32016-10-26 10:30:09 -040020#include "SkiaPipeline.h"
Stan Iliev021693b2016-10-17 16:26:15 -040021
22#include <SkBlurMask.h>
23#include <SkBlurMaskFilter.h>
Stan Iliev021693b2016-10-17 16:26:15 -040024#include <SkPathOps.h>
Derek Sollenbergerf87da672016-11-02 11:34:27 -040025#include <SkRRectsGaussianEdgeMaskFilter.h>
Stan Iliev30a75de2017-01-24 17:24:27 -050026#include <SkShadowUtils.h>
Stan Iliev021693b2016-10-17 16:26:15 -040027
28namespace android {
29namespace uirenderer {
30namespace skiapipeline {
31
32StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
John Reck1bcacfd2017-11-03 10:12:19 -070033 : mEndChildIndex(0), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {}
Stan Iliev021693b2016-10-17 16:26:15 -040034
35void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
36 if (mChildren.empty()) {
John Reck1bcacfd2017-11-03 10:12:19 -070037 // mChildren is allocated and initialized only the first time onDraw is called and cached
38 // for
39 // subsequent calls
Stan Iliev021693b2016-10-17 16:26:15 -040040 mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1);
Stan Iliev347691f2016-12-01 12:25:07 -050041 for (int i = mBeginChildIndex; i <= mEndChildIndex; i++) {
Stan Iliev021693b2016-10-17 16:26:15 -040042 mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i]));
43 }
44 }
45 std::stable_sort(mChildren.begin(), mChildren.end(),
John Reck1bcacfd2017-11-03 10:12:19 -070046 [](RenderNodeDrawable* a, RenderNodeDrawable* b) {
47 const float aZValue = a->getNodeProperties().getZ();
48 const float bZValue = b->getNodeProperties().getZ();
49 return aZValue < bZValue;
50 });
Stan Iliev021693b2016-10-17 16:26:15 -040051
Stan Iliev021693b2016-10-17 16:26:15 -040052 size_t drawIndex = 0;
53 const size_t endIndex = mChildren.size();
54 while (drawIndex < endIndex) {
55 RenderNodeDrawable* childNode = mChildren[drawIndex];
56 SkASSERT(childNode);
57 const float casterZ = childNode->getNodeProperties().getZ();
John Reck1bcacfd2017-11-03 10:12:19 -070058 if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z
Stan Iliev021693b2016-10-17 16:26:15 -040059 return;
60 }
61 childNode->forceDraw(canvas);
62 drawIndex++;
63 }
64}
65
66EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
67 : mStartBarrier(startBarrier) {
68 mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
69}
70
71#define SHADOW_DELTA 0.1f
72
73void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
74 auto& zChildren = mStartBarrier->mChildren;
Stan Iliev021693b2016-10-17 16:26:15 -040075
76 /**
77 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
78 * with very similar Z heights to draw together.
79 *
80 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
81 * underneath both, and neither's shadow is drawn on top of the other.
82 */
83 size_t drawIndex = 0;
84
85 const size_t endIndex = zChildren.size();
John Reck1bcacfd2017-11-03 10:12:19 -070086 while (drawIndex < endIndex // draw only children with positive Z
87 && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON)
88 drawIndex++;
Stan Iliev021693b2016-10-17 16:26:15 -040089 size_t shadowIndex = drawIndex;
90
91 float lastCasterZ = 0.0f;
92 while (shadowIndex < endIndex || drawIndex < endIndex) {
93 if (shadowIndex < endIndex) {
94 const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ();
95
96 // attempt to render the shadow if the caster about to be drawn is its caster,
97 // OR if its caster's Z value is similar to the previous potential caster
98 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
99 this->drawShadow(canvas, zChildren[shadowIndex]);
John Reck1bcacfd2017-11-03 10:12:19 -0700100 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
Stan Iliev021693b2016-10-17 16:26:15 -0400101 shadowIndex++;
102 continue;
103 }
104 }
105
106 RenderNodeDrawable* childNode = zChildren[drawIndex];
107 SkASSERT(childNode);
108 childNode->forceDraw(canvas);
109
110 drawIndex++;
111 }
112}
113
John Reckd8be4a02017-11-17 15:06:24 -0800114static SkColor multiplyAlpha(SkColor color, float alpha) {
115 return SkColorSetA(color, alpha * SkColorGetA(color));
116}
117
Stan Iliev021693b2016-10-17 16:26:15 -0400118// copied from FrameBuilder::deferShadow
119void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
120 const RenderProperties& casterProperties = caster->getNodeProperties();
121
John Reck1bcacfd2017-11-03 10:12:19 -0700122 if (casterProperties.getAlpha() <= 0.0f || casterProperties.getOutline().getAlpha() <= 0.0f ||
123 !casterProperties.getOutline().getPath() || casterProperties.getScaleX() == 0 ||
124 casterProperties.getScaleY() == 0) {
Stan Iliev021693b2016-10-17 16:26:15 -0400125 // no shadow to draw
126 return;
127 }
128
John Reck1bcacfd2017-11-03 10:12:19 -0700129 const SkScalar casterAlpha =
130 casterProperties.getAlpha() * casterProperties.getOutline().getAlpha();
Stan Iliev021693b2016-10-17 16:26:15 -0400131 if (casterAlpha <= 0.0f) {
132 return;
133 }
134
John Reck1bcacfd2017-11-03 10:12:19 -0700135 float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha() / 255.f) * casterAlpha;
136 float spotAlpha = (SkiaPipeline::getSpotShadowAlpha() / 255.f) * casterAlpha;
Stan Iliev021693b2016-10-17 16:26:15 -0400137
138 const RevealClip& revealClip = casterProperties.getRevealClip();
139 const SkPath* revealClipPath = revealClip.getPath();
140 if (revealClipPath && revealClipPath->isEmpty()) {
141 // An empty reveal clip means nothing is drawn
142 return;
143 }
144
145 bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS;
146
Derek Sollenbergerf87da672016-11-02 11:34:27 -0400147 SkRect casterClipRect = SkRect::MakeEmpty();
Stan Iliev021693b2016-10-17 16:26:15 -0400148 if (clippedToBounds) {
149 Rect clipBounds;
150 casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
151 casterClipRect = clipBounds.toSkRect();
Derek Sollenbergerf87da672016-11-02 11:34:27 -0400152 if (casterClipRect.isEmpty()) {
153 // An empty clip rect means nothing is drawn
154 return;
155 }
Stan Iliev021693b2016-10-17 16:26:15 -0400156 }
157
158 SkAutoCanvasRestore acr(canvas, true);
159
160 SkMatrix shadowMatrix;
Stan Ilievd7410f72017-04-04 15:23:54 -0400161 mat4 hwuiMatrix;
Stan Iliev021693b2016-10-17 16:26:15 -0400162 // TODO we don't pass the optional boolean to treat it as a 4x4 matrix
163 caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
164 hwuiMatrix.copyTo(shadowMatrix);
165 canvas->concat(shadowMatrix);
166
Derek Sollenberger579317d2017-08-29 16:33:49 -0400167 // default the shadow-casting path to the outline of the caster
168 const SkPath* casterPath = casterProperties.getOutline().getPath();
Stan Iliev021693b2016-10-17 16:26:15 -0400169
Derek Sollenberger579317d2017-08-29 16:33:49 -0400170 // intersect the shadow-casting path with the clipBounds, if present
171 if (clippedToBounds && !casterClipRect.contains(casterPath->getBounds())) {
172 casterPath = caster->getRenderNode()->getClippedOutline(casterClipRect);
173 }
174
Stan Iliev021693b2016-10-17 16:26:15 -0400175 // intersect the shadow-casting path with the reveal, if present
John Reck1bcacfd2017-11-03 10:12:19 -0700176 SkPath tmpPath; // holds temporary SkPath to store the result of intersections
Stan Iliev021693b2016-10-17 16:26:15 -0400177 if (revealClipPath) {
178 Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
Jim Van Verth749b93d2017-05-12 10:03:03 -0400179 tmpPath.setIsVolatile(true);
Stan Iliev021693b2016-10-17 16:26:15 -0400180 casterPath = &tmpPath;
181 }
182
Stan Iliev30a75de2017-01-24 17:24:27 -0500183 const Vector3 lightPos = SkiaPipeline::getLightCenter();
184 SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z);
Jim Van Verth749b93d2017-05-12 10:03:03 -0400185 SkPoint3 zParams;
186 if (shadowMatrix.hasPerspective()) {
187 // get the matrix with the full 3D transform
188 mat4 zMatrix;
189 caster->getRenderNode()->applyViewPropertyTransforms(zMatrix, true);
190 zParams = SkPoint3::Make(zMatrix[2], zMatrix[6], zMatrix[mat4::kTranslateZ]);
Jim Van Verth5745a0a2017-04-12 14:08:46 -0400191 } else {
Jim Van Verth749b93d2017-05-12 10:03:03 -0400192 zParams = SkPoint3::Make(0, 0, casterProperties.getZ());
Jim Van Verth5745a0a2017-04-12 14:08:46 -0400193 }
John Reckd8be4a02017-11-17 15:06:24 -0800194 SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha);
195 SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha);
John Reck1bcacfd2017-11-03 10:12:19 -0700196 SkShadowUtils::DrawShadow(
197 canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(),
John Reckd8be4a02017-11-17 15:06:24 -0800198 ambientColor, spotColor,
John Reck1bcacfd2017-11-03 10:12:19 -0700199 casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
Stan Iliev021693b2016-10-17 16:26:15 -0400200}
201
John Reck1bcacfd2017-11-03 10:12:19 -0700202}; // namespace skiapipeline
203}; // namespace uirenderer
204}; // namespace android