blob: 35257fb1a42b80347af4aca88e0dc4a2657f79f5 [file] [log] [blame]
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/utils/SkCustomTypeface.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkData.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkFontArguments.h"
#include "include/core/SkFontMetrics.h"
#include "include/core/SkFontParameters.h"
#include "include/core/SkFontStyle.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkStream.h"
#include "include/core/SkString.h"
#include "include/core/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "include/private/SkFixed.h"
#include "include/private/base/SkAlign.h"
#include "include/private/base/SkMalloc.h"
#include "include/private/base/SkTo.h"
#include "src/core/SkAdvancedTypefaceMetrics.h" // IWYU pragma: keep
#include "src/core/SkGlyph.h"
#include "src/core/SkMask.h"
#include "src/core/SkScalerContext.h"
#include "src/core/SkStreamPriv.h"
#include <cstdint>
#include <cstring>
#include <memory>
#include <utility>
#include <vector>
class SkArenaAlloc;
class SkDescriptor;
class SkFontDescriptor;
namespace {
static inline const constexpr bool kSkShowTextBlitCoverage = false;
}
static SkFontMetrics scale_fontmetrics(const SkFontMetrics& src, float sx, float sy) {
SkFontMetrics dst = src;
#define SCALE_X(field) dst.field *= sx
#define SCALE_Y(field) dst.field *= sy
SCALE_X(fAvgCharWidth);
SCALE_X(fMaxCharWidth);
SCALE_X(fXMin);
SCALE_X(fXMax);
SCALE_Y(fTop);
SCALE_Y(fAscent);
SCALE_Y(fDescent);
SCALE_Y(fBottom);
SCALE_Y(fLeading);
SCALE_Y(fXHeight);
SCALE_Y(fCapHeight);
SCALE_Y(fUnderlineThickness);
SCALE_Y(fUnderlinePosition);
SCALE_Y(fStrikeoutThickness);
SCALE_Y(fStrikeoutPosition);
#undef SCALE_X
#undef SCALE_Y
return dst;
}
class SkUserTypeface final : public SkTypeface {
private:
friend class SkCustomTypefaceBuilder;
friend class SkUserScalerContext;
explicit SkUserTypeface(SkFontStyle style, const SkFontMetrics& metrics,
std::vector<SkCustomTypefaceBuilder::GlyphRec>&& recs)
: SkTypeface(style)
, fGlyphRecs(std::move(recs))
, fMetrics(metrics)
{}
const std::vector<SkCustomTypefaceBuilder::GlyphRec> fGlyphRecs;
const SkFontMetrics fMetrics;
std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects&,
const SkDescriptor* desc) const override;
void onFilterRec(SkScalerContextRec* rec) const override;
void getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const override;
std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override;
void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
void onGetFamilyName(SkString* familyName) const override;
bool onGetPostScriptName(SkString*) const override;
SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
std::unique_ptr<SkStreamAsset> onOpenStream(int*) const override;
// trivial
std::unique_ptr<SkStreamAsset> onOpenExistingStream(int*) const override { return nullptr; }
sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override {
return sk_ref_sp(this);
}
int onCountGlyphs() const override { return this->glyphCount(); }
int onGetUPEM() const override { return 2048; /* ?? */ }
bool onComputeBounds(SkRect* bounds) const override {
bounds->setLTRB(fMetrics.fXMin, fMetrics.fTop, fMetrics.fXMax, fMetrics.fBottom);
return true;
}
// noops
void getPostScriptGlyphNames(SkString*) const override {}
bool onGlyphMaskNeedsCurrentColor() const override { return false; }
int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate[],
int) const override { return 0; }
int onGetVariationDesignParameters(SkFontParameters::Variation::Axis[],
int) const override { return 0; }
int onGetTableTags(SkFontTableTag tags[]) const override { return 0; }
size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; }
int glyphCount() const {
return SkToInt(fGlyphRecs.size());
}
};
SkCustomTypefaceBuilder::SkCustomTypefaceBuilder() {
sk_bzero(&fMetrics, sizeof(fMetrics));
}
void SkCustomTypefaceBuilder::setMetrics(const SkFontMetrics& fm, float scale) {
fMetrics = scale_fontmetrics(fm, scale, scale);
}
void SkCustomTypefaceBuilder::setFontStyle(SkFontStyle style) {
fStyle = style;
}
SkCustomTypefaceBuilder::GlyphRec& SkCustomTypefaceBuilder::ensureStorage(SkGlyphID index) {
if (index >= fGlyphRecs.size()) {
fGlyphRecs.resize(SkToSizeT(index) + 1);
}
return fGlyphRecs[index];
}
void SkCustomTypefaceBuilder::setGlyph(SkGlyphID index, float advance, const SkPath& path) {
auto& rec = this->ensureStorage(index);
rec.fAdvance = advance;
rec.fPath = path;
rec.fDrawable = nullptr;
}
void SkCustomTypefaceBuilder::setGlyph(SkGlyphID index, float advance,
sk_sp<SkDrawable> drawable, const SkRect& bounds) {
auto& rec = this->ensureStorage(index);
rec.fAdvance = advance;
rec.fDrawable = std::move(drawable);
rec.fBounds = bounds;
rec.fPath.reset();
}
sk_sp<SkTypeface> SkCustomTypefaceBuilder::detach() {
if (fGlyphRecs.empty()) return nullptr;
// initially inverted, so that any "union" will overwrite the first time
SkRect bounds = {SK_ScalarMax, SK_ScalarMax, -SK_ScalarMax, -SK_ScalarMax};
for (const auto& rec : fGlyphRecs) {
bounds.join(rec.isDrawable()
? rec.fBounds
: rec.fPath.getBounds());
}
fMetrics.fTop = bounds.top();
fMetrics.fBottom = bounds.bottom();
fMetrics.fXMin = bounds.left();
fMetrics.fXMax = bounds.right();
return sk_sp<SkUserTypeface>(new SkUserTypeface(fStyle, fMetrics, std::move(fGlyphRecs)));
}
/////////////
void SkUserTypeface::onFilterRec(SkScalerContextRec* rec) const {
rec->setHinting(SkFontHinting::kNone);
}
void SkUserTypeface::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const {
for (int gid = 0; gid < this->glyphCount(); ++gid) {
glyphToUnicode[gid] = SkTo<SkUnichar>(gid);
}
}
std::unique_ptr<SkAdvancedTypefaceMetrics> SkUserTypeface::onGetAdvancedMetrics() const {
return nullptr;
}
void SkUserTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
*isLocal = true;
}
void SkUserTypeface::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
for (int i = 0; i < count; ++i) {
glyphs[i] = uni[i] < this->glyphCount() ? SkTo<SkGlyphID>(uni[i]) : 0;
}
}
void SkUserTypeface::onGetFamilyName(SkString* familyName) const {
*familyName = "";
}
bool SkUserTypeface::onGetPostScriptName(SkString*) const {
return false;
}
SkTypeface::LocalizedStrings* SkUserTypeface::onCreateFamilyNameIterator() const {
return nullptr;
}
//////////////
class SkUserScalerContext : public SkScalerContext {
public:
SkUserScalerContext(sk_sp<SkUserTypeface> face,
const SkScalerContextEffects& effects,
const SkDescriptor* desc)
: SkScalerContext(std::move(face), effects, desc) {
fRec.getSingleMatrix(&fMatrix);
this->forceGenerateImageFromPath();
}
const SkUserTypeface* userTF() const {
return static_cast<SkUserTypeface*>(this->getTypeface());
}
protected:
bool generateAdvance(SkGlyph* glyph) override {
const SkUserTypeface* tf = this->userTF();
auto advance = fMatrix.mapXY(tf->fGlyphRecs[glyph->getGlyphID()].fAdvance, 0);
glyph->fAdvanceX = advance.fX;
glyph->fAdvanceY = advance.fY;
return true;
}
void generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) override {
glyph->zeroMetrics();
this->generateAdvance(glyph);
const auto& rec = this->userTF()->fGlyphRecs[glyph->getGlyphID()];
if (rec.isDrawable()) {
glyph->fMaskFormat = SkMask::kARGB32_Format;
SkRect bounds = fMatrix.mapRect(rec.fBounds);
bounds.offset(SkFixedToScalar(glyph->getSubXFixed()),
SkFixedToScalar(glyph->getSubYFixed()));
SkIRect ibounds;
bounds.roundOut(&ibounds);
glyph->fLeft = ibounds.fLeft;
glyph->fTop = ibounds.fTop;
glyph->fWidth = ibounds.width();
glyph->fHeight = ibounds.height();
// These do not have an outline path.
glyph->setPath(alloc, nullptr, false);
}
}
void generateImage(const SkGlyph& glyph) override {
const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()];
SkASSERTF(rec.isDrawable(), "Only drawable-backed glyphs should reach generateImage.");
auto canvas = SkCanvas::MakeRasterDirectN32(glyph.fWidth, glyph.fHeight,
static_cast<SkPMColor*>(glyph.fImage),
glyph.rowBytes());
if constexpr (kSkShowTextBlitCoverage) {
canvas->clear(0x33FF0000);
} else {
canvas->clear(SK_ColorTRANSPARENT);
}
canvas->translate(-glyph.fLeft, -glyph.fTop);
canvas->translate(SkFixedToScalar(glyph.getSubXFixed()),
SkFixedToScalar(glyph.getSubYFixed()));
canvas->drawDrawable(rec.fDrawable.get(), &fMatrix);
}
bool generatePath(const SkGlyph& glyph, SkPath* path) override {
const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()];
SkASSERT(!rec.isDrawable());
rec.fPath.transform(fMatrix, path);
return true;
}
sk_sp<SkDrawable> generateDrawable(const SkGlyph& glyph) override {
class DrawableMatrixWrapper final : public SkDrawable {
public:
DrawableMatrixWrapper(sk_sp<SkDrawable> drawable, const SkMatrix& m)
: fDrawable(std::move(drawable))
, fMatrix(m)
{}
SkRect onGetBounds() override {
return fMatrix.mapRect(fDrawable->getBounds());
}
size_t onApproximateBytesUsed() override {
return fDrawable->approximateBytesUsed() + sizeof(DrawableMatrixWrapper);
}
void onDraw(SkCanvas* canvas) override {
if constexpr (kSkShowTextBlitCoverage) {
SkPaint paint;
paint.setColor(0x3300FF00);
paint.setStyle(SkPaint::kFill_Style);
canvas->drawRect(this->onGetBounds(), paint);
}
canvas->drawDrawable(fDrawable.get(), &fMatrix);
}
private:
const sk_sp<SkDrawable> fDrawable;
const SkMatrix fMatrix;
};
const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()];
return rec.fDrawable
? sk_make_sp<DrawableMatrixWrapper>(rec.fDrawable, fMatrix)
: nullptr;
}
void generateFontMetrics(SkFontMetrics* metrics) override {
auto [sx, sy] = fMatrix.mapXY(1, 1);
*metrics = scale_fontmetrics(this->userTF()->fMetrics, sx, sy);
}
private:
SkMatrix fMatrix;
};
std::unique_ptr<SkScalerContext> SkUserTypeface::onCreateScalerContext(
const SkScalerContextEffects& effects, const SkDescriptor* desc) const
{
return std::make_unique<SkUserScalerContext>(
sk_ref_sp(const_cast<SkUserTypeface*>(this)), effects, desc);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
static constexpr int kMaxGlyphCount = 65536;
static constexpr size_t kHeaderSize = 16;
static const char gHeaderString[] = "SkUserTypeface01";
static_assert(sizeof(gHeaderString) == 1 + kHeaderSize, "need header to be 16 bytes");
enum GlyphType : uint32_t { kPath, kDrawable };
std::unique_ptr<SkStreamAsset> SkUserTypeface::onOpenStream(int* ttcIndex) const {
SkDynamicMemoryWStream wstream;
wstream.write(gHeaderString, kHeaderSize);
wstream.write(&fMetrics, sizeof(fMetrics));
SkFontStyle style = this->fontStyle();
wstream.write(&style, sizeof(style));
wstream.write32(this->glyphCount());
for (const auto& rec : fGlyphRecs) {
wstream.write32(rec.isDrawable() ? GlyphType::kDrawable : GlyphType::kPath);
wstream.writeScalar(rec.fAdvance);
wstream.write(&rec.fBounds, sizeof(rec.fBounds));
auto data = rec.isDrawable()
? rec.fDrawable->serialize()
: rec.fPath.serialize();
const size_t sz = data->size();
SkASSERT(SkIsAlign4(sz));
wstream.write(&sz, sizeof(sz));
wstream.write(data->data(), sz);
}
*ttcIndex = 0;
return wstream.detachAsStream();
}
class AutoRestorePosition {
SkStream* fStream;
size_t fPosition;
public:
AutoRestorePosition(SkStream* stream) : fStream(stream) {
fPosition = stream->getPosition();
}
~AutoRestorePosition() {
if (fStream) {
fStream->seek(fPosition);
}
}
// So we don't restore the position
void markDone() { fStream = nullptr; }
};
sk_sp<SkTypeface> SkCustomTypefaceBuilder::Deserialize(SkStream* stream) {
AutoRestorePosition arp(stream);
char header[kHeaderSize];
if (stream->read(header, kHeaderSize) != kHeaderSize ||
0 != memcmp(header, gHeaderString, kHeaderSize))
{
return nullptr;
}
SkFontMetrics metrics;
if (stream->read(&metrics, sizeof(metrics)) != sizeof(metrics)) {
return nullptr;
}
SkFontStyle style;
if (stream->read(&style, sizeof(style)) != sizeof(style)) {
return nullptr;
}
int glyphCount;
if (!stream->readS32(&glyphCount) || glyphCount < 0 || glyphCount > kMaxGlyphCount) {
return nullptr;
}
SkCustomTypefaceBuilder builder;
builder.setMetrics(metrics);
builder.setFontStyle(style);
for (int i = 0; i < glyphCount; ++i) {
uint32_t gtype;
if (!stream->readU32(&gtype) ||
(gtype != GlyphType::kDrawable && gtype != GlyphType::kPath)) {
return nullptr;
}
float advance;
if (!stream->readScalar(&advance)) {
return nullptr;
}
SkRect bounds;
if (stream->read(&bounds, sizeof(bounds)) != sizeof(bounds) || !bounds.isFinite()) {
return nullptr;
}
// SkPath and SkDrawable cannot read from a stream, so we have to page them into ram
size_t sz;
if (stream->read(&sz, sizeof(sz)) != sizeof(sz)) {
return nullptr;
}
// The amount of bytes in the stream must be at least as big as sz, otherwise
// sz is invalid.
if (StreamRemainingLengthIsBelow(stream, sz)) {
return nullptr;
}
auto data = SkData::MakeUninitialized(sz);
if (stream->read(data->writable_data(), sz) != sz) {
return nullptr;
}
switch (gtype) {
case GlyphType::kDrawable: {
auto drawable = SkDrawable::Deserialize(data->data(), data->size());
if (!drawable) {
return nullptr;
}
builder.setGlyph(i, advance, std::move(drawable), bounds);
} break;
case GlyphType::kPath: {
SkPath path;
if (path.readFromMemory(data->data(), data->size()) != data->size()) {
return nullptr;
}
builder.setGlyph(i, advance, path);
} break;
default:
return nullptr;
}
}
arp.markDone();
return builder.detach();
}