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