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:
bungeman 2016-01-21 14:17:47 -08:00 committed by Commit bot
parent 27a6e86fb1
commit f6c7107d03
8 changed files with 334 additions and 25 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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());

View File

@ -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));
}
}
)
}

View File

@ -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;

View File

@ -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(&paramAxisCount);
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) {

View File

@ -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(&paramAxisCount);
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)

View File

@ -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(&paramAxisCount);
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);
}