Snapshot.cpp revision 69e5adffb19135d51bde8e458f4907d7265f3e23
1/*
2 * Copyright (C) 2012 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 "OpenGLRenderer"
18
19#include "Snapshot.h"
20
21#include <SkCanvas.h>
22
23namespace android {
24namespace uirenderer {
25
26///////////////////////////////////////////////////////////////////////////////
27// Constructors
28///////////////////////////////////////////////////////////////////////////////
29
30Snapshot::Snapshot()
31        : flags(0)
32        , previous(NULL)
33        , layer(NULL)
34        , fbo(0)
35        , invisible(false)
36        , empty(false)
37        , alpha(1.0f)
38        , roundRectClipState(NULL) {
39    transform = &mTransformRoot;
40    clipRect = &mClipRectRoot;
41    region = NULL;
42    clipRegion = &mClipRegionRoot;
43}
44
45/**
46 * Copies the specified snapshot/ The specified snapshot is stored as
47 * the previous snapshot.
48 */
49Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
50        : flags(0)
51        , previous(s)
52        , layer(s->layer)
53        , fbo(s->fbo)
54        , invisible(s->invisible)
55        , empty(false)
56        , alpha(s->alpha)
57        , roundRectClipState(s->roundRectClipState)
58        , mViewportData(s->mViewportData)
59        , mRelativeLightCenter(s->mRelativeLightCenter) {
60    if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
61        mTransformRoot.load(*s->transform);
62        transform = &mTransformRoot;
63    } else {
64        transform = s->transform;
65    }
66
67    if (saveFlags & SkCanvas::kClip_SaveFlag) {
68        mClipRectRoot.set(*s->clipRect);
69        clipRect = &mClipRectRoot;
70        if (!s->clipRegion->isEmpty()) {
71            mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op);
72        }
73        clipRegion = &mClipRegionRoot;
74    } else {
75        clipRect = s->clipRect;
76        clipRegion = s->clipRegion;
77    }
78
79    if (s->flags & Snapshot::kFlagFboTarget) {
80        flags |= Snapshot::kFlagFboTarget;
81        region = s->region;
82    } else {
83        region = NULL;
84    }
85}
86
87///////////////////////////////////////////////////////////////////////////////
88// Clipping
89///////////////////////////////////////////////////////////////////////////////
90
91void Snapshot::ensureClipRegion() {
92    if (clipRegion->isEmpty()) {
93        clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
94    }
95}
96
97void Snapshot::copyClipRectFromRegion() {
98    if (!clipRegion->isEmpty()) {
99        const SkIRect& bounds = clipRegion->getBounds();
100        clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
101
102        if (clipRegion->isRect()) {
103            clipRegion->setEmpty();
104        }
105    } else {
106        clipRect->setEmpty();
107    }
108}
109
110bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) {
111    SkIRect tmp;
112    tmp.set(left, top, right, bottom);
113    clipRegion->op(tmp, op);
114    copyClipRectFromRegion();
115    return true;
116}
117
118bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
119    ensureClipRegion();
120    clipRegion->op(region, op);
121    copyClipRectFromRegion();
122    flags |= Snapshot::kFlagClipSet;
123    return true;
124}
125
126bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
127    Rect r(left, top, right, bottom);
128    transform->mapRect(r);
129    return clipTransformed(r, op);
130}
131
132bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
133    bool clipped = false;
134
135    switch (op) {
136        case SkRegion::kIntersect_Op: {
137            if (CC_UNLIKELY(!clipRegion->isEmpty())) {
138                ensureClipRegion();
139                clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
140            } else {
141                clipped = clipRect->intersect(r);
142                if (!clipped) {
143                    clipRect->setEmpty();
144                    clipped = true;
145                }
146            }
147            break;
148        }
149        case SkRegion::kReplace_Op: {
150            setClip(r.left, r.top, r.right, r.bottom);
151            clipped = true;
152            break;
153        }
154        default: {
155            ensureClipRegion();
156            clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, op);
157            break;
158        }
159    }
160
161    if (clipped) {
162        flags |= Snapshot::kFlagClipSet;
163    }
164
165    return clipped;
166}
167
168void Snapshot::setClip(float left, float top, float right, float bottom) {
169    clipRect->set(left, top, right, bottom);
170    if (!clipRegion->isEmpty()) {
171        clipRegion->setEmpty();
172    }
173    flags |= Snapshot::kFlagClipSet;
174}
175
176bool Snapshot::hasPerspectiveTransform() const {
177    return transform->isPerspective();
178}
179
180const Rect& Snapshot::getLocalClip() {
181    mat4 inverse;
182    inverse.loadInverse(*transform);
183
184    mLocalClip.set(*clipRect);
185    inverse.mapRect(mLocalClip);
186
187    return mLocalClip;
188}
189
190void Snapshot::resetClip(float left, float top, float right, float bottom) {
191    // TODO: This is incorrect, when we start rendering into a new layer,
192    // we may have to modify the previous snapshot's clip rect and clip
193    // region if the previous restore() call did not restore the clip
194    clipRect = &mClipRectRoot;
195    clipRegion = &mClipRegionRoot;
196    setClip(left, top, right, bottom);
197}
198
199///////////////////////////////////////////////////////////////////////////////
200// Transforms
201///////////////////////////////////////////////////////////////////////////////
202
203void Snapshot::resetTransform(float x, float y, float z) {
204    // before resetting, map current light pos with inverse of current transform
205    Vector3 center = mRelativeLightCenter;
206    mat4 inverse;
207    inverse.loadInverse(*transform);
208    inverse.mapPoint3d(center);
209    mRelativeLightCenter = center;
210
211    transform = &mTransformRoot;
212    transform->loadTranslate(x, y, z);
213}
214
215///////////////////////////////////////////////////////////////////////////////
216// Clipping round rect
217///////////////////////////////////////////////////////////////////////////////
218
219void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius) {
220    if (bounds.isEmpty()) {
221        clipRect->setEmpty();
222        return;
223    }
224
225    RoundRectClipState* state = new (allocator) RoundRectClipState;
226
227    // store the inverse drawing matrix
228    Matrix4 roundRectDrawingMatrix;
229    roundRectDrawingMatrix.load(getOrthoMatrix());
230    roundRectDrawingMatrix.multiply(*transform);
231    state->matrix.loadInverse(roundRectDrawingMatrix);
232
233    // compute area under rounded corners - only draws overlapping these rects need to be clipped
234    for (int i = 0 ; i < 4; i++) {
235        state->dangerRects[i] = bounds;
236    }
237    state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius;
238    state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius;
239    state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius;
240    state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius;
241    for (int i = 0; i < 4; i++) {
242        transform->mapRect(state->dangerRects[i]);
243
244        // round danger rects out as though they are AA geometry (since they essentially are)
245        state->dangerRects[i].snapGeometryToPixelBoundaries(true);
246    }
247
248    // store RR area
249    state->innerRect = bounds;
250    state->innerRect.inset(radius);
251    state->radius = radius;
252
253    // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info
254    roundRectClipState = state;
255}
256
257///////////////////////////////////////////////////////////////////////////////
258// Queries
259///////////////////////////////////////////////////////////////////////////////
260
261bool Snapshot::isIgnored() const {
262    return invisible || empty;
263}
264
265void Snapshot::dump() const {
266    ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
267            this, flags, previous.get(), getViewportHeight(), isIgnored(), clipRegion && !clipRegion->isEmpty());
268    ALOGD("  ClipRect (at %p) %.1f %.1f %.1f %.1f",
269            clipRect, clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
270    ALOGD("  Transform (at %p):", transform);
271    transform->dump();
272}
273
274}; // namespace uirenderer
275}; // namespace android
276