diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 05de7433e336..6da4b705e0ff 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -51,6 +51,7 @@ int main(int, char**) // Setup Dear ImGui style ImGui::StyleColorsDark(); + ImGui::StyleColorsLight(); //ImGui::StyleColorsClassic(); // Setup Platform/Renderer bindings @@ -67,7 +68,7 @@ int main(int, char**) //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 17.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); //IM_ASSERT(font != NULL); diff --git a/imgui.cpp b/imgui.cpp index 6dcb2358bf93..082fdfa5cbbc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4269,6 +4269,13 @@ void ImGui::EndFrame() g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; g.IO.InputQueueCharacters.resize(0); memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); + + // Perform style texture update if requested + if (g.WantStyleUpdateTextureInEndFrame) + { + StyleUpdateTexture(); + g.WantStyleUpdateTextureInEndFrame = false; + } } void ImGui::Render() diff --git a/imgui.h b/imgui.h index 3aaa9ac7e573..71e849480aac 100644 --- a/imgui.h +++ b/imgui.h @@ -53,6 +53,7 @@ Index of this file: // Includes #include // FLT_MIN, FLT_MAX +#include // INT_MAX #include // va_list, va_start, va_end #include // ptrdiff_t, NULL #include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp @@ -265,6 +266,7 @@ namespace ImGui IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // best used with borders and a custom, thicker font + IMGUI_API void StyleUpdateTexture(); // Update the texture atlas for new style parameters. This needs to be called if you change a parameter (e.g. shadow texture settings) that requires it. If called during a frame it will flag the texture for updating at the end of the frame (as the texture cannot change mid-frame). If rendering back-end does not support ImGuiBackendFlags_RendererHasTexReload, then this function can only be used prior to the texture being built for the first time. // Windows // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack. @@ -1082,7 +1084,8 @@ enum ImGuiBackendFlags_ ImGuiBackendFlags_HasGamepad = 1 << 0, // Back-end Platform supports gamepad and currently has one connected. ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Back-end Platform supports honoring GetMouseCursor() value to change the OS cursor shape. ImGuiBackendFlags_HasSetMousePos = 1 << 2, // Back-end Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set). - ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3 // Back-end Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. + ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3, // Back-end Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. + ImGuiBackendFlags_RendererHasTexReload = 1 << 4 // Back-end Renderer checks IsDirty() on the font atlas texture after EndFrame()/Render() and reupload the texture to GPU if required. }; // Enumeration for PushStyleColor() / PopStyleColor() @@ -1361,6 +1364,21 @@ struct ImVector inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; } }; +// Shadow Texture baking config +// This is part of ImGuiStyle, but kept separate to make future support for multiple simultaneous shadow texture styles easier to implement. +struct ImGuiStyleShadowTexConfig +{ + int TexCornerSize; // Size of the corner areas. + int TexEdgeSize; // Size of the edge areas (and by extension the center). Changing this is normally unnecessary. + float TexFalloffPower; // The power factor for the shadow falloff curve. + float TexDistanceFieldOffset; // How much to offset the distance field by (allows over/under-shadowing, potentially useful for accommodating rounded corners on the "casting" shape). + bool TexBlur; // Do we want to Gaussian blur the shadow texture? + + IMGUI_API ImGuiStyleShadowTexConfig(); + int GetPadding() const { return 2; } // Number of pixels of padding to add to avoid sampling artifacts at the edges. + int CalcTexSize() const { return TexCornerSize + TexEdgeSize + GetPadding(); } // The size of the texture area required for the actual 2x2 shadow texture (after the redundant corners have been removed). Padding is required here to avoid sampling artifacts at the edge adjoining the removed corners. +}; + //----------------------------------------------------------------------------- // ImGuiStyle // You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame(). @@ -1409,6 +1427,7 @@ struct ImGuiStyle float WindowShadowSize; // Size (in pixels) of window shadows. Set this to zero to disable shadows. float WindowShadowOffsetDist; // Offset distance (in pixels) of window shadows from casting window. float WindowShadowOffsetAngle; // Offset angle of window shadows from casting window (0.0f = left, 0.5f*PI = bottom, 1.0f*PI = right, 1.5f*PI = top). + ImGuiStyleShadowTexConfig ShadowTexConfig; // Configuration for shadow texture - changing this after the initial setup requires that the backend supports font texture rebuilding. ImVec4 Colors[ImGuiCol_COUNT]; IMGUI_API ImGuiStyle(); @@ -2105,20 +2124,6 @@ struct ImDrawData // Font and Atlas API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont) //----------------------------------------------------------------------------- -// [Internal] Shadow texture baking config -struct ImFontAtlasShadowTexConfig -{ - int TexCornerSize; // Size of the corner areas. - int TexEdgeSize; // Size of the edge areas (and by extension the center). Changing this is normally unnecessary. - float TexFalloffPower; // The power factor for the shadow falloff curve. - float TexDistanceFieldOffset; // How much to offset the distance field by (allows over/under-shadowing, potentially useful for accommodating rounded corners on the "casting" shape). - bool TexBlur; // Do we want to Gaussian blur the shadow texture? - - IMGUI_API ImFontAtlasShadowTexConfig(); - int GetPadding() const { return 2; } // Number of pixels of padding to add to avoid sampling artifacts at the edges. - int CalcTexSize() const { return TexCornerSize + TexEdgeSize + GetPadding(); } // The size of the texture area required for the actual 2x2 shadow texture (after the redundant corners have been removed). Padding is required here to avoid sampling artifacts at the edge adjoining the removed corners. -}; - struct ImFontConfig { void* FontData; // // TTF/OTF data @@ -2221,6 +2226,7 @@ struct ImFontAtlas IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. IMGUI_API void ClearInputData(); // Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. + IMGUI_API void ClearCustomRectData(); // Clear custom rectangle data (for when parameters that affect custom rectangles change). Automatically done by ClearInputData(). IMGUI_API void ClearTexData(); // Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory. IMGUI_API void ClearFonts(); // Clear output font data (glyphs storage, UV coordinates). IMGUI_API void Clear(); // Clear all input and output. @@ -2230,9 +2236,10 @@ struct ImFontAtlas // The pitch is always = Width * BytesPerPixels (1 or 4) // Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste. - IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. - IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel - IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel + IMGUI_API bool Build(int* out_dirty_x = NULL, int* out_dirty_y = NULL, int* out_dirty_width = NULL, int* out_dirty_height = NULL); // Build pixels data. This is called automatically for you by the GetTexData*** functions. Returns the dirty region of the texture (i.e. the region that has changed since the last call to Build()). + IMGUI_API bool IsDirty(); // Returns true if the font needs to be (re-)built. User code should call GetTexData*** and update either the whole texture or dirty region as required. + IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL, int* out_dirty_x = NULL, int* out_dirty_y = NULL, int* out_dirty_width = NULL, int* out_dirty_height = NULL); // 1 byte per-pixel. Returns the dirty region since the last GetTexData*** or Build() call. + IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL, int* out_dirty_x = NULL, int* out_dirty_y = NULL, int* out_dirty_width = NULL, int* out_dirty_height = NULL); // 4 bytes-per-pixel. Returns the dirty region since the last GetTexData*** or Build() call. bool IsBuilt() const { return Fonts.Size > 0 && (TexPixelsAlpha8 != NULL || TexPixelsRGBA32 != NULL); } void SetTexID(ImTextureID id) { TexID = id; } @@ -2269,6 +2276,9 @@ struct ImFontAtlas // [Internal] IMGUI_API void CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const; IMGUI_API bool GetMouseCursorTexData(ImGuiMouseCursor cursor, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]); + IMGUI_API void MarkDirty(int rect_x = 0, int rect_y = 0, int rect_width = INT_MAX, int rect_height = INT_MAX); // Mark a region (or the entire texture) as dirty + IMGUI_API void MarkClean(); // Mark the whole texture as clean (i.e. remove any dirty region). Should be called after uploading any dirty region update. + IMGUI_API void GetDirtyRegion(int* out_dirty_x = NULL, int* out_dirty_y = NULL, int* out_dirty_width = NULL, int* out_dirty_height = NULL); // Helper that gets the dirty region in the format user-side code receives it, clamped to the current texture size. Returns 0,0,0,0 if there is no dirty region. //------------------------------------------- // Members @@ -2279,6 +2289,7 @@ struct ImFontAtlas ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0. + int DirtyRectLeft, DirtyRectTop, DirtyRectRight, DirtyRectBottom; // Dirty region (i.e. that which needs to be rebuilt). Boundaries are inclusive (i.e. pixels at X=Left and X=Right are both in the region, same with Y). Will be all -1 if there is no dirty region. // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. @@ -2294,7 +2305,6 @@ struct ImFontAtlas int MainRectId; // ID of rect for white pixel and mouse cursors int ShadowRectId; // ID of rect for shadow texture ImVec4 ShadowRectUvs[9]; // UV coordinates for shadow texture. - ImFontAtlasShadowTexConfig ShadowTexConfig; // Shadow texture baking config #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 36d9aaef7917..4bbf9cd16220 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -419,6 +419,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasMouseCursors); ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasSetMousePos); ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", (unsigned int *)&backend_flags, ImGuiBackendFlags_RendererHasVtxOffset); + ImGui::CheckboxFlags("io.BackendFlags: RendererHasTexReload", (unsigned int *)&backend_flags, ImGuiBackendFlags_RendererHasTexReload); ImGui::TreePop(); ImGui::Separator(); } @@ -3848,6 +3849,34 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("Offset distance", &style.WindowShadowOffsetDist, 0.0f, 64.0f, "%.0f"); ImGui::SliderAngle("Offset angle", &style.WindowShadowOffsetAngle); + // FIXME-SHADOWS: Revert ref breaks (doesn't call StyleUpdateTexture) + ImGuiIO& io = ImGui::GetIO(); + ImGuiStyleShadowTexConfig* shadow_cfg = &style.ShadowTexConfig; + ImGui::Spacing(); + ImGui::Text("Shadow Texture"); + ImGui::SameLine(); + HelpMarker("These settings can only be edited after initialization if the rendering back-end implements ImGuiBackendFlags_RendererHasTexReload."); + + // Because editing these parameters after initialization requires font atlas reloading, disable them if the back-end does not support it + const bool editing_allowed = (io.BackendFlags & ImGuiBackendFlags_RendererHasTexReload) != 0; + if (!editing_allowed) + { + ImGui::TextWrapped("Error: Rendering back-end needs to support ImGuiBackendFlags_RendererHasTexReload in order to be able to change settings at runtime!"); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, style.Alpha * 0.5f); + } + + bool need_rebuild = false; + need_rebuild |= ImGui::SliderInt("Corner size", &shadow_cfg->TexCornerSize, 4, 128); + need_rebuild |= ImGui::SliderInt("Edge size", &shadow_cfg->TexEdgeSize, 1, 128); + need_rebuild |= ImGui::SliderFloat("Falloff power", &shadow_cfg->TexFalloffPower, 0.1f, 8.0f, "%.2f"); + need_rebuild |= ImGui::SliderFloat("Distance field offset", &shadow_cfg->TexDistanceFieldOffset, -16.0f, 16.0f, "%.2f"); + need_rebuild |= ImGui::Checkbox("Blur texture", &shadow_cfg->TexBlur); + + if (!editing_allowed) + ImGui::PopStyleVar(); + if (need_rebuild && editing_allowed) + ImGui::StyleUpdateTexture(); + ImGui::EndTabItem(); } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 3059537ec7df..26aba4571e60 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -7,12 +7,12 @@ Index of this file: // [SECTION] STB libraries implementation // [SECTION] Style functions +// [SECTION] ImGuiStyleShadowTexConfig // [SECTION] ImDrawList // [SECTION] ImDrawList Shadow Primitives // [SECTION] ImDrawListSplitter // [SECTION] ImDrawData // [SECTION] Helpers ShadeVertsXXX functions -// [SECTION] ImFontAtlasShadowTexConfig // [SECTION] ImFontConfig // [SECTION] ImFontAtlas // [SECTION] ImFontAtlas glyph ranges helpers @@ -346,6 +346,38 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_WindowShadow] = ImVec4(0.08f, 0.08f, 0.08f, 0.35f); } +// Flag that we need to update the built texture data +void ImGui::StyleUpdateTexture() +{ + IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?"); + + ImGuiContext& g = *GImGui; + IM_ASSERT(((g.IO.Fonts == NULL) || (!g.IO.Fonts->IsBuilt()) || (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTexReload)) && "ImGui::StyleUpdateTexture() can only be called prior to the first frame if the back-end does not support font atlas reloading"); + + if (g.WithinFrameScope) // If we are currently in a frame, defer doing anything until the end of the frame (as we can't update the texture mid-frame) + { + g.WantStyleUpdateTextureInEndFrame = true; + return; + } + + if (g.IO.Fonts) + g.IO.Fonts->ClearCustomRectData(); +} + +//----------------------------------------------------------------------------- +// [SECTION] ImGuiStyleShadowTexConfig +//----------------------------------------------------------------------------- + +ImGuiStyleShadowTexConfig::ImGuiStyleShadowTexConfig() +{ + TexCornerSize = 16; + TexEdgeSize = 1; + TexFalloffPower = 4.8f; + TexDistanceFieldOffset = 3.8f; + TexBlur = true; +} + + //----------------------------------------------------------------------------- // ImDrawList //----------------------------------------------------------------------------- @@ -1967,19 +1999,6 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve } } -//----------------------------------------------------------------------------- -// [SECTION] ImFontAtlasShadowTexConfig -//----------------------------------------------------------------------------- - -ImFontAtlasShadowTexConfig::ImFontAtlasShadowTexConfig() -{ - TexCornerSize = 16; - TexEdgeSize = 1; - TexFalloffPower = 4.8f; - TexDistanceFieldOffset = 3.8f; - TexBlur = true; -} - //----------------------------------------------------------------------------- // [SECTION] ImFontConfig //----------------------------------------------------------------------------- @@ -2074,6 +2093,10 @@ ImFontAtlas::ImFontAtlas() TexUvWhitePixel = ImVec2(0.0f, 0.0f); MainRectId = -1; ShadowRectId = -1; + DirtyRectLeft = 0; // Start entirely dirty + DirtyRectTop = 0; + DirtyRectRight = INT_MAX; + DirtyRectBottom = INT_MAX; } ImFontAtlas::~ImFontAtlas() @@ -2100,9 +2123,17 @@ void ImFontAtlas::ClearInputData() Fonts[i]->ConfigDataCount = 0; } ConfigData.clear(); + ClearCustomRectData(); + MarkDirty(); +} + +void ImFontAtlas::ClearCustomRectData() +{ + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); CustomRects.clear(); MainRectId = -1; ShadowRectId = -1; + MarkDirty(); } void ImFontAtlas::ClearTexData() @@ -2114,6 +2145,7 @@ void ImFontAtlas::ClearTexData() IM_FREE(TexPixelsRGBA32); TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; + MarkDirty(); } void ImFontAtlas::ClearFonts() @@ -2122,6 +2154,7 @@ void ImFontAtlas::ClearFonts() for (int i = 0; i < Fonts.Size; i++) IM_DELETE(Fonts[i]); Fonts.clear(); + MarkDirty(); } void ImFontAtlas::Clear() @@ -2131,14 +2164,81 @@ void ImFontAtlas::Clear() ClearFonts(); } -void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +void ImFontAtlas::MarkDirty(int rect_x, int rect_y, int rect_width, int rect_height) +{ + if ((rect_width < 1) || (rect_height < 1)) + return; // Early out if the region is zero-sized + + IM_ASSERT((rect_x >= 0) && (rect_y >= 0) && "Dirty rectangle cannot have a negative position"); + + int rect_right = rect_x + rect_width - 1; + int rect_bottom = rect_y + rect_height - 1; + + if (DirtyRectLeft == -1) + { + // No existing dirty region + DirtyRectLeft = rect_x; + DirtyRectTop = rect_y; + DirtyRectRight = rect_right; + DirtyRectBottom = rect_bottom; + } + else + { + // Combine with existing region + DirtyRectLeft = ImMin(DirtyRectLeft, rect_x); + DirtyRectTop = ImMin(DirtyRectTop, rect_y); + DirtyRectRight = ImMax(DirtyRectRight, rect_right); + DirtyRectBottom = ImMax(DirtyRectBottom, rect_bottom); + } +} + +void ImFontAtlas::MarkClean() +{ + DirtyRectLeft = DirtyRectTop = DirtyRectRight = DirtyRectBottom = -1; +} + +bool ImFontAtlas::IsDirty() +{ + return (DirtyRectLeft >= 0); +} + +void ImFontAtlas::GetDirtyRegion(int* out_dirty_x, int* out_dirty_y, int* out_dirty_width, int* out_dirty_height) +{ + if (DirtyRectLeft < 0) + { + // Region is empty + if (out_dirty_x) *out_dirty_x = 0; + if (out_dirty_y) *out_dirty_y = 0; + if (out_dirty_width) *out_dirty_width = 0; + if (out_dirty_height) *out_dirty_height = 0; + } + else + { + // Clamp to our current texture size + int dirtyLeft = ImMin(DirtyRectLeft, TexWidth - 1); + int dirtyTop = ImMin(DirtyRectTop, TexHeight - 1); + int dirtyRight = ImMin(DirtyRectRight, TexWidth - 1); + int dirtyBottom = ImMin(DirtyRectBottom, TexHeight - 1); + + if (out_dirty_x) *out_dirty_x = dirtyLeft; + if (out_dirty_y) *out_dirty_y = dirtyTop; + if (out_dirty_width) *out_dirty_width = (dirtyRight - dirtyLeft) + 1; + if (out_dirty_height) *out_dirty_height = (dirtyBottom - dirtyTop) + 1; + } +} + +void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel, int* out_dirty_x, int* out_dirty_y, int* out_dirty_width, int* out_dirty_height) { // Build atlas on demand - if (TexPixelsAlpha8 == NULL) + if (TexPixelsAlpha8 == NULL || IsDirty()) { if (ConfigData.empty()) AddFontDefault(); - Build(); + Build(out_dirty_x, out_dirty_y, out_dirty_width, out_dirty_height); + } + else + { + GetDirtyRegion(out_dirty_x, out_dirty_y, out_dirty_width, out_dirty_height); // Not calling Build() so retrieve the region (which will be empty) manually } *out_pixels = TexPixelsAlpha8; @@ -2147,14 +2247,14 @@ void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_wid if (out_bytes_per_pixel) *out_bytes_per_pixel = 1; } -void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel, int* out_dirty_x, int* out_dirty_y, int* out_dirty_width, int* out_dirty_height) { // Convert to RGBA32 format on demand // Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp - if (!TexPixelsRGBA32) + if (TexPixelsRGBA32 == NULL || IsDirty()) { unsigned char* pixels = NULL; - GetTexDataAsAlpha8(&pixels, NULL, NULL); + GetTexDataAsAlpha8(&pixels, NULL, NULL, NULL, out_dirty_x, out_dirty_y, out_dirty_width, out_dirty_height); if (pixels) { TexPixelsRGBA32 = (unsigned int*)IM_ALLOC((size_t)TexWidth * (size_t)TexHeight * 4); @@ -2164,6 +2264,10 @@ void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_wid *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++)); } } + else + { + GetDirtyRegion(out_dirty_x, out_dirty_y, out_dirty_width, out_dirty_height); // Not calling Build() so retrieve the region (which will be empty) manually + } *out_pixels = (unsigned char*)TexPixelsRGBA32; if (out_width) *out_width = TexWidth; @@ -2199,6 +2303,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) // Invalidate texture ClearTexData(); + MarkDirty(); return new_font_cfg.DstFont; } @@ -2356,10 +2461,10 @@ bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* ou return true; } -bool ImFontAtlas::Build() +bool ImFontAtlas::Build(int* out_dirty_x, int* out_dirty_y, int* out_dirty_width, int* out_dirty_height) { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - return ImFontAtlasBuildWithStbTruetype(this); + return ImFontAtlasBuildWithStbTruetype(this, out_dirty_x, out_dirty_y, out_dirty_width, out_dirty_height); } void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_brighten_factor) @@ -2416,14 +2521,16 @@ static void UnpackBitVectorToFlatIndexList(const ImBitVector* in, ImVector* out->push_back((int)(((it - it_begin) << 5) + bit_n)); } -bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) +bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas, int* out_dirty_x, int* out_dirty_y, int* out_dirty_width, int* out_dirty_height) { IM_ASSERT(atlas->ConfigData.Size > 0); + // Clear custom rects so that they will get re-registered - this is arguably "wrong" in that it might make more sense to track when a related parameter has changed and only do this then, but given that right now at this point we're already rebuilding everything this is much simpler. + atlas->ClearCustomRectData(); + ImFontAtlasBuildInit(atlas); // Clear atlas - atlas->TexID = (ImTextureID)NULL; atlas->TexWidth = atlas->TexHeight = 0; atlas->TexUvScale = ImVec2(0.0f, 0.0f); atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); @@ -2669,6 +2776,16 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) src_tmp_array[src_i].~ImFontBuildSrcData(); ImFontAtlasBuildFinish(atlas); + + // For now, because we rebuilt everything and don't know if items have moved, we need to dirty the entire texture + atlas->MarkDirty(); + + // Return dirty region + atlas->GetDirtyRegion(out_dirty_x, out_dirty_y, out_dirty_width, out_dirty_height); + + // Mark the dirty region as clean + atlas->MarkClean(); + return true; } @@ -2765,7 +2882,8 @@ static void ImFontAtlasBuildRegisterShadowCustomRects(ImFontAtlas* atlas) return; // The actual size we want to reserve, including padding - const ImFontAtlasShadowTexConfig* shadow_cfg = &atlas->ShadowTexConfig; + // FIXME-SHADOWS: Should not access ImGui style? + const ImGuiStyleShadowTexConfig* shadow_cfg = &GImGui->Style.ShadowTexConfig; const unsigned int effective_size = shadow_cfg->CalcTexSize() + shadow_cfg->GetPadding(); atlas->ShadowRectId = atlas->AddCustomRectRegular(effective_size, effective_size); } @@ -2834,7 +2952,8 @@ static void ImFontAtlasBuildRenderShadowTexData(ImFontAtlas* atlas) // Because of the blur, we have to generate the full 3x3 texture here, and then we chop that down to just the 2x2 section we need later. // 'size' correspond to the our 3x3 size, whereas 'shadow_tex_size' correspond to our 2x2 version where duplicate mirrored corners are not stored. - const ImFontAtlasShadowTexConfig* shadow_cfg = &atlas->ShadowTexConfig; + // FIXME-SHADOWS: Should not access ImGui style? + const ImGuiStyleShadowTexConfig* shadow_cfg = &GImGui->Style.ShadowTexConfig; const int size = shadow_cfg->TexCornerSize + shadow_cfg->TexEdgeSize + shadow_cfg->TexCornerSize; const int corner_size = shadow_cfg->TexCornerSize; const int edge_size = shadow_cfg->TexEdgeSize; diff --git a/imgui_internal.h b/imgui_internal.h index 1260efd94710..89a2a3fc4324 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -103,7 +103,6 @@ struct ImGuiNextItemData; // Storage for SetNextItem** functions struct ImGuiPopupData; // Storage for current popup stack struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it -struct ImGuiStyleShadowTexConfig; // Shadow Texture baking config struct ImGuiTabBar; // Storage for a tab bar struct ImGuiTabItem; // Storage for a tab item (within a tab bar) struct ImGuiWindow; // Storage for one window @@ -1318,6 +1317,7 @@ struct ImGuiContext int WantCaptureKeyboardNextFrame; int WantTextInputNextFrame; char TempBuffer[1024*3+1]; // Temporary text buffer + bool WantStyleUpdateTextureInEndFrame; // Do we want to do StyleUpdateTexture() at the end of the frame? ImGuiContext(ImFontAtlas* shared_font_atlas) : BackgroundDrawList(&DrawListSharedData), ForegroundDrawList(&DrawListSharedData) { @@ -1454,6 +1454,7 @@ struct ImGuiContext FramerateSecPerFrameAccum = 0.0f; WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; memset(TempBuffer, 0, sizeof(TempBuffer)); + WantStyleUpdateTextureInEndFrame = false; } }; @@ -2016,7 +2017,7 @@ namespace ImGui } // namespace ImGui // ImFontAtlas internals -IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); +IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas, int* out_dirty_x, int* out_dirty_y, int* out_dirty_width, int* out_dirty_height); IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);