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