Add a GDI-based font selector for libass

This deliberately doesn't perform any font substitutions as the sort of
people that use libass on Windows tend to perfer to manually pick
fonts with the correctly glyphs.
This commit is contained in:
Thomas Goyne 2015-10-30 20:57:08 -07:00
parent e06385b6d4
commit e5a6abd215
4 changed files with 133 additions and 17 deletions

View File

@ -3,3 +3,4 @@
#define CONFIG_ASM 1
#define CONFIG_RASTERIZER 1
#define CONFIG_DIRECTWRITE 1

View File

@ -96,6 +96,7 @@
<ClCompile Include="$(LibassSrcDir)\libass\ass_string.c" />
<ClCompile Include="$(LibassSrcDir)\libass\ass_strtod.c" />
<ClCompile Include="$(LibassSrcDir)\libass\ass_utils.c" />
<ClCompile Include="..\..\src\libass_gdi_fontselect.cpp" />
</ItemGroup>
<!-- Project References -->

View File

@ -19,9 +19,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="$(LibassSrcDir)\libass\x86\x86inc.h">
<Filter>Assembly Files</Filter>
</None>
<Yasm Include="$(LibassSrcDir)\libass\x86\be_blur.asm">
<Filter>Assembly Files</Filter>
</Yasm>
@ -49,6 +46,9 @@
<ClInclude Include="$(LibassSrcDir)\libass\x86\rasterizer.h">
<Filter>Assembly Files</Filter>
</ClInclude>
<None Include="$(LibassSrcDir)\libass\x86\x86inc.asm">
<Filter>Assembly Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(LibassSrcDir)\libass\ass.h">
@ -87,22 +87,13 @@
<ClInclude Include="$(LibassSrcDir)\libass\ass_utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(LibassSrcDir)\win32\config.h">
<ClInclude Include="$(MSBuildThisFileDirectory)config.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(LibassSrcDir)\win32\headers\enca.h">
<ClInclude Include="$(MSBuildThisFileDirectory)strings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(LibassSrcDir)\win32\headers\inttypes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(LibassSrcDir)\win32\headers\stdint.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(LibassSrcDir)\win32\headers\strings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(LibassSrcDir)\win32\headers\unistd.h">
<ClInclude Include="$(MSBuildThisFileDirectory)unistd.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -158,5 +149,8 @@
<ClCompile Include="$(LibassSrcDir)\libass\ass_utils.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\libass_gdi_fontselect.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,120 @@
// Copyright (c) 2016, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
extern "C" {
#include "ass_fontselect.h"
}
#undef inline
#include <memory>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
namespace {
class GdiFont {
HFONT font;
std::shared_ptr<HDC__> dc;
size_t size = 0;
std::unique_ptr<char[]> font_data;
public:
GdiFont(HFONT font, std::shared_ptr<HDC__> dc) : font(font), dc(dc) { }
~GdiFont() { DeleteObject(font); }
size_t GetData(unsigned char *data, size_t offset, size_t len);
bool CheckPostscript() { return false; }
bool CheckGlyph(uint32_t codepoint) { return true; }
void Destroy() { delete this; }
};
size_t GdiFont::GetData(unsigned char *data, size_t offset, size_t len) {
if (!font_data) {
SelectObject(dc.get(), font);
size = GetFontData(dc.get(), 0, 0, 0, 0);
if (size == GDI_ERROR)
return 0;
font_data.reset(new char[size]);
GetFontData(dc.get(), 0, 0, font_data.get(), size);
}
if (!data)
return size;
memcpy(data, font_data.get() + offset, len);
return len;
}
void match_fonts(ASS_Library *lib, ASS_FontProvider *provider, char *name) {
std::shared_ptr<HDC__> dc(CreateCompatibleDC(nullptr), [](HDC dc) { DeleteDC(dc); });
LOGFONTW lf{};
lf.lfCharSet = DEFAULT_CHARSET;
MultiByteToWideChar(CP_UTF8, 0, name, -1, lf.lfFaceName, LF_FACESIZE);
auto cb = [=](LOGFONTW const& lf) {
ASS_FontProviderMetaData meta{};
meta.weight = lf.lfWeight;
meta.slant = lf.lfItalic ? FONT_SLANT_ITALIC : FONT_SLANT_NONE;
meta.width = FONT_WIDTH_NORMAL;
meta.families= static_cast<char **>(malloc(sizeof(char *)));
meta.n_family = 1;
auto name = static_cast<char *>(malloc(LF_FACESIZE * 4));
auto len = wcsnlen(lf.lfFaceName, LF_FACESIZE);
auto written = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, len,
name, LF_FACESIZE * 4, nullptr, nullptr);
name[written] = 0;
meta.families[0] = name;
auto hfont = CreateFontIndirectW(&lf);
ass_font_provider_add_font(provider, &meta, nullptr, 0, new GdiFont(hfont, dc));
};
using type = decltype(cb);
EnumFontFamiliesEx(dc.get(), &lf, [](const LOGFONT *lf, const TEXTMETRIC *, DWORD, LPARAM lParam) -> int {
(*reinterpret_cast<type*>(lParam))(*lf);
return 1;
}, (LPARAM)&cb, 0);
}
template <typename T, T> struct wrapper;
template <typename T, typename R, typename... Args, R (T::*method)(Args...)>
struct wrapper<R (T::*)(Args...), method> {
static R call(void *obj, Args... args) {
return (static_cast<T*>(obj)->*method)(args...);
}
};
}
extern "C"
ASS_FontProvider *ass_directwrite_add_provider(ASS_Library *,
ASS_FontSelector *selector,
const char *) {
#define WRAP(method) &wrapper<decltype(&method), &method>::call
static ASS_FontProviderFuncs callbacks = {
WRAP(GdiFont::GetData),
WRAP(GdiFont::CheckPostscript),
WRAP(GdiFont::CheckGlyph),
WRAP(GdiFont::Destroy),
nullptr, // destroy_provider
&match_fonts,
nullptr, // get_substitution
nullptr, // get_fallback
};
return ass_font_provider_new(selector, &callbacks, nullptr);
}