blob: d749d2f2596b944962a849868068f28d8409403b [file] [log] [blame]
John Reckdc95f102020-11-16 12:35:02 -05001/*
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
29namespace android::uirenderer {
30
31// Exists to avoid forcing all this common logic into the templated class
32class CanvasStateHelper {
33protected:
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 Reckb5eeb182020-12-09 13:45:39 -050060
John Reckdc95f102020-11-16 12:35:02 -050061 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 Reckb5eeb182020-12-09 13:45:39 -050091 void resetState(int width, int height);
92
John Reckdc95f102020-11-16 12:35:02 -050093public:
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
145template <typename CanvasOpReceiver>
146class CanvasFrontend final : public CanvasStateHelper {
147public:
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 Reckb5eeb182020-12-09 13:45:39 -0500189 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 Reckdc95f102020-11-16 12:35:02 -0500202
203private:
John Reckb5eeb182020-12-09 13:45:39 -0500204 std::optional<CanvasOpReceiver> mReceiver;
John Reckdc95f102020-11-16 12:35:02 -0500205
206 template <CanvasOpType T>
207 void submit(CanvasOp<T>&& op) {
John Reckb5eeb182020-12-09 13:45:39 -0500208 mReceiver->push_container(CanvasOpContainer(std::move(op), transform()));
John Reckdc95f102020-11-16 12:35:02 -0500209 }
210};
211
212} // namespace android::uirenderer