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