blob: 898e81a1a822c818229523e6b66b1f0a402bb124 [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
17#define LOG_TAG "DamageAccumulator"
18
19#include "DamageAccumulator.h"
20
21#include <cutils/log.h>
22
23#include "RenderNode.h"
24#include "utils/MathUtils.h"
25
26namespace android {
27namespace uirenderer {
28
John Recka447d292014-06-11 18:39:44 -070029NullDamageAccumulator NullDamageAccumulator::sInstance;
30
31NullDamageAccumulator* NullDamageAccumulator::instance() {
32 return &sInstance;
33}
34
35enum TransformType {
36 TransformRenderNode,
37 TransformMatrix4,
38};
39
John Recke4267ea2014-06-03 15:53:15 -070040struct DirtyStack {
John Recka447d292014-06-11 18:39:44 -070041 TransformType type;
42 union {
43 const RenderNode* renderNode;
44 const Matrix4* matrix4;
45 };
John Recke4267ea2014-06-03 15:53:15 -070046 // When this frame is pop'd, this rect is mapped through the above transform
47 // and applied to the previous (aka parent) frame
48 SkRect pendingDirty;
49 DirtyStack* prev;
50 DirtyStack* next;
51};
52
53DamageAccumulator::DamageAccumulator() {
54 mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
55 memset(mHead, 0, sizeof(DirtyStack));
56 // Create a root that we will not pop off
57 mHead->prev = mHead;
58}
59
John Recka447d292014-06-11 18:39:44 -070060void DamageAccumulator::pushCommon() {
John Recke4267ea2014-06-03 15:53:15 -070061 if (!mHead->next) {
62 DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
63 nextFrame->next = 0;
64 nextFrame->prev = mHead;
65 mHead->next = nextFrame;
66 }
67 mHead = mHead->next;
John Recke4267ea2014-06-03 15:53:15 -070068 mHead->pendingDirty.setEmpty();
69}
70
John Recka447d292014-06-11 18:39:44 -070071void DamageAccumulator::pushTransform(const RenderNode* transform) {
72 pushCommon();
73 mHead->type = TransformRenderNode;
74 mHead->renderNode = transform;
75}
76
77void DamageAccumulator::pushTransform(const Matrix4* transform) {
78 pushCommon();
79 mHead->type = TransformMatrix4;
80 mHead->matrix4 = transform;
81}
82
83void DamageAccumulator::popTransform() {
John Recke4267ea2014-06-03 15:53:15 -070084 LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
85 DirtyStack* dirtyFrame = mHead;
86 mHead = mHead->prev;
John Recka447d292014-06-11 18:39:44 -070087 if (dirtyFrame->type == TransformRenderNode) {
88 applyRenderNodeTransform(dirtyFrame);
89 } else {
90 applyMatrix4Transform(dirtyFrame);
91 }
92}
93
94static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
95 if (in.isEmpty()) return;
96 Rect temp(in);
97 matrix->mapRect(temp);
98 out->join(RECT_ARGS(temp));
99}
100
101void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
102 mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
103}
104
105static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
106 if (in.isEmpty()) return;
107 const SkMatrix* transform = props.getTransformMatrix();
108 SkRect temp(in);
109 if (transform && !transform->isIdentity()) {
110 transform->mapRect(&temp);
111 }
112 temp.offset(props.getLeft(), props.getTop());
113 out->join(temp);
114}
115
116static DirtyStack* findParentRenderNode(DirtyStack* frame) {
117 while (frame->prev != frame) {
118 frame = frame->prev;
119 if (frame->type == TransformRenderNode) {
120 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700121 }
John Recka447d292014-06-11 18:39:44 -0700122 }
123 return NULL;
124}
125
126static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
127 if (frame) {
128 while (frame->prev != frame) {
129 frame = frame->prev;
130 if (frame->type == TransformRenderNode
131 && frame->renderNode->hasProjectionReceiver()) {
132 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700133 }
John Recke4267ea2014-06-03 15:53:15 -0700134 }
John Recka447d292014-06-11 18:39:44 -0700135 }
136 return NULL;
137}
138
139static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
140 SkRect* rect = &frame->pendingDirty;
141 while (frame != end) {
142 if (frame->type == TransformRenderNode) {
143 mapRect(frame->renderNode->properties(), *rect, rect);
144 } else {
145 mapRect(frame->matrix4, *rect, rect);
146 }
147 frame = frame->prev;
148 }
149}
150
151void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
152 if (frame->pendingDirty.isEmpty()) {
153 return;
154 }
155
156 const RenderProperties& props = frame->renderNode->properties();
157
158 // Perform clipping
159 if (props.getClipToBounds() && !frame->pendingDirty.isEmpty()) {
160 if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
161 frame->pendingDirty.setEmpty();
162 }
163 }
164
165 // apply all transforms
166 mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
167
168 // project backwards if necessary
169 if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
170 // First, find our parent RenderNode:
171 DirtyStack* parentNode = findParentRenderNode(frame);
172 // Find our parent's projection receiver, which is what we project onto
173 DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
174 if (projectionReceiver) {
175 applyTransforms(frame, projectionReceiver);
176 projectionReceiver->pendingDirty.join(frame->pendingDirty);
177 } else {
178 ALOGW("Failed to find projection receiver? Dropping on the floor...");
179 }
180
181 frame->pendingDirty.setEmpty();
John Recke4267ea2014-06-03 15:53:15 -0700182 }
183}
184
185void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
186 mHead->pendingDirty.join(left, top, right, bottom);
187}
188
189void DamageAccumulator::finish(SkRect* totalDirty) {
190 LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
191 // Root node never has a transform, so this is the fully mapped dirty rect
192 *totalDirty = mHead->pendingDirty;
193 totalDirty->roundOut();
194 mHead->pendingDirty.setEmpty();
195}
196
197} /* namespace uirenderer */
198} /* namespace android */