You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
8.7 KiB
293 lines
8.7 KiB
/* |
|
* Copyright (C) 2014 The Android Open Source Project |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
#include "CanvasState.h" |
|
#include "hwui/Canvas.h" |
|
#include "utils/MathUtils.h" |
|
|
|
namespace android { |
|
namespace uirenderer { |
|
|
|
|
|
CanvasState::CanvasState(CanvasStateClient& renderer) |
|
: mWidth(-1) |
|
, mHeight(-1) |
|
, mSaveCount(1) |
|
, mCanvas(renderer) |
|
, mSnapshot(&mFirstSnapshot) { |
|
} |
|
|
|
CanvasState::~CanvasState() { |
|
// First call freeSnapshot on all but mFirstSnapshot |
|
// to invoke all the dtors |
|
freeAllSnapshots(); |
|
|
|
// Now actually release the memory |
|
while (mSnapshotPool) { |
|
void* temp = mSnapshotPool; |
|
mSnapshotPool = mSnapshotPool->previous; |
|
free(temp); |
|
} |
|
} |
|
|
|
void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) { |
|
if (mWidth != viewportWidth || mHeight != viewportHeight) { |
|
mWidth = viewportWidth; |
|
mHeight = viewportHeight; |
|
mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight); |
|
mCanvas.onViewportInitialized(); |
|
} |
|
|
|
freeAllSnapshots(); |
|
mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip); |
|
mSnapshot->setRelativeLightCenter(Vector3()); |
|
mSaveCount = 1; |
|
} |
|
|
|
void CanvasState::initializeSaveStack( |
|
int viewportWidth, int viewportHeight, |
|
float clipLeft, float clipTop, |
|
float clipRight, float clipBottom, const Vector3& lightCenter) { |
|
if (mWidth != viewportWidth || mHeight != viewportHeight) { |
|
mWidth = viewportWidth; |
|
mHeight = viewportHeight; |
|
mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight); |
|
mCanvas.onViewportInitialized(); |
|
} |
|
|
|
freeAllSnapshots(); |
|
mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip); |
|
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); |
|
mSnapshot->fbo = mCanvas.getTargetFbo(); |
|
mSnapshot->setRelativeLightCenter(lightCenter); |
|
mSaveCount = 1; |
|
} |
|
|
|
Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) { |
|
void* memory; |
|
if (mSnapshotPool) { |
|
memory = mSnapshotPool; |
|
mSnapshotPool = mSnapshotPool->previous; |
|
mSnapshotPoolCount--; |
|
} else { |
|
memory = malloc(sizeof(Snapshot)); |
|
} |
|
return new (memory) Snapshot(previous, savecount); |
|
} |
|
|
|
void CanvasState::freeSnapshot(Snapshot* snapshot) { |
|
snapshot->~Snapshot(); |
|
// Arbitrary number, just don't let this grown unbounded |
|
if (mSnapshotPoolCount > 10) { |
|
free((void*) snapshot); |
|
} else { |
|
snapshot->previous = mSnapshotPool; |
|
mSnapshotPool = snapshot; |
|
mSnapshotPoolCount++; |
|
} |
|
} |
|
|
|
void CanvasState::freeAllSnapshots() { |
|
while (mSnapshot != &mFirstSnapshot) { |
|
Snapshot* temp = mSnapshot; |
|
mSnapshot = mSnapshot->previous; |
|
freeSnapshot(temp); |
|
} |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// Save (layer) |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
/** |
|
* Guaranteed to save without side-effects |
|
* |
|
* This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save |
|
* stack, and ensures restoreToCount() doesn't call back into subclass overrides. |
|
*/ |
|
int CanvasState::saveSnapshot(int flags) { |
|
mSnapshot = allocSnapshot(mSnapshot, flags); |
|
return mSaveCount++; |
|
} |
|
|
|
int CanvasState::save(int flags) { |
|
return saveSnapshot(flags); |
|
} |
|
|
|
/** |
|
* Guaranteed to restore without side-effects. |
|
*/ |
|
void CanvasState::restoreSnapshot() { |
|
Snapshot* toRemove = mSnapshot; |
|
Snapshot* toRestore = mSnapshot->previous; |
|
|
|
mSaveCount--; |
|
mSnapshot = toRestore; |
|
|
|
// subclass handles restore implementation |
|
mCanvas.onSnapshotRestored(*toRemove, *toRestore); |
|
|
|
freeSnapshot(toRemove); |
|
} |
|
|
|
void CanvasState::restore() { |
|
if (mSaveCount > 1) { |
|
restoreSnapshot(); |
|
} |
|
} |
|
|
|
void CanvasState::restoreToCount(int saveCount) { |
|
if (saveCount < 1) saveCount = 1; |
|
|
|
while (mSaveCount > saveCount) { |
|
restoreSnapshot(); |
|
} |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// Matrix |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
void CanvasState::getMatrix(SkMatrix* matrix) const { |
|
mSnapshot->transform->copyTo(*matrix); |
|
} |
|
|
|
void CanvasState::translate(float dx, float dy, float dz) { |
|
mSnapshot->transform->translate(dx, dy, dz); |
|
} |
|
|
|
void CanvasState::rotate(float degrees) { |
|
mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); |
|
} |
|
|
|
void CanvasState::scale(float sx, float sy) { |
|
mSnapshot->transform->scale(sx, sy, 1.0f); |
|
} |
|
|
|
void CanvasState::skew(float sx, float sy) { |
|
mSnapshot->transform->skew(sx, sy); |
|
} |
|
|
|
void CanvasState::setMatrix(const SkMatrix& matrix) { |
|
mSnapshot->transform->load(matrix); |
|
} |
|
|
|
void CanvasState::setMatrix(const Matrix4& matrix) { |
|
*(mSnapshot->transform) = matrix; |
|
} |
|
|
|
void CanvasState::concatMatrix(const SkMatrix& matrix) { |
|
mat4 transform(matrix); |
|
mSnapshot->transform->multiply(transform); |
|
} |
|
|
|
void CanvasState::concatMatrix(const Matrix4& matrix) { |
|
mSnapshot->transform->multiply(matrix); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// Clip |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) { |
|
mSnapshot->clip(Rect(left, top, right, bottom), op); |
|
return !mSnapshot->clipIsEmpty(); |
|
} |
|
|
|
bool CanvasState::clipPath(const SkPath* path, SkClipOp op) { |
|
mSnapshot->clipPath(*path, op); |
|
return !mSnapshot->clipIsEmpty(); |
|
} |
|
|
|
void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { |
|
Rect bounds; |
|
float radius; |
|
if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported |
|
|
|
bool outlineIsRounded = MathUtils::isPositive(radius); |
|
if (!outlineIsRounded || currentTransform()->isSimple()) { |
|
// TODO: consider storing this rect separately, so that this can't be replaced with clip ops |
|
clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect); |
|
} |
|
if (outlineIsRounded) { |
|
setClippingRoundRect(allocator, bounds, radius, false); |
|
} |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// Quick Rejection |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
/** |
|
* Calculates whether content drawn within the passed bounds would be outside of, or intersect with |
|
* the clipRect. Does not modify the scissor. |
|
* |
|
* @param clipRequired if not null, will be set to true if element intersects clip |
|
* (and wasn't rejected) |
|
* |
|
* @param snapOut if set, the geometry will be treated as having an AA ramp. |
|
* See Rect::snapGeometryToPixelBoundaries() |
|
*/ |
|
bool CanvasState::calculateQuickRejectForScissor(float left, float top, |
|
float right, float bottom, |
|
bool* clipRequired, bool* roundRectClipRequired, |
|
bool snapOut) const { |
|
if (bottom <= top || right <= left) { |
|
return true; |
|
} |
|
|
|
Rect r(left, top, right, bottom); |
|
currentTransform()->mapRect(r); |
|
r.snapGeometryToPixelBoundaries(snapOut); |
|
|
|
Rect clipRect(currentRenderTargetClip()); |
|
clipRect.snapToPixelBoundaries(); |
|
|
|
if (!clipRect.intersects(r)) return true; |
|
|
|
// clip is required if geometry intersects clip rect |
|
if (clipRequired) { |
|
*clipRequired = !clipRect.contains(r); |
|
} |
|
|
|
// round rect clip is required if RR clip exists, and geometry intersects its corners |
|
if (roundRectClipRequired) { |
|
*roundRectClipRequired = mSnapshot->roundRectClipState != nullptr |
|
&& mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); |
|
} |
|
return false; |
|
} |
|
|
|
bool CanvasState::quickRejectConservative(float left, float top, |
|
float right, float bottom) const { |
|
if (bottom <= top || right <= left) { |
|
return true; |
|
} |
|
|
|
Rect r(left, top, right, bottom); |
|
currentTransform()->mapRect(r); |
|
r.roundOut(); // rounded out to be conservative |
|
|
|
Rect clipRect(currentRenderTargetClip()); |
|
clipRect.snapToPixelBoundaries(); |
|
|
|
if (!clipRect.intersects(r)) return true; |
|
|
|
return false; |
|
} |
|
|
|
} // namespace uirenderer |
|
} // namespace android
|
|
|