1 // Copyright 2021 Google LLC.
2
3 #include <stack>
4 #include "experimental/sktext/include/Processor.h"
5 #include "experimental/sktext/src/Formatter.h"
6 #include "experimental/sktext/src/Shaper.h"
7 #include "experimental/sktext/src/Wrapper.h"
8
9 namespace skia {
10 namespace text {
11
12 // The result of shaping is a set of Runs placed on one endless line
13 // It has all the glyph information
shape(TextFontStyle fontStyle,SkTArray<FontBlock,true> fontBlocks)14 bool Processor::shape(TextFontStyle fontStyle, SkTArray<FontBlock, true> fontBlocks) {
15
16 if (fUnicode == nullptr) {
17 return false;
18 }
19
20 fFontBlocks = std::move(fontBlocks);
21
22 Shaper shaper(this, fontStyle);
23 if (!shaper.process()) {
24 return false;
25 }
26
27 this->markGlyphs();
28
29 return true;
30 }
31
32 // TODO: we can wrap to any shape, not just a rectangle
33 // The result of wrapping is a set of Lines that fit the required sizes and
34 // contain all the glyph information
wrap(SkScalar width,SkScalar height)35 bool Processor::wrap(SkScalar width, SkScalar height) {
36
37 Wrapper wrapper(this, width, height);
38 if (!wrapper.process()) {
39 return false;
40 }
41 return true;
42 }
43
44 // The result of formatting is a possible shift of glyphs as the format type requires
format(TextFormatStyle formatStyle)45 bool Processor::format(TextFormatStyle formatStyle) {
46
47 Formatter formatter(this, formatStyle);
48 if (!formatter.process()) {
49 return false;
50 }
51 return true;
52 }
53
54 // Once the text is decorated you can iterate it by segments (intersect of run, decor block and line)
decorate(SkTArray<DecorBlock,true> decorBlocks)55 bool Processor::decorate(SkTArray<DecorBlock, true> decorBlocks) {
56
57 this->iterateByVisualOrder(decorBlocks,
58 [&](SkSize offset, SkScalar baseline, const TextRun* run, TextRange textRange, GlyphRange glyphRange, const DecorBlock& block) {
59 SkTextBlobBuilder builder;
60 const auto& blobBuffer = builder.allocRunPos(run->fFont, SkToInt(glyphRange.width()));
61 sk_careful_memcpy(blobBuffer.glyphs, run->fGlyphs.data() + glyphRange.fStart, glyphRange.width() * sizeof(SkGlyphID));
62 sk_careful_memcpy(blobBuffer.points(), run->fPositions.data() + glyphRange.fStart, glyphRange.width() * sizeof(SkPoint));
63
64 offset.fHeight += baseline;
65 fTextOutputs.emplace_back(builder.make(), *block.fForegroundColor, offset);
66 });
67
68 return true;
69 }
70
71 // All at once
drawText(const char * text,SkCanvas * canvas,SkScalar x,SkScalar y)72 bool Processor::drawText(const char* text, SkCanvas* canvas, SkScalar x, SkScalar y) {
73
74 return drawText(text, canvas, TextFormatStyle(TextAlign::kLeft, TextDirection::kLtr), SK_ColorBLACK, SK_ColorWHITE, SkString("Roboto"), 14, SkFontStyle::Normal(), x, y);
75 }
76
drawText(const char * text,SkCanvas * canvas,SkScalar width)77 bool Processor::drawText(const char* text, SkCanvas* canvas, SkScalar width) {
78 return drawText(text, canvas,
79 TextFormatStyle(TextAlign::kLeft, TextDirection::kLtr), SK_ColorBLACK, SK_ColorWHITE, SkString("Roboto"), 14, SkFontStyle::Normal(),
80 SkSize::Make(width, SK_ScalarInfinity), 0, 0);
81 }
82
drawText(const char * text,SkCanvas * canvas,TextFormatStyle textFormat,SkColor foreground,SkColor background,const SkString & fontFamily,SkScalar fontSize,SkFontStyle fontStyle,SkScalar x,SkScalar y)83 bool Processor::drawText(const char* text, SkCanvas* canvas, TextFormatStyle textFormat, SkColor foreground, SkColor background, const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle, SkScalar x, SkScalar y) {
84 return drawText(text, canvas, textFormat, foreground, background, fontFamily, fontSize, fontStyle, SkSize::Make(SK_ScalarInfinity, SK_ScalarInfinity), x, y);
85 }
86
drawText(const char * text,SkCanvas * canvas,TextFormatStyle textFormat,SkColor foreground,SkColor background,const SkString & fontFamily,SkScalar fontSize,SkFontStyle fontStyle,SkSize reqSize,SkScalar x,SkScalar y)87 bool Processor::drawText(const char* text, SkCanvas* canvas,
88 TextFormatStyle textFormat, SkColor foreground, SkColor background, const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle,
89 SkSize reqSize, SkScalar x, SkScalar y) {
90
91 SkString str(text);
92 TextRange textRange(0, str.size());
93 Processor processor(str);
94 if (!processor.computeCodeUnitProperties()) {
95 return false;
96 }
97 if (!processor.shape({ textFormat.fDefaultTextDirection, SkFontMgr::RefDefault()}, {{{ fontFamily, fontSize, fontStyle, textRange }}})) {
98 return false;
99 }
100 if (!processor.wrap(reqSize.fWidth, reqSize.fHeight)) {
101 return false;
102 }
103 if (!processor.format(textFormat)) {
104 return false;
105 }
106 SkTArray<DecorBlock, true> decor;
107 SkPaint backgroundPaint; backgroundPaint.setColor(background);
108 SkPaint foregroundPaint; foregroundPaint.setColor(foreground);
109 if (!processor.decorate({{{&foregroundPaint, &backgroundPaint, textRange}}})) {
110 return false;
111 }
112
113 for (auto& output : processor.fTextOutputs) {
114 canvas->drawTextBlob(output.fTextBlob, x + output.fOffset.fWidth, y + output.fOffset.fHeight, output.fPaint);
115 }
116
117 return true;
118 }
119
120 // Also adjust the decoration block edges to cluster edges while we at it
121 // to avoid an enormous amount of complications
sortDecorBlocks(SkTArray<DecorBlock,true> & decorBlocks)122 void Processor::sortDecorBlocks(SkTArray<DecorBlock, true>& decorBlocks) {
123 // Soft the blocks
124 std::sort(decorBlocks.begin(), decorBlocks.end(),
125 [](const DecorBlock& a, const DecorBlock& b) {
126 return a.fRange.fStart < b.fRange.fStart;
127 });
128 // Walk through the blocks using the default when missing
129 SkPaint* foreground = new SkPaint();
130 foreground->setColor(SK_ColorBLACK);
131 std::stack<DecorBlock> defaultBlocks;
132 defaultBlocks.emplace(foreground, nullptr, TextRange(0, fText.size()));
133 size_t start = 0;
134 for (auto& block : decorBlocks) {
135 this->adjustLeft(&block.fRange.fStart);
136 this->adjustLeft(&block.fRange.fEnd);
137 SkASSERT(start <= block.fRange.fStart);
138 if (start < block.fRange.fStart) {
139 auto defaultBlock = defaultBlocks.top();
140 decorBlocks.emplace_back(defaultBlock.fForegroundColor, defaultBlock.fBackgroundColor, Range(start, block.fRange.fStart));
141 }
142 start = block.fRange.fEnd;
143 while (!defaultBlocks.empty()) {
144 auto defaultBlock = defaultBlocks.top();
145 if (defaultBlock.fRange.fEnd <= block.fRange.fEnd) {
146 defaultBlocks.pop();
147 }
148 }
149 defaultBlocks.push(block);
150 }
151 if (start < fText.size()) {
152 auto defaultBlock = defaultBlocks.top();
153 decorBlocks.emplace_back(defaultBlock.fForegroundColor, defaultBlock.fBackgroundColor, Range(start, fText.size()));
154 }
155 }
156
computeCodeUnitProperties()157 bool Processor::computeCodeUnitProperties() {
158
159 fCodeUnitProperties.push_back_n(fText.size() + 1, CodeUnitFlags::kNoCodeUnitFlag);
160
161 fUnicode = std::move(SkUnicode::Make());
162 if (nullptr == fUnicode) {
163 return false;
164 }
165
166 // Get white spaces
167 fUnicode->forEachCodepoint(fText.c_str(), fText.size(),
168 [this](SkUnichar unichar, int32_t start, int32_t end) {
169 if (fUnicode->isWhitespace(unichar)) {
170 for (auto i = start; i < end; ++i) {
171 fCodeUnitProperties[i] |= CodeUnitFlags::kPartOfWhiteSpace;
172 }
173 }
174 });
175
176 // Get line breaks
177 std::vector<SkUnicode::LineBreakBefore> lineBreaks;
178 if (!fUnicode->getLineBreaks(fText.c_str(), fText.size(), &lineBreaks)) {
179 return false;
180 }
181 for (auto& lineBreak : lineBreaks) {
182 fCodeUnitProperties[lineBreak.pos] |= lineBreak.breakType == SkUnicode::LineBreakType::kHardLineBreak
183 ? CodeUnitFlags::kHardLineBreakBefore
184 : CodeUnitFlags::kSoftLineBreakBefore;
185 }
186
187 // Get graphemes
188 std::vector<SkUnicode::Position> graphemes;
189 if (!fUnicode->getGraphemes(fText.c_str(), fText.size(), &graphemes)) {
190 return false;
191 }
192 for (auto pos : graphemes) {
193 fCodeUnitProperties[pos] |= CodeUnitFlags::kGraphemeStart;
194 }
195
196 return true;
197 }
198
markGlyphs()199 void Processor::markGlyphs() {
200 for (auto& run : fRuns) {
201 for (auto index : run.fClusters) {
202 fCodeUnitProperties[index] |= CodeUnitFlags::kGlyphStart;
203 }
204 }
205 }
206
207 template<typename Visitor>
iterateByVisualOrder(CodeUnitFlags units,Visitor visitor)208 void Processor::iterateByVisualOrder(CodeUnitFlags units, Visitor visitor) {
209 SkSize offset = SkSize::MakeEmpty();
210 for (auto& line : fLines) {
211 offset.fWidth = 0;
212 for (auto& runIndex : line.fRunsInVisualOrder) {
213 auto& run = fRuns[runIndex];
214
215 auto startGlyph = runIndex == line.fTextStart.runIndex() ? line.fTextStart.glyphIndex() : 0;
216 auto endGlyph = runIndex == line.fTextEnd.runIndex() ? line.fTextEnd.glyphIndex() : run.fGlyphs.size();
217
218 Range textRange(run.fUtf8Range.begin(), run.fUtf8Range.end());
219 Range glyphRange(startGlyph, endGlyph);
220 for (auto glyphIndex = startGlyph; glyphIndex <= endGlyph; ++glyphIndex) {
221 auto textIndex = run.fClusters[glyphIndex];
222 if (glyphIndex < endGlyph && !this->hasProperty(textIndex, units)) {
223 continue;
224 }
225 textRange.fEnd = textIndex;
226 glyphRange.fEnd = glyphIndex;
227 visitor(offset, line.fTextMetrics.baseline(), &run, textRange, glyphRange, this->fCodeUnitProperties[textIndex]);
228 textRange.fStart = textIndex;
229 glyphRange.fStart = glyphIndex;
230 offset.fWidth += run.calculateWidth(glyphRange);
231 }
232 }
233 offset.fHeight += line.fTextMetrics.height();
234 }
235 }
236
237 template<typename Visitor>
iterateByVisualOrder(SkTArray<DecorBlock,true> & decorBlocks,Visitor visitor)238 void Processor::iterateByVisualOrder(SkTArray<DecorBlock, true>& decorBlocks, Visitor visitor) {
239
240 this->sortDecorBlocks(decorBlocks);
241 // Decor blocks have to be sorted by text cannot intersect but can skip some parts of the text
242 // (in which case we use default text style from paragraph style)
243 // The edges of the decor blocks don't have to match glyph, grapheme or even unicode code point edges
244 // It's out responsibility to adjust them to some reasonable values
245 // [a:b) -> [c:d) where
246 // c is closest GG cluster edge to a from the left and d is closest GG cluster edge to b from the left
247
248 DecorBlock* currentBlock = &decorBlocks[0];
249 SkSize offset = SkSize::MakeEmpty();
250 for (auto& line : fLines) {
251 offset.fWidth = 0;
252 for (auto& runIndex : line.fRunsInVisualOrder) {
253 auto& run = fRuns[runIndex];
254 // The run edges are good (aligned to GGC)
255 // "ABCdef" -> "defCBA"
256 // "AB": red
257 // "Cd": green
258 // "ef": blue
259 // green[d] blue[ef] green [C] red [BA]
260 auto startGlyph = runIndex == line.fTextStart.runIndex() ? line.fTextStart.glyphIndex() : 0;
261 auto endGlyph = runIndex == line.fTextEnd.runIndex() ? line.fTextEnd.glyphIndex() : run.fGlyphs.size();
262
263 TextRange textRange(run.fClusters[startGlyph], run.fClusters[endGlyph]);
264 GlyphRange glyphRange(startGlyph, endGlyph);
265
266 SkASSERT(currentBlock->fRange.fStart <= textRange.fStart);
267 for (auto glyphIndex = startGlyph; glyphIndex <= endGlyph; ++glyphIndex) {
268 auto textIndex = run.fClusters[glyphIndex];
269 if (run.leftToRight() && textIndex < currentBlock->fRange.fEnd) {
270 continue;
271 } else if (!run.leftToRight() && textIndex > currentBlock->fRange.fStart) {
272 continue;
273 }
274
275 if (run.leftToRight()) {
276 textRange.fEnd = textIndex;
277 } else {
278 textRange.fStart = textIndex;
279 }
280 glyphRange.fEnd = glyphIndex;
281 SkSize shift = SkSize::Make(offset.fWidth - run.fPositions[startGlyph].fX, offset.fHeight);
282 visitor(shift, line.fTextMetrics.baseline(), &run, textRange, glyphRange, *currentBlock);
283 if (run.leftToRight()) {
284 textRange.fStart = textIndex;
285 } else {
286 textRange.fEnd = textIndex;
287 }
288 glyphRange.fStart = glyphIndex;
289 offset.fWidth += run.calculateWidth(glyphRange);
290 }
291
292 // The last line
293 if (run.leftToRight()) {
294 textRange.fEnd = run.fClusters[endGlyph];
295 } else {
296 textRange.fStart = run.fClusters[endGlyph];
297 }
298 glyphRange.fEnd = endGlyph;
299 SkSize shift = SkSize::Make(offset.fWidth - run.fPositions[startGlyph].fX, offset.fHeight);
300 visitor(shift, line.fTextMetrics.baseline(), &run, textRange, glyphRange, *currentBlock);
301 }
302 offset.fHeight += line.fTextMetrics.height();
303 }
304 }
305
306 } // namespace text
307 } // namespace skia
308