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