Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/text/GlyphRun.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2018 The Android Open Source Project
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/text/GlyphRun.h"
9
10
#include "include/core/SkFont.h"
11
#include "include/core/SkMatrix.h"
12
#include "include/core/SkRSXform.h"
13
#include "include/core/SkScalar.h"
14
#include "include/private/base/SkTLogic.h"
15
#include "src/core/SkFontPriv.h"
16
#include "src/core/SkGlyph.h"
17
#include "src/core/SkStrikeSpec.h"
18
#include "src/core/SkTextBlobPriv.h"
19
20
#include <cstring>
21
#include <initializer_list>
22
23
class SkPaint;
24
25
namespace sktext {
26
// -- GlyphRun -------------------------------------------------------------------------------------
27
GlyphRun::GlyphRun(const SkFont& font,
28
                   SkSpan<const SkPoint> positions,
29
                   SkSpan<const SkGlyphID> glyphIDs,
30
                   SkSpan<const char> text,
31
                   SkSpan<const uint32_t> clusters,
32
                   SkSpan<const SkVector> scaledRotations)
33
        : fSource{SkMakeZip(glyphIDs, positions)}
34
        , fText{text}
35
        , fClusters{clusters}
36
        , fScaledRotations{scaledRotations}
37
54.1k
        , fFont{font} {}
38
39
GlyphRun::GlyphRun(const GlyphRun& that, const SkFont& font)
40
    : fSource{that.fSource}
41
    , fText{that.fText}
42
    , fClusters{that.fClusters}
43
0
    , fFont{font} {}
44
45
// -- GlyphRunList ---------------------------------------------------------------------------------
46
GlyphRunList::GlyphRunList(const SkTextBlob* blob,
47
                           SkRect bounds,
48
                           SkPoint origin,
49
                           SkSpan<const GlyphRun> glyphRunList,
50
                           GlyphRunBuilder* builder)
51
        : fGlyphRuns{glyphRunList}
52
        , fOriginalTextBlob{blob}
53
        , fSourceBounds{bounds}
54
        , fOrigin{origin}
55
15.2k
        , fBuilder{builder} {}
56
57
GlyphRunList::GlyphRunList(const GlyphRun& glyphRun,
58
                           const SkRect& bounds,
59
                           SkPoint origin,
60
                           GlyphRunBuilder* builder)
61
        : fGlyphRuns{SkSpan<const GlyphRun>{&glyphRun, 1}}
62
        , fOriginalTextBlob{nullptr}
63
        , fSourceBounds{bounds}
64
        , fOrigin{origin}
65
23.5k
        , fBuilder{builder} {}
66
67
1.18k
uint64_t GlyphRunList::uniqueID() const {
68
1.18k
    return fOriginalTextBlob != nullptr ? fOriginalTextBlob->uniqueID()
69
1.18k
                                        : SK_InvalidUniqueID;
70
1.18k
}
71
72
1.18k
bool GlyphRunList::anyRunsLCD() const {
73
7.11k
    for (const auto& r : fGlyphRuns) {
74
7.11k
        if (r.font().getEdging() == SkFont::Edging::kSubpixelAntiAlias) {
75
17
            return true;
76
17
        }
77
7.11k
    }
78
1.16k
    return false;
79
1.18k
}
80
81
void GlyphRunList::temporaryShuntBlobNotifyAddedToCache(uint32_t cacheID,
82
816
                                                        SkTextBlob::PurgeDelegate pd) const {
83
816
    SkASSERT(fOriginalTextBlob != nullptr);
84
816
    SkASSERT(pd != nullptr);
85
816
    fOriginalTextBlob->notifyAddedToCache(cacheID, pd);
86
816
}
87
88
1.86k
sk_sp<SkTextBlob> GlyphRunList::makeBlob() const {
89
1.86k
    SkTextBlobBuilder builder;
90
1.86k
    for (auto& run : *this) {
91
1.86k
        SkTextBlobBuilder::RunBuffer buffer;
92
1.86k
        if (run.scaledRotations().empty()) {
93
1.86k
            if (run.text().empty()) {
94
1.86k
                buffer = builder.allocRunPos(run.font(), run.runSize(), nullptr);
95
1.86k
            } else {
96
0
                buffer = builder.allocRunTextPos(run.font(), run.runSize(), run.text().size(), nullptr);
97
0
                auto text = run.text();
98
0
                memcpy(buffer.utf8text, text.data(), text.size_bytes());
99
0
                auto clusters = run.clusters();
100
0
                memcpy(buffer.clusters, clusters.data(), clusters.size_bytes());
101
0
            }
102
1.86k
            auto positions = run.positions();
103
1.86k
            memcpy(buffer.points(), positions.data(), positions.size_bytes());
104
1.86k
        } else {
105
0
            buffer = builder.allocRunRSXform(run.font(), run.runSize());
106
0
            for (auto [xform, pos, sr] : SkMakeZip(buffer.xforms(),
107
0
                                                   run.positions(),
108
0
                                                   run.scaledRotations())) {
109
0
                xform = SkRSXform::Make(sr.x(), sr.y(), pos.x(), pos.y());
110
0
            }
111
0
        }
112
1.86k
        auto glyphIDs = run.glyphsIDs();
113
1.86k
        memcpy(buffer.glyphs, glyphIDs.data(), glyphIDs.size_bytes());
114
1.86k
    }
115
1.86k
    return builder.make();
116
1.86k
}
117
118
// -- GlyphRunBuilder ------------------------------------------------------------------------------
119
static SkRect glyphrun_source_bounds(
120
        const SkFont& font,
121
        const SkPaint& paint,
122
        SkZip<const SkGlyphID, const SkPoint> source,
123
30.5k
        SkSpan<const SkVector> scaledRotations) {
124
30.5k
    SkASSERT(!source.empty());
125
30.5k
    const SkRect fontBounds = SkFontPriv::GetFontBounds(font);
126
127
30.5k
    SkSpan<const SkGlyphID> glyphIDs = source.get<0>();
128
30.5k
    SkSpan<const SkPoint> positions = source.get<1>();
129
130
30.5k
    if (fontBounds.isEmpty()) {
131
        // Empty font bounds are likely a font bug.  TightBounds has a better chance of
132
        // producing useful results in this case.
133
14.5k
        auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(font, &paint);
134
14.5k
        SkBulkGlyphMetrics metrics{strikeSpec};
135
14.5k
        SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs);
136
14.5k
        if (scaledRotations.empty()) {
137
            // No RSXForm data - glyphs x/y aligned.
138
14.5k
            auto scaleAndTranslateRect =
139
14.5k
                    [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) {
140
13.0k
                        return SkRect::MakeLTRB(in.left()   * scale + pos.x(),
141
13.0k
                                                in.top()    * scale + pos.y(),
142
13.0k
                                                in.right()  * scale + pos.x(),
143
13.0k
                                                in.bottom() * scale + pos.y());
144
13.0k
                    };
GlyphRun.cpp:sktext::glyphrun_source_bounds(SkFont const&, SkPaint const&, SkZip<unsigned short const, SkPoint const>, SkSpan<SkPoint const>)::$_0::operator()(SkRect const&, SkPoint const&) const
Line
Count
Source
139
13.0k
                    [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) {
140
13.0k
                        return SkRect::MakeLTRB(in.left()   * scale + pos.x(),
141
13.0k
                                                in.top()    * scale + pos.y(),
142
13.0k
                                                in.right()  * scale + pos.x(),
143
13.0k
                                                in.bottom() * scale + pos.y());
144
13.0k
                    };
Unexecuted instantiation: GlyphRun.cpp:sktext::glyphrun_source_bounds(SkFont const&, SkPaint const&, SkZip<unsigned short const, SkPoint const>, SkSpan<SkPoint const>)::$_1::operator()(SkRect const&, SkPoint const&) const
145
146
14.5k
            SkRect bounds = SkRect::MakeEmpty();
147
63.3k
            for (auto [pos, glyph] : SkMakeZip(positions, glyphs)) {
148
63.3k
                if (SkRect r = glyph->rect(); !r.isEmpty()) {
149
13.0k
                    bounds.join(scaleAndTranslateRect(r, pos));
150
13.0k
                }
151
63.3k
            }
152
14.5k
            return bounds;
153
14.5k
        } else {
154
            // RSXForm - glyphs can be any scale or rotation.
155
0
            SkRect bounds = SkRect::MakeEmpty();
156
0
            for (auto [pos, scaleRotate, glyph] : SkMakeZip(positions, scaledRotations, glyphs)) {
157
0
                if (!glyph->rect().isEmpty()) {
158
0
                    SkMatrix xform = SkMatrix().setRSXform(
159
0
                            SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()});
160
0
                    xform.preScale(strikeToSourceScale, strikeToSourceScale);
161
0
                    bounds.join(xform.mapRect(glyph->rect()));
162
0
                }
163
0
            }
164
0
            return bounds;
165
0
        }
166
14.5k
    }
