diff --git a/src/config.h b/src/config.h index 8442892d..630dd3c0 100644 --- a/src/config.h +++ b/src/config.h @@ -1,7 +1,7 @@ /* Project-wide configurable constants */ /* TODO: Remove this */ -#define FONT_TEST 1 +#define FONT_TEST 0 #define WRITE_DIR "power_play" #define SETTINGS_FILENAME "settings.json" diff --git a/src/draw.c b/src/draw.c index 58505ac9..383c5ebc 100644 --- a/src/draw.c +++ b/src/draw.c @@ -270,6 +270,61 @@ void draw_text(struct renderer_canvas *canvas, struct font *font, struct v2 pos, void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 pos, f32 scale, struct string str) { +#if FONT_TEST + + struct temp_arena scratch = scratch_begin_no_conflict(); + + renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture = font->texture.renderer_handle }); + struct string32 str32 = string32_from_string(scratch.arena, str); + + struct v2 draw_pos = pos; + draw_pos.y += font->point_size * scale; + + struct font_glyphs_list glyphs = font_get_glyphs(font, str32); + + + + + + + + struct string_codepoint_iter iter = string_codepoint_iter_begin(str); + while (string_codepoint_iter_next(&iter)) { + u32 codepoint = iter.codepoint; + + /* TODO: Remove this (placeholder \n) */ + if (codepoint == '\n') { + draw_pos.x = pos.x; + draw_pos.y += (font->point_size * 1.5f) * scale; + continue; + } + + struct font_glyph *glyph = font_get_glyph(font, codepoint); + f32 x = draw_pos.x + glyph->off_x * scale; + f32 y = draw_pos.y + glyph->off_y * scale; + f32 width = glyph->width * scale; + f32 height = glyph->height * scale; + + struct clip_rect clip = { + { + glyph->atlas_rect.x / font->texture.size.x, + glyph->atlas_rect.y / font->texture.size.y + }, + + { + (glyph->atlas_rect.x + glyph->atlas_rect.width) / font->texture.size.x, + (glyph->atlas_rect.y + glyph->atlas_rect.height) / font->texture.size.y + } + }; + + struct quad quad = quad_from_rect(RECT(x, y, width, height)); + draw_texture_quad_internal(canvas, clip, 0xFFFFFFFF, quad); + + draw_pos.x += glyph->advance * scale; + } + string_codepoint_iter_end(&iter); + +#else renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture = font->texture.renderer_handle }); struct v2 draw_pos = pos; @@ -310,4 +365,5 @@ void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 p draw_pos.x += glyph->advance * scale; } string_codepoint_iter_end(&iter); +#endif } diff --git a/src/font.c b/src/font.c index bf5c3511..a06c9287 100644 --- a/src/font.c +++ b/src/font.c @@ -1,3 +1,5 @@ +#if FONT_TEST + #include "font.h" #include "arena.h" #include "ttf.h" @@ -254,3 +256,265 @@ struct font_glyph *font_get_glyph(struct font *font, u32 codepoint) } return &font->glyphs[0]; } + + +#else + +#include "font.h" +#include "arena.h" +#include "ttf.h" +#include "work.h" +#include "scratch.h" +#include "asset_cache.h" +#include "resource.h" +#include "log.h" +#include "string.h" +#include "renderer.h" + +#if FONT_TEST + +#define LOOKUP_TABLE_SIZE (ARRAY_COUNT(g_font_codes) * 2) +GLOBAL u32 g_font_codes[(1 << 20)] = { 0 }; + +#else + +#define LOOKUP_TABLE_SIZE (256) +GLOBAL u32 g_font_codes[] = { + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; + +#endif + +struct font_task_params { + struct font_task_params *next_free; + + struct asset *asset; + f32 point_size; + u64 path_len; + char path_cstr[1024]; +}; + +struct font_task_params_store { + struct font_task_params *head_free; + struct arena arena; + struct sys_mutex mutex; +}; + +/* ========================== * + * Global state + * ========================== */ + +GLOBAL struct { + struct font_task_params_store params; +} L = { 0 }, DEBUG_LVAR(L_font); + +/* ========================== * + * Startup + * ========================== */ + +struct font_startup_receipt font_startup(struct work_startup_receipt *work_sr, + struct renderer_startup_receipt *renderer_sr, + struct asset_cache_startup_receipt *asset_cache_sr, + struct ttf_startup_receipt *ttf_sr, + struct resource_startup_receipt *resource_sr) +{ + (UNUSED)work_sr; + (UNUSED)renderer_sr; + (UNUSED)asset_cache_sr; + (UNUSED)ttf_sr; + (UNUSED)resource_sr; + + L.params.arena = arena_alloc(GIGABYTE(64)); + L.params.mutex = sys_mutex_alloc(); + +#if FONT_TEST + for (u64 i = 0; i < ARRAY_COUNT(g_font_codes); ++i) { + g_font_codes[i] = i; + } +#endif + + return (struct font_startup_receipt) { 0 }; +} + +/* ========================== * + * Load task param store + * ========================== */ + +INTERNAL struct font_task_params *font_task_params_alloc(void) +{ + struct font_task_params *p = NULL; + sys_mutex_lock(&L.params.mutex); + { + if (L.params.head_free) { + p = L.params.head_free; + L.params.head_free = p->next_free; + } else { + p = arena_push_zero(&L.params.arena, struct font_task_params); + } + } + sys_mutex_unlock(&L.params.mutex); + return p; +} + +INTERNAL void font_task_params_release(struct font_task_params *p) +{ + sys_mutex_lock(&L.params.mutex); + { + p->next_free = L.params.head_free; + L.params.head_free = p; + } + sys_mutex_unlock(&L.params.mutex); +} + +/* ========================== * + * Load + * ========================== */ + +INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams) +{ + __prof; + struct font_task_params *params = (struct font_task_params *)vparams; + struct temp_arena scratch = scratch_begin_no_conflict(); + struct string path = string_from_cstr_len(params->path_cstr, params->path_len); + f32 point_size = params->point_size; + struct asset *asset = params->asset; + + logf_info("Loading font \"%F\" (point size %F)", FMT_STR(path), FMT_FLOAT((f64)point_size)); + sys_timestamp_t start_ts = sys_timestamp(); + + ASSERT(string_ends_with(path, STR(".ttf"))); + if (!resource_exists(path)) { + /* FIME: Load baked font instead of panicking */ + sys_panic(string_format(scratch.arena, + STR("Font \"%F\" not found"), + FMT_STR(path))); + } + + ASSERT(ARRAY_COUNT(g_font_codes) < LOOKUP_TABLE_SIZE); + + /* Decode */ + struct resource res = resource_open(path); + struct ttf_decode_result result = ttf_decode(scratch.arena, res.bytes, point_size, g_font_codes, ARRAY_COUNT(g_font_codes)); + resource_close(res); + + /* Send texture to GPU */ + struct renderer_handle texture_renderer_handle = renderer_texture_alloc(result.image_data); + + /* Allocate store memory */ + struct font *font = NULL; + { + struct asset_cache_store store = asset_cache_store_open(); + font = arena_push_zero(store.arena, struct font); + font->glyphs = arena_push_array(store.arena, struct font_glyph, result.glyphs_count); + font->lookup = arena_push_array_zero(store.arena, u16, LOOKUP_TABLE_SIZE); + asset_cache_store_close(&store); + } + + /* Set font data */ + font->texture = (struct texture) { + .renderer_handle = texture_renderer_handle, + .size = V2(result.image_data.width, result.image_data.height), + }; + font->glyphs_count = result.glyphs_count; + font->point_size = point_size; + + /* FIXME: Load baked font instead of panicking */ + if (font->glyphs_count <= 0) { + sys_panic(string_format(scratch.arena, + STR("Parsed 0 glyphs from font \"%F\"!"), + FMT_STR(path))); + } + + /* Copy glyphs from decode result */ + MEMCPY(font->glyphs, result.glyphs, sizeof(*font->glyphs) * result.glyphs_count); + + /* Build lookup table */ + for (u64 i = 0; i < ARRAY_COUNT(g_font_codes); ++i) { + u32 codepoint = g_font_codes[i]; + font->lookup[codepoint] = result.cache_indices[i]; + } + + font_task_params_release(params); + + logf_info("Finished loading font \"%F\" (point size %F) in %F seconds", FMT_STR(path), FMT_FLOAT((f64)point_size), FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts))); + asset_cache_mark_ready(asset, font); + + scratch_end_and_decommit(scratch); +} + +/* Returns the asset from the asset cache */ +struct asset *font_load_asset(struct string path, f32 point_size, b32 help) +{ + __prof; + struct temp_arena scratch = scratch_begin_no_conflict(); + + /* Concatenate point_size to path for key */ + struct string key = string_format(scratch.arena, + STR("%F%F_font"), + FMT_STR(path), + FMT_FLOAT_P((f64)point_size, 3)); + u64 hash = asset_cache_hash(key); + b32 is_first_touch; + struct asset *asset = asset_cache_touch(key, hash, &is_first_touch); + + if (is_first_touch) { + /* Assemble task params */ + struct font_task_params *params = font_task_params_alloc(); + if (path.len > (sizeof(params->path_cstr) - 1)) { + sys_panic(string_format(scratch.arena, + STR("Font path \"%F\" too long!"), + FMT_STR(path))); + } + cstr_buff_from_string(BUFFER_FROM_ARRAY(params->path_cstr), path); + params->path_len = path.len; + params->asset = asset; + params->point_size = point_size; + + /* Push task */ + asset_cache_mark_loading(asset); + struct work_handle wh = { 0 }; + if (help) { + wh = work_push_task_and_help(&font_load_asset_task, params, WORK_PRIORITY_NORMAL); + } else { + wh = work_push_task(&font_load_asset_task, params, WORK_PRIORITY_NORMAL); + } + asset_cache_set_work(asset, &wh); + } + + scratch_end(scratch); + return asset; +} + +struct font *font_load_async(struct string path, f32 point_size) +{ + __prof; + struct asset *asset = font_load_asset(path, point_size, false); + struct font *f = (struct font *)asset_cache_get_store_data(asset); + return f; +} + +struct font *font_load(struct string path, f32 point_size) +{ + __prof; + struct asset *asset = font_load_asset(path, point_size, true); + asset_cache_wait(asset); + struct font *f = (struct font *)asset_cache_get_store_data(asset); + return f; +} + +/* ========================== * + * Other + * ========================== */ + +struct font_glyph *font_get_glyph(struct font *font, u32 codepoint) +{ + if (codepoint < LOOKUP_TABLE_SIZE) { + u16 index = font->lookup[codepoint]; + if (index < font->glyphs_count) { + return &font->glyphs[index]; + } + } + return &font->glyphs[0]; +} + +#endif diff --git a/src/font.h b/src/font.h index 3757f99f..4d62abe0 100644 --- a/src/font.h +++ b/src/font.h @@ -1,3 +1,60 @@ +#if FONT_TEST + +#ifndef FONT_H +#define FONT_H + +#include "texture.h" +#include "util.h" + + +struct asset; +struct work_startup_receipt; +struct renderer_startup_receipt; +struct asset_cache_startup_receipt; +struct ttf_startup_receipt; +struct resource_startup_receipt; + +struct font_glyph_list { + struct font_glyph *first; + struct font_glyph *last; +}; + +struct font_glyph { + f32 off_x; + f32 off_y; + i32 advance; + f32 width; + f32 height; + struct rect atlas_rect; + + struct font_glyph *next; +}; + +struct font { + f32 point_size; + struct texture texture; + u16 glyphs_count; + struct font_glyph *glyphs; + u16 *lookup; +}; + +struct font_startup_receipt { i32 _; }; +struct font_startup_receipt font_startup(struct work_startup_receipt *work_sr, + struct renderer_startup_receipt *renderer_sr, + struct asset_cache_startup_receipt *asset_cache_sr, + struct ttf_startup_receipt *ttf_sr, + struct resource_startup_receipt *resource_sr); + +struct asset *font_load_asset(struct string path, f32 point_size, b32 help); +struct font *font_load_async(struct string path, f32 point_size); +struct font *font_load(struct string path, f32 point_size); + +struct font_glyph *font_get_glyph(struct font *font, u32 codepoint); + +#endif + +#else + #ifndef FONT_H #define FONT_H @@ -43,3 +100,5 @@ struct font *font_load(struct string path, f32 point_size); struct font_glyph *font_get_glyph(struct font *font, u32 codepoint); #endif + +#endif diff --git a/src/ttf_dwrite.cpp b/src/ttf_dwrite.cpp index edc49b61..365af505 100644 --- a/src/ttf_dwrite.cpp +++ b/src/ttf_dwrite.cpp @@ -1,3 +1,4 @@ +#if FONT_TEST /* Based on Allen Webster's dwrite rasterizer example - * https://github.com/4th-dimention/examps */ @@ -299,3 +300,309 @@ struct ttf_decode_result ttf_decode(struct arena *arena, struct buffer encoded, result.image_data.height = (u32)atlas_h; return result; } + +#else + +/* Based on Allen Webster's dwrite rasterizer example - + * https://github.com/4th-dimention/examps */ + +extern "C" +{ +#include "ttf.h" +#include "scratch.h" +#include "util.h" +#include "string.h" +#include "memory.h" +#include "arena.h" +#include "font.h" +} + +#include +#include + +#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; +} L = { 0 }, DEBUG_LVAR(L_ttf_dwrite); + +/* ========================== * + * Decode font + * ========================== */ + +INTERNAL i32 round_up(f32 x) +{ + i32 r = (i32)x; + if ((f32)r < x) { + r += 1; + } + return r; +} + +/* Call this during font system startup */ +struct ttf_startup_receipt ttf_startup(void) +{ + ASSERT(!L.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 + * implemented until then) */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wlanguage-extension-token" /* for __uuidof */ + HRESULT error = DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory5), + (IUnknown **)&L.factory + ); +#pragma clang diagnostic pop + if (error) { + sys_panic(STR("Error creating DWrite factory")); + } + + return (struct ttf_startup_receipt) { 0 }; +} + +struct ttf_decode_result ttf_decode(struct arena *arena, struct buffer encoded, f32 point_size, u32 *cache_codes, u32 cache_codes_count) +{ + COLORREF bg_color = RGB(0,0,0); + COLORREF fg_color = RGB(255,255,255); + + IDWriteFactory5 *factory = L.factory; + + /* TODO: handle errors */ + HRESULT error = 0; + (UNUSED)error; + + /* File */ + IDWriteFontFile *font_file = NULL; + { + /* Create in memory loader */ + IDWriteInMemoryFontFileLoader *loader = NULL; + error = factory->CreateInMemoryFontFileLoader(&loader); + error = factory->RegisterFontFileLoader(loader); + + IDWriteFontSetBuilder1 *builder = NULL; + error = factory->CreateFontSetBuilder(&builder); + error = loader->CreateInMemoryFontFileReference(factory, encoded.data, (u32)encoded.size, NULL, &font_file); + error = builder->AddFontFile(font_file); + } + + /* Face */ + IDWriteFontFace *font_face = NULL; + error = factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &font_file, 0, DWRITE_FONT_SIMULATIONS_NONE, &font_face); + + /* Render settings */ + IDWriteRenderingParams *default_rendering_params = NULL; + error = factory->CreateRenderingParams(&default_rendering_params); + IDWriteRenderingParams *rendering_params = NULL; + FLOAT gamma = default_rendering_params->GetGamma(); + FLOAT enhanced_contrast = default_rendering_params->GetEnhancedContrast(); + FLOAT clear_type_level = default_rendering_params->GetClearTypeLevel(); + error = factory->CreateCustomRenderingParams(gamma, + enhanced_contrast, + clear_type_level, + DWRITE_PIXEL_GEOMETRY_FLAT, + DWRITE_RENDERING_MODE_DEFAULT, + &rendering_params); + + /* Interop */ + IDWriteGdiInterop *dwrite_gdi_interop = NULL; + error = factory->GetGdiInterop(&dwrite_gdi_interop); + + /* Get Metrics */ + DWRITE_FONT_METRICS metrics = { 0 }; + font_face->GetMetrics(&metrics); + + f32 pixel_per_em = point_size * (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); + i32 raster_target_h = raster_target_w; + + f32 raster_target_x = (f32)(raster_target_w / 2); + f32 raster_target_y = raster_target_x; + + 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 */ + IDWriteBitmapRenderTarget *render_target = NULL; + /* FIXME: errors when point_size too high */ + error = dwrite_gdi_interop->CreateBitmapRenderTarget(0, raster_target_w, raster_target_h, &render_target); + render_target->SetPixelsPerDip(1.0); + + /* Clear the render target */ + HDC dc = 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, raster_target_w, raster_target_h); + SelectObject(dc, original); + } + + /* Allocate font memory */ + struct font_glyph *glyphs = (struct font_glyph *)arena_push_array_zero(arena, struct font_glyph, glyph_count); + + /* Allocate (starting) atlas memory + * NOTE: This is unecessary since atlas memory will grow anyway. Could + * just start w/ atlas height 0. + */ + u64 atlas_w = 1024; + u64 atlas_h = 1; + u32 *atlas_memory = arena_push_array_zero(arena, u32, atlas_w * atlas_h); + + /* Fill CPU side atlas & metric data */ + u32 out_offset_x = 0; + u32 out_offset_y = 0; + u32 row_height = 0; + for (u16 i = 0; i < glyph_count; ++i) { + /* Render glyph to target */ + DWRITE_GLYPH_RUN glyph_run = { 0 }; + glyph_run.fontFace = font_face; + glyph_run.fontEmSize = pixel_per_em; + glyph_run.glyphCount = 1; + glyph_run.glyphIndices = &i; + + RECT bounding_box = { 0 }; + error = render_target->DrawGlyphRun( + raster_target_x, + raster_target_y, + DWRITE_MEASURING_MODE_NATURAL, + &glyph_run, + rendering_params, + fg_color, + &bounding_box + ); + + if (bounding_box.left < 0 + || bounding_box.top < 0 + || bounding_box.right > raster_target_w + || bounding_box.bottom > raster_target_h) { + /* Skip */ + continue; + } + + /* Compute glyph metrics */ + DWRITE_GLYPH_METRICS glyph_metrics = { 0 }; + error = font_face->GetDesignGlyphMetrics(&i, 1, &glyph_metrics, false); + + 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; + + struct font_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; + + /* Get the bitmap */ + HBITMAP bitmap = (HBITMAP)GetCurrentObject(dc, OBJ_BITMAP); + DIBSECTION dib = { 0 }; + GetObject(bitmap, sizeof(dib), &dib); + + /* Start new row if necessary */ + 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) { + 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) */ + arena_push_array_zero(arena, u32, diff * atlas_w); + atlas_h += diff; + } + + /* Set bounding box metrics (now that we know atlas x & y) */ + glyph->atlas_rect = { 0 }; + glyph->atlas_rect.x = (f32)out_offset_x; + glyph->atlas_rect.y = (f32)out_offset_y; + glyph->atlas_rect.width = (f32)tex_w; + glyph->atlas_rect.height = (f32)tex_h; + + /* Fill atlas */ + u64 in_pitch = dib.dsBm.bmWidthBytes / 4; + u32 *in_data = (u32 *)dib.dsBm.bmBits; + u32 *out_data = atlas_memory; + for (i32 y = 0; y < tex_h; ++y) { + u64 out_y = out_offset_y + y; + u64 in_y = bounding_box.top + y; + for (i32 x = 0; x < tex_w; ++x) { + u64 out_x = out_offset_x + x; + u64 in_x = bounding_box.left + x; + u32 *out_pixel = out_data + (out_x + (out_y * atlas_w)); + u32 *in_pixel = in_data + (in_x + (in_y * in_pitch)); + *out_pixel = RGBA(0xFF, 0xFF, 0xFF, *in_pixel & 0xFF); + } + } + out_offset_x += tex_w; + + /* Grow row height */ + if ((u32)tex_h > row_height) { + row_height = tex_h; + } + + /* Clear the render target */ + { + HGDIOBJ original = SelectObject(dc, GetStockObject(DC_PEN)); + SetDCPenColor(dc, bg_color); + SelectObject(dc, GetStockObject(DC_BRUSH)); + SetDCBrushColor(dc, bg_color); + Rectangle(dc, bounding_box.left, bounding_box.top, bounding_box.right, bounding_box.bottom); + SelectObject(dc, original); + } + } + + /* Construct indices array */ + u16 *cache_indices = NULL; + if (cache_codes_count > 0) { + cache_indices = arena_push_array_zero(arena, u16, cache_codes_count); + font_face->GetGlyphIndices(cache_codes, cache_codes_count, cache_indices); + } + + /* Release */ + dwrite_gdi_interop->Release(); + rendering_params->Release(); + default_rendering_params->Release(); + // NOTE FROM ALLEN: We don't release font face because we intend to keep the font face around after the baking process + // font_face->Release(); + font_file->Release(); + //loader->Release(); + factory->Release(); + + /* Return */ + struct ttf_decode_result result = { 0 }; + result.glyphs = glyphs; + result.glyphs_count = glyph_count; + result.cache_indices = cache_indices; + result.image_data.pixels = (u32 *)atlas_memory; + result.image_data.width = (u32)atlas_w; + result.image_data.height = (u32)atlas_h; + return result; +} + +#endif