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