blob: 8277a9a4afa331d7d72bec426d7de05b162a038d [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,
John Reck25fbb3f2014-06-12 13:46:45 -070038 TransformNone,
John Recka447d292014-06-11 18:39:44 -070039};
40
John Recke4267ea2014-06-03 15:53:15 -070041struct DirtyStack {
John Recka447d292014-06-11 18:39:44 -070042 TransformType type;
43 union {
44 const RenderNode* renderNode;
45 const Matrix4* matrix4;
46 };
John Recke4267ea2014-06-03 15:53:15 -070047 // When this frame is pop'd, this rect is mapped through the above transform
48 // and applied to the previous (aka parent) frame
49 SkRect pendingDirty;
50 DirtyStack* prev;
51 DirtyStack* next;
52};
53
54DamageAccumulator::DamageAccumulator() {
55 mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
56 memset(mHead, 0, sizeof(DirtyStack));
57 // Create a root that we will not pop off
58 mHead->prev = mHead;
59}
60
John Recka447d292014-06-11 18:39:44 -070061void DamageAccumulator::pushCommon() {
John Recke4267ea2014-06-03 15:53:15 -070062 if (!mHead->next) {
63 DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
64 nextFrame->next = 0;
65 nextFrame->prev = mHead;
66 mHead->next = nextFrame;
67 }
68 mHead = mHead->next;
John Recke4267ea2014-06-03 15:53:15 -070069 mHead->pendingDirty.setEmpty();
70}
71
John Recka447d292014-06-11 18:39:44 -070072void DamageAccumulator::pushTransform(const RenderNode* transform) {
73 pushCommon();
74 mHead->type = TransformRenderNode;
75 mHead->renderNode = transform;
76}
77
78void DamageAccumulator::pushTransform(const Matrix4* transform) {
79 pushCommon();
80 mHead->type = TransformMatrix4;
81 mHead->matrix4 = transform;
82}
83
John Reck25fbb3f2014-06-12 13:46:45 -070084void DamageAccumulator::pushNullTransform() {
85 pushCommon();
86 mHead->type = TransformNone;
87}
88
John Recka447d292014-06-11 18:39:44 -070089void DamageAccumulator::popTransform() {
John Recke4267ea2014-06-03 15:53:15 -070090 LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
91 DirtyStack* dirtyFrame = mHead;
92 mHead = mHead->prev;
John Reck25fbb3f2014-06-12 13:46:45 -070093 switch (dirtyFrame->type) {
94 case TransformRenderNode:
John Recka447d292014-06-11 18:39:44 -070095 applyRenderNodeTransform(dirtyFrame);
John Reck25fbb3f2014-06-12 13:46:45 -070096 break;
97 case TransformMatrix4:
John Recka447d292014-06-11 18:39:44 -070098 applyMatrix4Transform(dirtyFrame);
John Reck25fbb3f2014-06-12 13:46:45 -070099 break;
100 case TransformNone:
101 mHead->pendingDirty.join(dirtyFrame->pendingDirty);
102 break;
John Recka447d292014-06-11 18:39:44 -0700103 }
104}
105
106static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
107 if (in.isEmpty()) return;
108 Rect temp(in);
109 matrix->mapRect(temp);
110 out->join(RECT_ARGS(temp));
111}
112
113void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
114 mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
115}
116
117static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
118 if (in.isEmpty()) return;
119 const SkMatrix* transform = props.getTransformMatrix();
120 SkRect temp(in);
121 if (transform && !transform->isIdentity()) {
122 transform->mapRect(&temp);
123 }
124 temp.offset(props.getLeft(), props.getTop());
125 out->join(temp);
126}
127
128static DirtyStack* findParentRenderNode(DirtyStack* frame) {
129 while (frame->prev != frame) {
130 frame = frame->prev;
131 if (frame->type == TransformRenderNode) {
132 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700133 }
John Recka447d292014-06-11 18:39:44 -0700134 }
135 return NULL;
136}
137
138static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
139 if (frame) {
140 while (frame->prev != frame) {
141 frame = frame->prev;
142 if (frame->type == TransformRenderNode
143 && frame->renderNode->hasProjectionReceiver()) {
144 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700145 }
John Recke4267ea2014-06-03 15:53:15 -0700146 }
John Recka447d292014-06-11 18:39:44 -0700147 }
148 return NULL;
149}
150
151static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
152 SkRect* rect = &frame->pendingDirty;
153 while (frame != end) {
154 if (frame->type == TransformRenderNode) {
155 mapRect(frame->renderNode->properties(), *rect, rect);
156 } else {
157 mapRect(frame->matrix4, *rect, rect);
158 }
159 frame = frame->prev;
160 }
161}
162
163void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
164 if (frame->pendingDirty.isEmpty()) {
165 return;
166 }
167
168 const RenderProperties& props = frame->renderNode->properties();
169
170 // Perform clipping
John Reck293e8682014-06-17 10:34:02 -0700171 if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
John Recka447d292014-06-11 18:39:44 -0700172 if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
173 frame->pendingDirty.setEmpty();
174 }
175 }
176
177 // apply all transforms
178 mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
179
180 // project backwards if necessary
181 if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
182 // First, find our parent RenderNode:
183 DirtyStack* parentNode = findParentRenderNode(frame);
184 // Find our parent's projection receiver, which is what we project onto
185 DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
186 if (projectionReceiver) {
187 applyTransforms(frame, projectionReceiver);
188 projectionReceiver->pendingDirty.join(frame->pendingDirty);
189 } else {
190 ALOGW("Failed to find projection receiver? Dropping on the floor...");
191 }
192
193 frame->pendingDirty.setEmpty();
John Recke4267ea2014-06-03 15:53:15 -0700194 }
195}
196
197void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
198 mHead->pendingDirty.join(left, top, right, bottom);
199}
200
John Reck25fbb3f2014-06-12 13:46:45 -0700201void DamageAccumulator::peekAtDirty(SkRect* dest) {
202 *dest = mHead->pendingDirty;
203}
204
John Recke4267ea2014-06-03 15:53:15 -0700205void DamageAccumulator::finish(SkRect* totalDirty) {
206 LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
207 // Root node never has a transform, so this is the fully mapped dirty rect
208 *totalDirty = mHead->pendingDirty;
209 totalDirty->roundOut();
210 mHead->pendingDirty.setEmpty();
211}
212
213} /* namespace uirenderer */
214} /* namespace android */