• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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