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