Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 155 additions & 22 deletions misc/freetype/imgui_freetype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_
//-------------------------------------------------------------------------

#define FT_CEIL(X) (((X + 63) & -64) / 64) // From SDL_ttf: Handy routines for converting from fixed point
#define FT_SCALEFACTOR 64.0f
#define FT_SCALEFACTOR 64.0f // For converting from/to 26.6 factionals

// Glyph metrics:
// --------------
Expand Down Expand Up @@ -435,19 +435,59 @@ static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* s

FT_New_Size(bd_font_data->FtFace, &bd_baked_data->FtSize);
FT_Activate_Size(bd_baked_data->FtSize);

// 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'
// 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.
// FT_Set_Pixel_Sizes() doesn't seem to get us the same result."
// (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL)
const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
FT_Size_RequestRec req;
req.type = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
req.width = 0;
req.height = (uint32_t)(size * 64 * rasterizer_density);
req.horiResolution = 0;
req.vertResolution = 0;
FT_Request_Size(bd_font_data->FtFace, &req);
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))
{
IM_ASSERT(bd_font_data->FtFace->num_fixed_sizes > 0);

// Loop over sizes and pick the closest, larger (or better equal) size.
int best_index = 0;
float best_height = bd_font_data->FtFace->available_sizes[best_index].y_ppem / FT_SCALEFACTOR;
for (int i = 1; i < bd_font_data->FtFace->num_fixed_sizes; i++)
{
const float cur_height = bd_font_data->FtFace->available_sizes[i].y_ppem / FT_SCALEFACTOR;
// TODO: is this overkill?
// TODO: is-float-close with epsilon param would be nice, maybe
if (ImFabs(cur_height - size) < 0.001f)
{
best_index = i;
best_height = cur_height;
break;
}
else if (cur_height < size)
{
if (best_height < cur_height)
{
best_index = i;
best_height = cur_height;
}
}
else
{
if (best_height < size && best_height < cur_height)
{
best_index = i;
best_height = cur_height;
}
}
}
FT_Select_Size(bd_font_data->FtFace, best_index);
}
else
{
// 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'
// 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.
// FT_Set_Pixel_Sizes() doesn't seem to get us the same result."
// (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL)

FT_Size_RequestRec req;
req.type = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
req.width = 0;
req.height = (uint32_t)(size * FT_SCALEFACTOR * rasterizer_density);
req.horiResolution = 0;
req.vertResolution = 0;
FT_Request_Size(bd_font_data->FtFace, &req);
}

// Output
if (src->MergeMode == false)
Expand Down Expand Up @@ -475,6 +515,71 @@ static void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig
bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE()
}

static void RescaleBitmap(uint32_t* dst, const int dst_w, const int dst_h, uint32_t* src, const int src_w, const int src_h)
{
#if 0
// Point Sampling / Nearest Neighbor
for (int y = 0; y < dst_h; y++)
{
const int src_y = ImFloor(((y + 0.5f) * src_h) / dst_h);
for (int x = 0; x < dst_w; x++)
{
const int src_x = ImFloor(((x + 0.5f) * src_w) / dst_w);
dst[y * dst_w + x] = src[src_y * src_w + src_x];
}
}
#else
// Box sampling - Imagine projecting the new, smaller pixels onto the larger source, covering multiple pixel.
for (int y = 0; y < dst_h; y++)
{
for (int x = 0; x < dst_w; x++)
{
// We perform a weighted mean.
ImVec4 color;
float weight_sum = 0.f;

// Walk from upper edge to bottom edge (vertical)
const float edge_up = ((float)y * src_h) / dst_h;
const float edge_down = ((y + 1.f) * src_h) / dst_h;
for (float frac_pos_y = edge_up; frac_pos_y < edge_down;)
{
const int src_y = (int)ImFloor(frac_pos_y); IM_ASSERT(src_y < src_h);
const float frac_y = 1.f - (frac_pos_y - src_y);

// Walk from left edge to right edge (horizontal)
const float edge_left = ((float)x * src_w) / dst_w;
const float edge_right = ((x + 1.f) * src_w) / dst_w;
for (float frac_pos_x = edge_left; frac_pos_x < edge_right;)
{
const int src_x = (int)ImFloor(frac_pos_x); IM_ASSERT(src_x < src_w);
const float frac_x = 1.f - (frac_pos_x - src_x);

const float src_pixel_weight = frac_x * frac_y;

const ImVec4 pixel_color = ImGui::ColorConvertU32ToFloat4(src[src_y * src_w + src_x]);
color.x += pixel_color.x * src_pixel_weight;
color.y += pixel_color.y * src_pixel_weight;
color.z += pixel_color.z * src_pixel_weight;
color.w += pixel_color.w * src_pixel_weight;
weight_sum += src_pixel_weight;

frac_pos_x += frac_x;
}

frac_pos_y += frac_y;
}

color.x /= weight_sum;
color.y /= weight_sum;
color.z /= weight_sum;
color.w /= weight_sum;

dst[y * dst_w + x] = ImGui::ColorConvertFloat4ToU32(color);
}
}
#endif
}

