CanvasState.cpp revision 5e00c7ce063116c11315639f0035aca8ad73e8cc
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 "CanvasState.h"
18#include "hwui/Canvas.h"
19#include "utils/MathUtils.h"
20
21namespace android {
22namespace uirenderer {
23
24
25CanvasState::CanvasState(CanvasStateClient& renderer)
26        : mWidth(-1)
27        , mHeight(-1)
28        , mSaveCount(1)
29        , mCanvas(renderer)
30        , mSnapshot(&mFirstSnapshot) {
31}
32
33CanvasState::~CanvasState() {
34    // First call freeSnapshot on all but mFirstSnapshot
35    // to invoke all the dtors
36    freeAllSnapshots();
37
38    // Now actually release the memory
39    while (mSnapshotPool) {
40        void* temp = mSnapshotPool;
41        mSnapshotPool = mSnapshotPool->previous;
42        free(temp);
43    }
44}
45
46void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) {
47    if (mWidth != viewportWidth || mHeight != viewportHeight) {
48        mWidth = viewportWidth;
49        mHeight = viewportHeight;
50        mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
51        mCanvas.onViewportInitialized();
52    }
53
54    freeAllSnapshots();
55    mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
56    mSnapshot->setRelativeLightCenter(Vector3());
57    mSaveCount = 1;
58}
59
60void CanvasState::initializeSaveStack(
61        int viewportWidth, int viewportHeight,
62        float clipLeft, float clipTop,
63        float clipRight, float clipBottom, const Vector3& lightCenter) {
64    if (mWidth != viewportWidth || mHeight != viewportHeight) {
65        mWidth = viewportWidth;
66        mHeight = viewportHeight;
67        mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
68        mCanvas.onViewportInitialized();
69    }
70
71    freeAllSnapshots();
72    mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
73    mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
74    mSnapshot->fbo = mCanvas.getTargetFbo();
75    mSnapshot->setRelativeLightCenter(lightCenter);
76    mSaveCount = 1;
77}
78
79Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) {
80    void* memory;
81    if (mSnapshotPool) {
82        memory = mSnapshotPool;
83        mSnapshotPool = mSnapshotPool->previous;
84        mSnapshotPoolCount--;
85    } else {
86        memory = malloc(sizeof(Snapshot));
87    }
88    return new (memory) Snapshot(previous, savecount);
89}
90
91void CanvasState::freeSnapshot(Snapshot* snapshot) {
92    snapshot->~Snapshot();
93    // Arbitrary number, just don't let this grown unbounded
94    if (mSnapshotPoolCount > 10) {
95        free((void*) snapshot);
96    } else {
97        snapshot->previous = mSnapshotPool;
98        mSnapshotPool = snapshot;
99        mSnapshotPoolCount++;
100    }
101}
102
103void CanvasState::freeAllSnapshots() {
104    while (mSnapshot != &mFirstSnapshot) {
105        Snapshot* temp = mSnapshot;
106        mSnapshot = mSnapshot->previous;
107        freeSnapshot(temp);
108    }
109}
110
111///////////////////////////////////////////////////////////////////////////////
112// Save (layer)
113///////////////////////////////////////////////////////////////////////////////
114
115/**
116 * Guaranteed to save without side-effects
117 *
118 * This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save
119 * stack, and ensures restoreToCount() doesn't call back into subclass overrides.
120 */
121int CanvasState::saveSnapshot(int flags) {
122    mSnapshot = allocSnapshot(mSnapshot, flags);
123    return mSaveCount++;
124}
125
126int CanvasState::save(int flags) {
127    return saveSnapshot(flags);
128}
129
130/**
131 * Guaranteed to restore without side-effects.
132 */
133void CanvasState::restoreSnapshot() {
134    Snapshot* toRemove = mSnapshot;
135    Snapshot* toRestore = mSnapshot->previous;
136
137    mSaveCount--;
138    mSnapshot = toRestore;
139
140    // subclass handles restore implementation
141    mCanvas.onSnapshotRestored(*toRemove, *toRestore);
142
143    freeSnapshot(toRemove);
144}
145
146void CanvasState::restore() {
147    if (mSaveCount > 1) {
148        restoreSnapshot();
149    }
150}
151
152void CanvasState::restoreToCount(int saveCount) {
153    if (saveCount < 1) saveCount = 1;
154
155    while (mSaveCount > saveCount) {
156        restoreSnapshot();
157    }
158}
159
160///////////////////////////////////////////////////////////////////////////////
161// Matrix
162///////////////////////////////////////////////////////////////////////////////
163
164void CanvasState::getMatrix(SkMatrix* matrix) const {
165    mSnapshot->transform->copyTo(*matrix);
166}
167
168void CanvasState::translate(float dx, float dy, float dz) {
169    mSnapshot->transform->translate(dx, dy, dz);
170}
171
172void CanvasState::rotate(float degrees) {
173    mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
174}
175
176void CanvasState::scale(float sx, float sy) {
177    mSnapshot->transform->scale(sx, sy, 1.0f);
178}
179
180void CanvasState::skew(float sx, float sy) {
181    mSnapshot->transform->skew(sx, sy);
182}
183
184void CanvasState::setMatrix(const SkMatrix& matrix) {
185    mSnapshot->transform->load(matrix);
186}
187
188void CanvasState::setMatrix(const Matrix4& matrix) {
189    *(mSnapshot->transform) = matrix;
190}
191
192void CanvasState::concatMatrix(const SkMatrix& matrix) {
193    mat4 transform(matrix);
194    mSnapshot->transform->multiply(transform);
195}
196
197void CanvasState::concatMatrix(const Matrix4& matrix) {
198    mSnapshot->transform->multiply(matrix);
199}
200
201///////////////////////////////////////////////////////////////////////////////
202// Clip
203///////////////////////////////////////////////////////////////////////////////
204
205bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
206    mSnapshot->clip(Rect(left, top, right, bottom), op);
207    return !mSnapshot->clipIsEmpty();
208}
209
210bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) {
211    mSnapshot->clipPath(*path, op);
212    return !mSnapshot->clipIsEmpty();
213}
214
215bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) {
216    mSnapshot->clipRegionTransformed(*region, op);
217    return !mSnapshot->clipIsEmpty();
218}
219
220void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
221    Rect bounds;
222    float radius;
223    if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported
224
225    bool outlineIsRounded = MathUtils::isPositive(radius);
226    if (!outlineIsRounded || currentTransform()->isSimple()) {
227        // TODO: consider storing this rect separately, so that this can't be replaced with clip ops
228        clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkRegion::kIntersect_Op);
229    }
230    if (outlineIsRounded) {
231        setClippingRoundRect(allocator, bounds, radius, false);
232    }
233}
234
235///////////////////////////////////////////////////////////////////////////////
236// Quick Rejection
237///////////////////////////////////////////////////////////////////////////////
238
239/**
240 * Calculates whether content drawn within the passed bounds would be outside of, or intersect with
241 * the clipRect. Does not modify the scissor.
242 *
243 * @param clipRequired if not null, will be set to true if element intersects clip
244 *         (and wasn't rejected)
245 *
246 * @param snapOut if set, the geometry will be treated as having an AA ramp.
247 *         See Rect::snapGeometryToPixelBoundaries()
248 */
249bool CanvasState::calculateQuickRejectForScissor(float left, float top,
250        float right, float bottom,
251        bool* clipRequired, bool* roundRectClipRequired,
252        bool snapOut) const {
253    if (bottom <= top || right <= left) {
254        return true;
255    }
256
257    Rect r(left, top, right, bottom);
258    currentTransform()->mapRect(r);
259    r.snapGeometryToPixelBoundaries(snapOut);
260
261    Rect clipRect(currentRenderTargetClip());
262    clipRect.snapToPixelBoundaries();
263
264    if (!clipRect.intersects(r)) return true;
265
266    // clip is required if geometry intersects clip rect
267    if (clipRequired) {
268        *clipRequired = !clipRect.contains(r);
269    }
270
271    // round rect clip is required if RR clip exists, and geometry intersects its corners
272    if (roundRectClipRequired) {
273        *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr
274                && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
275    }
276    return false;
277}
278
279bool CanvasState::quickRejectConservative(float left, float top,
280        float right, float bottom) const {
281    if (bottom <= top || right <= left) {
282        return true;
283    }
284
285    Rect r(left, top, right, bottom);
286    currentTransform()->mapRect(r);
287    r.roundOut(); // rounded out to be conservative
288
289    Rect clipRect(currentRenderTargetClip());
290    clipRect.snapToPixelBoundaries();
291
292    if (!clipRect.intersects(r)) return true;
293
294    return false;
295}
296
297} // namespace uirenderer
298} // namespace android
299