/* * Copyright 2018 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/core/SkGlyphRun.h" #include "include/core/SkFont.h" #include "include/core/SkPaint.h" #include "include/core/SkRSXform.h" #include "include/core/SkTextBlob.h" #include "include/private/SkTo.h" #include "src/core/SkDevice.h" #include "src/core/SkFontPriv.h" #include "src/core/SkScalerCache.h" #include "src/core/SkStrikeCache.h" #include "src/core/SkStrikeSpec.h" #include "src/core/SkTextBlobPriv.h" #include "src/core/SkUtils.h" // -- SkGlyphRun ----------------------------------------------------------------------------------- SkGlyphRun::SkGlyphRun(const SkFont& font, SkSpan positions, SkSpan glyphIDs, SkSpan text, SkSpan clusters, SkSpan scaledRotations) : fSource{SkMakeZip(glyphIDs, positions)} , fText{text} , fClusters{clusters} , fScaledRotations{scaledRotations} , fFont{font} {} SkGlyphRun::SkGlyphRun(const SkGlyphRun& that, const SkFont& font) : fSource{that.fSource} , fText{that.fText} , fClusters{that.fClusters} , fFont{font} {} SkRect SkGlyphRun::sourceBounds(const SkPaint& paint) const { SkASSERT(this->runSize() > 0); const SkRect fontBounds = SkFontPriv::GetFontBounds(fFont); if (fontBounds.isEmpty()) { // Empty font bounds are likely a font bug. TightBounds has a better chance of // producing useful results in this case. SkStrikeSpec strikeSpec = SkStrikeSpec::MakeCanonicalized(fFont, &paint); SkBulkGlyphMetrics metrics{strikeSpec}; SkSpan glyphs = metrics.glyphs(this->glyphsIDs()); if (fScaledRotations.empty()) { // No RSXForm data - glyphs x/y aligned. auto scaleAndTranslateRect = [scale = strikeSpec.strikeToSourceRatio()](const SkRect& in, const SkPoint& pos) { return SkRect::MakeLTRB(in.left() * scale + pos.x(), in.top() * scale + pos.y(), in.right() * scale + pos.x(), in.bottom() * scale + pos.y()); }; SkRect bounds = SkRect::MakeEmpty(); for (auto [pos, glyph] : SkMakeZip(this->positions(), glyphs)) { if (SkRect r = glyph->rect(); !r.isEmpty()) { bounds.join(scaleAndTranslateRect(r, pos)); } } return bounds; } else { // RSXForm - glyphs can be any scale or rotation. SkScalar scale = strikeSpec.strikeToSourceRatio(); SkRect bounds = SkRect::MakeEmpty(); for (auto [pos, scaleRotate, glyph] : SkMakeZip(this->positions(), fScaledRotations, glyphs)) { if (!glyph->rect().isEmpty()) { SkMatrix xform = SkMatrix().setRSXform( SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}); xform.preScale(scale, scale); bounds.join(xform.mapRect(glyph->rect())); } } return bounds; } } // Use conservative bounds. All glyph have a box of fontBounds size. if (fScaledRotations.empty()) { SkRect bounds; bounds.setBounds(this->positions().data(), SkCount(this->positions())); bounds.fLeft += fontBounds.left(); bounds.fTop += fontBounds.top(); bounds.fRight += fontBounds.right(); bounds.fBottom += fontBounds.bottom(); return bounds; } else { // RSXForm case glyphs can be any scale or rotation. SkRect bounds; bounds.setEmpty(); for (auto [pos, scaleRotate] : SkMakeZip(this->positions(), fScaledRotations)) { const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}; bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds)); } return bounds; } } // -- SkGlyphRunList ------------------------------------------------------------------------------- SkGlyphRunList::SkGlyphRunList() = default; SkGlyphRunList::SkGlyphRunList( const SkTextBlob* blob, SkRect bounds, SkPoint origin, SkSpan glyphRunList) : fGlyphRuns{glyphRunList} , fOriginalTextBlob{blob} , fSourceBounds{bounds} , fOrigin{origin} { } SkGlyphRunList::SkGlyphRunList(const SkGlyphRun& glyphRun, const SkRect& bounds, SkPoint origin) : fGlyphRuns{SkSpan{&glyphRun, 1}} , fOriginalTextBlob{nullptr} , fSourceBounds{bounds} , fOrigin{origin} {} uint64_t SkGlyphRunList::uniqueID() const { return fOriginalTextBlob != nullptr ? fOriginalTextBlob->uniqueID() : SK_InvalidUniqueID; } bool SkGlyphRunList::anyRunsLCD() const { for (const auto& r : fGlyphRuns) { if (r.font().getEdging() == SkFont::Edging::kSubpixelAntiAlias) { return true; } } return false; } void SkGlyphRunList::temporaryShuntBlobNotifyAddedToCache(uint32_t cacheID) const { SkASSERT(fOriginalTextBlob != nullptr); fOriginalTextBlob->notifyAddedToCache(cacheID); } sk_sp SkGlyphRunList::makeBlob() const { SkTextBlobBuilder builder; for (auto& run : *this) { SkTextBlobBuilder::RunBuffer buffer; if (run.scaledRotations().empty()) { if (run.text().empty()) { buffer = builder.allocRunPos(run.font(), run.runSize(), nullptr); } else { buffer = builder.allocRunTextPos(run.font(), run.runSize(), run.text().size(), nullptr); auto text = run.text(); memcpy(buffer.utf8text, text.data(), text.size_bytes()); auto clusters = run.clusters(); memcpy(buffer.clusters, clusters.data(), clusters.size_bytes()); } auto positions = run.positions(); memcpy(buffer.points(), positions.data(), positions.size_bytes()); } else { buffer = builder.allocRunRSXform(run.font(), run.runSize()); for (auto [xform, pos, sr] : SkMakeZip(buffer.xforms(), run.positions(), run.scaledRotations())) { xform = SkRSXform::Make(sr.x(), sr.y(), pos.x(), pos.y()); } } auto glyphIDs = run.glyphsIDs(); memcpy(buffer.glyphs, glyphIDs.data(), glyphIDs.size_bytes()); } return builder.make(); } // -- SkGlyphRunBuilder ---------------------------------------------------------------------------- static SkSpan draw_text_positions( const SkFont& font, SkSpan glyphIDs, SkPoint origin, SkPoint* buffer) { SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(font); SkBulkGlyphMetrics storage{strikeSpec}; auto glyphs = storage.glyphs(glyphIDs); SkPoint* positionCursor = buffer; SkPoint endOfLastGlyph = origin; for (auto glyph : glyphs) { *positionCursor++ = endOfLastGlyph; endOfLastGlyph += glyph->advanceVector(); } return SkMakeSpan(buffer, glyphIDs.size()); } const SkGlyphRunList& SkGlyphRunBuilder::textToGlyphRunList( const SkFont& font, const SkPaint& paint, const void* bytes, size_t byteLength, SkPoint origin, SkTextEncoding encoding) { auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding); SkRect bounds = SkRect::MakeEmpty(); this->prepareBuffers(glyphIDs.size(), 0); if (!glyphIDs.empty()) { SkSpan positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions); this->makeGlyphRun(font, glyphIDs, positions, SkSpan{}, SkSpan{}, SkSpan{}); bounds = fGlyphRunListStorage.front().sourceBounds(paint); } return this->makeGlyphRunList(nullptr, bounds.makeOffset(origin), origin); } const SkGlyphRunList& SkGlyphRunBuilder::blobToGlyphRunList( const SkTextBlob& blob, SkPoint origin) { // Pre-size all the buffers so they don't move during processing. this->initialize(blob); SkPoint* positionCursor = fPositions; SkVector* scaledRotationsCursor = fScaledRotations; for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { size_t runSize = it.glyphCount(); if (runSize == 0 || !SkFontPriv::IsFinite(it.font())) { // If no glyphs or the font is not finite, don't add the run. continue; } const SkFont& font = it.font(); auto glyphIDs = SkSpan{it.glyphs(), runSize}; SkSpan positions; SkSpan scaledRotations; switch (it.positioning()) { case SkTextBlobRunIterator::kDefault_Positioning: { positions = draw_text_positions(font, glyphIDs, it.offset(), positionCursor); positionCursor += positions.size(); break; } case SkTextBlobRunIterator::kHorizontal_Positioning: { positions = SkMakeSpan(positionCursor, runSize); for (auto x : SkSpan{it.pos(), glyphIDs.size()}) { *positionCursor++ = SkPoint::Make(x, it.offset().y()); } break; } case SkTextBlobRunIterator::kFull_Positioning: { positions = SkMakeSpan(it.points(), runSize); break; } case SkTextBlobRunIterator::kRSXform_Positioning: { positions = SkMakeSpan(positionCursor, runSize); scaledRotations = SkMakeSpan(scaledRotationsCursor, runSize); for (const SkRSXform& xform : SkMakeSpan(it.xforms(), runSize)) { *positionCursor++ = {xform.fTx, xform.fTy}; *scaledRotationsCursor++ = {xform.fSCos, xform.fSSin}; } break; } } this->makeGlyphRun( font, glyphIDs, positions, SkSpan(it.text(), it.textSize()), SkSpan(it.clusters(), runSize), scaledRotations); } return this->makeGlyphRunList(&blob, blob.bounds().makeOffset(origin), origin); } std::tuple, SkSpan> SkGlyphRunBuilder::convertRSXForm(SkSpan xforms) { const int count = SkCount(xforms); this->prepareBuffers(count, count); auto positions = SkMakeSpan(fPositions.get(), count); auto scaledRotations = SkMakeSpan(fScaledRotations.get(), count); for (auto [pos, sr, xform] : SkMakeZip(positions, scaledRotations, xforms)) { auto [scos, ssin, tx, ty] = xform; pos = {tx, ty}; sr = {scos, ssin}; } return {positions, scaledRotations}; } void SkGlyphRunBuilder::initialize(const SkTextBlob& blob) { int positionCount = 0; int rsxFormCount = 0; for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { if (it.positioning() != SkTextBlobRunIterator::kFull_Positioning) { positionCount += it.glyphCount(); } if (it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning) { rsxFormCount += it.glyphCount(); } } prepareBuffers(positionCount, rsxFormCount); } void SkGlyphRunBuilder::prepareBuffers(int positionCount, int RSXFormCount) { if (positionCount > fMaxTotalRunSize) { fMaxTotalRunSize = positionCount; fPositions.reset(fMaxTotalRunSize); } if (RSXFormCount > fMaxScaledRotations) { fMaxScaledRotations = RSXFormCount; fScaledRotations.reset(RSXFormCount); } fGlyphRunListStorage.clear(); } SkSpan SkGlyphRunBuilder::textToGlyphIDs( const SkFont& font, const void* bytes, size_t byteLength, SkTextEncoding encoding) { if (encoding != SkTextEncoding::kGlyphID) { int count = font.countText(bytes, byteLength, encoding); if (count > 0) { fScratchGlyphIDs.resize(count); font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs.data(), count); return SkMakeSpan(fScratchGlyphIDs); } else { return SkSpan(); } } else { return SkSpan((const SkGlyphID*)bytes, byteLength / 2); } } void SkGlyphRunBuilder::makeGlyphRun( const SkFont& font, SkSpan glyphIDs, SkSpan positions, SkSpan text, SkSpan clusters, SkSpan scaledRotations) { // Ignore empty runs. if (!glyphIDs.empty()) { fGlyphRunListStorage.emplace_back( font, positions, glyphIDs, text, clusters, scaledRotations); } } const SkGlyphRunList& SkGlyphRunBuilder::makeGlyphRunList( const SkTextBlob* blob, const SkRect& bounds, SkPoint origin) { fGlyphRunList.~SkGlyphRunList(); return *new (&fGlyphRunList) SkGlyphRunList{blob, bounds, origin, SkMakeSpan(fGlyphRunListStorage)}; }