• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
7 
8 #include "include/core/SkFontMetrics.h"
9 #include "include/core/SkStream.h"
10 #include "include/core/SkTypeface.h"
11 #include "include/private/base/SkTo.h"
12 #include "modules/skshaper/include/SkShaper.h"
13 #include "src/base/SkUTF.h"
14 
15 class SkShaperPrimitive : public SkShaper {
16 public:
SkShaperPrimitive()17     SkShaperPrimitive() {}
18 private:
19     void shape(const char* utf8, size_t utf8Bytes,
20                const SkFont& srcFont,
21                bool leftToRight,
22                SkScalar width,
23                RunHandler*) const override;
24 
25     void shape(const char* utf8, size_t utf8Bytes,
26                FontRunIterator&,
27                BiDiRunIterator&,
28                ScriptRunIterator&,
29                LanguageRunIterator&,
30                SkScalar width,
31                RunHandler*) const override;
32 
33     void shape(const char* utf8, size_t utf8Bytes,
34                FontRunIterator&,
35                BiDiRunIterator&,
36                ScriptRunIterator&,
37                LanguageRunIterator&,
38                const Feature*, size_t featureSize,
39                SkScalar width,
40                RunHandler*) const override;
41 };
42 
MakePrimitive()43 std::unique_ptr<SkShaper> SkShaper::MakePrimitive() {
44     return std::make_unique<SkShaperPrimitive>();
45 }
46 
is_breaking_whitespace(SkUnichar c)47 static inline bool is_breaking_whitespace(SkUnichar c) {
48     switch (c) {
49         case 0x0020: // SPACE
50         //case 0x00A0: // NO-BREAK SPACE
51         case 0x1680: // OGHAM SPACE MARK
52         case 0x180E: // MONGOLIAN VOWEL SEPARATOR
53         case 0x2000: // EN QUAD
54         case 0x2001: // EM QUAD
55         case 0x2002: // EN SPACE (nut)
56         case 0x2003: // EM SPACE (mutton)
57         case 0x2004: // THREE-PER-EM SPACE (thick space)
58         case 0x2005: // FOUR-PER-EM SPACE (mid space)
59         case 0x2006: // SIX-PER-EM SPACE
60         case 0x2007: // FIGURE SPACE
61         case 0x2008: // PUNCTUATION SPACE
62         case 0x2009: // THIN SPACE
63         case 0x200A: // HAIR SPACE
64         case 0x200B: // ZERO WIDTH SPACE
65         case 0x202F: // NARROW NO-BREAK SPACE
66         case 0x205F: // MEDIUM MATHEMATICAL SPACE
67         case 0x3000: // IDEOGRAPHIC SPACE
68         //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
69             return true;
70         default:
71             return false;
72     }
73 }
74 
linebreak(const char text[],const char stop[],const SkFont & font,SkScalar width,SkScalar * advance,size_t * trailing)75 static size_t linebreak(const char text[], const char stop[],
76                         const SkFont& font, SkScalar width,
77                         SkScalar* advance,
78                         size_t* trailing)
79 {
80     SkScalar accumulatedWidth = 0;
81     int glyphIndex = 0;
82     const char* start = text;
83     const char* wordStart = text;
84     bool prevWS = true;
85     *trailing = 0;
86 
87     while (text < stop) {
88         const char* prevText = text;
89         SkUnichar uni = SkUTF::NextUTF8(&text, stop);
90         accumulatedWidth += advance[glyphIndex++];
91         bool currWS = is_breaking_whitespace(uni);
92 
93         if (!currWS && prevWS) {
94             wordStart = prevText;
95         }
96         prevWS = currWS;
97 
98         if (width < accumulatedWidth) {
99             bool consumeWhitespace = false;
100             if (currWS) {
101                 // previous fit, put this and following whitespace in trailing
102                 if (prevText == start) {
103                     // don't put this in trailing if it's the first thing
104                     prevText = text;
105                 }
106                 consumeWhitespace = true;
107             } else if (wordStart != start) {
108                 // backup to the last whitespace that fit
109                 text = wordStart;
110             } else if (prevText > start) {
111                 // backup to just before the glyph that didn't fit
112                 text = prevText;
113             } else {
114                 // let it overflow, put any following whitespace in trailing
115                 prevText = text;
116                 consumeWhitespace = true;
117             }
118             if (consumeWhitespace) {
119                 const char* next = text;
120                 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
121                     text = next;
122                 }
123                 if (trailing) {
124                     *trailing = text - prevText;
125                 }
126             }
127             break;
128         }
129     }
130 
131     return text - start;
132 }
133 
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & font,BiDiRunIterator & bidi,ScriptRunIterator &,LanguageRunIterator &,SkScalar width,RunHandler * handler) const134 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
135                               FontRunIterator& font,
136                               BiDiRunIterator& bidi,
137                               ScriptRunIterator&,
138                               LanguageRunIterator&,
139                               SkScalar width,
140                               RunHandler* handler) const
141 {
142     SkFont skfont;
143     if (!font.atEnd()) {
144         font.consume();
145         skfont = font.currentFont();
146     } else {
147         skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault()));
148     }
149     SkASSERT(skfont.getTypeface());
150     bool skbidi = 0;
151     if (!bidi.atEnd()) {
152         bidi.consume();
153         skbidi = (bidi.currentLevel() % 2) == 0;
154     }
155     return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
156 }
157 
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & font,BiDiRunIterator & bidi,ScriptRunIterator &,LanguageRunIterator &,const Feature *,size_t,SkScalar width,RunHandler * handler) const158 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
159                               FontRunIterator& font,
160                               BiDiRunIterator& bidi,
161                               ScriptRunIterator&,
162                               LanguageRunIterator&,
163                               const Feature*, size_t,
164                               SkScalar width,
165                               RunHandler* handler) const {
166     font.consume();
167     SkASSERT(font.currentFont().getTypeface());
168     bidi.consume();
169     return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
170                        width, handler);
171 }
172 
shape(const char * utf8,size_t utf8Bytes,const SkFont & font,bool leftToRight,SkScalar width,RunHandler * handler) const173 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
174                               const SkFont& font,
175                               bool leftToRight,
176                               SkScalar width,
177                               RunHandler* handler) const {
178     sk_ignore_unused_variable(leftToRight);
179 
180     int glyphCount = font.countText(utf8, utf8Bytes, SkTextEncoding::kUTF8);
181     if (glyphCount < 0) {
182         return;
183     }
184 
185     std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
186     font.textToGlyphs(utf8, utf8Bytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
187 
188     std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
189     font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
190 
191     size_t glyphOffset = 0;
192     size_t utf8Offset = 0;
193     do {
194         size_t bytesCollapsed;
195         size_t bytesConsumed = linebreak(utf8, utf8 + utf8Bytes, font, width,
196                                          advances.get() + glyphOffset, &bytesCollapsed);
197         size_t bytesVisible = bytesConsumed - bytesCollapsed;
198 
199         size_t numGlyphs = SkUTF::CountUTF8(utf8, bytesVisible);
200         const RunHandler::RunInfo info = {
201             font,
202             0,
203             { font.measureText(utf8, bytesVisible, SkTextEncoding::kUTF8), 0 },
204             numGlyphs,
205             RunHandler::Range(utf8Offset, bytesVisible)
206         };
207         handler->beginLine();
208         if (info.glyphCount) {
209             handler->runInfo(info);
210         }
211         handler->commitRunInfo();
212         if (info.glyphCount) {
213             const auto buffer = handler->runBuffer(info);
214 
215             memcpy(buffer.glyphs, glyphs.get() + glyphOffset, info.glyphCount * sizeof(SkGlyphID));
216             SkPoint position = buffer.point;
217             for (size_t i = 0; i < info.glyphCount; ++i) {
218                 buffer.positions[i] = position;
219                 position.fX += advances[i + glyphOffset];
220             }
221             if (buffer.clusters) {
222                 const char* txtPtr = utf8;
223                 for (size_t i = 0; i < info.glyphCount; ++i) {
224                     // Each character maps to exactly one glyph.
225                     buffer.clusters[i] = SkToU32(txtPtr - utf8 + utf8Offset);
226                     SkUTF::NextUTF8(&txtPtr, utf8 + utf8Bytes);
227                 }
228             }
229             handler->commitRunBuffer(info);
230         }
231         handler->commitLine();
232 
233         glyphOffset += SkUTF::CountUTF8(utf8, bytesConsumed);
234         utf8Offset += bytesConsumed;
235         utf8 += bytesConsumed;
236         utf8Bytes -= bytesConsumed;
237     } while (0 < utf8Bytes);
238 
239     return;
240 }
241