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