DamageAccumulator.cpp revision a7c2ea20c43ab797bef5801530687e22e83def8f
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::popTransform() {
87    LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
88    DirtyStack* dirtyFrame = mHead;
89    mHead = mHead->prev;
90    switch (dirtyFrame->type) {
91    case TransformRenderNode:
92        applyRenderNodeTransform(dirtyFrame);
93        break;
94    case TransformMatrix4:
95        applyMatrix4Transform(dirtyFrame);
96        break;
97    case TransformNone:
98        mHead->pendingDirty.join(dirtyFrame->pendingDirty);
99        break;
100    default:
101        LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
102    }
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;
132        }
133    }
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;
144            }
145        }
146    }
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();
168    if (props.getAlpha() <= 0) {
169        return;
170    }
171
172    // Perform clipping
173    if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
174        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);
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