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/Macros.h"
27 #include "minikin/MinikinFont.h"
28 #include "minikin/Range.h"
29 #include "minikin/U16StringPiece.h"
30 
31 namespace minikin {
32 
33 class Run {
34 public:
Run(const Range & range)35     Run(const Range& range) : mRange(range) {}
~Run()36     virtual ~Run() {}
37 
38     // Returns true if this run is RTL. Otherwise returns false.
39     virtual bool isRtl() const = 0;
40 
41     // Returns true if this run is a target of hyphenation. Otherwise return false.
42     virtual bool canHyphenate() const = 0;
43 
44     // Returns the locale list ID for this run.
45     virtual uint32_t getLocaleListId() const = 0;
46 
47     // Fills the each character's advances, extents and overhangs.
48     virtual void getMetrics(const U16StringPiece& text, float* advances, MinikinExtent* extents,
49                             LayoutPieces* piece) const = 0;
50 
51     virtual std::pair<float, MinikinRect> getBounds(const U16StringPiece& text, const Range& range,
52                                                     const LayoutPieces& pieces) const = 0;
53 
54     // Following two methods are only called when the implementation returns true for
55     // canHyphenate method.
56 
57     // Returns the paint pointer used for this run.
58     // Returns null if canHyphenate has not returned true.
getPaint()59     virtual const MinikinPaint* getPaint() const { return nullptr; }
60 
61     // Measures the hyphenation piece and fills each character's advances and overhangs.
measureHyphenPiece(const U16StringPiece &,const Range &,StartHyphenEdit,EndHyphenEdit,float *,LayoutPieces *)62     virtual float measureHyphenPiece(const U16StringPiece& /* text */,
63                                      const Range& /* hyphenPieceRange */,
64                                      StartHyphenEdit /* startHyphen */,
65                                      EndHyphenEdit /* endHyphen */, float* /* advances */,
66                                      LayoutPieces* /* pieces */) const {
67         return 0.0;
68     }
69 
getRange()70     inline const Range& getRange() const { return mRange; }
71 
72 protected:
73     const Range mRange;
74 };
75 
76 class StyleRun : public Run {
77 public:
StyleRun(const Range & range,MinikinPaint && paint,bool isRtl)78     StyleRun(const Range& range, MinikinPaint&& paint, bool isRtl)
79             : Run(range), mPaint(std::move(paint)), mIsRtl(isRtl) {}
80 
canHyphenate()81     bool canHyphenate() const override { return true; }
getLocaleListId()82     uint32_t getLocaleListId() const override { return mPaint.localeListId; }
isRtl()83     bool isRtl() const override { return mIsRtl; }
84 
getMetrics(const U16StringPiece & text,float * advances,MinikinExtent * extents,LayoutPieces * pieces)85     void getMetrics(const U16StringPiece& text, float* advances, MinikinExtent* extents,
86                     LayoutPieces* pieces) const override {
87         Bidi bidiFlag = mIsRtl ? Bidi::FORCE_RTL : Bidi::FORCE_LTR;
88         Layout::measureText(text, mRange, bidiFlag, mPaint, StartHyphenEdit::NO_EDIT,
89                             EndHyphenEdit::NO_EDIT, advances, extents, pieces);
90     }
91 
getBounds(const U16StringPiece & text,const Range & range,const LayoutPieces & pieces)92     std::pair<float, MinikinRect> getBounds(const U16StringPiece& text, const Range& range,
93                                             const LayoutPieces& pieces) const override {
94         Bidi bidiFlag = mIsRtl ? Bidi::FORCE_RTL : Bidi::FORCE_LTR;
95         return Layout::getBoundsWithPrecomputedPieces(text, range, bidiFlag, mPaint, pieces);
96     }
97 
getPaint()98     const MinikinPaint* getPaint() const override { return &mPaint; }
99 
measureHyphenPiece(const U16StringPiece & text,const Range & range,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen,float * advances,LayoutPieces * pieces)100     float measureHyphenPiece(const U16StringPiece& text, const Range& range,
101                              StartHyphenEdit startHyphen, EndHyphenEdit endHyphen, float* advances,
102                              LayoutPieces* pieces) const override {
103         Bidi bidiFlag = mIsRtl ? Bidi::FORCE_RTL : Bidi::FORCE_LTR;
104         return Layout::measureText(text, range, bidiFlag, mPaint, startHyphen, endHyphen, advances,
105                                    nullptr /* extent */, pieces);
106     }
107 
108 private:
109     MinikinPaint mPaint;
110     const bool mIsRtl;
111 };
112 
113 class ReplacementRun : public Run {
114 public:
ReplacementRun(const Range & range,float width,uint32_t localeListId)115     ReplacementRun(const Range& range, float width, uint32_t localeListId)
116             : Run(range), mWidth(width), mLocaleListId(localeListId) {}
117 
isRtl()118     bool isRtl() const { return false; }
canHyphenate()119     bool canHyphenate() const { return false; }
getLocaleListId()120     uint32_t getLocaleListId() const { return mLocaleListId; }
121 
getMetrics(const U16StringPiece &,float * advances,MinikinExtent *,LayoutPieces *)122     void getMetrics(const U16StringPiece& /* unused */, float* advances,
123                     MinikinExtent* /* unused */, LayoutPieces* /* pieces */) const override {
124         advances[0] = mWidth;
125         // TODO: Get the extents information from the caller.
126     }
127 
getBounds(const U16StringPiece &,const Range &,const LayoutPieces &)128     std::pair<float, MinikinRect> getBounds(const U16StringPiece& /* text */,
129                                             const Range& /* range */,
130                                             const LayoutPieces& /* pieces */) const override {
131         // Bounding Box is not used in replacement run.
132         return std::make_pair(mWidth, MinikinRect());
133     }
134 
135 private:
136     const float mWidth;
137     const uint32_t mLocaleListId;
138 };
139 
140 // Represents a hyphenation break point.
141 struct HyphenBreak {
142     // The break offset.
143     uint32_t offset;
144 
145     // The hyphenation type.
146     HyphenationType type;
147 
148     // The width of preceding piece after break at hyphenation point.
149     float first;
150 
151     // The width of following piece after break at hyphenation point.
152     float second;
153 
HyphenBreakHyphenBreak154     HyphenBreak(uint32_t offset, HyphenationType type, float first, float second)
155             : offset(offset), type(type), first(first), second(second) {}
156 };
157 
158 class MeasuredText {
159 public:
160     // Character widths.
161     std::vector<float> widths;
162 
163     // Font vertical extents for characters.
164     // TODO: Introduce compression for extents. Usually, this has the same values for all chars.
165     std::vector<MinikinExtent> extents;
166 
167     // Hyphenation points.
168     std::vector<HyphenBreak> hyphenBreaks;
169 
170     // The style information.
171     std::vector<std::unique_ptr<Run>> runs;
172 
173     // The copied layout pieces for construcing final layouts.
174     // TODO: Stop assigning width/extents if layout pieces are available for reducing memory impact.
175     LayoutPieces layoutPieces;
176 
getMemoryUsage()177     uint32_t getMemoryUsage() const {
178         return sizeof(float) * widths.size() + sizeof(MinikinExtent) * extents.size() +
179                sizeof(HyphenBreak) * hyphenBreaks.size() + layoutPieces.getMemoryUsage();
180     }
181 
182     void buildLayout(const U16StringPiece& textBuf, const Range& range, const MinikinPaint& paint,
183                      Bidi bidiFlag, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
184                      Layout* layout);
185     MinikinRect getBounds(const U16StringPiece& textBuf, const Range& range);
186 
187     MeasuredText(MeasuredText&&) = default;
188     MeasuredText& operator=(MeasuredText&&) = default;
189 
190     MINIKIN_PREVENT_COPY_AND_ASSIGN(MeasuredText);
191 
192 private:
193     friend class MeasuredTextBuilder;
194 
195     void measure(const U16StringPiece& textBuf, bool computeHyphenation, bool computeLayout);
196 
197     // Use MeasuredTextBuilder instead.
MeasuredText(const U16StringPiece & textBuf,std::vector<std::unique_ptr<Run>> && runs,bool computeHyphenation,bool computeLayout)198     MeasuredText(const U16StringPiece& textBuf, std::vector<std::unique_ptr<Run>>&& runs,
199                  bool computeHyphenation, bool computeLayout)
200             : widths(textBuf.size()), extents(textBuf.size()), runs(std::move(runs)) {
201         measure(textBuf, computeHyphenation, computeLayout);
202     }
203 };
204 
205 class MeasuredTextBuilder {
206 public:
MeasuredTextBuilder()207     MeasuredTextBuilder() {}
208 
addStyleRun(int32_t start,int32_t end,MinikinPaint && paint,bool isRtl)209     void addStyleRun(int32_t start, int32_t end, MinikinPaint&& paint, bool isRtl) {
210         mRuns.emplace_back(std::make_unique<StyleRun>(Range(start, end), std::move(paint), isRtl));
211     }
212 
addReplacementRun(int32_t start,int32_t end,float width,uint32_t localeListId)213     void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
214         mRuns.emplace_back(
215                 std::make_unique<ReplacementRun>(Range(start, end), width, localeListId));
216     }
217 
218     template <class T, typename... Args>
addCustomRun(Args &&...args)219     void addCustomRun(Args&&... args) {
220         mRuns.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
221     }
222 
build(const U16StringPiece & textBuf,bool computeHyphenation,bool computeLayout)223     std::unique_ptr<MeasuredText> build(const U16StringPiece& textBuf, bool computeHyphenation,
224                                         bool computeLayout) {
225         // Unable to use make_unique here since make_unique is not a friend of MeasuredText.
226         return std::unique_ptr<MeasuredText>(
227                 new MeasuredText(textBuf, std::move(mRuns), computeHyphenation, computeLayout));
228     }
229 
230     MINIKIN_PREVENT_COPY_ASSIGN_AND_MOVE(MeasuredTextBuilder);
231 
232 private:
233     std::vector<std::unique_ptr<Run>> mRuns;
234 };
235 
236 }  // namespace minikin
237 
238 #endif  // MINIKIN_MEASURED_TEXT_H
239