blob: 9f22b900e4ab1cc185ded5823a0e642f100d1386 [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
John Reckdc95f102020-11-16 12:35:02 -050024#include <ui/FatVector.h>
25
26#include <optional>
27
28namespace android::uirenderer {
29
30// Exists to avoid forcing all this common logic into the templated class
31class CanvasStateHelper {
32protected:
33 CanvasStateHelper(int width, int height);
34 ~CanvasStateHelper() = default;
35
36 struct SaveEntry {
37 bool clip : 1 = false;
38 bool matrix : 1 = false;
39 bool layer : 1 = false;
40 };
41
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000042 template <typename T>
43 struct DeferredEntry {
44 T entry;
45 int deferredSaveCount = 0;
46
47 DeferredEntry() = default;
48 DeferredEntry(const T& t) : entry(t) {}
49 };
50
51 struct ConservativeClip {
52 SkIRect bounds = SkIRect::MakeEmpty();
53 bool rect = true;
54 bool aa = false;
55
56 bool quickReject(const SkMatrix& matrix, const SkRect& bounds) const;
57
58 void apply(SkClipOp op, const SkMatrix& matrix, const SkRect& bounds, bool aa,
59 bool fillsBounds);
60 };
61
John Reckdc95f102020-11-16 12:35:02 -050062 constexpr SaveEntry saveEntryForLayer() {
63 return {
64 .clip = true,
65 .matrix = true,
66 .layer = true,
67 };
68 }
69
70 constexpr SaveEntry flagsToSaveEntry(SaveFlags::Flags flags) {
71 return SaveEntry {
72 .clip = static_cast<bool>(flags & SaveFlags::Clip),
73 .matrix = static_cast<bool>(flags & SaveFlags::Matrix),
74 .layer = false
75 };
76 }
77
78 bool internalSave(SaveEntry saveEntry);
John Reckb5eeb182020-12-09 13:45:39 -050079
John Reckdc95f102020-11-16 12:35:02 -050080 void internalSaveLayer(const SkCanvas::SaveLayerRec& layerRec) {
81 internalSave({
82 .clip = true,
83 .matrix = true,
84 .layer = true
85 });
86 internalClipRect(*layerRec.fBounds, SkClipOp::kIntersect);
87 }
88
89 bool internalRestore();
90
91 void internalClipRect(const SkRect& rect, SkClipOp op);
92 void internalClipPath(const SkPath& path, SkClipOp op);
93
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000094 // The canvas' clip will never expand beyond these bounds since intersect
95 // and difference operations only subtract pixels.
John Reckdc95f102020-11-16 12:35:02 -050096 SkIRect mInitialBounds;
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000097 // Every save() gets a SaveEntry to track what needs to be restored.
John Reckdc95f102020-11-16 12:35:02 -050098 FatVector<SaveEntry, 6> mSaveStack;
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000099 // Transform and clip entries record a deferred save count and do not
100 // make a new entry until that particular state is modified.
101 FatVector<DeferredEntry<SkMatrix>, 6> mTransformStack;
102 FatVector<DeferredEntry<ConservativeClip>, 6> mClipStack;
John Reckdc95f102020-11-16 12:35:02 -0500103
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000104 const ConservativeClip& clip() const { return mClipStack.back().entry; }
John Reckdc95f102020-11-16 12:35:02 -0500105
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000106 ConservativeClip& clip();
John Reckdc95f102020-11-16 12:35:02 -0500107
John Reckb5eeb182020-12-09 13:45:39 -0500108 void resetState(int width, int height);
109
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000110 // Stack manipulation for transform and clip stacks
111 template <typename T, size_t N>
112 void pushEntry(FatVector<DeferredEntry<T>, N>* stack) {
113 stack->back().deferredSaveCount += 1;
114 }
115
116 template <typename T, size_t N>
117 void popEntry(FatVector<DeferredEntry<T>, N>* stack) {
118 if (!(stack->back().deferredSaveCount--)) {
119 stack->pop_back();
120 }
121 }
122
123 template <typename T, size_t N>
124 T& writableEntry(FatVector<DeferredEntry<T>, N>* stack) {
125 DeferredEntry<T>& back = stack->back();
126 if (back.deferredSaveCount == 0) {
127 return back.entry;
128 } else {
129 back.deferredSaveCount -= 1;
130 // saved in case references move when re-allocating vector storage
131 T state = back.entry;
132 return stack->emplace_back(state).entry;
133 }
134 }
135
John Reckdc95f102020-11-16 12:35:02 -0500136public:
137 int saveCount() const { return mSaveStack.size(); }
138
139 SkRect getClipBounds() const;
140 bool quickRejectRect(float left, float top, float right, float bottom) const;
141 bool quickRejectPath(const SkPath& path) const;
142
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000143 bool isClipAA() const { return clip().aa; }
144 bool isClipEmpty() const { return clip().bounds.isEmpty(); }
145 bool isClipRect() const { return clip().rect; }
146 bool isClipComplex() const { return !isClipEmpty() && (isClipAA() || !isClipRect()); }
John Reckdc95f102020-11-16 12:35:02 -0500147
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000148 const SkMatrix& transform() const { return mTransformStack.back().entry; }
149
150 SkMatrix& transform();
John Reckdc95f102020-11-16 12:35:02 -0500151
152 // For compat with existing HWUI Canvas interface
153 void getMatrix(SkMatrix* outMatrix) const {
154 *outMatrix = transform();
155 }
156
157 void setMatrix(const SkMatrix& matrix) {
158 transform() = matrix;
159 }
160
161 void concat(const SkMatrix& matrix) {
162 transform().preConcat(matrix);
163 }
164
165 void rotate(float degrees) {
166 SkMatrix m;
167 m.setRotate(degrees);
168 concat(m);
169 }
170
171 void scale(float sx, float sy) {
172 SkMatrix m;
173 m.setScale(sx, sy);
174 concat(m);
175 }
176
177 void skew(float sx, float sy) {
178 SkMatrix m;
179 m.setSkew(sx, sy);
180 concat(m);
181 }
182
183 void translate(float dx, float dy) {
184 transform().preTranslate(dx, dy);
185 }
186};
187
188// Front-end canvas that handles queries, up-front state, and produces CanvasOp<> output downstream
189template <typename CanvasOpReceiver>
190class CanvasFrontend final : public CanvasStateHelper {
191public:
192 template<class... Args>
193 CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height),
John Reckd34d6ce2021-01-19 21:29:24 -0500194 mReceiver(std::in_place, std::forward<Args>(args)...) { }
John Reckdc95f102020-11-16 12:35:02 -0500195
196 void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) {
197 if (internalSave(flagsToSaveEntry(flags))) {
198 submit<CanvasOpType::Save>({});
199 }
200 }
201
202 void restore() {
203 if (internalRestore()) {
204 submit<CanvasOpType::Restore>({});
205 }
206 }
207
208 template <CanvasOpType T>
209 void draw(CanvasOp<T>&& op) {
210 // The front-end requires going through certain front-doors, which these aren't.
211 static_assert(T != CanvasOpType::Save, "Must use CanvasFrontend::save() call instead");
212 static_assert(T != CanvasOpType::Restore, "Must use CanvasFrontend::restore() call instead");
213
214 if constexpr (T == CanvasOpType::SaveLayer) {
215 internalSaveLayer(op.saveLayerRec);
216 }
217 if constexpr (T == CanvasOpType::SaveBehind) {
218 // Don't use internalSaveLayer as this doesn't apply clipping, it's a "regular" save
219 // But we do want to flag it as a layer, such that restore is Definitely Required
220 internalSave(saveEntryForLayer());
221 }
222 if constexpr (T == CanvasOpType::ClipRect) {
223 internalClipRect(op.rect, op.op);
224 }
225 if constexpr (T == CanvasOpType::ClipPath) {
226 internalClipPath(op.path, op.op);
227 }
228
229 submit(std::move(op));
230 }
231
John Reckd34d6ce2021-01-19 21:29:24 -0500232 const CanvasOpReceiver& receiver() const {
233 LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
234 return *mReceiver;
235 }
John Reckb5eeb182020-12-09 13:45:39 -0500236
237 CanvasOpReceiver finish() {
238 auto ret = std::move(mReceiver.value());
239 mReceiver.reset();
240 return std::move(ret);
241 }
242
243 template<class... Args>
244 void reset(int newWidth, int newHeight, Args&&... args) {
245 resetState(newWidth, newHeight);
246 mReceiver.emplace(std::forward<Args>(args)...);
247 }
John Reckdc95f102020-11-16 12:35:02 -0500248
249private:
John Reckb5eeb182020-12-09 13:45:39 -0500250 std::optional<CanvasOpReceiver> mReceiver;
John Reckdc95f102020-11-16 12:35:02 -0500251
252 template <CanvasOpType T>
253 void submit(CanvasOp<T>&& op) {
John Reckd34d6ce2021-01-19 21:29:24 -0500254 LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
John Reckb5eeb182020-12-09 13:45:39 -0500255 mReceiver->push_container(CanvasOpContainer(std::move(op), transform()));
John Reckdc95f102020-11-16 12:35:02 -0500256 }
257};
258
259} // namespace android::uirenderer