/* * Copyright (C) 2012 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 "Snapshot.h" #include "hwui/Canvas.h" namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// // Constructors /////////////////////////////////////////////////////////////////////////////// Snapshot::Snapshot() : flags(0) , previous(nullptr) , layer(nullptr) , fbo(0) , alpha(1.0f) , roundRectClipState(nullptr) , projectionPathMask(nullptr) , mClipArea(&mClipAreaRoot) { transform = &mTransformRoot; mRelativeLightCenter.x = mRelativeLightCenter.y = mRelativeLightCenter.z = 0; } /** * Copies the specified snapshot/ The specified snapshot is stored as * the previous snapshot. */ Snapshot::Snapshot(Snapshot* s, int saveFlags) : flags(0) , previous(s) , layer(s->layer) , fbo(s->fbo) , alpha(s->alpha) , roundRectClipState(s->roundRectClipState) , projectionPathMask(s->projectionPathMask) , mClipArea(nullptr) , mViewportData(s->mViewportData) , mRelativeLightCenter(s->mRelativeLightCenter) { if (saveFlags & SaveFlags::Matrix) { mTransformRoot = *s->transform; transform = &mTransformRoot; } else { transform = s->transform; } if (saveFlags & SaveFlags::Clip) { mClipAreaRoot = s->getClipArea(); mClipArea = &mClipAreaRoot; } else { mClipArea = s->mClipArea; } } /////////////////////////////////////////////////////////////////////////////// // Clipping /////////////////////////////////////////////////////////////////////////////// void Snapshot::clip(const Rect& localClip, SkClipOp op) { flags |= Snapshot::kFlagClipSet; mClipArea->clipRectWithTransform(localClip, transform, static_cast(op)); } void Snapshot::clipPath(const SkPath& path, SkClipOp op) { flags |= Snapshot::kFlagClipSet; mClipArea->clipPathWithTransform(path, transform, static_cast(op)); } void Snapshot::setClip(float left, float top, float right, float bottom) { flags |= Snapshot::kFlagClipSet; mClipArea->setClip(left, top, right, bottom); } bool Snapshot::hasPerspectiveTransform() const { return transform->isPerspective(); } const Rect& Snapshot::getLocalClip() { mat4 inverse; inverse.loadInverse(*transform); mLocalClip.set(mClipArea->getClipRect()); inverse.mapRect(mLocalClip); return mLocalClip; } void Snapshot::resetClip(float left, float top, float right, float bottom) { // TODO: This is incorrect, when we start rendering into a new layer, // we may have to modify the previous snapshot's clip rect and clip // region if the previous restore() call did not restore the clip mClipArea = &mClipAreaRoot; setClip(left, top, right, bottom); } /////////////////////////////////////////////////////////////////////////////// // Clipping round rect /////////////////////////////////////////////////////////////////////////////// void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius, bool highPriority) { if (bounds.isEmpty()) { mClipArea->setEmpty(); return; } if (roundRectClipState && roundRectClipState->highPriority) { // ignore, don't replace, already have a high priority clip return; } RoundRectClipState* state = new (allocator) RoundRectClipState; state->highPriority = highPriority; // store the inverse drawing matrix Matrix4 roundRectDrawingMatrix = getOrthoMatrix(); roundRectDrawingMatrix.multiply(*transform); state->matrix.loadInverse(roundRectDrawingMatrix); // compute area under rounded corners - only draws overlapping these rects need to be clipped for (int i = 0 ; i < 4; i++) { state->dangerRects[i] = bounds; } state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius; state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius; state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius; state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius; for (int i = 0; i < 4; i++) { transform->mapRect(state->dangerRects[i]); // round danger rects out as though they are AA geometry (since they essentially are) state->dangerRects[i].snapGeometryToPixelBoundaries(true); } // store RR area state->innerRect = bounds; state->innerRect.inset(radius); state->radius = radius; // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info roundRectClipState = state; } void Snapshot::setProjectionPathMask(const SkPath* path) { projectionPathMask = path; } static Snapshot* getClipRoot(Snapshot* target) { while (target->previous && target->previous->previous) { target = target->previous; } return target; } const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator, const ClipBase* recordedClip, const Matrix4& recordedClipTransform) { auto target = this; if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { // Clip must be intersected with root, instead of current clip. target = getClipRoot(this); } return target->mClipArea->serializeIntersectedClip(allocator, recordedClip, recordedClipTransform); } void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) { if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { // current clip is being replaced, but must intersect with clip root *mClipArea = *(getClipRoot(this)->mClipArea); } mClipArea->applyClip(recordedClip, transform); } /////////////////////////////////////////////////////////////////////////////// // Queries /////////////////////////////////////////////////////////////////////////////// void Snapshot::dump() const { ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d", this, flags, previous, getViewportHeight(), !mClipArea->isSimple()); const Rect& clipRect(mClipArea->getClipRect()); ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple()); ALOGD(" Transform (at %p):", transform); transform->dump(); } }; // namespace uirenderer }; // namespace android