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