DamageAccumulator.cpp revision c71bfcaa182e3d4fd9874362d3b4781fda934a21
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#include "DamageAccumulator.h"
18
19#include <cutils/log.h>
20
21#include "RenderNode.h"
22#include "utils/MathUtils.h"
23
24namespace android {
25namespace uirenderer {
26
27enum TransformType {
28    TransformInvalid = 0,
29    TransformRenderNode,
30    TransformMatrix4,
31    TransformNone,
32};
33
34struct DirtyStack {
35    TransformType type;
36    union {
37        const RenderNode* renderNode;
38        const Matrix4* matrix4;
39    };
40    // When this frame is pop'd, this rect is mapped through the above transform
41    // and applied to the previous (aka parent) frame
42    SkRect pendingDirty;
43    DirtyStack* prev;
44    DirtyStack* next;
45};
46
47DamageAccumulator::DamageAccumulator() {
48    mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
49    memset(mHead, 0, sizeof(DirtyStack));
50    // Create a root that we will not pop off
51    mHead->prev = mHead;
52    mHead->type = TransformNone;
53}
54
55static void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMatrix) {
56    if (currentFrame->prev != currentFrame) {
57        computeTransformImpl(currentFrame->prev, outMatrix);
58    }
59    switch (currentFrame->type) {
60    case TransformRenderNode:
61        currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
62        break;
63    case TransformMatrix4:
64        outMatrix->multiply(*currentFrame->matrix4);
65        break;
66    case TransformNone:
67        // nothing to be done
68        break;
69    default:
70        LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", currentFrame->type);
71    }
72}
73
74void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
75    outMatrix->loadIdentity();
76    computeTransformImpl(mHead, outMatrix);
77}
78
79void DamageAccumulator::pushCommon() {
80    if (!mHead->next) {
81        DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
82        nextFrame->next = 0;
83        nextFrame->prev = mHead;
84        mHead->next = nextFrame;
85    }
86    mHead = mHead->next;
87    mHead->pendingDirty.setEmpty();
88}
89
90void DamageAccumulator::pushTransform(const RenderNode* transform) {
91    pushCommon();
92    mHead->type = TransformRenderNode;
93    mHead->renderNode = transform;
94}
95
96void DamageAccumulator::pushTransform(const Matrix4* transform) {
97    pushCommon();
98    mHead->type = TransformMatrix4;
99    mHead->matrix4 = transform;
100}
101
102void DamageAccumulator::popTransform() {
103    LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
104    DirtyStack* dirtyFrame = mHead;
105    mHead = mHead->prev;
106    switch (dirtyFrame->type) {
107    case TransformRenderNode:
108        applyRenderNodeTransform(dirtyFrame);
109        break;
110    case TransformMatrix4:
111        applyMatrix4Transform(dirtyFrame);
112        break;
113    case TransformNone:
114        mHead->pendingDirty.join(dirtyFrame->pendingDirty);
115        break;
116    default:
117        LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
118    }
119}
120
121static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
122    if (in.isEmpty()) return;
123    Rect temp(in);
124    matrix->mapRect(temp);
125    out->join(RECT_ARGS(temp));
126}
127
128void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
129    mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
130}
131
132static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
133    if (in.isEmpty()) return;
134    const SkMatrix* transform = props.getTransformMatrix();
135    SkRect temp(in);
136    if (transform && !transform->isIdentity()) {
137        transform->mapRect(&temp);
138    }
139    temp.offset(props.getLeft(), props.getTop());
140    out->join(temp);
141}
142
143static DirtyStack* findParentRenderNode(DirtyStack* frame) {
144    while (frame->prev != frame) {
145        frame = frame->prev;
146        if (frame->type == TransformRenderNode) {
147            return frame;
148        }
149    }
150    return NULL;
151}
152
153static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
154    if (frame) {
155        while (frame->prev != frame) {
156            frame = frame->prev;
157            if (frame->type == TransformRenderNode
158                    && frame->renderNode->hasProjectionReceiver()) {
159                return frame;
160            }
161        }
162    }
163    return NULL;
164}
165
166static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
167    SkRect* rect = &frame->pendingDirty;
168    while (frame != end) {
169        if (frame->type == TransformRenderNode) {
170            mapRect(frame->renderNode->properties(), *rect, rect);
171        } else {
172            mapRect(frame->matrix4, *rect, rect);
173        }
174        frame = frame->prev;
175    }
176}
177
178void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
179    if (frame->pendingDirty.isEmpty()) {
180        return;
181    }
182
183    const RenderProperties& props = frame->renderNode->properties();
184    if (props.getAlpha() <= 0) {
185        return;
186    }
187
188    // Perform clipping
189    if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
190        if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
191            frame->pendingDirty.setEmpty();
192        }
193    }
194
195    // apply all transforms
196    mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
197
198    // project backwards if necessary
199    if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
200        // First, find our parent RenderNode:
201        DirtyStack* parentNode = findParentRenderNode(frame);
202        // Find our parent's projection receiver, which is what we project onto
203        DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
204        if (projectionReceiver) {
205            applyTransforms(frame, projectionReceiver);
206            projectionReceiver->pendingDirty.join(frame->pendingDirty);
207        }
208
209        frame->pendingDirty.setEmpty();
210    }
211}
212
213void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
214    mHead->pendingDirty.join(left, top, right, bottom);
215}
216
217void DamageAccumulator::peekAtDirty(SkRect* dest) const {
218    *dest = mHead->pendingDirty;
219}
220
221void DamageAccumulator::finish(SkRect* totalDirty) {
222    LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
223    // Root node never has a transform, so this is the fully mapped dirty rect
224    *totalDirty = mHead->pendingDirty;
225    totalDirty->roundOut();
226    mHead->pendingDirty.setEmpty();
227}
228
229} /* namespace uirenderer */
230} /* namespace android */
231