DamageAccumulator.cpp revision 25fbb3fa1138675379102a44405852555cefccbd
1/*
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
29NullDamageAccumulator NullDamageAccumulator::sInstance;
30
31NullDamageAccumulator* NullDamageAccumulator::instance() {
32    return &sInstance;
33}
34
35enum TransformType {
36    TransformRenderNode,
37    TransformMatrix4,
38    TransformNone,
39};
40
41struct DirtyStack {
42    TransformType type;
43    union {
44        const RenderNode* renderNode;
45        const Matrix4* matrix4;
46    };
47    // 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
61void DamageAccumulator::pushCommon() {
62    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;
69    mHead->pendingDirty.setEmpty();
70}
71
72void 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
84void DamageAccumulator::pushNullTransform() {
85    pushCommon();
86    mHead->type = TransformNone;
87}
88
89void DamageAccumulator::popTransform() {
90    LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
91    DirtyStack* dirtyFrame = mHead;
92    mHead = mHead->prev;
93    switch (dirtyFrame->type) {
94    case TransformRenderNode:
95        applyRenderNodeTransform(dirtyFrame);
96        break;
97    case TransformMatrix4:
98        applyMatrix4Transform(dirtyFrame);
99        break;
100    case TransformNone:
101        mHead->pendingDirty.join(dirtyFrame->pendingDirty);
102        break;
103    }
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;
133        }
134    }
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;
145            }
146        }
147    }
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
171    if (props.getClipToBounds() && !frame->pendingDirty.isEmpty()) {
172        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();
194    }
195}
196
197void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
198    mHead->pendingDirty.join(left, top, right, bottom);
199}
200
201void DamageAccumulator::peekAtDirty(SkRect* dest) {
202    *dest = mHead->pendingDirty;
203}
204
205void 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 */
215