ttf layer refactor
This commit is contained in:
parent
adcff577ce
commit
b60b799199
@ -239,7 +239,7 @@ void P_AppStartup(String args_str)
|
||||
|
||||
/* Subsystems */
|
||||
AC_StartupReceipt asset_cache_sr = AC_Startup();
|
||||
TTF_StartupReceipt ttf_sr = ttf_startup();
|
||||
TTF_StartupReceipt ttf_sr = TTF_Startup();
|
||||
F_StartupReceipt font_sr = F_Startup(&asset_cache_sr, &ttf_sr);
|
||||
S_StartupReceipt sprite_sr = sprite_startup();
|
||||
M_StartupReceipt mixer_sr = M_Startup();
|
||||
|
||||
@ -89,7 +89,7 @@ P_JobDef(F_LoadAssetJob, job)
|
||||
Lit("Font \"%F\" not found"),
|
||||
FmtString(path)));
|
||||
}
|
||||
TTF_Result result = ttf_decode(scratch.arena, R_GetResourceData(&res), point_size, font_codes, countof(font_codes));
|
||||
TTF_Result result = TTF_Decode(scratch.arena, R_GetResourceData(&res), point_size, font_codes, countof(font_codes));
|
||||
R_CloseResource(&res);
|
||||
|
||||
/* Send texture to GPU */
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
Struct(F_Glyph) {
|
||||
f32 off_x;
|
||||
f32 off_y;
|
||||
i32 advance;
|
||||
f32 width;
|
||||
f32 height;
|
||||
i32 advance;
|
||||
Rect atlas_rect;
|
||||
};
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ extern "C"
|
||||
}
|
||||
|
||||
#if PlatformIsWindows
|
||||
# include "ttf_core_dwrite.cpp"
|
||||
# include "ttf_dwrite.cpp"
|
||||
#else
|
||||
# error TTF core not implemented for this platform
|
||||
# error TTF not implemented for this platform
|
||||
#endif
|
||||
|
||||
@ -5,4 +5,8 @@
|
||||
|
||||
#include "ttf_core.h"
|
||||
|
||||
#if PlatformIsWindows
|
||||
# include "ttf_dwrite.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
typedef struct TTF_Glyph TTF_Glyph;
|
||||
struct TTF_Glyph {
|
||||
Struct(TTF_Glyph)
|
||||
{
|
||||
f32 off_x;
|
||||
f32 off_y;
|
||||
i32 advance;
|
||||
f32 width;
|
||||
f32 height;
|
||||
i32 advance;
|
||||
Rect atlas_rect;
|
||||
};
|
||||
|
||||
typedef struct TTF_Result TTF_Result;
|
||||
struct TTF_Result {
|
||||
Struct(TTF_Result)
|
||||
{
|
||||
TTF_Glyph *glyphs;
|
||||
u16 glyphs_count;
|
||||
u16 *cache_indices; /* Array of indices into the `glyphs` array in order of `cache_chars` */
|
||||
@ -18,8 +18,7 @@ struct TTF_Result {
|
||||
u32 *image_pixels; /* Array of [width * height] pixels */
|
||||
};
|
||||
|
||||
typedef struct TTF_StartupReceipt TTF_StartupReceipt;
|
||||
struct TTF_StartupReceipt { i32 _; };
|
||||
TTF_StartupReceipt ttf_startup(void);
|
||||
Struct(TTF_StartupReceipt) { i32 _; };
|
||||
TTF_StartupReceipt TTF_Startup(void);
|
||||
|
||||
TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_codes, u32 cache_codes_count);
|
||||
TTF_Result TTF_Decode(Arena *arena, String encoded, f32 point_size, u32 *cache_codes, u32 cache_codes_count);
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
/* Based on Allen Webster's dwrite rasterizer example -
|
||||
* https://github.com/4th-dimention/examps */
|
||||
|
||||
extern TTF_DW_SharedState TTF_DW_shared_state = ZI;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Win32 libs
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define UNICODE
|
||||
#pragma warning(push, 0)
|
||||
@ -12,38 +17,15 @@
|
||||
#pragma comment(lib, "dwrite")
|
||||
#pragma comment(lib, "gdi32")
|
||||
|
||||
/* TODO: Determine DPI accurately? */
|
||||
#define DPI (96.0f)
|
||||
|
||||
#define CPPSTR(cstr_lit) { (sizeof((cstr_lit)) - 1), (u8 *)(cstr_lit) }
|
||||
|
||||
/* ========================== *
|
||||
* Global state
|
||||
* ========================== */
|
||||
|
||||
Global struct {
|
||||
/* FIXME: Do we need to wrap this in a mutex? */
|
||||
IDWriteFactory5 *factory;
|
||||
} G = ZI, DebugAlias(G, G_ttf_dwrite);
|
||||
|
||||
/* ========================== *
|
||||
* Decode font
|
||||
* ========================== */
|
||||
|
||||
internal i32 round_up(f32 x)
|
||||
{
|
||||
i32 r = (i32)x;
|
||||
if ((f32)r < x) {
|
||||
r += 1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
/* Call this during font system startup */
|
||||
TTF_StartupReceipt ttf_startup(void)
|
||||
TTF_StartupReceipt TTF_Startup(void)
|
||||
{
|
||||
__prof;
|
||||
Assert(!G.factory);
|
||||
TTF_DW_SharedState *g = &TTF_DW_shared_state;
|
||||
Assert(!g->factory);
|
||||
/* FIXME: I think IDWriteFactory5 only exists on later updates of windows
|
||||
* 10? Need to verify. Maybe should just use a custom loader. (We're only
|
||||
* using a factory5 since I think WriteInMemoryFileLoader wasn't
|
||||
@ -55,12 +37,13 @@ TTF_StartupReceipt ttf_startup(void)
|
||||
HRESULT error = DWriteCreateFactory(
|
||||
DWRITE_FACTORY_TYPE_SHARED,
|
||||
__uuidof(IDWriteFactory5),
|
||||
(IUnknown **)&G.factory
|
||||
(IUnknown **)&g->factory
|
||||
);
|
||||
#if CompilerIsClang
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
if (error != S_OK) {
|
||||
if (error != S_OK)
|
||||
{
|
||||
/* FIXME: Enable this */
|
||||
//P_Panic(Lit("Error creating DWrite factory"));
|
||||
(*(volatile int *)0) = 0;
|
||||
@ -69,13 +52,17 @@ TTF_StartupReceipt ttf_startup(void)
|
||||
return { 0 };
|
||||
}
|
||||
|
||||
TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_codes, u32 cache_codes_count)
|
||||
////////////////////////////////
|
||||
//~ Decode
|
||||
|
||||
TTF_Result TTF_Decode(Arena *arena, String encoded, f32 point_size, u32 *cache_codes, u32 cache_codes_count)
|
||||
{
|
||||
__prof;
|
||||
TTF_DW_SharedState *g = &TTF_DW_shared_state;
|
||||
COLORREF bg_color = Rgb32(0, 0, 0);
|
||||
COLORREF fg_color = Rgb32(255, 255, 255);
|
||||
|
||||
IDWriteFactory5 *factory = G.factory;
|
||||
IDWriteFactory5 *factory = g->factory;
|
||||
|
||||
/* TODO: handle errors */
|
||||
HRESULT error = 0;
|
||||
@ -95,11 +82,11 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
error = builder->AddFontFile(font_file);
|
||||
}
|
||||
|
||||
/* Face */
|
||||
//- Create face
|
||||
IDWriteFontFace *font_face = 0;
|
||||
error = factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &font_file, 0, DWRITE_FONT_SIMULATIONS_NONE, &font_face);
|
||||
|
||||
/* Render settings */
|
||||
//- Setup rendering params
|
||||
IDWriteRenderingParams *default_rendering_params = 0;
|
||||
error = factory->CreateRenderingParams(&default_rendering_params);
|
||||
IDWriteRenderingParams *rendering_params = 0;
|
||||
@ -113,15 +100,17 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
DWRITE_RENDERING_MODE_DEFAULT,
|
||||
&rendering_params);
|
||||
|
||||
/* Interop */
|
||||
//- Setup interop
|
||||
IDWriteGdiInterop *dwrite_gdi_interop = 0;
|
||||
error = factory->GetGdiInterop(&dwrite_gdi_interop);
|
||||
|
||||
/* Get Metrics */
|
||||
//- Get metrics
|
||||
DWRITE_FONT_METRICS metrics = ZI;
|
||||
font_face->GetMetrics(&metrics);
|
||||
|
||||
f32 pixel_per_em = point_size * (DPI / 72.0f);
|
||||
u16 glyph_count = font_face->GetGlyphCount();
|
||||
|
||||
f32 pixel_per_em = point_size * (TTF_DW_Dpi / 72.0f);
|
||||
f32 pixel_per_design_unit = pixel_per_em / ((f32)metrics.designUnitsPerEm);
|
||||
|
||||
i32 raster_target_w = (i32)(8.0f * ((f32)metrics.capHeight) * pixel_per_design_unit);
|
||||
@ -133,10 +122,7 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
Assert((f32)((i32)raster_target_x) == raster_target_x);
|
||||
Assert((f32)((i32)raster_target_y) == raster_target_y);
|
||||
|
||||
/* Glyph count */
|
||||
u16 glyph_count = font_face->GetGlyphCount();
|
||||
|
||||
/* Render target */
|
||||
//- Setup render target
|
||||
IDWriteBitmapRenderTarget *render_target = 0;
|
||||
/* FIXME: errors when point_size too high */
|
||||
error = dwrite_gdi_interop->CreateBitmapRenderTarget(0, (UINT32)raster_target_w, (UINT32)raster_target_h, &render_target);
|
||||
@ -154,6 +140,7 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
SelectObject(dc, original);
|
||||
}
|
||||
|
||||
//- Setup atlas
|
||||
/* Allocate font memory */
|
||||
TTF_Glyph *glyphs = (TTF_Glyph *)PushStructs(arena, TTF_Glyph, glyph_count);
|
||||
|
||||
@ -165,14 +152,15 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
u64 atlas_h = 1;
|
||||
u32 *atlas_memory = PushStructs(arena, u32, atlas_w * atlas_h);
|
||||
|
||||
/* Fill CPU side atlas & metric data */
|
||||
//- Fill atlas & metric data
|
||||
u32 out_offset_x = 0;
|
||||
u32 out_offset_y = 0;
|
||||
u32 row_height = 0;
|
||||
{
|
||||
__profn("Build atlas");
|
||||
for (u16 i = 0; i < glyph_count; ++i) {
|
||||
/* Render glyph to target */
|
||||
for (u16 i = 0; i < glyph_count; ++i)
|
||||
{
|
||||
//- Render glyph to render target
|
||||
DWRITE_GLYPH_RUN glyph_run = ZI;
|
||||
glyph_run.fontFace = font_face;
|
||||
glyph_run.fontEmSize = pixel_per_em;
|
||||
@ -193,27 +181,28 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
if (bounding_box.left < 0
|
||||
|| bounding_box.top < 0
|
||||
|| bounding_box.right > raster_target_w
|
||||
|| bounding_box.bottom > raster_target_h) {
|
||||
|| bounding_box.bottom > raster_target_h)
|
||||
{
|
||||
/* Skip */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compute glyph metrics */
|
||||
//- Compute glyph metrics
|
||||
DWRITE_GLYPH_METRICS glyph_metrics = ZI;
|
||||
error = font_face->GetDesignGlyphMetrics(&i, 1, &glyph_metrics, 0);
|
||||
|
||||
f32 off_x = (f32)bounding_box.left - raster_target_x;
|
||||
f32 off_y = (f32)bounding_box.top - raster_target_y;
|
||||
f32 advance = (f32)glyph_metrics.advanceWidth * pixel_per_design_unit;
|
||||
i32 tex_w = bounding_box.right - bounding_box.left;
|
||||
i32 tex_h = bounding_box.bottom - bounding_box.top;
|
||||
f32 advance = CeilF32ToI32((f32)glyph_metrics.advanceWidth * pixel_per_design_unit);
|
||||
|
||||
TTF_Glyph *glyph = &glyphs[i];
|
||||
glyph->off_x = off_x;
|
||||
glyph->off_y = off_y;
|
||||
glyph->advance = round_up(advance);
|
||||
glyph->width = (f32)tex_w;
|
||||
glyph->height = (f32)tex_h;
|
||||
glyph->advance = advance;
|
||||
|
||||
/* Get the bitmap */
|
||||
HBITMAP bitmap = (HBITMAP)GetCurrentObject(dc, OBJ_BITMAP);
|
||||
@ -221,14 +210,16 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
GetObject(bitmap, sizeof(dib), &dib);
|
||||
|
||||
/* Start new row if necessary */
|
||||
if ((out_offset_x + tex_w) >= atlas_w) {
|
||||
if ((out_offset_x + tex_w) >= atlas_w)
|
||||
{
|
||||
out_offset_y += row_height;
|
||||
out_offset_x = 0;
|
||||
row_height = 0;
|
||||
}
|
||||
|
||||
/* Grow atlas height */
|
||||
if ((out_offset_y + tex_h) > atlas_h) {
|
||||
if ((out_offset_y + tex_h) > atlas_h)
|
||||
{
|
||||
u64 diff = (out_offset_y + tex_h) - atlas_h;
|
||||
/* NOTE: This allocation must be contiguous with the initial atlas
|
||||
* allocation (IE: No non-atlas arena PUSHes) */
|
||||
@ -243,14 +234,16 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
glyph->atlas_rect.width = (f32)tex_w;
|
||||
glyph->atlas_rect.height = (f32)tex_h;
|
||||
|
||||
/* Fill atlas */
|
||||
//- Fill atlas
|
||||
u64 in_pitch = (u64)dib.dsBm.bmWidthBytes / 4;
|
||||
u32 *in_data = (u32 *)dib.dsBm.bmBits;
|
||||
u32 *out_data = atlas_memory;
|
||||
for (i32 y = 0; y < tex_h; ++y) {
|
||||
for (i32 y = 0; y < tex_h; ++y)
|
||||
{
|
||||
u64 out_y = out_offset_y + y;
|
||||
u64 in_y = (u64)bounding_box.top + y;
|
||||
for (i32 x = 0; x < tex_w; ++x) {
|
||||
for (i32 x = 0; x < tex_w; ++x)
|
||||
{
|
||||
u64 out_x = out_offset_x + x;
|
||||
u64 in_x = (u64)bounding_box.left + x;
|
||||
u32 *out_pixel = out_data + (out_x + (out_y * atlas_w));
|
||||
@ -261,11 +254,12 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
out_offset_x += tex_w;
|
||||
|
||||
/* Grow row height */
|
||||
if ((u32)tex_h > row_height) {
|
||||
if ((u32)tex_h > row_height)
|
||||
{
|
||||
row_height = (u32)tex_h;
|
||||
}
|
||||
|
||||
/* Clear the render target */
|
||||
//- Clear render target
|
||||
{
|
||||
HGDIOBJ original = SelectObject(dc, GetStockObject(DC_PEN));
|
||||
SetDCPenColor(dc, bg_color);
|
||||
@ -277,14 +271,16 @@ TTF_Result ttf_decode(Arena *arena, String encoded, f32 point_size, u32 *cache_c
|
||||
}
|
||||
}
|
||||
|
||||
/* Construct indices array */
|
||||
//- Construct indices
|
||||
u16 *cache_indices = 0;
|
||||
if (cache_codes_count > 0) {
|
||||
if (cache_codes_count > 0)
|
||||
{
|
||||
cache_indices = PushStructs(arena, u16, cache_codes_count);
|
||||
font_face->GetGlyphIndices(cache_codes, cache_codes_count, cache_indices);
|
||||
}
|
||||
|
||||
/* Release */
|
||||
//- Release
|
||||
/* FIXME: Check for leaks */
|
||||
dwrite_gdi_interop->Release();
|
||||
rendering_params->Release();
|
||||
default_rendering_params->Release();
|
||||
12
src/ttf/ttf_dwrite.h
Normal file
12
src/ttf/ttf_dwrite.h
Normal file
@ -0,0 +1,12 @@
|
||||
////////////////////////////////
|
||||
//~ Shared state
|
||||
|
||||
/* TODO: Determine font dpi dynamically */
|
||||
#define TTF_DW_Dpi (96.0f)
|
||||
|
||||
Struct(TTF_DW_SharedState)
|
||||
{
|
||||
struct IDWriteFactory5 *factory;
|
||||
};
|
||||
|
||||
extern TTF_DW_SharedState TTF_DW_shared_state;
|
||||
Loading…
Reference in New Issue
Block a user