blob: 420e33136c0ec5b4b0b9b3644e5bf95925ef5511 [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
19#include <cutils/log.h>
20
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() {
48 mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
49 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) {
60 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", currentFrame->type);
71 }
72}
73
74void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
75 outMatrix->loadIdentity();
76 computeTransformImpl(mHead, outMatrix);
77}
78
John Recka447d292014-06-11 18:39:44 -070079void DamageAccumulator::pushCommon() {
John Recke4267ea2014-06-03 15:53:15 -070080 if (!mHead->next) {
81 DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
82 nextFrame->next = 0;
83 nextFrame->prev = mHead;
84 mHead->next = nextFrame;
85 }
86 mHead = mHead->next;
John Recke4267ea2014-06-03 15:53:15 -070087 mHead->pendingDirty.setEmpty();
88}
89
John Recka447d292014-06-11 18:39:44 -070090void DamageAccumulator::pushTransform(const RenderNode* transform) {
91 pushCommon();
92 mHead->type = TransformRenderNode;
93 mHead->renderNode = transform;
94}
95
96void DamageAccumulator::pushTransform(const Matrix4* transform) {
97 pushCommon();
98 mHead->type = TransformMatrix4;
99 mHead->matrix4 = transform;
100}
101
102void DamageAccumulator::popTransform() {
John Recke4267ea2014-06-03 15:53:15 -0700103 LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
104 DirtyStack* dirtyFrame = mHead;
105 mHead = mHead->prev;
John Reck25fbb3f2014-06-12 13:46:45 -0700106 switch (dirtyFrame->type) {
107 case TransformRenderNode:
John Recka447d292014-06-11 18:39:44 -0700108 applyRenderNodeTransform(dirtyFrame);
John Reck25fbb3f2014-06-12 13:46:45 -0700109 break;
110 case TransformMatrix4:
John Recka447d292014-06-11 18:39:44 -0700111 applyMatrix4Transform(dirtyFrame);
John Reck25fbb3f2014-06-12 13:46:45 -0700112 break;
113 case TransformNone:
114 mHead->pendingDirty.join(dirtyFrame->pendingDirty);
115 break;
John Reck2dc223d2014-06-17 10:46:09 -0700116 default:
117 LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
John Recka447d292014-06-11 18:39:44 -0700118 }
119}
120
121static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
122 if (in.isEmpty()) return;
123 Rect temp(in);
124 matrix->mapRect(temp);
125 out->join(RECT_ARGS(temp));
126}
127
128void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
129 mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
130}
131
132static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
133 if (in.isEmpty()) return;
134 const SkMatrix* transform = props.getTransformMatrix();
135 SkRect temp(in);
136 if (transform && !transform->isIdentity()) {
137 transform->mapRect(&temp);
138 }
139 temp.offset(props.getLeft(), props.getTop());
140 out->join(temp);
141}
142
143static DirtyStack* findParentRenderNode(DirtyStack* frame) {
144 while (frame->prev != frame) {
145 frame = frame->prev;
146 if (frame->type == TransformRenderNode) {
147 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700148 }
John Recka447d292014-06-11 18:39:44 -0700149 }
150 return NULL;
151}
152
153static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
154 if (frame) {
155 while (frame->prev != frame) {
156 frame = frame->prev;
157 if (frame->type == TransformRenderNode
158 && frame->renderNode->hasProjectionReceiver()) {
159 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700160 }
John Recke4267ea2014-06-03 15:53:15 -0700161 }
John Recka447d292014-06-11 18:39:44 -0700162 }
163 return NULL;
164}
165
166static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
167 SkRect* rect = &frame->pendingDirty;
168 while (frame != end) {
169 if (frame->type == TransformRenderNode) {
170 mapRect(frame->renderNode->properties(), *rect, rect);
171 } else {
172 mapRect(frame->matrix4, *rect, rect);
173 }
174 frame = frame->prev;
175 }
176}
177
178void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
179 if (frame->pendingDirty.isEmpty()) {
180 return;
181 }
182
183 const RenderProperties& props = frame->renderNode->properties();
John Reckce9f3082014-06-17 16:18:09 -0700184 if (props.getAlpha() <= 0) {
185 return;
186 }
John Recka447d292014-06-11 18:39:44 -0700187
188 // Perform clipping
John Reck293e8682014-06-17 10:34:02 -0700189 if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
John Recka447d292014-06-11 18:39:44 -0700190 if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
191 frame->pendingDirty.setEmpty();
192 }
193 }
194
195 // apply all transforms
196 mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
197
198 // project backwards if necessary
199 if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
200 // First, find our parent RenderNode:
201 DirtyStack* parentNode = findParentRenderNode(frame);
202 // Find our parent's projection receiver, which is what we project onto
203 DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
204 if (projectionReceiver) {
205 applyTransforms(frame, projectionReceiver);
206 projectionReceiver->pendingDirty.join(frame->pendingDirty);
John Recka447d292014-06-11 18:39:44 -0700207 }
208
209 frame->pendingDirty.setEmpty();
John Recke4267ea2014-06-03 15:53:15 -0700210 }
211}
212
213void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
214 mHead->pendingDirty.join(left, top, right, bottom);
215}
216
Chris Craikc71bfca2014-08-21 10:18:58 -0700217void DamageAccumulator::peekAtDirty(SkRect* dest) const {
John Reck25fbb3f2014-06-12 13:46:45 -0700218 *dest = mHead->pendingDirty;
219}
220
John Recke4267ea2014-06-03 15:53:15 -0700221void DamageAccumulator::finish(SkRect* totalDirty) {
222 LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
223 // Root node never has a transform, so this is the fully mapped dirty rect
224 *totalDirty = mHead->pendingDirty;
225 totalDirty->roundOut();
226 mHead->pendingDirty.setEmpty();
227}
228
229} /* namespace uirenderer */
230} /* namespace android */