360 lines
17 KiB
C++
360 lines
17 KiB
C++
/*
|
|
* Mr. 4th Dimention - Allen Webster
|
|
*
|
|
* 12.12.2014
|
|
*
|
|
* Win32 font rendering for nicer fonts
|
|
*
|
|
*/
|
|
|
|
// TOP
|
|
|
|
struct GMetrics{
|
|
f32 advance;
|
|
f32 xoff;
|
|
f32 xoff2;
|
|
};
|
|
|
|
internal b32
|
|
win32_glyph_metrics(HDC dc, int code, GMetrics *gmetrics){
|
|
b32 result = false;
|
|
ABCFLOAT abc = {0};
|
|
INT width;
|
|
|
|
if (GetCharWidth32W(dc, code, code, &width)){
|
|
gmetrics->advance = (f32)width;
|
|
if (GetCharABCWidthsFloat(dc, code, code, &abc)){
|
|
gmetrics->xoff = abc.abcfA;
|
|
gmetrics->xoff2 = (abc.abcfA + abc.abcfB);
|
|
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
internal i32
|
|
win32_draw_font_load(Partition *part,
|
|
Render_Font *font_out,
|
|
char *filename_untranslated,
|
|
char *fontname,
|
|
i32 pt_size,
|
|
i32 tab_width,
|
|
i32 oversample,
|
|
b32 store_texture){
|
|
|
|
char space_[1024];
|
|
String filename = make_fixed_width_string(space_);
|
|
b32 translate_success = sysshared_to_binary_path(&filename, filename_untranslated);
|
|
|
|
i32 result = 0;
|
|
|
|
if (translate_success){
|
|
HDC dc = GetDC(win32vars.window_handle);
|
|
|
|
AddFontResourceEx(filename.str, FR_PRIVATE, 0);
|
|
|
|
HFONT font_handle = CreateFont(
|
|
pt_size,
|
|
0,
|
|
0,
|
|
0,
|
|
FW_NORMAL,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE,
|
|
ANSI_CHARSET,
|
|
OUT_DEFAULT_PRECIS,
|
|
CLIP_DEFAULT_PRECIS,
|
|
DEFAULT_QUALITY,
|
|
FF_DONTCARE | DEFAULT_PITCH,
|
|
fontname
|
|
);
|
|
HBITMAP bmp_handle = CreateBitmap(
|
|
pt_size*2, pt_size*2,
|
|
4, 32,
|
|
0
|
|
);
|
|
|
|
|
|
if (font_handle != 0 && bmp_handle != 0){
|
|
SelectObject(dc, font_handle);
|
|
SelectObject(dc, bmp_handle);
|
|
|
|
memset(font_out, 0, sizeof(*font_out));
|
|
|
|
TEXTMETRIC metric;
|
|
if (GetTextMetrics(dc, &metric)){
|
|
|
|
font_out->height = (i32)(metric.tmHeight + metric.tmExternalLeading);
|
|
font_out->ascent = (i32)(metric.tmAscent);
|
|
font_out->descent = (i32)(metric.tmDescent);
|
|
font_out->line_skip = (i32)(metric.tmExternalLeading);
|
|
|
|
if (!store_texture){
|
|
result = 1;
|
|
}
|
|
else{
|
|
Temp_Memory temp = begin_temp_memory(part);
|
|
|
|
i32 tex_width = pt_size*16;
|
|
i32 tex_height = pt_size*16;
|
|
void *block = sysshared_push_block(part, tex_width * tex_height);
|
|
|
|
font_out->tex_width = tex_width;
|
|
font_out->tex_height = tex_height;
|
|
|
|
result = 1;
|
|
/////////////////////////////////////////////////////////////////
|
|
stbtt_pack_context spc_;
|
|
|
|
int pack_result;
|
|
{
|
|
stbtt_pack_context *spc = &spc_;
|
|
unsigned char *pixels = (u8*)block;
|
|
int pw = tex_width;
|
|
int ph = tex_height;
|
|
int stride_in_bytes = tex_width;
|
|
int padding = 1;
|
|
void *alloc_context = part;
|
|
|
|
{
|
|
stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context);
|
|
int num_nodes = pw - padding;
|
|
stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context);
|
|
|
|
if (context == NULL || nodes == NULL) {
|
|
if (context != NULL) STBTT_free(context, alloc_context);
|
|
if (nodes != NULL) STBTT_free(nodes , alloc_context);
|
|
pack_result = 0;
|
|
goto packbegin_end;
|
|
}
|
|
|
|
spc->user_allocator_context = alloc_context;
|
|
spc->width = pw;
|
|
spc->height = ph;
|
|
spc->pixels = pixels;
|
|
spc->pack_info = context;
|
|
spc->nodes = nodes;
|
|
spc->padding = padding;
|
|
spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
|
|
spc->h_oversample = 1;
|
|
spc->v_oversample = 1;
|
|
|
|
stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
|
|
|
|
STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
|
|
|
|
pack_result = 1;
|
|
goto packbegin_end;
|
|
}
|
|
}
|
|
packbegin_end:;
|
|
|
|
if (pack_result){
|
|
|
|
int pack_font_range_result;
|
|
|
|
{
|
|
stbtt_pack_range range;
|
|
range.first_unicode_char_in_range = 0;
|
|
range.num_chars_in_range = 128;
|
|
range.chardata_for_range = font_out->chardata;
|
|
range.font_size = STBTT_POINT_SIZE((f32)pt_size);
|
|
|
|
stbtt_pack_context *spc = &spc_;
|
|
stbtt_pack_range *ranges = ⦥
|
|
int num_ranges = 1;
|
|
|
|
{
|
|
int i,j,k,n, return_value = 1;
|
|
stbrp_context *context = (stbrp_context *) spc->pack_info;
|
|
stbrp_rect *rects;
|
|
|
|
// flag all characters as NOT packed
|
|
for (i=0; i < num_ranges; ++i)
|
|
for (j=0; j < ranges[i].num_chars_in_range; ++j)
|
|
ranges[i].chardata_for_range[j].x0 =
|
|
ranges[i].chardata_for_range[j].y0 =
|
|
ranges[i].chardata_for_range[j].x1 =
|
|
ranges[i].chardata_for_range[j].y1 = 0;
|
|
|
|
n = 0;
|
|
for (i=0; i < num_ranges; ++i)
|
|
n += ranges[i].num_chars_in_range;
|
|
|
|
rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
|
|
if (rects == NULL){
|
|
pack_font_range_result = 0;
|
|
goto pack_font_range_end;
|
|
}
|
|
|
|
//info.userdata = spc->user_allocator_context;
|
|
//stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
|
|
k=0;
|
|
for (i=0; i < num_ranges; ++i) {
|
|
//float fh = ranges[i].font_size;
|
|
//float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh);
|
|
for (j=0; j < ranges[i].num_chars_in_range; ++j) {
|
|
int w,h;
|
|
|
|
GMetrics gmetrics;
|
|
|
|
if (!win32_glyph_metrics(dc, ranges[i].first_unicode_char_in_range + j, &gmetrics)){
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
w = CEIL32(gmetrics.xoff2 - gmetrics.xoff);
|
|
h = font_out->ascent + font_out->descent;
|
|
|
|
rects[k].w = (stbrp_coord) (w + spc->padding);
|
|
rects[k].h = (stbrp_coord) (h + spc->padding);
|
|
++k;
|
|
}
|
|
}
|
|
|
|
stbrp_pack_rects(context, rects, k);
|
|
|
|
k = 0;
|
|
for (i=0; i < num_ranges; ++i) {
|
|
//float fh = ranges[i].font_size;
|
|
//float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh);
|
|
for (j=0; j < ranges[i].num_chars_in_range; ++j) {
|
|
stbrp_rect *r = &rects[k];
|
|
if (r->was_packed) {
|
|
stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
|
|
//int glyph = stbtt_FindGlyphIndex(&info, ranges[i].first_unicode_char_in_range + j);
|
|
int code = ranges[i].first_unicode_char_in_range + j;
|
|
stbrp_coord pad = (stbrp_coord) spc->padding;
|
|
|
|
// pad on left and top
|
|
r->x += pad;
|
|
r->y += pad;
|
|
r->w -= pad;
|
|
r->h -= pad;
|
|
|
|
#if 0
|
|
int advance, lsb, x0,y0,x1,y1;
|
|
|
|
stbtt_GetGlyphHMetrics(&info, glyph, &advance, &lsb);
|
|
stbtt_GetGlyphBitmapBox(&info, glyph,
|
|
scale * spc->h_oversample,
|
|
scale * spc->v_oversample,
|
|
&x0,&y0,&x1,&y1);
|
|
stbtt_MakeGlyphBitmapSubpixel(&info,
|
|
spc->pixels + r->x + r->y*spc->stride_in_bytes,
|
|
r->w - spc->h_oversample+1,
|
|
r->h - spc->v_oversample+1,
|
|
spc->stride_in_bytes,
|
|
scale * spc->h_oversample,
|
|
scale * spc->v_oversample,
|
|
0,0,
|
|
glyph);
|
|
#else
|
|
float advance, x0, y0, x1, y1;
|
|
|
|
GMetrics gmetrics;
|
|
if (!win32_glyph_metrics(dc, code, &gmetrics)){
|
|
result = false;
|
|
break;
|
|
}
|
|
|
|
advance = gmetrics.advance;
|
|
x0 = gmetrics.xoff;
|
|
y0 = -font_out->ascent;
|
|
x1 = gmetrics.xoff2;
|
|
y1 = font_out->descent;
|
|
|
|
#endif
|
|
|
|
bc->x0 = (stbtt_int16) r->x;
|
|
bc->y0 = (stbtt_int16) r->y;
|
|
bc->x1 = (stbtt_int16) (r->x + r->w);
|
|
bc->y1 = (stbtt_int16) (r->y + r->h);
|
|
bc->xadvance = (float) advance;
|
|
bc->xoff = (float) x0;
|
|
bc->yoff = (float) y0;
|
|
bc->xoff2 = (float) (x0 + r->w);
|
|
bc->yoff2 = (float) (y0 + r->h);
|
|
} else {
|
|
return_value = 0; // if any fail, report failure
|
|
}
|
|
|
|
++k;
|
|
}
|
|
}
|
|
|
|
STBTT_free(rects, spc->user_allocator_context);
|
|
pack_font_range_result = return_value;
|
|
goto pack_font_range_end;
|
|
}
|
|
}
|
|
pack_font_range_end:;
|
|
|
|
|
|
if (!pack_font_range_result){
|
|
result = 0;
|
|
}
|
|
|
|
{
|
|
stbtt_pack_context *spc = &spc_;
|
|
{
|
|
STBTT_free(spc->nodes , spc->user_allocator_context);
|
|
STBTT_free(spc->pack_info, spc->user_allocator_context);
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
result = 0;
|
|
}
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
if (result){
|
|
GLuint font_tex;
|
|
glGenTextures(1, &font_tex);
|
|
glBindTexture(GL_TEXTURE_2D, font_tex);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
memset(block, 0xFF, tex_width*tex_height);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex_width, tex_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, block);
|
|
|
|
font_out->tex = font_tex;
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
font_out->chardata['\r'] = font_out->chardata[' '];
|
|
font_out->chardata['\n'] = font_out->chardata[' '];
|
|
font_out->chardata['\t'] = font_out->chardata[' '];
|
|
font_out->chardata['\t'].xadvance *= tab_width;
|
|
|
|
for (u8 code_point = 0; code_point < 128; ++code_point){
|
|
font_out->advance_data[code_point] = font_out->chardata[code_point].xadvance;
|
|
}
|
|
|
|
i32 max_advance = metric.tmMaxCharWidth;
|
|
font_out->advance = max_advance - 1;
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
}
|
|
}
|
|
|
|
DeleteObject(font_handle);
|
|
DeleteObject(bmp_handle);
|
|
}
|
|
|
|
ReleaseDC(win32vars.window_handle, dc);
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
// BOTTOM
|
|
|
|
|