Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3260dc62 authored by John Reck's avatar John Reck Committed by Android (Google) Code Review
Browse files

Merge "Introduce CanvasFrontend"

parents b5493f19 dc95f101
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -429,6 +429,7 @@ cc_defaults {
    whole_static_libs: ["libskia"],

    srcs: [
        "canvas/CanvasFrontend.cpp",
        "canvas/CanvasOpBuffer.cpp",
        "canvas/CanvasOpRasterizer.cpp",
        "pipeline/skia/SkiaDisplayList.cpp",
@@ -607,6 +608,7 @@ cc_test {
        "tests/unit/CacheManagerTests.cpp",
        "tests/unit/CanvasContextTests.cpp",
        "tests/unit/CanvasOpTests.cpp",
        "tests/unit/CanvasFrontendTests.cpp",
        "tests/unit/CommonPoolTests.cpp",
        "tests/unit/DamageAccumulatorTests.cpp",
        "tests/unit/DeferredLayerUpdaterTests.cpp",

libs/hwui/SaveFlags.h

0 → 100644
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

#pragma once

#include <inttypes.h>

// TODO: Move this to an enum class
namespace android::SaveFlags {

// These must match the corresponding Canvas API constants.
enum {
    Matrix = 0x01,
    Clip = 0x02,
    HasAlphaLayer = 0x04,
    ClipToLayer = 0x10,

    // Helper constant
    MatrixClip = Matrix | Clip,
};
typedef uint32_t Flags;

}  // namespace android::SaveFlags
+117 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 "CanvasFrontend.h"
#include "CanvasOps.h"
#include "CanvasOpBuffer.h"

namespace android::uirenderer {

CanvasStateHelper::CanvasStateHelper(int width, int height) {
    mInitialBounds = SkIRect::MakeWH(width, height);
    mSaveStack.emplace_back();
    mClipStack.emplace_back().setRect(mInitialBounds);
    mTransformStack.emplace_back();
    mCurrentClipIndex = 0;
    mCurrentTransformIndex = 0;
}

bool CanvasStateHelper::internalSave(SaveEntry saveEntry) {
    mSaveStack.push_back(saveEntry);
    if (saveEntry.matrix) {
        // We need to push before accessing transform() to ensure the reference doesn't move
        // across vector resizes
        mTransformStack.emplace_back() = transform();
        mCurrentTransformIndex += 1;
    }
    if (saveEntry.clip) {
        // We need to push before accessing clip() to ensure the reference doesn't move
        // across vector resizes
        mClipStack.emplace_back() = clip();
        mCurrentClipIndex += 1;
        return true;
    }
    return false;
}

// Assert that the cast from SkClipOp to SkRegion::Op is valid
static_assert(static_cast<int>(SkClipOp::kDifference) == SkRegion::Op::kDifference_Op);
static_assert(static_cast<int>(SkClipOp::kIntersect) == SkRegion::Op::kIntersect_Op);
static_assert(static_cast<int>(SkClipOp::kUnion_deprecated) == SkRegion::Op::kUnion_Op);
static_assert(static_cast<int>(SkClipOp::kXOR_deprecated) == SkRegion::Op::kXOR_Op);
static_assert(static_cast<int>(SkClipOp::kReverseDifference_deprecated) == SkRegion::Op::kReverseDifference_Op);
static_assert(static_cast<int>(SkClipOp::kReplace_deprecated) == SkRegion::Op::kReplace_Op);

void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) {
    clip().opRect(rect, transform(), mInitialBounds, (SkRegion::Op)op, false);
}

void CanvasStateHelper::internalClipPath(const SkPath& path, SkClipOp op) {
    clip().opPath(path, transform(), mInitialBounds, (SkRegion::Op)op, true);
}

bool CanvasStateHelper::internalRestore() {
    // Prevent underflows
    if (saveCount() <= 1) {
        return false;
    }

    SaveEntry entry = mSaveStack[mSaveStack.size() - 1];
    mSaveStack.pop_back();
    bool needsRestorePropagation = entry.layer;
    if (entry.matrix) {
        mTransformStack.pop_back();
        mCurrentTransformIndex -= 1;
    }
    if (entry.clip) {
        // We need to push before accessing clip() to ensure the reference doesn't move
        // across vector resizes
        mClipStack.pop_back();
        mCurrentClipIndex -= 1;
        needsRestorePropagation = true;
    }
    return needsRestorePropagation;
}

SkRect CanvasStateHelper::getClipBounds() const {
    SkIRect ibounds = clip().getBounds();

    if (ibounds.isEmpty()) {
        return SkRect::MakeEmpty();
    }

    SkMatrix inverse;
    // if we can't invert the CTM, we can't return local clip bounds
    if (!transform().invert(&inverse)) {
        return SkRect::MakeEmpty();
    }

    SkRect ret = SkRect::MakeEmpty();
    inverse.mapRect(&ret, SkRect::Make(ibounds));
    return ret;
}

bool CanvasStateHelper::quickRejectRect(float left, float top, float right, float bottom) const {
    // TODO: Implement
    return false;
}

bool CanvasStateHelper::quickRejectPath(const SkPath& path) const {
    // TODO: Implement
    return false;
}

} // namespace android::uirenderer
+200 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

#pragma once

// TODO: Can we get the dependencies scoped down more?
#include "CanvasOps.h"
#include "CanvasOpBuffer.h"
#include <SaveFlags.h>

#include <SkRasterClip.h>
#include <ui/FatVector.h>

#include <optional>

namespace android::uirenderer {

// Exists to avoid forcing all this common logic into the templated class
class CanvasStateHelper {
protected:
    CanvasStateHelper(int width, int height);
    ~CanvasStateHelper() = default;

    struct SaveEntry {
        bool clip : 1 = false;
        bool matrix : 1 = false;
        bool layer : 1 = false;
    };

    constexpr SaveEntry saveEntryForLayer() {
        return {
            .clip = true,
            .matrix = true,
            .layer = true,
        };
    }

    constexpr SaveEntry flagsToSaveEntry(SaveFlags::Flags flags) {
        return SaveEntry {
            .clip = static_cast<bool>(flags & SaveFlags::Clip),
            .matrix = static_cast<bool>(flags & SaveFlags::Matrix),
            .layer = false
        };
    }

    bool internalSave(SaveEntry saveEntry);
    bool internalSave(SaveFlags::Flags flags) {
        return internalSave(flagsToSaveEntry(flags));
    }
    void internalSaveLayer(const SkCanvas::SaveLayerRec& layerRec) {
        internalSave({
            .clip = true,
            .matrix = true,
            .layer = true
        });
        internalClipRect(*layerRec.fBounds, SkClipOp::kIntersect);
    }

    bool internalRestore();

    void internalClipRect(const SkRect& rect, SkClipOp op);
    void internalClipPath(const SkPath& path, SkClipOp op);

    SkIRect mInitialBounds;
    FatVector<SaveEntry, 6> mSaveStack;
    FatVector<SkMatrix, 6> mTransformStack;
    FatVector<SkConservativeClip, 6> mClipStack;

    size_t mCurrentTransformIndex;
    size_t mCurrentClipIndex;

    const SkConservativeClip& clip() const {
        return mClipStack[mCurrentClipIndex];
    }

    SkConservativeClip& clip() {
        return mClipStack[mCurrentClipIndex];
    }

public:
    int saveCount() const { return mSaveStack.size(); }

    SkRect getClipBounds() const;
    bool quickRejectRect(float left, float top, float right, float bottom) const;
    bool quickRejectPath(const SkPath& path) const;

    const SkMatrix& transform() const {
        return mTransformStack[mCurrentTransformIndex];
    }

    SkMatrix& transform() {
        return mTransformStack[mCurrentTransformIndex];
    }

    // For compat with existing HWUI Canvas interface
    void getMatrix(SkMatrix* outMatrix) const {
        *outMatrix = transform();
    }

    void setMatrix(const SkMatrix& matrix) {
        transform() = matrix;
    }

    void concat(const SkMatrix& matrix) {
        transform().preConcat(matrix);
    }

    void rotate(float degrees) {
        SkMatrix m;
        m.setRotate(degrees);
        concat(m);
    }

    void scale(float sx, float sy) {
        SkMatrix m;
        m.setScale(sx, sy);
        concat(m);
    }

    void skew(float sx, float sy) {
        SkMatrix m;
        m.setSkew(sx, sy);
        concat(m);
    }

    void translate(float dx, float dy) {
        transform().preTranslate(dx, dy);
    }
};

// Front-end canvas that handles queries, up-front state, and produces CanvasOp<> output downstream
template <typename CanvasOpReceiver>
class CanvasFrontend final : public CanvasStateHelper {
public:
    template<class... Args>
    CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height),
            mReceiver(std::forward<Args>(args)...) { }
    ~CanvasFrontend() = default;

    void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) {
        if (internalSave(flagsToSaveEntry(flags))) {
            submit<CanvasOpType::Save>({});
        }
    }

    void restore() {
        if (internalRestore()) {
            submit<CanvasOpType::Restore>({});
        }
    }

    template <CanvasOpType T>
    void draw(CanvasOp<T>&& op) {
        // The front-end requires going through certain front-doors, which these aren't.
        static_assert(T != CanvasOpType::Save, "Must use CanvasFrontend::save() call instead");
        static_assert(T != CanvasOpType::Restore, "Must use CanvasFrontend::restore() call instead");

        if constexpr (T == CanvasOpType::SaveLayer) {
            internalSaveLayer(op.saveLayerRec);
        }
        if constexpr (T == CanvasOpType::SaveBehind) {
            // Don't use internalSaveLayer as this doesn't apply clipping, it's a "regular" save
            // But we do want to flag it as a layer, such that restore is Definitely Required
            internalSave(saveEntryForLayer());
        }
        if constexpr (T == CanvasOpType::ClipRect) {
            internalClipRect(op.rect, op.op);
        }
        if constexpr (T == CanvasOpType::ClipPath) {
            internalClipPath(op.path, op.op);
        }

        submit(std::move(op));
    }

    const CanvasOpReceiver& receiver() const { return mReceiver; }

private:
    CanvasOpReceiver mReceiver;

    template <CanvasOpType T>
    void submit(CanvasOp<T>&& op) {
        mReceiver.push_container(CanvasOpContainer(std::move(op), transform()));
    }
};

} // namespace android::uirenderer
+22 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 "CanvasOpRecorder.h"

#include "CanvasOpBuffer.h"
#include "CanvasOps.h"

namespace android::uirenderer {}  // namespace android::uirenderer
Loading