blob: 10d30f7989eb8f703828ba8476bd860ea16a67b1 [file] [log] [blame]
halcanary13cba492016-08-03 10:43:55 -07001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
Hal Canaryc640d0d2018-06-13 09:59:02 -04007
Mike Reed9a4a05e2019-01-21 20:42:04 -05008#include "SkFontMetrics.h"
Ben Wagnerb0591942019-02-15 14:46:18 -05009#include "SkMakeUnique.h"
10#include "SkShaper.h"
halcanary13cba492016-08-03 10:43:55 -070011#include "SkStream.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040012#include "SkTo.h"
halcanary13cba492016-08-03 10:43:55 -070013#include "SkTypeface.h"
Ben Wagner50becee2019-02-12 10:41:16 -050014#include "SkUTF.h"
halcanary13cba492016-08-03 10:43:55 -070015
Ben Wagnerb0591942019-02-15 14:46:18 -050016class SkShaperPrimitive : public SkShaper {
17public:
18 SkShaperPrimitive() {}
19private:
20 SkPoint shape(RunHandler* handler,
21 const SkFont& srcFont,
Ben Wagner1e08a7c2019-03-27 15:37:13 -040022 const char* utf8, size_t utf8Bytes,
Ben Wagnerb0591942019-02-15 14:46:18 -050023 bool leftToRight,
24 SkPoint point,
25 SkScalar width) const override;
Ben Wagner1e08a7c2019-03-27 15:37:13 -040026
27 SkPoint shape(const char* utf8, size_t utf8Bytes,
28 FontRunIterator&,
29 BiDiRunIterator&,
30 ScriptRunIterator&,
31 LanguageRunIterator&,
32 SkPoint point,
33 SkScalar width,
34 RunHandler*) const override;
Ben Wagnerb0591942019-02-15 14:46:18 -050035};
halcanary13cba492016-08-03 10:43:55 -070036
Ben Wagnerb0591942019-02-15 14:46:18 -050037std::unique_ptr<SkShaper> SkShaper::MakePrimitive() {
38 return skstd::make_unique<SkShaperPrimitive>();
39}
halcanary13cba492016-08-03 10:43:55 -070040
Ben Wagner50becee2019-02-12 10:41:16 -050041static inline bool is_breaking_whitespace(SkUnichar c) {
42 switch (c) {
43 case 0x0020: // SPACE
44 //case 0x00A0: // NO-BREAK SPACE
45 case 0x1680: // OGHAM SPACE MARK
46 case 0x180E: // MONGOLIAN VOWEL SEPARATOR
47 case 0x2000: // EN QUAD
48 case 0x2001: // EM QUAD
49 case 0x2002: // EN SPACE (nut)
50 case 0x2003: // EM SPACE (mutton)
51 case 0x2004: // THREE-PER-EM SPACE (thick space)
52 case 0x2005: // FOUR-PER-EM SPACE (mid space)
53 case 0x2006: // SIX-PER-EM SPACE
54 case 0x2007: // FIGURE SPACE
55 case 0x2008: // PUNCTUATION SPACE
56 case 0x2009: // THIN SPACE
57 case 0x200A: // HAIR SPACE
58 case 0x200B: // ZERO WIDTH SPACE
59 case 0x202F: // NARROW NO-BREAK SPACE
60 case 0x205F: // MEDIUM MATHEMATICAL SPACE
61 case 0x3000: // IDEOGRAPHIC SPACE
62 //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
63 return true;
64 default:
65 return false;
66 }
67}
68
69static size_t linebreak(const char text[], const char stop[],
70 const SkFont& font, SkScalar width,
71 SkScalar* advance,
72 size_t* trailing)
73{
74 SkScalar accumulatedWidth = 0;
75 int glyphIndex = 0;
76 const char* start = text;
77 const char* word_start = text;
78 bool prevWS = true;
79 *trailing = 0;
80
81 while (text < stop) {
82 const char* prevText = text;
83 SkUnichar uni = SkUTF::NextUTF8(&text, stop);
84 accumulatedWidth += advance[glyphIndex++];
85 bool currWS = is_breaking_whitespace(uni);
86
87 if (!currWS && prevWS) {
88 word_start = prevText;
89 }
90 prevWS = currWS;
91
92 if (width < accumulatedWidth) {
93 if (currWS) {
94 // eat the rest of the whitespace
95 const char* next = text;
96 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
97 text = next;
98 }
99 if (trailing) {
100 *trailing = text - prevText;
101 }
102 } else {
103 // backup until a whitespace (or 1 char)
104 if (word_start == start) {
105 if (prevText > start) {
106 text = prevText;
107 }
108 } else {
109 text = word_start;
110 }
111 }
112 break;
113 }
Ben Wagner50becee2019-02-12 10:41:16 -0500114 }
115
116 return text - start;
halcanary3eee9d92016-09-10 07:01:53 -0700117}
118
Ben Wagner1e08a7c2019-03-27 15:37:13 -0400119SkPoint SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
120 FontRunIterator& font,
121 BiDiRunIterator& bidi,
122 ScriptRunIterator&,
123 LanguageRunIterator& ,
124 SkPoint point,
125 SkScalar width,
126 RunHandler* handler) const
127{
128 font.consume();
129 bidi.consume();
130 return this->shape(handler, font.currentFont(), utf8, utf8Bytes,
131 (bidi.currentLevel() % 2) == 0, point, width);
132}
133
Ben Wagnerb0591942019-02-15 14:46:18 -0500134SkPoint SkShaperPrimitive::shape(RunHandler* handler,
135 const SkFont& font,
136 const char* utf8text,
137 size_t textBytes,
138 bool leftToRight,
139 SkPoint point,
140 SkScalar width) const {
Ben Wagnera25fbef2017-08-30 13:56:19 -0400141 sk_ignore_unused_variable(leftToRight);
142
Mike Reed6d595682018-12-05 17:28:14 -0500143 int glyphCount = font.countText(utf8text, textBytes, SkTextEncoding::kUTF8);
halcanary13cba492016-08-03 10:43:55 -0700144 if (glyphCount <= 0) {
Ben Wagner5d4dd8b2018-01-25 14:37:17 -0500145 return point;
halcanary13cba492016-08-03 10:43:55 -0700146 }
Florin Malita9867f612018-12-12 10:54:49 -0500147
Ben Wagner50becee2019-02-12 10:41:16 -0500148 std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
149 font.textToGlyphs(utf8text, textBytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
150
151 std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
152 font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
153
Mike Reedb5784ac2018-11-12 09:35:15 -0500154 SkFontMetrics metrics;
Mike Reed6d595682018-12-05 17:28:14 -0500155 font.getMetrics(&metrics);
Ben Wagner5d4dd8b2018-01-25 14:37:17 -0500156
Ben Wagner50becee2019-02-12 10:41:16 -0500157 size_t glyphOffset = 0;
158 size_t utf8Offset = 0;
159 while (0 < textBytes) {
160 point.fY -= metrics.fAscent;
Florin Malita9867f612018-12-12 10:54:49 -0500161
Ben Wagner50becee2019-02-12 10:41:16 -0500162 size_t bytesCollapsed;
163 size_t bytesConsumed = linebreak(utf8text, utf8text + textBytes, font, width,
164 advances.get() + glyphOffset, &bytesCollapsed);
165 size_t bytesVisible = bytesConsumed - bytesCollapsed;
166
167 int numGlyphs = SkUTF::CountUTF8(utf8text, bytesVisible);
168 const RunHandler::RunInfo info = {
169 { font.measureText(utf8text, bytesVisible, SkTextEncoding::kUTF8), 0 },
170 metrics.fAscent,
171 metrics.fDescent,
172 metrics.fLeading,
173 };
174 const auto buffer = handler->newRunBuffer(info, font, numGlyphs,
Ben Wagner7415a422019-03-25 15:38:22 -0400175 RunHandler::Range(utf8Offset, bytesVisible));
Ben Wagner50becee2019-02-12 10:41:16 -0500176
177 memcpy(buffer.glyphs, glyphs.get() + glyphOffset, numGlyphs * sizeof(SkGlyphID));
178 SkScalar position = point.fX;
179 for (int i = 0; i < numGlyphs; ++i) {
180 buffer.positions[i] = { position, point.fY };
181 position += advances[i + glyphOffset];
Florin Malita9867f612018-12-12 10:54:49 -0500182 }
Ben Wagner50becee2019-02-12 10:41:16 -0500183 if (buffer.clusters) {
184 const char* txtPtr = utf8text;
185 for (int i = 0; i < numGlyphs; ++i) {
186 // Each character maps to exactly one glyph.
187 buffer.clusters[i] = SkToU32(txtPtr - utf8text + utf8Offset);
188 SkUTF::NextUTF8(&txtPtr, utf8text + textBytes);
189 }
190 }
191 handler->commitRun();
192 handler->commitLine();
Florin Malita500133b2019-02-07 10:56:55 -0500193
Ben Wagner50becee2019-02-12 10:41:16 -0500194 glyphOffset += SkUTF::CountUTF8(utf8text, bytesConsumed);
195 utf8Offset += bytesConsumed;
196 utf8text += bytesConsumed;
197 textBytes -= bytesConsumed;
198 point.fY += metrics.fDescent + metrics.fLeading;
199 }
200
201 return point;
halcanary13cba492016-08-03 10:43:55 -0700202}