From ab4ef822f0e85a6dd65723db9e0e632a1e1a45d8 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jun 2020 16:56:09 +0200 Subject: [PATCH 01/18] Version 1.78 WIP --- docs/CHANGELOG.txt | 5 +++++ examples/README.txt | 2 +- imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4d757e0b..c62a140d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -31,6 +31,11 @@ HOW TO UPDATE? - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.78 WIP (In Progress) +----------------------------------------------------------------------- + + ----------------------------------------------------------------------- VERSION 1.77 (Released 2020-06-29) ----------------------------------------------------------------------- diff --git a/examples/README.txt b/examples/README.txt index 2137fe09..35ee87b9 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -1,5 +1,5 @@ ----------------------------------------------------------------------- - dear imgui, v1.77 + dear imgui, v1.78 WIP ----------------------------------------------------------------------- examples/README.txt (This is the README file for the examples/ folder. See docs/ for more documentation) diff --git a/imgui.cpp b/imgui.cpp index 295cb054..d8bf40be 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.77 +// dear imgui, v1.78 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 724aa289..dc4fa453 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.77 +// dear imgui, v1.78 WIP // (headers) // Help: @@ -59,8 +59,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.77" -#define IMGUI_VERSION_NUM 17701 +#define IMGUI_VERSION "1.78 WIP" +#define IMGUI_VERSION_NUM 17702 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) // Define attributes of all API symbols declarations (e.g. for DLL under Windows) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e0fc9af7..a2208ebe 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.77 +// dear imgui, v1.78 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 87e59205..ade1290d 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.77 +// dear imgui, v1.78 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index f55a061b..b57b01b5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.77 +// dear imgui, v1.78 WIP // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7d6bf847..86295bcd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.77 +// dear imgui, v1.78 WIP // (widgets code) /* From a1d2c6fad96ec7a3e444094a1522dc1796ab68fe Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jun 2020 19:00:31 +0200 Subject: [PATCH 02/18] Fixed invalid comment (#3327) --- imgui.h | 2 +- imgui_demo.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index dc4fa453..2da0d21a 100644 --- a/imgui.h +++ b/imgui.h @@ -740,7 +740,7 @@ namespace ImGui IMGUI_API bool IsMouseDown(ImGuiMouseButton button); // is mouse button held? IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down) IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) - IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. + IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? (note that a double-click will also report IsMouseClicked() == true) IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available IMGUI_API bool IsAnyMouseDown(); // is any mouse button held? diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a2208ebe..ed38e52f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3224,7 +3224,7 @@ static void ShowDemoWindowMisc() ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); From 0d03e1fafafb8d1cebcb6268b3a8014dff08d97c Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 7 Jul 2020 12:49:25 +0300 Subject: [PATCH 03/18] CI: Fix emscripten builds that broke due behavior change of emscripten SDK. --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8edef47b..b3b910f2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -394,7 +394,9 @@ jobs: - name: Build example_emscripten run: | - source emsdk-master/emsdk_env.sh + pushd emsdk-master + source ./emsdk_env.sh + popd make -C examples/example_emscripten Static-Analysis: From 8e4046e13ba3642de7aa5e2795021c035259f710 Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Wed, 8 Jul 2020 17:17:52 +0200 Subject: [PATCH 04/18] Atlas build use GetCustomRectByIndex() + comments, rename, and shallow merge from tex_antialiasing_lines branch. --- imgui.cpp | 4 +- imgui.h | 25 ++++++------ imgui_demo.cpp | 1 + imgui_draw.cpp | 101 +++++++++++++++++++++++++++---------------------- 4 files changed, 72 insertions(+), 59 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d8bf40be..d09d61ed 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -934,8 +934,8 @@ ImGuiStyle::ImGuiStyle() DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. - AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. - AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) + AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. + AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. diff --git a/imgui.h b/imgui.h index 2da0d21a..adb2ab1c 100644 --- a/imgui.h +++ b/imgui.h @@ -151,7 +151,7 @@ typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling typedef int ImDrawCornerFlags; // -> enum ImDrawCornerFlags_ // Flags: for ImDrawList::AddRect(), AddRectFilled() etc. typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList -typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas +typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc. typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags @@ -1438,8 +1438,8 @@ struct ImGuiStyle ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. - bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. - bool AntiAliasedFill; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) + bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. + bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. float CircleSegmentMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. ImVec4 Colors[ImGuiCol_COUNT]; @@ -1994,10 +1994,10 @@ enum ImDrawCornerFlags_ enum ImDrawListFlags_ { - ImDrawListFlags_None = 0, - ImDrawListFlags_AntiAliasedLines = 1 << 0, // Lines are anti-aliased (*2 the number of triangles for 1.0f wide line, otherwise *3 the number of triangles) - ImDrawListFlags_AntiAliasedFill = 1 << 1, // Filled shapes have anti-aliased edges (*2 the number of vertices) - ImDrawListFlags_AllowVtxOffset = 1 << 2 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. + ImDrawListFlags_None = 0, + ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles) + ImDrawListFlags_AntiAliasedFill = 1 << 1, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). + ImDrawListFlags_AllowVtxOffset = 1 << 2 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. }; // Draw command list @@ -2211,11 +2211,12 @@ struct ImFontAtlasCustomRect bool IsPacked() const { return X != 0xFFFF; } }; +// Flags for ImFontAtlas build enum ImFontAtlasFlags_ { ImFontAtlasFlags_None = 0, ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two - ImFontAtlasFlags_NoMouseCursors = 1 << 1 // Don't build software mouse cursors into the atlas + ImFontAtlasFlags_NoMouseCursors = 1 << 1 // Don't build software mouse cursors into the atlas (save a little texture memory) }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: @@ -2289,7 +2290,7 @@ struct ImFontAtlas // Note: this API may be redesigned later in order to support multi-monitor varying DPI settings. IMGUI_API int AddCustomRectRegular(int width, int height); IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0, 0)); - const ImFontAtlasCustomRect*GetCustomRectByIndex(int index) const { if (index < 0) return NULL; return &CustomRects[index]; } + ImFontAtlasCustomRect* GetCustomRectByIndex(int index) { IM_ASSERT(index >= 0); return &CustomRects[index]; } // [Internal] IMGUI_API void CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const; @@ -2315,8 +2316,10 @@ struct ImFontAtlas ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. - ImVector ConfigData; // Internal data - int CustomRectIds[1]; // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList + ImVector ConfigData; // Configuration data + + // [Internal] Packing data + int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ed38e52f..b481c305 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -405,6 +405,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); + ImGui::Text("Also see Style->Rendering for rendering options."); ImGui::TreePop(); ImGui::Separator(); } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index ade1290d..30b9b9fe 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -666,11 +666,9 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 return; const ImVec2 opaque_uv = _Data->TexUvWhitePixel; - int count = points_count; - if (!closed) - count = points_count - 1; - + const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw const bool thick_line = (thickness > 1.0f); + if (Flags & ImDrawListFlags_AntiAliasedLines) { // Anti-aliased stroke @@ -682,9 +680,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 PrimReserve(idx_count, vtx_count); // Temporary buffer + // The first items are normals at each line point, then after that there are either 2 or 4 temp points for each line point ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); //-V630 ImVec2* temp_points = temp_normals + points_count; + // Calculate normals (tangents) for each line segment for (int i1 = 0; i1 < count; i1++) { const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; @@ -707,12 +707,14 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * AA_SIZE; } + // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges + // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps) // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. - unsigned int idx1 = _VtxCurrentIdx; - for (int i1 = 0; i1 < count; i1++) + unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment + for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment { const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; - unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : idx1 + 3; + const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : idx1 + 3; // Average normals float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; @@ -721,14 +723,14 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 dm_x *= AA_SIZE; dm_y *= AA_SIZE; - // Add temporary vertices + // Add temporary vertexes for the outer edges ImVec2* out_vtx = &temp_points[i2 * 2]; out_vtx[0].x = points[i2].x + dm_x; out_vtx[0].y = points[i2].y + dm_y; out_vtx[1].x = points[i2].x - dm_x; out_vtx[1].y = points[i2].y - dm_y; - // Add indexes + // Add indexes for four triangles _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+0); _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); @@ -738,7 +740,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 idx1 = idx2; } - // Add vertices + // Add vertexes for each point on the line for (int i = 0; i < points_count; i++) { _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; @@ -749,7 +751,10 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 } else { + // Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; + + // If line is not closed, the first and last points need to be generated differently as there are no normals to blend if (!closed) { const int points_last = points_count - 1; @@ -763,9 +768,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 temp_points[points_last * 4 + 3] = points[points_last] - temp_normals[points_last] * (half_inner_thickness + AA_SIZE); } + // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges + // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps) // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. - unsigned int idx1 = _VtxCurrentIdx; - for (int i1 = 0; i1 < count; i1++) + unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment + for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment { const int i2 = (i1 + 1) == points_count ? 0 : (i1 + 1); // i2 is the second point of the line segment const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : (idx1 + 4); // Vertex index for end of segment @@ -816,7 +823,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 } else { - // Non Anti-aliased Stroke + // Non texture-based, Non anti-aliased lines const int idx_count = count * 6; const int vtx_count = count * 4; // FIXME-OPT: Not sharing edges PrimReserve(idx_count, vtx_count); @@ -1656,8 +1663,7 @@ ImFontAtlas::ImFontAtlas() TexWidth = TexHeight = 0; TexUvScale = ImVec2(0.0f, 0.0f); TexUvWhitePixel = ImVec2(0.0f, 0.0f); - for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++) - CustomRectIds[n] = -1; + PackIdMouseCursors = -1; } ImFontAtlas::~ImFontAtlas() @@ -1685,8 +1691,7 @@ void ImFontAtlas::ClearInputData() } ConfigData.clear(); CustomRects.clear(); - for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++) - CustomRectIds[n] = -1; + PackIdMouseCursors = -1; } void ImFontAtlas::ClearTexData() @@ -1926,9 +1931,9 @@ bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* ou if (Flags & ImFontAtlasFlags_NoMouseCursors) return false; - IM_ASSERT(CustomRectIds[0] != -1); - ImFontAtlasCustomRect& r = CustomRects[CustomRectIds[0]]; - ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r.X, (float)r.Y); + IM_ASSERT(PackIdMouseCursors != -1); + ImFontAtlasCustomRect* r = GetCustomRectByIndex(PackIdMouseCursors); + ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r->X, (float)r->Y); ImVec2 size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][1]; *out_size = size; *out_offset = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][2]; @@ -2256,17 +2261,6 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) return true; } -// Register default custom rectangles (this is called/shared by both the stb_truetype and the FreeType builder) -void ImFontAtlasBuildInit(ImFontAtlas* atlas) -{ - if (atlas->CustomRectIds[0] >= 0) - return; - if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) - atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H); - else - atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(2, 2); -} - void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) { if (!font_config->MergeMode) @@ -2310,20 +2304,18 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) { - IM_ASSERT(atlas->CustomRectIds[0] >= 0); - IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); - ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->CustomRectIds[0]]; - IM_ASSERT(r.IsPacked()); + ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdMouseCursors); + IM_ASSERT(r->IsPacked()); const int w = atlas->TexWidth; if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) { // Render/copy pixels - IM_ASSERT(r.Width == FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1 && r.Height == FONT_ATLAS_DEFAULT_TEX_DATA_H); + IM_ASSERT(r->Width == FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1 && r->Height == FONT_ATLAS_DEFAULT_TEX_DATA_H); for (int y = 0, n = 0; y < FONT_ATLAS_DEFAULT_TEX_DATA_H; y++) for (int x = 0; x < FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF; x++, n++) { - const int offset0 = (int)(r.X + x) + (int)(r.Y + y) * w; + const int offset0 = (int)(r->X + x) + (int)(r->Y + y) * w; const int offset1 = offset0 + FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1; atlas->TexPixelsAlpha8[offset0] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == '.' ? 0xFF : 0x00; atlas->TexPixelsAlpha8[offset1] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == 'X' ? 0xFF : 0x00; @@ -2331,29 +2323,46 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) } else { - IM_ASSERT(r.Width == 2 && r.Height == 2); - const int offset = (int)(r.X) + (int)(r.Y) * w; + // Render 4 white pixels + IM_ASSERT(r->Width == 2 && r->Height == 2); + const int offset = (int)r->X + (int)r->Y * w; atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF; } - atlas->TexUvWhitePixel = ImVec2((r.X + 0.5f) * atlas->TexUvScale.x, (r.Y + 0.5f) * atlas->TexUvScale.y); + atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); } + +// Note: this is called / shared by both the stb_truetype and the FreeType builder +void ImFontAtlasBuildInit(ImFontAtlas* atlas) +{ + // Register texture region for mouse cursors or standard white pixels + if (atlas->PackIdMouseCursors < 0) + { + if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) + atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H); + else + atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(2, 2); + } +} + +// This is called/shared by both the stb_truetype and the FreeType builder. void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { - // Render into our custom data block + // Render into our custom data blocks + IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); ImFontAtlasBuildRenderDefaultTexData(atlas); // Register custom rectangle glyphs for (int i = 0; i < atlas->CustomRects.Size; i++) { - const ImFontAtlasCustomRect& r = atlas->CustomRects[i]; - if (r.Font == NULL || r.GlyphID == 0) + const ImFontAtlasCustomRect* r = &atlas->CustomRects[i]; + if (r->Font == NULL || r->GlyphID == 0) continue; - IM_ASSERT(r.Font->ContainerAtlas == atlas); + IM_ASSERT(r->Font->ContainerAtlas == atlas); ImVec2 uv0, uv1; - atlas->CalcCustomRectUV(&r, &uv0, &uv1); - r.Font->AddGlyph((ImWchar)r.GlyphID, r.GlyphOffset.x, r.GlyphOffset.y, r.GlyphOffset.x + r.Width, r.GlyphOffset.y + r.Height, uv0.x, uv0.y, uv1.x, uv1.y, r.GlyphAdvanceX); + atlas->CalcCustomRectUV(r, &uv0, &uv1); + r->Font->AddGlyph((ImWchar)r->GlyphID, r->GlyphOffset.x, r->GlyphOffset.y, r->GlyphOffset.x + r->Width, r->GlyphOffset.y + r->Height, uv0.x, uv0.y, uv1.x, uv1.y, r->GlyphAdvanceX); } // Build all fonts lookup tables From 1d3c3070d8462b6ca2d84e3ca0f05d28896018ec Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Mon, 13 Jan 2020 14:24:55 +0900 Subject: [PATCH 05/18] Texture-based thick lines: Initial version of AA line drawing using textures (press SHIFT to enable) --- imgui.cpp | 6 ++ imgui.h | 10 ++- imgui_demo.cpp | 57 +++++++++++++++++ imgui_draw.cpp | 156 +++++++++++++++++++++++++++++++++++++++-------- imgui_internal.h | 4 ++ 5 files changed, 206 insertions(+), 27 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d09d61ed..34b63566 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -935,6 +935,7 @@ ImGuiStyle::ImGuiStyle() DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. + TexturedAntiAliasedLines= true; // Draw anti-aliased lines using textures where possible. AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. @@ -3688,10 +3689,14 @@ void ImGui::NewFrame() g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; if (g.Style.AntiAliasedLines) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; + if (g.Style.TexturedAntiAliasedLines) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_TexturedAALines; if (g.Style.AntiAliasedFill) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; + if ((g.Style.TexturedAntiAliasedLines) && (!(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_TexturedAALines; g.BackgroundDrawList._ResetForNewFrame(); g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); @@ -6158,6 +6163,7 @@ void ImGui::SetCurrentFont(ImFont* font) ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; + g.DrawListSharedData.TexUvAALines = &atlas->TexUvAALines; g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.FontSize = g.FontSize; } diff --git a/imgui.h b/imgui.h index adb2ab1c..09d92309 100644 --- a/imgui.h +++ b/imgui.h @@ -1439,6 +1439,7 @@ struct ImGuiStyle ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. + bool TexturedAntiAliasedLines; // Draw anti-aliased lines using textures where possible. bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. float CircleSegmentMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. @@ -1997,7 +1998,8 @@ enum ImDrawListFlags_ ImDrawListFlags_None = 0, ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles) ImDrawListFlags_AntiAliasedFill = 1 << 1, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). - ImDrawListFlags_AllowVtxOffset = 1 << 2 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. + ImDrawListFlags_AllowVtxOffset = 1 << 2, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. + ImDrawListFlags_TexturedAALines = 1 << 3 // Should anti-aliased lines be drawn using textures where possible? }; // Draw command list @@ -2216,7 +2218,8 @@ enum ImFontAtlasFlags_ { ImFontAtlasFlags_None = 0, ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two - ImFontAtlasFlags_NoMouseCursors = 1 << 1 // Don't build software mouse cursors into the atlas (save a little texture memory) + ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory) + ImFontAtlasFlags_NoAALines = 1 << 2 // Don't build anti-aliased line textures into the atlas }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: @@ -2320,6 +2323,9 @@ struct ImFontAtlas // [Internal] Packing data int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors + int AALineMaxWidth; // Maximum line width to build anti-aliased textures for + ImVector AALineRectIds; // Custom texture rectangle IDs for anti-aliased lines + ImVector TexUvAALines; // UVs for anti-aliased line textures #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b481c305..f06d8526 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -297,6 +297,62 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); + // Test lines + + if (ImGui::Begin("Lines")) + { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + const int num_cols = 16; + const int num_rows = 3; + const float line_len = 64.0f; + const float line_spacing = 128.0f; + + static float base_rot = 0.0f; + ImGui::SliderFloat("Base rotation", &base_rot, 0.0f, 360.0f); + static float line_width = 1.0f; + ImGui::SliderFloat("Line width", &line_width, 1.0f, 10.0f); + + ImVec2 window_pos = ImGui::GetWindowPos(); + ImVec2 cursor_pos = ImGui::GetCursorPos(); + ImVec2 base_pos(window_pos.x + cursor_pos.x + (line_spacing * 0.5f), window_pos.y + cursor_pos.y); + + for (int i = 0; i < num_rows; i++) + { + const char* name = ""; + switch (i) + { + case 0: name = "No AA"; draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; break; + case 1: name = "AA no texturing"; draw_list->Flags |= ImDrawListFlags_AntiAliasedLines; draw_list->Flags &= ~ImDrawListFlags_TexturedAALines; break; + case 2: name = "AA with texturing"; draw_list->Flags |= ImDrawListFlags_AntiAliasedLines; draw_list->Flags |= ImDrawListFlags_TexturedAALines; break; + } + + int initial_vtx_count = draw_list->VtxBuffer.Size; + int initial_idx_count = draw_list->IdxBuffer.Size; + + for (int j = 0; j < num_cols; j++) + { + const float pi = 3.14159265359f; + float r = (base_rot * pi / 180.0f) + ((j * pi * 0.5f) / (num_cols - 1)); + + ImVec2 center = ImVec2(base_pos.x + (line_spacing * (j * 0.5f)), base_pos.y + (line_spacing * (i + 0.5f))); + ImVec2 start = ImVec2(center.x + (sinf(r) * line_len * 0.5f), center.y + (cosf(r) * line_len * 0.5f)); + ImVec2 end = ImVec2(center.x - (sinf(r) * line_len * 0.5f), center.y - (cosf(r) * line_len * 0.5f)); + + draw_list->AddLine(start, end, IM_COL32(255, 255, 255, 255), line_width); + } + + ImGui::SetCursorPosY(cursor_pos.y + (i * line_spacing)); + ImGui::Text("%s - %d vertices, %d indices", name, draw_list->VtxBuffer.Size - initial_vtx_count, draw_list->IdxBuffer.Size - initial_idx_count); + } + + ImGui::SetCursorPosY(cursor_pos.y + (num_rows * line_spacing)); + + //ImGui::Spacing(); ImGui::Spacing(); ImGui::Spacing(); + //ImGui::Image(ImGui::GetFont()->ContainerAtlas->TexID, ImVec2((float)ImGui::GetFont()->ContainerAtlas->TexWidth, (float)ImGui::GetFont()->ContainerAtlas->TexHeight)); + } + ImGui::End(); + // Main body of the Demo window starts here. if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags)) { @@ -3831,6 +3887,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) { ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); + ImGui::Checkbox("Use textures for anti-aliased lines", &style.TexturedAntiAliasedLines); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); ImGui::PushItemWidth(100); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 30b9b9fe..7c845f94 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -667,6 +667,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const ImVec2 opaque_uv = _Data->TexUvWhitePixel; const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw + const bool thick_line = (thickness > 1.0f); if (Flags & ImDrawListFlags_AntiAliasedLines) @@ -675,13 +676,24 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const float AA_SIZE = 1.0f; const ImU32 col_trans = col & ~IM_COL32_A_MASK; - const int idx_count = thick_line ? count * 18 : count * 12; - const int vtx_count = thick_line ? points_count * 4 : points_count * 3; + const int integer_thickness = (int)thickness; + + // Do we want to draw this line using a texture? + bool use_textures = (Flags & ImDrawListFlags_TexturedAALines) && + (integer_thickness >= 1) && + (integer_thickness <= _Data->Font->ContainerAtlas->AALineMaxWidth) && + ImGui::GetIO().KeyShift; // FIXME-AALINES: Remove this debug code + + // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_TexturedAALines unless ImFontAtlasFlags_NoAALines is off + IM_ASSERT_PARANOID((!use_textures) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); + + const int idx_count = use_textures ? (count * 6) : (thick_line ? count * 18 : count * 12); + const int vtx_count = use_textures ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3); PrimReserve(idx_count, vtx_count); // Temporary buffer // The first items are normals at each line point, then after that there are either 2 or 4 temp points for each line point - ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); //-V630 + ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((thick_line && !use_textures) ? 5 : 3) * sizeof(ImVec2)); //-V630 ImVec2* temp_points = temp_normals + points_count; // Calculate normals (tangents) for each line segment @@ -697,14 +709,19 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 if (!closed) temp_normals[points_count - 1] = temp_normals[points_count - 2]; - if (!thick_line) + // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point + if ((!thick_line) || (use_textures)) { + // The width of the geometry we need to draw + const float half_draw_size = (!thick_line) ? AA_SIZE : (AA_SIZE + (thickness * 0.5f)); + + // If line is not closed, the first and last points need to be generated differently as there are no normals to blend if (!closed) { - temp_points[0] = points[0] + temp_normals[0] * AA_SIZE; - temp_points[1] = points[0] - temp_normals[0] * AA_SIZE; - temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * AA_SIZE; - temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * AA_SIZE; + temp_points[0] = points[0] + temp_normals[0] * half_draw_size; + temp_points[1] = points[0] - temp_normals[0] * half_draw_size; + temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * half_draw_size; + temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * half_draw_size; } // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges @@ -713,15 +730,15 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment { - const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; - const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : idx1 + 3; + const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment + unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_textures ? 2 : 3)); // Vertex index for end of segment // Average normals float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; IM_FIXNORMAL2F(dm_x, dm_y); - dm_x *= AA_SIZE; - dm_y *= AA_SIZE; + dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area + dm_y *= half_draw_size; // Add temporary vertexes for the outer edges ImVec2* out_vtx = &temp_points[i2 * 2]; @@ -730,28 +747,54 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 out_vtx[1].x = points[i2].x - dm_x; out_vtx[1].y = points[i2].y - dm_y; - // Add indexes for four triangles - _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); - _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+0); - _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); - _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10]= (ImDrawIdx)(idx2+0); _IdxWritePtr[11]= (ImDrawIdx)(idx2+1); - _IdxWritePtr += 12; + if (use_textures) + { + // Add indices for two triangles + _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri + _IdxWritePtr[3] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[4] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Left tri + _IdxWritePtr += 6; + } + else + { + // Add indexes for four triangles + _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2); // Right tri 1 + _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Right tri 2 + _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); // Left tri 1 + _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); // Left tri 2 + _IdxWritePtr += 12; + } idx1 = idx2; } // Add vertexes for each point on the line - for (int i = 0; i < points_count; i++) + if (use_textures) + { + // If we're using textures we only need to emit the left/right edge vertices + const ImVec4 tex_uvs = (*_Data->TexUvAALines)[integer_thickness - 1]; + + for (int i = 0; i < points_count; i++) + { + _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = ImVec2(tex_uvs.x, tex_uvs.y); _VtxWritePtr[0].col = col; // Left-side outer edge + _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = ImVec2(tex_uvs.z, tex_uvs.y); _VtxWritePtr[1].col = col; // Right-side outer edge + _VtxWritePtr += 2; + } + } + else { - _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; - _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; - _VtxWritePtr += 3; + // If we're not using a texture, we need the centre vertex as well + for (int i = 0; i < points_count; i++) + { + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; // Centre of line + _VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge + _VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge + _VtxWritePtr += 3; + } } } else { - // Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point + // For untextured lines that are greater than a pixel in width, we need to draw the solid line core and thus require four vertices per point const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; // If line is not closed, the first and last points need to be generated differently as there are no normals to blend @@ -1664,6 +1707,8 @@ ImFontAtlas::ImFontAtlas() TexUvScale = ImVec2(0.0f, 0.0f); TexUvWhitePixel = ImVec2(0.0f, 0.0f); PackIdMouseCursors = -1; + + AALineMaxWidth = 8; } ImFontAtlas::~ImFontAtlas() @@ -2010,6 +2055,7 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) IM_ASSERT(atlas->ConfigData.Size > 0); ImFontAtlasBuildInit(atlas); + ImFontAtlasBuildRegisterAALineCustomRects(atlas); // Clear atlas atlas->TexID = (ImTextureID)NULL; @@ -2331,7 +2377,6 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); } - // Note: this is called / shared by both the stb_truetype and the FreeType builder void ImFontAtlasBuildInit(ImFontAtlas* atlas) { @@ -2346,12 +2391,73 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) } // This is called/shared by both the stb_truetype and the FreeType builder. +const unsigned int FONT_ATLAS_AA_LINE_TEX_HEIGHT = 1; // Technically we only need 1 pixel in the ideal case but this can be increased if necessary to give a border to avoid sampling artifacts + +void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) +{ + if (atlas->AALineRectIds.size() > 0) + return; + + if ((atlas->Flags & ImFontAtlasFlags_NoAALines)) + return; + + const int max = atlas->AALineMaxWidth; + + for (int n = 0; n < max; n++) + { + const int width = n + 1; // The line width this entry corresponds to + // The "width + 3" here is interesting - +2 is to give space for the end caps, but the remaining +1 is because (empirically) to match the behaviour of the untextured render path we need to draw lines one pixel wider + atlas->AALineRectIds.push_back(atlas->AddCustomRectRegular(width + 3, FONT_ATLAS_AA_LINE_TEX_HEIGHT)); + } +} + +void ImFontAtlasBuildAALinesTexData(ImFontAtlas* atlas) +{ + IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); + IM_ASSERT(atlas->TexUvAALines.size() == 0); + + if (atlas->Flags & ImFontAtlasFlags_NoAALines) + return; + + const int w = atlas->TexWidth; + const unsigned int max = atlas->AALineMaxWidth; + + for (unsigned int n = 0; n < max; n++) + { + IM_ASSERT(atlas->AALineRectIds.size() > (int)n); + ImFontAtlas::CustomRect& r = atlas->CustomRects[atlas->AALineRectIds[n]]; + IM_ASSERT(r.IsPacked()); + + // We fill as many lines as we were given, to allow for >1 lines being used to work around sampling weirdness + for (unsigned int y = 0; y < r.Height; y++) + { + unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r.X + ((r.Y + y) * w)]; + + // Each line consists of two empty pixels at the ends, with a line of solid pixels in the middle + *(write_ptr++) = 0; + for (unsigned short x = 0; x < (r.Width - 2U); x++) + { + *(write_ptr++) = 0xFF; + } + *(write_ptr++) = 0; + } + + ImVec2 uv0, uv1; + atlas->CalcCustomRectUV(&r, &uv0, &uv1); + float halfV = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the texture as we want a horizontal slice (with some padding either side to avoid sampling artifacts) + atlas->TexUvAALines.push_back(ImVec4(uv0.x, halfV, uv1.x, halfV)); + } +} + void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { // Render into our custom data blocks IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); ImFontAtlasBuildRenderDefaultTexData(atlas); + // Render anti-aliased line textures + ImFontAtlasBuildAALinesTexData(atlas); + // Register custom rectangle glyphs for (int i = 0; i < atlas->CustomRects.Size; i++) { diff --git a/imgui_internal.h b/imgui_internal.h index b57b01b5..2035f324 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -545,6 +545,8 @@ struct IMGUI_API ImDrawListSharedData ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead) + ImVector* TexUvAALines; // UV of anti-aliased lines in the atlas + ImDrawListSharedData(); void SetCircleSegmentMaxError(float max_error); }; @@ -2018,8 +2020,10 @@ namespace ImGui // ImFontAtlas internals IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildRegisterAALineCustomRects(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); +IMGUI_API void ImFontAtlasBuildAALinesTexData(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); From 741ab74b5575f0b4b5db82db549f07fda130d244 Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Wed, 15 Jan 2020 16:33:09 +0900 Subject: [PATCH 06/18] Texture-based thick lines: Improvements to code for drawing anti-aliased lines using textures Moved line width into a constant Removed test code (now in imgui-tests) Improved matching between geometry and texture rendering at non-integer sizes --- imgui.h | 1 - imgui_demo.cpp | 56 ------------------------------------------------ imgui_draw.cpp | 14 +++++------- imgui_internal.h | 3 +++ 4 files changed, 8 insertions(+), 66 deletions(-) diff --git a/imgui.h b/imgui.h index 09d92309..cc4be5e0 100644 --- a/imgui.h +++ b/imgui.h @@ -2323,7 +2323,6 @@ struct ImFontAtlas // [Internal] Packing data int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors - int AALineMaxWidth; // Maximum line width to build anti-aliased textures for ImVector AALineRectIds; // Custom texture rectangle IDs for anti-aliased lines ImVector TexUvAALines; // UVs for anti-aliased line textures diff --git a/imgui_demo.cpp b/imgui_demo.cpp index f06d8526..2afa7a4f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -297,62 +297,6 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); - // Test lines - - if (ImGui::Begin("Lines")) - { - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - const int num_cols = 16; - const int num_rows = 3; - const float line_len = 64.0f; - const float line_spacing = 128.0f; - - static float base_rot = 0.0f; - ImGui::SliderFloat("Base rotation", &base_rot, 0.0f, 360.0f); - static float line_width = 1.0f; - ImGui::SliderFloat("Line width", &line_width, 1.0f, 10.0f); - - ImVec2 window_pos = ImGui::GetWindowPos(); - ImVec2 cursor_pos = ImGui::GetCursorPos(); - ImVec2 base_pos(window_pos.x + cursor_pos.x + (line_spacing * 0.5f), window_pos.y + cursor_pos.y); - - for (int i = 0; i < num_rows; i++) - { - const char* name = ""; - switch (i) - { - case 0: name = "No AA"; draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; break; - case 1: name = "AA no texturing"; draw_list->Flags |= ImDrawListFlags_AntiAliasedLines; draw_list->Flags &= ~ImDrawListFlags_TexturedAALines; break; - case 2: name = "AA with texturing"; draw_list->Flags |= ImDrawListFlags_AntiAliasedLines; draw_list->Flags |= ImDrawListFlags_TexturedAALines; break; - } - - int initial_vtx_count = draw_list->VtxBuffer.Size; - int initial_idx_count = draw_list->IdxBuffer.Size; - - for (int j = 0; j < num_cols; j++) - { - const float pi = 3.14159265359f; - float r = (base_rot * pi / 180.0f) + ((j * pi * 0.5f) / (num_cols - 1)); - - ImVec2 center = ImVec2(base_pos.x + (line_spacing * (j * 0.5f)), base_pos.y + (line_spacing * (i + 0.5f))); - ImVec2 start = ImVec2(center.x + (sinf(r) * line_len * 0.5f), center.y + (cosf(r) * line_len * 0.5f)); - ImVec2 end = ImVec2(center.x - (sinf(r) * line_len * 0.5f), center.y - (cosf(r) * line_len * 0.5f)); - - draw_list->AddLine(start, end, IM_COL32(255, 255, 255, 255), line_width); - } - - ImGui::SetCursorPosY(cursor_pos.y + (i * line_spacing)); - ImGui::Text("%s - %d vertices, %d indices", name, draw_list->VtxBuffer.Size - initial_vtx_count, draw_list->IdxBuffer.Size - initial_idx_count); - } - - ImGui::SetCursorPosY(cursor_pos.y + (num_rows * line_spacing)); - - //ImGui::Spacing(); ImGui::Spacing(); ImGui::Spacing(); - //ImGui::Image(ImGui::GetFont()->ContainerAtlas->TexID, ImVec2((float)ImGui::GetFont()->ContainerAtlas->TexWidth, (float)ImGui::GetFont()->ContainerAtlas->TexHeight)); - } - ImGui::End(); - // Main body of the Demo window starts here. if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags)) { diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 7c845f94..72ccb963 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -676,13 +676,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const float AA_SIZE = 1.0f; const ImU32 col_trans = col & ~IM_COL32_A_MASK; - const int integer_thickness = (int)thickness; + // The -0.5f here is to better match the geometry-based code, and also shift the transition point from one width texture to another off the integer values (where it will be less noticeable) + const int integer_thickness = ImMax((int)(thickness - 0.5f), 1); // Do we want to draw this line using a texture? - bool use_textures = (Flags & ImDrawListFlags_TexturedAALines) && - (integer_thickness >= 1) && - (integer_thickness <= _Data->Font->ContainerAtlas->AALineMaxWidth) && - ImGui::GetIO().KeyShift; // FIXME-AALINES: Remove this debug code + bool use_textures = (Flags & ImDrawListFlags_TexturedAALines) && (integer_thickness <= IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_TexturedAALines unless ImFontAtlasFlags_NoAALines is off IM_ASSERT_PARANOID((!use_textures) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); @@ -1707,8 +1705,6 @@ ImFontAtlas::ImFontAtlas() TexUvScale = ImVec2(0.0f, 0.0f); TexUvWhitePixel = ImVec2(0.0f, 0.0f); PackIdMouseCursors = -1; - - AALineMaxWidth = 8; } ImFontAtlas::~ImFontAtlas() @@ -2401,7 +2397,7 @@ void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) if ((atlas->Flags & ImFontAtlasFlags_NoAALines)) return; - const int max = atlas->AALineMaxWidth; + const int max = IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; for (int n = 0; n < max; n++) { @@ -2420,7 +2416,7 @@ void ImFontAtlasBuildAALinesTexData(ImFontAtlas* atlas) return; const int w = atlas->TexWidth; - const unsigned int max = atlas->AALineMaxWidth; + const unsigned int max = IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; for (unsigned int n = 0; n < max; n++) { diff --git a/imgui_internal.h b/imgui_internal.h index 2035f324..643aea4d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -529,6 +529,9 @@ struct IMGUI_API ImChunkStream #define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER 1 #endif +// The maximum line width to build anti-aliased textures for +#define IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX 65 + // Data shared between all ImDrawList instances // You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure. struct IMGUI_API ImDrawListSharedData From 222b7ddbfa16635905772faed268e33501ee22ae Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 23 Jan 2020 15:23:23 +0100 Subject: [PATCH 07/18] Texture-based thick lines: Tweaks, fix for truetype builder. --- imgui.cpp | 8 ++--- imgui.h | 4 +-- imgui_demo.cpp | 3 +- imgui_draw.cpp | 84 ++++++++++++++++++++++-------------------------- imgui_internal.h | 2 -- 5 files changed, 46 insertions(+), 55 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 34b63566..e6fe1184 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -935,7 +935,7 @@ ImGuiStyle::ImGuiStyle() DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. - TexturedAntiAliasedLines= true; // Draw anti-aliased lines using textures where possible. + AntiAliasedLinesUseTexData = true; // Draw anti-aliased lines using textures where possible. AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. @@ -3689,14 +3689,12 @@ void ImGui::NewFrame() g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; if (g.Style.AntiAliasedLines) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; - if (g.Style.TexturedAntiAliasedLines) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_TexturedAALines; + if (g.Style.AntiAliasedLinesUseTexData && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines)) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTexData; if (g.Style.AntiAliasedFill) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; - if ((g.Style.TexturedAntiAliasedLines) && (!(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_TexturedAALines; g.BackgroundDrawList._ResetForNewFrame(); g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); diff --git a/imgui.h b/imgui.h index cc4be5e0..f361102c 100644 --- a/imgui.h +++ b/imgui.h @@ -1439,7 +1439,7 @@ struct ImGuiStyle ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. - bool TexturedAntiAliasedLines; // Draw anti-aliased lines using textures where possible. + bool AntiAliasedLinesUseTexData; // Draw anti-aliased lines using textures where possible. bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. float CircleSegmentMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. @@ -1999,7 +1999,7 @@ enum ImDrawListFlags_ ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles) ImDrawListFlags_AntiAliasedFill = 1 << 1, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). ImDrawListFlags_AllowVtxOffset = 1 << 2, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. - ImDrawListFlags_TexturedAALines = 1 << 3 // Should anti-aliased lines be drawn using textures where possible? + ImDrawListFlags_AntiAliasedLinesUseTexData = 1 << 3 // Should anti-aliased lines be drawn using textures where possible? }; // Draw command list diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2afa7a4f..e90fd517 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3831,7 +3831,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) { ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); - ImGui::Checkbox("Use textures for anti-aliased lines", &style.TexturedAntiAliasedLines); + ImGui::Checkbox("Anti-aliased lines use texture data", &style.AntiAliasedLinesUseTexData); + ImGui::SameLine(); HelpMarker("Faster lines using texture data. Requires texture to use bilinear sampling (not nearest)."); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); ImGui::PushItemWidth(100); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 72ccb963..41d25955 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -358,6 +358,7 @@ ImDrawListSharedData::ImDrawListSharedData() ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a)); } memset(CircleSegmentCounts, 0, sizeof(CircleSegmentCounts)); // This will be set by SetCircleSegmentMaxError() + TexUvAALines = NULL; } void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error) @@ -680,18 +681,18 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const int integer_thickness = ImMax((int)(thickness - 0.5f), 1); // Do we want to draw this line using a texture? - bool use_textures = (Flags & ImDrawListFlags_TexturedAALines) && (integer_thickness <= IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); + const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTexData) && (integer_thickness <= IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); - // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_TexturedAALines unless ImFontAtlasFlags_NoAALines is off - IM_ASSERT_PARANOID((!use_textures) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); + // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTexData unless ImFontAtlasFlags_NoAALines is off + IM_ASSERT_PARANOID((!use_texture) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); - const int idx_count = use_textures ? (count * 6) : (thick_line ? count * 18 : count * 12); - const int vtx_count = use_textures ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3); + const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12); + const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3); PrimReserve(idx_count, vtx_count); // Temporary buffer // The first items are normals at each line point, then after that there are either 2 or 4 temp points for each line point - ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((thick_line && !use_textures) ? 5 : 3) * sizeof(ImVec2)); //-V630 + ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((thick_line && !use_texture) ? 5 : 3) * sizeof(ImVec2)); //-V630 ImVec2* temp_points = temp_normals + points_count; // Calculate normals (tangents) for each line segment @@ -708,7 +709,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 temp_normals[points_count - 1] = temp_normals[points_count - 2]; // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point - if ((!thick_line) || (use_textures)) + if (!thick_line || use_texture) { // The width of the geometry we need to draw const float half_draw_size = (!thick_line) ? AA_SIZE : (AA_SIZE + (thickness * 0.5f)); @@ -729,7 +730,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment { const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment - unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_textures ? 2 : 3)); // Vertex index for end of segment + unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment // Average normals float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; @@ -745,7 +746,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 out_vtx[1].x = points[i2].x - dm_x; out_vtx[1].y = points[i2].y - dm_y; - if (use_textures) + if (use_texture) { // Add indices for two triangles _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri @@ -766,7 +767,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 } // Add vertexes for each point on the line - if (use_textures) + if (use_texture) { // If we're using textures we only need to emit the left/right edge vertices const ImVec4 tex_uvs = (*_Data->TexUvAALines)[integer_thickness - 1]; @@ -2051,7 +2052,6 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) IM_ASSERT(atlas->ConfigData.Size > 0); ImFontAtlasBuildInit(atlas); - ImFontAtlasBuildRegisterAALineCustomRects(atlas); // Clear atlas atlas->TexID = (ImTextureID)NULL; @@ -2373,68 +2373,49 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); } -// Note: this is called / shared by both the stb_truetype and the FreeType builder -void ImFontAtlasBuildInit(ImFontAtlas* atlas) -{ - // Register texture region for mouse cursors or standard white pixels - if (atlas->PackIdMouseCursors < 0) - { - if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) - atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H); - else - atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(2, 2); - } -} - // This is called/shared by both the stb_truetype and the FreeType builder. const unsigned int FONT_ATLAS_AA_LINE_TEX_HEIGHT = 1; // Technically we only need 1 pixel in the ideal case but this can be increased if necessary to give a border to avoid sampling artifacts -void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) +static void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) { - if (atlas->AALineRectIds.size() > 0) + if (atlas->AALineRectIds.Size > 0) return; if ((atlas->Flags & ImFontAtlasFlags_NoAALines)) return; - const int max = IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; - - for (int n = 0; n < max; n++) + for (int n = 0; n < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; n++) { + // The "width + 3" here is interesting. +2 is to give space for the end caps, but the remaining +1 is + // because (empirically) to match the behavior of the untextured render path we need to draw lines one pixel wider. const int width = n + 1; // The line width this entry corresponds to - // The "width + 3" here is interesting - +2 is to give space for the end caps, but the remaining +1 is because (empirically) to match the behaviour of the untextured render path we need to draw lines one pixel wider atlas->AALineRectIds.push_back(atlas->AddCustomRectRegular(width + 3, FONT_ATLAS_AA_LINE_TEX_HEIGHT)); } } -void ImFontAtlasBuildAALinesTexData(ImFontAtlas* atlas) +static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) { IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); - IM_ASSERT(atlas->TexUvAALines.size() == 0); + IM_ASSERT(atlas->TexUvAALines.Size == 0); if (atlas->Flags & ImFontAtlasFlags_NoAALines) return; const int w = atlas->TexWidth; - const unsigned int max = IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; - - for (unsigned int n = 0; n < max; n++) + for (unsigned int n = 0; n < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; n++) { - IM_ASSERT(atlas->AALineRectIds.size() > (int)n); - ImFontAtlas::CustomRect& r = atlas->CustomRects[atlas->AALineRectIds[n]]; + IM_ASSERT(atlas->AALineRectIds.Size > (int)n); + ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->AALineRectIds[n]]; IM_ASSERT(r.IsPacked()); // We fill as many lines as we were given, to allow for >1 lines being used to work around sampling weirdness for (unsigned int y = 0; y < r.Height; y++) { - unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r.X + ((r.Y + y) * w)]; - // Each line consists of two empty pixels at the ends, with a line of solid pixels in the middle + unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r.X + ((r.Y + y) * w)]; *(write_ptr++) = 0; for (unsigned short x = 0; x < (r.Width - 2U); x++) - { *(write_ptr++) = 0xFF; - } *(write_ptr++) = 0; } @@ -2445,14 +2426,27 @@ void ImFontAtlasBuildAALinesTexData(ImFontAtlas* atlas) } } +// Note: this is called / shared by both the stb_truetype and the FreeType builder +void ImFontAtlasBuildInit(ImFontAtlas* atlas) +{ + // Register texture region for mouse cursors or standard white pixels + if (atlas->PackIdMouseCursors < 0) + { + if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) + atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H); + else + atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(2, 2); + } + + ImFontAtlasBuildRegisterAALineCustomRects(atlas); +} + +// This is called/shared by both the stb_truetype and the FreeType builder. void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { // Render into our custom data blocks - IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); ImFontAtlasBuildRenderDefaultTexData(atlas); - - // Render anti-aliased line textures - ImFontAtlasBuildAALinesTexData(atlas); + ImFontAtlasBuildRenderAALinesTexData(atlas); // Register custom rectangle glyphs for (int i = 0; i < atlas->CustomRects.Size; i++) diff --git a/imgui_internal.h b/imgui_internal.h index 643aea4d..d38d9b78 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2023,10 +2023,8 @@ namespace ImGui // ImFontAtlas internals IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildRegisterAALineCustomRects(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); -IMGUI_API void ImFontAtlasBuildAALinesTexData(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); From 403bf45245901031e112f8de4828cdd8c7eee805 Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Thu, 6 Feb 2020 14:30:29 +0900 Subject: [PATCH 08/18] Texture-based thick lines: Allow interpolation between textures for non-integer line widths --- imgui.h | 2 +- imgui_draw.cpp | 84 +++++++++++++++++++++++++++++------------------- imgui_internal.h | 2 +- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/imgui.h b/imgui.h index f361102c..47f15fb1 100644 --- a/imgui.h +++ b/imgui.h @@ -2323,7 +2323,7 @@ struct ImFontAtlas // [Internal] Packing data int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors - ImVector AALineRectIds; // Custom texture rectangle IDs for anti-aliased lines + int AALineRectId; // Custom texture rectangle ID for anti-aliased lines ImVector TexUvAALines; // UVs for anti-aliased line textures #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 41d25955..376d4141 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -677,11 +677,12 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const float AA_SIZE = 1.0f; const ImU32 col_trans = col & ~IM_COL32_A_MASK; - // The -0.5f here is to better match the geometry-based code, and also shift the transition point from one width texture to another off the integer values (where it will be less noticeable) - const int integer_thickness = ImMax((int)(thickness - 0.5f), 1); + // The thick_line test is an attempt to compensate for the way half_draw_size gets calculated later, which special-cases 1.0f width lines + const int integer_thickness = thick_line ? ImMax((int)(thickness), 1) : 2; + const float fractional_thickness = thick_line ? (thickness) - integer_thickness : 0.0f; // Do we want to draw this line using a texture? - const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTexData) && (integer_thickness <= IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); + const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTexData) && (integer_thickness < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTexData unless ImFontAtlasFlags_NoAALines is off IM_ASSERT_PARANOID((!use_texture) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); @@ -711,8 +712,9 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point if (!thick_line || use_texture) { - // The width of the geometry we need to draw - const float half_draw_size = (!thick_line) ? AA_SIZE : (AA_SIZE + (thickness * 0.5f)); + // The width of the geometry we need to draw - this is essentially pixels for the line itself, plus one pixel for AA + // We don't use AA_SIZE here because the +1 is tied to the generated texture and so alternate values won't work without changes to that code + const float half_draw_size = (thickness * 0.5f) + 1; // If line is not closed, the first and last points need to be generated differently as there are no normals to blend if (!closed) @@ -770,7 +772,18 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 if (use_texture) { // If we're using textures we only need to emit the left/right edge vertices - const ImVec4 tex_uvs = (*_Data->TexUvAALines)[integer_thickness - 1]; + + ImVec4 tex_uvs; + + if (fractional_thickness == 0.0f) // Fast path for pure integer widths + tex_uvs = (*_Data->TexUvAALines)[integer_thickness]; + else + { + // Calculate UV by interpolating between the two nearest integer line widths + const ImVec4 tex_uvs_0 = (*_Data->TexUvAALines)[integer_thickness]; + const ImVec4 tex_uvs_1 = (*_Data->TexUvAALines)[integer_thickness + 1]; + tex_uvs = ImLerp(tex_uvs_0, tex_uvs_1, fractional_thickness); + } for (int i = 0; i < points_count; i++) { @@ -2373,24 +2386,14 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); } -// This is called/shared by both the stb_truetype and the FreeType builder. -const unsigned int FONT_ATLAS_AA_LINE_TEX_HEIGHT = 1; // Technically we only need 1 pixel in the ideal case but this can be increased if necessary to give a border to avoid sampling artifacts - static void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) { - if (atlas->AALineRectIds.Size > 0) - return; - if ((atlas->Flags & ImFontAtlasFlags_NoAALines)) return; - for (int n = 0; n < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; n++) - { - // The "width + 3" here is interesting. +2 is to give space for the end caps, but the remaining +1 is - // because (empirically) to match the behavior of the untextured render path we need to draw lines one pixel wider. - const int width = n + 1; // The line width this entry corresponds to - atlas->AALineRectIds.push_back(atlas->AddCustomRectRegular(width + 3, FONT_ATLAS_AA_LINE_TEX_HEIGHT)); - } + const int max_width = IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; // The maximum line width we want to generate + // The "max_width + 2" here is to give space for the end caps, whilst height (IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX+1) is to accommodate the fact we have a zero-width row + atlas->AALineRectId = atlas->AddCustomRectRegular(max_width + 2, IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1); } static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) @@ -2401,27 +2404,42 @@ static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) if (atlas->Flags & ImFontAtlasFlags_NoAALines) return; + ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->AALineRectId]; + IM_ASSERT(r.IsPacked()); + + // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them const int w = atlas->TexWidth; - for (unsigned int n = 0; n < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; n++) + for (unsigned int n = 0; n < (IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1); n++) // +1 because of the zero-width row { - IM_ASSERT(atlas->AALineRectIds.Size > (int)n); - ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->AALineRectIds[n]]; - IM_ASSERT(r.IsPacked()); + unsigned int y = n; + unsigned int line_width = n; + // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle + unsigned int pad_left = (r.Width - line_width) / 2; + unsigned int pad_right = r.Width - (pad_left + line_width); - // We fill as many lines as we were given, to allow for >1 lines being used to work around sampling weirdness - for (unsigned int y = 0; y < r.Height; y++) - { - // Each line consists of two empty pixels at the ends, with a line of solid pixels in the middle - unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r.X + ((r.Y + y) * w)]; + // Make sure we're inside the texture bounds before we start writing pixels + IM_ASSERT_PARANOID(pad_left + line_width + pad_right == r.Width); + IM_ASSERT_PARANOID(y < r.Height); + + // Write each slice + unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r.X + ((r.Y + y) * w)]; + for (unsigned int x = 0; x < pad_left; x++) *(write_ptr++) = 0; - for (unsigned short x = 0; x < (r.Width - 2U); x++) - *(write_ptr++) = 0xFF; + for (unsigned int x = 0; x < line_width; x++) + *(write_ptr++) = 0xFF; + for (unsigned int x = 0; x < pad_right; x++) *(write_ptr++) = 0; - } + + // Calculate UVs for this line + ImFontAtlasCustomRect line_rect = r; + line_rect.X += (unsigned short)(pad_left - 1); + line_rect.Width = (unsigned short)(line_width + 2); + line_rect.Y += (unsigned short)y; + line_rect.Height = 1; ImVec2 uv0, uv1; - atlas->CalcCustomRectUV(&r, &uv0, &uv1); - float halfV = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the texture as we want a horizontal slice (with some padding either side to avoid sampling artifacts) + atlas->CalcCustomRectUV(&line_rect, &uv0, &uv1); + float halfV = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts atlas->TexUvAALines.push_back(ImVec4(uv0.x, halfV, uv1.x, halfV)); } } diff --git a/imgui_internal.h b/imgui_internal.h index d38d9b78..4f3882c9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -529,7 +529,7 @@ struct IMGUI_API ImChunkStream #define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER 1 #endif -// The maximum line width to build anti-aliased textures for +// The maximum line width to build anti-aliased textures for (note that this needs to be one greater than the maximum line width you want to be able to draw using the textured path) #define IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX 65 // Data shared between all ImDrawList instances From 21d9e8e1f44b52a1f12eb04f650db99d7c34ac8a Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Tue, 11 Feb 2020 11:23:43 +0900 Subject: [PATCH 09/18] Texture-based thick lines: Simplified line width calculation code and removed hack for thickness 1.0 lines --- imgui_draw.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 376d4141..18ca4bd8 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -677,9 +677,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const float AA_SIZE = 1.0f; const ImU32 col_trans = col & ~IM_COL32_A_MASK; - // The thick_line test is an attempt to compensate for the way half_draw_size gets calculated later, which special-cases 1.0f width lines - const int integer_thickness = thick_line ? ImMax((int)(thickness), 1) : 2; - const float fractional_thickness = thick_line ? (thickness) - integer_thickness : 0.0f; + // Thicknesses <1.0 should behave like thickness 1.0 + thickness = ImMax(thickness, 1.0f); + + const int integer_thickness = (int)thickness ; + const float fractional_thickness = (thickness) - integer_thickness; // Do we want to draw this line using a texture? const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTexData) && (integer_thickness < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); @@ -714,7 +716,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 { // The width of the geometry we need to draw - this is essentially pixels for the line itself, plus one pixel for AA // We don't use AA_SIZE here because the +1 is tied to the generated texture and so alternate values won't work without changes to that code - const float half_draw_size = (thickness * 0.5f) + 1; + const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : 1.0f; // If line is not closed, the first and last points need to be generated differently as there are no normals to blend if (!closed) From a07c8b69991af2988fce91fabab975ffcb976a41 Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 17 Feb 2020 11:58:22 +0100 Subject: [PATCH 10/18] Texture-based thick lines: Fixes for AddCustomRect api, add IMGUI_HAS_TEXLINES define (temporarily) to facilitate working with test cases, Demo allows growing FrameBorderSize for testing --- imgui.h | 2 ++ imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 47f15fb1..e5e54290 100644 --- a/imgui.h +++ b/imgui.h @@ -1993,6 +1993,8 @@ enum ImDrawCornerFlags_ ImDrawCornerFlags_All = 0xF // In your function calls you may use ~0 (= all bits sets) instead of ImDrawCornerFlags_All, as a convenience }; +#define IMGUI_HAS_TEXLINES 1 + enum ImDrawListFlags_ { ImDrawListFlags_None = 0, diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e90fd517..f599e034 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3698,7 +3698,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 4.0f, "%.1f"); ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::Text("Rounding"); ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 18ca4bd8..a4f68f9d 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2393,8 +2393,8 @@ static void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) if ((atlas->Flags & ImFontAtlasFlags_NoAALines)) return; - const int max_width = IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; // The maximum line width we want to generate // The "max_width + 2" here is to give space for the end caps, whilst height (IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX+1) is to accommodate the fact we have a zero-width row + const int max_width = IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; // The maximum line width we want to generate atlas->AALineRectId = atlas->AddCustomRectRegular(max_width + 2, IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1); } From 78d6bdf08036fdeea08ee9e6a0191f058f89dca0 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 16 May 2020 16:55:05 +0200 Subject: [PATCH 11/18] Texture-based thick lines: Remove unnecessary indirection in fetching UV data, removed lerp call, renames, tweaks. --- imgui.cpp | 8 ++--- imgui.h | 17 ++++++---- imgui_demo.cpp | 4 +-- imgui_draw.cpp | 84 +++++++++++++++++++++++------------------------- imgui_internal.h | 5 +-- 5 files changed, 58 insertions(+), 60 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e6fe1184..b9c300fa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -935,7 +935,7 @@ ImGuiStyle::ImGuiStyle() DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. - AntiAliasedLinesUseTexData = true; // Draw anti-aliased lines using textures where possible. + AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Requires back-end to render with bilinear filtering. AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. @@ -3689,8 +3689,8 @@ void ImGui::NewFrame() g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; if (g.Style.AntiAliasedLines) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; - if (g.Style.AntiAliasedLinesUseTexData && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines)) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTexData; + if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAntiAliasedLines)) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex; if (g.Style.AntiAliasedFill) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) @@ -6161,7 +6161,7 @@ void ImGui::SetCurrentFont(ImFont* font) ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; - g.DrawListSharedData.TexUvAALines = &atlas->TexUvAALines; + g.DrawListSharedData.TexUvAALines = atlas->TexUvAALines; g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.FontSize = g.FontSize; } diff --git a/imgui.h b/imgui.h index e5e54290..5e86bae5 100644 --- a/imgui.h +++ b/imgui.h @@ -1439,7 +1439,7 @@ struct ImGuiStyle ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. - bool AntiAliasedLinesUseTexData; // Draw anti-aliased lines using textures where possible. + bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Requires back-end to render with bilinear filtering. bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. float CircleSegmentMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. @@ -1897,6 +1897,11 @@ struct ImColor // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. //----------------------------------------------------------------------------- +// The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoAALines to disable baking. +#ifndef IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX +#define IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX (63) +#endif + // ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h] // NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering, // you can poke into the draw list for that! Draw callback may be useful for example to: @@ -2000,8 +2005,8 @@ enum ImDrawListFlags_ ImDrawListFlags_None = 0, ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles) ImDrawListFlags_AntiAliasedFill = 1 << 1, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). - ImDrawListFlags_AllowVtxOffset = 1 << 2, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. - ImDrawListFlags_AntiAliasedLinesUseTexData = 1 << 3 // Should anti-aliased lines be drawn using textures where possible? + ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 2, // Should anti-aliased lines be drawn using textures where possible? + ImDrawListFlags_AllowVtxOffset = 1 << 3 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. }; // Draw command list @@ -2221,7 +2226,7 @@ enum ImFontAtlasFlags_ ImFontAtlasFlags_None = 0, ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory) - ImFontAtlasFlags_NoAALines = 1 << 2 // Don't build anti-aliased line textures into the atlas + ImFontAtlasFlags_NoAntiAliasedLines = 1 << 2 // Don't build anti-aliased line textures into the atlas (save a little texture memory). They will be rendered using polygons (a little bit more expensive) }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: @@ -2322,11 +2327,11 @@ struct ImFontAtlas ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. ImVector ConfigData; // Configuration data + ImVec4 TexUvAALines[IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1]; // UVs for anti-aliased line textures // [Internal] Packing data int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors - int AALineRectId; // Custom texture rectangle ID for anti-aliased lines - ImVector TexUvAALines; // UVs for anti-aliased line textures + int PackIdAALines; // Custom texture rectangle ID for anti-aliased lines #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index f599e034..8b5d57d4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3698,7 +3698,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 4.0f, "%.1f"); + ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::Text("Rounding"); ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); @@ -3831,7 +3831,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) { ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); - ImGui::Checkbox("Anti-aliased lines use texture data", &style.AntiAliasedLinesUseTexData); + ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex); ImGui::SameLine(); HelpMarker("Faster lines using texture data. Requires texture to use bilinear sampling (not nearest)."); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); ImGui::PushItemWidth(100); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index a4f68f9d..24e8a654 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -668,7 +668,6 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const ImVec2 opaque_uv = _Data->TexUvWhitePixel; const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw - const bool thick_line = (thickness > 1.0f); if (Flags & ImDrawListFlags_AntiAliasedLines) @@ -679,12 +678,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 // Thicknesses <1.0 should behave like thickness 1.0 thickness = ImMax(thickness, 1.0f); - - const int integer_thickness = (int)thickness ; - const float fractional_thickness = (thickness) - integer_thickness; + const int integer_thickness = (int)thickness; + const float fractional_thickness = thickness - integer_thickness; // Do we want to draw this line using a texture? - const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTexData) && (integer_thickness < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); + const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTexData unless ImFontAtlasFlags_NoAALines is off IM_ASSERT_PARANOID((!use_texture) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); @@ -712,8 +710,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 temp_normals[points_count - 1] = temp_normals[points_count - 2]; // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point - if (!thick_line || use_texture) + if (use_texture || !thick_line) { + // [PATH 1] Texture-based lines (thick or non-thick) + // [PATH 2] Non texture-based lines (non-thick) + // The width of the geometry we need to draw - this is essentially pixels for the line itself, plus one pixel for AA // We don't use AA_SIZE here because the +1 is tied to the generated texture and so alternate values won't work without changes to that code const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : 1.0f; @@ -734,7 +735,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment { const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment - unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment + const unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment // Average normals float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; @@ -774,32 +775,30 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 if (use_texture) { // If we're using textures we only need to emit the left/right edge vertices - - ImVec4 tex_uvs; - - if (fractional_thickness == 0.0f) // Fast path for pure integer widths - tex_uvs = (*_Data->TexUvAALines)[integer_thickness]; - else + ImVec4 tex_uvs = _Data->TexUvAALines[integer_thickness]; + if (fractional_thickness != 0.0f) { - // Calculate UV by interpolating between the two nearest integer line widths - const ImVec4 tex_uvs_0 = (*_Data->TexUvAALines)[integer_thickness]; - const ImVec4 tex_uvs_1 = (*_Data->TexUvAALines)[integer_thickness + 1]; - tex_uvs = ImLerp(tex_uvs_0, tex_uvs_1, fractional_thickness); + const ImVec4 tex_uvs_1 = _Data->TexUvAALines[integer_thickness + 1]; + tex_uvs.x = tex_uvs.x + (tex_uvs_1.x - tex_uvs.x) * fractional_thickness; // inlined ImLerp() + tex_uvs.y = tex_uvs.y + (tex_uvs_1.y - tex_uvs.y) * fractional_thickness; + tex_uvs.z = tex_uvs.z + (tex_uvs_1.z - tex_uvs.z) * fractional_thickness; + tex_uvs.w = tex_uvs.w + (tex_uvs_1.w - tex_uvs.w) * fractional_thickness; } - + ImVec2 tex_uv0(tex_uvs.x, tex_uvs.y); + ImVec2 tex_uv1(tex_uvs.z, tex_uvs.w); for (int i = 0; i < points_count; i++) { - _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = ImVec2(tex_uvs.x, tex_uvs.y); _VtxWritePtr[0].col = col; // Left-side outer edge - _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = ImVec2(tex_uvs.z, tex_uvs.y); _VtxWritePtr[1].col = col; // Right-side outer edge + _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = tex_uv0; _VtxWritePtr[0].col = col; // Left-side outer edge + _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = tex_uv1; _VtxWritePtr[1].col = col; // Right-side outer edge _VtxWritePtr += 2; } } else { - // If we're not using a texture, we need the centre vertex as well + // If we're not using a texture, we need the center vertex as well for (int i = 0; i < points_count; i++) { - _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; // Centre of line + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; // Center of line _VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge _VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge _VtxWritePtr += 3; @@ -808,7 +807,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 } else { - // For untextured lines that are greater than a pixel in width, we need to draw the solid line core and thus require four vertices per point + // [PATH 2] Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; // If line is not closed, the first and last points need to be generated differently as there are no normals to blend @@ -880,7 +879,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 } else { - // Non texture-based, Non anti-aliased lines + // [PATH 4] Non texture-based, Non anti-aliased lines const int idx_count = count * 6; const int vtx_count = count * 4; // FIXME-OPT: Not sharing edges PrimReserve(idx_count, vtx_count); @@ -1720,7 +1719,7 @@ ImFontAtlas::ImFontAtlas() TexWidth = TexHeight = 0; TexUvScale = ImVec2(0.0f, 0.0f); TexUvWhitePixel = ImVec2(0.0f, 0.0f); - PackIdMouseCursors = -1; + PackIdMouseCursors = PackIdAALines = -1; } ImFontAtlas::~ImFontAtlas() @@ -1748,7 +1747,7 @@ void ImFontAtlas::ClearInputData() } ConfigData.clear(); CustomRects.clear(); - PackIdMouseCursors = -1; + PackIdMouseCursors = PackIdAALines = -1; } void ImFontAtlas::ClearTexData() @@ -2390,41 +2389,38 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) static void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) { - if ((atlas->Flags & ImFontAtlasFlags_NoAALines)) + if (atlas->Flags & ImFontAtlasFlags_NoAntiAliasedLines) return; - // The "max_width + 2" here is to give space for the end caps, whilst height (IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX+1) is to accommodate the fact we have a zero-width row - const int max_width = IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; // The maximum line width we want to generate - atlas->AALineRectId = atlas->AddCustomRectRegular(max_width + 2, IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1); + // The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row + atlas->PackIdAALines = atlas->AddCustomRectRegular(IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1); } static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) { IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); - IM_ASSERT(atlas->TexUvAALines.Size == 0); - - if (atlas->Flags & ImFontAtlasFlags_NoAALines) + if (atlas->Flags & ImFontAtlasFlags_NoAntiAliasedLines) return; - ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->AALineRectId]; - IM_ASSERT(r.IsPacked()); + ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdAALines); + IM_ASSERT(r->IsPacked()); // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them const int w = atlas->TexWidth; - for (unsigned int n = 0; n < (IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1); n++) // +1 because of the zero-width row + for (unsigned int n = 0; n < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row { + // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle unsigned int y = n; unsigned int line_width = n; - // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle - unsigned int pad_left = (r.Width - line_width) / 2; - unsigned int pad_right = r.Width - (pad_left + line_width); + unsigned int pad_left = (r->Width - line_width) / 2; + unsigned int pad_right = r->Width - (pad_left + line_width); // Make sure we're inside the texture bounds before we start writing pixels IM_ASSERT_PARANOID(pad_left + line_width + pad_right == r.Width); IM_ASSERT_PARANOID(y < r.Height); // Write each slice - unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r.X + ((r.Y + y) * w)]; + unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * w)]; for (unsigned int x = 0; x < pad_left; x++) *(write_ptr++) = 0; for (unsigned int x = 0; x < line_width; x++) @@ -2433,16 +2429,16 @@ static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) *(write_ptr++) = 0; // Calculate UVs for this line - ImFontAtlasCustomRect line_rect = r; + ImFontAtlasCustomRect line_rect = *r; line_rect.X += (unsigned short)(pad_left - 1); - line_rect.Width = (unsigned short)(line_width + 2); line_rect.Y += (unsigned short)y; + line_rect.Width = (unsigned short)(line_width + 2); line_rect.Height = 1; ImVec2 uv0, uv1; atlas->CalcCustomRectUV(&line_rect, &uv0, &uv1); - float halfV = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts - atlas->TexUvAALines.push_back(ImVec4(uv0.x, halfV, uv1.x, halfV)); + float half_v = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts + atlas->TexUvAALines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v); } } diff --git a/imgui_internal.h b/imgui_internal.h index 4f3882c9..629ddc32 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -529,9 +529,6 @@ struct IMGUI_API ImChunkStream #define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER 1 #endif -// The maximum line width to build anti-aliased textures for (note that this needs to be one greater than the maximum line width you want to be able to draw using the textured path) -#define IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX 65 - // Data shared between all ImDrawList instances // You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure. struct IMGUI_API ImDrawListSharedData @@ -548,7 +545,7 @@ struct IMGUI_API ImDrawListSharedData ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead) - ImVector* TexUvAALines; // UV of anti-aliased lines in the atlas + const ImVec4* TexUvAALines; // UV of anti-aliased lines in the atlas ImDrawListSharedData(); void SetCircleSegmentMaxError(float max_error); From b5bae9781df1204a7dc6ae8b4b1f605c05f0fd74 Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Tue, 7 Jul 2020 16:19:27 +0900 Subject: [PATCH 12/18] Texture-based thick lines: Only use textured lines for integer line widths --- imgui_draw.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 24e8a654..15a6c6f2 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -681,8 +681,8 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const int integer_thickness = (int)thickness; const float fractional_thickness = thickness - integer_thickness; - // Do we want to draw this line using a texture? - const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); + // Do we want to draw this line using a texture? (for now, only draw integer-width lines using textures to avoid issues with the way scaling occurs) + const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX) && (fractional_thickness >= -0.00001f) && (fractional_thickness <= 0.00001f); // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTexData unless ImFontAtlasFlags_NoAALines is off IM_ASSERT_PARANOID((!use_texture) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); From 3a6c9907cd182795f82c67df5d77e88bcfe9563c Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 7 Jul 2020 13:24:10 +0200 Subject: [PATCH 13/18] Texture-based thick lines: Minor tweaks and rename toward merging in master. Changes to allow changing AA_SIZE (disable texture path). --- docs/CHANGELOG.txt | 9 +++++ imgui.cpp | 6 ++-- imgui.h | 28 +++++++-------- imgui_demo.cpp | 2 +- imgui_draw.cpp | 85 +++++++++++++++++++--------------------------- imgui_internal.h | 3 +- 6 files changed, 63 insertions(+), 70 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c62a140d..8043bf47 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,6 +35,15 @@ HOW TO UPDATE? VERSION 1.78 WIP (In Progress) ----------------------------------------------------------------------- +Other Changes: + +- ImDrawList: Thick anti-aliased strokes (> 1.0f) with integer thickness now use a texture-based + path, reducing the amount of vertices/indices and CPU/GPU usage. (#3245) [@Shironekoben] + - This change will facilitate the wider use of thick borders in future style changes. + - Requires an extra bit of texture space (~64x64 by default), relies on GPU bilinear filtering. + - Clear io.AntiAliasedLinesUseTex = false; to disable rendering using this method. + - Clear ImFontAtlasFlags_NoBakedLines in ImFontAtlas::Flags to disable baking data in texture. + ----------------------------------------------------------------------- VERSION 1.77 (Released 2020-06-29) diff --git a/imgui.cpp b/imgui.cpp index b9c300fa..37e611ad 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -935,7 +935,7 @@ ImGuiStyle::ImGuiStyle() DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. - AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Requires back-end to render with bilinear filtering. + AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require back-end to render with bilinear filtering. AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. @@ -3689,7 +3689,7 @@ void ImGui::NewFrame() g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; if (g.Style.AntiAliasedLines) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; - if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAntiAliasedLines)) + if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex; if (g.Style.AntiAliasedFill) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; @@ -6161,7 +6161,7 @@ void ImGui::SetCurrentFont(ImFont* font) ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; - g.DrawListSharedData.TexUvAALines = atlas->TexUvAALines; + g.DrawListSharedData.TexUvLines = atlas->TexUvLines; g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.FontSize = g.FontSize; } diff --git a/imgui.h b/imgui.h index 5e86bae5..6f4dec52 100644 --- a/imgui.h +++ b/imgui.h @@ -60,7 +60,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.78 WIP" -#define IMGUI_VERSION_NUM 17702 +#define IMGUI_VERSION_NUM 17703 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) // Define attributes of all API symbols declarations (e.g. for DLL under Windows) @@ -1438,9 +1438,9 @@ struct ImGuiStyle ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. - bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. - bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Requires back-end to render with bilinear filtering. - bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. + bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). + bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require back-end to render with bilinear filtering. Latched at the beginning of the frame (copied to ImDrawList). + bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. float CircleSegmentMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. ImVec4 Colors[ImGuiCol_COUNT]; @@ -1897,9 +1897,9 @@ struct ImColor // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. //----------------------------------------------------------------------------- -// The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoAALines to disable baking. -#ifndef IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX -#define IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX (63) +// The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking. +#ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX +#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX (63) #endif // ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h] @@ -1998,14 +1998,14 @@ enum ImDrawCornerFlags_ ImDrawCornerFlags_All = 0xF // In your function calls you may use ~0 (= all bits sets) instead of ImDrawCornerFlags_All, as a convenience }; -#define IMGUI_HAS_TEXLINES 1 - +// Flags for ImDrawList. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. +// It is however possible to temporarily alter flags between calls to ImDrawList:: functions. enum ImDrawListFlags_ { ImDrawListFlags_None = 0, ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles) - ImDrawListFlags_AntiAliasedFill = 1 << 1, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). - ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 2, // Should anti-aliased lines be drawn using textures where possible? + ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require back-end to render with bilinear filtering. + ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). ImDrawListFlags_AllowVtxOffset = 1 << 3 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. }; @@ -2226,7 +2226,7 @@ enum ImFontAtlasFlags_ ImFontAtlasFlags_None = 0, ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory) - ImFontAtlasFlags_NoAntiAliasedLines = 1 << 2 // Don't build anti-aliased line textures into the atlas (save a little texture memory). They will be rendered using polygons (a little bit more expensive) + ImFontAtlasFlags_NoBakedLines = 1 << 2 // Don't build thick line textures into the atlas (save a little texture memory). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU). }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: @@ -2327,11 +2327,11 @@ struct ImFontAtlas ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. ImVector ConfigData; // Configuration data - ImVec4 TexUvAALines[IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1]; // UVs for anti-aliased line textures + ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines // [Internal] Packing data int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors - int PackIdAALines; // Custom texture rectangle ID for anti-aliased lines + int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8b5d57d4..d66bf014 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3832,7 +3832,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex); - ImGui::SameLine(); HelpMarker("Faster lines using texture data. Requires texture to use bilinear sampling (not nearest)."); + ImGui::SameLine(); HelpMarker("Faster lines using texture data. Require back-end to render with bilinear filtering (not point/nearest filtering)."); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); ImGui::PushItemWidth(100); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 15a6c6f2..942c2d90 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -358,7 +358,7 @@ ImDrawListSharedData::ImDrawListSharedData() ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a)); } memset(CircleSegmentCounts, 0, sizeof(CircleSegmentCounts)); // This will be set by SetCircleSegmentMaxError() - TexUvAALines = NULL; + TexUvLines = NULL; } void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error) @@ -681,11 +681,13 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const int integer_thickness = (int)thickness; const float fractional_thickness = thickness - integer_thickness; - // Do we want to draw this line using a texture? (for now, only draw integer-width lines using textures to avoid issues with the way scaling occurs) - const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX) && (fractional_thickness >= -0.00001f) && (fractional_thickness <= 0.00001f); + // Do we want to draw this line using a texture? + // - For now, only draw integer-width lines using textures to avoid issues with the way scaling occurs, could be improved. + // - If AA_SIZE is not 1.0f we cannot use the texture path. + const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f); - // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTexData unless ImFontAtlasFlags_NoAALines is off - IM_ASSERT_PARANOID((!use_texture) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); + // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off + IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)); const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12); const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3); @@ -693,7 +695,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 // Temporary buffer // The first items are normals at each line point, then after that there are either 2 or 4 temp points for each line point - ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((thick_line && !use_texture) ? 5 : 3) * sizeof(ImVec2)); //-V630 + ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((use_texture || !thick_line) ? 3 : 5) * sizeof(ImVec2)); //-V630 ImVec2* temp_points = temp_normals + points_count; // Calculate normals (tangents) for each line segment @@ -717,7 +719,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 // The width of the geometry we need to draw - this is essentially pixels for the line itself, plus one pixel for AA // We don't use AA_SIZE here because the +1 is tied to the generated texture and so alternate values won't work without changes to that code - const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : 1.0f; + const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : AA_SIZE; // If line is not closed, the first and last points need to be generated differently as there are no normals to blend if (!closed) @@ -775,10 +777,10 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 if (use_texture) { // If we're using textures we only need to emit the left/right edge vertices - ImVec4 tex_uvs = _Data->TexUvAALines[integer_thickness]; + ImVec4 tex_uvs = _Data->TexUvLines[integer_thickness]; if (fractional_thickness != 0.0f) { - const ImVec4 tex_uvs_1 = _Data->TexUvAALines[integer_thickness + 1]; + const ImVec4 tex_uvs_1 = _Data->TexUvLines[integer_thickness + 1]; tex_uvs.x = tex_uvs.x + (tex_uvs_1.x - tex_uvs.x) * fractional_thickness; // inlined ImLerp() tex_uvs.y = tex_uvs.y + (tex_uvs_1.y - tex_uvs.y) * fractional_thickness; tex_uvs.z = tex_uvs.z + (tex_uvs_1.z - tex_uvs.z) * fractional_thickness; @@ -1719,7 +1721,7 @@ ImFontAtlas::ImFontAtlas() TexWidth = TexHeight = 0; TexUvScale = ImVec2(0.0f, 0.0f); TexUvWhitePixel = ImVec2(0.0f, 0.0f); - PackIdMouseCursors = PackIdAALines = -1; + PackIdMouseCursors = PackIdLines = -1; } ImFontAtlas::~ImFontAtlas() @@ -1747,7 +1749,7 @@ void ImFontAtlas::ClearInputData() } ConfigData.clear(); CustomRects.clear(); - PackIdMouseCursors = PackIdAALines = -1; + PackIdMouseCursors = PackIdLines = -1; } void ImFontAtlas::ClearTexData() @@ -2387,27 +2389,15 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); } -static void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) +static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas) { - if (atlas->Flags & ImFontAtlasFlags_NoAntiAliasedLines) + if (atlas->Flags & ImFontAtlasFlags_NoBakedLines) return; - // The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row - atlas->PackIdAALines = atlas->AddCustomRectRegular(IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1); -} - -static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) -{ - IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); - if (atlas->Flags & ImFontAtlasFlags_NoAntiAliasedLines) - return; - - ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdAALines); - IM_ASSERT(r->IsPacked()); - // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them - const int w = atlas->TexWidth; - for (unsigned int n = 0; n < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row + ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdLines); + IM_ASSERT(r->IsPacked()); + for (unsigned int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row { // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle unsigned int y = n; @@ -2415,30 +2405,18 @@ static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) unsigned int pad_left = (r->Width - line_width) / 2; unsigned int pad_right = r->Width - (pad_left + line_width); - // Make sure we're inside the texture bounds before we start writing pixels - IM_ASSERT_PARANOID(pad_left + line_width + pad_right == r.Width); - IM_ASSERT_PARANOID(y < r.Height); - // Write each slice - unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * w)]; - for (unsigned int x = 0; x < pad_left; x++) - *(write_ptr++) = 0; - for (unsigned int x = 0; x < line_width; x++) - *(write_ptr++) = 0xFF; - for (unsigned int x = 0; x < pad_right; x++) - *(write_ptr++) = 0; + IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels + unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)]; + memset(write_ptr, 0x00, pad_left); + memset(write_ptr + pad_left, 0xFF, line_width); + memset(write_ptr + pad_left + line_width, 0x00, pad_right); // Calculate UVs for this line - ImFontAtlasCustomRect line_rect = *r; - line_rect.X += (unsigned short)(pad_left - 1); - line_rect.Y += (unsigned short)y; - line_rect.Width = (unsigned short)(line_width + 2); - line_rect.Height = 1; - - ImVec2 uv0, uv1; - atlas->CalcCustomRectUV(&line_rect, &uv0, &uv1); + ImVec2 uv0 = ImVec2((float)(r->X + pad_left - 1), (float)(r->Y + y)) * atlas->TexUvScale; + ImVec2 uv1 = ImVec2((float)(r->X + pad_left + line_width + 1), (float)(r->Y + y + 1)) * atlas->TexUvScale; float half_v = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts - atlas->TexUvAALines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v); + atlas->TexUvLines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v); } } @@ -2454,15 +2432,22 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(2, 2); } - ImFontAtlasBuildRegisterAALineCustomRects(atlas); + // Register texture region for thick lines + // The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row + if (atlas->PackIdLines < 0) + { + if (!(atlas->Flags & ImFontAtlasFlags_NoBakedLines)) + atlas->PackIdLines = atlas->AddCustomRectRegular(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1); + } } // This is called/shared by both the stb_truetype and the FreeType builder. void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { // Render into our custom data blocks + IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); ImFontAtlasBuildRenderDefaultTexData(atlas); - ImFontAtlasBuildRenderAALinesTexData(atlas); + ImFontAtlasBuildRenderLinesTexData(atlas); // Register custom rectangle glyphs for (int i = 0; i < atlas->CustomRects.Size; i++) diff --git a/imgui_internal.h b/imgui_internal.h index 629ddc32..a6245d1a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -544,8 +544,7 @@ struct IMGUI_API ImDrawListSharedData // [Internal] Lookup tables ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead) - - const ImVec4* TexUvAALines; // UV of anti-aliased lines in the atlas + const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas ImDrawListSharedData(); void SetCircleSegmentMaxError(float max_error); From 89685b346c7b02b7d557678fd9d5d11a80967f83 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 9 Jul 2020 11:21:31 +0200 Subject: [PATCH 14/18] ImDrawList: Fixed minor bug introduced in 1.75 where AddCircle() with 12 segments would generate an extra unrequired vertex. Actual missing code for d3b37180a3ff186b481d93d09664bb244a428d10, thanks @domgho! --- docs/CHANGELOG.txt | 8 ++++---- imgui_draw.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8043bf47..8f4de8d9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,12 +37,14 @@ HOW TO UPDATE? Other Changes: -- ImDrawList: Thick anti-aliased strokes (> 1.0f) with integer thickness now use a texture-based +- ImDrawList: Thick anti-aliased strokes (> 1.0f) with integer thickness now use a texture-based path, reducing the amount of vertices/indices and CPU/GPU usage. (#3245) [@Shironekoben] - - This change will facilitate the wider use of thick borders in future style changes. + - This change will facilitate the wider use of thick borders in future style changes. - Requires an extra bit of texture space (~64x64 by default), relies on GPU bilinear filtering. - Clear io.AntiAliasedLinesUseTex = false; to disable rendering using this method. - Clear ImFontAtlasFlags_NoBakedLines in ImFontAtlas::Flags to disable baking data in texture. +- ImDrawList: Fixed minor bug introduced in 1.75 where AddCircle() with 12 segments would generate + an extra vertex. (This bug was mistakenly marked as fixed in earlier 1.77 release). [@ShironekoBen] ----------------------------------------------------------------------- @@ -112,8 +114,6 @@ Other Changes: VtxOffset was not zero would lead to draw commands with wrong VtxOffset. (#2591) - ImDrawList, ImDrawListSplitter, Columns: Fixed an issue where starting a split right after a callback draw command would incorrectly override the callback draw command. -- ImDrawList: Fixed minor bug introduced in 1.75 where AddCircle() with 12 segments would - generate an extra unrequired vertex. [@ShironekoBen] - Misc, Freetype: Fix for rare case where FT_Get_Char_Index() succeeds but FT_Load_Glyph() fails. - Docs: Improved and moved font documentation to docs/FONTS.md so it can be readable on the web. Updated various links/wiki accordingly. Added FAQ entry about DPI. (#2861) [@ButternCream, @ocornut] diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 942c2d90..cdaa34fa 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1235,7 +1235,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; if (num_segments == 12) - PathArcToFast(center, radius - 0.5f, 0, 12); + PathArcToFast(center, radius - 0.5f, 0, 12 - 1); else PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); PathStroke(col, true, thickness); @@ -1265,7 +1265,7 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; if (num_segments == 12) - PathArcToFast(center, radius, 0, 12); + PathArcToFast(center, radius, 0, 12 - 1); else PathArcTo(center, radius, 0.0f, a_max, num_segments - 1); PathFillConvex(col); From 06f7854b160200ac942aba1137e76be7600d7fc5 Mon Sep 17 00:00:00 2001 From: Moritz Heinemann Date: Wed, 1 Jul 2020 23:13:59 +0200 Subject: [PATCH 15/18] Backends: OpenGL3: Add glad 2 to OpenGL loaders. (#3330) --- examples/example_glfw_opengl3/Makefile | 4 ++++ examples/example_glfw_opengl3/main.cpp | 4 ++++ examples/example_sdl_opengl3/Makefile | 4 ++++ examples/example_sdl_opengl3/main.cpp | 4 ++++ examples/imgui_impl_opengl3.cpp | 5 +++++ examples/imgui_impl_opengl3.h | 3 +++ 6 files changed, 24 insertions(+) diff --git a/examples/example_glfw_opengl3/Makefile b/examples/example_glfw_opengl3/Makefile index 3bc72b6f..0e7faa12 100644 --- a/examples/example_glfw_opengl3/Makefile +++ b/examples/example_glfw_opengl3/Makefile @@ -42,6 +42,10 @@ CXXFLAGS += -I../libs/gl3w -DIMGUI_IMPL_OPENGL_LOADER_GL3W # SOURCES += ../libs/glad/src/glad.c # CXXFLAGS += -I../libs/glad/include -DIMGUI_IMPL_OPENGL_LOADER_GLAD +## Using OpenGL loader: glad2 +# SOURCES += ../libs/glad/src/gl.c +# CXXFLAGS += -I../libs/glad/include -DIMGUI_IMPL_OPENGL_LOADER_GLAD2 + ## Using OpenGL loader: glbinding ## This assumes a system-wide installation ## of either version 3.0.0 (or newer) diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 6a3592f1..fa4e4bb9 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -17,6 +17,8 @@ #include // Initialize with glewInit() #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) #include // Initialize with gladLoadGL() +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) +#include // Initialize with gladLoadGL(...) or gladLoaderLoadGL() #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) #define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors. #include // Initialize with glbinding::Binding::initialize() @@ -84,6 +86,8 @@ int main(int, char**) bool err = glewInit() != GLEW_OK; #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) bool err = gladLoadGL() == 0; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) + bool err = gladLoadGL(glfwGetProcAddress) == 0; // glad docs recommend using the windowing library loader instead of the (optionally) bundled one. #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) bool err = false; glbinding::Binding::initialize(); diff --git a/examples/example_sdl_opengl3/Makefile b/examples/example_sdl_opengl3/Makefile index 41826741..e8009a4c 100644 --- a/examples/example_sdl_opengl3/Makefile +++ b/examples/example_sdl_opengl3/Makefile @@ -42,6 +42,10 @@ CXXFLAGS += -I../libs/gl3w -DIMGUI_IMPL_OPENGL_LOADER_GL3W # SOURCES += ../libs/glad/src/glad.c # CXXFLAGS += -I../libs/glad/include -DIMGUI_IMPL_OPENGL_LOADER_GLAD +## Using OpenGL loader: glad2 +# SOURCES += ../libs/glad/src/gl.c +# CXXFLAGS += -I../libs/glad/include -DIMGUI_IMPL_OPENGL_LOADER_GLAD2 + ## Using OpenGL loader: glbinding ## This assumes a system-wide installation ## of either version 3.0.0 (or newer) diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 3146084d..96e5617b 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -19,6 +19,8 @@ #include // Initialize with glewInit() #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) #include // Initialize with gladLoadGL() +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) +#include // Initialize with gladLoadGL(...) or gladLoaderLoadGL() #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) #define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors. #include // Initialize with glbinding::Binding::initialize() @@ -79,6 +81,8 @@ int main(int, char**) bool err = glewInit() != GLEW_OK; #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) bool err = gladLoadGL() == 0; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) + bool err = gladLoadGL((GLADloadfunc) SDL_GL_GetProcAddress) == 0; // glad docs recommend using the windowing library loader instead of the (optionally) bundled one. #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) bool err = false; glbinding::Binding::initialize(); diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 028a704b..df64bfa2 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -13,6 +13,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2020-07-01: OpenGL: Added support for glad2 OpenGL loader. // 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX. // 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix. // 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset. @@ -101,6 +102,8 @@ #include // Needs to be initialized with glewInit() in user's code. #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) #include // Needs to be initialized with gladLoadGL() in user's code. +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) +#include // Needs to be initialized with gladLoadGL(...) or gladLoaderLoadGL() in user's code. #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) #ifndef GLFW_INCLUDE_NONE #define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors. @@ -189,6 +192,8 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) gl_loader = "GLEW"; #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) gl_loader = "GLAD"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) + gl_loader = "GLAD2"; #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) gl_loader = "glbinding2"; #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) diff --git a/examples/imgui_impl_opengl3.h b/examples/imgui_impl_opengl3.h index 07d35219..14eb2842 100644 --- a/examples/imgui_impl_opengl3.h +++ b/examples/imgui_impl_opengl3.h @@ -49,6 +49,7 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); && !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) @@ -68,6 +69,8 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); #define IMGUI_IMPL_OPENGL_LOADER_GLEW #elif __has_include() #define IMGUI_IMPL_OPENGL_LOADER_GLAD +#elif __has_include() + #define IMGUI_IMPL_OPENGL_LOADER_GLAD2 #elif __has_include() #define IMGUI_IMPL_OPENGL_LOADER_GL3W #elif __has_include() From fb7f6cab8c322731da336e553915e944bf386e62 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 10 Jul 2020 14:36:00 +0200 Subject: [PATCH 16/18] Backends: Amend, docs + extra comments. (#3330, #3245) --- docs/CHANGELOG.txt | 1 + examples/README.txt | 4 ++-- examples/example_glfw_opengl3/main.cpp | 2 +- examples/example_sdl_opengl3/main.cpp | 2 +- examples/imgui_impl_opengl3.cpp | 2 +- imgui_draw.cpp | 7 +++++-- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8f4de8d9..6ee96d78 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,7 @@ Other Changes: - Clear ImFontAtlasFlags_NoBakedLines in ImFontAtlas::Flags to disable baking data in texture. - ImDrawList: Fixed minor bug introduced in 1.75 where AddCircle() with 12 segments would generate an extra vertex. (This bug was mistakenly marked as fixed in earlier 1.77 release). [@ShironekoBen] +- Backends: OpenGL3: Added support for glad2 loader. (#3330) [@moritz-h] ----------------------------------------------------------------------- diff --git a/examples/README.txt b/examples/README.txt index 35ee87b9..8c89ef00 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -204,7 +204,7 @@ example_glfw_opengl2/ GLFW + OpenGL2 example (legacy, fixed pipeline). = main.cpp + imgui_impl_glfw.cpp + imgui_impl_opengl2.cpp **DO NOT USE OPENGL2 CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** - **Prefer using OPENGL3 code (with gl3w/glew/glad/glbinding, you can replace the OpenGL function loader)** + **Prefer using OPENGL3 code (with gl3w/glew/glad/glad2/glbinding, you can replace the OpenGL function loader)** This code is mostly provided as a reference to learn about Dear ImGui integration, because it is shorter. If your code is using GL3+ context or any semi modern OpenGL calls, using this renderer is likely to make things more complicated, will require your code to reset many OpenGL attributes to their initial @@ -253,7 +253,7 @@ example_sdl_opengl2/ SDL2 (Win32, Mac, Linux etc.) + OpenGL example (legacy, fixed pipeline). = main.cpp + imgui_impl_sdl.cpp + imgui_impl_opengl2.cpp **DO NOT USE OPENGL2 CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** - **Prefer using OPENGL3 code (with gl3w/glew/glad/glbinding, you can replace the OpenGL function loader)** + **Prefer using OPENGL3 code (with gl3w/glew/glad/glad2/glbinding, you can replace the OpenGL function loader)** This code is mostly provided as a reference to learn about Dear ImGui integration, because it is shorter. If your code is using GL3+ context or any semi modern OpenGL calls, using this renderer is likely to make things more complicated, will require your code to reset many OpenGL attributes to their initial diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index fa4e4bb9..30943301 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -87,7 +87,7 @@ int main(int, char**) #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) bool err = gladLoadGL() == 0; #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) - bool err = gladLoadGL(glfwGetProcAddress) == 0; // glad docs recommend using the windowing library loader instead of the (optionally) bundled one. + bool err = gladLoadGL(glfwGetProcAddress) == 0; // glad2 recommend using the windowing library loader instead of the (optionally) bundled one. #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) bool err = false; glbinding::Binding::initialize(); diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 96e5617b..92d03138 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -82,7 +82,7 @@ int main(int, char**) #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) bool err = gladLoadGL() == 0; #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) - bool err = gladLoadGL((GLADloadfunc) SDL_GL_GetProcAddress) == 0; // glad docs recommend using the windowing library loader instead of the (optionally) bundled one. + bool err = gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress) == 0; // glad2 recommend using the windowing library loader instead of the (optionally) bundled one. #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) bool err = false; glbinding::Binding::initialize(); diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index df64bfa2..cb60304d 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -13,7 +13,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2020-07-01: OpenGL: Added support for glad2 OpenGL loader. +// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader. // 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX. // 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix. // 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index cdaa34fa..864364e9 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -717,8 +717,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 // [PATH 1] Texture-based lines (thick or non-thick) // [PATH 2] Non texture-based lines (non-thick) - // The width of the geometry we need to draw - this is essentially pixels for the line itself, plus one pixel for AA - // We don't use AA_SIZE here because the +1 is tied to the generated texture and so alternate values won't work without changes to that code + // The width of the geometry we need to draw - this is essentially pixels for the line itself, plus "one pixel" for AA. + // - In the texture-based path, we don't use AA_SIZE here because the +1 is tied to the generated texture + // (see ImFontAtlasBuildRenderLinesTexData() function), and so alternate values won't work without changes to that code. + // - In the non texture-based paths, we would allow AA_SIZE to potentially be != 1.0f with a patch (e.g. fringe_scale patch to + // allow scaling geometry while preserving one-screen-pixel AA fringe). const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : AA_SIZE; // If line is not closed, the first and last points need to be generated differently as there are no normals to blend From 550f110354fc922d1a471906360558b14374c863 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 12 Jul 2020 23:51:13 +0200 Subject: [PATCH 17/18] InputText, ImDrawList: Fixed assert triggering when drawing single line of text with more than ~16 KB characters. (#3349) --- docs/CHANGELOG.txt | 5 +++++ docs/TODO.txt | 3 ++- imgui_draw.cpp | 5 ++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6ee96d78..0264e314 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,6 +37,11 @@ HOW TO UPDATE? Other Changes: +- InputText, ImDrawList: Fixed assert triggering when drawing single line of text with more + than ~16 KB characters. (Note that current code is going to show corrupted display if after + clipping, more than 16 KB characters are visible in the same low-level ImDrawList::RenderText + call. ImGui-level functions such as TextUnformatted() are not affected. This is quite rare + but it will be addressed later). (#3349) - ImDrawList: Thick anti-aliased strokes (> 1.0f) with integer thickness now use a texture-based path, reducing the amount of vertices/indices and CPU/GPU usage. (#3245) [@Shironekoben] - This change will facilitate the wider use of thick borders in future style changes. diff --git a/docs/TODO.txt b/docs/TODO.txt index abb9d513..9502e264 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -308,7 +308,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font/atlas: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. - font/draw: vertical and/or rotated text renderer (#705) - vertical is easier clipping wise - font/draw: need to be able to specify wrap start position. - - font/draw: better reserve policy for large horizontal block of text (shouldn't reserve for all clipped lines) + - font/draw: better reserve policy for large horizontal block of text (shouldn't reserve for all clipped lines). also see #3349. + - font/draw: fix for drawing 16k+ visible characters in same call. - font/draw: underline, squiggle line rendering helpers. - font: optimization: for monospace font (like the default one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance (need to make sure TAB is still correct), would save on cache line. - font: add support for kerning, probably optional. A) perhaps default to (32..128)^2 matrix ~ 9K entries = 36KB, then hash for non-ascii?. B) or sparse lookup into per-char list? diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 864364e9..09b5a413 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -517,7 +517,7 @@ void ImDrawList::_OnChangedVtxOffset() // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this. _VtxCurrentIdx = 0; ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); + //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349 if (curr_cmd->ElemCount != 0) { AddDrawCmd(); @@ -582,6 +582,9 @@ void ImDrawList::PrimReserve(int idx_count, int vtx_count) IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0); if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset)) { + // FIXME: In theory we should be testing that vtx_count <64k here. + // In practice, RenderText() relies on reserving ahead for a worst case scenario so it is currently useful for us + // to not make that check until we rework the text functions to handle clipping and large horizontal lines better. _CmdHeader.VtxOffset = VtxBuffer.Size; _OnChangedVtxOffset(); } From eefae082617cd36d9b1d412e481a7913ab0ef3f2 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 14 Jul 2020 18:36:35 +0200 Subject: [PATCH 18/18] Nav: Fixed clicking on void from not clearing focused window. Amend d31fe97f7. (#3344, #2880) This would be problematic e.g. in situation where the application relies on io.WantCaptureKeyboard flag being cleared accordingly. --- docs/CHANGELOG.txt | 5 ++++- imgui.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0264e314..b11ed192 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,6 +37,9 @@ HOW TO UPDATE? Other Changes: +- Nav: Fixed clicking on void from not clearing focused window. + This would be problematic e.g. in situation where the application relies on io.WantCaptureKeyboard + flag being cleared accordingly. (bug introduced in 1.77 WIP on 2020/06/16) (#3344, #2880) - InputText, ImDrawList: Fixed assert triggering when drawing single line of text with more than ~16 KB characters. (Note that current code is going to show corrupted display if after clipping, more than 16 KB characters are visible in the same low-level ImDrawList::RenderText @@ -62,7 +65,7 @@ Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.77 Breaking Changes: - Removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular() function. Please - note that this is a Beta api and will likely be reworked in order to support multi-DPI accross + note that this is a Beta api and will likely be reworked in order to support multi-DPI across multiple monitors. - Renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). - Removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor diff --git a/imgui.cpp b/imgui.cpp index 37e611ad..b17e2ee8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3367,7 +3367,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) g.MovingWindow = NULL; } - else if (root_window != NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL) + else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL) { // Clicking on void disable focus FocusWindow(NULL);