• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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