From 0f4d74d6147ee66c6bef2aa97ce48d139b58f7e0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 11 Jan 2015 21:06:57 +0000 Subject: [PATCH] ImFont::GetTextureData API allow to retrieve 8/32 bits data + lazily load defaults font Examples: OpenGL3 and DirectX11 back to using 32-bits texture solely for ease of integration. --- examples/directx11_example/main.cpp | 29 ++++++------- examples/directx9_example/main.cpp | 21 ++++----- examples/opengl3_example/main.cpp | 20 ++++----- examples/opengl_example/main.cpp | 17 +++----- imgui.cpp | 60 +++++++++++++++++++++----- imgui.h | 66 +++++++++++++++++------------ 6 files changed, 125 insertions(+), 88 deletions(-) diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index bee3602e..bf15c858 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -286,8 +286,7 @@ HRESULT InitDeviceD3D(HWND hWnd) \ float4 main(PS_INPUT input) : SV_Target\ {\ - float4 out_col = input.col; \ - out_col.w *= texture0.Sample(sampler0, input.uv).w; \ + float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ return out_col; \ }"; @@ -381,16 +380,18 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) void LoadFontTexture(ImFont* font) { - IM_ASSERT(font && font->IsLoaded()); + unsigned char* pixels; + int width, height; + font->GetTextureDataRGBA32(&pixels, &width, &height); // Create texture D3D11_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.Width = font->TexWidth; - desc.Height = font->TexHeight; + desc.Width = width; + desc.Height = height; desc.MipLevels = 1; desc.ArraySize = 1; - desc.Format = DXGI_FORMAT_A8_UNORM; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; @@ -398,15 +399,15 @@ void LoadFontTexture(ImFont* font) ID3D11Texture2D *pTexture = NULL; D3D11_SUBRESOURCE_DATA subResource; - subResource.pSysMem = font->TexPixels; - subResource.SysMemPitch = desc.Width * 1; + subResource.pSysMem = pixels; + subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); // Create texture view D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; ZeroMemory(&srvDesc, sizeof(srvDesc)); - srvDesc.Format = DXGI_FORMAT_A8_UNORM; + srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; @@ -464,10 +465,9 @@ void InitImGui() } } - // Load font - io.Font->LoadDefault(); + // Load font (optionally load a custom TTF font) //io.Font->LoadFromFileTTF("myfont.ttf", font_size_px, ImFont::GetGlyphRangesDefault()); - //io.Font->DisplayOffset.y += 0.0f; + //io.Font->DisplayOffset.y += 1.0f; LoadFontTexture(io.Font); // Create texture sampler @@ -569,11 +569,6 @@ int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, int) show_test_window ^= ImGui::Button("Test Window"); show_another_window ^= ImGui::Button("Another Window"); - static ImFont* font2 = NULL; - if (!font2) { font2 = new ImFont(); font2->LoadFromFileTTF("../../extra_fonts/ArialUni.ttf", 30.0f); LoadFontTexture(font2); } - ImGui::Image(font2->TexID, ImVec2((float)font2->TexWidth, (FLOAT)font2->TexHeight)); - //ImGui::GetWindowDrawList()->AddText(font2, 30.0f, ImGui::GetCursorScreenPos(), 0xFFFFFFFF, "Another font"); - // Calculate and show frame rate static float ms_per_frame[120] = { 0 }; static int ms_per_frame_idx = 0; diff --git a/examples/directx9_example/main.cpp b/examples/directx9_example/main.cpp index c6cc0125..0e846404 100644 --- a/examples/directx9_example/main.cpp +++ b/examples/directx9_example/main.cpp @@ -176,10 +176,13 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) void LoadFontTexture(ImFont* font) { - IM_ASSERT(font && font->IsLoaded()); + unsigned char* pixels; + int width, height; + int bytes_per_pixel; + font->GetTextureDataAlpha8(&pixels, &width, &height, &bytes_per_pixel); LPDIRECT3DTEXTURE9 pTexture = NULL; - if (D3DXCreateTexture(g_pd3dDevice, font->TexWidth, font->TexHeight, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8, D3DPOOL_DEFAULT, &pTexture) < 0) + if (D3DXCreateTexture(g_pd3dDevice, width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8, D3DPOOL_DEFAULT, &pTexture) < 0) { IM_ASSERT(0); return; @@ -192,8 +195,8 @@ void LoadFontTexture(ImFont* font) IM_ASSERT(0); return; } - for (int y = 0; y < font->TexHeight; y++) - memcpy((unsigned char *)tex_locked_rect.pBits + tex_locked_rect.Pitch * y, font->TexPixels + font->TexWidth * y, font->TexWidth); + for (int y = 0; y < height; y++) + memcpy((unsigned char *)tex_locked_rect.pBits + tex_locked_rect.Pitch * y, pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel)); pTexture->UnlockRect(0); font->TexID = (void *)pTexture; @@ -237,10 +240,9 @@ void InitImGui() return; } - // Load font - io.Font->LoadDefault(); + // Load font (optionally load a custom TTF font) //io.Font->LoadFromFileTTF("myfont.ttf", font_size_px, ImFont::GetGlyphRangesDefault()); - //io.Font->DisplayOffset.y += 0.0f; + //io.Font->DisplayOffset.y += 1.0f; LoadFontTexture(io.Font); } @@ -327,11 +329,6 @@ int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, int) show_test_window ^= ImGui::Button("Test Window"); show_another_window ^= ImGui::Button("Another Window"); - static ImFont* font2 = NULL; - if (!font2) { font2 = new ImFont(); font2->LoadFromFileTTF("../../extra_fonts/ArialUni.ttf", 30.0f); LoadFontTexture(font2); } - ImGui::Image(font2->TexID, ImVec2((float)font2->TexWidth, (FLOAT)font2->TexHeight)); - //ImGui::GetWindowDrawList()->AddText(font2, 30.0f, ImGui::GetCursorScreenPos(), 0xFFFFFFFF, "Another font"); - // Calculate and show frame rate static float ms_per_frame[120] = { 0 }; static int ms_per_frame_idx = 0; diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 5a454384..0f0763b0 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -198,8 +198,7 @@ void InitGL() "out vec4 Out_Color;\n" "void main()\n" "{\n" - " Out_Color = Frag_Color;\n" - " Out_Color.w *= texture( Texture, Frag_UV.st).x;\n" + " Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n" "}\n"; shader_handle = glCreateProgram(); @@ -239,14 +238,17 @@ void InitGL() void LoadFontTexture(ImFont* font) { - IM_ASSERT(font && font->IsLoaded()); + unsigned char* pixels; + int width, height; + font->GetTextureDataRGBA32(&pixels, &width, &height); GLuint tex_id; glGenTextures(1, &tex_id); glBindTexture(GL_TEXTURE_2D, tex_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, font->TexWidth, font->TexHeight, 0, GL_RED, GL_UNSIGNED_BYTE, font->TexPixels); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + font->TexID = (void *)(intptr_t)tex_id; } @@ -277,10 +279,9 @@ void InitImGui() io.SetClipboardTextFn = ImImpl_SetClipboardTextFn; io.GetClipboardTextFn = ImImpl_GetClipboardTextFn; - // Load font - io.Font->LoadDefault(); + // Load font (optionally load a custom TTF font) //io.Font->LoadFromFileTTF("myfont.ttf", font_size_px, ImFont::GetGlyphRangesDefault()); - //io.Font->DisplayOffset.y += 0.0f; + //io.Font->DisplayOffset.y += 1.0f; LoadFontTexture(io.Font); } @@ -341,11 +342,6 @@ int main(int argc, char** argv) show_test_window ^= ImGui::Button("Test Window"); show_another_window ^= ImGui::Button("Another Window"); - static ImFont* font2 = NULL; - if (!font2) { font2 = new ImFont(); font2->LoadFromFileTTF("../../extra_fonts/ArialUni.ttf", 30.0f); LoadFontTexture(font2); } - ImGui::Image(font2->TexID, ImVec2((float)font2->TexWidth, (float)font2->TexHeight)); - //ImGui::GetWindowDrawList()->AddText(font2, 30.0f, ImGui::GetCursorScreenPos(), 0xFFFFFFFF, "Another font"); - // Calculate and show frame rate static float ms_per_frame[120] = { 0 }; static int ms_per_frame_idx = 0; diff --git a/examples/opengl_example/main.cpp b/examples/opengl_example/main.cpp index 40f26870..18ffee38 100644 --- a/examples/opengl_example/main.cpp +++ b/examples/opengl_example/main.cpp @@ -148,14 +148,17 @@ void InitGL() void LoadFontTexture(ImFont* font) { - IM_ASSERT(font && font->IsLoaded()); + unsigned char* pixels; + int width, height; + font->GetTextureDataAlpha8(&pixels, &width, &height); GLuint tex_id; glGenTextures(1, &tex_id); glBindTexture(GL_TEXTURE_2D, tex_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, font->TexWidth, font->TexHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, font->TexPixels); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels); + font->TexID = (void *)(intptr_t)tex_id; } @@ -186,10 +189,9 @@ void InitImGui() io.SetClipboardTextFn = ImImpl_SetClipboardTextFn; io.GetClipboardTextFn = ImImpl_GetClipboardTextFn; - // Load font - io.Font->LoadDefault(); + // Load font (optionally load a custom TTF font) //io.Font->LoadFromFileTTF("myfont.ttf", font_size_px, ImFont::GetGlyphRangesDefault()); - //io.Font->DisplayOffset.y += 0.0f; + //io.Font->DisplayOffset.y += 1.0f; LoadFontTexture(io.Font); } @@ -249,11 +251,6 @@ int main(int argc, char** argv) show_test_window ^= ImGui::Button("Test Window"); show_another_window ^= ImGui::Button("Another Window"); - static ImFont* font2 = NULL; - if (!font2) { font2 = new ImFont(); font2->LoadFromFileTTF("../../extra_fonts/ArialUni.ttf", 30.0f); LoadFontTexture(font2); } - ImGui::Image(font2->TexID, ImVec2((float)font2->TexWidth, (float)font2->TexHeight)); - //ImGui::GetWindowDrawList()->AddText(font2, 30.0f, ImGui::GetCursorScreenPos(), 0xFFFFFFFF, "Another font"); - // Calculate and show frame rate static float ms_per_frame[120] = { 0 }; static int ms_per_frame_idx = 0; diff --git a/imgui.cpp b/imgui.cpp index 7cc2c749..7f8aed8f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6208,7 +6208,8 @@ ImFont::ImFont() DisplayOffset = ImVec2(0.5f, 0.5f); FallbackChar = (ImWchar)'?'; - TexPixels = NULL; + TexPixelsAlpha8 = NULL; + TexPixelsRGBA32 = NULL; Clear(); } @@ -6217,15 +6218,55 @@ ImFont::~ImFont() Clear(); } +void ImFont::GetTextureDataAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +{ + // Lazily load default font + if (!IsLoaded()) + LoadDefault(); + + *out_pixels = TexPixelsAlpha8; + if (out_width) *out_width = TexWidth; + if (out_height) *out_height = TexHeight; + if (out_bytes_per_pixel) *out_bytes_per_pixel = 1; +} + +void ImFont::GetTextureDataRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +{ + // Lazily convert to RGBA32 format + // Although it is likely to be the most commonly used format, our font rendering is 8 bpp + if (!TexPixelsRGBA32) + { + unsigned char* pixels; + GetTextureDataAlpha8(&pixels, NULL, NULL); + TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc(TexWidth * TexHeight * 4); + const unsigned char* src = pixels; + unsigned int* dst = TexPixelsRGBA32; + for (int n = TexWidth * TexHeight; n > 0; n--) + *dst++ = ((*src++) << 24) | 0x00FFFFFF; + } + + *out_pixels = (unsigned char*)TexPixelsRGBA32; + if (out_width) *out_width = TexWidth; + if (out_height) *out_height = TexHeight; + if (out_bytes_per_pixel) *out_bytes_per_pixel = 4; +} + +void ImFont::ClearTextureData() +{ + if (TexPixelsAlpha8) + ImGui::MemFree(TexPixelsAlpha8); + if (TexPixelsRGBA32) + ImGui::MemFree(TexPixelsRGBA32); + TexPixelsAlpha8 = NULL; + TexPixelsRGBA32 = NULL; +} + void ImFont::Clear() { - if (TexPixels) - ImGui::MemFree(TexPixels); - DisplayOffset = ImVec2(0.5f, 0.5f); + ClearTextureData(); TexID = NULL; - TexPixels = NULL; TexWidth = TexHeight = 0; TexExtraDataPos = TexUvWhitePixel = ImVec2(0, 0); @@ -6392,7 +6433,6 @@ bool ImFont::LoadFromMemoryTTF(const void* data, size_t data_size, float size { TexWidth = 512; TexHeight = 0; - TexPixels = NULL; const int max_tex_height = 1024*16; stbtt_pack_context spc; int ret = stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL); @@ -6423,11 +6463,11 @@ bool ImFont::LoadFromMemoryTTF(const void* data, size_t data_size, float size if (rects[i].was_packed) tex_h = ImMax(tex_h, rects[i].y + rects[i].h); TexHeight = ImUpperPowerOfTwo(tex_h); - TexPixels = (unsigned char*)ImGui::MemRealloc(TexPixels, TexWidth * TexHeight); - memset(TexPixels, 0, TexWidth * TexHeight); + TexPixelsAlpha8 = (unsigned char*)ImGui::MemRealloc(TexPixelsAlpha8, TexWidth * TexHeight); + memset(TexPixelsAlpha8, 0, TexWidth * TexHeight); // Render characters - spc.pixels = TexPixels; + spc.pixels = TexPixelsAlpha8; spc.height = TexHeight; ret = stbtt_PackFontRangesRenderIntoRects(&spc, &ttf_info, ranges.begin(), ranges.size(), rects); stbtt_PackEnd(&spc); @@ -6476,7 +6516,7 @@ bool ImFont::LoadFromMemoryTTF(const void* data, size_t data_size, float size ImGui::MemFree(ranges[i].chardata_for_range); // Draw white pixel and make UV points to it - TexPixels[0] = TexPixels[1] = TexPixels[TexWidth+0] = TexPixels[TexWidth+1] = 0xFF; + TexPixelsAlpha8[0] = TexPixelsAlpha8[1] = TexPixelsAlpha8[TexWidth+0] = TexPixelsAlpha8[TexWidth+1] = 0xFF; TexUvWhitePixel = ImVec2((TexExtraDataPos.x + 0.5f) / TexWidth, (TexExtraDataPos.y + 0.5f) / TexHeight); return true; diff --git a/imgui.h b/imgui.h index 3723eacb..495591a2 100644 --- a/imgui.h +++ b/imgui.h @@ -739,39 +739,26 @@ struct ImDrawList }; // TTF font loading and rendering -// NB: kerning pair are not supported (because some ImGui code does per-character CalcTextSize calls, need to turn it into something more state-ful to allow for kerning) +// - ImGui automatically loads a default embedded font for you +// - Call GetTextureData() to retrieve pixels data so you can upload the texture to your graphics system. +// - Store your texture handle in 'TexID'. It will be passed back to you when rendering ('texture_id' field in ImDrawCmd) +// (NB: kerning isn't supported. At the moment some ImGui code does per-character CalcTextSize calls, need something more state-ful) struct ImFont { // Settings float Scale; // = 1.0f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() ImVec2 DisplayOffset; // = (0.0f,0.0f) // Offset font rendering by xx pixels ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. + ImTextureID TexID; // = 0 // After loading texture, store your texture handle here (ignore if you aren't using multiple fonts/textures) - // Texture data: user is in charge of copying the pixels into a GPU texture. - // You can set 'TexID' to uniquely identify your texture. TexId is copied to the ImDrawCmd structure which you receive during rendering. - ImTextureID TexID; // User reference to texture used by the font (ignore if you aren't using multiple fonts/textures) - unsigned char* TexPixels; // 1 byte, 1 component per pixel. Total byte size of TexWidth * TexHeight - int TexWidth; - int TexHeight; - - // [Internal] - ImVec2 TexExtraDataPos; // Position of our rectangle where we draw non-font graphics - ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel (part of the TexExtraData block) - - struct Glyph - { - ImWchar Codepoint; - signed short XAdvance; - signed short Width, Height; - signed short XOffset, YOffset; - float U0, V0, U1, V1; // Texture coordinates - }; - - // Runtime data - float FontSize; // Height of characters - ImVector Glyphs; - ImVector IndexLookup; - const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) + // Retrieve texture data + // User is in charge of copying the pixels into graphics memory, then set 'TexID'. + // RGBA32 format is provided for convenience and high compatibility, but note that all RGB pixels are white. + // If you intend to use large font it may be pref + // NB: the data is invalidated as soon as you call a Load* function. + IMGUI_API void GetTextureDataRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel + IMGUI_API void GetTextureDataAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel + IMGUI_API void ClearTextureData(); // Save RAM once the texture has been copied to graphics memory. // Methods IMGUI_API ImFont(); @@ -781,8 +768,9 @@ struct ImFont IMGUI_API bool LoadFromMemoryTTF(const void* data, size_t data_size, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0); IMGUI_API void Clear(); IMGUI_API void BuildLookupTable(); + struct Glyph; IMGUI_API const Glyph* FindGlyph(unsigned short c) const; - IMGUI_API bool IsLoaded() const { return TexPixels != NULL && !Glyphs.empty(); } + IMGUI_API bool IsLoaded() const { return !Glyphs.empty(); } // Retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) static IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin @@ -795,6 +783,30 @@ struct ImFont IMGUI_API ImVec2 CalcTextSizeW(float size, float max_width, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL) const; // wchar IMGUI_API void RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, ImDrawVert*& out_vertices, float wrap_width = 0.0f) const; IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; + + // Texture data + // Access via GetTextureData() which will load the font if not loaded + unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight + unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 + int TexWidth; + int TexHeight; + ImVec2 TexExtraDataPos; // Position of our rectangle where we draw non-font graphics + ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel (part of the TexExtraData block) + + struct Glyph + { + ImWchar Codepoint; + signed short XAdvance; + signed short Width, Height; + signed short XOffset, YOffset; + float U0, V0, U1, V1; // Texture coordinates + }; + + // Runtime data + float FontSize; // Height of characters + ImVector Glyphs; + ImVector IndexLookup; + const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) }; //---- Include imgui_user.h at the end of imgui.h