Flutter Engine
The Flutter Engine
SkShaperJSONWriter.cpp
Go to the documentation of this file.
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
9
10#include "include/core/SkFont.h"
11#include "include/core/SkSpan.h"
15#include "src/base/SkUTF.h"
17
18#include <algorithm>
19#include <cstddef>
20#include <limits>
21#include <string>
22#include <type_traits>
23
25 : fJSONWriter{JSONWriter}
26 , fUTF8{utf8, size} {}
27
29
31
33
36 fGlyphs.resize(info.glyphCount);
37 fPositions.resize(info.glyphCount);
38 fClusters.resize(info.glyphCount);
39 return {fGlyphs.data(), fPositions.data(), nullptr, fClusters.data(), {0, 0}};
40}
41
42static bool is_one_to_one(const char utf8[], size_t utf8Begin, size_t utf8End,
43 std::vector<uint32_t>& clusters) {
44 size_t lastUtf8Index = utf8End;
45
46 auto checkCluster = [&](size_t clusterIndex) {
47 if (clusters[clusterIndex] >= lastUtf8Index) {
48 return false;
49 }
50 size_t utf8ClusterSize = lastUtf8Index - clusters[clusterIndex];
51 if (SkUTF::CountUTF8(&utf8[clusters[clusterIndex]], utf8ClusterSize) != 1) {
52 return false;
53 }
54 lastUtf8Index = clusters[clusterIndex];
55 return true;
56 };
57
58 if (clusters.front() <= clusters.back()) {
59 // left-to-right clusters
60 size_t clusterCursor = clusters.size();
61 while (clusterCursor > 0) {
62 if (!checkCluster(--clusterCursor)) { return false; }
63 }
64 } else {
65 // right-to-left clusters
66 size_t clusterCursor = 0;
67 while (clusterCursor < clusters.size()) {
68 if (!checkCluster(clusterCursor++)) { return false; }
69 }
70 }
71
72 return true;
73}
74
76 fJSONWriter->beginObject("run", true);
77
78 // Font name
79 SkString fontName;
80 info.fFont.getTypeface()->getFamilyName(&fontName);
81 fJSONWriter->appendString("font name", fontName);
82
83 // Font size
84 fJSONWriter->appendFloat("font size", info.fFont.getSize());
85
86 if (info.fBidiLevel > 0) {
87 std::string bidiType = info.fBidiLevel % 2 == 0 ? "left-to-right" : "right-to-left";
88 std::string bidiOutput = bidiType + " lvl " + std::to_string(info.fBidiLevel);
89 fJSONWriter->appendString("BiDi", bidiOutput);
90 }
91
92 if (is_one_to_one(fUTF8.c_str(), info.utf8Range.begin(), info.utf8Range.end(), fClusters)) {
93 std::string utf8{&fUTF8[info.utf8Range.begin()], info.utf8Range.size()};
94 fJSONWriter->appendString("UTF8", utf8);
95
96 fJSONWriter->beginArray("glyphs", false);
97 for (auto glyphID : fGlyphs) {
98 fJSONWriter->appendU32(glyphID);
99 }
100 fJSONWriter->endArray();
101
102 fJSONWriter->beginArray("clusters", false);
103 for (auto cluster : fClusters) {
104 fJSONWriter->appendU32(cluster);
105 }
106 fJSONWriter->endArray();
107 } else {
108 VisualizeClusters(fUTF8.c_str(),
109 info.utf8Range.begin(), info.utf8Range.end(),
110 SkSpan(fGlyphs),
111 SkSpan(fClusters),
112 [this](size_t codePointCount, SkSpan<const char> utf1to1,
113 SkSpan<const SkGlyphID> glyph1to1) {
114 this->displayMToN(codePointCount, utf1to1, glyph1to1);
115 });
116 }
117
118 if (info.glyphCount > 1) {
119 fJSONWriter->beginArray("horizontal positions", false);
120 for (auto position : fPositions) {
121 fJSONWriter->appendFloat(position.x());
122 }
123 fJSONWriter->endArray();
124 }
125
126 fJSONWriter->beginArray("advances", false);
127 for (size_t i = 1; i < info.glyphCount; i++) {
128 fJSONWriter->appendFloat(fPositions[i].fX - fPositions[i-1].fX);
129 }
130 SkPoint lastAdvance = info.fAdvance - (fPositions.back() - fPositions.front());
131 fJSONWriter->appendFloat(lastAdvance.fX);
132 fJSONWriter->endArray();
133
134 fJSONWriter->endObject();
135}
136
137void SkShaperJSONWriter::BreakupClusters(size_t utf8Begin, size_t utf8End,
138 SkSpan<const uint32_t> clusters,
139 const BreakupClustersCallback& processMToN) {
140
141 if (clusters.front() <= clusters.back()) {
142 // Handle left-to-right text direction
143 size_t glyphStartIndex = 0;
144 for (size_t glyphEndIndex = 0; glyphEndIndex < clusters.size(); glyphEndIndex++) {
145
146 if (clusters[glyphStartIndex] == clusters[glyphEndIndex]) { continue; }
147
148 processMToN(glyphStartIndex, glyphEndIndex,
149 clusters[glyphStartIndex], clusters[glyphEndIndex]);
150
151 glyphStartIndex = glyphEndIndex;
152 }
153
154 processMToN(glyphStartIndex, clusters.size(), clusters[glyphStartIndex], utf8End);
155
156 } else {
157 // Handle right-to-left text direction.
158 SkASSERT(clusters.size() >= 2);
159 size_t glyphStartIndex = 0;
160 uint32_t utf8EndIndex = utf8End;
161 for (size_t glyphEndIndex = 0; glyphEndIndex < clusters.size(); glyphEndIndex++) {
162
163 if (clusters[glyphStartIndex] == clusters[glyphEndIndex]) { continue; }
164
165 processMToN(glyphStartIndex, glyphEndIndex,
166 clusters[glyphStartIndex], utf8EndIndex);
167
168 utf8EndIndex = clusters[glyphStartIndex];
169 glyphStartIndex = glyphEndIndex;
170 }
171 processMToN(glyphStartIndex, clusters.size(), utf8Begin, clusters[glyphStartIndex-1]);
172 }
173}
174
175void SkShaperJSONWriter::VisualizeClusters(const char* utf8, size_t utf8Begin, size_t utf8End,
177 SkSpan<const uint32_t> clusters,
178 const VisualizeClustersCallback& processMToN) {
179
180 size_t glyphRangeStart, glyphRangeEnd;
181 uint32_t utf8RangeStart, utf8RangeEnd;
182
183 auto resetRanges = [&]() {
184 glyphRangeStart = std::numeric_limits<size_t>::max();
185 glyphRangeEnd = 0;
186 utf8RangeStart = std::numeric_limits<uint32_t>::max();
187 utf8RangeEnd = 0;
188 };
189
190 auto checkRangesAndProcess = [&]() {
191 if (glyphRangeStart < glyphRangeEnd) {
192 size_t glyphRangeCount = glyphRangeEnd - glyphRangeStart;
193 SkSpan<const char> utf8Span{&utf8[utf8RangeStart], utf8RangeEnd - utf8RangeStart};
194 SkSpan<const SkGlyphID> glyphSpan{&glyphIDs[glyphRangeStart], glyphRangeCount};
195
196 // Glyph count is the same as codepoint count for 1:1.
197 processMToN(glyphRangeCount, utf8Span, glyphSpan);
198 }
199 resetRanges();
200 };
201
202 auto gatherRuns = [&](size_t glyphStartIndex, size_t glyphEndIndex,
203 uint32_t utf8StartIndex, uint32_t utf8EndIndex) {
204 int possibleCount = SkUTF::CountUTF8(&utf8[utf8StartIndex], utf8EndIndex - utf8StartIndex);
205 if (possibleCount == -1) { return; }
206 size_t codePointCount = SkTo<size_t>(possibleCount);
207 if (codePointCount == 1 && glyphEndIndex - glyphStartIndex == 1) {
208 glyphRangeStart = std::min(glyphRangeStart, glyphStartIndex);
209 glyphRangeEnd = std::max(glyphRangeEnd, glyphEndIndex );
210 utf8RangeStart = std::min(utf8RangeStart, utf8StartIndex );
211 utf8RangeEnd = std::max(utf8RangeEnd, utf8EndIndex );
212 } else {
213 checkRangesAndProcess();
214
215 SkSpan<const char> utf8Span{&utf8[utf8StartIndex], utf8EndIndex - utf8StartIndex};
216 SkSpan<const SkGlyphID> glyphSpan{&glyphIDs[glyphStartIndex],
217 glyphEndIndex - glyphStartIndex};
218
219 processMToN(codePointCount, utf8Span, glyphSpan);
220 }
221 };
222
223 resetRanges();
224 BreakupClusters(utf8Begin, utf8End, clusters, gatherRuns);
225 checkRangesAndProcess();
226}
227
228void SkShaperJSONWriter::displayMToN(size_t codePointCount,
230 SkSpan<const SkGlyphID> glyphIDs) {
231 std::string nString = std::to_string(codePointCount);
232 std::string mString = std::to_string(glyphIDs.size());
233 std::string clusterName = "cluster " + nString + " to " + mString;
234 fJSONWriter->beginObject(clusterName.c_str(), true);
235 std::string utf8String{utf8.data(), utf8.size()};
236 fJSONWriter->appendString("UTF", utf8String);
237 fJSONWriter->beginArray("glyphsIDs", false);
238 for (auto glyphID : glyphIDs) {
239 fJSONWriter->appendU32(glyphID);
240 }
241 fJSONWriter->endArray();
242 fJSONWriter->endObject();
243}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define SkASSERT(cond)
Definition: SkAssert.h:116
static bool is_one_to_one(const char utf8[], size_t utf8Begin, size_t utf8End, std::vector< uint32_t > &clusters)
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
void beginArray(const char *name=nullptr, bool multiline=true)
Definition: SkJSONWriter.h:146
void appendU32(uint32_t value)
Definition: SkJSONWriter.h:239
void beginObject(const char *name=nullptr, bool multiline=true)
Definition: SkJSONWriter.h:114
void appendFloat(float value)
Definition: SkJSONWriter.h:241
void endObject()
Definition: SkJSONWriter.h:126
void endArray()
Definition: SkJSONWriter.h:158
void appendString(const char *value, size_t size)
Definition: SkJSONWriter.h:176
void commitRunInfo() override
std::function< void(size_t, SkSpan< const char >, SkSpan< const SkGlyphID >)> VisualizeClustersCallback
SkShaperJSONWriter(SkJSONWriter *JSONWriter, const char *utf8, size_t size)
static void BreakupClusters(size_t utf8Begin, size_t utf8End, SkSpan< const uint32_t > clusters, const BreakupClustersCallback &processMToN)
void commitRunBuffer(const RunInfo &info) override
void runInfo(const RunInfo &info) override
std::function< void(size_t, size_t, uint32_t, uint32_t)> BreakupClustersCallback
void beginLine() override
Buffer runBuffer(const RunInfo &info) override
static void VisualizeClusters(const char utf8[], size_t utf8Begin, size_t utf8End, SkSpan< const SkGlyphID > glyphIDs, SkSpan< const uint32_t > clusters, const VisualizeClustersCallback &processMToN)
constexpr T & front() const
Definition: SkSpan_impl.h:88
constexpr T & back() const
Definition: SkSpan_impl.h:89
constexpr size_t size() const
Definition: SkSpan_impl.h:95
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
SK_SPI int CountUTF8(const char *utf8, size_t byteLength)
Definition: SkUTF.cpp:47
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static SkString to_string(int n)
Definition: nanobench.cpp:119
float fX
x-axis value
Definition: SkPoint_impl.h:164