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