167
168
    // Use conservative bounds. All glyph have a box of fontBounds size.
169
16.0k
    if (scaledRotations.empty()) {
170
16.0k
        SkRect bounds;
171
16.0k
        bounds.setBounds(positions.data(), SkCount(positions));
172
16.0k
        bounds.fLeft   += fontBounds.left();
173
16.0k
        bounds.fTop    += fontBounds.top();
174
16.0k
        bounds.fRight  += fontBounds.right();
175
16.0k
        bounds.fBottom += fontBounds.bottom();
176
16.0k
        return bounds;
177
16.0k
    } else {
178
        // RSXForm case glyphs can be any scale or rotation.
179
0
        SkRect bounds;
180
0
        bounds.setEmpty();
181
0
        for (auto [pos, scaleRotate] : SkMakeZip(positions, scaledRotations)) {
182
0
            const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()};
183
0
            bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds));
184
0
        }
185
0
        return bounds;
186
0
    }
187
16.0k
}
GlyphRun.cpp:sktext::glyphrun_source_bounds(SkFont const&, SkPaint const&, SkZip<unsigned short const, SkPoint const>, SkSpan<SkPoint const>)
Line
Count
Source
123
30.5k
        SkSpan<const SkVector> scaledRotations) {
124
30.5k
    SkASSERT(!source.empty());
125
30.5k
    const SkRect fontBounds = SkFontPriv::GetFontBounds(font);
126
127
30.5k
    SkSpan<const SkGlyphID> glyphIDs = source.get<0>();
128
30.5k
    SkSpan<const SkPoint> positions = source.get<1>();
129
130
30.5k
    if (fontBounds.isEmpty()) {
131
        // Empty font bounds are likely a font bug.  TightBounds has a better chance of
132
        // producing useful results in this case.
133
14.5k
        auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(font, &paint);
134
14.5k
        SkBulkGlyphMetrics metrics{strikeSpec};
135
14.5k
        SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs);
136
14.5k
        if (scaledRotations.empty()) {
137
            // No RSXForm data - glyphs x/y aligned.
138
14.5k
            auto scaleAndTranslateRect =
139
14.5k
                    [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) {
140
14.5k
                        return SkRect::MakeLTRB(in.left()   * scale + pos.x(),
141
14.5k
                                                in.top()    * scale + pos.y(),
142
14.5k
                                                in.right()  * scale + pos.x(),
143
14.5k
                                                in.bottom() * scale + pos.y());
144
14.5k
                    };
145
146
14.5k
            SkRect bounds = SkRect::MakeEmpty();
147
63.3k
            for (auto [pos, glyph] : SkMakeZip(positions, glyphs)) {
148
63.3k
                if (SkRect r = glyph->rect(); !r.isEmpty()) {
149
13.0k
                    bounds.join(scaleAndTranslateRect(r, pos));
150
13.0k
                }
151
63.3k
            }
152
14.5k
            return bounds;
153
14.5k
        } else {
154
            // RSXForm - glyphs can be any scale or rotation.
155
0
            SkRect bounds = SkRect::MakeEmpty();
156
0
            for (auto [pos, scaleRotate, glyph] : SkMakeZip(positions, scaledRotations, glyphs)) {
157
0
                if (!glyph->rect().isEmpty()) {
158
0
                    SkMatrix xform = SkMatrix().setRSXform(
159
0
                            SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()});
160
0
                    xform.preScale(strikeToSourceScale, strikeToSourceScale);
161
0
                    bounds.join(xform.mapRect(glyph->rect()));
162
0
                }
163
0
            }
164
0
            return bounds;
165
0
        }
