@@ -108,7 +108,7 @@ static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_
108108// -------------------------------------------------------------------------
109109
110110#define FT_CEIL (X ) (((X + 63 ) & -64 ) / 64 ) // From SDL_ttf: Handy routines for converting from fixed point
111- #define FT_SCALEFACTOR 64 .0f
111+ #define FT_SCALEFACTOR 64 .0f // For converting from/to 26.6 factionals
112112
113113// Glyph metrics:
114114// --------------
@@ -435,19 +435,58 @@ static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* s
435435
436436 FT_New_Size (bd_font_data->FtFace , &bd_baked_data->FtSize );
437437 FT_Activate_Size (bd_baked_data->FtSize );
438-
439- // Vuhdo 2017: "I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
440- // is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
441- // FT_Set_Pixel_Sizes() doesn't seem to get us the same result."
442- // (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL)
443438 const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity ;
444- FT_Size_RequestRec req;
445- req.type = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
446- req.width = 0 ;
447- req.height = (uint32_t )(size * 64 * rasterizer_density);
448- req.horiResolution = 0 ;
449- req.vertResolution = 0 ;
450- FT_Request_Size (bd_font_data->FtFace , &req);
439+ if (((bd_font_data->FtFace ->face_flags & FT_FACE_FLAG_FIXED_SIZES) != 0 ) && ((bd_font_data->FtFace ->face_flags & FT_FACE_FLAG_SCALABLE) == 0 ) && ((bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) != 0 ))
440+ {
441+ IM_ASSERT (bd_font_data->FtFace ->num_fixed_sizes > 0 );
442+
443+ // Loop over sizes and pick the closest, larger (or better equal) size.
444+ int best_index = 0 ;
445+ float best_height = bd_font_data->FtFace ->available_sizes [best_index].y_ppem / FT_SCALEFACTOR;
446+ for (int i = 1 ; i < bd_font_data->FtFace ->num_fixed_sizes ; i++)
447+ {
448+ const float cur_height = bd_font_data->FtFace ->available_sizes [i].y_ppem / FT_SCALEFACTOR;
449+ // TODO: is this overkill?
450+ // TODO: is-float-close with epsilon param would be nice, maybe
451+ if (ImFabs (cur_height - size) < 0 .001f )
452+ {
453+ best_index = i;
454+ break ;
455+ }
456+ else if (cur_height < size)
457+ {
458+ if (best_height < cur_height)
459+ {
460+ best_index = i;
461+ best_height = cur_height;
462+ }
463+ }
464+ else
465+ {
466+ if (best_height > cur_height)
467+ {
468+ best_index = i;
469+ best_height = cur_height;
470+ }
471+ }
472+ }
473+ FT_Select_Size (bd_font_data->FtFace , best_index);
474+ }
475+ else
476+ {
477+ // Vuhdo 2017: "I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
478+ // is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
479+ // FT_Set_Pixel_Sizes() doesn't seem to get us the same result."
480+ // (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL)
481+
482+ FT_Size_RequestRec req;
483+ req.type = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
484+ req.width = 0 ;
485+ req.height = (uint32_t )(size * FT_SCALEFACTOR * rasterizer_density);
486+ req.horiResolution = 0 ;
487+ req.vertResolution = 0 ;
488+ FT_Request_Size (bd_font_data->FtFace , &req);
489+ }
451490
452491 // Output
453492 if (src->MergeMode == false )
@@ -497,9 +536,21 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
497536 FT_Face face = bd_font_data->FtFace ;
498537 FT_GlyphSlot slot = face->glyph ;
499538 const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity ;
539+ float bitmap_x_scale = 1 .f ;
540+ float bitmap_y_scale = 1 .f ;
541+ if (((face->face_flags & FT_FACE_FLAG_FIXED_SIZES) != 0 ) && ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0 ) && ((bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) != 0 ))
542+ {
543+ // TODO: what if this just means invisible?
544+ IM_ASSERT (bd_font_data->FtFace ->size ->metrics .x_ppem > 0 );
545+ IM_ASSERT (bd_font_data->FtFace ->size ->metrics .y_ppem > 0 );
546+
547+ // Scale fixed size bitmap to target size
548+ bitmap_x_scale = baked->Size / bd_font_data->FtFace ->size ->metrics .x_ppem ;
549+ bitmap_y_scale = baked->Size / bd_font_data->FtFace ->size ->metrics .y_ppem ;
550+ }
500551
501552 // Load metrics only mode
502- const float advance_x = (slot->advance .x / FT_SCALEFACTOR) / rasterizer_density;
553+ const float advance_x = (( slot->advance .x / FT_SCALEFACTOR) * bitmap_x_scale ) / rasterizer_density;
503554 if (out_advance_x != NULL )
504555 {
505556 IM_ASSERT (out_glyph == NULL );
@@ -514,9 +565,9 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
514565 if (error != 0 || ft_bitmap == nullptr )
515566 return false ;
516567
517- const int w = (int )ft_bitmap->width ;
518- const int h = (int )ft_bitmap->rows ;
519- const bool is_visible = (w != 0 && h != 0 );
568+ const int bitmap_w = (int )ft_bitmap->width ;
569+ const int bitmap_h = (int )ft_bitmap->rows ;
570+ const bool is_visible = (bitmap_w != 0 && bitmap_h != 0 );
520571
521572 // Prepare glyph
522573 out_glyph->Codepoint = codepoint;
@@ -525,6 +576,11 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
525576 // Pack and retrieve position inside texture atlas
526577 if (is_visible)
527578 {
579+ const int w = (int )ImFloor (bitmap_w * bitmap_x_scale);
580+ const int h = (int )ImFloor (bitmap_h * bitmap_y_scale);
581+ IM_ASSERT (!((h < bitmap_h && w > bitmap_w) || (h > bitmap_h && w < bitmap_w))); // Can't up AND downscale at the same time // TODO: or can we?
582+ const bool down_scaling = h < bitmap_h || w < bitmap_w;
583+
528584 ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect (atlas, w, h);
529585 if (pack_id == ImFontAtlasRectId_Invalid)
530586 {
@@ -534,10 +590,35 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
534590 }
535591 ImTextureRect* r = ImFontAtlasPackGetRect (atlas, pack_id);
536592
537- // Render pixels to our temporary buffer
538- atlas->Builder ->TempBuffer .resize (w * h * 4 );
593+ // Render pixels to our temporary buffer, while making sure we have space for an extra copy used during downscaling.
594+ atlas->Builder ->TempBuffer .resize (((down_scaling ? bitmap_w * bitmap_h : 0 ) + w * h) * 4 );
539595 uint32_t * temp_buffer = (uint32_t *)atlas->Builder ->TempBuffer .Data ;
540- ImGui_ImplFreeType_BlitGlyph (ft_bitmap, temp_buffer, w);
596+ // Blit (and convert) into the first bm_w * bm_h * 4 bytes.
597+ ImGui_ImplFreeType_BlitGlyph (ft_bitmap, temp_buffer, bitmap_w);
598+
599+ if (down_scaling)
600+ {
601+ uint32_t * dst_buffer = temp_buffer + bitmap_w * bitmap_h;
602+ // Perform downscale, from temp_buffer (bitmap_w * bitmap_h) to dst_buffer (w * h)
603+
604+ #if 1
605+ // Point Sampling / Nearest Neighbor
606+ for (int y = 0 ; y < h; y++)
607+ {
608+ const int bitmap_y = ImFloor (((y + 0 .5f ) * bitmap_h) / h);
609+ for (int x = 0 ; x < w; x++)
610+ {
611+ const int bitmap_x = ImFloor (((x + 0 .5f ) * bitmap_w) / w);
612+ dst_buffer[y * w + x] = temp_buffer[bitmap_y * bitmap_w + bitmap_x];
613+ }
614+ }
615+ #else
616+ // TODO: box scaling
617+ #endif
618+
619+ // Redirect to downscaled part of the buffer
620+ temp_buffer = dst_buffer;
621+ }
541622
542623 const float ref_size = baked->ContainerFont ->Sources [0 ]->SizePixels ;
543624 const float offsets_scale = (ref_size != 0 .0f ) ? (baked->Size / ref_size) : 1 .0f ;
@@ -551,8 +632,8 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
551632 float recip_v = 1 .0f / rasterizer_density;
552633
553634 // Register glyph
554- float glyph_off_x = ( float )face->glyph ->bitmap_left ;
555- float glyph_off_y = ( float )-face->glyph ->bitmap_top ;
635+ float glyph_off_x = ImFloor (( float )face->glyph ->bitmap_left * bitmap_x_scale) ;
636+ float glyph_off_y = ImFloor (( float )-face->glyph ->bitmap_top * bitmap_y_scale) ;
556637 out_glyph->X0 = glyph_off_x * recip_h + font_off_x;
557638 out_glyph->Y0 = glyph_off_y * recip_v + font_off_y;
558639 out_glyph->X1 = (glyph_off_x + w) * recip_h + font_off_x;
0 commit comments