blob: be436e4c5deca0871b82f6240e59ab69553a4e28 [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
halcanary13cba492016-08-03 10:43:55 -07008#include "SkShaper.h"
Mike Reed9a4a05e2019-01-21 20:42:04 -05009#include "SkFontMetrics.h"
halcanary13cba492016-08-03 10:43:55 -070010#include "SkStream.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040011#include "SkTo.h"
halcanary13cba492016-08-03 10:43:55 -070012#include "SkTypeface.h"
Ben Wagner50becee2019-02-12 10:41:16 -050013#include "SkUTF.h"
halcanary13cba492016-08-03 10:43:55 -070014
Ben Wagner50becee2019-02-12 10:41:16 -050015struct SkShaper::Impl {};
16SkShaper::SkShaper(sk_sp<SkTypeface> tf) : fImpl(new Impl()) {}
halcanary13cba492016-08-03 10:43:55 -070017
18SkShaper::~SkShaper() {}
19
20bool SkShaper::good() const { return true; }
21
Ben Wagner50becee2019-02-12 10:41:16 -050022static inline bool is_breaking_whitespace(SkUnichar c) {
23 switch (c) {
24 case 0x0020: // SPACE
25 //case 0x00A0: // NO-BREAK SPACE
26 case 0x1680: // OGHAM SPACE MARK
27 case 0x180E: // MONGOLIAN VOWEL SEPARATOR
28 case 0x2000: // EN QUAD
29 case 0x2001: // EM QUAD
30 case 0x2002: // EN SPACE (nut)
31 case 0x2003: // EM SPACE (mutton)
32 case 0x2004: // THREE-PER-EM SPACE (thick space)
33 case 0x2005: // FOUR-PER-EM SPACE (mid space)
34 case 0x2006: // SIX-PER-EM SPACE
35 case 0x2007: // FIGURE SPACE
36 case 0x2008: // PUNCTUATION SPACE
37 case 0x2009: // THIN SPACE
38 case 0x200A: // HAIR SPACE
39 case 0x200B: // ZERO WIDTH SPACE
40 case 0x202F: // NARROW NO-BREAK SPACE
41 case 0x205F: // MEDIUM MATHEMATICAL SPACE
42 case 0x3000: // IDEOGRAPHIC SPACE
43 //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
44 return true;
45 default:
46 return false;
47 }
48}
49
50static size_t linebreak(const char text[], const char stop[],
51 const SkFont& font, SkScalar width,
52 SkScalar* advance,
53 size_t* trailing)
54{
55 SkScalar accumulatedWidth = 0;
56 int glyphIndex = 0;
57 const char* start = text;
58 const char* word_start = text;
59 bool prevWS = true;
60 *trailing = 0;
61
62 while (text < stop) {
63 const char* prevText = text;
64 SkUnichar uni = SkUTF::NextUTF8(&text, stop);
65 accumulatedWidth += advance[glyphIndex++];
66 bool currWS = is_breaking_whitespace(uni);
67
68 if (!currWS && prevWS) {
69 word_start = prevText;
70 }
71 prevWS = currWS;
72
73 if (width < accumulatedWidth) {
74 if (currWS) {
75 // eat the rest of the whitespace
76 const char* next = text;
77 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
78 text = next;
79 }
80 if (trailing) {
81 *trailing = text - prevText;
82 }
83 } else {
84 // backup until a whitespace (or 1 char)
85 if (word_start == start) {
86 if (prevText > start) {
87 text = prevText;
88 }
89 } else {
90 text = word_start;
91 }
92 }
93 break;
94 }
95
96 if ('\n' == uni) {
97 size_t ret = text - start;
98 size_t lineBreakSize = 1;
99 if (text < stop) {
100 uni = SkUTF::NextUTF8(&text, stop);
101 if ('\r' == uni) {
102 ret = text - start;
103 ++lineBreakSize;
104 }
105 }
106 if (trailing) {
107 *trailing = lineBreakSize;
108 }
109 return ret;
110 }
111
112 if ('\r' == uni) {
113 size_t ret = text - start;
114 size_t lineBreakSize = 1;
115 if (text < stop) {
116 uni = SkUTF::NextUTF8(&text, stop);
117 if ('\n' == uni) {
118 ret = text - start;
119 ++lineBreakSize;
120 }
121 }
122 if (trailing) {
123 *trailing = lineBreakSize;
124 }
125 return ret;
126 }
127 }
128
129 return text - start;
halcanary3eee9d92016-09-10 07:01:53 -0700130}
131
Florin Malita950243d2019-01-11 11:08:35 -0500132SkPoint SkShaper::shape(RunHandler* handler,
Ben Wagner50becee2019-02-12 10:41:16 -0500133 const SkFont& font,
Ben Wagner5d4dd8b2018-01-25 14:37:17 -0500134 const char* utf8text,
135 size_t textBytes,
136 bool leftToRight,
137 SkPoint point,
138 SkScalar width) const {
Ben Wagnera25fbef2017-08-30 13:56:19 -0400139 sk_ignore_unused_variable(leftToRight);
140
Mike Reed6d595682018-12-05 17:28:14 -0500141 int glyphCount = font.countText(utf8text, textBytes, SkTextEncoding::kUTF8);
halcanary13cba492016-08-03 10:43:55 -0700142 if (glyphCount <= 0) {
Ben Wagner5d4dd8b2018-01-25 14:37:17 -0500143 return point;
halcanary13cba492016-08-03 10:43:55 -0700144 }
Florin Malita9867f612018-12-12 10:54:49 -0500145
Ben Wagner50becee2019-02-12 10:41:16 -0500146 std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
147 font.textToGlyphs(utf8text, textBytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
148
149 std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
150 font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
151
Mike Reedb5784ac2018-11-12 09:35:15 -0500152 SkFontMetrics metrics;
Mike Reed6d595682018-12-05 17:28:14 -0500153 font.getMetrics(&metrics);
Ben Wagner5d4dd8b2018-01-25 14:37:17 -0500154
Ben Wagner50becee2019-02-12 10:41:16 -0500155 size_t glyphOffset = 0;
156 size_t utf8Offset = 0;
157 while (0 < textBytes) {
158 point.fY -= metrics.fAscent;
Florin Malita9867f612018-12-12 10:54:49 -0500159
Ben Wagner50becee2019-02-12 10:41:16 -0500160 size_t bytesCollapsed;
161 size_t bytesConsumed = linebreak(utf8text, utf8text + textBytes, font, width,
162 advances.get() + glyphOffset, &bytesCollapsed);
163 size_t bytesVisible = bytesConsumed - bytesCollapsed;
164
165 int numGlyphs = SkUTF::CountUTF8(utf8text, bytesVisible);
166 const RunHandler::RunInfo info = {
167 { font.measureText(utf8text, bytesVisible, SkTextEncoding::kUTF8), 0 },
168 metrics.fAscent,
169 metrics.fDescent,
170 metrics.fLeading,
171 };
172 const auto buffer = handler->newRunBuffer(info, font, numGlyphs,
173 SkSpan<const char>(utf8text, bytesVisible));
174
175 memcpy(buffer.glyphs, glyphs.get() + glyphOffset, numGlyphs * sizeof(SkGlyphID));
176 SkScalar position = point.fX;
177 for (int i = 0; i < numGlyphs; ++i) {
178 buffer.positions[i] = { position, point.fY };
179 position += advances[i + glyphOffset];
Florin Malita9867f612018-12-12 10:54:49 -0500180 }
Ben Wagner50becee2019-02-12 10:41:16 -0500181 if (buffer.clusters) {
182 const char* txtPtr = utf8text;
183 for (int i = 0; i < numGlyphs; ++i) {
184 // Each character maps to exactly one glyph.
185 buffer.clusters[i] = SkToU32(txtPtr - utf8text + utf8Offset);
186 SkUTF::NextUTF8(&txtPtr, utf8text + textBytes);
187 }
188 }
189 handler->commitRun();
190 handler->commitLine();
Florin Malita500133b2019-02-07 10:56:55 -0500191
Ben Wagner50becee2019-02-12 10:41:16 -0500192 glyphOffset += SkUTF::CountUTF8(utf8text, bytesConsumed);
193 utf8Offset += bytesConsumed;
194 utf8text += bytesConsumed;
195 textBytes -= bytesConsumed;
196 point.fY += metrics.fDescent + metrics.fLeading;
197 }
198
199 return point;
halcanary13cba492016-08-03 10:43:55 -0700200}