166
14.5k
    }
167
168
    // Use conservative bounds. All glyph have a box of fontBounds size.
169
16.0k
    if (scaledRotations.empty()) {
170
16.0k
        SkRect bounds;
171
16.0k
        bounds.setBounds(positions.data(), SkCount(positions));
172
16.0k
        bounds.fLeft   += fontBounds.left();
173
16.0k
        bounds.fTop    += fontBounds.top();
174
16.0k
        bounds.fRight  += fontBounds.right();
175
16.0k
        bounds.fBottom += fontBounds.bottom();
176
16.0k
        return bounds;
177
16.0k
    } else {
178
        // RSXForm case glyphs can be any scale or rotation.
179
0
        SkRect bounds;
180
0
        bounds.setEmpty();
181
0
        for (auto [pos, scaleRotate] : SkMakeZip(positions, scaledRotations)) {
182
0
            const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()};
183
0
            bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds));
184
0
        }
185
0
        return bounds;
186
0
    }
187
16.0k
}
Unexecuted instantiation: GlyphRun.cpp:sktext::glyphrun_source_bounds(SkFont const&, SkPaint const&, SkZip<unsigned short const, SkPoint const>, SkSpan<SkPoint const>)
188
189
GlyphRunList GlyphRunBuilder::makeGlyphRunList(
190
23.5k
        const GlyphRun& run, const SkPaint& paint, SkPoint origin) {
191
23.5k
    const SkRect bounds =
192
23.5k
            glyphrun_source_bounds(run.font(), paint, run.source(), run.scaledRotations());
193
23.5k
    return GlyphRunList{run, bounds, origin, this};
194
23.5k
}
195
196
static SkSpan<const SkPoint> draw_text_positions(
197
22.1k
        const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin, SkPoint* buffer) {
198
22.1k
    SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(font);
199
22.1k
    SkBulkGlyphMetrics storage{strikeSpec};
200
22.1k
    auto glyphs = storage.glyphs(glyphIDs);
201
202
22.1k
    SkPoint* positionCursor = buffer;
203
22.1k
    SkPoint endOfLastGlyph = origin;
204
261k
    for (auto glyph : glyphs) {
205
261k
        *positionCursor++ = endOfLastGlyph;
206
261k
        endOfLastGlyph += glyph->advanceVector();
207
261k
    }
208
22.1k
    return SkSpan(buffer, glyphIDs.size());
209
22.1k
}
210
211
const GlyphRunList& GlyphRunBuilder::textToGlyphRunList(
212
        const SkFont& font, const SkPaint& paint,
213
        const void* bytes, size_t byteLength, SkPoint origin,
214
7.11k
        SkTextEncoding encoding) {
215
7.11k
    auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding);
