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