DamageAccumulator.cpp revision a447d29c65fb811cd184775a3476101a1cede929
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};
39
40struct DirtyStack {
41    TransformType type;
42    union {
43        const RenderNode* renderNode;
44        const Matrix4* matrix4;
45    };
46    // 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
60void DamageAccumulator::pushCommon() {
61    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;
68    mHead->pendingDirty.setEmpty();
69}
70
71void 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() {
84    LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
85    DirtyStack* dirtyFrame = mHead;
86    mHead = mHead->prev;
87    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;
121        }
122    }
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;
133            }
134        }
135    }
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();
182    }
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 */
199