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