blob: f69fecb84619c9d8f07cca66af381aff35fdee68 [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,
22 const char* utf8text,
23 size_t textBytes,
24 bool leftToRight,
25 SkPoint point,
26 SkScalar width) const override;
27};
halcanary13cba492016-08-03 10:43:55 -070028
Ben Wagnerb0591942019-02-15 14:46:18 -050029std::unique_ptr<SkShaper> SkShaper::MakePrimitive() {
30 return skstd::make_unique<SkShaperPrimitive>();
31}
halcanary13cba492016-08-03 10:43:55 -070032
Ben Wagner50becee2019-02-12 10:41:16 -050033static inline bool is_breaking_whitespace(SkUnichar c) {
34 switch (c) {
35 case 0x0020: // SPACE
36 //case 0x00A0: // NO-BREAK SPACE
37 case 0x1680: // OGHAM SPACE MARK
38 case 0x180E: // MONGOLIAN VOWEL SEPARATOR
39 case 0x2000: // EN QUAD
40 case 0x2001: // EM QUAD
41 case 0x2002: // EN SPACE (nut)
42 case 0x2003: // EM SPACE (mutton)
43 case 0x2004: // THREE-PER-EM SPACE (thick space)
44 case 0x2005: // FOUR-PER-EM SPACE (mid space)
45 case 0x2006: // SIX-PER-EM SPACE
46 case 0x2007: // FIGURE SPACE
47 case 0x2008: // PUNCTUATION SPACE
48 case 0x2009: // THIN SPACE
49 case 0x200A: // HAIR SPACE
50 case 0x200B: // ZERO WIDTH SPACE
51 case 0x202F: // NARROW NO-BREAK SPACE
52 case 0x205F: // MEDIUM MATHEMATICAL SPACE
53 case 0x3000: // IDEOGRAPHIC SPACE
54 //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
55 return true;
56 default:
57 return false;
58 }
59}
60
61static size_t linebreak(const char text[], const char stop[],
62 const SkFont& font, SkScalar width,
63 SkScalar* advance,
64 size_t* trailing)
65{
66 SkScalar accumulatedWidth = 0;
67 int glyphIndex = 0;
68 const char* start = text;
69 const char* word_start = text;
70 bool prevWS = true;
71 *trailing = 0;
72
73 while (text < stop) {
74 const char* prevText = text;
75 SkUnichar uni = SkUTF::NextUTF8(&text, stop);
76 accumulatedWidth += advance[glyphIndex++];
77 bool currWS = is_breaking_whitespace(uni);
78
79 if (!currWS && prevWS) {
80 word_start = prevText;
81 }
82 prevWS = currWS;
83
84 if (width < accumulatedWidth) {
85 if (currWS) {
86 // eat the rest of the whitespace
87 const char* next = text;
88 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
89 text = next;
90 }
91 if (trailing) {
92 *trailing = text - prevText;
93 }
94 } else {
95 // backup until a whitespace (or 1 char)
96 if (word_start == start) {
97 if (prevText > start) {
98 text = prevText;
99 }
100 } else {
101 text = word_start;
102 }
103 }
104 break;
105 }
106
107 if ('\n' == uni) {
108 size_t ret = text - start;
109 size_t lineBreakSize = 1;
110 if (text < stop) {
111 uni = SkUTF::NextUTF8(&text, stop);
112 if ('\r' == uni) {
113 ret = text - start;
114 ++lineBreakSize;
115 }
116 }
117 if (trailing) {
118 *trailing = lineBreakSize;
119 }
120 return ret;
121 }
122
123 if ('\r' == uni) {
124 size_t ret = text - start;
125 size_t lineBreakSize = 1;
126 if (text < stop) {
127 uni = SkUTF::NextUTF8(&text, stop);
128 if ('\n' == uni) {
129 ret = text - start;
130 ++lineBreakSize;
131 }
132 }
133 if (trailing) {
134 *trailing = lineBreakSize;
135 }
136 return ret;
137 }
138 }
139
140 return text - start;
halcanary3eee9d92016-09-10 07:01:53 -0700141}
142
Ben Wagnerb0591942019-02-15 14:46:18 -0500143SkPoint SkShaperPrimitive::shape(RunHandler* handler,
144 const SkFont& font,
145 const char* utf8text,
146 size_t textBytes,
147 bool leftToRight,
148 SkPoint point,
149 SkScalar width) const {
Ben Wagnera25fbef2017-08-30 13:56:19 -0400150 sk_ignore_unused_variable(leftToRight);
151
Mike Reed6d595682018-12-05 17:28:14 -0500152 int glyphCount = font.countText(utf8text, textBytes, SkTextEncoding::kUTF8);
halcanary13cba492016-08-03 10:43:55 -0700153 if (glyphCount <= 0) {
Ben Wagner5d4dd8b2018-01-25 14:37:17 -0500154 return point;
halcanary13cba492016-08-03 10:43:55 -0700155 }
Florin Malita9867f612018-12-12 10:54:49 -0500156
Ben Wagner50becee2019-02-12 10:41:16 -0500157 std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
158 font.textToGlyphs(utf8text, textBytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
159
160 std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
161 font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
162
Mike Reedb5784ac2018-11-12 09:35:15 -0500163 SkFontMetrics metrics;
Mike Reed6d595682018-12-05 17:28:14 -0500164 font.getMetrics(&metrics);
Ben Wagner5d4dd8b2018-01-25 14:37:17 -0500165
Ben Wagner50becee2019-02-12 10:41:16 -0500166 size_t glyphOffset = 0;
167 size_t utf8Offset = 0;
168 while (0 < textBytes) {
169 point.fY -= metrics.fAscent;
Florin Malita9867f612018-12-12 10:54:49 -0500170
Ben Wagner50becee2019-02-12 10:41:16 -0500171 size_t bytesCollapsed;
172 size_t bytesConsumed = linebreak(utf8text, utf8text + textBytes, font, width,
173 advances.get() + glyphOffset, &bytesCollapsed);
174 size_t bytesVisible = bytesConsumed - bytesCollapsed;
175
176 int numGlyphs = SkUTF::CountUTF8(utf8text, bytesVisible);
177 const RunHandler::RunInfo info = {
178 { font.measureText(utf8text, bytesVisible, SkTextEncoding::kUTF8), 0 },
179 metrics.fAscent,
180 metrics.fDescent,
181 metrics.fLeading,
182 };
183 const auto buffer = handler->newRunBuffer(info, font, numGlyphs,
184 SkSpan<const char>(utf8text, bytesVisible));
185
186 memcpy(buffer.glyphs, glyphs.get() + glyphOffset, numGlyphs * sizeof(SkGlyphID));
187 SkScalar position = point.fX;
188 for (int i = 0; i < numGlyphs; ++i) {
189 buffer.positions[i] = { position, point.fY };
190 position += advances[i + glyphOffset];
Florin Malita9867f612018-12-12 10:54:49 -0500191 }
Ben Wagner50becee2019-02-12 10:41:16 -0500192 if (buffer.clusters) {
193 const char* txtPtr = utf8text;
194 for (int i = 0; i < numGlyphs; ++i) {
195 // Each character maps to exactly one glyph.
196 buffer.clusters[i] = SkToU32(txtPtr - utf8text + utf8Offset);
197 SkUTF::NextUTF8(&txtPtr, utf8text + textBytes);
198 }
199 }
200 handler->commitRun();
201 handler->commitLine();
Florin Malita500133b2019-02-07 10:56:55 -0500202
Ben Wagner50becee2019-02-12 10:41:16 -0500203 glyphOffset += SkUTF::CountUTF8(utf8text, bytesConsumed);
204 utf8Offset += bytesConsumed;
205 utf8text += bytesConsumed;
206 textBytes -= bytesConsumed;
207 point.fY += metrics.fDescent + metrics.fLeading;
208 }
209
210 return point;
halcanary13cba492016-08-03 10:43:55 -0700211}