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