Replace StatefulBaseRenderer inheritance with CanvasState member
Incrementally disentangles DisplayListRenderer and OpenGLRenderer.
Introduces abstract CanvasStateClient class to share three functions
between the two.
Design doc at https://docs.google.com/a/google.com/document/d/1PY1JF7AfPEF2UOUAnETS5j_4_tnJShTAMExvpCJfP8o/edit?usp=sharing.
BUG:15672762
R=djsollen@google.com,ccraik@google.com,jreck@google.com
Change-Id: Ic9fdffe18808e7d921ad06d01ea1ca25b2ad6f23
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
new file mode 100644
index 0000000..20cc17e
--- /dev/null
+++ b/libs/hwui/CanvasState.cpp
@@ -0,0 +1,285 @@
+/*
+ * 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 <SkCanvas.h>
+
+#include "CanvasState.h"
+#include "utils/MathUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+
+CanvasState::CanvasState(CanvasStateClient& renderer)
+ : mDirtyClip(false)
+ , mWidth(-1)
+ , mHeight(-1)
+ , mSaveCount(1)
+ , mFirstSnapshot(new Snapshot)
+ , mCanvas(renderer)
+ , mSnapshot(mFirstSnapshot) {
+
+}
+
+CanvasState::~CanvasState() {
+
+}
+
+void CanvasState::initializeSaveStack(float clipLeft, float clipTop,
+ float clipRight, float clipBottom, const Vector3& lightCenter) {
+ mSnapshot = new Snapshot(mFirstSnapshot,
+ SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
+ mSnapshot->fbo = mCanvas.onGetTargetFbo();
+ mSnapshot->setRelativeLightCenter(lightCenter);
+ mSaveCount = 1;
+}
+
+void CanvasState::setViewport(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ mFirstSnapshot->initializeViewport(width, height);
+ mCanvas.onViewportInitialized();
+
+ // create a temporary 1st snapshot, so old snapshots are released,
+ // and viewport can be queried safely.
+ // TODO: remove, combine viewport + save stack initialization
+ mSnapshot = new Snapshot(mFirstSnapshot,
+ SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ mSaveCount = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// 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 = new Snapshot(mSnapshot, flags);
+ return mSaveCount++;
+}
+
+int CanvasState::save(int flags) {
+ return saveSnapshot(flags);
+}
+
+/**
+ * Guaranteed to restore without side-effects.
+ */
+void CanvasState::restoreSnapshot() {
+ sp<Snapshot> toRemove = mSnapshot;
+ sp<Snapshot> toRestore = mSnapshot->previous;
+
+ mSaveCount--;
+ mSnapshot = toRestore;
+
+ // subclass handles restore implementation
+ mCanvas.onSnapshotRestored(*toRemove, *toRestore);
+}
+
+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->load(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, 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 CanvasState::clipPath(&path, op);
+}
+
+bool CanvasState::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);
+
+ // region is the transformed input path, masked by the previous clip
+ mDirtyClip |= mSnapshot->clipRegionTransformed(region, op);
+ return !mSnapshot->clipRect->isEmpty();
+}
+
+bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) {
+ mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op);
+ return !mSnapshot->clipRect->isEmpty();
+}
+
+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, SkRegion::kIntersect_Op);
+ }
+ if (outlineIsRounded) {
+ setClippingRoundRect(allocator, bounds, radius, false);
+ }
+}
+
+void CanvasState::setClippingRoundRect(LinearAllocator& allocator,
+ const Rect& rect, float radius, bool highPriority) {
+ mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// 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 (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);
+ }
+
+ // round rect clip is required if RR clip exists, and geometry intersects its corners
+ if (roundRectClipRequired) {
+ *roundRectClipRequired = mSnapshot->roundRectClipState != NULL
+ && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
+ }
+ return false;
+}
+
+bool CanvasState::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