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 <cmath>
22 #include <iostream>
23 #include <mutex>
24 #include <string>
25 #include <vector>
26
27 #include <hb-icu.h>
28 #include <hb-ot.h>
29 #include <log/log.h>
30 #include <unicode/ubidi.h>
31 #include <unicode/utf16.h>
32 #include <utils/LruCache.h>
33
34 #include "minikin/Emoji.h"
35 #include "minikin/HbUtils.h"
36 #include "minikin/LayoutCache.h"
37 #include "minikin/LayoutPieces.h"
38 #include "minikin/Macros.h"
39
40 #include "BidiUtils.h"
41 #include "LayoutSplitter.h"
42 #include "LayoutUtils.h"
43 #include "LocaleListCache.h"
44 #include "MinikinInternal.h"
45
46 namespace minikin {
47
doLayout(const U16StringPiece & textBuf,const Range & range,Bidi bidiFlags,const MinikinPaint & paint,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen)48 void Layout::doLayout(const U16StringPiece& textBuf, const Range& range, Bidi bidiFlags,
49 const MinikinPaint& paint, StartHyphenEdit startHyphen,
50 EndHyphenEdit endHyphen) {
51 const uint32_t count = range.getLength();
52 mAdvances.resize(count, 0);
53 mGlyphs.reserve(count);
54 for (const BidiText::RunInfo& runInfo : BidiText(textBuf, range, bidiFlags)) {
55 doLayoutRunCached(textBuf, runInfo.range, runInfo.isRtl, paint, range.getStart(),
56 startHyphen, endHyphen, this, nullptr);
57 }
58 }
59
measureText(const U16StringPiece & textBuf,const Range & range,Bidi bidiFlags,const MinikinPaint & paint,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen,float * advances)60 float Layout::measureText(const U16StringPiece& textBuf, const Range& range, Bidi bidiFlags,
61 const MinikinPaint& paint, StartHyphenEdit startHyphen,
62 EndHyphenEdit endHyphen, float* advances) {
63 float advance = 0;
64 for (const BidiText::RunInfo& runInfo : BidiText(textBuf, range, bidiFlags)) {
65 const size_t offset = range.toRangeOffset(runInfo.range.getStart());
66 float* advancesForRun = advances ? advances + offset : nullptr;
67 advance += doLayoutRunCached(textBuf, runInfo.range, runInfo.isRtl, paint, 0, startHyphen,
68 endHyphen, nullptr, advancesForRun);
69 }
70 return advance;
71 }
72
doLayoutRunCached(const U16StringPiece & textBuf,const Range & range,bool isRtl,const MinikinPaint & paint,size_t dstStart,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen,Layout * layout,float * advances)73 float Layout::doLayoutRunCached(const U16StringPiece& textBuf, const Range& range, bool isRtl,
74 const MinikinPaint& paint, size_t dstStart,
75 StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
76 Layout* layout, float* advances) {
77 if (!range.isValid()) {
78 return 0.0f; // ICU failed to retrieve the bidi run?
79 }
80 float advance = 0;
81 for (const auto[context, piece] : LayoutSplitter(textBuf, range, isRtl)) {
82 // Hyphenation only applies to the start/end of run.
83 const StartHyphenEdit pieceStartHyphen =
84 (piece.getStart() == range.getStart()) ? startHyphen : StartHyphenEdit::NO_EDIT;
85 const EndHyphenEdit pieceEndHyphen =
86 (piece.getEnd() == range.getEnd()) ? endHyphen : EndHyphenEdit::NO_EDIT;
87 float* advancesForRun =
88 advances ? advances + (piece.getStart() - range.getStart()) : nullptr;
89 advance += doLayoutWord(textBuf.data() + context.getStart(),
90 piece.getStart() - context.getStart(), piece.getLength(),
91 context.getLength(), isRtl, paint, piece.getStart() - dstStart,
92 pieceStartHyphen, pieceEndHyphen, layout, advancesForRun);
93 }
94 return advance;
95 }
96
97 class LayoutAppendFunctor {
98 public:
LayoutAppendFunctor(Layout * layout,float * advances,float * totalAdvance,uint32_t outOffset,float wordSpacing)99 LayoutAppendFunctor(Layout* layout, float* advances, float* totalAdvance, uint32_t outOffset,
100 float wordSpacing)
101 : mLayout(layout),
102 mAdvances(advances),
103 mTotalAdvance(totalAdvance),
104 mOutOffset(outOffset),
105 mWordSpacing(wordSpacing) {}
106
operator ()(const LayoutPiece & layoutPiece,const MinikinPaint &)107 void operator()(const LayoutPiece& layoutPiece, const MinikinPaint& /* paint */) {
108 if (mLayout) {
109 mLayout->appendLayout(layoutPiece, mOutOffset, mWordSpacing);
110 }
111 if (mAdvances) {
112 const std::vector<float>& advances = layoutPiece.advances();
113 std::copy(advances.begin(), advances.end(), mAdvances);
114 }
115 if (mTotalAdvance) {
116 *mTotalAdvance = layoutPiece.advance();
117 }
118 }
119
120 private:
121 Layout* mLayout;
122 float* mAdvances;
123 float* mTotalAdvance;
124 const uint32_t mOutOffset;
125 const float mWordSpacing;
126 };
127
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)128 float Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
129 bool isRtl, const MinikinPaint& paint, size_t bufStart,
130 StartHyphenEdit startHyphen, EndHyphenEdit endHyphen, Layout* layout,
131 float* advances) {
132 float wordSpacing = count == 1 && isWordSpace(buf[start]) ? paint.wordSpacing : 0;
133 float totalAdvance = 0;
134
135 const U16StringPiece textBuf(buf, bufSize);
136 const Range range(start, start + count);
137 LayoutAppendFunctor f(layout, advances, &totalAdvance, bufStart, wordSpacing);
138 LayoutCache::getInstance().getOrCreate(textBuf, range, paint, isRtl, startHyphen, endHyphen, f);
139
140 if (wordSpacing != 0) {
141 totalAdvance += wordSpacing;
142 if (advances) {
143 advances[0] += wordSpacing;
144 }
145 }
146 return totalAdvance;
147 }
148
appendLayout(const LayoutPiece & src,size_t start,float extraAdvance)149 void Layout::appendLayout(const LayoutPiece& src, size_t start, float extraAdvance) {
150 for (size_t i = 0; i < src.glyphCount(); i++) {
151 mGlyphs.emplace_back(src.fontAt(i), src.glyphIdAt(i), mAdvance + src.pointAt(i).x,
152 src.pointAt(i).y);
153 }
154 const std::vector<float>& advances = src.advances();
155 for (size_t i = 0; i < advances.size(); i++) {
156 mAdvances[i + start] = advances[i];
157 if (i == 0) {
158 mAdvances[start] += extraAdvance;
159 }
160 }
161 MinikinRect srcBounds(src.bounds());
162 srcBounds.offset(mAdvance, 0);
163 mBounds.join(srcBounds);
164 mAdvance += src.advance() + extraAdvance;
165 }
166
purgeCaches()167 void Layout::purgeCaches() {
168 LayoutCache::getInstance().clear();
169 }
170
171 } // namespace minikin
172