216
7.11k
    SkRect bounds = SkRect::MakeEmpty();
217
7.11k
    this->prepareBuffers(glyphIDs.size(), 0);
218
7.11k
    if (!glyphIDs.empty()) {
219
7.01k
        SkSpan<const SkPoint> positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions);
220
7.01k
        this->makeGlyphRun(font,
221
7.01k
                           glyphIDs,
222
7.01k
                           positions,
223
7.01k
                           SkSpan<const char>{},
224
7.01k
                           SkSpan<const uint32_t>{},
225
7.01k
                           SkSpan<const SkVector>{});
226
7.01k
        auto run = fGlyphRunListStorage.front();
227
7.01k
        bounds = glyphrun_source_bounds(run.font(), paint, run.source(), run.scaledRotations());
228
7.01k
    }
229
230
7.11k
    return this->setGlyphRunList(nullptr, bounds, origin);
231
7.11k
}
232
233
const GlyphRunList& sktext::GlyphRunBuilder::blobToGlyphRunList(
234
8.12k
        const SkTextBlob& blob, SkPoint origin) {
235
    // Pre-size all the buffers, so they don't move during processing.
236
8.12k
    this->initialize(blob);
237
238
8.12k
    SkPoint* positionCursor = fPositions;
239
8.12k
    SkVector* scaledRotationsCursor = fScaledRotations;
240
36.3k
    for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
241
28.2k
        size_t runSize = it.glyphCount();
242
28.2k
        if (runSize == 0 || !SkFontPriv::IsFinite(it.font())) {
243
            // If no glyphs or the font is not finite, don't add the run.
244
189
            continue;
245
189
        }
246
247
28.0k
        const SkFont& font = it.font();
248
28.0k
        auto glyphIDs = SkSpan<const SkGlyphID>{it.glyphs(), runSize};
249
250
28.0k
        SkSpan<const SkPoint> positions;
251
28.0k
        SkSpan<const SkVector> scaledRotations;
252
28.0k
        switch (it.positioning()) {
253
15.1k
            case SkTextBlobRunIterator::kDefault_Positioning: {
254
15.1k
                positions = draw_text_positions(font, glyphIDs, it.offset(), positionCursor);
255
15.1k
                positionCursor += positions.size();
256
15.1k
                break;
257
0
            }
258
1.57k
            case SkTextBlobRunIterator::kHorizontal_Positioning: {
259
1.57k
                positions = SkSpan(positionCursor, runSize);
260
33.4k
                for (auto x : SkSpan<const SkScalar>{it.pos(), glyphIDs.size()}) {
261
33.4k
                    *positionCursor++ = SkPoint::Make(x, it.offset().y());
262
33.4k
                }
263
1.57k
                break;
264
0
            }
265
10.0k
            case SkTextBlobRunIterator::kFull_Positioning: {
266
10.0k
                positions = SkSpan(it.points(), runSize);
267
10.0k
                break;
268
0
            }
269
1.28k
            case SkTextBlobRunIterator::kRSXform_Positioning: {
270
1.28k
                positions = SkSpan(positionCursor, runSize);
271
1.28k
                scaledRotations = SkSpan(scaledRotationsCursor, runSize);
272
23.1k
                for (const SkRSXform& xform : SkSpan(it.xforms(), runSize)) {
273
23.1k
                    *positionCursor++ = {xform.fTx, xform.fTy};
274
23.1k
                    *scaledRotationsCursor++ = {xform.fSCos, xform.fSSin};
275
23.1k
                }
276
1.28k
                break;
277
0
            }
278
28.0k
        }
279
280
28.0k
        const uint32_t* clusters = it.clusters();
281
28.0k
        this->makeGlyphRun(
282
28.0k
                font,
283
28.0k
                glyphIDs,
284
28.0k
                positions,
285
28.0k
                SkSpan<const char>(it.text(), it.textSize()),
286
28.0k
                SkSpan<const uint32_t>(clusters, clusters ? runSize : 0),
287
28.0k
                scaledRotations);
288
28.0k
    }
