Ensure variable font from data created at default.

If font data is used to create an SkTypeface and the font data describes
a variable font and no variation parameters are specified, the
SkTypeface created should have the default values for all axes. This is
particularaly interesting with DirectWrite since it makes this not
straight forward.

Bug: skia:10929
Cq-Include-Trybots: luci.skia.skia.primary:Test-Win10-Clang-NUC5i7RYH-CPU-AVX2-x86_64-Debug-All-NativeFonts
Change-Id: I4620deebf52142bbdffa1a283343b503cd1e6981
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/338604
Commit-Queue: Ben Wagner <bungeman@google.com>
Reviewed-by: Herb Derby <herb@google.com>
This commit is contained in:
Ben Wagner 2020-11-30 15:53:13 -05:00 committed by Skia Commit-Bot
parent 4c6f57a23e
commit 416fe32ce1
4 changed files with 118 additions and 82 deletions

Binary file not shown.

Binary file not shown.

View File

@ -920,51 +920,9 @@ private:
sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
int ttcIndex) const {
SkTScopedComPtr<StreamFontFileLoader> fontFileLoader;
HRN(StreamFontFileLoader::Create(std::move(stream), &fontFileLoader));
HRN(fFactory->RegisterFontFileLoader(fontFileLoader.get()));
SkAutoIDWriteUnregister<StreamFontFileLoader> autoUnregisterFontFileLoader(
fFactory.get(), fontFileLoader.get());
SkTScopedComPtr<StreamFontCollectionLoader> fontCollectionLoader;
HRN(StreamFontCollectionLoader::Create(fontFileLoader.get(), &fontCollectionLoader));
HRN(fFactory->RegisterFontCollectionLoader(fontCollectionLoader.get()));
SkAutoIDWriteUnregister<StreamFontCollectionLoader> autoUnregisterFontCollectionLoader(
fFactory.get(), fontCollectionLoader.get());
SkTScopedComPtr<IDWriteFontCollection> fontCollection;
HRN(fFactory->CreateCustomFontCollection(fontCollectionLoader.get(), nullptr, 0, &fontCollection));
// Find the first non-simulated font which has the given ttc index.
UINT32 familyCount = fontCollection->GetFontFamilyCount();
for (UINT32 familyIndex = 0; familyIndex < familyCount; ++familyIndex) {
SkTScopedComPtr<IDWriteFontFamily> fontFamily;
HRN(fontCollection->GetFontFamily(familyIndex, &fontFamily));
UINT32 fontCount = fontFamily->GetFontCount();
for (UINT32 fontIndex = 0; fontIndex < fontCount; ++fontIndex) {
SkTScopedComPtr<IDWriteFont> font;
HRN(fontFamily->GetFont(fontIndex, &font));
if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
continue;
}
SkTScopedComPtr<IDWriteFontFace> fontFace;
HRN(font->CreateFontFace(&fontFace));
int faceIndex = fontFace->GetIndex();
if (faceIndex == ttcIndex) {
return DWriteFontTypeface::Make(fFactory.get(),
fontFace.get(), font.get(), fontFamily.get(),
sk_make_sp<DWriteFontTypeface::Loaders>(
fFactory.get(),
autoUnregisterFontFileLoader.detatch(),
autoUnregisterFontCollectionLoader.detatch()));
}
}
}
return nullptr;
SkFontArguments args;
args.setCollectionIndex(ttcIndex);
return this->onMakeFromStreamArgs(std::move(stream), args);
}
static HRESULT apply_fontargument_variation(SkTScopedComPtr<IDWriteFontFace>& fontFace,

View File

@ -172,52 +172,130 @@ DEF_TEST(FontDescriptorDeserializeOldFormat, reporter) {
};
DEF_TEST(TypefaceAxes, reporter) {
std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
if (!distortable) {
REPORT_FAILURE(reporter, "distortable", SkString());
return;
}
constexpr int numberOfAxesInDistortable = 1;
using Variation = SkFontArguments::VariationPosition;
// In DWrite in at least up to 1901 18363.1198 IDWriteFontFace5::GetFontAxisValues and
// GetFontAxisValueCount along with IDWriteFontResource::GetFontAxisAttributes and
// GetFontAxisCount (and related) seem to incorrectly collapse multiple axes with the same tag.
// Since this is a limitation of the underlying implementation, for now allow the test to pass
// with the axis tag count (as opposed to the axis count). Eventually all implementations should
// pass this test without 'alsoAcceptedAxisTagCount'.
auto test = [&](SkTypeface* typeface, const Variation& expected, int alsoAcceptedAxisTagCount) {
if (!typeface) {
return; // Not all SkFontMgr can makeFromStream().
}
int actualCount = typeface->getVariationDesignPosition(nullptr, 0);
if (actualCount == -1) {
return; // The number of axes is unknown.
}
REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
actualCount == alsoAcceptedAxisTagCount);
// Variable font conservative bounds don't vary, so ensure they aren't reported.
REPORTER_ASSERT(reporter, typeface->getBounds().isEmpty());
std::unique_ptr<Variation::Coordinate[]> actual(new Variation::Coordinate[actualCount]);
actualCount = typeface->getVariationDesignPosition(actual.get(), actualCount);
if (actualCount == -1) {
return; // The position cannot be determined.
}
REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
actualCount == alsoAcceptedAxisTagCount);
// Every actual must be expected.
std::unique_ptr<bool[]> expectedUsed(new bool[expected.coordinateCount]());
for (int actualIdx = 0; actualIdx < actualCount; ++actualIdx) {
bool actualFound = false;
for (int expectedIdx = 0; expectedIdx < expected.coordinateCount; ++expectedIdx) {
if (expectedUsed[expectedIdx]) {
continue;
}
if (actual[actualIdx].axis != expected.coordinates[expectedIdx].axis) {
continue;
}
// Convert to fixed for "almost equal".
SkFixed fixedRead = SkScalarToFixed(actual[actualIdx].value);
SkFixed fixedOriginal = SkScalarToFixed(expected.coordinates[expectedIdx].value);
if (!(SkTAbs(fixedRead - fixedOriginal) < 2 || // variation set correctly
SkTAbs(fixedRead - SK_Fixed1 ) < 2) ) // variation remained default
{
continue;
}
// This actual matched an unused expected.
actualFound = true;
expectedUsed[expectedIdx] = true;
break;
}
REPORTER_ASSERT(reporter, actualFound,
"Actual axis '%c%c%c%c' with value '%f' not expected",
(actual[actualIdx].axis >> 24) & 0xFF,
(actual[actualIdx].axis >> 16) & 0xFF,
(actual[actualIdx].axis >> 8) & 0xFF,
(actual[actualIdx].axis ) & 0xFF,
SkScalarToDouble(actual[actualIdx].value));
}
};
sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
// The position may be over specified. If there are multiple values for a given axis,
// ensure the last one since that's what css-fonts-4 requires.
const SkFontArguments::VariationPosition::Coordinate position[] = {
{ SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
{ SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
};
SkFontArguments params;
params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
// TODO: if axes are set and the back-end doesn't support them, should we create the typeface?
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
if (!typeface) {
return; // Not all SkFontMgr can makeFromStream().
// Not specifying a position should produce the default.
{
std::unique_ptr<SkStreamAsset> variable(GetResourceAsStream("fonts/Variable.ttf"));
if (!variable) {
REPORT_FAILURE(reporter, "variable", SkString());
return;
}
const Variation::Coordinate defaultPosition[] = {
{ SkSetFourByteTag('w','g','h','t'), 400.0f },
{ SkSetFourByteTag('w','d','t','h'), 100.0f },
};
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(variable), 0);
test(typeface.get(), Variation{&defaultPosition[0], 2}, -1);
}
int count = typeface->getVariationDesignPosition(nullptr, 0);
if (count == -1) {
return; // The number of axes is unknown.
// Multiple axes with the same tag (and min, max, default) works.
{
std::unique_ptr<SkStreamAsset> dupTags(GetResourceAsStream("fonts/VaryAlongQuads.ttf"));
if (!dupTags) {
REPORT_FAILURE(reporter, "dupTags", SkString());
return;
}
// The position may be over specified. If there are multiple values for a given axis,
// ensure the last one since that's what css-fonts-4 requires.
const Variation::Coordinate position[] = {
{ SkSetFourByteTag('w','g','h','t'), 700.0f },
{ SkSetFourByteTag('w','g','h','t'), 600.0f },
{ SkSetFourByteTag('w','g','h','t'), 600.0f },
};
SkFontArguments params;
params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(dupTags), params);
test(typeface.get(), Variation{&position[1], 2}, 1);
}
REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
// Variable font conservative bounds don't vary, so ensure they aren't reported.
REPORTER_ASSERT(reporter, typeface->getBounds().isEmpty());
// Overspecifying an axis tag value applies the last one in the list.
{
std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
if (!distortable) {
REPORT_FAILURE(reporter, "distortable", SkString());
return;
}
SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
if (count == -1) {
return; // The position cannot be determined.
// The position may be over specified. If there are multiple values for a given axis,
// ensure the last one since that's what css-fonts-4 requires.
const Variation::Coordinate position[] = {
{ SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
{ SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
};
SkFontArguments params;
params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
test(typeface.get(), Variation{&position[1], 1}, -1);
}
REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
// Convert to fixed for "almost equal".
SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
REPORTER_ASSERT(reporter, SkTAbs(fixedRead - fixedOriginal) < 2 || // variation set correctly
SkTAbs(fixedRead - SK_Fixed1 ) < 2); // variation remained default
}
DEF_TEST(TypefaceVariationIndex, reporter) {