mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-20 10:54:59 -07:00
455 lines
14 KiB
C++
455 lines
14 KiB
C++
////////////////////////////////
|
|
//~ rjf: Globals
|
|
|
|
global FP_DWrite_State *fp_dwrite_state = 0;
|
|
global FP_DWrite_FontFileLoaderVTable fp_dwrite_static_data_font_file_loader__vtable =
|
|
{
|
|
fp_dwrite_iunknown_noop__query_interface,
|
|
fp_dwrite_iunknown_noop__add_ref,
|
|
fp_dwrite_iunknown_noop__release,
|
|
fp_dwrite_static_font_file_loader__stream_from_key,
|
|
};
|
|
global FP_DWrite_FontFileLoader fp_dwrite_static_data_font_file_loader = {&fp_dwrite_static_data_font_file_loader__vtable};
|
|
global FP_DWrite_FontFileStreamVTable fp_dwrite_static_data_font_file_stream__vtable =
|
|
{
|
|
fp_dwrite_iunknown_noop__query_interface,
|
|
fp_dwrite_iunknown_noop__add_ref,
|
|
fp_dwrite_iunknown_noop__release,
|
|
fp_dwrite_static_font_file_stream__read_file_fragment,
|
|
fp_dwrite_static_font_file_stream__release_file_fragment,
|
|
fp_dwrite_static_font_file_stream__get_file_size,
|
|
fp_dwrite_static_font_file_stream__get_last_write_time,
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Helpers
|
|
|
|
//- rjf: handle conversion functions
|
|
|
|
internal FP_DWrite_Font
|
|
fp_dwrite_font_from_handle(FP_Handle handle)
|
|
{
|
|
FP_DWrite_Font result = {0};
|
|
result.file = (IDWriteFontFile *)handle.u64[0];
|
|
result.face = (IDWriteFontFace *)handle.u64[1];
|
|
return result;
|
|
}
|
|
|
|
internal FP_Handle
|
|
fp_dwrite_handle_from_font(FP_DWrite_Font font)
|
|
{
|
|
FP_Handle result = {0};
|
|
result.u64[0] = (U64)font.file;
|
|
result.u64[1] = (U64)font.face;
|
|
return result;
|
|
}
|
|
|
|
//- rjf: file stream allocator
|
|
|
|
internal FP_DWrite_FontFileStreamNode *
|
|
fp_dwrite_font_file_stream_node_alloc(String8 *data_ptr)
|
|
{
|
|
FP_DWrite_FontFileStreamNode *node = 0;
|
|
for(FP_DWrite_FontFileStreamNode *n = fp_dwrite_state->first_stream_node; n != 0; n = n->next)
|
|
{
|
|
if(n->stream.data == data_ptr)
|
|
{
|
|
node = n;
|
|
break;
|
|
}
|
|
}
|
|
if(node == 0)
|
|
{
|
|
node = fp_dwrite_state->free_stream_node;
|
|
if(node != 0)
|
|
{
|
|
SLLStackPop(fp_dwrite_state->free_stream_node);
|
|
}
|
|
else
|
|
{
|
|
node = push_array_no_zero(fp_dwrite_state->arena, FP_DWrite_FontFileStreamNode, 1);
|
|
}
|
|
MemoryZeroStruct(node);
|
|
node->stream.lpVtbl = &fp_dwrite_static_data_font_file_stream__vtable;
|
|
node->stream.data = data_ptr;
|
|
DLLPushBack(fp_dwrite_state->first_stream_node, fp_dwrite_state->last_stream_node, node);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
internal void
|
|
fp_dwrite_font_file_stream_node_release(FP_DWrite_FontFileStreamNode *node)
|
|
{
|
|
DLLPushBack(fp_dwrite_state->first_stream_node, fp_dwrite_state->last_stream_node, node);
|
|
SLLStackPush(fp_dwrite_state->free_stream_node, node);
|
|
}
|
|
|
|
//- rjf: iunknown no-op helpers
|
|
|
|
internal HRESULT
|
|
fp_dwrite_iunknown_noop__query_interface(void *obj, REFIID riid, void *ptr_to_object)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
internal ULONG
|
|
fp_dwrite_iunknown_noop__add_ref(void *obj)
|
|
{
|
|
ULONG result = 1;
|
|
return result;
|
|
}
|
|
|
|
internal ULONG
|
|
fp_dwrite_iunknown_noop__release(void *obj)
|
|
{
|
|
ULONG result = 1;
|
|
return result;
|
|
}
|
|
|
|
//- rjf: font file loader interface function implementations
|
|
|
|
internal HRESULT
|
|
fp_dwrite_static_font_file_loader__stream_from_key(FP_DWrite_FontFileLoader *obj, void const *font_file_ref_key, UINT32 font_file_ref_key_size, IDWriteFontFileStream **stream_out)
|
|
{
|
|
HRESULT result = S_OK;
|
|
String8 *key = *(String8 **)font_file_ref_key;
|
|
FP_DWrite_FontFileStreamNode *node = fp_dwrite_font_file_stream_node_alloc(key);
|
|
*stream_out = (IDWriteFontFileStream *)&node->stream;
|
|
return result;
|
|
}
|
|
|
|
//- rjf: font file stream interface function implementations
|
|
|
|
internal HRESULT
|
|
fp_dwrite_static_font_file_stream__read_file_fragment(FP_DWrite_FontFileStream *obj, void const **fragment_start, UINT64 file_offset, UINT64 fragment_size, void **fragment_context)
|
|
{
|
|
HRESULT result = S_OK;
|
|
*fragment_start = obj->data->str + file_offset;
|
|
*fragment_context = 0;
|
|
return result;
|
|
}
|
|
|
|
internal HRESULT
|
|
fp_dwrite_static_font_file_stream__release_file_fragment(FP_DWrite_FontFileStream *obj, void *fragment_context)
|
|
{
|
|
HRESULT result = S_OK;
|
|
return result;
|
|
}
|
|
|
|
internal HRESULT
|
|
fp_dwrite_static_font_file_stream__get_file_size(FP_DWrite_FontFileStream *obj, UINT64 *size_out)
|
|
{
|
|
HRESULT result = S_OK;
|
|
*size_out = obj->data->size;
|
|
return result;
|
|
}
|
|
|
|
internal HRESULT
|
|
fp_dwrite_static_font_file_stream__get_last_write_time(FP_DWrite_FontFileStream *obj, UINT64 *time_out)
|
|
{
|
|
HRESULT result = S_OK;
|
|
*time_out = 0;
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Backend Implementations
|
|
|
|
fp_hook void
|
|
fp_init(void)
|
|
{
|
|
ProfBeginFunction();
|
|
HRESULT error = 0;
|
|
|
|
//- rjf: initialize main state
|
|
{
|
|
Arena *arena = arena_alloc();
|
|
fp_dwrite_state = push_array(arena, FP_DWrite_State, 1);
|
|
fp_dwrite_state->arena = arena;
|
|
}
|
|
|
|
//- rjf: make dwrite factory
|
|
error = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), (IUnknown **)&fp_dwrite_state->factory);
|
|
|
|
//- rjf: register static data font "loader" interface
|
|
error = fp_dwrite_state->factory->RegisterFontFileLoader((IDWriteFontFileLoader *)&fp_dwrite_static_data_font_file_loader);
|
|
|
|
//- rjf: make base rendering params
|
|
error = fp_dwrite_state->factory->CreateRenderingParams(&fp_dwrite_state->base_rendering_params);
|
|
|
|
//- rjf: make sharp rendering params
|
|
{
|
|
FLOAT gamma = 1.f;
|
|
FLOAT enhanced_contrast = fp_dwrite_state->base_rendering_params->GetEnhancedContrast();
|
|
// FLOAT clear_type_level = fp_dwrite_state->base_rendering_params->GetClearTypeLevel();
|
|
error = fp_dwrite_state->factory->CreateCustomRenderingParams(gamma,
|
|
enhanced_contrast,
|
|
2.f,
|
|
DWRITE_PIXEL_GEOMETRY_FLAT,
|
|
DWRITE_RENDERING_MODE_GDI_NATURAL,
|
|
&fp_dwrite_state->rendering_params[FP_RasterMode_Sharp]);
|
|
}
|
|
|
|
//- rjf: make smooth rendering params
|
|
{
|
|
FLOAT gamma = 1.f;
|
|
FLOAT enhanced_contrast = fp_dwrite_state->base_rendering_params->GetEnhancedContrast();
|
|
// FLOAT clear_type_level = fp_dwrite_state->base_rendering_params->GetClearTypeLevel();
|
|
error = fp_dwrite_state->factory->CreateCustomRenderingParams(gamma,
|
|
enhanced_contrast,
|
|
2.f,
|
|
DWRITE_PIXEL_GEOMETRY_FLAT,
|
|
DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC,
|
|
&fp_dwrite_state->rendering_params[FP_RasterMode_Smooth]);
|
|
}
|
|
|
|
//- rjf: make dwrite gdi interop
|
|
error = fp_dwrite_state->factory->GetGdiInterop(&fp_dwrite_state->gdi_interop);
|
|
|
|
//- rjf: build render target for rasterization
|
|
fp_dwrite_state->bitmap_render_target_dim = v2s32(2048, 256);
|
|
error = fp_dwrite_state->gdi_interop->CreateBitmapRenderTarget(0, fp_dwrite_state->bitmap_render_target_dim.x, fp_dwrite_state->bitmap_render_target_dim.y, &fp_dwrite_state->bitmap_render_target);
|
|
fp_dwrite_state->bitmap_render_target->SetPixelsPerDip(1.0);
|
|
ProfEnd();
|
|
}
|
|
|
|
fp_hook FP_Handle
|
|
fp_font_open(String8 path)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(0, 0);
|
|
String16 path16 = str16_from_8(scratch.arena, path);
|
|
FP_DWrite_Font font = {0};
|
|
HRESULT error = 0;
|
|
|
|
//- rjf: open font file reference
|
|
error = fp_dwrite_state->factory->CreateFontFileReference((WCHAR *)path16.str, 0, &font.file);
|
|
|
|
//- rjf: open font face
|
|
error = fp_dwrite_state->factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &font.file, 0, DWRITE_FONT_SIMULATIONS_NONE, &font.face);
|
|
|
|
//- rjf: handlify & return
|
|
FP_Handle handle = fp_dwrite_handle_from_font(font);
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return handle;
|
|
}
|
|
|
|
fp_hook FP_Handle
|
|
fp_font_open_from_static_data_string(String8 *data_ptr)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(0, 0);
|
|
FP_DWrite_Font font = {0};
|
|
HRESULT error = 0;
|
|
|
|
//- rjf: open font file reference
|
|
error = fp_dwrite_state->factory->CreateCustomFontFileReference(&data_ptr, sizeof(String8 *), (IDWriteFontFileLoader *)&fp_dwrite_static_data_font_file_loader, &font.file);
|
|
|
|
//- rjf: open font face
|
|
error = fp_dwrite_state->factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &font.file, 0, DWRITE_FONT_SIMULATIONS_NONE, &font.face);
|
|
|
|
//- rjf: handlify & return
|
|
FP_Handle handle = fp_dwrite_handle_from_font(font);
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return handle;
|
|
}
|
|
|
|
fp_hook void
|
|
fp_font_close(FP_Handle handle)
|
|
{
|
|
ProfBeginFunction();
|
|
FP_DWrite_Font font = fp_dwrite_font_from_handle(handle);
|
|
if(font.face != 0)
|
|
{
|
|
font.face->Release();
|
|
}
|
|
if(font.file != 0)
|
|
{
|
|
font.file->Release();
|
|
}
|
|
ProfEnd();
|
|
}
|
|
|
|
fp_hook FP_Metrics
|
|
fp_metrics_from_font(FP_Handle handle)
|
|
{
|
|
ProfBeginFunction();
|
|
FP_DWrite_Font font = fp_dwrite_font_from_handle(handle);
|
|
DWRITE_FONT_METRICS metrics = {0};
|
|
if(font.face != 0)
|
|
{
|
|
font.face->GetMetrics(&metrics);
|
|
}
|
|
FP_Metrics result = {0};
|
|
{
|
|
result.design_units_per_em = (F32)metrics.designUnitsPerEm;
|
|
result.ascent = (F32)metrics.ascent;
|
|
result.descent = (F32)metrics.descent;
|
|
result.line_gap = (F32)metrics.lineGap;
|
|
result.capital_height = (F32)metrics.capHeight;
|
|
}
|
|
ProfEnd();
|
|
return result;
|
|
}
|
|
|
|
fp_hook NO_ASAN FP_RasterResult
|
|
fp_raster(Arena *arena, FP_Handle font_handle, F32 size, FP_RasterMode mode, String8 string)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
HRESULT error = 0;
|
|
String32 string32 = str32_from_8(scratch.arena, string);
|
|
FP_DWrite_Font font = fp_dwrite_font_from_handle(font_handle);
|
|
COLORREF bg_color = RGB(0, 0, 0);
|
|
COLORREF fg_color = RGB(255, 255, 255);
|
|
|
|
//- rjf: get font metrics
|
|
DWRITE_FONT_METRICS font_metrics = {0};
|
|
if(font.face != 0)
|
|
{
|
|
font.face->GetMetrics(&font_metrics);
|
|
}
|
|
F32 design_units_per_em = (F32)font_metrics.designUnitsPerEm;
|
|
|
|
//- rjf: get glyph indices
|
|
U16 *glyph_indices = push_array_no_zero(scratch.arena, U16, string32.size);
|
|
if(font.face != 0)
|
|
{
|
|
error = font.face->GetGlyphIndices(string32.str, string32.size, glyph_indices);
|
|
}
|
|
|
|
//- rjf: get metrics info
|
|
U64 glyphs_count = string32.size;
|
|
DWRITE_GLYPH_METRICS *glyphs_metrics = push_array_no_zero(scratch.arena, DWRITE_GLYPH_METRICS, glyphs_count);
|
|
if(font.face != 0)
|
|
{
|
|
error = font.face->GetGdiCompatibleGlyphMetrics((96.f/72.f)*size, 1.f, 0, 1, glyph_indices, glyphs_count, glyphs_metrics, 0);
|
|
}
|
|
|
|
//- rjf: derive info from metrics
|
|
F32 advance = 0;
|
|
Vec2S16 atlas_dim = {0};
|
|
if(font.face != 0)
|
|
{
|
|
atlas_dim.y = (S16)((96.f/72.f) * size * (font_metrics.ascent + font_metrics.descent) / design_units_per_em);
|
|
for(U64 idx = 0; idx < glyphs_count; idx += 1)
|
|
{
|
|
DWRITE_GLYPH_METRICS *glyph_metrics = glyphs_metrics + idx;
|
|
F32 glyph_advance_width = (96.f/72.f) * size * glyph_metrics->advanceWidth / design_units_per_em;
|
|
F32 glyph_advance_height = (96.f/72.f) * size * glyph_metrics->advanceHeight / design_units_per_em;
|
|
advance += glyph_advance_width;
|
|
atlas_dim.x = Max(atlas_dim.x, (S16)(advance+1));
|
|
}
|
|
atlas_dim.x += 7;
|
|
atlas_dim.x -= atlas_dim.x%8;
|
|
atlas_dim.x += 4;
|
|
atlas_dim.y += 4;
|
|
}
|
|
|
|
//- rjf: make dwrite bitmap for rendering
|
|
IDWriteBitmapRenderTarget *render_target = 0;
|
|
if(font.face != 0)
|
|
{
|
|
error = fp_dwrite_state->gdi_interop->CreateBitmapRenderTarget(0, atlas_dim.x, atlas_dim.y, &render_target);
|
|
render_target->SetPixelsPerDip(1.0);
|
|
}
|
|
|
|
//- rjf: get bitmap & clear
|
|
HDC dc = 0;
|
|
if(font.face != 0)
|
|
{
|
|
dc = render_target->GetMemoryDC();
|
|
HGDIOBJ original = SelectObject(dc, GetStockObject(DC_PEN));
|
|
SetDCPenColor(dc, bg_color);
|
|
SelectObject(dc, GetStockObject(DC_BRUSH));
|
|
SetDCBrushColor(dc, bg_color);
|
|
Rectangle(dc, 0, 0, atlas_dim.x, atlas_dim.y);
|
|
SelectObject(dc, original);
|
|
}
|
|
|
|
//- rjf: draw glyph run
|
|
Vec2F32 draw_p = {1, (F32)atlas_dim.y - 2.f};
|
|
if(font.face != 0)
|
|
{
|
|
F32 ascent = (96.f/72.f)*size * font_metrics.ascent / design_units_per_em;
|
|
F32 descent = (96.f/72.f)*size * font_metrics.descent / design_units_per_em;
|
|
draw_p.y -= descent;
|
|
}
|
|
DWRITE_GLYPH_RUN glyph_run = {0};
|
|
if(font.face != 0)
|
|
{
|
|
glyph_run.fontFace = font.face;
|
|
glyph_run.fontEmSize = size * 96.f/72.f;
|
|
glyph_run.glyphCount = string32.size;
|
|
glyph_run.glyphIndices = glyph_indices;
|
|
}
|
|
RECT bounding_box = {0};
|
|
if(font.face != 0)
|
|
{
|
|
error = render_target->DrawGlyphRun(draw_p.x, draw_p.y,
|
|
DWRITE_MEASURING_MODE_NATURAL,
|
|
&glyph_run,
|
|
fp_dwrite_state->rendering_params[mode],
|
|
fg_color,
|
|
&bounding_box);
|
|
}
|
|
|
|
//- rjf: get bitmap
|
|
DIBSECTION dib = {0};
|
|
if(font.face != 0)
|
|
{
|
|
HBITMAP bitmap = (HBITMAP)GetCurrentObject(dc, OBJ_BITMAP);
|
|
GetObject(bitmap, sizeof(dib), &dib);
|
|
}
|
|
|
|
//- rjf: fill & return
|
|
FP_RasterResult result = {0};
|
|
if(font.face != 0)
|
|
{
|
|
// rjf: fill basics
|
|
result.atlas_dim = atlas_dim;
|
|
result.atlas = push_array_no_zero(arena, U8, atlas_dim.x*atlas_dim.y*4);
|
|
result.advance = advance;
|
|
result.height = bounding_box.bottom + 1.f;
|
|
|
|
// rjf: fill atlas
|
|
{
|
|
U8 *in_data = (U8 *)dib.dsBm.bmBits;
|
|
U64 in_pitch = (U64)dib.dsBm.bmWidthBytes;
|
|
U8 *out_data = (U8 *)result.atlas;
|
|
U64 out_pitch = atlas_dim.x * 4;
|
|
U64 color_sum = 0;
|
|
U8 *in_line = (U8 *)in_data;
|
|
U8 *out_line = out_data;
|
|
for(U64 y = 0; y < atlas_dim.y; y += 1)
|
|
{
|
|
U8 *in_pixel = in_line;
|
|
U8 *out_pixel = out_line;
|
|
for(U64 x = 0; x < atlas_dim.x; x += 1)
|
|
{
|
|
U8 in_pixel_byte = in_pixel[0];
|
|
out_pixel[0] = 255;
|
|
out_pixel[1] = 255;
|
|
out_pixel[2] = 255;
|
|
out_pixel[3] = in_pixel_byte;
|
|
color_sum += in_pixel_byte;
|
|
in_pixel += 4;
|
|
out_pixel += 4;
|
|
}
|
|
in_line += in_pitch;
|
|
out_line += out_pitch;
|
|
}
|
|
if(color_sum == 0)
|
|
{
|
|
result.atlas_dim = v2s16(0, 0);
|
|
}
|
|
}
|
|
render_target->Release();
|
|
}
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return result;
|
|
}
|