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