/* * 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 #include "StatefulBaseRenderer.h" namespace android { namespace uirenderer { StatefulBaseRenderer::StatefulBaseRenderer() : mDirtyClip(false), mWidth(-1), mHeight(-1), mSaveCount(1), mFirstSnapshot(new Snapshot), mSnapshot(mFirstSnapshot) { } void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom) { mSnapshot = new Snapshot(mFirstSnapshot, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); mSnapshot->fbo = getTargetFbo(); mSaveCount = 1; } void StatefulBaseRenderer::initializeViewport(int width, int height) { mWidth = width; mHeight = height; mFirstSnapshot->height = height; mFirstSnapshot->viewport.set(0, 0, width, height); } /////////////////////////////////////////////////////////////////////////////// // Save (layer) /////////////////////////////////////////////////////////////////////////////// /** * Non-virtual implementation of save, guaranteed to save without side-effects * * The approach here and in restoreSnapshot(), allows subclasses to directly manipulate the save * stack, and ensures restoreToCount() doesn't call back into subclass overrides. */ int StatefulBaseRenderer::saveSnapshot(int flags) { mSnapshot = new Snapshot(mSnapshot, flags); return mSaveCount++; } int StatefulBaseRenderer::save(int flags) { return saveSnapshot(flags); } /** * Non-virtual implementation of restore, guaranteed to restore without side-effects. */ void StatefulBaseRenderer::restoreSnapshot() { sp toRemove = mSnapshot; sp toRestore = mSnapshot->previous; mSaveCount--; mSnapshot = toRestore; // subclass handles restore implementation onSnapshotRestored(*toRemove, *toRestore); } void StatefulBaseRenderer::restore() { if (mSaveCount > 1) { restoreSnapshot(); } } void StatefulBaseRenderer::restoreToCount(int saveCount) { if (saveCount < 1) saveCount = 1; while (mSaveCount > saveCount) { restoreSnapshot(); } } /////////////////////////////////////////////////////////////////////////////// // Matrix /////////////////////////////////////////////////////////////////////////////// void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const { mSnapshot->transform->copyTo(*matrix); } void StatefulBaseRenderer::translate(float dx, float dy, float dz) { mSnapshot->transform->translate(dx, dy, dz); } void StatefulBaseRenderer::rotate(float degrees) { mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); } void StatefulBaseRenderer::scale(float sx, float sy) { mSnapshot->transform->scale(sx, sy, 1.0f); } void StatefulBaseRenderer::skew(float sx, float sy) { mSnapshot->transform->skew(sx, sy); } void StatefulBaseRenderer::setMatrix(const SkMatrix* matrix) { if (matrix) { mSnapshot->transform->load(*matrix); } else { mSnapshot->transform->loadIdentity(); } } void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) { mSnapshot->transform->load(matrix); } void StatefulBaseRenderer::concatMatrix(const SkMatrix* matrix) { mat4 transform(*matrix); mSnapshot->transform->multiply(transform); } void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) { mSnapshot->transform->multiply(matrix); } /////////////////////////////////////////////////////////////////////////////// // Clip /////////////////////////////////////////////////////////////////////////////// bool StatefulBaseRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { if (CC_LIKELY(currentTransform()->rectToRect())) { mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op); return !mSnapshot->clipRect->isEmpty(); } SkPath path; path.addRect(left, top, right, bottom); return StatefulBaseRenderer::clipPath(&path, op); } bool StatefulBaseRenderer::clipPath(const SkPath* path, SkRegion::Op op) { SkMatrix transform; currentTransform()->copyTo(transform); SkPath transformed; path->transform(transform, &transformed); SkRegion clip; if (!mSnapshot->previous->clipRegion->isEmpty()) { clip.setRegion(*mSnapshot->previous->clipRegion); } else { if (mSnapshot->previous == firstSnapshot()) { clip.setRect(0, 0, getWidth(), getHeight()); } else { Rect* bounds = mSnapshot->previous->clipRect; clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom); } } SkRegion region; region.setPath(transformed, clip); mDirtyClip |= mSnapshot->clipRegionTransformed(region, op); return !mSnapshot->clipRect->isEmpty(); } bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op); return !mSnapshot->clipRect->isEmpty(); } /////////////////////////////////////////////////////////////////////////////// // 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 StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, float right, float bottom, bool* clipRequired, bool snapOut) const { if (mSnapshot->isIgnored() || bottom <= top || right <= left) { return true; } Rect r(left, top, right, bottom); currentTransform()->mapRect(r); r.snapGeometryToPixelBoundaries(snapOut); Rect clipRect(*currentClipRect()); clipRect.snapToPixelBoundaries(); if (!clipRect.intersects(r)) return true; // clip is required if geometry intersects clip rect if (clipRequired) *clipRequired = !clipRect.contains(r); return false; } /** * Returns false if drawing won't be clipped out. * * Makes the decision conservatively, by rounding out the mapped rect before comparing with the * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but * rejection is still desired. * * This function, unlike quickRejectSetupScissor, should be used where precise geometry information * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass * rejection where precise rejection isn't important, or precise information isn't available. */ bool StatefulBaseRenderer::quickRejectConservative(float left, float top, float right, float bottom) const { if (mSnapshot->isIgnored() || bottom <= top || right <= left) { return true; } Rect r(left, top, right, bottom); currentTransform()->mapRect(r); r.roundOut(); // rounded out to be conservative Rect clipRect(*currentClipRect()); clipRect.snapToPixelBoundaries(); if (!clipRect.intersects(r)) return true; return false; } }; // namespace uirenderer }; // namespace android