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