• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <ui/FatVector.h>
25 
26 #include <optional>
27 
28 namespace android::uirenderer {
29 
30 // Exists to avoid forcing all this common logic into the templated class
31 class CanvasStateHelper {
32 protected:
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 
42     template <typename T>
43     struct DeferredEntry {
44         T entry;
45         int deferredSaveCount = 0;
46 
47         DeferredEntry() = default;
DeferredEntryDeferredEntry48         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 
saveEntryForLayer()62     constexpr SaveEntry saveEntryForLayer() {
63         return {
64             .clip = true,
65             .matrix = true,
66             .layer = true,
67         };
68     }
69 
flagsToSaveEntry(SaveFlags::Flags flags)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);
79 
internalSaveLayer(const SkCanvas::SaveLayerRec & layerRec)80     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 
94     // The canvas' clip will never expand beyond these bounds since intersect
95     // and difference operations only subtract pixels.
96     SkIRect mInitialBounds;
97     // Every save() gets a SaveEntry to track what needs to be restored.
98     FatVector<SaveEntry, 6> mSaveStack;
99     // 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;
103 
clip()104     const ConservativeClip& clip() const { return mClipStack.back().entry; }
105 
106     ConservativeClip& clip();
107 
108     void resetState(int width, int height);
109 
110     // Stack manipulation for transform and clip stacks
111     template <typename T, size_t N>
pushEntry(FatVector<DeferredEntry<T>,N> * stack)112     void pushEntry(FatVector<DeferredEntry<T>, N>* stack) {
113         stack->back().deferredSaveCount += 1;
114     }
115 
116     template <typename T, size_t N>
popEntry(FatVector<DeferredEntry<T>,N> * stack)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>
writableEntry(FatVector<DeferredEntry<T>,N> * stack)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 
136 public:
saveCount()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 
isClipAA()143     bool isClipAA() const { return clip().aa; }
isClipEmpty()144     bool isClipEmpty() const { return clip().bounds.isEmpty(); }
isClipRect()145     bool isClipRect() const { return clip().rect; }
isClipComplex()146     bool isClipComplex() const { return !isClipEmpty() && (isClipAA() || !isClipRect()); }
147 
transform()148     const SkMatrix& transform() const { return mTransformStack.back().entry; }
149 
150     SkMatrix& transform();
151 
152     // For compat with existing HWUI Canvas interface
getMatrix(SkMatrix * outMatrix)153     void getMatrix(SkMatrix* outMatrix) const {
154         *outMatrix = transform();
155     }
156 
setMatrix(const SkMatrix & matrix)157     void setMatrix(const SkMatrix& matrix) {
158         transform() = matrix;
159     }
160 
concat(const SkMatrix & matrix)161     void concat(const SkMatrix& matrix) {
162         transform().preConcat(matrix);
163     }
164 
rotate(float degrees)165     void rotate(float degrees) {
166         SkMatrix m;
167         m.setRotate(degrees);
168         concat(m);
169     }
170 
scale(float sx,float sy)171     void scale(float sx, float sy) {
172         SkMatrix m;
173         m.setScale(sx, sy);
174         concat(m);
175     }
176 
skew(float sx,float sy)177     void skew(float sx, float sy) {
178         SkMatrix m;
179         m.setSkew(sx, sy);
180         concat(m);
181     }
182 
translate(float dx,float dy)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
189 template <typename CanvasOpReceiver>
190 class CanvasFrontend final : public CanvasStateHelper {
191 public:
192     template<class... Args>
CanvasFrontend(int width,int height,Args &&...args)193     CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height),
194             mReceiver(std::in_place, std::forward<Args>(args)...) { }
195 
196     void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) {
197         if (internalSave(flagsToSaveEntry(flags))) {
198             submit<CanvasOpType::Save>({});
199         }
200     }
201 
restore()202     void restore() {
203         if (internalRestore()) {
204             submit<CanvasOpType::Restore>({});
205         }
206     }
207 
208     template <CanvasOpType T>
draw(CanvasOp<T> && op)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 
receiver()232     const CanvasOpReceiver& receiver() const {
233         LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
234         return *mReceiver;
235     }
236 
finish()237     CanvasOpReceiver finish() {
238         auto ret = std::move(mReceiver.value());
239         mReceiver.reset();
240         return std::move(ret);
241     }
242 
243     template<class... Args>
reset(int newWidth,int newHeight,Args &&...args)244     void reset(int newWidth, int newHeight, Args&&... args) {
245         resetState(newWidth, newHeight);
246         mReceiver.emplace(std::forward<Args>(args)...);
247     }
248 
249 private:
250     std::optional<CanvasOpReceiver> mReceiver;
251 
252     template <CanvasOpType T>
submit(CanvasOp<T> && op)253     void submit(CanvasOp<T>&& op) {
254         LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
255         mReceiver->push_container(CanvasOpContainer(std::move(op), transform()));
256     }
257 };
258 
259 } // namespace android::uirenderer
260