1 /* 2 * Copyright 2020 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkSVGTextPriv_DEFINED 9 #define SkSVGTextPriv_DEFINED 10 11 #include "include/core/SkContourMeasure.h" 12 #include "include/core/SkFont.h" 13 #include "include/core/SkMatrix.h" 14 #include "include/core/SkPaint.h" 15 #include "include/core/SkPoint.h" 16 #include "include/core/SkRefCnt.h" 17 #include "include/core/SkTypes.h" 18 #include "include/private/base/SkTArray.h" 19 #include "include/private/base/SkTo.h" 20 #include "modules/skshaper/include/SkShaper.h" 21 #include "src/base/SkTLazy.h" 22 23 #include <cstddef> 24 #include <cstdint> 25 #include <functional> 26 #include <limits> 27 #include <memory> 28 #include <vector> 29 30 class SkSVGLengthContext; 31 class SkSVGRenderContext; 32 class SkSVGTextContainer; 33 class SkSVGTextPath; 34 class SkString; 35 class SkTextBlob; 36 enum class SkSVGXmlSpace; 37 struct SkRSXform; 38 39 // SkSVGTextContext is responsible for sequencing input text chars into "chunks". 40 // A single text chunk can span multiple structural elements (<text>, <tspan>, etc), 41 // and per [1] new chunks are emitted 42 // 43 // a) for each top level text element (<text>, <textPath>) 44 // b) whenever a character with an explicit absolute position is encountered 45 // 46 // The implementation queues shaped run data until a full text chunk is resolved, at which 47 // point we have enough information to perform final alignment and rendering. 48 // 49 // [1] https://www.w3.org/TR/SVG11/text.html#TextLayoutIntroduction 50 class SkSVGTextContext final : SkShaper::RunHandler { 51 public: 52 using ShapedTextCallback = std::function<void(const SkSVGRenderContext&, 53 const sk_sp<SkTextBlob>&, 54 const SkPaint*, 55 const SkPaint*)>; 56 57 // Helper for encoding optional positional attributes. 58 class PosAttrs { 59 public: 60 // TODO: rotate 61 enum Attr : size_t { 62 kX = 0, 63 kY = 1, 64 kDx = 2, 65 kDy = 3, 66 kRotate = 4, 67 }; 68 69 float operator[](Attr a) const { return fStorage[a]; } 70 float& operator[](Attr a) { return fStorage[a]; } 71 has(Attr a)72 bool has(Attr a) const { return fStorage[a] != kNone; } hasAny()73 bool hasAny() const { 74 return this->has(kX) 75 || this->has(kY) 76 || this->has(kDx) 77 || this->has(kDy) 78 || this->has(kRotate); 79 } 80 setImplicitRotate(bool imp)81 void setImplicitRotate(bool imp) { fImplicitRotate = imp; } isImplicitRotate()82 bool isImplicitRotate() const { return fImplicitRotate; } 83 84 private: 85 inline static constexpr auto kNone = std::numeric_limits<float>::infinity(); 86 87 float fStorage[5] = { kNone, kNone, kNone, kNone, kNone }; 88 bool fImplicitRotate = false; 89 }; 90 91 // Helper for cascading position attribute resolution (x, y, dx, dy, rotate) [1]: 92 // - each text position element can specify an arbitrary-length attribute array 93 // - for each character, we look up a given attribute first in its local attribute array, 94 // then in the ancestor chain (cascading/fallback) - and return the first value encountered. 95 // - the lookup is based on character index relative to the text content subtree 96 // (i.e. the index crosses chunk boundaries) 97 // 98 // [1] https://www.w3.org/TR/SVG11/text.html#TSpanElementXAttribute 99 class ScopedPosResolver { 100 public: 101 ScopedPosResolver(const SkSVGTextContainer&, const SkSVGLengthContext&, SkSVGTextContext*, 102 size_t); 103 104 ScopedPosResolver(const SkSVGTextContainer&, const SkSVGLengthContext&, SkSVGTextContext*); 105 106 ~ScopedPosResolver(); 107 108 PosAttrs resolve(size_t charIndex) const; 109 110 private: 111 SkSVGTextContext* fTextContext; 112 const ScopedPosResolver* fParent; // parent resolver (fallback) 113 const size_t fCharIndexOffset; // start index for the current resolver 114 const std::vector<float> fX, 115 fY, 116 fDx, 117 fDy; 118 const std::vector<float>& fRotate; 119 120 // cache for the last known index with explicit positioning 121 mutable size_t fLastPosIndex = std::numeric_limits<size_t>::max(); 122 123 }; 124 125 SkSVGTextContext(const SkSVGRenderContext&, 126 const ShapedTextCallback&, 127 const SkSVGTextPath* = nullptr); 128 ~SkSVGTextContext() override; 129 130 // Shape and queue codepoints for final alignment. 131 void shapeFragment(const SkString&, const SkSVGRenderContext&, SkSVGXmlSpace); 132 133 // Perform final adjustments and push shaped blobs to the callback. 134 void flushChunk(const SkSVGRenderContext& ctx); 135 getCallback()136 const ShapedTextCallback& getCallback() const { return fCallback; } 137 138 private: 139 struct PositionAdjustment { 140 SkVector offset; 141 float rotation; 142 }; 143 144 struct ShapeBuffer { 145 skia_private::STArray<128, char , true> fUtf8; 146 // per-utf8-char cumulative pos adjustments 147 skia_private::STArray<128, PositionAdjustment, true> fUtf8PosAdjust; 148 reserveShapeBuffer149 void reserve(size_t size) { 150 fUtf8.reserve_exact(fUtf8.size() + SkToInt(size)); 151 fUtf8PosAdjust.reserve_exact(fUtf8PosAdjust.size() + SkToInt(size)); 152 } 153 resetShapeBuffer154 void reset() { 155 fUtf8.clear(); 156 fUtf8PosAdjust.clear(); 157 } 158 159 void append(SkUnichar, PositionAdjustment); 160 }; 161 162 struct RunRec { 163 SkFont font; 164 std::unique_ptr<SkPaint> fillPaint, 165 strokePaint; 166 std::unique_ptr<SkGlyphID[]> glyphs; // filled by SkShaper 167 std::unique_ptr<SkPoint[]> glyphPos; // filled by SkShaper 168 std::unique_ptr<PositionAdjustment[]> glyhPosAdjust; // deferred positioning adjustments 169 size_t glyphCount; 170 SkVector advance; 171 }; 172 173 // Caches path information to accelerate position lookups. 174 class PathData { 175 public: 176 PathData(const SkSVGRenderContext&, const SkSVGTextPath&); 177 178 SkMatrix getMatrixAt(float offset) const; 179 length()180 float length() const { return fLength; } 181 182 private: 183 std::vector<sk_sp<SkContourMeasure>> fContours; 184 float fLength = 0; // total path length 185 }; 186 187 void shapePendingBuffer(const SkSVGRenderContext&, const SkFont&); 188 189 SkRSXform computeGlyphXform(SkGlyphID, const SkFont&, const SkPoint& glyph_pos, 190 const PositionAdjustment&) const; 191 192 // SkShaper callbacks beginLine()193 void beginLine() override {} runInfo(const RunInfo &)194 void runInfo(const RunInfo&) override {} commitRunInfo()195 void commitRunInfo() override {} 196 Buffer runBuffer(const RunInfo& ri) override; 197 void commitRunBuffer(const RunInfo& ri) override; 198 void commitLine() override; 199 200 // http://www.w3.org/TR/SVG11/text.html#TextLayout 201 const SkSVGRenderContext& fRenderContext; // original render context 202 const ShapedTextCallback& fCallback; 203 std::unique_ptr<SkShaper> fShaper; 204 std::vector<RunRec> fRuns; 205 const ScopedPosResolver* fPosResolver = nullptr; 206 std::unique_ptr<PathData> fPathData; 207 208 // shaper state 209 ShapeBuffer fShapeBuffer; 210 std::vector<uint32_t> fShapeClusterBuffer; 211 212 // chunk state 213 SkPoint fChunkPos = {0,0}; // current text chunk position 214 SkVector fChunkAdvance = {0,0}; // cumulative advance 215 float fChunkAlignmentFactor; // current chunk alignment 216 217 // tracks the global text subtree char index (cross chunks). Used for position resolution. 218 size_t fCurrentCharIndex = 0; 219 220 // cached for access from SkShaper callbacks. 221 SkTLazy<SkPaint> fCurrentFill; 222 SkTLazy<SkPaint> fCurrentStroke; 223 224 bool fPrevCharSpace = true; // WS filter state 225 bool fForcePrimitiveShaping = false; 226 }; 227 228 #endif // SkSVGTextPriv_DEFINED 229