DamageAccumulator.cpp revision 2dc223d23764a78f052529e86a59a1979d03b811
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    TransformInvalid = 0,
37    TransformRenderNode,
38    TransformMatrix4,
39    TransformNone,
40};
41
42struct DirtyStack {
43    TransformType type;
44    union {
45        const RenderNode* renderNode;
46        const Matrix4* matrix4;
47    };
48    // 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;
60    mHead->type = TransformNone;
61}
62
63void DamageAccumulator::pushCommon() {
64    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;
71    mHead->pendingDirty.setEmpty();
72}
73
74void 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::pushNullTransform() {
87    pushCommon();
88    mHead->type = TransformNone;
89}
90
91void DamageAccumulator::popTransform() {
92    LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
93    DirtyStack* dirtyFrame = mHead;
94    mHead = mHead->prev;
95    switch (dirtyFrame->type) {
96    case TransformRenderNode:
97        applyRenderNodeTransform(dirtyFrame);
98        break;
99    case TransformMatrix4:
100        applyMatrix4Transform(dirtyFrame);
101        break;
102    case TransformNone:
103        mHead->pendingDirty.join(dirtyFrame->pendingDirty);
104        break;
105    default:
106        LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
107    }
108}
109
110static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
111    if (in.isEmpty()) return;
112    Rect temp(in);
113    matrix->mapRect(temp);
114    out->join(RECT_ARGS(temp));
115}
116
117void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
118    mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
119}
120
121static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
122    if (in.isEmpty()) return;
123    const SkMatrix* transform = props.getTransformMatrix();
124    SkRect temp(in);
125    if (transform && !transform->isIdentity()) {
126        transform->mapRect(&temp);
127    }
128    temp.offset(props.getLeft(), props.getTop());
129    out->join(temp);
130}
131
132static DirtyStack* findParentRenderNode(DirtyStack* frame) {
133    while (frame->prev != frame) {
134        frame = frame->prev;
135        if (frame->type == TransformRenderNode) {
136            return frame;
137        }
138    }
139    return NULL;
140}
141
142static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
143    if (frame) {
144        while (frame->prev != frame) {
145            frame = frame->prev;
146            if (frame->type == TransformRenderNode
147                    && frame->renderNode->hasProjectionReceiver()) {
148                return frame;
149            }
150        }
151    }
152    return NULL;
153}
154
155static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
156    SkRect* rect = &frame->pendingDirty;
157    while (frame != end) {
158        if (frame->type == TransformRenderNode) {
159            mapRect(frame->renderNode->properties(), *rect, rect);
160        } else {
161            mapRect(frame->matrix4, *rect, rect);
162        }
163        frame = frame->prev;
164    }
165}
166
167void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
168    if (frame->pendingDirty.isEmpty()) {
169        return;
170    }
171
172    const RenderProperties& props = frame->renderNode->properties();
173
174    // Perform clipping
175    if (props.getClipToBounds() && !frame->pendingDirty.isEmpty()) {
176        if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
177            frame->pendingDirty.setEmpty();
178        }
179    }
180
181    // apply all transforms
182    mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
183
184    // project backwards if necessary
185    if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
186        // First, find our parent RenderNode:
187        DirtyStack* parentNode = findParentRenderNode(frame);
188        // Find our parent's projection receiver, which is what we project onto
189        DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
190        if (projectionReceiver) {
191            applyTransforms(frame, projectionReceiver);
192            projectionReceiver->pendingDirty.join(frame->pendingDirty);
193        }
194
195        frame->pendingDirty.setEmpty();
196    }
197}
198
199void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
200    mHead->pendingDirty.join(left, top, right, bottom);
201}
202
203void DamageAccumulator::peekAtDirty(SkRect* dest) {
204    *dest = mHead->pendingDirty;
205}
206
207void DamageAccumulator::finish(SkRect* totalDirty) {
208    LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
209    // Root node never has a transform, so this is the fully mapped dirty rect
210    *totalDirty = mHead->pendingDirty;
211    totalDirty->roundOut();
212    mHead->pendingDirty.setEmpty();
213}
214
215} /* namespace uirenderer */
216} /* namespace android */
217