289
290
8.12k
    return this->setGlyphRunList(&blob, blob.bounds(), origin);
291
8.12k
}
292
293
std::tuple<SkSpan<const SkPoint>, SkSpan<const SkVector>>
294
0
GlyphRunBuilder::convertRSXForm(SkSpan<const SkRSXform> xforms) {
295
0
    const int count = SkCount(xforms);
296
0
    this->prepareBuffers(count, count);
297
0
    auto positions = SkSpan(fPositions.get(), count);
298
0
    auto scaledRotations = SkSpan(fScaledRotations.get(), count);
299
0
    for (auto [pos, sr, xform] : SkMakeZip(positions, scaledRotations, xforms)) {
300
0
        auto [scos, ssin, tx, ty] = xform;
301
0
        pos = {tx, ty};
302
0
        sr = {scos, ssin};
303
0
    }
304
0
    return {positions, scaledRotations};
305
0
}
306
307
8.12k
void GlyphRunBuilder::initialize(const SkTextBlob& blob) {
308
8.12k
    int positionCount = 0;
309
8.12k
    int rsxFormCount = 0;
310
36.3k
    for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
311
28.2k
        if (it.positioning() != SkTextBlobRunIterator::kFull_Positioning) {
312
18.0k
            positionCount += it.glyphCount();
313
18.0k
        }
314
28.2k
        if (it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning) {
315
1.28k
            rsxFormCount += it.glyphCount();
316
1.28k
        }
317
28.2k
    }
