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::CanvasState(CanvasStateClient& renderer) 25 : mWidth(-1), mHeight(-1), mSaveCount(1), mCanvas(renderer), mSnapshot(&mFirstSnapshot) {} 26 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 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 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 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 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 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 */ 114 int CanvasState::saveSnapshot(int flags) { 115 mSnapshot = allocSnapshot(mSnapshot, flags); 116 return mSaveCount++; 117 } 118 119 int CanvasState::save(int flags) { 120 return saveSnapshot(flags); 121 } 122 123 /** 124 * Guaranteed to restore without side-effects. 125 */ 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 139 void CanvasState::restore() { 140 if (mSaveCount > 1) { 141 restoreSnapshot(); 142 } 143 } 144 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 157 void CanvasState::getMatrix(SkMatrix* matrix) const { 158 mSnapshot->transform->copyTo(*matrix); 159 } 160 161 void CanvasState::translate(float dx, float dy, float dz) { 162 mSnapshot->transform->translate(dx, dy, dz); 163 } 164 165 void CanvasState::rotate(float degrees) { 166 mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); 167 } 168 169 void CanvasState::scale(float sx, float sy) { 170 mSnapshot->transform->scale(sx, sy, 1.0f); 171 } 172 173 void CanvasState::skew(float sx, float sy) { 174 mSnapshot->transform->skew(sx, sy); 175 } 176 177 void CanvasState::setMatrix(const SkMatrix& matrix) { 178 mSnapshot->transform->load(matrix); 179 } 180 181 void CanvasState::setMatrix(const Matrix4& matrix) { 182 *(mSnapshot->transform) = matrix; 183 } 184 185 void CanvasState::concatMatrix(const SkMatrix& matrix) { 186 mat4 transform(matrix); 187 mSnapshot->transform->multiply(transform); 188 } 189 190 void CanvasState::concatMatrix(const Matrix4& matrix) { 191 mSnapshot->transform->multiply(matrix); 192 } 193 194 /////////////////////////////////////////////////////////////////////////////// 195 // Clip 196 /////////////////////////////////////////////////////////////////////////////// 197 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 203 bool CanvasState::clipPath(const SkPath* path, SkClipOp op) { 204 mSnapshot->clipPath(*path, op); 205 return !mSnapshot->clipIsEmpty(); 206 } 207 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 */ 237 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 266 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