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