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