1 | /* |
2 | * Copyright 2019 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 "src/utils/SkShaperJSONWriter.h" |
9 | |
10 | #include <algorithm> |
11 | #include <limits> |
12 | #include <string> |
13 | |
14 | #include "src/utils/SkJSONWriter.h" |
15 | #include "src/utils/SkUTF.h" |
16 | |
17 | SkShaperJSONWriter::SkShaperJSONWriter(SkJSONWriter* JSONWriter, const char* utf8, size_t size) |
18 | : fJSONWriter{JSONWriter} |
19 | , fUTF8{utf8, size} {} |
20 | |
21 | void SkShaperJSONWriter::beginLine() { } |
22 | |
23 | void SkShaperJSONWriter::runInfo(const SkShaper::RunHandler::RunInfo& info) { } |
24 | |
25 | void SkShaperJSONWriter::commitRunInfo() { } |
26 | |
27 | SkShaper::RunHandler::Buffer |
28 | SkShaperJSONWriter::runBuffer(const SkShaper::RunHandler::RunInfo& info) { |
29 | fGlyphs.resize(info.glyphCount); |
30 | fPositions.resize(info.glyphCount); |
31 | fClusters.resize(info.glyphCount); |
32 | return {fGlyphs.data(), fPositions.data(), nullptr, fClusters.data(), {0, 0}}; |
33 | } |
34 | |
35 | static bool is_one_to_one(const char utf8[], size_t utf8Begin, size_t utf8End, |
36 | std::vector<uint32_t>& clusters) { |
37 | size_t lastUtf8Index = utf8End; |
38 | |
39 | auto checkCluster = [&](size_t clusterIndex) { |
40 | if (clusters[clusterIndex] >= lastUtf8Index) { |
41 | return false; |
42 | } |
43 | size_t utf8ClusterSize = lastUtf8Index - clusters[clusterIndex]; |
44 | if (SkUTF::CountUTF8(&utf8[clusters[clusterIndex]], utf8ClusterSize) != 1) { |
45 | return false; |
46 | } |
47 | lastUtf8Index = clusters[clusterIndex]; |
48 | return true; |
49 | }; |
50 | |
51 | if (clusters.front() <= clusters.back()) { |
52 | // left-to-right clusters |
53 | size_t clusterCursor = clusters.size(); |
54 | while (clusterCursor > 0) { |
55 | if (!checkCluster(--clusterCursor)) { return false; } |
56 | } |
57 | } else { |
58 | // right-to-left clusters |
59 | size_t clusterCursor = 0; |
60 | while (clusterCursor < clusters.size()) { |
61 | if (!checkCluster(clusterCursor++)) { return false; } |
62 | } |
63 | } |
64 | |
65 | return true; |
66 | } |
67 | |
68 | void SkShaperJSONWriter::commitRunBuffer(const SkShaper::RunHandler::RunInfo& info) { |
69 | fJSONWriter->beginObject("run" , true); |
70 | |
71 | // Font name |
72 | SkString fontName; |
73 | info.fFont.getTypeface()->getFamilyName(&fontName); |
74 | fJSONWriter->appendString("font name" , fontName.c_str()); |
75 | |
76 | // Font size |
77 | fJSONWriter->appendFloat("font size" , info.fFont.getSize()); |
78 | |
79 | if (info.fBidiLevel > 0) { |
80 | std::string bidiType = info.fBidiLevel % 2 == 0 ? "left-to-right" : "right-to-left" ; |
81 | std::string bidiOutput = bidiType + " lvl " + std::to_string(info.fBidiLevel); |
82 | fJSONWriter->appendString("BiDi" , bidiOutput.c_str()); |
83 | } |
84 | |
85 | if (is_one_to_one(fUTF8.c_str(), info.utf8Range.begin(), info.utf8Range.end(), fClusters)) { |
86 | std::string utf8{&fUTF8[info.utf8Range.begin()], info.utf8Range.size()}; |
87 | fJSONWriter->appendString("UTF8" , utf8.c_str()); |
88 | |
89 | fJSONWriter->beginArray("glyphs" , false); |
90 | for (auto glyphID : fGlyphs) { |
91 | fJSONWriter->appendU32(glyphID); |
92 | } |
93 | fJSONWriter->endArray(); |
94 | |
95 | fJSONWriter->beginArray("clusters" , false); |
96 | for (auto cluster : fClusters) { |
97 | fJSONWriter->appendU32(cluster); |
98 | } |
99 | fJSONWriter->endArray(); |
100 | } else { |
101 | VisualizeClusters(fUTF8.c_str(), |
102 | info.utf8Range.begin(), info.utf8Range.end(), |
103 | SkMakeSpan(fGlyphs), |
104 | SkMakeSpan(fClusters), |
105 | [this](size_t codePointCount, SkSpan<const char> utf1to1, |
106 | SkSpan<const SkGlyphID> glyph1to1) { |
107 | this->displayMToN(codePointCount, utf1to1, glyph1to1); |
108 | }); |
109 | } |
110 | |
111 | if (info.glyphCount > 1) { |
112 | fJSONWriter->beginArray("horizontal positions" , false); |
113 | for (auto position : fPositions) { |
114 | fJSONWriter->appendFloat(position.x()); |
115 | } |
116 | fJSONWriter->endArray(); |
117 | } |
118 | |
119 | fJSONWriter->beginArray("advances" , false); |
120 | for (size_t i = 1; i < info.glyphCount; i++) { |
121 | fJSONWriter->appendFloat(fPositions[i].fX - fPositions[i-1].fX); |
122 | } |
123 | SkPoint lastAdvance = info.fAdvance - (fPositions.back() - fPositions.front()); |
124 | fJSONWriter->appendFloat(lastAdvance.fX); |
125 | fJSONWriter->endArray(); |
126 | |
127 | fJSONWriter->endObject(); |
128 | } |
129 | |
130 | void SkShaperJSONWriter::BreakupClusters(size_t utf8Begin, size_t utf8End, |
131 | SkSpan<const uint32_t> clusters, |
132 | const BreakupCluastersCallback& processMToN) { |
133 | |
134 | if (clusters.front() <= clusters.back()) { |
135 | // Handle left-to-right text direction |
136 | size_t glyphStartIndex = 0; |
137 | for (size_t glyphEndIndex = 0; glyphEndIndex < clusters.size(); glyphEndIndex++) { |
138 | |
139 | if (clusters[glyphStartIndex] == clusters[glyphEndIndex]) { continue; } |
140 | |
141 | processMToN(glyphStartIndex, glyphEndIndex, |
142 | clusters[glyphStartIndex], clusters[glyphEndIndex]); |
143 | |
144 | glyphStartIndex = glyphEndIndex; |
145 | } |
146 | |
147 | processMToN(glyphStartIndex, clusters.size(), clusters[glyphStartIndex], utf8End); |
148 | |
149 | } else { |
150 | // Handle right-to-left text direction. |
151 | SkASSERT(clusters.size() >= 2); |
152 | size_t glyphStartIndex = 0; |
153 | uint32_t utf8EndIndex = utf8End; |
154 | for (size_t glyphEndIndex = 0; glyphEndIndex < clusters.size(); glyphEndIndex++) { |
155 | |
156 | if (clusters[glyphStartIndex] == clusters[glyphEndIndex]) { continue; } |
157 | |
158 | processMToN(glyphStartIndex, glyphEndIndex, |
159 | clusters[glyphStartIndex], utf8EndIndex); |
160 | |
161 | utf8EndIndex = clusters[glyphStartIndex]; |
162 | glyphStartIndex = glyphEndIndex; |
163 | } |
164 | processMToN(glyphStartIndex, clusters.size(), utf8Begin, clusters[glyphStartIndex-1]); |
165 | } |
166 | } |
167 | |
168 | void SkShaperJSONWriter::VisualizeClusters(const char* utf8, size_t utf8Begin, size_t utf8End, |
169 | SkSpan<const SkGlyphID> glyphIDs, |
170 | SkSpan<const uint32_t> clusters, |
171 | const VisualizeClustersCallback& processMToN) { |
172 | |
173 | size_t glyphRangeStart, glyphRangeEnd; |
174 | uint32_t utf8RangeStart, utf8RangeEnd; |
175 | |
176 | auto resetRanges = [&]() { |
177 | glyphRangeStart = std::numeric_limits<size_t>::max(); |
178 | glyphRangeEnd = 0; |
179 | utf8RangeStart = std::numeric_limits<uint32_t>::max(); |
180 | utf8RangeEnd = 0; |
181 | }; |
182 | |
183 | auto checkRangesAndProcess = [&]() { |
184 | if (glyphRangeStart < glyphRangeEnd) { |
185 | size_t glyphRangeCount = glyphRangeEnd - glyphRangeStart; |
186 | SkSpan<const char> utf8Span{&utf8[utf8RangeStart], utf8RangeEnd - utf8RangeStart}; |
187 | SkSpan<const SkGlyphID> glyphSpan{&glyphIDs[glyphRangeStart], glyphRangeCount}; |
188 | |
189 | // Glyph count is the same as codepoint count for 1:1. |
190 | processMToN(glyphRangeCount, utf8Span, glyphSpan); |
191 | } |
192 | resetRanges(); |
193 | }; |
194 | |
195 | auto gatherRuns = [&](size_t glyphStartIndex, size_t glyphEndIndex, |
196 | uint32_t utf8StartIndex, uint32_t utf8EndIndex) { |
197 | int possibleCount = SkUTF::CountUTF8(&utf8[utf8StartIndex], utf8EndIndex - utf8StartIndex); |
198 | if (possibleCount == -1) { return; } |
199 | size_t codePointCount = SkTo<size_t>(possibleCount); |
200 | if (codePointCount == 1 && glyphEndIndex - glyphStartIndex == 1) { |
201 | glyphRangeStart = std::min(glyphRangeStart, glyphStartIndex); |
202 | glyphRangeEnd = std::max(glyphRangeEnd, glyphEndIndex ); |
203 | utf8RangeStart = std::min(utf8RangeStart, utf8StartIndex ); |
204 | utf8RangeEnd = std::max(utf8RangeEnd, utf8EndIndex ); |
205 | } else { |
206 | checkRangesAndProcess(); |
207 | |
208 | SkSpan<const char> utf8Span{&utf8[utf8StartIndex], utf8EndIndex - utf8StartIndex}; |
209 | SkSpan<const SkGlyphID> glyphSpan{&glyphIDs[glyphStartIndex], |
210 | glyphEndIndex - glyphStartIndex}; |
211 | |
212 | processMToN(codePointCount, utf8Span, glyphSpan); |
213 | } |
214 | }; |
215 | |
216 | resetRanges(); |
217 | BreakupClusters(utf8Begin, utf8End, clusters, gatherRuns); |
218 | checkRangesAndProcess(); |
219 | } |
220 | |
221 | void SkShaperJSONWriter::displayMToN(size_t codePointCount, |
222 | SkSpan<const char> utf8, |
223 | SkSpan<const SkGlyphID> glyphIDs) { |
224 | std::string nString = std::to_string(codePointCount); |
225 | std::string mString = std::to_string(glyphIDs.size()); |
226 | std::string clusterName = "cluster " + nString + " to " + mString; |
227 | fJSONWriter->beginObject(clusterName.c_str(), true); |
228 | std::string utf8String{utf8.data(), utf8.size()}; |
229 | fJSONWriter->appendString("UTF" , utf8String.c_str()); |
230 | fJSONWriter->beginArray("glyphsIDs" , false); |
231 | for (auto glyphID : glyphIDs) { |
232 | fJSONWriter->appendU32(glyphID); |
233 | } |
234 | fJSONWriter->endArray(); |
235 | fJSONWriter->endObject(); |
236 | } |
237 | |