John Reck | dc95f10 | 2020-11-16 12:35:02 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #pragma once |
| 18 | |
| 19 | // TODO: Can we get the dependencies scoped down more? |
| 20 | #include "CanvasOps.h" |
| 21 | #include "CanvasOpBuffer.h" |
| 22 | #include <SaveFlags.h> |
| 23 | |
| 24 | #include <SkRasterClip.h> |
| 25 | #include <ui/FatVector.h> |
| 26 | |
| 27 | #include <optional> |
| 28 | |
| 29 | namespace android::uirenderer { |
| 30 | |
| 31 | // Exists to avoid forcing all this common logic into the templated class |
| 32 | class CanvasStateHelper { |
| 33 | protected: |
| 34 | CanvasStateHelper(int width, int height); |
| 35 | ~CanvasStateHelper() = default; |
| 36 | |
| 37 | struct SaveEntry { |
| 38 | bool clip : 1 = false; |
| 39 | bool matrix : 1 = false; |
| 40 | bool layer : 1 = false; |
| 41 | }; |
| 42 | |
| 43 | constexpr SaveEntry saveEntryForLayer() { |
| 44 | return { |
| 45 | .clip = true, |
| 46 | .matrix = true, |
| 47 | .layer = true, |
| 48 | }; |
| 49 | } |
| 50 | |
| 51 | constexpr SaveEntry flagsToSaveEntry(SaveFlags::Flags flags) { |
| 52 | return SaveEntry { |
| 53 | .clip = static_cast<bool>(flags & SaveFlags::Clip), |
| 54 | .matrix = static_cast<bool>(flags & SaveFlags::Matrix), |
| 55 | .layer = false |
| 56 | }; |
| 57 | } |
| 58 | |
| 59 | bool internalSave(SaveEntry saveEntry); |
John Reck | b5eeb18 | 2020-12-09 13:45:39 -0500 | [diff] [blame] | 60 | |
John Reck | dc95f10 | 2020-11-16 12:35:02 -0500 | [diff] [blame] | 61 | void internalSaveLayer(const SkCanvas::SaveLayerRec& layerRec) { |
| 62 | internalSave({ |
| 63 | .clip = true, |
| 64 | .matrix = true, |
| 65 | .layer = true |
| 66 | }); |
| 67 | internalClipRect(*layerRec.fBounds, SkClipOp::kIntersect); |
| 68 | } |
| 69 | |
| 70 | bool internalRestore(); |
| 71 | |
| 72 | void internalClipRect(const SkRect& rect, SkClipOp op); |
| 73 | void internalClipPath(const SkPath& path, SkClipOp op); |
| 74 | |
| 75 | SkIRect mInitialBounds; |
| 76 | FatVector<SaveEntry, 6> mSaveStack; |
| 77 | FatVector<SkMatrix, 6> mTransformStack; |
| 78 | FatVector<SkConservativeClip, 6> mClipStack; |
| 79 | |
| 80 | size_t mCurrentTransformIndex; |
| 81 | size_t mCurrentClipIndex; |
| 82 | |
| 83 | const SkConservativeClip& clip() const { |
| 84 | return mClipStack[mCurrentClipIndex]; |
| 85 | } |
| 86 | |
| 87 | SkConservativeClip& clip() { |
| 88 | return mClipStack[mCurrentClipIndex]; |
| 89 | } |
| 90 | |
John Reck | b5eeb18 | 2020-12-09 13:45:39 -0500 | [diff] [blame] | 91 | void resetState(int width, int height); |
| 92 | |
John Reck | dc95f10 | 2020-11-16 12:35:02 -0500 | [diff] [blame] | 93 | public: |
| 94 | int saveCount() const { return mSaveStack.size(); } |
| 95 | |
| 96 | SkRect getClipBounds() const; |
| 97 | bool quickRejectRect(float left, float top, float right, float bottom) const; |
| 98 | bool quickRejectPath(const SkPath& path) const; |
| 99 | |
| 100 | const SkMatrix& transform() const { |
| 101 | return mTransformStack[mCurrentTransformIndex]; |
| 102 | } |
| 103 | |
| 104 | SkMatrix& transform() { |
| 105 | return mTransformStack[mCurrentTransformIndex]; |
| 106 | } |
| 107 | |
| 108 | // For compat with existing HWUI Canvas interface |
| 109 | void getMatrix(SkMatrix* outMatrix) const { |
| 110 | *outMatrix = transform(); |
| 111 | } |
| 112 | |
| 113 | void setMatrix(const SkMatrix& matrix) { |
| 114 | transform() = matrix; |
| 115 | } |
| 116 | |
| 117 | void concat(const SkMatrix& matrix) { |
| 118 | transform().preConcat(matrix); |
| 119 | } |
| 120 | |
| 121 | void rotate(float degrees) { |
| 122 | SkMatrix m; |
| 123 | m.setRotate(degrees); |
| 124 | concat(m); |
| 125 | } |
| 126 | |
| 127 | void scale(float sx, float sy) { |
| 128 | SkMatrix m; |
| 129 | m.setScale(sx, sy); |
| 130 | concat(m); |
| 131 | } |
| 132 | |
| 133 | void skew(float sx, float sy) { |
| 134 | SkMatrix m; |
| 135 | m.setSkew(sx, sy); |
| 136 | concat(m); |
| 137 | } |
| 138 | |
| 139 | void translate(float dx, float dy) { |
| 140 | transform().preTranslate(dx, dy); |
| 141 | } |
| 142 | }; |
| 143 | |
| 144 | // Front-end canvas that handles queries, up-front state, and produces CanvasOp<> output downstream |
| 145 | template <typename CanvasOpReceiver> |
| 146 | class CanvasFrontend final : public CanvasStateHelper { |
| 147 | public: |
| 148 | template<class... Args> |
| 149 | CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height), |
| 150 | mReceiver(std::forward<Args>(args)...) { } |
| 151 | ~CanvasFrontend() = default; |
| 152 | |
| 153 | void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) { |
| 154 | if (internalSave(flagsToSaveEntry(flags))) { |
| 155 | submit<CanvasOpType::Save>({}); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | void restore() { |
| 160 | if (internalRestore()) { |
| 161 | submit<CanvasOpType::Restore>({}); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | template <CanvasOpType T> |
| 166 | void draw(CanvasOp<T>&& op) { |
| 167 | // The front-end requires going through certain front-doors, which these aren't. |
| 168 | static_assert(T != CanvasOpType::Save, "Must use CanvasFrontend::save() call instead"); |
| 169 | static_assert(T != CanvasOpType::Restore, "Must use CanvasFrontend::restore() call instead"); |
| 170 | |
| 171 | if constexpr (T == CanvasOpType::SaveLayer) { |
| 172 | internalSaveLayer(op.saveLayerRec); |
| 173 | } |
| 174 | if constexpr (T == CanvasOpType::SaveBehind) { |
| 175 | // Don't use internalSaveLayer as this doesn't apply clipping, it's a "regular" save |
| 176 | // But we do want to flag it as a layer, such that restore is Definitely Required |
| 177 | internalSave(saveEntryForLayer()); |
| 178 | } |
| 179 | if constexpr (T == CanvasOpType::ClipRect) { |
| 180 | internalClipRect(op.rect, op.op); |
| 181 | } |
| 182 | if constexpr (T == CanvasOpType::ClipPath) { |
| 183 | internalClipPath(op.path, op.op); |
| 184 | } |
| 185 | |
| 186 | submit(std::move(op)); |
| 187 | } |
| 188 | |
John Reck | b5eeb18 | 2020-12-09 13:45:39 -0500 | [diff] [blame] | 189 | const CanvasOpReceiver& receiver() const { return *mReceiver; } |
| 190 | |
| 191 | CanvasOpReceiver finish() { |
| 192 | auto ret = std::move(mReceiver.value()); |
| 193 | mReceiver.reset(); |
| 194 | return std::move(ret); |
| 195 | } |
| 196 | |
| 197 | template<class... Args> |
| 198 | void reset(int newWidth, int newHeight, Args&&... args) { |
| 199 | resetState(newWidth, newHeight); |
| 200 | mReceiver.emplace(std::forward<Args>(args)...); |
| 201 | } |
John Reck | dc95f10 | 2020-11-16 12:35:02 -0500 | [diff] [blame] | 202 | |
| 203 | private: |
John Reck | b5eeb18 | 2020-12-09 13:45:39 -0500 | [diff] [blame] | 204 | std::optional<CanvasOpReceiver> mReceiver; |
John Reck | dc95f10 | 2020-11-16 12:35:02 -0500 | [diff] [blame] | 205 | |
| 206 | template <CanvasOpType T> |
| 207 | void submit(CanvasOp<T>&& op) { |
John Reck | b5eeb18 | 2020-12-09 13:45:39 -0500 | [diff] [blame] | 208 | mReceiver->push_container(CanvasOpContainer(std::move(op), transform())); |
John Reck | dc95f10 | 2020-11-16 12:35:02 -0500 | [diff] [blame] | 209 | } |
| 210 | }; |
| 211 | |
| 212 | } // namespace android::uirenderer |