• 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/SkTo.h"
12 #include "modules/skshaper/include/SkShaper.h"
13 #include "src/core/SkMakeUnique.h"
14 #include "src/utils/SkUTF.h"
15 
16 class SkShaperPrimitive : public SkShaper {
17 public:
SkShaperPrimitive()18     SkShaperPrimitive() {}
19 private:
20     void shape(const char* utf8, size_t utf8Bytes,
21                const SkFont& srcFont,
22                bool leftToRight,
23                SkScalar width,
24                RunHandler*) const override;
25 
26     void shape(const char* utf8, size_t utf8Bytes,
27                FontRunIterator&,
28                BiDiRunIterator&,
29                ScriptRunIterator&,
30                LanguageRunIterator&,
31                SkScalar width,
32                RunHandler*) const override;
33 };
34 
MakePrimitive()35 std::unique_ptr<SkShaper> SkShaper::MakePrimitive() {
36     return skstd::make_unique<SkShaperPrimitive>();
37 }
38 
is_breaking_whitespace(SkUnichar c)39 static inline bool is_breaking_whitespace(SkUnichar c) {
40     switch (c) {
41         case 0x0020: // SPACE
42         //case 0x00A0: // NO-BREAK SPACE
43         case 0x1680: // OGHAM SPACE MARK
44         case 0x180E: // MONGOLIAN VOWEL SEPARATOR
45         case 0x2000: // EN QUAD
46         case 0x2001: // EM QUAD
47         case 0x2002: // EN SPACE (nut)
48         case 0x2003: // EM SPACE (mutton)
49         case 0x2004: // THREE-PER-EM SPACE (thick space)
50         case 0x2005: // FOUR-PER-EM SPACE (mid space)
51         case 0x2006: // SIX-PER-EM SPACE
52         case 0x2007: // FIGURE SPACE
53         case 0x2008: // PUNCTUATION SPACE
54         case 0x2009: // THIN SPACE
55         case 0x200A: // HAIR SPACE
56         case 0x200B: // ZERO WIDTH SPACE
57         case 0x202F: // NARROW NO-BREAK SPACE
58         case 0x205F: // MEDIUM MATHEMATICAL SPACE
59         case 0x3000: // IDEOGRAPHIC SPACE
60         //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
61             return true;
62         default:
63             return false;
64     }
65 }
66 
linebreak(const char text[],const char stop[],const SkFont & font,SkScalar width,SkScalar * advance,size_t * trailing)67 static size_t linebreak(const char text[], const char stop[],
68                         const SkFont& font, SkScalar width,
69                         SkScalar* advance,
70                         size_t* trailing)
71 {
72     SkScalar accumulatedWidth = 0;
73     int glyphIndex = 0;
74     const char* start = text;
75     const char* word_start = text;
76     bool prevWS = true;
77     *trailing = 0;
78 
79     while (text < stop) {
80         const char* prevText = text;
81         SkUnichar uni = SkUTF::NextUTF8(&text, stop);
82         accumulatedWidth += advance[glyphIndex++];
83         bool currWS = is_breaking_whitespace(uni);
84 
85         if (!currWS && prevWS) {
86             word_start = prevText;
87         }
88         prevWS = currWS;
89 
90         if (width < accumulatedWidth) {
91             if (currWS) {
92                 // eat the rest of the whitespace
93                 const char* next = text;
94                 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
95                     text = next;
96                 }
97                 if (trailing) {
98                     *trailing = text - prevText;
99                 }
100             } else {
101                 // backup until a whitespace (or 1 char)
102                 if (word_start == start) {
103                     if (prevText > start) {
104                         text = prevText;
105                     }
106                 } else {
107                     text = word_start;
108                 }
109             }
110             break;
111         }
112     }
113 
114     return text - start;
115 }
116 
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & font,BiDiRunIterator & bidi,ScriptRunIterator &,LanguageRunIterator &,SkScalar width,RunHandler * handler) const117 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
118                               FontRunIterator& font,
119                               BiDiRunIterator& bidi,
120                               ScriptRunIterator&,
121                               LanguageRunIterator&,
122                               SkScalar width,
123                               RunHandler* handler) const
124 {
125     font.consume();
126     SkASSERT(font.currentFont().getTypeface());
127     bidi.consume();
128     return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
129                        width, handler);
130 }
131 
shape(const char * utf8,size_t utf8Bytes,const SkFont & font,bool leftToRight,SkScalar width,RunHandler * handler) const132 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
133                               const SkFont& font,
134                               bool leftToRight,
135                               SkScalar width,
136                               RunHandler* handler) const {
137     sk_ignore_unused_variable(leftToRight);
138 
139     int glyphCount = font.countText(utf8, utf8Bytes, SkTextEncoding::kUTF8);
140     if (glyphCount <= 0) {
141         return;
142     }
143 
144     std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
145     font.textToGlyphs(utf8, utf8Bytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
146 
147     std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
148     font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
149 
150     size_t glyphOffset = 0;
151     size_t utf8Offset = 0;
152     while (0 < utf8Bytes) {
153         size_t bytesCollapsed;
154         size_t bytesConsumed = linebreak(utf8, utf8 + utf8Bytes, font, width,
155                                          advances.get() + glyphOffset, &bytesCollapsed);
156         size_t bytesVisible = bytesConsumed - bytesCollapsed;
157 
158         size_t numGlyphs = SkUTF::CountUTF8(utf8, bytesVisible);
159         const RunHandler::RunInfo info = {
160             font,
161             0,
162             { font.measureText(utf8, bytesVisible, SkTextEncoding::kUTF8), 0 },
163             numGlyphs,
164             RunHandler::Range(utf8Offset, bytesVisible)
165         };
166         handler->beginLine();
167         handler->runInfo(info);
168         handler->commitRunInfo();
169         const auto buffer = handler->runBuffer(info);
170 
171         memcpy(buffer.glyphs, glyphs.get() + glyphOffset, numGlyphs * sizeof(SkGlyphID));
172         SkPoint position = buffer.point;
173         for (size_t i = 0; i < numGlyphs; ++i) {
174             buffer.positions[i] = position;
175             position.fX += advances[i + glyphOffset];
176         }
177         if (buffer.clusters) {
178             const char* txtPtr = utf8;
179             for (size_t i = 0; i < numGlyphs; ++i) {
180                 // Each character maps to exactly one glyph.
181                 buffer.clusters[i] = SkToU32(txtPtr - utf8 + utf8Offset);
182                 SkUTF::NextUTF8(&txtPtr, utf8 + utf8Bytes);
183             }
184         }
185         handler->commitRunBuffer(info);
186         handler->commitLine();
187 
188         glyphOffset += SkUTF::CountUTF8(utf8, bytesConsumed);
189         utf8Offset += bytesConsumed;
190         utf8 += bytesConsumed;
191         utf8Bytes -= bytesConsumed;
192     }
193 
194     return;
195 }
196