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