xref: /aosp_15_r20/frameworks/base/libs/hwui/canvas/CanvasFrontend.h (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2020 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #pragma once
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker // TODO: Can we get the dependencies scoped down more?
20*d57664e9SAndroid Build Coastguard Worker #include "CanvasOps.h"
21*d57664e9SAndroid Build Coastguard Worker #include "CanvasOpBuffer.h"
22*d57664e9SAndroid Build Coastguard Worker #include <SaveFlags.h>
23*d57664e9SAndroid Build Coastguard Worker 
24*d57664e9SAndroid Build Coastguard Worker #include <ui/FatVector.h>
25*d57664e9SAndroid Build Coastguard Worker 
26*d57664e9SAndroid Build Coastguard Worker #include <optional>
27*d57664e9SAndroid Build Coastguard Worker 
28*d57664e9SAndroid Build Coastguard Worker namespace android::uirenderer {
29*d57664e9SAndroid Build Coastguard Worker 
30*d57664e9SAndroid Build Coastguard Worker // Exists to avoid forcing all this common logic into the templated class
31*d57664e9SAndroid Build Coastguard Worker class CanvasStateHelper {
32*d57664e9SAndroid Build Coastguard Worker protected:
33*d57664e9SAndroid Build Coastguard Worker     CanvasStateHelper(int width, int height);
34*d57664e9SAndroid Build Coastguard Worker     ~CanvasStateHelper() = default;
35*d57664e9SAndroid Build Coastguard Worker 
36*d57664e9SAndroid Build Coastguard Worker     struct SaveEntry {
37*d57664e9SAndroid Build Coastguard Worker         bool clip : 1 = false;
38*d57664e9SAndroid Build Coastguard Worker         bool matrix : 1 = false;
39*d57664e9SAndroid Build Coastguard Worker         bool layer : 1 = false;
40*d57664e9SAndroid Build Coastguard Worker     };
41*d57664e9SAndroid Build Coastguard Worker 
42*d57664e9SAndroid Build Coastguard Worker     template <typename T>
43*d57664e9SAndroid Build Coastguard Worker     struct DeferredEntry {
44*d57664e9SAndroid Build Coastguard Worker         T entry;
45*d57664e9SAndroid Build Coastguard Worker         int deferredSaveCount = 0;
46*d57664e9SAndroid Build Coastguard Worker 
47*d57664e9SAndroid Build Coastguard Worker         DeferredEntry() = default;
DeferredEntryDeferredEntry48*d57664e9SAndroid Build Coastguard Worker         DeferredEntry(const T& t) : entry(t) {}
49*d57664e9SAndroid Build Coastguard Worker     };
50*d57664e9SAndroid Build Coastguard Worker 
51*d57664e9SAndroid Build Coastguard Worker     struct ConservativeClip {
52*d57664e9SAndroid Build Coastguard Worker         SkIRect bounds = SkIRect::MakeEmpty();
53*d57664e9SAndroid Build Coastguard Worker         bool rect = true;
54*d57664e9SAndroid Build Coastguard Worker         bool aa = false;
55*d57664e9SAndroid Build Coastguard Worker 
56*d57664e9SAndroid Build Coastguard Worker         bool quickReject(const SkMatrix& matrix, const SkRect& bounds) const;
57*d57664e9SAndroid Build Coastguard Worker 
58*d57664e9SAndroid Build Coastguard Worker         void apply(SkClipOp op, const SkMatrix& matrix, const SkRect& bounds, bool aa,
59*d57664e9SAndroid Build Coastguard Worker                    bool fillsBounds);
60*d57664e9SAndroid Build Coastguard Worker     };
61*d57664e9SAndroid Build Coastguard Worker 
saveEntryForLayer()62*d57664e9SAndroid Build Coastguard Worker     constexpr SaveEntry saveEntryForLayer() {
63*d57664e9SAndroid Build Coastguard Worker         return {
64*d57664e9SAndroid Build Coastguard Worker             .clip = true,
65*d57664e9SAndroid Build Coastguard Worker             .matrix = true,
66*d57664e9SAndroid Build Coastguard Worker             .layer = true,
67*d57664e9SAndroid Build Coastguard Worker         };
68*d57664e9SAndroid Build Coastguard Worker     }
69*d57664e9SAndroid Build Coastguard Worker 
flagsToSaveEntry(SaveFlags::Flags flags)70*d57664e9SAndroid Build Coastguard Worker     constexpr SaveEntry flagsToSaveEntry(SaveFlags::Flags flags) {
71*d57664e9SAndroid Build Coastguard Worker         return SaveEntry {
72*d57664e9SAndroid Build Coastguard Worker             .clip = static_cast<bool>(flags & SaveFlags::Clip),
73*d57664e9SAndroid Build Coastguard Worker             .matrix = static_cast<bool>(flags & SaveFlags::Matrix),
74*d57664e9SAndroid Build Coastguard Worker             .layer = false
75*d57664e9SAndroid Build Coastguard Worker         };
76*d57664e9SAndroid Build Coastguard Worker     }
77*d57664e9SAndroid Build Coastguard Worker 
78*d57664e9SAndroid Build Coastguard Worker     bool internalSave(SaveEntry saveEntry);
79*d57664e9SAndroid Build Coastguard Worker 
internalSaveLayer(const SkCanvas::SaveLayerRec & layerRec)80*d57664e9SAndroid Build Coastguard Worker     void internalSaveLayer(const SkCanvas::SaveLayerRec& layerRec) {
81*d57664e9SAndroid Build Coastguard Worker         internalSave({
82*d57664e9SAndroid Build Coastguard Worker             .clip = true,
83*d57664e9SAndroid Build Coastguard Worker             .matrix = true,
84*d57664e9SAndroid Build Coastguard Worker             .layer = true
85*d57664e9SAndroid Build Coastguard Worker         });
86*d57664e9SAndroid Build Coastguard Worker         internalClipRect(*layerRec.fBounds, SkClipOp::kIntersect);
87*d57664e9SAndroid Build Coastguard Worker     }
88*d57664e9SAndroid Build Coastguard Worker 
89*d57664e9SAndroid Build Coastguard Worker     bool internalRestore();
90*d57664e9SAndroid Build Coastguard Worker 
91*d57664e9SAndroid Build Coastguard Worker     void internalClipRect(const SkRect& rect, SkClipOp op);
92*d57664e9SAndroid Build Coastguard Worker     void internalClipPath(const SkPath& path, SkClipOp op);
93*d57664e9SAndroid Build Coastguard Worker 
94*d57664e9SAndroid Build Coastguard Worker     // The canvas' clip will never expand beyond these bounds since intersect
95*d57664e9SAndroid Build Coastguard Worker     // and difference operations only subtract pixels.
96*d57664e9SAndroid Build Coastguard Worker     SkIRect mInitialBounds;
97*d57664e9SAndroid Build Coastguard Worker     // Every save() gets a SaveEntry to track what needs to be restored.
98*d57664e9SAndroid Build Coastguard Worker     FatVector<SaveEntry, 6> mSaveStack;
99*d57664e9SAndroid Build Coastguard Worker     // Transform and clip entries record a deferred save count and do not
100*d57664e9SAndroid Build Coastguard Worker     // make a new entry until that particular state is modified.
101*d57664e9SAndroid Build Coastguard Worker     FatVector<DeferredEntry<SkMatrix>, 6> mTransformStack;
102*d57664e9SAndroid Build Coastguard Worker     FatVector<DeferredEntry<ConservativeClip>, 6> mClipStack;
103*d57664e9SAndroid Build Coastguard Worker 
clip()104*d57664e9SAndroid Build Coastguard Worker     const ConservativeClip& clip() const { return mClipStack.back().entry; }
105*d57664e9SAndroid Build Coastguard Worker 
106*d57664e9SAndroid Build Coastguard Worker     ConservativeClip& clip();
107*d57664e9SAndroid Build Coastguard Worker 
108*d57664e9SAndroid Build Coastguard Worker     void resetState(int width, int height);
109*d57664e9SAndroid Build Coastguard Worker 
110*d57664e9SAndroid Build Coastguard Worker     // Stack manipulation for transform and clip stacks
111*d57664e9SAndroid Build Coastguard Worker     template <typename T, size_t N>
pushEntry(FatVector<DeferredEntry<T>,N> * stack)112*d57664e9SAndroid Build Coastguard Worker     void pushEntry(FatVector<DeferredEntry<T>, N>* stack) {
113*d57664e9SAndroid Build Coastguard Worker         stack->back().deferredSaveCount += 1;
114*d57664e9SAndroid Build Coastguard Worker     }
115*d57664e9SAndroid Build Coastguard Worker 
116*d57664e9SAndroid Build Coastguard Worker     template <typename T, size_t N>
popEntry(FatVector<DeferredEntry<T>,N> * stack)117*d57664e9SAndroid Build Coastguard Worker     void popEntry(FatVector<DeferredEntry<T>, N>* stack) {
118*d57664e9SAndroid Build Coastguard Worker         if (!(stack->back().deferredSaveCount--)) {
119*d57664e9SAndroid Build Coastguard Worker             stack->pop_back();
120*d57664e9SAndroid Build Coastguard Worker         }
121*d57664e9SAndroid Build Coastguard Worker     }
122*d57664e9SAndroid Build Coastguard Worker 
123*d57664e9SAndroid Build Coastguard Worker     template <typename T, size_t N>
writableEntry(FatVector<DeferredEntry<T>,N> * stack)124*d57664e9SAndroid Build Coastguard Worker     T& writableEntry(FatVector<DeferredEntry<T>, N>* stack) {
125*d57664e9SAndroid Build Coastguard Worker         DeferredEntry<T>& back = stack->back();
126*d57664e9SAndroid Build Coastguard Worker         if (back.deferredSaveCount == 0) {
127*d57664e9SAndroid Build Coastguard Worker             return back.entry;
128*d57664e9SAndroid Build Coastguard Worker         } else {
129*d57664e9SAndroid Build Coastguard Worker             back.deferredSaveCount -= 1;
130*d57664e9SAndroid Build Coastguard Worker             // saved in case references move when re-allocating vector storage
131*d57664e9SAndroid Build Coastguard Worker             T state = back.entry;
132*d57664e9SAndroid Build Coastguard Worker             return stack->emplace_back(state).entry;
133*d57664e9SAndroid Build Coastguard Worker         }
134*d57664e9SAndroid Build Coastguard Worker     }
135*d57664e9SAndroid Build Coastguard Worker 
136*d57664e9SAndroid Build Coastguard Worker public:
saveCount()137*d57664e9SAndroid Build Coastguard Worker     int saveCount() const { return mSaveStack.size(); }
138*d57664e9SAndroid Build Coastguard Worker 
139*d57664e9SAndroid Build Coastguard Worker     SkRect getClipBounds() const;
140*d57664e9SAndroid Build Coastguard Worker     bool quickRejectRect(float left, float top, float right, float bottom) const;
141*d57664e9SAndroid Build Coastguard Worker     bool quickRejectPath(const SkPath& path) const;
142*d57664e9SAndroid Build Coastguard Worker 
isClipAA()143*d57664e9SAndroid Build Coastguard Worker     bool isClipAA() const { return clip().aa; }
isClipEmpty()144*d57664e9SAndroid Build Coastguard Worker     bool isClipEmpty() const { return clip().bounds.isEmpty(); }
isClipRect()145*d57664e9SAndroid Build Coastguard Worker     bool isClipRect() const { return clip().rect; }
isClipComplex()146*d57664e9SAndroid Build Coastguard Worker     bool isClipComplex() const { return !isClipEmpty() && (isClipAA() || !isClipRect()); }
147*d57664e9SAndroid Build Coastguard Worker 
transform()148*d57664e9SAndroid Build Coastguard Worker     const SkMatrix& transform() const { return mTransformStack.back().entry; }
149*d57664e9SAndroid Build Coastguard Worker 
150*d57664e9SAndroid Build Coastguard Worker     SkMatrix& transform();
151*d57664e9SAndroid Build Coastguard Worker 
152*d57664e9SAndroid Build Coastguard Worker     // For compat with existing HWUI Canvas interface
getMatrix(SkMatrix * outMatrix)153*d57664e9SAndroid Build Coastguard Worker     void getMatrix(SkMatrix* outMatrix) const {
154*d57664e9SAndroid Build Coastguard Worker         *outMatrix = transform();
155*d57664e9SAndroid Build Coastguard Worker     }
156*d57664e9SAndroid Build Coastguard Worker 
setMatrix(const SkMatrix & matrix)157*d57664e9SAndroid Build Coastguard Worker     void setMatrix(const SkMatrix& matrix) {
158*d57664e9SAndroid Build Coastguard Worker         transform() = matrix;
159*d57664e9SAndroid Build Coastguard Worker     }
160*d57664e9SAndroid Build Coastguard Worker 
concat(const SkMatrix & matrix)161*d57664e9SAndroid Build Coastguard Worker     void concat(const SkMatrix& matrix) {
162*d57664e9SAndroid Build Coastguard Worker         transform().preConcat(matrix);
163*d57664e9SAndroid Build Coastguard Worker     }
164*d57664e9SAndroid Build Coastguard Worker 
rotate(float degrees)165*d57664e9SAndroid Build Coastguard Worker     void rotate(float degrees) {
166*d57664e9SAndroid Build Coastguard Worker         SkMatrix m;
167*d57664e9SAndroid Build Coastguard Worker         m.setRotate(degrees);
168*d57664e9SAndroid Build Coastguard Worker         concat(m);
169*d57664e9SAndroid Build Coastguard Worker     }
170*d57664e9SAndroid Build Coastguard Worker 
scale(float sx,float sy)171*d57664e9SAndroid Build Coastguard Worker     void scale(float sx, float sy) {
172*d57664e9SAndroid Build Coastguard Worker         SkMatrix m;
173*d57664e9SAndroid Build Coastguard Worker         m.setScale(sx, sy);
174*d57664e9SAndroid Build Coastguard Worker         concat(m);
175*d57664e9SAndroid Build Coastguard Worker     }
176*d57664e9SAndroid Build Coastguard Worker 
skew(float sx,float sy)177*d57664e9SAndroid Build Coastguard Worker     void skew(float sx, float sy) {
178*d57664e9SAndroid Build Coastguard Worker         SkMatrix m;
179*d57664e9SAndroid Build Coastguard Worker         m.setSkew(sx, sy);
180*d57664e9SAndroid Build Coastguard Worker         concat(m);
181*d57664e9SAndroid Build Coastguard Worker     }
182*d57664e9SAndroid Build Coastguard Worker 
translate(float dx,float dy)183*d57664e9SAndroid Build Coastguard Worker     void translate(float dx, float dy) {
184*d57664e9SAndroid Build Coastguard Worker         transform().preTranslate(dx, dy);
185*d57664e9SAndroid Build Coastguard Worker     }
186*d57664e9SAndroid Build Coastguard Worker };
187*d57664e9SAndroid Build Coastguard Worker 
188*d57664e9SAndroid Build Coastguard Worker // Front-end canvas that handles queries, up-front state, and produces CanvasOp<> output downstream
189*d57664e9SAndroid Build Coastguard Worker template <typename CanvasOpReceiver>
190*d57664e9SAndroid Build Coastguard Worker class CanvasFrontend final : public CanvasStateHelper {
191*d57664e9SAndroid Build Coastguard Worker public:
192*d57664e9SAndroid Build Coastguard Worker     template<class... Args>
CanvasFrontend(int width,int height,Args &&...args)193*d57664e9SAndroid Build Coastguard Worker     CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height),
194*d57664e9SAndroid Build Coastguard Worker             mReceiver(std::in_place, std::forward<Args>(args)...) { }
195*d57664e9SAndroid Build Coastguard Worker 
196*d57664e9SAndroid Build Coastguard Worker     void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) {
197*d57664e9SAndroid Build Coastguard Worker         if (internalSave(flagsToSaveEntry(flags))) {
198*d57664e9SAndroid Build Coastguard Worker             submit<CanvasOpType::Save>({});
199*d57664e9SAndroid Build Coastguard Worker         }
200*d57664e9SAndroid Build Coastguard Worker     }
201*d57664e9SAndroid Build Coastguard Worker 
restore()202*d57664e9SAndroid Build Coastguard Worker     void restore() {
203*d57664e9SAndroid Build Coastguard Worker         if (internalRestore()) {
204*d57664e9SAndroid Build Coastguard Worker             submit<CanvasOpType::Restore>({});
205*d57664e9SAndroid Build Coastguard Worker         }
206*d57664e9SAndroid Build Coastguard Worker     }
207*d57664e9SAndroid Build Coastguard Worker 
208*d57664e9SAndroid Build Coastguard Worker     template <CanvasOpType T>
draw(CanvasOp<T> && op)209*d57664e9SAndroid Build Coastguard Worker     void draw(CanvasOp<T>&& op) {
210*d57664e9SAndroid Build Coastguard Worker         // The front-end requires going through certain front-doors, which these aren't.
211*d57664e9SAndroid Build Coastguard Worker         static_assert(T != CanvasOpType::Save, "Must use CanvasFrontend::save() call instead");
212*d57664e9SAndroid Build Coastguard Worker         static_assert(T != CanvasOpType::Restore, "Must use CanvasFrontend::restore() call instead");
213*d57664e9SAndroid Build Coastguard Worker 
214*d57664e9SAndroid Build Coastguard Worker         if constexpr (T == CanvasOpType::SaveLayer) {
215*d57664e9SAndroid Build Coastguard Worker             internalSaveLayer(op.saveLayerRec);
216*d57664e9SAndroid Build Coastguard Worker         }
217*d57664e9SAndroid Build Coastguard Worker         if constexpr (T == CanvasOpType::SaveBehind) {
218*d57664e9SAndroid Build Coastguard Worker             // Don't use internalSaveLayer as this doesn't apply clipping, it's a "regular" save
219*d57664e9SAndroid Build Coastguard Worker             // But we do want to flag it as a layer, such that restore is Definitely Required
220*d57664e9SAndroid Build Coastguard Worker             internalSave(saveEntryForLayer());
221*d57664e9SAndroid Build Coastguard Worker         }
222*d57664e9SAndroid Build Coastguard Worker         if constexpr (T == CanvasOpType::ClipRect) {
223*d57664e9SAndroid Build Coastguard Worker             internalClipRect(op.rect, op.op);
224*d57664e9SAndroid Build Coastguard Worker         }
225*d57664e9SAndroid Build Coastguard Worker         if constexpr (T == CanvasOpType::ClipPath) {
226*d57664e9SAndroid Build Coastguard Worker             internalClipPath(op.path, op.op);
227*d57664e9SAndroid Build Coastguard Worker         }
228*d57664e9SAndroid Build Coastguard Worker 
229*d57664e9SAndroid Build Coastguard Worker         submit(std::move(op));
230*d57664e9SAndroid Build Coastguard Worker     }
231*d57664e9SAndroid Build Coastguard Worker 
receiver()232*d57664e9SAndroid Build Coastguard Worker     const CanvasOpReceiver& receiver() const {
233*d57664e9SAndroid Build Coastguard Worker         LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
234*d57664e9SAndroid Build Coastguard Worker         return *mReceiver;
235*d57664e9SAndroid Build Coastguard Worker     }
236*d57664e9SAndroid Build Coastguard Worker 
finish()237*d57664e9SAndroid Build Coastguard Worker     CanvasOpReceiver finish() {
238*d57664e9SAndroid Build Coastguard Worker         auto ret = std::move(mReceiver.value());
239*d57664e9SAndroid Build Coastguard Worker         mReceiver.reset();
240*d57664e9SAndroid Build Coastguard Worker         return std::move(ret);
241*d57664e9SAndroid Build Coastguard Worker     }
242*d57664e9SAndroid Build Coastguard Worker 
243*d57664e9SAndroid Build Coastguard Worker     template<class... Args>
reset(int newWidth,int newHeight,Args &&...args)244*d57664e9SAndroid Build Coastguard Worker     void reset(int newWidth, int newHeight, Args&&... args) {
245*d57664e9SAndroid Build Coastguard Worker         resetState(newWidth, newHeight);
246*d57664e9SAndroid Build Coastguard Worker         mReceiver.emplace(std::forward<Args>(args)...);
247*d57664e9SAndroid Build Coastguard Worker     }
248*d57664e9SAndroid Build Coastguard Worker 
249*d57664e9SAndroid Build Coastguard Worker private:
250*d57664e9SAndroid Build Coastguard Worker     std::optional<CanvasOpReceiver> mReceiver;
251*d57664e9SAndroid Build Coastguard Worker 
252*d57664e9SAndroid Build Coastguard Worker     template <CanvasOpType T>
submit(CanvasOp<T> && op)253*d57664e9SAndroid Build Coastguard Worker     void submit(CanvasOp<T>&& op) {
254*d57664e9SAndroid Build Coastguard Worker         LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
255*d57664e9SAndroid Build Coastguard Worker         mReceiver->push_container(CanvasOpContainer(std::move(op), transform()));
256*d57664e9SAndroid Build Coastguard Worker     }
257*d57664e9SAndroid Build Coastguard Worker };
258*d57664e9SAndroid Build Coastguard Worker 
259*d57664e9SAndroid Build Coastguard Worker } // namespace android::uirenderer
260