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