CanvasState.cpp revision 5e00c7ce063116c11315639f0035aca8ad73e8cc
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 21namespace android { 22namespace uirenderer { 23 24 25CanvasState::CanvasState(CanvasStateClient& renderer) 26 : mWidth(-1) 27 , mHeight(-1) 28 , mSaveCount(1) 29 , mCanvas(renderer) 30 , mSnapshot(&mFirstSnapshot) { 31} 32 33CanvasState::~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 46void 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 60void 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 79Snapshot* 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 91void 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 103void 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 */ 121int CanvasState::saveSnapshot(int flags) { 122 mSnapshot = allocSnapshot(mSnapshot, flags); 123 return mSaveCount++; 124} 125 126int CanvasState::save(int flags) { 127 return saveSnapshot(flags); 128} 129 130/** 131 * Guaranteed to restore without side-effects. 132 */ 133void 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 146void CanvasState::restore() { 147 if (mSaveCount > 1) { 148 restoreSnapshot(); 149 } 150} 151 152void 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 164void CanvasState::getMatrix(SkMatrix* matrix) const { 165 mSnapshot->transform->copyTo(*matrix); 166} 167 168void CanvasState::translate(float dx, float dy, float dz) { 169 mSnapshot->transform->translate(dx, dy, dz); 170} 171 172void CanvasState::rotate(float degrees) { 173 mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); 174} 175 176void CanvasState::scale(float sx, float sy) { 177 mSnapshot->transform->scale(sx, sy, 1.0f); 178} 179 180void CanvasState::skew(float sx, float sy) { 181 mSnapshot->transform->skew(sx, sy); 182} 183 184void CanvasState::setMatrix(const SkMatrix& matrix) { 185 mSnapshot->transform->load(matrix); 186} 187 188void CanvasState::setMatrix(const Matrix4& matrix) { 189 *(mSnapshot->transform) = matrix; 190} 191 192void CanvasState::concatMatrix(const SkMatrix& matrix) { 193 mat4 transform(matrix); 194 mSnapshot->transform->multiply(transform); 195} 196 197void CanvasState::concatMatrix(const Matrix4& matrix) { 198 mSnapshot->transform->multiply(matrix); 199} 200 201/////////////////////////////////////////////////////////////////////////////// 202// Clip 203/////////////////////////////////////////////////////////////////////////////// 204 205bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { 206 mSnapshot->clip(Rect(left, top, right, bottom), op); 207 return !mSnapshot->clipIsEmpty(); 208} 209 210bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) { 211 mSnapshot->clipPath(*path, op); 212 return !mSnapshot->clipIsEmpty(); 213} 214 215bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) { 216 mSnapshot->clipRegionTransformed(*region, op); 217 return !mSnapshot->clipIsEmpty(); 218} 219 220void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { 221 Rect bounds; 222 float radius; 223 if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported 224 225 bool outlineIsRounded = MathUtils::isPositive(radius); 226 if (!outlineIsRounded || currentTransform()->isSimple()) { 227 // TODO: consider storing this rect separately, so that this can't be replaced with clip ops 228 clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkRegion::kIntersect_Op); 229 } 230 if (outlineIsRounded) { 231 setClippingRoundRect(allocator, bounds, radius, false); 232 } 233} 234 235/////////////////////////////////////////////////////////////////////////////// 236// Quick Rejection 237/////////////////////////////////////////////////////////////////////////////// 238 239/** 240 * Calculates whether content drawn within the passed bounds would be outside of, or intersect with 241 * the clipRect. Does not modify the scissor. 242 * 243 * @param clipRequired if not null, will be set to true if element intersects clip 244 * (and wasn't rejected) 245 * 246 * @param snapOut if set, the geometry will be treated as having an AA ramp. 247 * See Rect::snapGeometryToPixelBoundaries() 248 */ 249bool CanvasState::calculateQuickRejectForScissor(float left, float top, 250 float right, float bottom, 251 bool* clipRequired, bool* roundRectClipRequired, 252 bool snapOut) const { 253 if (bottom <= top || right <= left) { 254 return true; 255 } 256 257 Rect r(left, top, right, bottom); 258 currentTransform()->mapRect(r); 259 r.snapGeometryToPixelBoundaries(snapOut); 260 261 Rect clipRect(currentRenderTargetClip()); 262 clipRect.snapToPixelBoundaries(); 263 264 if (!clipRect.intersects(r)) return true; 265 266 // clip is required if geometry intersects clip rect 267 if (clipRequired) { 268 *clipRequired = !clipRect.contains(r); 269 } 270 271 // round rect clip is required if RR clip exists, and geometry intersects its corners 272 if (roundRectClipRequired) { 273 *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr 274 && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); 275 } 276 return false; 277} 278 279bool CanvasState::quickRejectConservative(float left, float top, 280 float right, float bottom) const { 281 if (bottom <= top || right <= left) { 282 return true; 283 } 284 285 Rect r(left, top, right, bottom); 286 currentTransform()->mapRect(r); 287 r.roundOut(); // rounded out to be conservative 288 289 Rect clipRect(currentRenderTargetClip()); 290 clipRect.snapToPixelBoundaries(); 291 292 if (!clipRect.intersects(r)) return true; 293 294 return false; 295} 296 297} // namespace uirenderer 298} // namespace android 299