blob: 1773ec5c1d32e846991bd1cb4e56f20240f86afd [file] [log] [blame]
Julia Lavrovaa3552c52019-05-30 16:12:56 -04001// Copyright 2019 Google LLC.
2#include "modules/skparagraph/src/Run.h"
3#include <unicode/brkiter.h>
4#include "include/core/SkFontMetrics.h"
5
6namespace skia {
7namespace textlayout {
8
9Run::Run(SkSpan<const char> text,
10 const SkShaper::RunHandler::RunInfo& info,
11 SkScalar lineHeight,
12 size_t index,
13 SkScalar offsetX) {
14 TRACE_EVENT0("skia", TRACE_FUNC);
15 fFont = info.fFont;
16 fHeightMultiplier = lineHeight;
17 fBidiLevel = info.fBidiLevel;
18 fAdvance = info.fAdvance;
19 fText = SkSpan<const char>(text.begin() + info.utf8Range.begin(), info.utf8Range.size());
20
21 fIndex = index;
22 fUtf8Range = info.utf8Range;
23 fOffset = SkVector::Make(offsetX, 0);
24 fGlyphs.push_back_n(info.glyphCount);
25 fPositions.push_back_n(info.glyphCount + 1);
26 fOffsets.push_back_n(info.glyphCount + 1, SkScalar(0));
27 fClusterIndexes.push_back_n(info.glyphCount + 1);
28 info.fFont.getMetrics(&fFontMetrics);
29 fSpaced = false;
30 // To make edge cases easier:
31 fPositions[info.glyphCount] = fOffset + fAdvance;
32 fClusterIndexes[info.glyphCount] = info.utf8Range.end();
33}
34
35SkShaper::RunHandler::Buffer Run::newRunBuffer() {
36 TRACE_EVENT0("skia", TRACE_FUNC);
37 return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
38}
39
40SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
41 TRACE_EVENT0("skia", TRACE_FUNC);
42 SkASSERT(start <= end);
43 // clip |= end == size(); // Clip at the end of the run?
44 SkScalar offset = 0;
45 if (fSpaced && end > start) {
46 offset = fOffsets[clip ? end - 1 : end] - fOffsets[start];
47 }
48 return fPositions[end].fX - fPositions[start].fX + offset;
49}
50
51void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const {
52 TRACE_EVENT0("skia", TRACE_FUNC);
53 SkASSERT(pos + size <= this->size());
54 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
55 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
56
57 if (fSpaced || offset.fX != 0 || offset.fY != 0) {
58 for (size_t i = 0; i < size; ++i) {
59 auto point = fPositions[i + pos];
60 if (fSpaced) {
61 point.fX += fOffsets[i + pos];
62 }
63 blobBuffer.points()[i] = point + offset;
64 }
65 } else {
66 // Good for the first line
67 sk_careful_memcpy(blobBuffer.points(), fPositions.data() + pos, size * sizeof(SkPoint));
68 }
69}
70
71// TODO: Make the search more effective
72std::tuple<bool, Cluster*, Cluster*> Run::findLimitingClusters(SkSpan<const char> text) {
73 if (text.empty()) {
74 Cluster* found = nullptr;
75 for (auto& cluster : fClusters) {
76 if (cluster.contains(text.begin())) {
77 found = &cluster;
78 break;
79 }
80 }
81 return std::make_tuple(found != nullptr, found, found);
82 }
83
84 auto first = text.begin();
85 auto last = text.end() - 1;
86
87 Cluster* start = nullptr;
88 Cluster* end = nullptr;
89 for (auto& cluster : fClusters) {
90 if (cluster.contains(first)) start = &cluster;
91 if (cluster.contains(last)) end = &cluster;
92 }
93 if (!leftToRight()) {
94 std::swap(start, end);
95 }
96
97 return std::make_tuple(start != nullptr && end != nullptr, start, end);
98}
99
100void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
101 TRACE_EVENT0("skia", TRACE_FUNC);
102 // Can't figure out how to do it with one code for both cases without 100 ifs
103 // Can't go through clusters because there are no cluster table yet
104 if (leftToRight()) {
105 size_t start = 0;
106 size_t cluster = this->clusterIndex(start);
107 for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
108 auto nextCluster = this->clusterIndex(glyph);
109 if (nextCluster == cluster) {
110 continue;
111 }
112
113 visitor(start,
114 glyph,
115 cluster,
116 nextCluster,
117 this->calculateWidth(start, glyph, glyph == size()),
118 this->calculateHeight());
119
120 start = glyph;
121 cluster = nextCluster;
122 }
123 } else {
124 size_t glyph = this->size();
125 size_t cluster = this->fUtf8Range.begin();
126 for (int32_t start = this->size() - 1; start >= 0; --start) {
127 size_t nextCluster =
128 start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
129 if (nextCluster == cluster) {
130 continue;
131 }
132
133 visitor(start,
134 glyph,
135 cluster,
136 nextCluster,
137 this->calculateWidth(start, glyph, glyph == 0),
138 this->calculateHeight());
139
140 glyph = start;
141 cluster = nextCluster;
142 }
143 }
144}
145
146SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
147 TRACE_EVENT0("skia", TRACE_FUNC);
148 if (cluster->endPos() == cluster->startPos()) {
149 return 0;
150 }
151
152 fOffsets[cluster->endPos() - 1] += space;
153 // Increment the run width
154 fSpaced = true;
155 fAdvance.fX += space;
156 // Increment the cluster width
157 cluster->space(space, space);
158
159 return space;
160}
161
162SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
163 TRACE_EVENT0("skia", TRACE_FUNC);
164 // Offset all the glyphs in the cluster
165 SkScalar shift = 0;
166 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
167 fOffsets[i] += shift;
168 shift += space;
169 }
170 if (this->size() == cluster->endPos()) {
171 // To make calculations easier
172 fOffsets[cluster->endPos()] += shift;
173 }
174 // Increment the run width
175 fSpaced = true;
176 fAdvance.fX += shift;
177 // Increment the cluster width
178 cluster->space(shift, space);
179
180 return shift;
181}
182
183void Run::shift(const Cluster* cluster, SkScalar offset) {
184 TRACE_EVENT0("skia", TRACE_FUNC);
185 if (offset == 0) {
186 return;
187 }
188
189 fSpaced = true;
190 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
191 fOffsets[i] += offset;
192 }
193 if (this->size() == cluster->endPos()) {
194 // To make calculations easier
195 fOffsets[cluster->endPos()] += offset;
196 }
197}
198
199void Cluster::setIsWhiteSpaces() {
200 TRACE_EVENT0("skia", TRACE_FUNC);
201 auto pos = fText.end();
202 while (--pos >= fText.begin()) {
203 auto ch = *pos;
204 if (!u_isspace(ch) && u_charType(ch) != U_CONTROL_CHAR &&
205 u_charType(ch) != U_NON_SPACING_MARK) {
206 return;
207 }
208 }
209 fWhiteSpaces = true;
210}
211
212SkScalar Cluster::sizeToChar(const char* ch) const {
213 TRACE_EVENT0("skia", TRACE_FUNC);
214 if (ch < fText.begin() || ch >= fText.end()) {
215 return 0;
216 }
217 auto shift = ch - fText.begin();
218 auto ratio = shift * 1.0 / fText.size();
219
220 return SkDoubleToScalar(fWidth * ratio);
221}
222
223SkScalar Cluster::sizeFromChar(const char* ch) const {
224 TRACE_EVENT0("skia", TRACE_FUNC);
225 if (ch < fText.begin() || ch >= fText.end()) {
226 return 0;
227 }
228 auto shift = fText.end() - ch - 1;
229 auto ratio = shift * 1.0 / fText.size();
230
231 return SkDoubleToScalar(fWidth * ratio);
232}
233
234size_t Cluster::roundPos(SkScalar s) const {
235 TRACE_EVENT0("skia", TRACE_FUNC);
236 auto ratio = (s * 1.0) / fWidth;
237 return sk_double_floor2int(ratio * size());
238}
239
240SkScalar Cluster::trimmedWidth(size_t pos) const {
241 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
242 return SkTMin(this->run()->positionX(pos) - this->run()->positionX(fStart), fWidth - fSpacing);
243}
244
245} // namespace textlayout
246} // namespace skia