static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x)
{
ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;
Expand All @@ -497,9 +602,21 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
FT_Face face = bd_font_data->FtFace;
FT_GlyphSlot slot = face->glyph;
const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
float bitmap_x_scale = 1.f;
float bitmap_y_scale = 1.f;
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))
{
// TODO: what if this just means invisible?
IM_ASSERT(bd_font_data->FtFace->size->metrics.x_ppem > 0);
IM_ASSERT(bd_font_data->FtFace->size->metrics.y_ppem > 0);

// Scale fixed size bitmap to target size
bitmap_x_scale = baked->Size / bd_font_data->FtFace->size->metrics.x_ppem;
bitmap_y_scale = baked->Size / bd_font_data->FtFace->size->metrics.y_ppem;
}

// Load metrics only mode
const float advance_x = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density;
const float advance_x = ((slot->advance.x / FT_SCALEFACTOR) * bitmap_x_scale) / rasterizer_density;
if (out_advance_x != NULL)
{
IM_ASSERT(out_glyph == NULL);
Expand All @@ -514,9 +631,9 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
if (error != 0 || ft_bitmap == nullptr)
return false;

const int w = (int)ft_bitmap->width;
const int h = (int)ft_bitmap->rows;
const bool is_visible = (w != 0 && h != 0);
const int bitmap_w = (int)ft_bitmap->width;
const int bitmap_h = (int)ft_bitmap->rows;
const bool is_visible = (bitmap_w != 0 && bitmap_h != 0);

// Prepare glyph
out_glyph->Codepoint = codepoint;
Expand All @@ -525,6 +642,10 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
// Pack and retrieve position inside texture atlas
if (is_visible)
{
const int w = (int)ImCeil(bitmap_w * bitmap_x_scale);
const int h = (int)ImCeil(bitmap_h * bitmap_y_scale);
const bool rescaling = h != bitmap_h || w != bitmap_w;

ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h);
if (pack_id == ImFontAtlasRectId_Invalid)
{
Expand All @@ -534,10 +655,22 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
}
ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id);

// Render pixels to our temporary buffer
atlas->Builder->TempBuffer.resize(w * h * 4);
// Render pixels to our temporary buffer, while making sure we have space for an extra copy used during rescaling.
atlas->Builder->TempBuffer.resize(((rescaling ? bitmap_w * bitmap_h : 0) + w * h) * 4);
uint32_t* temp_buffer = (uint32_t*)atlas->Builder->TempBuffer.Data;
ImGui_ImplFreeType_BlitGlyph(ft_bitmap, temp_buffer, w);
// Blit (and convert) into the first bitmap_w * bitmap_h * 4 bytes.
ImGui_ImplFreeType_BlitGlyph(ft_bitmap, temp_buffer, bitmap_w);

if (rescaling)
{
uint32_t* dst_buffer = temp_buffer + bitmap_w * bitmap_h;

// Perform rescale, from temp_buffer (bitmap_w * bitmap_h) to dst_buffer (w * h)
RescaleBitmap(dst_buffer, w, h, temp_buffer, bitmap_w, bitmap_h);

// Redirect to rescaled part of the buffer
temp_buffer = dst_buffer;
}

const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
Expand All @@ -551,8 +684,8 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf
float recip_v = 1.0f / rasterizer_density;

// Register glyph
float glyph_off_x = (float)face->glyph->bitmap_left;
float glyph_off_y = (float)-face->glyph->bitmap_top;
float glyph_off_x = ImCeil((float)face->glyph->bitmap_left * bitmap_x_scale);
float glyph_off_y = ImCeil((float)-face->glyph->bitmap_top * bitmap_y_scale);
out_glyph->X0 = glyph_off_x * recip_h + font_off_x;
out_glyph->Y0 = glyph_off_y * recip_v + font_off_y;
out_glyph->X1 = (glyph_off_x + w) * recip_h + font_off_x;
Expand Down
Loading