StatefulBaseRenderer.cpp revision 058fc640017c90120c599d378a4cbc55668b05b7
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 "OpenGLRenderer"
18
19#include <SkCanvas.h>
20
21#include "StatefulBaseRenderer.h"
22
23namespace android {
24namespace uirenderer {
25
26StatefulBaseRenderer::StatefulBaseRenderer()
27        : mDirtyClip(false)
28        , mWidth(-1)
29        , mHeight(-1)
30        , mSaveCount(1)
31        , mFirstSnapshot(new Snapshot)
32        , mSnapshot(mFirstSnapshot) {
33}
34
35void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop,
36        float clipRight, float clipBottom) {
37    mSnapshot = new Snapshot(mFirstSnapshot,
38            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
39    mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
40    mSnapshot->fbo = getTargetFbo();
41    mSaveCount = 1;
42}
43
44void StatefulBaseRenderer::setViewport(int width, int height) {
45    mWidth = width;
46    mHeight = height;
47    mFirstSnapshot->initializeViewport(width, height);
48    onViewportInitialized();
49}
50
51///////////////////////////////////////////////////////////////////////////////
52// Save (layer)
53///////////////////////////////////////////////////////////////////////////////
54
55/**
56 * Non-virtual implementation of save, guaranteed to save without side-effects
57 *
58 * The approach here and in restoreSnapshot(), allows subclasses to directly manipulate the save
59 * stack, and ensures restoreToCount() doesn't call back into subclass overrides.
60 */
61int StatefulBaseRenderer::saveSnapshot(int flags) {
62    mSnapshot = new Snapshot(mSnapshot, flags);
63    return mSaveCount++;
64}
65
66int StatefulBaseRenderer::save(int flags) {
67    return saveSnapshot(flags);
68}
69
70/**
71 * Non-virtual implementation of restore, guaranteed to restore without side-effects.
72 */
73void StatefulBaseRenderer::restoreSnapshot() {
74    sp<Snapshot> toRemove = mSnapshot;
75    sp<Snapshot> toRestore = mSnapshot->previous;
76
77    mSaveCount--;
78    mSnapshot = toRestore;
79
80    // subclass handles restore implementation
81    onSnapshotRestored(*toRemove, *toRestore);
82}
83
84void StatefulBaseRenderer::restore() {
85    if (mSaveCount > 1) {
86        restoreSnapshot();
87    }
88}
89
90void StatefulBaseRenderer::restoreToCount(int saveCount) {
91    if (saveCount < 1) saveCount = 1;
92
93    while (mSaveCount > saveCount) {
94        restoreSnapshot();
95    }
96}
97
98///////////////////////////////////////////////////////////////////////////////
99// Matrix
100///////////////////////////////////////////////////////////////////////////////
101
102void StatefulBaseRenderer::getMatrix(Matrix4* matrix) const {
103    matrix->load(*(mSnapshot->transform));
104}
105
106void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const {
107    mSnapshot->transform->copyTo(*matrix);
108}
109
110void StatefulBaseRenderer::translate(float dx, float dy, float dz) {
111    mSnapshot->transform->translate(dx, dy, dz);
112}
113
114void StatefulBaseRenderer::rotate(float degrees) {
115    mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
116}
117
118void StatefulBaseRenderer::scale(float sx, float sy) {
119    mSnapshot->transform->scale(sx, sy, 1.0f);
120}
121
122void StatefulBaseRenderer::skew(float sx, float sy) {
123    mSnapshot->transform->skew(sx, sy);
124}
125
126void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) {
127    mSnapshot->transform->load(matrix);
128}
129
130void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) {
131    mSnapshot->transform->load(matrix);
132}
133
134void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) {
135    mat4 transform(matrix);
136    mSnapshot->transform->multiply(transform);
137}
138
139void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) {
140    mSnapshot->transform->multiply(matrix);
141}
142
143///////////////////////////////////////////////////////////////////////////////
144// Clip
145///////////////////////////////////////////////////////////////////////////////
146
147bool StatefulBaseRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
148    if (CC_LIKELY(currentTransform()->rectToRect())) {
149        mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op);
150        return !mSnapshot->clipRect->isEmpty();
151    }
152
153    SkPath path;
154    path.addRect(left, top, right, bottom);
155
156    return StatefulBaseRenderer::clipPath(&path, op);
157}
158
159bool StatefulBaseRenderer::clipPath(const SkPath* path, SkRegion::Op op) {
160    SkMatrix transform;
161    currentTransform()->copyTo(transform);
162
163    SkPath transformed;
164    path->transform(transform, &transformed);
165
166    SkRegion clip;
167    if (!mSnapshot->previous->clipRegion->isEmpty()) {
168        clip.setRegion(*mSnapshot->previous->clipRegion);
169    } else {
170        if (mSnapshot->previous == firstSnapshot()) {
171            clip.setRect(0, 0, getWidth(), getHeight());
172        } else {
173            Rect* bounds = mSnapshot->previous->clipRect;
174            clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
175        }
176    }
177
178    SkRegion region;
179    region.setPath(transformed, clip);
180
181    mDirtyClip |= mSnapshot->clipRegionTransformed(region, op);
182    return !mSnapshot->clipRect->isEmpty();
183}
184
185bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
186    mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op);
187    return !mSnapshot->clipRect->isEmpty();
188}
189
190void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
191    mSnapshot->setClippingOutline(allocator, outline);
192}
193
194///////////////////////////////////////////////////////////////////////////////
195// Quick Rejection
196///////////////////////////////////////////////////////////////////////////////
197
198/**
199 * Calculates whether content drawn within the passed bounds would be outside of, or intersect with
200 * the clipRect. Does not modify the scissor.
201 *
202 * @param clipRequired if not null, will be set to true if element intersects clip
203 *         (and wasn't rejected)
204 *
205 * @param snapOut if set, the geometry will be treated as having an AA ramp.
206 *         See Rect::snapGeometryToPixelBoundaries()
207 */
208bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top,
209        float right, float bottom,
210        bool* clipRequired, bool* roundRectClipRequired,
211        bool snapOut) const {
212    if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
213        return true;
214    }
215
216    Rect r(left, top, right, bottom);
217    currentTransform()->mapRect(r);
218    r.snapGeometryToPixelBoundaries(snapOut);
219
220    Rect clipRect(*currentClipRect());
221    clipRect.snapToPixelBoundaries();
222
223    if (!clipRect.intersects(r)) return true;
224
225    // clip is required if geometry intersects clip rect
226    if (clipRequired) {
227        *clipRequired = !clipRect.contains(r);
228    }
229
230    // round rect clip is required if RR clip exists, and geometry intersects its corners
231    if (roundRectClipRequired) {
232        *roundRectClipRequired = mSnapshot->roundRectClipState != NULL
233                && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
234    }
235    return false;
236}
237
238/**
239 * Returns false if drawing won't be clipped out.
240 *
241 * Makes the decision conservatively, by rounding out the mapped rect before comparing with the
242 * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but
243 * rejection is still desired.
244 *
245 * This function, unlike quickRejectSetupScissor, should be used where precise geometry information
246 * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass
247 * rejection where precise rejection isn't important, or precise information isn't available.
248 */
249bool StatefulBaseRenderer::quickRejectConservative(float left, float top,
250        float right, float bottom) const {
251    if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
252        return true;
253    }
254
255    Rect r(left, top, right, bottom);
256    currentTransform()->mapRect(r);
257    r.roundOut(); // rounded out to be conservative
258
259    Rect clipRect(*currentClipRect());
260    clipRect.snapToPixelBoundaries();
261
262    if (!clipRect.intersects(r)) return true;
263
264    return false;
265}
266
267}; // namespace uirenderer
268}; // namespace android
269