/* * 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 "SkGlyphRun.h" #include "SkDevice.h" #include "SkFont.h" #include "SkFontPriv.h" #include "SkPaint.h" #include "SkStrike.h" #include "SkStrikeCache.h" #include "SkTextBlob.h" #include "SkTextBlobPriv.h" #include "SkTo.h" #include "SkUtils.h" // -- SkGlyphRun ----------------------------------------------------------------------------------- SkGlyphRun::SkGlyphRun(const SkFont& font, SkSpan<const SkPoint> positions, SkSpan<const SkGlyphID> glyphIDs, SkSpan<const char> text, SkSpan<const uint32_t> clusters) : fPositions{positions} , fGlyphIDs{glyphIDs} , fText{text} , fClusters{clusters} , fFont{font} {} SkGlyphRun::SkGlyphRun(const SkGlyphRun& that, const SkFont& font) : fPositions{that.fPositions} , fGlyphIDs{that.fGlyphIDs} , fText{that.fText} , fClusters{that.fClusters} , fFont{font} {} void SkGlyphRun::filloutGlyphsAndPositions(SkGlyphID* glyphIDs, SkPoint* positions) { memcpy(glyphIDs, fGlyphIDs.data(), fGlyphIDs.size_bytes()); memcpy(positions, fPositions.data(), fPositions.size_bytes()); } // -- SkGlyphRunList ------------------------------------------------------------------------------- SkGlyphRunList::SkGlyphRunList() = default; SkGlyphRunList::SkGlyphRunList( const SkPaint& paint, const SkTextBlob* blob, SkPoint origin, SkSpan<const SkGlyphRun> glyphRunList) : fOriginalPaint{&paint} , fOriginalTextBlob{blob} , fOrigin{origin} , fGlyphRuns{glyphRunList} { } SkGlyphRunList::SkGlyphRunList(const SkGlyphRun& glyphRun, const SkPaint& paint) : fOriginalPaint{&paint} , fOriginalTextBlob{nullptr} , fOrigin{SkPoint::Make(0, 0)} , fGlyphRuns{SkSpan<const SkGlyphRun>{&glyphRun, 1}} {} 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; } bool SkGlyphRunList::anyRunsSubpixelPositioned() const { for (const auto& r : fGlyphRuns) { if (r.font().isSubpixel()) { return true; } } return false; } bool SkGlyphRunList::allFontsFinite() const { for (const auto& r : fGlyphRuns) { if (!SkFontPriv::IsFinite(r.font())) { return false; } } return true; } void SkGlyphRunList::temporaryShuntBlobNotifyAddedToCache(uint32_t cacheID) const { SkASSERT(fOriginalTextBlob != nullptr); fOriginalTextBlob->notifyAddedToCache(cacheID); } // -- SkGlyphIDSet --------------------------------------------------------------------------------- // A faster set implementation that does not need any initialization, and reading the set items // is order the number of items, and not the size of the universe. // This implementation is based on the paper by Briggs and Torczon, "An Efficient Representation // for Sparse Sets" // // This implementation assumes that the unique glyphs added are appended to a vector that may // already have unique glyph from a previous computation. This allows the packing of multiple // UniqueID sequences in a single vector. SkSpan<const SkGlyphID> SkGlyphIDSet::uniquifyGlyphIDs( uint32_t universeSize, SkSpan<const SkGlyphID> glyphIDs, SkGlyphID* uniqueGlyphIDs, uint16_t* denseIndices) { static constexpr SkGlyphID kUndefGlyph{0}; if (universeSize > fUniverseToUniqueSize) { fUniverseToUnique.reset(universeSize); fUniverseToUniqueSize = universeSize; // If the following bzero becomes a performance problem, the memory can be marked as // initialized for valgrind and msan. // valgrind = VALGRIND_MAKE_MEM_DEFINED(fUniverseToUnique, universeSize * sizeof(SkGlyphID)) // msan = sk_msan_mark_initialized(fUniverseToUnique, universeSize * sizeof(SkGlyphID)) sk_bzero(fUniverseToUnique, universeSize * sizeof(SkGlyphID)); } // No need to clear fUniverseToUnique here... the set insertion algorithm is designed to work // correctly even when the fUniverseToUnique buffer is uninitialized! size_t uniqueSize = 0; size_t denseIndicesCursor = 0; for (auto glyphID : glyphIDs) { // If the glyphID is not in range then it is the undefined glyph. if (glyphID >= universeSize) { glyphID = kUndefGlyph; } // The index into the unique ID vector. auto uniqueIndex = fUniverseToUnique[glyphID]; if (uniqueIndex >= uniqueSize || uniqueGlyphIDs[uniqueIndex] != glyphID) { uniqueIndex = SkTo<uint16_t>(uniqueSize); uniqueGlyphIDs[uniqueSize] = glyphID; fUniverseToUnique[glyphID] = uniqueIndex; uniqueSize += 1; } denseIndices[denseIndicesCursor++] = uniqueIndex; } // If we're hanging onto these arrays for a long time, we don't want their size to drift // endlessly upwards. It's unusual to see a typeface with more than 4096 possible glyphs. if (fUniverseToUniqueSize > 4096) { fUniverseToUnique.reset(4096); sk_bzero(fUniverseToUnique, 4096 * sizeof(SkGlyphID)); fUniverseToUniqueSize = 4096; } return SkSpan<const SkGlyphID>(uniqueGlyphIDs, uniqueSize); } // -- SkGlyphRunBuilder ---------------------------------------------------------------------------- void SkGlyphRunBuilder::drawTextUTF8(const SkPaint& paint, const SkFont& font, const void* bytes, size_t byteLength, SkPoint origin) { auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, kUTF8_SkTextEncoding); if (!glyphIDs.empty()) { this->initialize(glyphIDs.size()); this->simplifyDrawText(font, glyphIDs, origin, fPositions); } this->makeGlyphRunList(paint, nullptr, SkPoint::Make(0, 0)); } void SkGlyphRunBuilder::drawTextBlob(const SkPaint& paint, const SkTextBlob& blob, SkPoint origin, SkBaseDevice* device) { // Figure out all the storage needed to pre-size everything below. size_t totalGlyphs = 0; for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { totalGlyphs += it.glyphCount(); } // Pre-size all the buffers so they don't move during processing. this->initialize(totalGlyphs); SkPoint* positions = fPositions; for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { // applyFontToPaint() always overwrites the exact same attributes, // so it is safe to not re-seed the paint for this reason. size_t runSize = it.glyphCount(); auto text = SkSpan<const char>(it.text(), it.textSize()); auto clusters = SkSpan<const uint32_t>(it.clusters(), runSize); const SkPoint& offset = it.offset(); auto glyphIDs = SkSpan<const SkGlyphID>{it.glyphs(), runSize}; switch (it.positioning()) { case SkTextBlobRunIterator::kDefault_Positioning: { this->simplifyDrawText( it.font(), glyphIDs, offset, positions, text, clusters); } break; case SkTextBlobRunIterator::kHorizontal_Positioning: { auto constY = offset.y(); this->simplifyDrawPosTextH( it.font(), glyphIDs, it.pos(), constY, positions, text, clusters); } break; case SkTextBlobRunIterator::kFull_Positioning: this->simplifyDrawPosText( it.font(), glyphIDs, (const SkPoint*)it.pos(), text, clusters); break; case SkTextBlobRunIterator::kRSXform_Positioning: { if (!this->empty()) { this->makeGlyphRunList(paint, &blob, origin); device->drawGlyphRunList(this->useGlyphRunList()); } device->drawGlyphRunRSXform(it.font(), it.glyphs(), (const SkRSXform*)it.pos(), runSize, origin, paint); // re-init in case we keep looping and need the builder again this->initialize(totalGlyphs); } break; } positions += runSize; } if (!this->empty()) { this->makeGlyphRunList(paint, &blob, origin); device->drawGlyphRunList(this->useGlyphRunList()); } } void SkGlyphRunBuilder::drawGlyphsWithPositions(const SkPaint& paint, const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, const SkPoint* pos) { if (!glyphIDs.empty()) { this->initialize(glyphIDs.size()); this->simplifyDrawPosText(font, glyphIDs, pos); this->makeGlyphRunList(paint, nullptr, SkPoint::Make(0, 0)); } } const SkGlyphRunList& SkGlyphRunBuilder::useGlyphRunList() { return fGlyphRunList; } void SkGlyphRunBuilder::initialize(size_t totalRunSize) { if (totalRunSize > fMaxTotalRunSize) { fMaxTotalRunSize = totalRunSize; fPositions.reset(fMaxTotalRunSize); } fGlyphRunListStorage.clear(); } SkSpan<const SkGlyphID> SkGlyphRunBuilder::textToGlyphIDs( const SkFont& font, const void* bytes, size_t byteLength, SkTextEncoding encoding) { if (encoding != kGlyphID_SkTextEncoding) { int count = font.countText(bytes, byteLength, encoding); if (count > 0) { fScratchGlyphIDs.resize(count); font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs.data(), count); return SkSpan<const SkGlyphID>{fScratchGlyphIDs}; } else { return SkSpan<const SkGlyphID>(); } } else { return SkSpan<const SkGlyphID>((const SkGlyphID*)bytes, byteLength / 2); } } void SkGlyphRunBuilder::makeGlyphRun( const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, SkSpan<const SkPoint> positions, SkSpan<const char> text, SkSpan<const uint32_t> clusters) { // Ignore empty runs. if (!glyphIDs.empty()) { fGlyphRunListStorage.emplace_back( font, positions, glyphIDs, text, clusters); } } void SkGlyphRunBuilder::makeGlyphRunList( const SkPaint& paint, const SkTextBlob* blob, SkPoint origin) { fGlyphRunList.~SkGlyphRunList(); new (&fGlyphRunList) SkGlyphRunList{ paint, blob, origin, SkSpan<const SkGlyphRun>{fGlyphRunListStorage}}; } void SkGlyphRunBuilder::simplifyDrawText( const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin, SkPoint* positions, SkSpan<const char> text, SkSpan<const uint32_t> clusters) { SkASSERT(!glyphIDs.empty()); auto runSize = glyphIDs.size(); if (!glyphIDs.empty()) { fScratchAdvances.resize(runSize); { auto cache = SkStrikeCache::FindOrCreateStrikeWithNoDeviceExclusive(font); cache->getAdvances(glyphIDs, fScratchAdvances.data()); } SkPoint endOfLastGlyph = origin; for (size_t i = 0; i < runSize; i++) { positions[i] = endOfLastGlyph; endOfLastGlyph += fScratchAdvances[i]; } this->makeGlyphRun( font, glyphIDs, SkSpan<const SkPoint>{positions, runSize}, text, clusters); } } void SkGlyphRunBuilder::simplifyDrawPosTextH( const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, const SkScalar* xpos, SkScalar constY, SkPoint* positions, SkSpan<const char> text, SkSpan<const uint32_t> clusters) { auto posCursor = positions; for (auto x : SkSpan<const SkScalar>{xpos, glyphIDs.size()}) { *posCursor++ = SkPoint::Make(x, constY); } simplifyDrawPosText(font, glyphIDs, positions, text, clusters); } void SkGlyphRunBuilder::simplifyDrawPosText( const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, const SkPoint* pos, SkSpan<const char> text, SkSpan<const uint32_t> clusters) { auto runSize = glyphIDs.size(); this->makeGlyphRun( font, glyphIDs, SkSpan<const SkPoint>{pos, runSize}, text, clusters); }