318
319
8.12k
    prepareBuffers(positionCount, rsxFormCount);
320
8.12k
}
321
322
15.2k
void GlyphRunBuilder::prepareBuffers(int positionCount, int RSXFormCount) {
323
15.2k
    if (positionCount > fMaxTotalRunSize) {
324
9.69k
        fMaxTotalRunSize = positionCount;
325
9.69k
        fPositions.reset(fMaxTotalRunSize);
326
9.69k
    }
327
328
15.2k
    if (RSXFormCount > fMaxScaledRotations) {
329
422
        fMaxScaledRotations = RSXFormCount;
330
422
        fScaledRotations.reset(RSXFormCount);
331
422
    }
332
333
15.2k
    fGlyphRunListStorage.clear();
334
15.2k
}
335
336
SkSpan<const SkGlyphID> GlyphRunBuilder::textToGlyphIDs(
337
7.11k
        const SkFont& font, const void* bytes, size_t byteLength, SkTextEncoding encoding) {
338
7.11k
    if (encoding != SkTextEncoding::kGlyphID) {
339
7.11k
        int count = font.countText(bytes, byteLength, encoding);
340
7.11k
        if (count > 0) {
341
7.01k
            fScratchGlyphIDs.resize(count);
342
7.01k
            font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs.data(), count);
343
7.01k
            return SkSpan(fScratchGlyphIDs);
344
7.01k
        } else {
345
96
            return SkSpan<const SkGlyphID>();
346
96
        }
347
7.11k
    } else {
348
0
        return SkSpan<const SkGlyphID>((const SkGlyphID*)bytes, byteLength / 2);
349
0
    }
350
7.11k
}
351
352
void GlyphRunBuilder::makeGlyphRun(
353
        const SkFont& font,
354
        SkSpan<const SkGlyphID> glyphIDs,
355
        SkSpan<const SkPoint> positions,
356
        SkSpan<const char> text,
357
        SkSpan<const uint32_t> clusters,
358
35.0k
        SkSpan<const SkVector> scaledRotations) {
359
360
    // Ignore empty runs.
361
35.0k
    if (!glyphIDs.empty()) {
362
35.0k
        fGlyphRunListStorage.emplace_back(
363
35.0k
                font,
364
35.0k
                positions,
365
35.0k
                glyphIDs,
366
35.0k
                text,
367
35.0k
                clusters,
368
35.0k
                scaledRotations);
369
35.0k
    }
370
35.0k
}
371
372
const GlyphRunList& sktext::GlyphRunBuilder::setGlyphRunList(
373
15.2k
        const SkTextBlob* blob, const SkRect& bounds, SkPoint origin) {
374
15.2k
    fGlyphRunList.emplace(blob, bounds, origin, SkSpan(fGlyphRunListStorage), this);
375
15.2k
    return fGlyphRunList.value();
376
15.2k
}
377
}  // namespace sktext