Browse Source

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.
ocornut 10 years ago
parent
commit
0f4d74d614

+ 12 - 17
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;

+ 9 - 12
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;

+ 8 - 12
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;

+ 7 - 10
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;

+ 50 - 10
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::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 (TexPixels)
-        ImGui::MemFree(TexPixels);
+    if (TexPixelsAlpha8)
+        ImGui::MemFree(TexPixelsAlpha8);
+    if (TexPixelsRGBA32)
+        ImGui::MemFree(TexPixelsRGBA32);
+    TexPixelsAlpha8 = NULL;
+    TexPixelsRGBA32 = NULL;
+}
 
+void    ImFont::Clear()
+{
     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;

+ 39 - 27
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<Glyph>     Glyphs;
-    ImVector<int>       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<Glyph>     Glyphs;
+	ImVector<int>       IndexLookup;
+	const Glyph*        FallbackGlyph;      // == FindGlyph(FontFallbackChar)
 };
 
 //---- Include imgui_user.h at the end of imgui.h