Directly measure CoreText weight mapping.
CoreText reports weight on a [-1.0, 1.0] scale with 0 being 'regular'. Unfortunately the mapping from the [1, 1000] scale more commonly used is not uniform and also varies between system fonts and fonts created from data. For system fonts the NSFontWeight values will be used if available, but there is no corresponding API for fonts created from data. Previously the values for fonts created from data were hardcoded, but the values have changed again recently. Directly read these values by varying the weight in the font data, creating a CTFontDescriptor from this data, and then using the value reported by CoreText. Bug: skia:11176 Change-Id: I071e2ff7ac3f676c8395b13aa82dde7a97fdf2ce Reviewed-on: https://skia-review.googlesource.com/c/skia/+/358535 Commit-Queue: Ben Wagner <bungeman@google.com> Reviewed-by: Herb Derby <herb@google.com>
This commit is contained in:
parent
131decbf41
commit
e24127ce05
@ -82,8 +82,8 @@ skia_utils_sources = [
|
||||
#mac
|
||||
"$_src/utils/mac/SkCGBase.h",
|
||||
"$_src/utils/mac/SkCGGeometry.h",
|
||||
"$_src/utils/mac/SkCTFontSmoothBehavior.cpp",
|
||||
"$_src/utils/mac/SkCTFontSmoothBehavior.h",
|
||||
"$_src/utils/mac/SkCTFont.cpp",
|
||||
"$_src/utils/mac/SkCTFont.h",
|
||||
"$_src/utils/mac/SkCreateCGImageRef.cpp",
|
||||
"$_src/utils/mac/SkUniqueCFRef.h",
|
||||
|
||||
|
@ -46,7 +46,7 @@
|
||||
#include "src/sfnt/SkOTTable_OS_2.h"
|
||||
#include "src/utils/mac/SkCGBase.h"
|
||||
#include "src/utils/mac/SkCGGeometry.h"
|
||||
#include "src/utils/mac/SkCTFontSmoothBehavior.h"
|
||||
#include "src/utils/mac/SkCTFont.h"
|
||||
#include "src/utils/mac/SkUniqueCFRef.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -57,7 +57,7 @@
|
||||
#include "src/utils/SkUTF.h"
|
||||
#include "src/utils/mac/SkCGBase.h"
|
||||
#include "src/utils/mac/SkCGGeometry.h"
|
||||
#include "src/utils/mac/SkCTFontSmoothBehavior.h"
|
||||
#include "src/utils/mac/SkCTFont.h"
|
||||
#include "src/utils/mac/SkUniqueCFRef.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
@ -321,56 +321,6 @@ struct CGFloatIdentity {
|
||||
CGFloat operator()(CGFloat s) { return s; }
|
||||
};
|
||||
|
||||
/** Returns the [-1, 1] CTFontDescriptor weights for the
|
||||
* <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
|
||||
*
|
||||
* It is assumed that the values will be interpolated linearly between these points.
|
||||
* NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
|
||||
* The actual values appear to be stable, but they may change in the future without notice.
|
||||
*/
|
||||
static CGFloat(&get_NSFontWeight_mapping())[11] {
|
||||
|
||||
// Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
|
||||
#ifdef SK_BUILD_FOR_MAC
|
||||
# define SK_KIT_FONT_WEIGHT_PREFIX "NS"
|
||||
#endif
|
||||
#ifdef SK_BUILD_FOR_IOS
|
||||
# define SK_KIT_FONT_WEIGHT_PREFIX "UI"
|
||||
#endif
|
||||
static constexpr struct {
|
||||
CGFloat defaultValue;
|
||||
const char* name;
|
||||
} nsFontWeightLoaderInfos[] = {
|
||||
{ -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
|
||||
{ -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
|
||||
{ -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
|
||||
{ 0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
|
||||
{ 0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
|
||||
{ 0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
|
||||
{ 0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
|
||||
{ 0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
|
||||
{ 0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
|
||||
};
|
||||
|
||||
static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
|
||||
static CGFloat nsFontWeights[11];
|
||||
static SkOnce once;
|
||||
once([&] {
|
||||
size_t i = 0;
|
||||
nsFontWeights[i++] = -1.00;
|
||||
for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
|
||||
void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
|
||||
if (nsFontWeightValuePtr) {
|
||||
nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
|
||||
} else {
|
||||
nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
|
||||
}
|
||||
}
|
||||
nsFontWeights[i++] = 1.00;
|
||||
});
|
||||
return nsFontWeights;
|
||||
}
|
||||
|
||||
/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
|
||||
*
|
||||
* The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
|
||||
@ -385,7 +335,7 @@ CGFloat SkCTFontCTWeightForCSSWeight(int fontstyleWeight) {
|
||||
static Interpolator::Mapping nativeWeightMappings[11];
|
||||
static SkOnce once;
|
||||
once([&] {
|
||||
CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
|
||||
const CGFloat(&nsFontWeights)[11] = SkCTFontGetNSFontWeightMapping();
|
||||
for (int i = 0; i < 11; ++i) {
|
||||
nativeWeightMappings[i].src_val = i * 100;
|
||||
nativeWeightMappings[i].dst_val = nsFontWeights[i];
|
||||
@ -397,7 +347,6 @@ CGFloat SkCTFontCTWeightForCSSWeight(int fontstyleWeight) {
|
||||
return nativeInterpolator.map(fontstyleWeight);
|
||||
}
|
||||
|
||||
|
||||
/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
|
||||
*
|
||||
* The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
|
||||
@ -409,37 +358,24 @@ static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
|
||||
// Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
|
||||
// However, on this end we can't tell, so this is ignored.
|
||||
|
||||
/** This mapping for CGDataProvider created fonts is determined by creating font data with every
|
||||
* weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
|
||||
* in tests/TypefaceTest.cpp for the code used to determine these values.
|
||||
*/
|
||||
static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
|
||||
{ -1.00, 0 },
|
||||
{ -0.70, 100 },
|
||||
{ -0.50, 200 },
|
||||
{ -0.23, 300 },
|
||||
{ 0.00, 400 },
|
||||
{ 0.20, 500 },
|
||||
{ 0.30, 600 },
|
||||
{ 0.40, 700 },
|
||||
{ 0.60, 800 },
|
||||
{ 0.80, 900 },
|
||||
{ 1.00, 1000 },
|
||||
};
|
||||
static constexpr Interpolator dataProviderInterpolator(
|
||||
dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
|
||||
|
||||
static Interpolator::Mapping nativeWeightMappings[11];
|
||||
static Interpolator::Mapping dataProviderWeightMappings[11];
|
||||
static SkOnce once;
|
||||
once([&] {
|
||||
CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
|
||||
const CGFloat(&nsFontWeights)[11] = SkCTFontGetNSFontWeightMapping();
|
||||
const CGFloat(&userFontWeights)[11] = SkCTFontGetDataFontWeightMapping();
|
||||
for (int i = 0; i < 11; ++i) {
|
||||
nativeWeightMappings[i].src_val = nsFontWeights[i];
|
||||
nativeWeightMappings[i].dst_val = i * 100;
|
||||
|
||||
dataProviderWeightMappings[i].src_val = userFontWeights[i];
|
||||
dataProviderWeightMappings[i].dst_val = i * 100;
|
||||
}
|
||||
});
|
||||
static constexpr Interpolator nativeInterpolator(
|
||||
nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
|
||||
static constexpr Interpolator dataProviderInterpolator(
|
||||
dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
|
||||
|
||||
return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
|
||||
: nativeInterpolator.map(cgWeight);
|
||||
|
@ -9,7 +9,12 @@
|
||||
|
||||
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
|
||||
|
||||
#include "src/utils/mac/SkCTFontSmoothBehavior.h"
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/private/SkOnce.h"
|
||||
#include "src/sfnt/SkOTTable_OS_2.h"
|
||||
#include "src/sfnt/SkSFNTHeader.h"
|
||||
#include "src/utils/mac/SkCTFont.h"
|
||||
#include "src/utils/mac/SkUniqueCFRef.h"
|
||||
|
||||
#ifdef SK_BUILD_FOR_MAC
|
||||
@ -23,6 +28,8 @@
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
static constexpr CGBitmapInfo kBitmapInfoRGB = ((CGBitmapInfo)kCGImageAlphaNoneSkipFirst |
|
||||
kCGBitmapByteOrder32Host);
|
||||
|
||||
@ -268,4 +275,139 @@ SkCTFontSmoothBehavior SkCTFontGetSmoothBehavior() {
|
||||
return gSmoothBehavior;
|
||||
}
|
||||
|
||||
SkCTFontWeightMapping& SkCTFontGetNSFontWeightMapping() {
|
||||
// In the event something goes wrong finding the real values, use this mapping.
|
||||
static constexpr CGFloat defaultNSFontWeights[] =
|
||||
{ -1.00, -0.80, -0.60, -0.40, 0.00, 0.23, 0.30, 0.40, 0.56, 0.62, 1.00 };
|
||||
|
||||
// Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
|
||||
#ifdef SK_BUILD_FOR_MAC
|
||||
# define SK_KIT_FONT_WEIGHT_PREFIX "NS"
|
||||
#endif
|
||||
#ifdef SK_BUILD_FOR_IOS
|
||||
# define SK_KIT_FONT_WEIGHT_PREFIX "UI"
|
||||
#endif
|
||||
static constexpr const char* nsFontWeightNames[] = {
|
||||
SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight",
|
||||
SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin",
|
||||
SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight",
|
||||
SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular",
|
||||
SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium",
|
||||
SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold",
|
||||
SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold",
|
||||
SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy",
|
||||
SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack",
|
||||
};
|
||||
static_assert(SK_ARRAY_COUNT(nsFontWeightNames) == 9, "");
|
||||
|
||||
static CGFloat nsFontWeights[11];
|
||||
static const CGFloat (*selectedNSFontWeights)[11] = &defaultNSFontWeights;
|
||||
static SkOnce once;
|
||||
once([&] {
|
||||
size_t i = 0;
|
||||
nsFontWeights[i++] = -1.00;
|
||||
for (const char* nsFontWeightName : nsFontWeightNames) {
|
||||
void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightName);
|
||||
if (nsFontWeightValuePtr) {
|
||||
nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
nsFontWeights[i++] = 1.00;
|
||||
selectedNSFontWeights = &nsFontWeights;
|
||||
});
|
||||
return *selectedNSFontWeights;
|
||||
}
|
||||
|
||||
SkCTFontWeightMapping& SkCTFontGetDataFontWeightMapping() {
|
||||
// In the event something goes wrong finding the real values, use this mapping.
|
||||
// These were the values from macOS 10.13 to 10.15.
|
||||
static constexpr CGFloat defaultDataFontWeights[] =
|
||||
{ -1.00, -0.70, -0.50, -0.23, 0.00, 0.20, 0.30, 0.40, 0.60, 0.80, 1.00 };
|
||||
|
||||
static const CGFloat (*selectedDataFontWeights)[11] = &defaultDataFontWeights;
|
||||
static CGFloat dataFontWeights[11];
|
||||
static SkOnce once;
|
||||
once([&] {
|
||||
constexpr size_t dataSize = SK_ARRAY_COUNT(kSpiderSymbol_ttf);
|
||||
sk_sp<SkData> data = SkData::MakeWithCopy(kSpiderSymbol_ttf, dataSize);
|
||||
const SkSFNTHeader* sfntHeader = reinterpret_cast<const SkSFNTHeader*>(data->data());
|
||||
const SkSFNTHeader::TableDirectoryEntry* tableEntry =
|
||||
SkTAfter<const SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
|
||||
const SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
|
||||
int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
|
||||
for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
|
||||
if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
|
||||
os2TableEntry = tableEntry + tableEntryIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!os2TableEntry) {
|
||||
return;
|
||||
}
|
||||
size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
|
||||
SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(data->writable_data(),
|
||||
os2TableOffset);
|
||||
|
||||
for (int i = 0; i < 11; ++i) {
|
||||
os2Table->usWeightClass.value = SkEndian_SwapBE16(i * 100);
|
||||
|
||||
// On macOS 10.14 and earlier it appears that the CFDataGetBytePtr is used somehow in
|
||||
// font caching. Creating a slightly modified font with data at the same address seems
|
||||
// to in some ways act like a font previously created at that address. As a result,
|
||||
// always make a copy of the data.
|
||||
SkUniqueCFRef<CFDataRef> cfData(
|
||||
CFDataCreate(kCFAllocatorDefault, (const UInt8 *)data->data(), data->size()));
|
||||
if (!cfData) {
|
||||
return;
|
||||
}
|
||||
SkUniqueCFRef<CTFontDescriptorRef> desc(
|
||||
CTFontManagerCreateFontDescriptorFromData(cfData.get()));
|
||||
if (!desc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// On macOS 10.14 and earlier, the CTFontDescriptorRef returned from
|
||||
// CTFontManagerCreateFontDescriptorFromData is incomplete and does not have the
|
||||
// correct traits. It is necessary to create the CTFont and then get the descriptor
|
||||
// off of it.
|
||||
SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc.get(), 9, nullptr));
|
||||
if (!ctFont) {
|
||||
return;
|
||||
}
|
||||
SkUniqueCFRef<CTFontDescriptorRef> desc2(CTFontCopyFontDescriptor(ctFont.get()));
|
||||
if (!desc2) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkUniqueCFRef<CFTypeRef> traitsRef(
|
||||
CTFontDescriptorCopyAttribute(desc2.get(), kCTFontTraitsAttribute));
|
||||
if (!traitsRef || CFGetTypeID(traitsRef.get()) != CFDictionaryGetTypeID()) {
|
||||
return;
|
||||
}
|
||||
CFDictionaryRef fontTraitsDict = static_cast<CFDictionaryRef>(traitsRef.get());
|
||||
|
||||
CFTypeRef weightRef;
|
||||
if (!CFDictionaryGetValueIfPresent(fontTraitsDict, kCTFontWeightTrait, &weightRef) ||
|
||||
!weightRef ||
|
||||
CFGetTypeID(weightRef) != CFNumberGetTypeID())
|
||||
{
|
||||
return;
|
||||
}
|
||||
CGFloat weight;
|
||||
CFNumberRef weightNumber = static_cast<CFNumberRef>(weightRef);
|
||||
if (!CFNumberIsFloatType(weightNumber) ||
|
||||
!CFNumberGetValue(weightNumber, kCFNumberCGFloatType, &weight))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dataFontWeights[i] = weight;
|
||||
}
|
||||
selectedDataFontWeights = &dataFontWeights;
|
||||
});
|
||||
return *selectedDataFontWeights;
|
||||
}
|
||||
|
||||
#endif
|
52
src/utils/mac/SkCTFont.h
Normal file
52
src/utils/mac/SkCTFont.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkCTFont_DEFINED
|
||||
#define SkCTFont_DEFINED
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
|
||||
|
||||
#ifdef SK_BUILD_FOR_MAC
|
||||
#import <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
#ifdef SK_BUILD_FOR_IOS
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#endif
|
||||
|
||||
enum class SkCTFontSmoothBehavior {
|
||||
none, // SmoothFonts produces no effect.
|
||||
some, // SmoothFonts produces some effect, but not subpixel coverage.
|
||||
subpixel, // SmoothFonts produces some effect and provides subpixel coverage.
|
||||
};
|
||||
|
||||
SkCTFontSmoothBehavior SkCTFontGetSmoothBehavior();
|
||||
|
||||
using SkCTFontWeightMapping = const CGFloat[11];
|
||||
|
||||
/** Returns the [-1, 1] CTFontDescriptor weights for the
|
||||
* <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
|
||||
*
|
||||
* It is assumed that the values will be interpolated linearly between these points.
|
||||
* NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
|
||||
* The actual values appear to be stable, but they may change without notice.
|
||||
* These values are valid for system fonts only.
|
||||
*/
|
||||
SkCTFontWeightMapping& SkCTFontGetNSFontWeightMapping();
|
||||
|
||||
/** Returns the [-1, 1] CTFontDescriptor weights for the
|
||||
* <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
|
||||
*
|
||||
* It is assumed that the values will be interpolated linearly between these points.
|
||||
* The actual values appear to be stable, but they may change without notice.
|
||||
* These values are valid for fonts created from data only.
|
||||
*/
|
||||
SkCTFontWeightMapping& SkCTFontGetDataFontWeightMapping();
|
||||
|
||||
#endif
|
||||
#endif // SkCTFontSmoothBehavior_DEFINED
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkCTFontSmoothBehavior_DEFINED
|
||||
#define SkCTFontSmoothBehavior_DEFINED
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
|
||||
|
||||
enum class SkCTFontSmoothBehavior {
|
||||
none, // SmoothFonts produces no effect.
|
||||
some, // SmoothFonts produces some effect, but not subpixel coverage.
|
||||
subpixel, // SmoothFonts produces some effect and provides subpixel coverage.
|
||||
};
|
||||
|
||||
SkCTFontSmoothBehavior SkCTFontGetSmoothBehavior();
|
||||
|
||||
#endif
|
||||
#endif // SkCTFontSmoothBehavior_DEFINED
|
Loading…
Reference in New Issue
Block a user