tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GlyphRun.cpp (14618B)


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