blob: 41f50134e9140e03fb6d66350feb65b52d196588 [file] [log] [blame]
/*
* 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 <algorithm>
#include <tuple>
#include "SkDraw.h"
#include "SkGlyphCache.h"
#include "SkMakeUnique.h"
#include "SkMSAN.h"
#include "SkPaint.h"
#include "SkPaintPriv.h"
#include "SkStrikeCache.h"
#include "SkUtils.h"
namespace {
static SkTypeface::Encoding convert_encoding(SkPaint::TextEncoding encoding) {
switch (encoding) {
case SkPaint::kUTF8_TextEncoding: return SkTypeface::kUTF8_Encoding;
case SkPaint::kUTF16_TextEncoding: return SkTypeface::kUTF16_Encoding;
case SkPaint::kUTF32_TextEncoding: return SkTypeface::kUTF32_Encoding;
default: return SkTypeface::kUTF32_Encoding;
}
}
using Core = std::tuple<size_t, std::unique_ptr<uint16_t[]>, std::vector<SkGlyphID>>;
Core make_from_glyphids(
size_t glyphCount, const SkGlyphID* glyphs, SkGlyphID maxGlyphID, SkGlyphSet* glyphSet) {
if (glyphCount == 0) { return Core(0, nullptr, std::vector<SkGlyphID>()); }
glyphSet->reuse(maxGlyphID);
auto denseIndex = skstd::make_unique_default<uint16_t[]>(glyphCount);
for (size_t i = 0; i < glyphCount; i++) {
denseIndex[i] = glyphSet->add(glyphs[i]);
}
return Core(glyphCount, std::move(denseIndex), glyphSet->uniqueGlyphIDs());
}
Core make_from_utfn(size_t byteLength, const void* utfN, const SkTypeface& typeface,
SkTypeface::Encoding encoding, SkGlyphSet* glyphSet) {
auto count = SkUTFN_CountUnichars(encoding, utfN, byteLength);
if (count <= 0) {
return Core(0, nullptr, std::vector<SkGlyphID>());
}
auto glyphs = skstd::make_unique_default<SkGlyphID[]>(count);
// TODO: move to using cached version.
typeface.charsToGlyphs(utfN, encoding, glyphs.get(), count);
return make_from_glyphids(count, glyphs.get(), typeface.countGlyphs(), glyphSet);
}
Core make_core(const SkPaint& paint, const void* bytes, size_t byteLength, SkGlyphSet* glyphSet) {
auto encoding = paint.getTextEncoding();
auto typeface = SkPaintPriv::GetTypefaceOrDefault(paint);
if (encoding == SkPaint::kGlyphID_TextEncoding) {
return make_from_glyphids(
byteLength / 2, reinterpret_cast<const SkGlyphID*>(bytes),
typeface->countGlyphs(), glyphSet);
} else {
return make_from_utfn(byteLength, bytes, *typeface, convert_encoding(encoding), glyphSet);
}
}
} // namespace
SkGlyphRun SkGlyphRun::MakeFromDrawText(
const SkPaint& paint, const void* bytes, size_t byteLength,
const SkPoint origin, SkGlyphSet* glyphSet) {
size_t runSize;
std::unique_ptr<uint16_t[]> denseIndex;
std::vector<SkGlyphID> uniqueGlyphIDs;
std::tie(runSize, denseIndex, uniqueGlyphIDs) = make_core(paint, bytes, byteLength, glyphSet);
if (runSize == 0) { return SkGlyphRun{}; }
auto advances = skstd::make_unique_default<SkPoint[]>(uniqueGlyphIDs.size());
{
auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(paint);
cache->getAdvances(SkSpan<SkGlyphID>{uniqueGlyphIDs.data(),
uniqueGlyphIDs.size()}, advances.get());
}
auto positions = skstd::make_unique_default<SkPoint[]>(runSize);
SkPoint endOfLastGlyph = origin;
for (size_t i = 0; i < runSize; i++) {
positions[i] = endOfLastGlyph;
endOfLastGlyph += advances[denseIndex[i]];
}
if (paint.getTextAlign() != SkPaint::kLeft_Align) {
SkVector len = endOfLastGlyph - origin;
if (paint.getTextAlign() == SkPaint::kCenter_Align) {
len.scale(SK_ScalarHalf);
}
for (size_t i = 0; i < runSize; i++) {
positions[i] -= len;
}
}
return SkGlyphRun{
runSize, std::move(denseIndex), std::move(positions), std::move(uniqueGlyphIDs)};
}
SkGlyphRun SkGlyphRun::MakeFromDrawPosTextH(
const SkPaint& paint, const void* bytes, size_t byteLength,
const SkScalar xpos[], SkScalar constY, SkGlyphSet* glyphSet) {
size_t runSize;
std::unique_ptr<uint16_t[]> denseIndex;
std::vector<SkGlyphID> uniqueGlyphIDs;
std::tie(runSize, denseIndex, uniqueGlyphIDs) = make_core(paint, bytes, byteLength, glyphSet);
if (runSize == 0) { return SkGlyphRun{}; }
auto positions = skstd::make_unique_default<SkPoint[]>(runSize);
for (size_t i = 0; i < runSize; i++) {
positions[i] = SkPoint::Make(xpos[i], constY);
}
return SkGlyphRun{
runSize, std::move(denseIndex), std::move(positions), std::move(uniqueGlyphIDs)};
}
SkGlyphRun SkGlyphRun::MakeFromDrawPosText(
const SkPaint& paint, const void* bytes, size_t byteLength,
const SkPoint pos[], SkGlyphSet* glyphSet) {
size_t runSize;
std::unique_ptr<uint16_t[]> denseIndex;
std::vector<SkGlyphID> uniqueGlyphIDs;
std::tie(runSize, denseIndex, uniqueGlyphIDs) = make_core(paint, bytes, byteLength, glyphSet);
if (runSize == 0) { return SkGlyphRun{}; }
auto positions = skstd::make_unique_default<SkPoint[]>(runSize);
memcpy(positions.get(), pos, sizeof(SkPoint) * runSize);
return SkGlyphRun{
runSize, std::move(denseIndex), std::move(positions), std::move(uniqueGlyphIDs)};
}
std::unique_ptr<SkGlyphID[]> SkGlyphRun::copyGlyphIDs() const {
auto glyphs = skstd::make_unique_default<SkGlyphID[]>(fRunSize);
for (size_t i = 0; i < fRunSize; i++) {
glyphs[i] = fUniqueGlyphs[fDenseIndex[i]];
}
return glyphs;
}
SkGlyphRun::SkGlyphRun(size_t runSize,
std::unique_ptr<uint16_t[]>&& denseIndex,
std::unique_ptr<SkPoint[]>&& positions,
std::vector<SkGlyphID>&& uniqueGlyphIDs)
: fDenseIndex{std::move(denseIndex)}
, fPositions{std::move(positions)}
, fUniqueGlyphs{std::move(uniqueGlyphIDs)}
, fRunSize{runSize} { }
uint16_t SkGlyphSet::add(SkGlyphID glyphID) {
static constexpr SkGlyphID kUndefGlyph{0};
if (glyphID >= fUniverseSize) {
glyphID = kUndefGlyph;
}
if (glyphID >= fIndices.size()) {
fIndices.resize(glyphID + 1);
}
auto index = fIndices[glyphID];
if (index < fUniqueGlyphIDs.size() && fUniqueGlyphIDs[index] == glyphID) {
return index;
}
uint16_t newIndex = SkTo<uint16_t>(fUniqueGlyphIDs.size());
fUniqueGlyphIDs.push_back(glyphID);
fIndices[glyphID] = newIndex;
return newIndex;
}
std::vector<SkGlyphID> SkGlyphSet::uniqueGlyphIDs() {
return fUniqueGlyphIDs;
}
void SkGlyphSet::reuse(uint32_t glyphUniverseSize) {
SkASSERT(glyphUniverseSize <= (1 << 16));
fUniverseSize = glyphUniverseSize;
// 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 more than 256 unique glyphs used in a run,
// or a typeface with more than 4096 possible glyphs.
if (fUniqueGlyphIDs.size() > 256) {
fUniqueGlyphIDs.resize(256);
fUniqueGlyphIDs.shrink_to_fit();
}
fUniqueGlyphIDs.clear();
if (glyphUniverseSize < 4096 && fIndices.size() > 4096) {
fIndices.resize(4096);
fIndices.shrink_to_fit();
}
// No need to clear fIndices here... SkGlyphSet's set insertion algorithm is designed to work
// correctly even when the fIndexes buffer is uninitialized!
}