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 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 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 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 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 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 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 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 */ 122 int CanvasState::saveSnapshot(int flags) { 123 mSnapshot = allocSnapshot(mSnapshot, flags); 124 return mSaveCount++; 125 } 126 127 int CanvasState::save(int flags) { 128 return saveSnapshot(flags); 129 } 130 131 /** 132 * Guaranteed to restore without side-effects. 133 */ 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 147 void CanvasState::restore() { 148 if (mSaveCount > 1) { 149 restoreSnapshot(); 150 } 151 } 152 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 165 void CanvasState::getMatrix(SkMatrix* matrix) const { 166 mSnapshot->transform->copyTo(*matrix); 167 } 168 169 void CanvasState::translate(float dx, float dy, float dz) { 170 mSnapshot->transform->translate(dx, dy, dz); 171 } 172 173 void CanvasState::rotate(float degrees) { 174 mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); 175 } 176 177 void CanvasState::scale(float sx, float sy) { 178 mSnapshot->transform->scale(sx, sy, 1.0f); 179 } 180 181 void CanvasState::skew(float sx, float sy) { 182 mSnapshot->transform->skew(sx, sy); 183 } 184 185 void CanvasState::setMatrix(const SkMatrix& matrix) { 186 mSnapshot->transform->load(matrix); 187 } 188 189 void CanvasState::setMatrix(const Matrix4& matrix) { 190 *(mSnapshot->transform) = matrix; 191 } 192 193 void CanvasState::concatMatrix(const SkMatrix& matrix) { 194 mat4 transform(matrix); 195 mSnapshot->transform->multiply(transform); 196 } 197 198 void CanvasState::concatMatrix(const Matrix4& matrix) { 199 mSnapshot->transform->multiply(matrix); 200 } 201 202 /////////////////////////////////////////////////////////////////////////////// 203 // Clip 204 /////////////////////////////////////////////////////////////////////////////// 205 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 212 bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) { 213 mSnapshot->clipPath(*path, op); 214 mDirtyClip = true; 215 return !mSnapshot->clipIsEmpty(); 216 } 217 218 bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) { 219 mSnapshot->clipRegionTransformed(*region, op); 220 mDirtyClip = true; 221 return !mSnapshot->clipIsEmpty(); 222 } 223 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 239 void CanvasState::setClippingRoundRect(LinearAllocator& allocator, 240 const Rect& rect, float radius, bool highPriority) { 241 mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority); 242 } 243 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 */ 262 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 292 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