xref: /aosp_15_r20/external/skia/modules/skparagraph/src/ParagraphBuilderImpl.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 // Copyright 2019 Google LLC.
2 #include "modules/skparagraph/src/ParagraphBuilderImpl.h"
3 
4 #include "include/core/SkRefCnt.h"
5 #include "include/core/SkTypes.h"
6 #include "modules/skparagraph/include/FontCollection.h"
7 #include "modules/skparagraph/include/Paragraph.h"
8 #include "modules/skparagraph/include/ParagraphBuilder.h"
9 #include "modules/skparagraph/include/ParagraphStyle.h"
10 #include "modules/skparagraph/include/TextStyle.h"
11 #include "modules/skparagraph/src/ParagraphImpl.h"
12 #include "modules/skunicode/include/SkUnicode.h"
13 #include "src/core/SkStringUtils.h"
14 
15 #if !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE)
16 #if defined(SK_UNICODE_ICU_IMPLEMENTATION)
17 #include "modules/skunicode/include/SkUnicode_icu.h"
18 #endif
19 
20 #if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION)
21 #include "modules/skunicode/include/SkUnicode_libgrapheme.h"
22 #endif
23 
24 #if defined(SK_UNICODE_ICU4X_IMPLEMENTATION)
25 #include "modules/skunicode/include/SkUnicode_icu4x.h"
26 #endif
27 
28 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
29 #include "modules/skunicode/include/SkUnicode_client.h"
30 #endif
31 
32 #endif  // !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE)
33 
34 #include <memory>
35 #include <utility>
36 
37 namespace skia {
38 namespace textlayout {
39 
40 #if !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE)
41 
42 namespace {
43 // TODO(kjlubick,jlavrova) Remove these defines by having clients register something or somehow
44 // plumbing this all into the animation builder factories.
get_unicode()45 sk_sp<SkUnicode> get_unicode() {
46 #ifdef SK_UNICODE_ICU_IMPLEMENTATION
47     if (auto unicode = SkUnicodes::ICU::Make()) {
48         return unicode;
49     }
50 #endif
51 #ifdef SK_UNICODE_ICU4X_IMPLEMENTATION
52     if (auto unicode = SkUnicodes::ICU4X::Make()) {
53         return unicode;
54     }
55 #endif
56 #ifdef SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION
57     if (auto unicode = SkUnicodes::Libgrapheme::Make()) {
58         return unicode;
59     }
60 #endif
61     return nullptr;
62 }
63 }
64 
make(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection)65 std::unique_ptr<ParagraphBuilder> ParagraphBuilder::make(const ParagraphStyle& style,
66                                                          sk_sp<FontCollection> fontCollection) {
67     return ParagraphBuilderImpl::make(style, std::move(fontCollection), get_unicode());
68 }
69 
make(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection)70 std::unique_ptr<ParagraphBuilder> ParagraphBuilderImpl::make(const ParagraphStyle& style,
71                                                              sk_sp<FontCollection> fontCollection) {
72     return std::make_unique<ParagraphBuilderImpl>(style, std::move(fontCollection), get_unicode());
73 }
74 
ParagraphBuilderImpl(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection)75 ParagraphBuilderImpl::ParagraphBuilderImpl(
76         const ParagraphStyle& style, sk_sp<FontCollection> fontCollection)
77         : ParagraphBuilderImpl(style, std::move(fontCollection), get_unicode())
78 { }
79 
80 #endif  // !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE)
81 
make(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection,sk_sp<SkUnicode> unicode)82 std::unique_ptr<ParagraphBuilder> ParagraphBuilder::make(const ParagraphStyle& style,
83                                                          sk_sp<FontCollection> fontCollection,
84                                                          sk_sp<SkUnicode> unicode) {
85     return ParagraphBuilderImpl::make(style, std::move(fontCollection), std::move(unicode));
86 }
87 
make(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection,sk_sp<SkUnicode> unicode)88 std::unique_ptr<ParagraphBuilder> ParagraphBuilderImpl::make(const ParagraphStyle& style,
89                                                              sk_sp<FontCollection> fontCollection,
90                                                              sk_sp<SkUnicode> unicode) {
91     return std::make_unique<ParagraphBuilderImpl>(style, std::move(fontCollection),
92                                                   std::move(unicode));
93 }
94 
ParagraphBuilderImpl(const ParagraphStyle & style,sk_sp<FontCollection> fontCollection,sk_sp<SkUnicode> unicode)95 ParagraphBuilderImpl::ParagraphBuilderImpl(
96         const ParagraphStyle& style, sk_sp<FontCollection> fontCollection, sk_sp<SkUnicode> unicode)
97         : ParagraphBuilder()
98         , fUtf8()
99         , fFontCollection(std::move(fontCollection))
100         , fParagraphStyle(style)
101         , fUnicode(std::move(unicode))
102 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
103         , fTextIsFinalized(false)
104         , fUsingClientInfo(false)
105 #endif
106 {
107     SkASSERT(fFontCollection);
108     startStyledBlock();
109 }
110 
111 ParagraphBuilderImpl::~ParagraphBuilderImpl() = default;
112 
pushStyle(const TextStyle & style)113 void ParagraphBuilderImpl::pushStyle(const TextStyle& style) {
114     fTextStyles.push_back(style);
115     if (!fStyledBlocks.empty() && fStyledBlocks.back().fRange.end == fUtf8.size() &&
116         fStyledBlocks.back().fStyle == style) {
117         // Just continue with the same style
118     } else {
119         // Go with the new style
120         startStyledBlock();
121     }
122 }
123 
pop()124 void ParagraphBuilderImpl::pop() {
125     if (!fTextStyles.empty()) {
126         fTextStyles.pop_back();
127     } else {
128         // In this case we use paragraph style and skip Pop operation
129         SkDEBUGF("SkParagraphBuilder.Pop() called too many times.\n");
130     }
131 
132     this->startStyledBlock();
133 }
134 
internalPeekStyle()135 const TextStyle& ParagraphBuilderImpl::internalPeekStyle() {
136     if (fTextStyles.empty()) {
137         return fParagraphStyle.getTextStyle();
138     } else {
139         return fTextStyles.back();
140     }
141 }
142 
peekStyle()143 TextStyle ParagraphBuilderImpl::peekStyle() {
144     return this->internalPeekStyle();
145 }
146 
addText(const std::u16string & text)147 void ParagraphBuilderImpl::addText(const std::u16string& text) {
148 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
149     SkASSERT(!fTextIsFinalized);
150 #endif
151     auto utf8 = SkUnicode::convertUtf16ToUtf8(text);
152     fUtf8.append(utf8);
153 }
154 
addText(const char * text)155 void ParagraphBuilderImpl::addText(const char* text) {
156 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
157     SkASSERT(!fTextIsFinalized);
158 #endif
159     fUtf8.append(text);
160 }
161 
addText(const char * text,size_t len)162 void ParagraphBuilderImpl::addText(const char* text, size_t len) {
163 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
164     SkASSERT(!fTextIsFinalized);
165 #endif
166     fUtf8.append(text, len);
167 }
168 
addPlaceholder(const PlaceholderStyle & placeholderStyle)169 void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle) {
170 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
171     SkASSERT(!fTextIsFinalized);
172 #endif
173     addPlaceholder(placeholderStyle, false);
174 }
175 
addPlaceholder(const PlaceholderStyle & placeholderStyle,bool lastOne)176 void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle, bool lastOne) {
177 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
178     // The very last placeholder is added automatically
179     // and only AFTER finalize() is called
180     SkASSERT(!fTextIsFinalized || lastOne);
181 #endif
182     if (!fUtf8.isEmpty() && !lastOne) {
183         // We keep the very last text style
184         this->endRunIfNeeded();
185     }
186 
187     BlockRange stylesBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fBlocksBefore.end + 1,
188                             fStyledBlocks.size());
189     TextRange textBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fRange.end,
190                             fUtf8.size());
191     auto start = fUtf8.size();
192     auto topStyle = internalPeekStyle();
193     if (!lastOne) {
194         pushStyle(topStyle.cloneForPlaceholder());
195         addText(std::u16string(1ull, 0xFFFC));
196         pop();
197     }
198     auto end = fUtf8.size();
199     fPlaceholders.emplace_back(start, end, placeholderStyle, topStyle, stylesBefore, textBefore);
200 }
201 
endRunIfNeeded()202 void ParagraphBuilderImpl::endRunIfNeeded() {
203     if (fStyledBlocks.empty()) {
204         return;
205     }
206 
207     auto& last = fStyledBlocks.back();
208     if (last.fRange.start == fUtf8.size()) {
209         fStyledBlocks.pop_back();
210     } else {
211         last.fRange.end = fUtf8.size();
212     }
213 }
214 
startStyledBlock()215 void ParagraphBuilderImpl::startStyledBlock() {
216     endRunIfNeeded();
217     fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), internalPeekStyle());
218 }
219 
finalize()220 void ParagraphBuilderImpl::finalize() {
221 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
222     if (fTextIsFinalized) {
223         return;
224     }
225 #endif
226     if (!fUtf8.isEmpty()) {
227         this->endRunIfNeeded();
228     }
229 
230 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
231     fTextIsFinalized = true;
232 #endif
233 }
234 
Build()235 std::unique_ptr<Paragraph> ParagraphBuilderImpl::Build() {
236     this->finalize();
237     // Add one fake placeholder with the rest of the text
238     this->addPlaceholder(PlaceholderStyle(), true);
239 
240     fUTF8IndexForUTF16Index.clear();
241     fUTF16IndexForUTF8Index.clear();
242 #if !defined(SK_DISABLE_LEGACY_PARAGRAPH_UNICODE) && defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
243     if (fUsingClientInfo && !fUnicode) {
244         // This is the place where SkUnicode is paired with SkParagraph
245         fUnicode = SkUnicodes::Client::Make(this->getText(),
246                                             std::move(fWordsUtf16),
247                                             std::move(fGraphemeBreaksUtf8),
248                                             std::move(fLineBreaksUtf8));
249     }
250 #endif
251 
252     SkASSERT_RELEASE(fUnicode);
253     return std::make_unique<ParagraphImpl>(
254             fUtf8, fParagraphStyle, fStyledBlocks, fPlaceholders, fFontCollection, fUnicode);
255 }
256 
getText()257 SkSpan<char> ParagraphBuilderImpl::getText() {
258     this->finalize();
259     return SkSpan<char>(fUtf8.isEmpty() ? nullptr : fUtf8.data(), fUtf8.size());
260 }
261 
getParagraphStyle() const262 const ParagraphStyle& ParagraphBuilderImpl::getParagraphStyle() const {
263     return fParagraphStyle;
264 }
265 
ensureUTF16Mapping()266 void ParagraphBuilderImpl::ensureUTF16Mapping() {
267     fillUTF16MappingOnce([&] {
268         SkUnicode::extractUtfConversionMapping(
269                 this->getText(),
270                 [&](size_t index) { fUTF8IndexForUTF16Index.emplace_back(index); },
271                 [&](size_t index) { fUTF16IndexForUTF8Index.emplace_back(index); });
272     });
273 }
274 
275 #if !defined(SK_DISABLE_LEGACY_CLIENT_UNICODE) && defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
setWordsUtf8(std::vector<SkUnicode::Position> wordsUtf8)276 void ParagraphBuilderImpl::setWordsUtf8(std::vector<SkUnicode::Position> wordsUtf8) {
277     ensureUTF16Mapping();
278     std::vector<SkUnicode::Position> wordsUtf16;
279     for (SkUnicode::Position indexUtf8: wordsUtf8) {
280         wordsUtf16.emplace_back(fUTF16IndexForUTF8Index[indexUtf8]);
281     }
282     setWordsUtf16(wordsUtf16);
283 }
284 
setWordsUtf16(std::vector<SkUnicode::Position> wordsUtf16)285 void ParagraphBuilderImpl::setWordsUtf16(std::vector<SkUnicode::Position> wordsUtf16) {
286     fUsingClientInfo = true;
287     fWordsUtf16 = std::move(wordsUtf16);
288 }
289 
setGraphemeBreaksUtf8(std::vector<SkUnicode::Position> graphemeBreaksUtf8)290 void ParagraphBuilderImpl::setGraphemeBreaksUtf8(std::vector<SkUnicode::Position> graphemeBreaksUtf8) {
291     fUsingClientInfo = true;
292     fGraphemeBreaksUtf8 = std::move(graphemeBreaksUtf8);
293 }
294 
setGraphemeBreaksUtf16(std::vector<SkUnicode::Position> graphemeBreaksUtf16)295 void ParagraphBuilderImpl::setGraphemeBreaksUtf16(std::vector<SkUnicode::Position> graphemeBreaksUtf16) {
296     ensureUTF16Mapping();
297     std::vector<SkUnicode::Position> graphemeBreaksUtf8;
298     for (SkUnicode::Position indexUtf16: graphemeBreaksUtf16) {
299         graphemeBreaksUtf8.emplace_back(fUTF8IndexForUTF16Index[indexUtf16]);
300     }
301     setGraphemeBreaksUtf8(graphemeBreaksUtf8);
302 }
303 
setLineBreaksUtf8(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8)304 void ParagraphBuilderImpl::setLineBreaksUtf8(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8) {
305     fUsingClientInfo = true;
306     fLineBreaksUtf8 = std::move(lineBreaksUtf8);
307 }
308 
setLineBreaksUtf16(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf16)309 void ParagraphBuilderImpl::setLineBreaksUtf16(std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf16) {
310     ensureUTF16Mapping();
311     std::vector<SkUnicode::LineBreakBefore> lineBreaksUtf8;
312     for (SkUnicode::LineBreakBefore lineBreakUtf16: lineBreaksUtf16) {
313         lineBreaksUtf8.emplace_back(SkUnicode::LineBreakBefore(
314                 fUTF8IndexForUTF16Index[lineBreakUtf16.pos], lineBreakUtf16.breakType));
315     }
316     setLineBreaksUtf8(lineBreaksUtf8);
317 }
318 #endif
319 
Reset()320 void ParagraphBuilderImpl::Reset() {
321 
322     fTextStyles.clear();
323     fUtf8.reset();
324     fStyledBlocks.clear();
325     fPlaceholders.clear();
326     fUTF8IndexForUTF16Index.clear();
327     fUTF16IndexForUTF8Index.clear();
328 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
329     fWordsUtf16.clear();
330     fGraphemeBreaksUtf8.clear();
331     fLineBreaksUtf8.clear();
332     fTextIsFinalized = false;
333 #endif
334     startStyledBlock();
335 }
336 
RequiresClientICU()337 bool ParagraphBuilderImpl::RequiresClientICU() {
338 #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION)
339     return true;
340 #else
341     return false;
342 #endif
343 }
344 
345 }  // namespace textlayout
346 }  // namespace skia
347