Expose API for gx font variation axes.
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1590223003 Review URL: https://codereview.chromium.org/1590223003
This commit is contained in:
parent
27a6e86fb1
commit
f6c7107d03
@ -41,6 +41,7 @@ protected:
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setLCDRenderText(true);
|
||||
SkAutoTUnref<SkFontMgr> fontMgr(SkFontMgr::RefDefault());
|
||||
|
||||
SkAutoTDelete<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf"));
|
||||
if (!distortable) {
|
||||
@ -54,9 +55,11 @@ protected:
|
||||
SkScalar x = SkIntToScalar(10);
|
||||
SkScalar y = SkIntToScalar(20);
|
||||
|
||||
SkFixed axis = SkDoubleToFixed(0.5 + (5*j + i) * ((2.0 - 0.5) / (2 * 5)));
|
||||
SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromFontData(
|
||||
new SkFontData(distortable->duplicate(), 0, &axis, 1)));
|
||||
SkFourByteTag tag = SkSetFourByteTag('w','g','h','t');
|
||||
SkScalar styleValue = SkDoubleToScalar(0.5 + (5*j + i) * ((2.0 - 0.5) / (2 * 5)));
|
||||
SkFontMgr::FontParameters::Axis axes[] = { { tag, styleValue } };
|
||||
SkAutoTUnref<SkTypeface> typeface(fontMgr->createFromStream(
|
||||
distortable->duplicate(), SkFontMgr::FontParameters().setAxes(axes, 1)));
|
||||
paint.setTypeface(typeface);
|
||||
|
||||
SkAutoCanvasRestore acr(canvas, true);
|
||||
|
@ -8,8 +8,10 @@
|
||||
#ifndef SkFontMgr_DEFINED
|
||||
#define SkFontMgr_DEFINED
|
||||
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkFontStyle.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkScalar.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
class SkData;
|
||||
class SkFontData;
|
||||
@ -100,6 +102,52 @@ public:
|
||||
*/
|
||||
SkTypeface* createFromStream(SkStreamAsset*, int ttcIndex = 0) const;
|
||||
|
||||
struct FontParameters {
|
||||
struct Axis {
|
||||
SkFourByteTag fTag;
|
||||
SkScalar fStyleValue;
|
||||
};
|
||||
|
||||
FontParameters() : fCollectionIndex(0), fAxisCount(0), fAxes(nullptr) {}
|
||||
|
||||
/** Specify the index of the desired font.
|
||||
*
|
||||
* Font formats like ttc, dfont, cff, cid, pfr, t42, t1, and fon may actually be indexed
|
||||
* collections of fonts.
|
||||
*/
|
||||
FontParameters& setCollectionIndex(int collectionIndex) {
|
||||
fCollectionIndex = collectionIndex;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Specify the GX variation axis values.
|
||||
*
|
||||
* Any axes not specified will use the default value. Specified axes not present in the
|
||||
* font will be ignored.
|
||||
*
|
||||
* @param axes not copied. This pointer must remain valid for life of FontParameters.
|
||||
*/
|
||||
FontParameters& setAxes(const Axis* axes, int axisCount) {
|
||||
fAxisCount = axisCount;
|
||||
fAxes = axes;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int getCollectionIndex() const {
|
||||
return fCollectionIndex;
|
||||
}
|
||||
const Axis* getAxes(int* axisCount) const {
|
||||
*axisCount = fAxisCount;
|
||||
return fAxes;
|
||||
}
|
||||
private:
|
||||
int fCollectionIndex;
|
||||
int fAxisCount;
|
||||
const Axis* fAxes;
|
||||
};
|
||||
/* Experimental, API subject to change. */
|
||||
SkTypeface* createFromStream(SkStreamAsset*, const FontParameters&) const;
|
||||
|
||||
/**
|
||||
* Create a typeface from the specified font data.
|
||||
* Takes ownership of the font data, so the caller should not reference it again.
|
||||
@ -144,6 +192,7 @@ protected:
|
||||
virtual SkTypeface* onCreateFromData(SkData*, int ttcIndex) const = 0;
|
||||
virtual SkTypeface* onCreateFromStream(SkStreamAsset*, int ttcIndex) const = 0;
|
||||
// TODO: make pure virtual.
|
||||
virtual SkTypeface* onCreateFromStream(SkStreamAsset*, const FontParameters&) const;
|
||||
virtual SkTypeface* onCreateFromFontData(SkFontData*) const;
|
||||
virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const = 0;
|
||||
|
||||
|
@ -132,6 +132,13 @@ SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, int ttcIndex) con
|
||||
return this->onCreateFromStream(stream, ttcIndex);
|
||||
}
|
||||
|
||||
SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, const FontParameters& params) const {
|
||||
if (nullptr == stream) {
|
||||
return nullptr;
|
||||
}
|
||||
return this->onCreateFromStream(stream, params);
|
||||
}
|
||||
|
||||
SkTypeface* SkFontMgr::createFromFontData(SkFontData* data) const {
|
||||
if (nullptr == data) {
|
||||
return nullptr;
|
||||
@ -139,6 +146,11 @@ SkTypeface* SkFontMgr::createFromFontData(SkFontData* data) const {
|
||||
return this->onCreateFromFontData(data);
|
||||
}
|
||||
|
||||
// This implementation is temporary until it can be made pure virtual.
|
||||
SkTypeface* SkFontMgr::onCreateFromStream(SkStreamAsset* stream, const FontParameters& p) const {
|
||||
return this->createFromStream(stream, p.getCollectionIndex());
|
||||
}
|
||||
|
||||
// This implementation is temporary until it can be made pure virtual.
|
||||
SkTypeface* SkFontMgr::onCreateFromFontData(SkFontData* data) const {
|
||||
SkTypeface* ret = this->createFromStream(data->detachStream(), data->getIndex());
|
||||
|
@ -1741,3 +1741,57 @@ bool SkTypeface_FreeType::Scanner::scanFont(
|
||||
FT_Done_Face(face);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*static*/ void SkTypeface_FreeType::Scanner::computeAxisValues(
|
||||
AxisDefinitions axisDefinitions,
|
||||
const SkFontMgr::FontParameters::Axis* requestedAxes, int requestedAxisCount,
|
||||
SkFixed* axisValues,
|
||||
const SkString& name)
|
||||
{
|
||||
for (int i = 0; i < axisDefinitions.count(); ++i) {
|
||||
const Scanner::AxisDefinition& axisDefinition = axisDefinitions[i];
|
||||
axisValues[i] = axisDefinition.fDefault;
|
||||
for (int j = 0; j < requestedAxisCount; ++j) {
|
||||
const SkFontMgr::FontParameters::Axis& axisSpecified = requestedAxes[j];
|
||||
if (axisDefinition.fTag == axisSpecified.fTag) {
|
||||
SkFixed axisValue = SkScalarToFixed(axisSpecified.fStyleValue);
|
||||
axisValues[i] = SkTPin(axisValue, axisDefinition.fMinimum, axisDefinition.fMaximum);
|
||||
if (axisValues[i] != axisValue) {
|
||||
SkDEBUGF(("Requested font axis value out of range: "
|
||||
"%s '%c%c%c%c' %f; pinned to %f.\n",
|
||||
name.c_str(),
|
||||
(axisDefinition.fTag >> 24) & 0xFF,
|
||||
(axisDefinition.fTag >> 16) & 0xFF,
|
||||
(axisDefinition.fTag >> 8) & 0xFF,
|
||||
(axisDefinition.fTag ) & 0xFF,
|
||||
SkScalarToDouble(axisSpecified.fStyleValue),
|
||||
SkFixedToDouble(axisValues[i])));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: warn on defaulted axis?
|
||||
}
|
||||
|
||||
SkDEBUGCODE(
|
||||
// Check for axis specified, but not matched in font.
|
||||
for (int i = 0; i < requestedAxisCount; ++i) {
|
||||
SkFourByteTag skTag = requestedAxes[i].fTag;
|
||||
bool found = false;
|
||||
for (int j = 0; j < axisDefinitions.count(); ++j) {
|
||||
if (skTag == axisDefinitions[j].fTag) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
SkDEBUGF(("Requested font axis not found: %s '%c%c%c%c'\n",
|
||||
name.c_str(),
|
||||
(skTag >> 24) & 0xFF,
|
||||
(skTag >> 16) & 0xFF,
|
||||
(skTag >> 8) & 0xFF,
|
||||
(skTag) & 0xFF));
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "SkTypeface.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
#include "SkFontMgr.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
@ -55,6 +57,12 @@ public:
|
||||
bool scanFont(SkStream* stream, int ttcIndex,
|
||||
SkString* name, SkFontStyle* style, bool* isFixedPitch,
|
||||
AxisDefinitions* axes) const;
|
||||
static void computeAxisValues(
|
||||
AxisDefinitions axisDefinitions,
|
||||
const SkFontMgr::FontParameters::Axis* requestedAxis, int requestedAxisCount,
|
||||
SkFixed* axisValues,
|
||||
const SkString& name);
|
||||
|
||||
private:
|
||||
FT_Face openFace(SkStream* stream, int ttcIndex, FT_Stream ftStream) const;
|
||||
FT_Library fLibrary;
|
||||
|
@ -2371,6 +2371,145 @@ protected:
|
||||
return create_from_dataProvider(pr);
|
||||
}
|
||||
|
||||
static CFNumberRef get_tag_for_name(CFStringRef name, CFArrayRef ctAxes) {
|
||||
CFIndex ctAxisCount = CFArrayGetCount(ctAxes);
|
||||
for (int i = 0; i < ctAxisCount; ++i) {
|
||||
CFTypeRef ctAxisInfo = CFArrayGetValueAtIndex(ctAxes, i);
|
||||
if (CFDictionaryGetTypeID() != CFGetTypeID(ctAxisInfo)) {
|
||||
return nullptr;
|
||||
}
|
||||
CFDictionaryRef ctAxisInfoDict = static_cast<CFDictionaryRef>(ctAxisInfo);
|
||||
|
||||
CFTypeRef ctAxisName = CFDictionaryGetValue(ctAxisInfoDict,
|
||||
kCTFontVariationAxisNameKey);
|
||||
if (!ctAxisName || CFGetTypeID(ctAxisName) != CFStringGetTypeID()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (CFEqual(name, ctAxisName)) {
|
||||
CFTypeRef tag = CFDictionaryGetValue(ctAxisInfoDict,
|
||||
kCTFontVariationAxisIdentifierKey);
|
||||
if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<CFNumberRef>(tag);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static CFDictionaryRef get_axes(CGFontRef cg, const FontParameters& params) {
|
||||
AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
|
||||
if (!cgAxes) {
|
||||
return nullptr;
|
||||
}
|
||||
CFIndex axisCount = CFArrayGetCount(cgAxes);
|
||||
|
||||
// The CGFont variation data is keyed by name, and lacks the tag.
|
||||
// The CTFont variation data is keyed by tag, and also has the name.
|
||||
// We would like to work with CTFont variaitons, but creating a CTFont font with
|
||||
// CTFont variation dictionary runs into bugs. So use the CTFont variation data
|
||||
// to match names to tags to create the appropriate CGFont.
|
||||
AutoCFRelease<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
|
||||
AutoCFRelease<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
|
||||
if (!ctAxes || CFArrayGetCount(ctAxes) != axisCount) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int paramAxisCount;
|
||||
const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount);
|
||||
|
||||
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
for (int i = 0; i < axisCount; ++i) {
|
||||
CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i);
|
||||
if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
|
||||
return nullptr;
|
||||
}
|
||||
CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
|
||||
|
||||
CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
|
||||
if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CFNumberRef tagNumber = get_tag_for_name(static_cast<CFStringRef>(axisName), ctAxes);
|
||||
if (!tagNumber) {
|
||||
// Could not find a tag to go with the name of this index.
|
||||
// This would be a bug in CG/CT.
|
||||
continue;
|
||||
}
|
||||
int64_t tagLong;
|
||||
if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The variation axes can be set to any value, but cg will effectively pin them.
|
||||
// Pin them here to normalize.
|
||||
CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
|
||||
CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
|
||||
CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisDefaultValue);
|
||||
if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
|
||||
!max || CFGetTypeID(max) != CFNumberGetTypeID() ||
|
||||
!def || CFGetTypeID(def) != CFNumberGetTypeID())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
CFNumberRef minNumber = static_cast<CFNumberRef>(min);
|
||||
CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
|
||||
CFNumberRef defNumber = static_cast<CFNumberRef>(def);
|
||||
double minDouble;
|
||||
double maxDouble;
|
||||
double defDouble;
|
||||
if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
|
||||
!CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
|
||||
!CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
double value = defDouble;
|
||||
for (int j = 0; j < paramAxisCount; ++j) {
|
||||
if (paramAxes[j].fTag == tagLong) {
|
||||
value = SkTPin(SkScalarToDouble(paramAxes[j].fStyleValue),minDouble,maxDouble);
|
||||
break;
|
||||
}
|
||||
}
|
||||
CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType,
|
||||
&value);
|
||||
CFDictionaryAddValue(dict, axisName, valueNumber);
|
||||
CFRelease(valueNumber);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override {
|
||||
AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(s));
|
||||
if (nullptr == provider) {
|
||||
return nullptr;
|
||||
}
|
||||
AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
|
||||
if (nullptr == cg) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, params));
|
||||
// The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
|
||||
// created from a data provider does not appear to have any ownership of the underlying
|
||||
// data. The original CGFontRef must be kept alive until the copy will no longer be used.
|
||||
AutoCFRelease<CGFontRef> cgVariant;
|
||||
if (cgVariations) {
|
||||
cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations));
|
||||
} else {
|
||||
cgVariant.reset(cg.detach());
|
||||
}
|
||||
|
||||
CTFontRef ct = CTFontCreateWithGraphicsFont(cgVariant, 0, nullptr, nullptr);
|
||||
if (!ct) {
|
||||
return nullptr;
|
||||
}
|
||||
return NewFromFontRef(ct, cg.detach(), nullptr, true);
|
||||
}
|
||||
|
||||
static CFDictionaryRef get_axes(CGFontRef cg, SkFontData* fontData) {
|
||||
AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
|
||||
if (!cgAxes) {
|
||||
|
@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include "SkTypes.h"
|
||||
#if defined(SK_BUILD_FOR_ANDROID)
|
||||
|
||||
#include "SkFixed.h"
|
||||
#include "SkFontDescriptor.h"
|
||||
@ -208,26 +207,27 @@ public:
|
||||
}
|
||||
|
||||
SkDEBUGCODE(
|
||||
// Check for axis specified, but not matched in font.
|
||||
for (int i = 0; i < fontFile.fAxes.count(); ++i) {
|
||||
SkFourByteTag skTag = fontFile.fAxes[i].fTag;
|
||||
bool found = false;
|
||||
for (int j = 0; j < axisDefinitions.count(); ++j) {
|
||||
if (skTag == axisDefinitions[j].fTag) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
// Check for axis specified, but not matched in font.
|
||||
for (int i = 0; i < fontFile.fAxes.count(); ++i) {
|
||||
SkFourByteTag skTag = fontFile.fAxes[i].fTag;
|
||||
bool found = false;
|
||||
for (int j = 0; j < axisDefinitions.count(); ++j) {
|
||||
if (skTag == axisDefinitions[j].fTag) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
SkDEBUGF(("Requested font axis not found: %s '%c%c%c%c'\n",
|
||||
familyName.c_str(), (skTag >> 24) & 0xFF,
|
||||
(skTag >> 16) & 0xFF, (skTag >> 8) & 0xFF, (skTag)&0xFF));
|
||||
}
|
||||
})
|
||||
}
|
||||
if (!found) {
|
||||
SkDEBUGF(("Requested font axis not found: %s '%c%c%c%c'\n",
|
||||
familyName.c_str(), (skTag >> 24) & 0xFF,
|
||||
(skTag >> 16) & 0xFF, (skTag >> 8) & 0xFF, (skTag)&0xFF));
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fStyles.push_back().reset(new SkTypeface_AndroidSystem(
|
||||
pathName, ttcIndex, axisValues.get(), axisDefinitions.count(), style,
|
||||
isFixedWidth, familyName, lang, variant));
|
||||
fStyles.push_back().reset(new SkTypeface_AndroidSystem(
|
||||
pathName, ttcIndex, axisValues.get(), axisDefinitions.count(), style,
|
||||
isFixedWidth, familyName, lang, variant));
|
||||
}
|
||||
}
|
||||
|
||||
@ -476,6 +476,29 @@ protected:
|
||||
return new SkTypeface_AndroidStream(data, style, isFixedPitch, name);
|
||||
}
|
||||
|
||||
SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override {
|
||||
using Scanner = SkTypeface_FreeType::Scanner;
|
||||
SkAutoTDelete<SkStreamAsset> stream(s);
|
||||
bool isFixedPitch;
|
||||
SkFontStyle style;
|
||||
SkString name;
|
||||
Scanner::AxisDefinitions axisDefinitions;
|
||||
if (!fScanner.scanFont(stream, params.getCollectionIndex(), &name, &style, &isFixedPitch,
|
||||
&axisDefinitions))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int paramAxisCount;
|
||||
const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount);
|
||||
SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count());
|
||||
Scanner::computeAxisValues(axisDefinitions, paramAxes, paramAxisCount, axisValues, name);
|
||||
|
||||
SkFontData* data(new SkFontData(stream.detach(), params.getCollectionIndex(),
|
||||
axisValues.get(), axisDefinitions.count()));
|
||||
return new SkTypeface_AndroidStream(data, style, isFixedPitch, name);
|
||||
}
|
||||
|
||||
SkTypeface* onCreateFromFontData(SkFontData* data) const override {
|
||||
SkStreamAsset* stream(data->getStream());
|
||||
bool isFixedPitch;
|
||||
@ -589,5 +612,3 @@ SkFontMgr* SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom) {
|
||||
|
||||
return new SkFontMgr_Android(custom);
|
||||
}
|
||||
|
||||
#endif//defined(SK_BUILD_FOR_ANDROID)
|
||||
|
@ -834,6 +834,29 @@ protected:
|
||||
isFixedWidth);
|
||||
}
|
||||
|
||||
SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override {
|
||||
using Scanner = SkTypeface_FreeType::Scanner;
|
||||
SkAutoTDelete<SkStreamAsset> stream(s);
|
||||
bool isFixedPitch;
|
||||
SkFontStyle style;
|
||||
SkString name;
|
||||
Scanner::AxisDefinitions axisDefinitions;
|
||||
if (!fScanner.scanFont(stream, params.getCollectionIndex(), &name, &style, &isFixedPitch,
|
||||
&axisDefinitions))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int paramAxisCount;
|
||||
const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount);
|
||||
SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count());
|
||||
Scanner::computeAxisValues(axisDefinitions, paramAxes, paramAxisCount, axisValues, name);
|
||||
|
||||
SkFontData* data(new SkFontData(stream.detach(), params.getCollectionIndex(),
|
||||
axisValues.get(), axisDefinitions.count()));
|
||||
return new SkTypeface_stream(data, style, isFixedPitch);
|
||||
}
|
||||
|
||||
SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
|
||||
return this->createFromStream(new SkMemoryStream(data), ttcIndex);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user