StatefulBaseRenderer.cpp revision 62d307c2402777d5e53b4590af5f32f8c55afd81
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    // region is the transformed input path, masked by the previous clip
182    mDirtyClip |= mSnapshot->clipRegionTransformed(region, op);
183    return !mSnapshot->clipRect->isEmpty();
184}
185
186bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
187    mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op);
188    return !mSnapshot->clipRect->isEmpty();
189}
190
191void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
192    mSnapshot->setClippingOutline(allocator, outline);
193}
194
195///////////////////////////////////////////////////////////////////////////////
196// Quick Rejection
197///////////////////////////////////////////////////////////////////////////////
198
199/**
200 * Calculates whether content drawn within the passed bounds would be outside of, or intersect with
201 * the clipRect. Does not modify the scissor.
202 *
203 * @param clipRequired if not null, will be set to true if element intersects clip
204 *         (and wasn't rejected)
205 *
206 * @param snapOut if set, the geometry will be treated as having an AA ramp.
207 *         See Rect::snapGeometryToPixelBoundaries()
208 */
209bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top,
210        float right, float bottom,
211        bool* clipRequired, bool* roundRectClipRequired,
212        bool snapOut) const {
213    if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
214        return true;
215    }
216
217    Rect r(left, top, right, bottom);
218    currentTransform()->mapRect(r);
219    r.snapGeometryToPixelBoundaries(snapOut);
220
221    Rect clipRect(*currentClipRect());
222    clipRect.snapToPixelBoundaries();
223
224    if (!clipRect.intersects(r)) return true;
225
226    // clip is required if geometry intersects clip rect
227    if (clipRequired) {
228        *clipRequired = !clipRect.contains(r);
229    }
230
231    // round rect clip is required if RR clip exists, and geometry intersects its corners
232    if (roundRectClipRequired) {
233        *roundRectClipRequired = mSnapshot->roundRectClipState != NULL
234                && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
235    }
236    return false;
237}
238
239/**
240 * Returns false if drawing won't be clipped out.
241 *
242 * Makes the decision conservatively, by rounding out the mapped rect before comparing with the
243 * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but
244 * rejection is still desired.
245 *
246 * This function, unlike quickRejectSetupScissor, should be used where precise geometry information
247 * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass
248 * rejection where precise rejection isn't important, or precise information isn't available.
249 */
250bool StatefulBaseRenderer::quickRejectConservative(float left, float top,
251        float right, float bottom) const {
252    if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
253        return true;
254    }
255
256    Rect r(left, top, right, bottom);
257    currentTransform()->mapRect(r);
258    r.roundOut(); // rounded out to be conservative
259
260    Rect clipRect(*currentClipRect());
261    clipRect.snapToPixelBoundaries();
262
263    if (!clipRect.intersects(r)) return true;
264
265    return false;
266}
267
268}; // namespace uirenderer
269}; // namespace android
270