/*
* 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);
}