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