blob: 9db47c3ba0904470f5cab0f40b49c753d46af1dd [file] [log] [blame]
John Recke4267ea2014-06-03 15:53:15 -07001/*
2 * Copyright (C) 2014 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
John Recke4267ea2014-06-03 15:53:15 -070017#include "DamageAccumulator.h"
18
Mark Salyzyn52eb4e02016-09-28 16:15:30 -070019#include <log/log.h>
John Recke4267ea2014-06-03 15:53:15 -070020
21#include "RenderNode.h"
22#include "utils/MathUtils.h"
23
24namespace android {
25namespace uirenderer {
26
John Recka447d292014-06-11 18:39:44 -070027enum TransformType {
John Reck2dc223d2014-06-17 10:46:09 -070028 TransformInvalid = 0,
John Recka447d292014-06-11 18:39:44 -070029 TransformRenderNode,
30 TransformMatrix4,
John Reck25fbb3f2014-06-12 13:46:45 -070031 TransformNone,
John Recka447d292014-06-11 18:39:44 -070032};
33
John Recke4267ea2014-06-03 15:53:15 -070034struct DirtyStack {
John Recka447d292014-06-11 18:39:44 -070035 TransformType type;
36 union {
37 const RenderNode* renderNode;
38 const Matrix4* matrix4;
39 };
John Recke4267ea2014-06-03 15:53:15 -070040 // When this frame is pop'd, this rect is mapped through the above transform
41 // and applied to the previous (aka parent) frame
42 SkRect pendingDirty;
43 DirtyStack* prev;
44 DirtyStack* next;
45};
46
47DamageAccumulator::DamageAccumulator() {
John Reck7df9ff22016-02-10 16:08:08 -080048 mHead = mAllocator.create_trivial<DirtyStack>();
John Recke4267ea2014-06-03 15:53:15 -070049 memset(mHead, 0, sizeof(DirtyStack));
50 // Create a root that we will not pop off
51 mHead->prev = mHead;
John Reck2dc223d2014-06-17 10:46:09 -070052 mHead->type = TransformNone;
John Recke4267ea2014-06-03 15:53:15 -070053}
54
Chris Craik69e5adf2014-08-14 13:34:01 -070055static void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMatrix) {
56 if (currentFrame->prev != currentFrame) {
57 computeTransformImpl(currentFrame->prev, outMatrix);
58 }
59 switch (currentFrame->type) {
John Reck1bcacfd2017-11-03 10:12:19 -070060 case TransformRenderNode:
61 currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
62 break;
63 case TransformMatrix4:
64 outMatrix->multiply(*currentFrame->matrix4);
65 break;
66 case TransformNone:
67 // nothing to be done
68 break;
69 default:
70 LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d",
71 currentFrame->type);
Chris Craik69e5adf2014-08-14 13:34:01 -070072 }
73}
74
75void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
76 outMatrix->loadIdentity();
77 computeTransformImpl(mHead, outMatrix);
78}
79
John Recka447d292014-06-11 18:39:44 -070080void DamageAccumulator::pushCommon() {
John Recke4267ea2014-06-03 15:53:15 -070081 if (!mHead->next) {
John Reck7df9ff22016-02-10 16:08:08 -080082 DirtyStack* nextFrame = mAllocator.create_trivial<DirtyStack>();
Chris Craikd41c4d82015-01-05 15:51:13 -080083 nextFrame->next = nullptr;
John Recke4267ea2014-06-03 15:53:15 -070084 nextFrame->prev = mHead;
85 mHead->next = nextFrame;
86 }
87 mHead = mHead->next;
John Recke4267ea2014-06-03 15:53:15 -070088 mHead->pendingDirty.setEmpty();
89}
90
John Recka447d292014-06-11 18:39:44 -070091void DamageAccumulator::pushTransform(const RenderNode* transform) {
92 pushCommon();
93 mHead->type = TransformRenderNode;
94 mHead->renderNode = transform;
95}
96
97void DamageAccumulator::pushTransform(const Matrix4* transform) {
98 pushCommon();
99 mHead->type = TransformMatrix4;
100 mHead->matrix4 = transform;
101}
102
103void DamageAccumulator::popTransform() {
John Recke4267ea2014-06-03 15:53:15 -0700104 LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
105 DirtyStack* dirtyFrame = mHead;
106 mHead = mHead->prev;
John Reck25fbb3f2014-06-12 13:46:45 -0700107 switch (dirtyFrame->type) {
John Reck1bcacfd2017-11-03 10:12:19 -0700108 case TransformRenderNode:
109 applyRenderNodeTransform(dirtyFrame);
110 break;
111 case TransformMatrix4:
112 applyMatrix4Transform(dirtyFrame);
113 break;
114 case TransformNone:
115 mHead->pendingDirty.join(dirtyFrame->pendingDirty);
116 break;
117 default:
118 LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
John Recka447d292014-06-11 18:39:44 -0700119 }
120}
121
122static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
123 if (in.isEmpty()) return;
124 Rect temp(in);
John Reckc1288232015-08-12 13:39:11 -0700125 if (CC_LIKELY(!matrix->isPerspective())) {
126 matrix->mapRect(temp);
127 } else {
128 // Don't attempt to calculate damage for a perspective transform
129 // as the numbers this works with can break the perspective
130 // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX
131 temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
132 }
Mike Reed39adc882019-08-22 11:53:05 -0400133 out->join({RECT_ARGS(temp)});
John Recka447d292014-06-11 18:39:44 -0700134}
135
136void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
137 mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
138}
139
John Recke2750522019-06-26 13:46:09 -0700140static inline void applyMatrix(const SkMatrix* transform, SkRect* rect) {
John Recka447d292014-06-11 18:39:44 -0700141 if (transform && !transform->isIdentity()) {
John Reckc1288232015-08-12 13:39:11 -0700142 if (CC_LIKELY(!transform->hasPerspective())) {
John Recke2750522019-06-26 13:46:09 -0700143 transform->mapRect(rect);
John Reckc1288232015-08-12 13:39:11 -0700144 } else {
145 // Don't attempt to calculate damage for a perspective transform
146 // as the numbers this works with can break the perspective
147 // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX
Mike Reed39adc882019-08-22 11:53:05 -0400148 rect->setLTRB(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
John Reckc1288232015-08-12 13:39:11 -0700149 }
John Recka447d292014-06-11 18:39:44 -0700150 }
John Recke2750522019-06-26 13:46:09 -0700151}
152
John Reck8ed00dc2021-05-10 13:09:27 -0400153static inline void applyMatrix(const SkMatrix& transform, SkRect* rect) {
154 return applyMatrix(&transform, rect);
155}
156
John Recke2750522019-06-26 13:46:09 -0700157static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
158 if (in.isEmpty()) return;
159 SkRect temp(in);
Nader Jawad93d6e242021-05-17 18:12:29 -0700160 if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
John Reck8ed00dc2021-05-10 13:09:27 -0400161 const StretchEffect& stretch = props.layerProperties().getStretchEffect();
162 if (!stretch.isEmpty()) {
163 applyMatrix(stretch.makeLinearStretch(props.getWidth(), props.getHeight()), &temp);
164 }
165 }
John Recke2750522019-06-26 13:46:09 -0700166 applyMatrix(props.getTransformMatrix(), &temp);
167 if (props.getStaticMatrix()) {
168 applyMatrix(props.getStaticMatrix(), &temp);
169 } else if (props.getAnimationMatrix()) {
170 applyMatrix(props.getAnimationMatrix(), &temp);
171 }
John Recka447d292014-06-11 18:39:44 -0700172 temp.offset(props.getLeft(), props.getTop());
173 out->join(temp);
174}
175
176static DirtyStack* findParentRenderNode(DirtyStack* frame) {
177 while (frame->prev != frame) {
178 frame = frame->prev;
179 if (frame->type == TransformRenderNode) {
180 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700181 }
John Recka447d292014-06-11 18:39:44 -0700182 }
Chris Craikd41c4d82015-01-05 15:51:13 -0800183 return nullptr;
John Recka447d292014-06-11 18:39:44 -0700184}
185
186static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
187 if (frame) {
188 while (frame->prev != frame) {
189 frame = frame->prev;
John Reck1bcacfd2017-11-03 10:12:19 -0700190 if (frame->type == TransformRenderNode && frame->renderNode->hasProjectionReceiver()) {
John Recka447d292014-06-11 18:39:44 -0700191 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700192 }
John Recke4267ea2014-06-03 15:53:15 -0700193 }
John Recka447d292014-06-11 18:39:44 -0700194 }
Chris Craikd41c4d82015-01-05 15:51:13 -0800195 return nullptr;
John Recka447d292014-06-11 18:39:44 -0700196}
197
198static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
199 SkRect* rect = &frame->pendingDirty;
200 while (frame != end) {
201 if (frame->type == TransformRenderNode) {
202 mapRect(frame->renderNode->properties(), *rect, rect);
203 } else {
204 mapRect(frame->matrix4, *rect, rect);
205 }
206 frame = frame->prev;
207 }
208}
209
Nader Jawad197743f2021-04-19 19:45:13 -0700210static void computeTransformImpl(const DirtyStack* frame, const DirtyStack* end,
211 Matrix4* outMatrix) {
212 while (frame != end) {
213 switch (frame->type) {
214 case TransformRenderNode:
215 frame->renderNode->applyViewPropertyTransforms(*outMatrix);
216 break;
217 case TransformMatrix4:
218 outMatrix->multiply(*frame->matrix4);
219 break;
220 case TransformNone:
221 // nothing to be done
222 break;
223 default:
224 LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d",
225 frame->type);
226 }
227 frame = frame->prev;
228 }
229}
230
John Recka447d292014-06-11 18:39:44 -0700231void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
232 if (frame->pendingDirty.isEmpty()) {
233 return;
234 }
235
236 const RenderProperties& props = frame->renderNode->properties();
John Reckce9f3082014-06-17 16:18:09 -0700237 if (props.getAlpha() <= 0) {
238 return;
239 }
John Recka447d292014-06-11 18:39:44 -0700240
241 // Perform clipping
John Reck293e8682014-06-17 10:34:02 -0700242 if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
Mike Reed39adc882019-08-22 11:53:05 -0400243 if (!frame->pendingDirty.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) {
John Recka447d292014-06-11 18:39:44 -0700244 frame->pendingDirty.setEmpty();
245 }
246 }
247
248 // apply all transforms
249 mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
250
251 // project backwards if necessary
252 if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
253 // First, find our parent RenderNode:
254 DirtyStack* parentNode = findParentRenderNode(frame);
255 // Find our parent's projection receiver, which is what we project onto
256 DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
257 if (projectionReceiver) {
258 applyTransforms(frame, projectionReceiver);
259 projectionReceiver->pendingDirty.join(frame->pendingDirty);
John Recka447d292014-06-11 18:39:44 -0700260 }
261
262 frame->pendingDirty.setEmpty();
John Recke4267ea2014-06-03 15:53:15 -0700263 }
264}
265
266void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
Mike Reed39adc882019-08-22 11:53:05 -0400267 mHead->pendingDirty.join({left, top, right, bottom});
John Recke4267ea2014-06-03 15:53:15 -0700268}
269
Chris Craikc71bfca2014-08-21 10:18:58 -0700270void DamageAccumulator::peekAtDirty(SkRect* dest) const {
John Reck25fbb3f2014-06-12 13:46:45 -0700271 *dest = mHead->pendingDirty;
272}
273
John Recke4267ea2014-06-03 15:53:15 -0700274void DamageAccumulator::finish(SkRect* totalDirty) {
John Reck1bcacfd2017-11-03 10:12:19 -0700275 LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p",
276 mHead->prev, mHead);
John Recke4267ea2014-06-03 15:53:15 -0700277 // Root node never has a transform, so this is the fully mapped dirty rect
278 *totalDirty = mHead->pendingDirty;
Mike Reed71487eb2014-11-19 16:13:20 -0500279 totalDirty->roundOut(totalDirty);
John Recke4267ea2014-06-03 15:53:15 -0700280 mHead->pendingDirty.setEmpty();
281}
282
Nader Jawad197743f2021-04-19 19:45:13 -0700283DamageAccumulator::StretchResult DamageAccumulator::findNearestStretchEffect() const {
John Reck5d53a752021-02-08 19:22:59 -0500284 DirtyStack* frame = mHead;
Nader Jawadbc341862021-05-06 10:48:18 -0700285 const auto& headProperties = mHead->renderNode->properties();
286 float startWidth = headProperties.getWidth();
287 float startHeight = headProperties.getHeight();
John Reck5d53a752021-02-08 19:22:59 -0500288 while (frame->prev != frame) {
John Reck5d53a752021-02-08 19:22:59 -0500289 if (frame->type == TransformRenderNode) {
Nader Jawad197743f2021-04-19 19:45:13 -0700290 const auto& renderNode = frame->renderNode;
291 const auto& frameRenderNodeProperties = renderNode->properties();
John Reck5d53a752021-02-08 19:22:59 -0500292 const auto& effect =
Nader Jawad197743f2021-04-19 19:45:13 -0700293 frameRenderNodeProperties.layerProperties().getStretchEffect();
Nader Jawadbc341862021-05-06 10:48:18 -0700294 const float width = (float) frameRenderNodeProperties.getWidth();
295 const float height = (float) frameRenderNodeProperties.getHeight();
John Reck5d53a752021-02-08 19:22:59 -0500296 if (!effect.isEmpty()) {
Nader Jawad197743f2021-04-19 19:45:13 -0700297 Matrix4 stretchMatrix;
298 computeTransformImpl(mHead, frame, &stretchMatrix);
Nader Jawadbc341862021-05-06 10:48:18 -0700299 Rect stretchRect = Rect(0.f, 0.f, startWidth, startHeight);
Nader Jawad197743f2021-04-19 19:45:13 -0700300 stretchMatrix.mapRect(stretchRect);
301
302 return StretchResult{
303 .stretchEffect = &effect,
304 .childRelativeBounds = SkRect::MakeLTRB(
305 stretchRect.left,
306 stretchRect.top,
307 stretchRect.right,
308 stretchRect.bottom
309 ),
310 .width = width,
311 .height = height
312 };
John Reck5d53a752021-02-08 19:22:59 -0500313 }
314 }
Nader Jawad197743f2021-04-19 19:45:13 -0700315 frame = frame->prev;
John Reck5d53a752021-02-08 19:22:59 -0500316 }
Nader Jawad197743f2021-04-19 19:45:13 -0700317 return StretchResult{};
John Reck5d53a752021-02-08 19:22:59 -0500318}
319
John Recke4267ea2014-06-03 15:53:15 -0700320} /* namespace uirenderer */
321} /* namespace android */