xref: /aosp_15_r20/external/skia/modules/svg/src/SkSVGTextPriv.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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