• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef MINIKIN_MEASURED_TEXT_H
18 #define MINIKIN_MEASURED_TEXT_H
19 
20 #include <deque>
21 #include <vector>
22 
23 #include "minikin/FontCollection.h"
24 #include "minikin/Layout.h"
25 #include "minikin/LayoutPieces.h"
26 #include "minikin/LineBreakStyle.h"
27 #include "minikin/Macros.h"
28 #include "minikin/MinikinFont.h"
29 #include "minikin/Range.h"
30 #include "minikin/U16StringPiece.h"
31 
32 namespace minikin {
33 
34 // Structs that of line metrics information.
35 struct LineMetrics {
LineMetricsLineMetrics36     LineMetrics() : advance(0) {}
LineMetricsLineMetrics37     LineMetrics(const MinikinExtent& extent, const MinikinRect& bounds, float advance)
38             : extent(extent), bounds(bounds), advance(advance) {}
39 
appendLineMetrics40     void append(const LineMetrics& metrics) {
41         append(metrics.extent, metrics.bounds, metrics.advance);
42     }
43 
appendLineMetrics44     void append(const MinikinExtent& nextExtent, const MinikinRect& nextBounds, float nextAdvance) {
45         extent.extendBy(nextExtent);
46         bounds.join(nextBounds, advance, 0);
47         advance += nextAdvance;
48     }
49 
50     MinikinExtent extent;
51     MinikinRect bounds;
52     float advance;
53 };
54 
55 class Run {
56 public:
Run(const Range & range)57     Run(const Range& range) : mRange(range) {}
~Run()58     virtual ~Run() {}
59 
60     // Returns true if this run is RTL. Otherwise returns false.
61     virtual bool isRtl() const = 0;
62 
63     // Returns true if this run can be broken into multiple pieces for line breaking.
64     virtual bool canBreak() const = 0;
65 
66     // Returns true if this run can be hyphenated.
67     virtual bool canHyphenate() const = 0;
68 
69     // Return the line break style(lb) for this run.
70     virtual LineBreakStyle lineBreakStyle() const = 0;
71 
72     // Return the line break word style(lw) for this run.
73     virtual LineBreakWordStyle lineBreakWordStyle() const = 0;
74 
75     // Returns the locale list ID for this run.
76     virtual uint32_t getLocaleListId() const = 0;
77 
78     // Fills the each character's advances, extents and overhangs.
79     virtual void getMetrics(const U16StringPiece& text, std::vector<float>* advances,
80                             std::vector<uint8_t>* flags, LayoutPieces* precomputed,
81                             bool boundsCalculation, LayoutPieces* outPieces) const = 0;
82 
83     virtual std::pair<float, MinikinRect> getBounds(const U16StringPiece& text, const Range& range,
84                                                     const LayoutPieces& pieces) const = 0;
85     virtual MinikinExtent getExtent(const U16StringPiece& text, const Range& range,
86                                     const LayoutPieces& pieces) const = 0;
87 
88     virtual LineMetrics getLineMetrics(const U16StringPiece& text, const Range& range,
89                                        const LayoutPieces& pieces) const = 0;
90 
91     virtual void appendLayout(const U16StringPiece& text, const Range& range,
92                               const Range& contextRange, const LayoutPieces& pieces,
93                               const MinikinPaint& paint, uint32_t outOrigin,
94                               StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
95                               Layout* outLayout) const = 0;
96 
97     virtual float measureText(const U16StringPiece& text) const = 0;
98 
99     // Following two methods are only called when the implementation returns true for
100     // canBreak method.
101 
102     // Returns the paint pointer used for this run.
103     // Returns null if canBreak has not returned true.
getPaint()104     virtual const MinikinPaint* getPaint() const { return nullptr; }
105 
106     // Measures the hyphenation piece and fills each character's advances and overhangs.
measureHyphenPiece(const U16StringPiece &,const Range &,StartHyphenEdit,EndHyphenEdit,LayoutPieces *)107     virtual float measureHyphenPiece(const U16StringPiece& /* text */,
108                                      const Range& /* hyphenPieceRange */,
109                                      StartHyphenEdit /* startHyphen */,
110                                      EndHyphenEdit /* endHyphen */,
111                                      LayoutPieces* /* pieces */) const {
112         return 0.0;
113     }
114 
getRange()115     inline const Range& getRange() const { return mRange; }
116 
getLetterSpacingInPx()117     inline float getLetterSpacingInPx() const {
118         const MinikinPaint* paint = getPaint();
119         if (paint == nullptr) {
120             return 0;
121         } else {
122             return paint->getLetterSpacingInPx();
123         }
124     }
125 
126 protected:
127     const Range mRange;
128 };
129 
130 class StyleRun : public Run {
131 public:
StyleRun(const Range & range,MinikinPaint && paint,int lineBreakStyle,int lineBreakWordStyle,bool hyphenation,bool isRtl)132     StyleRun(const Range& range, MinikinPaint&& paint, int lineBreakStyle, int lineBreakWordStyle,
133              bool hyphenation, bool isRtl)
134             : Run(range),
135               mPaint(std::move(paint)),
136               mLineBreakStyle(lineBreakStyle),
137               mLineBreakWordStyle(lineBreakWordStyle),
138               mHyphenation(hyphenation),
139               mIsRtl(isRtl) {}
140 
canBreak()141     bool canBreak() const override { return true; }
lineBreakStyle()142     LineBreakStyle lineBreakStyle() const override {
143         return static_cast<LineBreakStyle>(mLineBreakStyle);
144     }
lineBreakWordStyle()145     LineBreakWordStyle lineBreakWordStyle() const override {
146         return static_cast<LineBreakWordStyle>(mLineBreakWordStyle);
147     }
canHyphenate()148     bool canHyphenate() const override { return mHyphenation; }
getLocaleListId()149     uint32_t getLocaleListId() const override { return mPaint.localeListId; }
isRtl()150     bool isRtl() const override { return mIsRtl; }
151 
152     void getMetrics(const U16StringPiece& text, std::vector<float>* advances,
153                     std::vector<uint8_t>* flags, LayoutPieces* precomputed, bool boundsCalculation,
154                     LayoutPieces* outPieces) const override;
155 
156     std::pair<float, MinikinRect> getBounds(const U16StringPiece& text, const Range& range,
157                                             const LayoutPieces& pieces) const override;
158 
159     MinikinExtent getExtent(const U16StringPiece& text, const Range& range,
160                             const LayoutPieces& pieces) const override;
161 
162     LineMetrics getLineMetrics(const U16StringPiece& text, const Range& range,
163                                const LayoutPieces& pieces) const override;
164 
165     void appendLayout(const U16StringPiece& text, const Range& range, const Range& contextRange,
166                       const LayoutPieces& pieces, const MinikinPaint& paint, uint32_t outOrigin,
167                       StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
168                       Layout* outLayout) const override;
169 
getPaint()170     const MinikinPaint* getPaint() const override { return &mPaint; }
171 
172     float measureHyphenPiece(const U16StringPiece& text, const Range& range,
173                              StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
174                              LayoutPieces* pieces) const override;
175     float measureText(const U16StringPiece& text) const;
176 
177 private:
178     MinikinPaint mPaint;
179     int mLineBreakStyle;
180     int mLineBreakWordStyle;
181     const bool mHyphenation;
182     const bool mIsRtl;
183 };
184 
185 class ReplacementRun : public Run {
186 public:
ReplacementRun(const Range & range,float width,uint32_t localeListId)187     ReplacementRun(const Range& range, float width, uint32_t localeListId)
188             : Run(range), mWidth(width), mLocaleListId(localeListId) {}
189 
isRtl()190     bool isRtl() const { return false; }
canBreak()191     bool canBreak() const { return false; }
canHyphenate()192     bool canHyphenate() const { return false; }
lineBreakStyle()193     LineBreakStyle lineBreakStyle() const override { return LineBreakStyle::None; }
lineBreakWordStyle()194     LineBreakWordStyle lineBreakWordStyle() const override { return LineBreakWordStyle::None; }
getLocaleListId()195     uint32_t getLocaleListId() const { return mLocaleListId; }
196 
getMetrics(const U16StringPiece &,std::vector<float> * advances,std::vector<uint8_t> *,LayoutPieces *,bool,LayoutPieces *)197     void getMetrics(const U16StringPiece& /* text */, std::vector<float>* advances,
198                     std::vector<uint8_t>* /*flags*/, LayoutPieces* /* precomputed */, bool,
199                     LayoutPieces* /* outPieces */) const override {
200         (*advances)[mRange.getStart()] = mWidth;
201         // TODO: Get the extents information from the caller.
202     }
203 
getBounds(const U16StringPiece &,const Range &,const LayoutPieces &)204     std::pair<float, MinikinRect> getBounds(const U16StringPiece& /* text */,
205                                             const Range& /* range */,
206                                             const LayoutPieces& /* pieces */) const override {
207         // Bounding Box is not used in replacement run.
208         return std::make_pair(mWidth, MinikinRect());
209     }
210 
getExtent(const U16StringPiece &,const Range &,const LayoutPieces &)211     MinikinExtent getExtent(const U16StringPiece& /* text */, const Range& /* range */,
212                             const LayoutPieces& /* pieces */) const override {
213         return MinikinExtent();
214     }
215 
getLineMetrics(const U16StringPiece &,const Range &,const LayoutPieces &)216     LineMetrics getLineMetrics(const U16StringPiece& /*text*/, const Range& /*range*/,
217                                const LayoutPieces& /*pieces*/) const override {
218         return LineMetrics();
219     }
220 
appendLayout(const U16StringPiece &,const Range &,const Range &,const LayoutPieces &,const MinikinPaint &,uint32_t,StartHyphenEdit,EndHyphenEdit,Layout *)221     void appendLayout(const U16StringPiece& /* text */, const Range& /* range */,
222                       const Range& /* contextRange */, const LayoutPieces& /* pieces */,
223                       const MinikinPaint& /* paint */, uint32_t /* outOrigin */,
224                       StartHyphenEdit /* startHyphen */, EndHyphenEdit /* endHyphen */,
225                       Layout* /* outLayout*/) const override {}
226 
measureText(const U16StringPiece &)227     float measureText(const U16StringPiece&) const { return 0; }
228 
229 private:
230     const float mWidth;
231     const uint32_t mLocaleListId;
232 };
233 
234 // Represents a hyphenation break point.
235 struct HyphenBreak {
236     // The break offset.
237     uint32_t offset;
238 
239     // The hyphenation type.
240     HyphenationType type;
241 
242     // The width of preceding piece after break at hyphenation point.
243     float first;
244 
245     // The width of following piece after break at hyphenation point.
246     float second;
247 
HyphenBreakHyphenBreak248     HyphenBreak(uint32_t offset, HyphenationType type, float first, float second)
249             : offset(offset), type(type), first(first), second(second) {}
250 };
251 
252 class MeasuredText {
253 public:
254     // Character widths.
255     std::vector<float> widths;
256 
257     // Hyphenation points.
258     std::vector<HyphenBreak> hyphenBreaks;
259 
260     // The style information.
261     std::vector<std::unique_ptr<Run>> runs;
262 
263     // Per character flags.
264     // The loweset bit represents that that character *may* have overhang. If this bit is not set,
265     // the character doesn't have overhang. If this bit is set, the character *may* have overhang.
266     // This information is used for determining using the bounding box based line breaking.
267     static constexpr uint8_t MAY_OVERHANG_BIT = 0b0000'0001;
268     std::vector<uint8_t> flags;
269 
270     // The copied layout pieces for construcing final layouts.
271     // TODO: Stop assigning width/extents if layout pieces are available for reducing memory impact.
272     LayoutPieces layoutPieces;
273 
hasOverhang(const Range & range)274     bool hasOverhang(const Range& range) const {
275         // Heuristics: Check first 5 and last 5 characters and treat there is overhang if at least
276         // one character has overhang.
277         constexpr uint32_t CHARS_TO_DUMPER = 5;
278 
279         if (range.getLength() < CHARS_TO_DUMPER * 2) {
280             for (uint32_t i : range) {
281                 if ((flags[i] & MAY_OVERHANG_BIT) == MAY_OVERHANG_BIT) {
282                     return true;
283                 }
284             }
285         } else {
286             Range first = Range(range.getStart(), range.getStart() + CHARS_TO_DUMPER);
287             Range last = Range(range.getEnd() - CHARS_TO_DUMPER, range.getEnd());
288             for (uint32_t i : first) {
289                 if ((flags[i] & MAY_OVERHANG_BIT) == MAY_OVERHANG_BIT) {
290                     return true;
291                 }
292             }
293             for (uint32_t i : last) {
294                 if ((flags[i] & MAY_OVERHANG_BIT) == MAY_OVERHANG_BIT) {
295                     return true;
296                 }
297             }
298         }
299         return false;
300     }
301 
302     uint32_t getMemoryUsage() const {
303         return sizeof(float) * widths.size() + sizeof(HyphenBreak) * hyphenBreaks.size() +
304                layoutPieces.getMemoryUsage();
305     }
306 
307     Layout buildLayout(const U16StringPiece& textBuf, const Range& range, const Range& contextRange,
308                        const MinikinPaint& paint, StartHyphenEdit startHyphen,
309                        EndHyphenEdit endHyphen);
310     MinikinRect getBounds(const U16StringPiece& textBuf, const Range& range) const;
311     MinikinExtent getExtent(const U16StringPiece& textBuf, const Range& range) const;
312     LineMetrics getLineMetrics(const U16StringPiece& textBuf, const Range& range) const;
313 
314     MeasuredText(MeasuredText&&) = default;
315     MeasuredText& operator=(MeasuredText&&) = default;
316 
317     MINIKIN_PREVENT_COPY_AND_ASSIGN(MeasuredText);
318 
319 private:
320     friend class MeasuredTextBuilder;
321 
322     void measure(const U16StringPiece& textBuf, bool computeHyphenation, bool computeLayout,
323                  bool computeBounds, bool ignoreHyphenKerning, MeasuredText* hint);
324 
325     // Use MeasuredTextBuilder instead.
326     MeasuredText(const U16StringPiece& textBuf, std::vector<std::unique_ptr<Run>>&& runs,
327                  bool computeHyphenation, bool computeLayout, bool computeBounds,
328                  bool ignoreHyphenKerning, MeasuredText* hint)
329             : widths(textBuf.size()), runs(std::move(runs)), flags(textBuf.size(), 0) {
330         measure(textBuf, computeHyphenation, computeLayout, computeBounds, ignoreHyphenKerning,
331                 hint);
332     }
333 };
334 
335 class MeasuredTextBuilder {
336 public:
337     MeasuredTextBuilder() {}
338 
339     void addStyleRun(int32_t start, int32_t end, MinikinPaint&& paint, int lineBreakStyle,
340                      int lineBreakWordStyle, bool hyphenation, bool isRtl) {
341         mRuns.emplace_back(std::make_unique<StyleRun>(Range(start, end), std::move(paint),
342                                                       lineBreakStyle, lineBreakWordStyle,
343                                                       hyphenation, isRtl));
344     }
345 
346     void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
347         mRuns.emplace_back(
348                 std::make_unique<ReplacementRun>(Range(start, end), width, localeListId));
349     }
350 
351     template <class T, typename... Args>
352     void addCustomRun(Args&&... args) {
353         mRuns.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
354     }
355 
356     std::unique_ptr<MeasuredText> build(const U16StringPiece& textBuf, bool computeHyphenation,
357                                         bool computeLayout, bool ignoreHyphenKerning,
358                                         MeasuredText* hint) {
359         return build(textBuf, computeHyphenation, computeLayout, false, ignoreHyphenKerning, hint);
360     }
361 
362     std::unique_ptr<MeasuredText> build(const U16StringPiece& textBuf, bool computeHyphenation,
363                                         bool computeLayout, bool computeBounds,
364                                         bool ignoreHyphenKerning, MeasuredText* hint) {
365         // Unable to use make_unique here since make_unique is not a friend of MeasuredText.
366         return std::unique_ptr<MeasuredText>(
367                 new MeasuredText(textBuf, std::move(mRuns), computeHyphenation, computeLayout,
368                                  computeBounds, ignoreHyphenKerning, hint));
369     }
370 
371     MINIKIN_PREVENT_COPY_ASSIGN_AND_MOVE(MeasuredTextBuilder);
372 
373 private:
374     std::vector<std::unique_ptr<Run>> mRuns;
375 };
376 
377 }  // namespace minikin
378 
379 #endif  // MINIKIN_MEASURED_TEXT_H
380