Skia升级小记m104->m122
最近需要把Skia进行一次升级,首先查看下接口变动,See:
大部分都是接口的小调整,基本没什么难度,但注意m122的一个改动:
SkFontMgr::RefDefault()
has been deleted. Clients should instantiate and manage their ownSkFontMgrs
and use them to explicitly createSkTypefaces
找到这个改动的原因:
是因为一个隐晦的编译问题,从这个链接来看,适配这个修改还是比较麻烦的,总结来说就是:没有统一的平台无关的static接口了,你们自己去实例化(instantiate)吧。
可以看看其他类似框架的处理
Flutter:Replace calls to SkFontMgr::RefDefault by kjlubick · Pull Request #48179 · flutter/engine,感觉之前就已经有所准备了,改成了另一个调用
Chrome:
// skia/ext/font_utils.cc
static sk_sp<SkFontMgr> fontmgr_factory() {
if (g_fontmgr_override) {
return sk_ref_sp(g_fontmgr_override);
}
#if BUILDFLAG(IS_ANDROID)
return SkFontMgr_New_Android(nullptr);
#elif BUILDFLAG(IS_APPLE)
return SkFontMgr_New_CoreText(nullptr);
#elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
sk_sp<SkFontConfigInterface> fci(SkFontConfigInterface::RefGlobal());
return fci ? SkFontMgr_New_FCI(std::move(fci)) : nullptr;
#elif BUILDFLAG(IS_FUCHSIA)
fuchsia::fonts::ProviderSyncPtr provider;
base::ComponentContextForProcess()->svc()->Connect(provider.NewRequest());
return SkFontMgr_New_Fuchsia(std::move(provider));
#elif BUILDFLAG(IS_WIN)
return SkFontMgr_New_DirectWrite();
#elif defined(SK_FONTMGR_FREETYPE_EMPTY_AVAILABLE)
return SkFontMgr_New_Custom_Empty();
#else
return SkFontMgr::RefEmpty();
#endif
这个比较有启发性!其实可以看下Skia的m122的代码是如何适配的。其实可以找到类似的修改:
// skia/tools/fonts/FontToolUtils.cpp
sk_sp<SkFontMgr> TestFontMgr() {
// ...... skip
#if defined(SK_BUILD_FOR_WIN) && defined(SK_FONTMGR_GDI_AVAILABLE)
else if (FLAGS_gdi) {
mgr = SkFontMgr_New_GDI();
}
#endif
else {
#if defined(SK_BUILD_FOR_ANDROID) && defined(SK_FONTMGR_ANDROID_AVAILABLE)
mgr = SkFontMgr_New_Android(nullptr);
#elif defined(SK_BUILD_FOR_WIN) && defined(SK_FONTMGR_DIRECTWRITE_AVAILABLE)
mgr = SkFontMgr_New_DirectWrite();
#elif defined(SK_FONTMGR_CORETEXT_AVAILABLE) && (defined(SK_BUILD_FOR_IOS) || \
defined(SK_BUILD_FOR_MAC))
mgr = SkFontMgr_New_CoreText(nullptr);
#elif defined(SK_FONTMGR_FONTCONFIG_AVAILABLE)
mgr = SkFontMgr_New_FontConfig(nullptr);
#elif defined(SK_FONTMGR_FREETYPE_DIRECTORY_AVAILABLE)
// In particular, this is used on ChromeOS, which is Linux-like but doesn't have
// FontConfig.
mgr = SkFontMgr_New_Custom_Directory(SK_FONT_FILE_PREFIX);
#elif defined(SK_FONTMGR_FREETYPE_EMPTY_AVAILABLE)
mgr = SkFontMgr_New_Custom_Empty();
#else
mgr = SkFontMgr::RefEmpty();
#endif
}
SkASSERT_RELEASE(mgr);
});
return mgr;
}
和Chrome的还是比较类似的,另外还可以看下m104的时候,SkFontMgr::RefDefault
是如何实现的?其实是调用了SkFontMgr::Factory()
,这个方法是分平台实现的:
$ grep -rI "SkFontMgr::Factory()" # grep in m104
./gn/skia.gni: # skia_fontmgr_factory should define SkFontMgr::Factory()
./src/core/SkFontMgr.cpp: : SkFontMgr::Factory();
./src/ports/SkFontMgr_android_factory.cpp:sk_sp<SkFontMgr> SkFontMgr::Factory() {
./src/ports/SkFontMgr_FontConfigInterface_factory.cpp:sk_sp<SkFontMgr> SkFontMgr::Factory() {
./src/ports/SkFontMgr_custom_empty_factory.cpp:sk_sp<SkFontMgr> SkFontMgr::Factory() {
./src/ports/SkFontMgr_mac_ct_factory.cpp:sk_sp<SkFontMgr> SkFontMgr::Factory() {
./src/ports/SkFontMgr_custom_directory_factory.cpp:sk_sp<SkFontMgr> SkFontMgr::Factory() {
./src/ports/SkFontMgr_empty_factory.cpp:sk_sp<SkFontMgr> SkFontMgr::Factory() {
./src/ports/SkFontMgr_fontconfig_factory.cpp:sk_sp<SkFontMgr> SkFontMgr::Factory() {
./src/ports/SkFontMgr_custom_embedded_factory.cpp:sk_sp<SkFontMgr> SkFontMgr::Factory() {
./src/ports/SkFontMgr_win_dw_factory.cpp:sk_sp<SkFontMgr> SkFontMgr::Factory() {
以Android为例,是通过skia_enable_fontmgr_android
控制的。那么,修改的思路就比较明确了,其实就是自己去分平台调用接口。
需要注意的是,include是不区分平台的,所以如果某个接口你调用了但Skia构建时没有开启,就可能出现Symbol Undefined错误。
另外遇到一个比较离谱的问题,就是Web平台适配时,发现SkFontMgr_New_Custom_Embedded
接口没有对外暴露,之前还通过Factory接口暴露了:
$ grep -rI "SkFontMgr_New_Custom_Embedded"
./skia-m122/src/ports/SkFontMgr_custom_embedded.cpp:sk_sp<SkFontMgr> SkFontMgr_New_Custom_Embedded(const SkEmbeddedResourceHeader* header) {
./skia-m104/src/ports/SkFontMgr_custom_embedded.cpp:sk_sp<SkFontMgr> SkFontMgr_New_Custom_Embedded(const SkEmbeddedResourceHeader* header) {
./skia-m104/src/ports/SkFontMgr_custom_embedded_factory.cpp:sk_sp<SkFontMgr> SkFontMgr_New_Custom_Embedded(const SkEmbeddedResourceHeader* header);
./skia-m104/src/ports/SkFontMgr_custom_embedded_factory.cpp: return SkFontMgr_New_Custom_Embedded(&SK_EMBEDDED_FONTS);
不过这个接口的参数本身不复杂,可以自己实现一个方法声明如下:
struct SkEmbeddedResource { const uint8_t* data; size_t size; };
struct SkEmbeddedResourceHeader { const SkEmbeddedResource* entries; int count; };
extern "C" const SkEmbeddedResourceHeader SK_EMBEDDED_FONTS;
sk_sp<SkFontMgr> SkFontMgr_New_Custom_Embedded(const SkEmbeddedResourceHeader* header);
再去调用,就能编译并链接过了。实际执行的是SkFontMgr_custom_embedded.cpp内部的实现,这样SkEmbeddedResourceHeader
实际就被定义了三次,第3处就是生成的字体文件本身:
$ tail NotoSansSC-Regular.otf.cc
0x54,0xa1,0x61,0x19,0xbf,0xb,0xa7,0x8c,0xa9,0x1b,0xf7,0xc0,0x6,0x87,0xb,0xec,0x95,0xf7,0x6,0x96,0xef,0x93,0x8a,0xb,0xf7,0x7,0x9f,0x8a,0xd8,0x18,0xfb,0xc,
0xb,0x0,0x0,0x0,};
static const size_t resource0_size = 8482020;
struct SkEmbeddedResource { const uint8_t* d; const size_t s; };
static const SkEmbeddedResource header[] = {
{ resource0, resource0_size },
};
static const int header_count = 1;
struct SkEmbeddedHeader {const SkEmbeddedResource* e; const int c;};
extern "C" const SkEmbeddedHeader SK_EMBEDDED_FONTS = { header, header_count };
还是要提个Isssue,让Skia赶紧把这个问题修复了。