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