1 /*
2 * Copyright (C) 2013 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 #include "minikin/Layout.h"
18
19 #include <hb-icu.h>
20 #include <hb-ot.h>
21 #include <log/log.h>
22 #include <unicode/ubidi.h>
23 #include <unicode/utf16.h>
24 #include <utils/LruCache.h>
25
26 #include <cmath>
27 #include <iostream>
28 #include <mutex>
29 #include <string>
30 #include <vector>
31
32 #include "BidiUtils.h"
33 #include "FeatureFlags.h"
34 #include "LayoutSplitter.h"
35 #include "LayoutUtils.h"
36 #include "LetterSpacingUtils.h"
37 #include "LocaleListCache.h"
38 #include "MinikinInternal.h"
39 #include "minikin/Emoji.h"
40 #include "minikin/HbUtils.h"
41 #include "minikin/LayoutCache.h"
42 #include "minikin/LayoutPieces.h"
43 #include "minikin/Macros.h"
44
45 namespace minikin {
46
47 // TODO: Instead of two-pass letter space adjustment, apply letter space after populating from the
48 // layout cache
adjustGlyphLetterSpacingEdge(const U16StringPiece & textBuf,const MinikinPaint & paint,RunFlag runFlag,std::vector<LayoutGlyph> * glyphs)49 void adjustGlyphLetterSpacingEdge(const U16StringPiece& textBuf, const MinikinPaint& paint,
50 RunFlag runFlag, std::vector<LayoutGlyph>* glyphs) {
51 const float letterSpacing = paint.letterSpacing * paint.size * paint.scaleX;
52 const float letterSpacingHalf = letterSpacing * 0.5f;
53 const uint32_t glyphCount = glyphs->size();
54
55 if (runFlag & RunFlag::LEFT_EDGE) {
56 uint32_t i;
57 for (i = 0; i < glyphCount; ++i) {
58 const uint32_t cluster = glyphs->at(i).cluster;
59 const uint32_t cp = textBuf.codePointAt(cluster);
60 if (!u_iscntrl(cp)) {
61 break;
62 }
63 }
64
65 for (; i < glyphCount; ++i) {
66 const uint32_t cluster = glyphs->at(i).cluster;
67 const uint32_t cp = textBuf.codePointAt(cluster);
68 if (!isLetterSpacingCapableCodePoint(cp)) {
69 break;
70 }
71 if (paint.verticalText) {
72 glyphs->at(i).y -= letterSpacingHalf;
73 } else {
74 glyphs->at(i).x -= letterSpacingHalf;
75 }
76 }
77 }
78
79 if (runFlag & RunFlag::RIGHT_EDGE) {
80 uint32_t i;
81 for (i = 0; i < glyphCount; ++i) {
82 const uint32_t cluster = glyphs->at(glyphCount - 1 - i).cluster;
83 const uint32_t cp = textBuf.codePointAt(cluster);
84 if (!u_iscntrl(cp)) {
85 if (!isLetterSpacingCapableCodePoint(cp)) {
86 i = glyphCount;
87 }
88 break;
89 }
90 }
91
92 if (i < glyphCount) {
93 for (uint32_t j = glyphCount - i; j < glyphCount; ++j) {
94 if (paint.verticalText) {
95 glyphs->at(j).y -= letterSpacingHalf;
96 } else {
97 glyphs->at(j).x -= letterSpacingHalf;
98 }
99 }
100 }
101 }
102 }
103
104 // TODO: Instead of two-pass letter space adjustment, apply letter space after populating from the
105 // layout cache
adjustAdvanceLetterSpacingEdge(const U16StringPiece & textBuf,const Range & range,const BidiText & bidiText,const MinikinPaint & paint,RunFlag runFlag,float advance,float * advances)106 float adjustAdvanceLetterSpacingEdge(const U16StringPiece& textBuf, const Range& range,
107 const BidiText& bidiText, const MinikinPaint& paint,
108 RunFlag runFlag, float advance, float* advances) {
109 const float letterSpacing = paint.letterSpacing * paint.size * paint.scaleX;
110 const float letterSpacingHalf = letterSpacing * 0.5f;
111 if (letterSpacing == 0 || textBuf.length() == 0) {
112 return advance;
113 }
114 const uint32_t advOffset = range.getStart();
115
116 if (runFlag & RunFlag::LEFT_EDGE) {
117 const BidiText::RunInfo lRun = bidiText.getRunInfoAt(0);
118 if (lRun.isRtl) {
119 uint32_t lastNonCtrlCharIndex = static_cast<uint32_t>(-1);
120 for (uint32_t i : lRun.range) {
121 if (!u_iscntrl(textBuf.codePointAt(i)) && advances[i - advOffset] != 0) {
122 lastNonCtrlCharIndex = i;
123 }
124 }
125 if (lastNonCtrlCharIndex != static_cast<uint32_t>(-1)) {
126 uint32_t cp = textBuf.codePointAt(lastNonCtrlCharIndex);
127 if (isLetterSpacingCapableCodePoint(cp)) {
128 advances[lastNonCtrlCharIndex - advOffset] -= letterSpacingHalf;
129 advance -= letterSpacingHalf;
130 }
131 }
132 } else {
133 for (uint32_t i : lRun.range) {
134 uint32_t cp = textBuf.codePointAt(i);
135 if (!u_iscntrl(cp) && advances[i - advOffset] != 0) {
136 if (isLetterSpacingCapableCodePoint(cp)) {
137 advances[i - advOffset] -= letterSpacingHalf;
138 advance -= letterSpacingHalf;
139 }
140 break;
141 }
142 }
143 }
144 }
145
146 if (runFlag & RunFlag::RIGHT_EDGE) {
147 const BidiText::RunInfo rRun = bidiText.getRunInfoAt(bidiText.getRunCount() - 1);
148 if (rRun.isRtl) {
149 for (uint32_t i : rRun.range) {
150 uint32_t cp = textBuf.codePointAt(i);
151 if (!u_iscntrl(cp) && advances[i - advOffset] != 0) {
152 if (isLetterSpacingCapableCodePoint(cp)) {
153 advances[i - advOffset] -= letterSpacingHalf;
154 advance -= letterSpacingHalf;
155 }
156 break;
157 }
158 }
159 } else {
160 uint32_t lastNonCtrlCharIndex = static_cast<uint32_t>(-1);
161 for (uint32_t i : rRun.range) {
162 if (!u_iscntrl(textBuf.codePointAt(i)) && advances[i - advOffset] != 0) {
163 lastNonCtrlCharIndex = i;
164 }
165 }
166 if (lastNonCtrlCharIndex != static_cast<uint32_t>(-1)) {
167 uint32_t cp = textBuf.codePointAt(lastNonCtrlCharIndex);
168 if (isLetterSpacingCapableCodePoint(cp)) {
169 advances[lastNonCtrlCharIndex - advOffset] -= letterSpacingHalf;
170 advance -= letterSpacingHalf;
171 }
172 }
173 }
174 }
175 return advance;
176 }
177
178 // TODO: Instead of two-pass letter space adjustment, apply letter space after populating from the
179 // layout cache
adjustBoundsLetterSpacingEdge(const MinikinPaint & paint,RunFlag runFlag,MinikinRect * bounds)180 void adjustBoundsLetterSpacingEdge(const MinikinPaint& paint, RunFlag runFlag,
181 MinikinRect* bounds) {
182 if (!bounds) {
183 return;
184 }
185 const float letterSpacing = paint.letterSpacing * paint.size * paint.scaleX;
186 const float letterSpacingHalf = letterSpacing * 0.5f;
187 if (letterSpacing == 0) {
188 return;
189 }
190 if (runFlag & RunFlag::LEFT_EDGE) {
191 if (paint.verticalText) {
192 bounds->mTop -= letterSpacingHalf;
193 bounds->mBottom -= letterSpacingHalf;
194 } else {
195 bounds->mLeft -= letterSpacingHalf;
196 bounds->mRight -= letterSpacingHalf;
197 }
198 }
199
200 if (runFlag & RunFlag::RIGHT_EDGE) {
201 if (paint.verticalText) {
202 bounds->mBottom -= letterSpacingHalf;
203 } else {
204 bounds->mRight -= letterSpacingHalf;
205 }
206 }
207 }
208
doLayout(const U16StringPiece & textBuf,const Range & range,Bidi bidiFlags,const MinikinPaint & paint,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen,RunFlag runFlag)209 void Layout::doLayout(const U16StringPiece& textBuf, const Range& range, Bidi bidiFlags,
210 const MinikinPaint& paint, StartHyphenEdit startHyphen,
211 EndHyphenEdit endHyphen, RunFlag runFlag) {
212 const uint32_t count = range.getLength();
213 mAdvances.resize(count, 0);
214 mGlyphs.reserve(count);
215 const BidiText bidiText(textBuf, range, bidiFlags);
216 for (const BidiText::RunInfo& runInfo : bidiText) {
217 doLayoutRunCached(textBuf, runInfo.range, runInfo.isRtl, paint, range.getStart(),
218 startHyphen, endHyphen, this, nullptr, nullptr, nullptr);
219 }
220 U16StringPiece substr = textBuf.substr(range);
221 adjustGlyphLetterSpacingEdge(substr, paint, runFlag, &mGlyphs);
222 mAdvance = adjustAdvanceLetterSpacingEdge(textBuf, range, bidiText, paint, runFlag, mAdvance,
223 mAdvances.data());
224 }
225
measureText(const U16StringPiece & textBuf,const Range & range,Bidi bidiFlags,const MinikinPaint & paint,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen,float * advances,MinikinRect * bounds,uint32_t * clusterCount,RunFlag runFlag)226 float Layout::measureText(const U16StringPiece& textBuf, const Range& range, Bidi bidiFlags,
227 const MinikinPaint& paint, StartHyphenEdit startHyphen,
228 EndHyphenEdit endHyphen, float* advances, MinikinRect* bounds,
229 uint32_t* clusterCount, RunFlag runFlag) {
230 float advance = 0;
231 if (clusterCount) {
232 *clusterCount = 0;
233 }
234
235 // To distinguish the control characters and combining character, need per character advances.
236 std::vector<float> tmpAdvances;
237 if (advances == nullptr) {
238 tmpAdvances.resize(range.getLength());
239 advances = tmpAdvances.data();
240 }
241
242 MinikinRect tmpBounds;
243 const BidiText bidiText(textBuf, range, bidiFlags);
244 for (const BidiText::RunInfo& runInfo : bidiText) {
245 const size_t offset = range.toRangeOffset(runInfo.range.getStart());
246 float* advancesForRun = advances ? advances + offset : nullptr;
247 tmpBounds.setEmpty();
248 float run_advance = doLayoutRunCached(textBuf, runInfo.range, runInfo.isRtl, paint, 0,
249 startHyphen, endHyphen, nullptr, advancesForRun,
250 bounds ? &tmpBounds : nullptr, clusterCount);
251 if (bounds) {
252 if (paint.verticalText) {
253 bounds->join(tmpBounds, 0, advance);
254 } else {
255 bounds->join(tmpBounds, advance, 0);
256 }
257 }
258 advance += run_advance;
259 }
260 adjustBoundsLetterSpacingEdge(paint, runFlag, bounds);
261 return adjustAdvanceLetterSpacingEdge(textBuf, range, bidiText, paint, runFlag, advance,
262 advances);
263 }
264
doLayoutRunCached(const U16StringPiece & textBuf,const Range & range,bool isRtl,const MinikinPaint & paint,size_t dstStart,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen,Layout * layout,float * advances,MinikinRect * bounds,uint32_t * clusterCount)265 float Layout::doLayoutRunCached(const U16StringPiece& textBuf, const Range& range, bool isRtl,
266 const MinikinPaint& paint, size_t dstStart,
267 StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
268 Layout* layout, float* advances, MinikinRect* bounds,
269 uint32_t* clusterCount) {
270 if (!range.isValid()) {
271 return 0.0f; // ICU failed to retrieve the bidi run?
272 }
273 float advance = 0;
274 MinikinRect tmpBounds;
275 for (const auto[context, piece] : LayoutSplitter(textBuf, range, isRtl)) {
276 // Hyphenation only applies to the start/end of run.
277 const StartHyphenEdit pieceStartHyphen =
278 (piece.getStart() == range.getStart()) ? startHyphen : StartHyphenEdit::NO_EDIT;
279 const EndHyphenEdit pieceEndHyphen =
280 (piece.getEnd() == range.getEnd()) ? endHyphen : EndHyphenEdit::NO_EDIT;
281 float* advancesForRun =
282 advances ? advances + (piece.getStart() - range.getStart()) : nullptr;
283 tmpBounds.setEmpty();
284 float word_advance = doLayoutWord(
285 textBuf.data() + context.getStart(), piece.getStart() - context.getStart(),
286 piece.getLength(), context.getLength(), isRtl, paint, piece.getStart() - dstStart,
287 pieceStartHyphen, pieceEndHyphen, layout, advancesForRun,
288 bounds ? &tmpBounds : nullptr, clusterCount);
289 if (bounds) {
290 if (paint.verticalText) {
291 bounds->join(tmpBounds, 0, advance);
292 } else {
293 bounds->join(tmpBounds, advance, 0);
294 }
295 }
296 advance += word_advance;
297 }
298 return advance;
299 }
300
301 class LayoutAppendFunctor {
302 public:
LayoutAppendFunctor(Layout * layout,float * advances,uint32_t outOffset,float wordSpacing,MinikinRect * bounds)303 LayoutAppendFunctor(Layout* layout, float* advances, uint32_t outOffset, float wordSpacing,
304 MinikinRect* bounds)
305 : mLayout(layout),
306 mAdvances(advances),
307 mOutOffset(outOffset),
308 mWordSpacing(wordSpacing),
309 mBounds(bounds) {}
310
operator ()(const LayoutPiece & layoutPiece,const MinikinPaint &,const MinikinRect & bounds)311 void operator()(const LayoutPiece& layoutPiece, const MinikinPaint& /* paint */,
312 const MinikinRect& bounds) {
313 if (mLayout) {
314 mLayout->appendLayout(layoutPiece, mOutOffset, mWordSpacing);
315 }
316 if (mAdvances) {
317 std::copy(layoutPiece.advances().begin(), layoutPiece.advances().end(), mAdvances);
318 }
319 if (mBounds) {
320 if (layoutPiece.isVerticalText()) {
321 mBounds->join(bounds, 0, mTotalAdvance);
322 } else {
323 mBounds->join(bounds, mTotalAdvance, 0);
324 }
325 }
326 mTotalAdvance += layoutPiece.advance();
327 mClusterCount += layoutPiece.clusterCount();
328 }
329
getTotalAdvance()330 float getTotalAdvance() { return mTotalAdvance; }
getClusterCount() const331 uint32_t getClusterCount() const { return mClusterCount; }
332
333 private:
334 Layout* mLayout;
335 float* mAdvances;
336 float mTotalAdvance{0};
337 const uint32_t mOutOffset;
338 const float mWordSpacing;
339 uint32_t mClusterCount{0};
340 MinikinRect* mBounds;
341 };
342
doLayoutWord(const uint16_t * buf,size_t start,size_t count,size_t bufSize,bool isRtl,const MinikinPaint & paint,size_t bufStart,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen,Layout * layout,float * advances,MinikinRect * bounds,uint32_t * clusterCount)343 float Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
344 bool isRtl, const MinikinPaint& paint, size_t bufStart,
345 StartHyphenEdit startHyphen, EndHyphenEdit endHyphen, Layout* layout,
346 float* advances, MinikinRect* bounds, uint32_t* clusterCount) {
347 float wordSpacing = count == 1 && isWordSpace(buf[start]) ? paint.wordSpacing : 0;
348 float totalAdvance = 0;
349 const bool boundsCalculation = bounds != nullptr;
350
351 const U16StringPiece textBuf(buf, bufSize);
352 const Range range(start, start + count);
353 LayoutAppendFunctor f(layout, advances, bufStart, wordSpacing, bounds);
354 LayoutCache::getInstance().getOrCreate(textBuf, range, paint, isRtl, startHyphen, endHyphen,
355 boundsCalculation, f);
356 totalAdvance = f.getTotalAdvance();
357 if (clusterCount) {
358 *clusterCount += f.getClusterCount();
359 }
360
361 if (wordSpacing != 0) {
362 totalAdvance += wordSpacing;
363 if (advances) {
364 advances[0] += wordSpacing;
365 }
366 }
367 return totalAdvance;
368 }
369
appendLayout(const LayoutPiece & src,size_t start,float extraAdvance)370 void Layout::appendLayout(const LayoutPiece& src, size_t start, float extraAdvance) {
371 float xAdvance = src.isVerticalText() ? 0 : mAdvance;
372 float yAdvance = src.isVerticalText() ? mAdvance : 0;
373 if (features::typeface_redesign_readonly()) {
374 if (src.glyphCount() == 0) {
375 return;
376 }
377 if (mFonts.empty()) {
378 mFonts.push_back(src.fontAt(0));
379 mEnds.push_back(1);
380 }
381 FakedFont* lastFont = &mFonts.back();
382
383 for (size_t i = 0; i < src.glyphCount(); i++) {
384 const FakedFont& font = src.fontAt(i);
385
386 if (font != *lastFont) {
387 mEnds.back() = mGlyphs.size();
388 mFonts.push_back(font);
389 mEnds.push_back(mGlyphs.size() + 1);
390 lastFont = &mFonts.back();
391 } else if (i == src.glyphCount() - 1) {
392 mEnds.back() = mGlyphs.size() + 1;
393 }
394
395 mGlyphs.emplace_back(src.fontAt(i), src.glyphIdAt(i), src.clusterAt(i) + start,
396 xAdvance + src.pointAt(i).x, yAdvance + src.pointAt(i).y);
397 }
398 } else {
399 for (size_t i = 0; i < src.glyphCount(); i++) {
400 mGlyphs.emplace_back(src.fontAt(i), src.glyphIdAt(i), src.clusterAt(i) + start,
401 xAdvance + src.pointAt(i).x, yAdvance + src.pointAt(i).y);
402 }
403 }
404 const AdvanceVector& advances = src.advances();
405 for (size_t i = 0; i < advances.size(); i++) {
406 mAdvances[i + start] = advances[i];
407 if (i == 0) {
408 mAdvances[start] += extraAdvance;
409 }
410 }
411 mAdvance += src.advance() + extraAdvance;
412 }
413
purgeCaches()414 void Layout::purgeCaches() {
415 LayoutCache::getInstance().clear();
416 }
417
418 } // namespace minikin
419