Revert "Revert "custom typeface""

Fix: implement onComputeBounds() and generateFontMetrics()

This reverts commit 0066adefa9.

Change-Id: Idb59336a3d201bb97e494ee0e0bb189e0a7186f1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/288536
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
This commit is contained in:
Mike Reed 2020-05-07 13:31:35 -04:00 committed by Skia Commit-Bot
parent 8650d33f7a
commit 61642b3366
7 changed files with 518 additions and 0 deletions

89
gm/userfont.cpp Normal file
View File

@ -0,0 +1,89 @@
/*
* 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 "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/utils/SkCustomTypeface.h"
#include "tools/Resources.h"
static sk_sp<SkTypeface> make_tf() {
SkCustomTypefaceBuilder builder(128);
SkFont font;
font.setSize(1.0f);
font.setHinting(SkFontHinting::kNone);
// Steal the first 128 chars from the default font
for (SkGlyphID index = 0; index <= 127; ++index) {
SkGlyphID glyph = font.unicharToGlyph(index);
SkScalar width;
font.getWidths(&glyph, 1, &width);
SkPath path;
font.getPath(glyph, &path);
// we use the charcode to be our glyph index, since we have no cmap table
builder.setGlyph(index, width, path);
}
return builder.detach();
}
#include "include/core/SkTextBlob.h"
class UserFontGM : public skiagm::GM {
sk_sp<SkTypeface> fTF;
sk_sp<SkTextBlob> fBlob;
SkPath fPath;
public:
UserFontGM() {}
void onOnceBeforeDraw() override {
fTF = make_tf();
SkFont font(fTF);
font.setSize(100);
font.setEdging(SkFont::Edging::kAntiAlias);
std::vector<SkGlyphID> array;
auto expand8to16 = [&](const char str[]) {
for (int i = 0; str[i]; ++i) {
array.push_back(str[i]);
}
};
expand8to16("User Typeface");
fBlob = SkTextBlob::MakeFromText(array.data(), array.size() * sizeof(SkGlyphID),
font, SkTextEncoding::kGlyphID);
}
bool runAsBench() const override { return true; }
SkString onShortName() override { return SkString("user_typeface"); }
SkISize onISize() override { return {512, 512}; }
void onDraw(SkCanvas* canvas) override {
SkScalar x = 20,
y = 250;
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
canvas->drawRect(fBlob->bounds().makeOffset(x, y), paint);
paint.setStyle(SkPaint::kFill_Style);
paint.setColor(SK_ColorRED);
canvas->drawTextBlob(fBlob, x, y, paint);
}
};
DEF_GM(return new UserFontGM;)

View File

@ -378,6 +378,7 @@ gm_sources = [
"$_gm/trickycubicstrokes.cpp",
"$_gm/typeface.cpp",
"$_gm/unpremul.cpp",
"$_gm/userfont.cpp",
"$_gm/variedtext.cpp",
"$_gm/verifiers/gmverifier.cpp",
"$_gm/vertices.cpp",

View File

@ -12,6 +12,7 @@ skia_utils_public = [
"$_include/utils/SkBase64.h",
"$_include/utils/SkCamera.h",
"$_include/utils/SkCanvasStateUtils.h",
"$_include/utils/SkCustomTypeface.h",
"$_include/utils/SkEventTracer.h",
"$_include/utils/SkInterpolator.h",
"$_include/utils/SkNWayCanvas.h",
@ -40,6 +41,7 @@ skia_utils_sources = [
"$_src/utils/SkCharToGlyphCache.h",
"$_src/utils/SkClipStackUtils.cpp",
"$_src/utils/SkClipStackUtils.h",
"$_src/utils/SkCustomTypeface.cpp",
"$_src/utils/SkDashPath.cpp",
"$_src/utils/SkDashPathPriv.h",
"$_src/utils/SkEventTracer.cpp",

View File

@ -0,0 +1,42 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkCustomTypeface_DEFINED
#define SkCustomTypeface_DEFINED
#include "include/core/SkImage.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkPicture.h"
#include "include/core/SkTypeface.h"
#include <vector>
class SkStream;
class SkCustomTypefaceBuilder {
public:
SkCustomTypefaceBuilder(int numGlyphs);
void setGlyph(SkGlyphID, float advance, const SkPath&);
void setGlyph(SkGlyphID, float advance, const SkPath&, const SkPaint&);
void setGlyph(SkGlyphID, float advance, sk_sp<SkImage>, float scale);
void setGlyph(SkGlyphID, float advance, sk_sp<SkPicture>);
sk_sp<SkTypeface> detach();
private:
int fGlyphCount;
std::vector<SkPath> fPaths;
std::vector<float> fAdvances;
static sk_sp<SkTypeface> Deserialize(SkStream*);
friend class SkTypeface;
};
#endif

View File

@ -296,6 +296,7 @@ private:
friend class SkStrikeServer;
friend class SkTestScalerContext;
friend class SkTestSVGScalerContext;
friend class SkUserScalerContext;
friend class TestSVGTypeface;
friend class TestTypeface;

View File

@ -11,6 +11,7 @@
#include "include/core/SkTypeface.h"
#include "include/private/SkMutex.h"
#include "include/private/SkOnce.h"
#include "include/utils/SkCustomTypeface.h"
#include "src/core/SkAdvancedTypefaceMetrics.h"
#include "src/core/SkEndian.h"
#include "src/core/SkFontDescriptor.h"
@ -161,6 +162,12 @@ sk_sp<SkTypeface> SkTypeface::MakeFromData(sk_sp<SkData> data, int index) {
}
sk_sp<SkTypeface> SkTypeface::MakeFromFontData(std::unique_ptr<SkFontData> data) {
if (data->hasStream()) {
if (auto tf = SkCustomTypefaceBuilder::Deserialize(data->getStream())) {
return tf;
}
}
return SkFontMgr::RefDefault()->makeFromFontData(std::move(data));
}

View File

@ -0,0 +1,376 @@
/*
* 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/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkFontMetrics.h"
#include "include/utils/SkCustomTypeface.h"
#include "src/core/SkAdvancedTypefaceMetrics.h"
class SkUserTypeface : public SkTypeface {
friend class SkCustomTypefaceBuilder;
friend class SkUserScalerContext;
SkUserTypeface(int count)
: SkTypeface(SkFontStyle())
, fGlyphCount(count)
{}
const int fGlyphCount;
std::vector<SkPath> fPaths;
std::vector<float> fAdvances;
SkRect fBounds;
protected:
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;
SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
std::unique_ptr<SkStreamAsset> onOpenStream(int*) const override;
// trivial
sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override {
return sk_ref_sp(this);
}
int onCountGlyphs() const override { return fGlyphCount; }
int onGetUPEM() const override { return 2048; /* ?? */ }
bool onComputeBounds(SkRect* bounds) const override { *bounds = fBounds; return true; }
// noops
void getPostScriptGlyphNames(SkString*) const override {}
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; }
};
SkCustomTypefaceBuilder::SkCustomTypefaceBuilder(int numGlyphs) : fGlyphCount(numGlyphs) {
fAdvances.resize(numGlyphs);
fPaths.resize(numGlyphs);
}
void SkCustomTypefaceBuilder::setGlyph(SkGlyphID index, float advance, const SkPath& path) {
if (index >= (unsigned)fGlyphCount) {
return;
}
fAdvances[index] = advance;
fPaths[index] = path;
}
sk_sp<SkTypeface> SkCustomTypefaceBuilder::detach() {
if (fGlyphCount <= 0) return nullptr;
SkUserTypeface* tf = new SkUserTypeface(fGlyphCount);
tf->fAdvances = std::move(fAdvances);
tf->fPaths = std::move(fPaths);
// initially inverted, so that any "union" will overwrite the first time
SkRect bounds = {SK_ScalarMax, SK_ScalarMax, -SK_ScalarMax, -SK_ScalarMax};
for (const auto& path : tf->fPaths) {
if (!path.isEmpty()) {
bounds.join(path.getBounds());
}
}
tf->fBounds = bounds;
return sk_sp<SkTypeface>(tf);
}
/////////////
#include "src/core/SkScalerContext.h"
void SkUserTypeface::onFilterRec(SkScalerContextRec* rec) const {
rec->setHinting(SkFontHinting::kNone);
}
void SkUserTypeface::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const {
for (int gid = 0; gid < fGlyphCount; ++gid) {
glyphToUnicode[gid] = 0;
}
}
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] = 0;
}
}
void SkUserTypeface::onGetFamilyName(SkString* familyName) const {
*familyName = "";
}
SkTypeface::LocalizedStrings* SkUserTypeface::onCreateFamilyNameIterator() const {
return nullptr;
}
//////////////
#include "src/core/SkScalerContext.h"
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:
unsigned generateGlyphCount() override {
return this->userTF()->fGlyphCount;
}
bool generateAdvance(SkGlyph* glyph) override {
const SkUserTypeface* tf = this->userTF();
auto advance = fMatrix.mapXY(tf->fAdvances[glyph->getGlyphID()], 0);
glyph->fAdvanceX = advance.fX;
glyph->fAdvanceY = advance.fY;
return true;
}
void generateMetrics(SkGlyph* glyph) override {
glyph->zeroMetrics();
this->generateAdvance(glyph);
// Always generates from paths, so SkScalerContext::getMetrics will figure the bounds.
}
void generateImage(const SkGlyph&) override { SK_ABORT("Should have generated from path."); }
bool generatePath(SkGlyphID glyph, SkPath* path) override {
this->userTF()->fPaths[glyph].transform(fMatrix, path);
return true;
}
void generateFontMetrics(SkFontMetrics* metrics) override {
const auto [_, sy] = fMatrix.mapXY(0, 1);
sk_bzero(metrics, sizeof(*metrics));
metrics->fTop = this->userTF()->fBounds.fTop * sy;
metrics->fBottom = this->userTF()->fBounds.fBottom * sy;
// todo: get these from the creator of the typeface?
metrics->fAscent = metrics->fTop;
metrics->fDescent = metrics->fBottom;
}
private:
SkMatrix fMatrix;
};
SkScalerContext* SkUserTypeface::onCreateScalerContext(const SkScalerContextEffects& effects,
const SkDescriptor* desc) const {
return new SkUserScalerContext(sk_ref_sp(const_cast<SkUserTypeface*>(this)), effects, desc);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "include/private/SkFloatingPoint.h"
#include "src/core/SkAutoMalloc.h"
#include "src/core/SkPathPriv.h"
static void write_scaled_float_to_16(SkWStream* stream, float x, float scale) {
stream->write16(SkToS16(sk_float_round2int(x * scale)) & 0xFFFF);
}
enum PVerb {
kMove,
kLine,
kCurve,
kClose,
};
static void compress_write(SkWStream* stream, const SkPath& path, int upem) {
int pCount = 0;
std::vector<PVerb> verbs;
for (auto [v, p, w] : SkPathPriv::Iterate(path)) {
switch (v) {
default: SkASSERT(!"oops"); break;
case SkPathVerb::kMove: verbs.push_back(kMove); pCount += 1; break;
case SkPathVerb::kQuad: verbs.push_back(kCurve); pCount += 2; break;
case SkPathVerb::kLine: verbs.push_back(kLine); pCount += 1; break;
case SkPathVerb::kClose: verbs.push_back(kClose); break;
}
}
int vCount = verbs.size();
stream->write16(upem); // share w/ other paths?
stream->write16(vCount);
stream->write16(pCount);
for (int i = 0; i < (vCount & ~3); i += 4) {
stream->write8((verbs[i+0]<<6) | (verbs[i+1]<<4) | (verbs[i+2]<<2) | verbs[i+3]);
}
if (vCount & 3) {
uint8_t b = 0;
int shift = 6;
for (int i = vCount & ~3; i < vCount; ++i) {
b |= verbs[i] << shift;
shift >>= 2;
}
stream->write8(b);
}
if (vCount & 1) {
stream->write8(0);
}
const float scale = (float)upem;
auto write_pts = [&](const SkPoint pts[], int count) {
for (int i = 0; i < count; ++i) {
write_scaled_float_to_16(stream, pts[i].fX, scale);
write_scaled_float_to_16(stream, pts[i].fY, scale);
}
};
for (auto [v, p, w] : SkPathPriv::Iterate(path)) {
switch (v) {
default: SkASSERT(!"oops"); break;
case SkPathVerb::kMove: write_pts(&p[0], 1); break;
case SkPathVerb::kQuad: write_pts(&p[1], 2); break;
case SkPathVerb::kLine: write_pts(&p[1], 1); break;
case SkPathVerb::kClose: break;
}
}
}
static constexpr int kMaxGlyphCount = 65536;
static constexpr size_t kHeaderSize = 16;
static const char gHeaderString[] = "SkUserTypeface00";
static_assert(sizeof(gHeaderString) == 1 + kHeaderSize, "need header to be 16 bytes");
std::unique_ptr<SkStreamAsset> SkUserTypeface::onOpenStream(int* ttcIndex) const {
SkDynamicMemoryWStream wstream;
wstream.write(gHeaderString, kHeaderSize);
SkASSERT(fAdvances.size() == (unsigned)fGlyphCount);
SkASSERT(fPaths.size() == (unsigned)fGlyphCount);
// just hacking around -- this makes the serialized font 1/2 size
const bool use_compression = false;
wstream.write32(fGlyphCount);
if (use_compression) {
for (float a : fAdvances) {
write_scaled_float_to_16(&wstream, a, 2048);
}
} else {
wstream.write(fAdvances.data(), fGlyphCount * sizeof(float));
}
for (const auto& p : fPaths) {
if (use_compression) {
compress_write(&wstream, p, 2048);
} else {
auto data = p.serialize();
SkASSERT(SkIsAlign4(data->size()));
wstream.write(data->data(), data->size());
}
}
// SkDebugf("%d glyphs, %d bytes\n", fGlyphCount, wstream.bytesWritten());
*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 ||
memcmp(header, gHeaderString, kHeaderSize) != 0)
{
return nullptr;
}
int glyphCount;
if (!stream->readS32(&glyphCount) || glyphCount < 0 || glyphCount > kMaxGlyphCount) {
return nullptr;
}
SkCustomTypefaceBuilder builder(glyphCount);
std::vector<float> advances(glyphCount);
if (stream->read(advances.data(), glyphCount * sizeof(float)) != glyphCount * sizeof(float)) {
return nullptr;
}
// SkPath can read from a stream, so we have to page the rest into ram
const size_t offset = stream->getPosition();
const size_t length = stream->getLength() - offset;
SkAutoMalloc ram(length);
char* buffer = (char*)ram.get();
if (stream->read(buffer, length) != length) {
return nullptr;
}
size_t totalUsed = 0;
for (int i = 0; i < glyphCount; ++i) {
SkPath path;
size_t used = path.readFromMemory(buffer + totalUsed, length - totalUsed);
if (used == 0) {
return nullptr;
}
builder.setGlyph(i, advances[i], path);
totalUsed += used;
SkASSERT(length >= totalUsed);
}
// all done, update the stream to only reflect the bytes we needed
stream->seek(offset + totalUsed);
arp.markDone();
return builder.detach();
}