blob: 15bed585cccf6f7ef2792b08bd6d8e2a48c7dedc [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 {
John Reck2dc223d2014-06-17 10:46:09 -070036 TransformInvalid = 0,
John Recka447d292014-06-11 18:39:44 -070037 TransformRenderNode,
38 TransformMatrix4,
John Reck25fbb3f2014-06-12 13:46:45 -070039 TransformNone,
John Recka447d292014-06-11 18:39:44 -070040};
41
John Recke4267ea2014-06-03 15:53:15 -070042struct DirtyStack {
John Recka447d292014-06-11 18:39:44 -070043 TransformType type;
44 union {
45 const RenderNode* renderNode;
46 const Matrix4* matrix4;
47 };
John Recke4267ea2014-06-03 15:53:15 -070048 // When this frame is pop'd, this rect is mapped through the above transform
49 // and applied to the previous (aka parent) frame
50 SkRect pendingDirty;
51 DirtyStack* prev;
52 DirtyStack* next;
53};
54
55DamageAccumulator::DamageAccumulator() {
56 mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
57 memset(mHead, 0, sizeof(DirtyStack));
58 // Create a root that we will not pop off
59 mHead->prev = mHead;
John Reck2dc223d2014-06-17 10:46:09 -070060 mHead->type = TransformNone;
John Recke4267ea2014-06-03 15:53:15 -070061}
62
John Recka447d292014-06-11 18:39:44 -070063void DamageAccumulator::pushCommon() {
John Recke4267ea2014-06-03 15:53:15 -070064 if (!mHead->next) {
65 DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
66 nextFrame->next = 0;
67 nextFrame->prev = mHead;
68 mHead->next = nextFrame;
69 }
70 mHead = mHead->next;
John Recke4267ea2014-06-03 15:53:15 -070071 mHead->pendingDirty.setEmpty();
72}
73
John Recka447d292014-06-11 18:39:44 -070074void DamageAccumulator::pushTransform(const RenderNode* transform) {
75 pushCommon();
76 mHead->type = TransformRenderNode;
77 mHead->renderNode = transform;
78}
79
80void DamageAccumulator::pushTransform(const Matrix4* transform) {
81 pushCommon();
82 mHead->type = TransformMatrix4;
83 mHead->matrix4 = transform;
84}
85
86void DamageAccumulator::popTransform() {
John Recke4267ea2014-06-03 15:53:15 -070087 LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
88 DirtyStack* dirtyFrame = mHead;
89 mHead = mHead->prev;
John Reck25fbb3f2014-06-12 13:46:45 -070090 switch (dirtyFrame->type) {
91 case TransformRenderNode:
John Recka447d292014-06-11 18:39:44 -070092 applyRenderNodeTransform(dirtyFrame);
John Reck25fbb3f2014-06-12 13:46:45 -070093 break;
94 case TransformMatrix4:
John Recka447d292014-06-11 18:39:44 -070095 applyMatrix4Transform(dirtyFrame);
John Reck25fbb3f2014-06-12 13:46:45 -070096 break;
97 case TransformNone:
98 mHead->pendingDirty.join(dirtyFrame->pendingDirty);
99 break;
John Reck2dc223d2014-06-17 10:46:09 -0700100 default:
101 LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
John Recka447d292014-06-11 18:39:44 -0700102 }
103}
104
105static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
106 if (in.isEmpty()) return;
107 Rect temp(in);
108 matrix->mapRect(temp);
109 out->join(RECT_ARGS(temp));
110}
111
112void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
113 mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
114}
115
116static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
117 if (in.isEmpty()) return;
118 const SkMatrix* transform = props.getTransformMatrix();
119 SkRect temp(in);
120 if (transform && !transform->isIdentity()) {
121 transform->mapRect(&temp);
122 }
123 temp.offset(props.getLeft(), props.getTop());
124 out->join(temp);
125}
126
127static DirtyStack* findParentRenderNode(DirtyStack* frame) {
128 while (frame->prev != frame) {
129 frame = frame->prev;
130 if (frame->type == TransformRenderNode) {
131 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700132 }
John Recka447d292014-06-11 18:39:44 -0700133 }
134 return NULL;
135}
136
137static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
138 if (frame) {
139 while (frame->prev != frame) {
140 frame = frame->prev;
141 if (frame->type == TransformRenderNode
142 && frame->renderNode->hasProjectionReceiver()) {
143 return frame;
John Recke4267ea2014-06-03 15:53:15 -0700144 }
John Recke4267ea2014-06-03 15:53:15 -0700145 }
John Recka447d292014-06-11 18:39:44 -0700146 }
147 return NULL;
148}
149
150static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
151 SkRect* rect = &frame->pendingDirty;
152 while (frame != end) {
153 if (frame->type == TransformRenderNode) {
154 mapRect(frame->renderNode->properties(), *rect, rect);
155 } else {
156 mapRect(frame->matrix4, *rect, rect);
157 }
158 frame = frame->prev;
159 }
160}
161
162void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
163 if (frame->pendingDirty.isEmpty()) {
164 return;
165 }
166
167 const RenderProperties& props = frame->renderNode->properties();
John Reckce9f3082014-06-17 16:18:09 -0700168 if (props.getAlpha() <= 0) {
169 return;
170 }
John Recka447d292014-06-11 18:39:44 -0700171
172 // Perform clipping
John Reck293e8682014-06-17 10:34:02 -0700173 if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
John Recka447d292014-06-11 18:39:44 -0700174 if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
175 frame->pendingDirty.setEmpty();
176 }
177 }
178
179 // apply all transforms
180 mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
181
182 // project backwards if necessary
183 if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
184 // First, find our parent RenderNode:
185 DirtyStack* parentNode = findParentRenderNode(frame);
186 // Find our parent's projection receiver, which is what we project onto
187 DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
188 if (projectionReceiver) {
189 applyTransforms(frame, projectionReceiver);
190 projectionReceiver->pendingDirty.join(frame->pendingDirty);
John Recka447d292014-06-11 18:39:44 -0700191 }
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 */