diff --git a/examples/example_sdl_opengl3/Makefile b/examples/example_sdl_opengl3/Makefile index 76601a1..9ac59b0 100644 --- a/examples/example_sdl_opengl3/Makefile +++ b/examples/example_sdl_opengl3/Makefile @@ -22,7 +22,7 @@ UNAME_S := $(shell uname -s) CXXFLAGS = -I../ -I../../ -CXXFLAGS += -g -Wall -Wformat +CXXFLAGS += -g -Wall -Wformat -std=c++03 LIBS = ##--------------------------------------------------------------------- diff --git a/examples/example_sdl_opengl3/Makefile b/examples/example_sdl_opengl3/Makefile index 76601a1..9ac59b0 100644 --- a/examples/example_sdl_opengl3/Makefile +++ b/examples/example_sdl_opengl3/Makefile @@ -22,7 +22,7 @@ UNAME_S := $(shell uname -s) CXXFLAGS = -I../ -I../../ -CXXFLAGS += -g -Wall -Wformat +CXXFLAGS += -g -Wall -Wformat -std=c++03 LIBS = ##--------------------------------------------------------------------- diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 38b9ebd..6aaf12c 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -22,6 +22,20 @@ #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM #endif +void GetVtxIdxDelta(ImDrawList* dl, int* vtx, int *idx) +{ + static int vtx_n, idx_n; + static int vtx_o, idx_o; + vtx_n = dl->VtxBuffer.Size; + idx_n = dl->IdxBuffer.Size; + + *vtx = vtx_n - vtx_o; + *idx = idx_n - idx_o; + + vtx_o = vtx_n; + idx_o = idx_n; +} + int main(int, char**) { // Setup SDL @@ -83,6 +97,8 @@ // Setup Dear ImGui style ImGui::StyleColorsDark(); + + ImGuiStyle& style = ImGui::GetStyle(); //ImGui::StyleColorsClassic(); // Setup Platform/Renderer bindings @@ -96,6 +112,8 @@ // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->RoundCornersMaxSize = 60; + style.WindowRounding = io.Fonts->RoundCornersMaxSize; //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); @@ -143,6 +161,95 @@ ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + // Rounded corners demo start + + ImGui::TextUnformatted("Press alt to toggle quads (hold to see them)."); + ImGui::TextUnformatted(io.KeyAlt ? "ALT ON -- Rasterized quad circle! w00t! OPTIMIZATION!" + : "ALT OFF -- Regular, boring circle with PathArcToFast."); + + static int r = io.Fonts->RoundCornersMaxSize / 2; + ImGui::SliderInt("radius", &r, 0, io.Fonts->RoundCornersMaxSize); + + ImGui::BeginGroup(); + + static int s = 20; + ImGui::PushItemWidth(120); + ImGui::SliderInt("segments", &s, 3, 100); + ImGui::PopItemWidth(); + + int vtx = 0, idx = 0; + ImDrawList* dl = ImGui::GetWindowDrawList(); + + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircleFilled(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircleFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircle(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircle\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + + static bool tl = true, tr = true, bl = true, br = true; + int flags = 0; + ImGui::Checkbox("TL", &tl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("TR", &tr); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BL", &bl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BR", &br); + + flags |= tl ? ImDrawCornerFlags_TopLeft : 0; + flags |= tr ? ImDrawCornerFlags_TopRight : 0; + flags |= bl ? ImDrawCornerFlags_BotLeft : 0; + flags |= br ? ImDrawCornerFlags_BotRight : 0; + + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRectFilled(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRectFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRect(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRect\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::Separator(); + + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + + // Rounded corners demo end + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); diff --git a/examples/example_sdl_opengl3/Makefile b/examples/example_sdl_opengl3/Makefile index 76601a1..9ac59b0 100644 --- a/examples/example_sdl_opengl3/Makefile +++ b/examples/example_sdl_opengl3/Makefile @@ -22,7 +22,7 @@ UNAME_S := $(shell uname -s) CXXFLAGS = -I../ -I../../ -CXXFLAGS += -g -Wall -Wformat +CXXFLAGS += -g -Wall -Wformat -std=c++03 LIBS = ##--------------------------------------------------------------------- diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 38b9ebd..6aaf12c 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -22,6 +22,20 @@ #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM #endif +void GetVtxIdxDelta(ImDrawList* dl, int* vtx, int *idx) +{ + static int vtx_n, idx_n; + static int vtx_o, idx_o; + vtx_n = dl->VtxBuffer.Size; + idx_n = dl->IdxBuffer.Size; + + *vtx = vtx_n - vtx_o; + *idx = idx_n - idx_o; + + vtx_o = vtx_n; + idx_o = idx_n; +} + int main(int, char**) { // Setup SDL @@ -83,6 +97,8 @@ // Setup Dear ImGui style ImGui::StyleColorsDark(); + + ImGuiStyle& style = ImGui::GetStyle(); //ImGui::StyleColorsClassic(); // Setup Platform/Renderer bindings @@ -96,6 +112,8 @@ // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->RoundCornersMaxSize = 60; + style.WindowRounding = io.Fonts->RoundCornersMaxSize; //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); @@ -143,6 +161,95 @@ ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + // Rounded corners demo start + + ImGui::TextUnformatted("Press alt to toggle quads (hold to see them)."); + ImGui::TextUnformatted(io.KeyAlt ? "ALT ON -- Rasterized quad circle! w00t! OPTIMIZATION!" + : "ALT OFF -- Regular, boring circle with PathArcToFast."); + + static int r = io.Fonts->RoundCornersMaxSize / 2; + ImGui::SliderInt("radius", &r, 0, io.Fonts->RoundCornersMaxSize); + + ImGui::BeginGroup(); + + static int s = 20; + ImGui::PushItemWidth(120); + ImGui::SliderInt("segments", &s, 3, 100); + ImGui::PopItemWidth(); + + int vtx = 0, idx = 0; + ImDrawList* dl = ImGui::GetWindowDrawList(); + + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircleFilled(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircleFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircle(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircle\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + + static bool tl = true, tr = true, bl = true, br = true; + int flags = 0; + ImGui::Checkbox("TL", &tl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("TR", &tr); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BL", &bl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BR", &br); + + flags |= tl ? ImDrawCornerFlags_TopLeft : 0; + flags |= tr ? ImDrawCornerFlags_TopRight : 0; + flags |= bl ? ImDrawCornerFlags_BotLeft : 0; + flags |= br ? ImDrawCornerFlags_BotRight : 0; + + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRectFilled(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRectFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRect(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRect\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::Separator(); + + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + + // Rounded corners demo end + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 49b2ff8..d4c05eb 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -21,14 +21,6 @@ Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.ActiveCfg = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.Build.0 = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|x64.ActiveCfg = Debug|x64 @@ -37,22 +29,6 @@ {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|Win32.Build.0 = Release|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.ActiveCfg = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.Build.0 = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.ActiveCfg = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.Build.0 = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|x64.ActiveCfg = Debug|x64 @@ -61,6 +37,30 @@ {345A953E-A004-4648-B442-DC5F9F11068C}.Release|Win32.Build.0 = Release|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.ActiveCfg = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.Build.0 = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/example_sdl_opengl3/Makefile b/examples/example_sdl_opengl3/Makefile index 76601a1..9ac59b0 100644 --- a/examples/example_sdl_opengl3/Makefile +++ b/examples/example_sdl_opengl3/Makefile @@ -22,7 +22,7 @@ UNAME_S := $(shell uname -s) CXXFLAGS = -I../ -I../../ -CXXFLAGS += -g -Wall -Wformat +CXXFLAGS += -g -Wall -Wformat -std=c++03 LIBS = ##--------------------------------------------------------------------- diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 38b9ebd..6aaf12c 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -22,6 +22,20 @@ #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM #endif +void GetVtxIdxDelta(ImDrawList* dl, int* vtx, int *idx) +{ + static int vtx_n, idx_n; + static int vtx_o, idx_o; + vtx_n = dl->VtxBuffer.Size; + idx_n = dl->IdxBuffer.Size; + + *vtx = vtx_n - vtx_o; + *idx = idx_n - idx_o; + + vtx_o = vtx_n; + idx_o = idx_n; +} + int main(int, char**) { // Setup SDL @@ -83,6 +97,8 @@ // Setup Dear ImGui style ImGui::StyleColorsDark(); + + ImGuiStyle& style = ImGui::GetStyle(); //ImGui::StyleColorsClassic(); // Setup Platform/Renderer bindings @@ -96,6 +112,8 @@ // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->RoundCornersMaxSize = 60; + style.WindowRounding = io.Fonts->RoundCornersMaxSize; //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); @@ -143,6 +161,95 @@ ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + // Rounded corners demo start + + ImGui::TextUnformatted("Press alt to toggle quads (hold to see them)."); + ImGui::TextUnformatted(io.KeyAlt ? "ALT ON -- Rasterized quad circle! w00t! OPTIMIZATION!" + : "ALT OFF -- Regular, boring circle with PathArcToFast."); + + static int r = io.Fonts->RoundCornersMaxSize / 2; + ImGui::SliderInt("radius", &r, 0, io.Fonts->RoundCornersMaxSize); + + ImGui::BeginGroup(); + + static int s = 20; + ImGui::PushItemWidth(120); + ImGui::SliderInt("segments", &s, 3, 100); + ImGui::PopItemWidth(); + + int vtx = 0, idx = 0; + ImDrawList* dl = ImGui::GetWindowDrawList(); + + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircleFilled(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircleFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircle(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircle\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + + static bool tl = true, tr = true, bl = true, br = true; + int flags = 0; + ImGui::Checkbox("TL", &tl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("TR", &tr); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BL", &bl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BR", &br); + + flags |= tl ? ImDrawCornerFlags_TopLeft : 0; + flags |= tr ? ImDrawCornerFlags_TopRight : 0; + flags |= bl ? ImDrawCornerFlags_BotLeft : 0; + flags |= br ? ImDrawCornerFlags_BotRight : 0; + + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRectFilled(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRectFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRect(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRect\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::Separator(); + + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + + // Rounded corners demo end + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 49b2ff8..d4c05eb 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -21,14 +21,6 @@ Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.ActiveCfg = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.Build.0 = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|x64.ActiveCfg = Debug|x64 @@ -37,22 +29,6 @@ {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|Win32.Build.0 = Release|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.ActiveCfg = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.Build.0 = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.ActiveCfg = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.Build.0 = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|x64.ActiveCfg = Debug|x64 @@ -61,6 +37,30 @@ {345A953E-A004-4648-B442-DC5F9F11068C}.Release|Win32.Build.0 = Release|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.ActiveCfg = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.Build.0 = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/imgui.cpp b/imgui.cpp index 72ea25e..425c63f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4876,16 +4876,62 @@ ImVec2 CornerPosN; ImVec2 InnerDir; int AngleMin12, AngleMax12; + ImDrawCornerFlags CornerFlags; }; static const ImGuiResizeGripDef resize_grip_def[4] = { - { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right - { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left - { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left - { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right + { ImVec2(1,1), ImVec2(-1,-1), 0, 3, ImDrawCornerFlags_BotRight }, // Lower right + { ImVec2(0,1), ImVec2(+1,-1), 3, 6, ImDrawCornerFlags_BotLeft }, // Lower left + { ImVec2(0,0), ImVec2(+1,+1), 6, 9, ImDrawCornerFlags_TopLeft }, // Upper left + { ImVec2(1,0), ImVec2(-1,+1), 9,12, ImDrawCornerFlags_TopRight }, // Upper right }; +static void AddResizeGrip(ImDrawList* dl, const ImVec2& corner, unsigned int rad, int rounding_corners_flags, ImU32 col) +{ + ImTextureID tex = dl->_Data->Font->ContainerAtlas->TexID; + IM_ASSERT(tex == dl->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + switch (rounding_corners_flags) + { + case ImDrawCornerFlags_TopLeft: + case ImDrawCornerFlags_TopRight: + case ImDrawCornerFlags_BotRight: + case ImDrawCornerFlags_BotLeft: + break; + default: + { + IM_ASSERT("Invalid ImDrawCornerFlags for corner quad. {Top,Bot}{Left,Right} pick exactly one of each!"); + return; + } + } + + const ImVec4& uvs = (*dl->_Data->TexUvRoundCornerFilled)[rad - 1]; + + // NOTE: test performance using locals instead of array + const ImVec2 uv[] = + { + ImVec2(ImLerp(uvs.x, uvs.z, 0.5f), ImLerp(uvs.y, uvs.w, 0.5f)), + ImVec2(uvs.x, uvs.w), + ImVec2(uvs.z, uvs.w), + }; + + ImVec2 in_x = corner, in_y = corner; + if (rounding_corners_flags & ImDrawCornerFlags_Top) + in_y.y += rad; + else if (rounding_corners_flags & ImDrawCornerFlags_Bot) + in_y.y -= rad; + if (rounding_corners_flags & ImDrawCornerFlags_Left) + in_x.x += rad; + else if (rounding_corners_flags & ImDrawCornerFlags_Right) + in_x.x -= rad; + + const ImVec2 mid = ImVec2(ImLerp(in_x.x, in_y.x, 0.5f), ImLerp(in_x.y, in_y.y, 0.5f)); + + dl->PrimReserve(6, 4); + dl->PrimQuadUV(mid, in_y, corner, in_x, uv[0], uv[1], uv[2], uv[1], col); +} + static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) { ImRect rect = window->Rect(); @@ -5129,10 +5175,20 @@ { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); - window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); - window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + if (g.IO.KeyAlt) + { + ImVec2 grip_corner = corner; + grip_corner.x += grip.InnerDir.x * window_border_size; + grip_corner.y += grip.InnerDir.y * window_border_size; + AddResizeGrip(window->DrawList, grip_corner, (unsigned int)window_rounding, grip.CornerFlags, resize_grip_col[resize_grip_n]); + } + else + { + //window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); + window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); + window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + } } } @@ -6085,6 +6141,8 @@ ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; + g.DrawListSharedData.TexUvRoundCornerFilled = &atlas->TexUvRoundCornerFilled; + g.DrawListSharedData.TexUvRoundCornerStroked = &atlas->TexUvRoundCornerStroked; g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.FontSize = g.FontSize; } diff --git a/examples/example_sdl_opengl3/Makefile b/examples/example_sdl_opengl3/Makefile index 76601a1..9ac59b0 100644 --- a/examples/example_sdl_opengl3/Makefile +++ b/examples/example_sdl_opengl3/Makefile @@ -22,7 +22,7 @@ UNAME_S := $(shell uname -s) CXXFLAGS = -I../ -I../../ -CXXFLAGS += -g -Wall -Wformat +CXXFLAGS += -g -Wall -Wformat -std=c++03 LIBS = ##--------------------------------------------------------------------- diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 38b9ebd..6aaf12c 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -22,6 +22,20 @@ #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM #endif +void GetVtxIdxDelta(ImDrawList* dl, int* vtx, int *idx) +{ + static int vtx_n, idx_n; + static int vtx_o, idx_o; + vtx_n = dl->VtxBuffer.Size; + idx_n = dl->IdxBuffer.Size; + + *vtx = vtx_n - vtx_o; + *idx = idx_n - idx_o; + + vtx_o = vtx_n; + idx_o = idx_n; +} + int main(int, char**) { // Setup SDL @@ -83,6 +97,8 @@ // Setup Dear ImGui style ImGui::StyleColorsDark(); + + ImGuiStyle& style = ImGui::GetStyle(); //ImGui::StyleColorsClassic(); // Setup Platform/Renderer bindings @@ -96,6 +112,8 @@ // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->RoundCornersMaxSize = 60; + style.WindowRounding = io.Fonts->RoundCornersMaxSize; //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); @@ -143,6 +161,95 @@ ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + // Rounded corners demo start + + ImGui::TextUnformatted("Press alt to toggle quads (hold to see them)."); + ImGui::TextUnformatted(io.KeyAlt ? "ALT ON -- Rasterized quad circle! w00t! OPTIMIZATION!" + : "ALT OFF -- Regular, boring circle with PathArcToFast."); + + static int r = io.Fonts->RoundCornersMaxSize / 2; + ImGui::SliderInt("radius", &r, 0, io.Fonts->RoundCornersMaxSize); + + ImGui::BeginGroup(); + + static int s = 20; + ImGui::PushItemWidth(120); + ImGui::SliderInt("segments", &s, 3, 100); + ImGui::PopItemWidth(); + + int vtx = 0, idx = 0; + ImDrawList* dl = ImGui::GetWindowDrawList(); + + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircleFilled(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircleFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircle(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircle\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + + static bool tl = true, tr = true, bl = true, br = true; + int flags = 0; + ImGui::Checkbox("TL", &tl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("TR", &tr); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BL", &bl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BR", &br); + + flags |= tl ? ImDrawCornerFlags_TopLeft : 0; + flags |= tr ? ImDrawCornerFlags_TopRight : 0; + flags |= bl ? ImDrawCornerFlags_BotLeft : 0; + flags |= br ? ImDrawCornerFlags_BotRight : 0; + + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRectFilled(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRectFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRect(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRect\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::Separator(); + + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + + // Rounded corners demo end + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 49b2ff8..d4c05eb 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -21,14 +21,6 @@ Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.ActiveCfg = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.Build.0 = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|x64.ActiveCfg = Debug|x64 @@ -37,22 +29,6 @@ {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|Win32.Build.0 = Release|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.ActiveCfg = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.Build.0 = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.ActiveCfg = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.Build.0 = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|x64.ActiveCfg = Debug|x64 @@ -61,6 +37,30 @@ {345A953E-A004-4648-B442-DC5F9F11068C}.Release|Win32.Build.0 = Release|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.ActiveCfg = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.Build.0 = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/imgui.cpp b/imgui.cpp index 72ea25e..425c63f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4876,16 +4876,62 @@ ImVec2 CornerPosN; ImVec2 InnerDir; int AngleMin12, AngleMax12; + ImDrawCornerFlags CornerFlags; }; static const ImGuiResizeGripDef resize_grip_def[4] = { - { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right - { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left - { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left - { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right + { ImVec2(1,1), ImVec2(-1,-1), 0, 3, ImDrawCornerFlags_BotRight }, // Lower right + { ImVec2(0,1), ImVec2(+1,-1), 3, 6, ImDrawCornerFlags_BotLeft }, // Lower left + { ImVec2(0,0), ImVec2(+1,+1), 6, 9, ImDrawCornerFlags_TopLeft }, // Upper left + { ImVec2(1,0), ImVec2(-1,+1), 9,12, ImDrawCornerFlags_TopRight }, // Upper right }; +static void AddResizeGrip(ImDrawList* dl, const ImVec2& corner, unsigned int rad, int rounding_corners_flags, ImU32 col) +{ + ImTextureID tex = dl->_Data->Font->ContainerAtlas->TexID; + IM_ASSERT(tex == dl->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + switch (rounding_corners_flags) + { + case ImDrawCornerFlags_TopLeft: + case ImDrawCornerFlags_TopRight: + case ImDrawCornerFlags_BotRight: + case ImDrawCornerFlags_BotLeft: + break; + default: + { + IM_ASSERT("Invalid ImDrawCornerFlags for corner quad. {Top,Bot}{Left,Right} pick exactly one of each!"); + return; + } + } + + const ImVec4& uvs = (*dl->_Data->TexUvRoundCornerFilled)[rad - 1]; + + // NOTE: test performance using locals instead of array + const ImVec2 uv[] = + { + ImVec2(ImLerp(uvs.x, uvs.z, 0.5f), ImLerp(uvs.y, uvs.w, 0.5f)), + ImVec2(uvs.x, uvs.w), + ImVec2(uvs.z, uvs.w), + }; + + ImVec2 in_x = corner, in_y = corner; + if (rounding_corners_flags & ImDrawCornerFlags_Top) + in_y.y += rad; + else if (rounding_corners_flags & ImDrawCornerFlags_Bot) + in_y.y -= rad; + if (rounding_corners_flags & ImDrawCornerFlags_Left) + in_x.x += rad; + else if (rounding_corners_flags & ImDrawCornerFlags_Right) + in_x.x -= rad; + + const ImVec2 mid = ImVec2(ImLerp(in_x.x, in_y.x, 0.5f), ImLerp(in_x.y, in_y.y, 0.5f)); + + dl->PrimReserve(6, 4); + dl->PrimQuadUV(mid, in_y, corner, in_x, uv[0], uv[1], uv[2], uv[1], col); +} + static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) { ImRect rect = window->Rect(); @@ -5129,10 +5175,20 @@ { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); - window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); - window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + if (g.IO.KeyAlt) + { + ImVec2 grip_corner = corner; + grip_corner.x += grip.InnerDir.x * window_border_size; + grip_corner.y += grip.InnerDir.y * window_border_size; + AddResizeGrip(window->DrawList, grip_corner, (unsigned int)window_rounding, grip.CornerFlags, resize_grip_col[resize_grip_n]); + } + else + { + //window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); + window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); + window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + } } } @@ -6085,6 +6141,8 @@ ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; + g.DrawListSharedData.TexUvRoundCornerFilled = &atlas->TexUvRoundCornerFilled; + g.DrawListSharedData.TexUvRoundCornerStroked = &atlas->TexUvRoundCornerStroked; g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.FontSize = g.FontSize; } diff --git a/imgui.h b/imgui.h index d5de110..6d79c77 100644 --- a/imgui.h +++ b/imgui.h @@ -1961,6 +1961,7 @@ inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } IMGUI_API void UpdateClipRect(); IMGUI_API void UpdateTextureID(); + }; // All draw data to render a Dear ImGui frame @@ -2043,7 +2044,8 @@ { 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 + ImFontAtlasFlags_NoRoundCorners = 1 << 2 // Don't use software rendered quads for round corners }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: @@ -2156,6 +2158,12 @@ ImVector ConfigData; // Internal data int CustomRectIds[1]; // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList + // FIXME-ROUND-SHAPES: WIP + int RoundCornersMaxSize; // Max pixel size of round corner textures to generate + ImVector RoundCornersRectIds; // Ids of custom rects for round corners indexed by size [0] is 1px, [n] is (n+1)px (index up to RoundCornersMaxSize - 1). + ImVector TexUvRoundCornerFilled; // Texture coordinates to filled round corner quads + ImVector TexUvRoundCornerStroked;// Texture coordinates to stroked round corner quads + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ #endif diff --git a/examples/example_sdl_opengl3/Makefile b/examples/example_sdl_opengl3/Makefile index 76601a1..9ac59b0 100644 --- a/examples/example_sdl_opengl3/Makefile +++ b/examples/example_sdl_opengl3/Makefile @@ -22,7 +22,7 @@ UNAME_S := $(shell uname -s) CXXFLAGS = -I../ -I../../ -CXXFLAGS += -g -Wall -Wformat +CXXFLAGS += -g -Wall -Wformat -std=c++03 LIBS = ##--------------------------------------------------------------------- diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 38b9ebd..6aaf12c 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -22,6 +22,20 @@ #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM #endif +void GetVtxIdxDelta(ImDrawList* dl, int* vtx, int *idx) +{ + static int vtx_n, idx_n; + static int vtx_o, idx_o; + vtx_n = dl->VtxBuffer.Size; + idx_n = dl->IdxBuffer.Size; + + *vtx = vtx_n - vtx_o; + *idx = idx_n - idx_o; + + vtx_o = vtx_n; + idx_o = idx_n; +} + int main(int, char**) { // Setup SDL @@ -83,6 +97,8 @@ // Setup Dear ImGui style ImGui::StyleColorsDark(); + + ImGuiStyle& style = ImGui::GetStyle(); //ImGui::StyleColorsClassic(); // Setup Platform/Renderer bindings @@ -96,6 +112,8 @@ // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->RoundCornersMaxSize = 60; + style.WindowRounding = io.Fonts->RoundCornersMaxSize; //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); @@ -143,6 +161,95 @@ ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + // Rounded corners demo start + + ImGui::TextUnformatted("Press alt to toggle quads (hold to see them)."); + ImGui::TextUnformatted(io.KeyAlt ? "ALT ON -- Rasterized quad circle! w00t! OPTIMIZATION!" + : "ALT OFF -- Regular, boring circle with PathArcToFast."); + + static int r = io.Fonts->RoundCornersMaxSize / 2; + ImGui::SliderInt("radius", &r, 0, io.Fonts->RoundCornersMaxSize); + + ImGui::BeginGroup(); + + static int s = 20; + ImGui::PushItemWidth(120); + ImGui::SliderInt("segments", &s, 3, 100); + ImGui::PopItemWidth(); + + int vtx = 0, idx = 0; + ImDrawList* dl = ImGui::GetWindowDrawList(); + + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircleFilled(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircleFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircle(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircle\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + + static bool tl = true, tr = true, bl = true, br = true; + int flags = 0; + ImGui::Checkbox("TL", &tl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("TR", &tr); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BL", &bl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BR", &br); + + flags |= tl ? ImDrawCornerFlags_TopLeft : 0; + flags |= tr ? ImDrawCornerFlags_TopRight : 0; + flags |= bl ? ImDrawCornerFlags_BotLeft : 0; + flags |= br ? ImDrawCornerFlags_BotRight : 0; + + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRectFilled(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRectFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRect(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRect\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::Separator(); + + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + + // Rounded corners demo end + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 49b2ff8..d4c05eb 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -21,14 +21,6 @@ Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.ActiveCfg = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.Build.0 = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|x64.ActiveCfg = Debug|x64 @@ -37,22 +29,6 @@ {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|Win32.Build.0 = Release|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.ActiveCfg = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.Build.0 = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.ActiveCfg = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.Build.0 = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|x64.ActiveCfg = Debug|x64 @@ -61,6 +37,30 @@ {345A953E-A004-4648-B442-DC5F9F11068C}.Release|Win32.Build.0 = Release|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.ActiveCfg = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.Build.0 = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/imgui.cpp b/imgui.cpp index 72ea25e..425c63f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4876,16 +4876,62 @@ ImVec2 CornerPosN; ImVec2 InnerDir; int AngleMin12, AngleMax12; + ImDrawCornerFlags CornerFlags; }; static const ImGuiResizeGripDef resize_grip_def[4] = { - { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right - { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left - { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left - { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right + { ImVec2(1,1), ImVec2(-1,-1), 0, 3, ImDrawCornerFlags_BotRight }, // Lower right + { ImVec2(0,1), ImVec2(+1,-1), 3, 6, ImDrawCornerFlags_BotLeft }, // Lower left + { ImVec2(0,0), ImVec2(+1,+1), 6, 9, ImDrawCornerFlags_TopLeft }, // Upper left + { ImVec2(1,0), ImVec2(-1,+1), 9,12, ImDrawCornerFlags_TopRight }, // Upper right }; +static void AddResizeGrip(ImDrawList* dl, const ImVec2& corner, unsigned int rad, int rounding_corners_flags, ImU32 col) +{ + ImTextureID tex = dl->_Data->Font->ContainerAtlas->TexID; + IM_ASSERT(tex == dl->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + switch (rounding_corners_flags) + { + case ImDrawCornerFlags_TopLeft: + case ImDrawCornerFlags_TopRight: + case ImDrawCornerFlags_BotRight: + case ImDrawCornerFlags_BotLeft: + break; + default: + { + IM_ASSERT("Invalid ImDrawCornerFlags for corner quad. {Top,Bot}{Left,Right} pick exactly one of each!"); + return; + } + } + + const ImVec4& uvs = (*dl->_Data->TexUvRoundCornerFilled)[rad - 1]; + + // NOTE: test performance using locals instead of array + const ImVec2 uv[] = + { + ImVec2(ImLerp(uvs.x, uvs.z, 0.5f), ImLerp(uvs.y, uvs.w, 0.5f)), + ImVec2(uvs.x, uvs.w), + ImVec2(uvs.z, uvs.w), + }; + + ImVec2 in_x = corner, in_y = corner; + if (rounding_corners_flags & ImDrawCornerFlags_Top) + in_y.y += rad; + else if (rounding_corners_flags & ImDrawCornerFlags_Bot) + in_y.y -= rad; + if (rounding_corners_flags & ImDrawCornerFlags_Left) + in_x.x += rad; + else if (rounding_corners_flags & ImDrawCornerFlags_Right) + in_x.x -= rad; + + const ImVec2 mid = ImVec2(ImLerp(in_x.x, in_y.x, 0.5f), ImLerp(in_x.y, in_y.y, 0.5f)); + + dl->PrimReserve(6, 4); + dl->PrimQuadUV(mid, in_y, corner, in_x, uv[0], uv[1], uv[2], uv[1], col); +} + static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) { ImRect rect = window->Rect(); @@ -5129,10 +5175,20 @@ { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); - window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); - window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + if (g.IO.KeyAlt) + { + ImVec2 grip_corner = corner; + grip_corner.x += grip.InnerDir.x * window_border_size; + grip_corner.y += grip.InnerDir.y * window_border_size; + AddResizeGrip(window->DrawList, grip_corner, (unsigned int)window_rounding, grip.CornerFlags, resize_grip_col[resize_grip_n]); + } + else + { + //window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); + window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); + window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + } } } @@ -6085,6 +6141,8 @@ ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; + g.DrawListSharedData.TexUvRoundCornerFilled = &atlas->TexUvRoundCornerFilled; + g.DrawListSharedData.TexUvRoundCornerStroked = &atlas->TexUvRoundCornerStroked; g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.FontSize = g.FontSize; } diff --git a/imgui.h b/imgui.h index d5de110..6d79c77 100644 --- a/imgui.h +++ b/imgui.h @@ -1961,6 +1961,7 @@ inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } IMGUI_API void UpdateClipRect(); IMGUI_API void UpdateTextureID(); + }; // All draw data to render a Dear ImGui frame @@ -2043,7 +2044,8 @@ { 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 + ImFontAtlasFlags_NoRoundCorners = 1 << 2 // Don't use software rendered quads for round corners }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: @@ -2156,6 +2158,12 @@ ImVector ConfigData; // Internal data int CustomRectIds[1]; // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList + // FIXME-ROUND-SHAPES: WIP + int RoundCornersMaxSize; // Max pixel size of round corner textures to generate + ImVector RoundCornersRectIds; // Ids of custom rects for round corners indexed by size [0] is 1px, [n] is (n+1)px (index up to RoundCornersMaxSize - 1). + ImVector TexUvRoundCornerFilled; // Texture coordinates to filled round corner quads + ImVector TexUvRoundCornerStroked;// Texture coordinates to stroked round corner quads + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ #endif diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 8d0c31d..021db91 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -978,15 +978,193 @@ PathStroke(col, false, thickness); } +inline void AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags, bool fill) +{ + const ImDrawListSharedData* data = draw_list->_Data; + const int rad = (int)rounding; + IM_ASSERT(rad <= data->Font->ContainerAtlas->RoundCornersMaxSize); + + ImTextureID tex_id = data->Font->ContainerAtlas->TexID; + IM_ASSERT(tex_id == draw_list->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + const ImVec4& uvs = (*(fill ? data->TexUvRoundCornerFilled : data->TexUvRoundCornerStroked))[rad - 1]; + const ImVec2 corner_uv[3] = + { + ImVec2(uvs.x, uvs.y), + fill ? ImVec2(uvs.x, uvs.w) : ImVec2(uvs.z, uvs.y), + ImVec2(uvs.z, uvs.w), + }; + + const bool ba = (rounding_corners_flags & ImDrawCornerFlags_TopLeft) != 0; + const bool bb = (rounding_corners_flags & ImDrawCornerFlags_TopRight) != 0; + const bool bc = (rounding_corners_flags & ImDrawCornerFlags_BotRight) != 0; + const bool bd = (rounding_corners_flags & ImDrawCornerFlags_BotLeft) != 0; + + // TODO: fix "D" shaped stroked rects + + const int rad_l = (ba || bd) ? rad : 0; + const int rad_t = (ba || bb) ? rad : 0; + const int rad_r = (bc || bb) ? rad : 0; + const int rad_b = (bc || bd) ? rad : 0; + + const ImVec2 ca(a.x, a.y), cb(b.x, a.y); + const ImVec2 ma1(ca.x + rad_l, ca.y), mb1(cb.x - rad_r, cb.y); + const ImVec2 ma2(ca.x, ca.y + rad_t), mb2(cb.x, cb.y + rad_t); + const ImVec2 ia(ma1.x, ma2.y), ib(mb1.x, mb2.y); + + const ImVec2 cc(b.x, b.y), cd(a.x, b.y); + const ImVec2 md3(cd.x, cd.y - rad_b), mc3(cc.x, cc.y - rad_b); + const ImVec2 md4(cd.x + rad_l, cd.y), mc4(cc.x - rad_r, cc.y); + const ImVec2 id(md4.x, md3.y), ic(mc4.x, mc3.y); + + const int vtcs = 16; + const int idcs = 54; + draw_list->PrimReserve(idcs, vtcs); + + const ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx; + + #define VTX_WRITE(d, p, i) \ + draw_list->_VtxWritePtr[d].pos = (p); \ + draw_list->_VtxWritePtr[d].uv = corner_uv[(i)]; \ + draw_list->_VtxWritePtr[d].col = col + + const int vca = 0, vcb = 1, vcc = 2, vcd = 3; + VTX_WRITE(vca, ca, ba ? 2 : 1); + VTX_WRITE(vcb, cb, bb ? 2 : 1); + VTX_WRITE(vcc, cc, bc ? 2 : 1); + VTX_WRITE(vcd, cd, bd ? 2 : 1); + + int dv = 4; + + int vya = vca, vxa = vca, via = vca; + int vyb = vcb, vxb = vcb, vib = vcb; + int vyc = vcc, vxc = vcc, vic = vcc; + int vyd = vcd, vxd = vcd, vid = vcd; + + // FIXME-ROUND_SHAPES: TODO: find a way of saving vertices/triangles here? + // currently it's the same cost regardless of how many corners are rounded + + if (ba || 1) + { + vya = dv; + vxa = dv + 1; + via = dv + 2; + VTX_WRITE(vya, ma1, 1); + VTX_WRITE(vxa, ma2, 1); + VTX_WRITE(via, ia, 0); + dv += 3; + } + + if (bb || 1) + { + vyb = dv; + vxb = dv + 1; + vib = dv + 2; + VTX_WRITE(vyb, mb1, 1); + VTX_WRITE(vxb, mb2, 1); + VTX_WRITE(vib, ib, 0); + dv += 3; + } + + if (bc || 1) + { + vyc = dv; + vxc = dv + 1; + vic = dv + 2; + VTX_WRITE(vyc, mc4, 1); + VTX_WRITE(vxc, mc3, 1); + VTX_WRITE(vic, ic, 0); + dv += 3; + } + + if (bd || 1) + { + vyd = dv; + vxd = dv + 1; + vid = dv + 2; + VTX_WRITE(vyd, md4, 1); + VTX_WRITE(vxd, md3, 1); + VTX_WRITE(vid, id, 0); + dv += 3; + } + + int di = 0; + #define IDX_WRITE_TRI(idx0, idx1, idx2) \ + draw_list->_IdxWritePtr[di] = (ImDrawIdx)(idx+(idx0)); \ + draw_list->_IdxWritePtr[di+1] = (ImDrawIdx)(idx+(idx1)); \ + draw_list->_IdxWritePtr[di+2] = (ImDrawIdx)(idx+(idx2)); \ + di += 3 + + // Inner + if (fill) + { + IDX_WRITE_TRI(via, vic, vib); + IDX_WRITE_TRI(via, vic, vid); + } + + if (ba || 1) + { + IDX_WRITE_TRI(vca, vya, via); + IDX_WRITE_TRI(vca, vxa, via); + + IDX_WRITE_TRI(vib, vya, via); + IDX_WRITE_TRI(vid, vxa, via); + } + + if (bb || 1) + { + IDX_WRITE_TRI(vcb, vyb, vib); + IDX_WRITE_TRI(vcb, vxb, vib); + + IDX_WRITE_TRI(vya, vyb, vib); + IDX_WRITE_TRI(vic, vxb, vib); + } + + if (bc || 1) + { + IDX_WRITE_TRI(vcc, vyc, vic); + IDX_WRITE_TRI(vcc, vxc, vic); + + IDX_WRITE_TRI(vxb, vxc, vic); + IDX_WRITE_TRI(vyd, vyc, vic); + } + + if (bd || 1) + { + IDX_WRITE_TRI(vcd, vyd, vid); + IDX_WRITE_TRI(vcd, vxd, vid); + + IDX_WRITE_TRI(vic, vyd, vid); + IDX_WRITE_TRI(vxa, vxd, vid); + } + + draw_list->_VtxWritePtr += dv; + draw_list->_VtxCurrentIdx += dv; + draw_list->_IdxWritePtr += di; + + draw_list->PrimReserve(di - idcs, dv - vtcs); // FIXME-OPT + + #undef IDX_WRITE_TRI + #undef VTX_WRITE +} + // a: upper-left, b: lower-right. we don't render 1 px sized rectangles properly. void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; + + rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((rounding_corners_flags & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners_flags & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f) - 1.0f); + rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((rounding_corners_flags & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners_flags & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f) - 1.0f); + + // FIXME-ROUND-SHAPES: NOTE HACK TODO figure out why it's broken on small rounding + if (ImGui::GetIO().KeyShift && rounding > 3) + return AddRoundCornerRect(this, a, b, col, rounding, rounding_corners_flags, /* fill */ false); + if (Flags & ImDrawListFlags_AntiAliasedLines) - PathRect(a + ImVec2(0.5f,0.5f), b - ImVec2(0.50f,0.50f), rounding, rounding_corners_flags); + PathRect(a + ImVec2(0.5f, 0.5f), b - ImVec2(0.50f, 0.50f), rounding, rounding_corners_flags); else - PathRect(a + ImVec2(0.5f,0.5f), b - ImVec2(0.49f,0.49f), rounding, rounding_corners_flags); // Better looking lower-right corner and rounded non-AA shapes. + PathRect(a + ImVec2(0.5f, 0.5f), b - ImVec2(0.49f, 0.49f), rounding, rounding_corners_flags); // Better looking lower-right corner and rounded non-AA shapes. PathStroke(col, true, thickness); } @@ -994,10 +1172,23 @@ { if ((col & IM_COL32_A_MASK) == 0) return; - if (rounding > 0.0f) + + rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((rounding_corners_flags & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners_flags & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f ) - 1.0f); + rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((rounding_corners_flags & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners_flags & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f ) - 1.0f); + + if (rounding > 0.0f && rounding_corners_flags != 0) { - PathRect(a, b, rounding, rounding_corners_flags); - PathFillConvex(col); + // // FIXME-ROUND-SHAPES: NOTE HACK TODO figure out why it's broken on small rounding + if (ImGui::GetIO().KeyShift && rounding > 3) + { + AddRoundCornerRect(this, a, b, col, rounding, rounding_corners_flags, /* fill */ true); + return; + } + else + { + PathRect(a, b, rounding, rounding_corners_flags); + PathFillConvex(col); + } } else { @@ -1067,11 +1258,85 @@ PathFillConvex(col); } +inline void AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& centre, float radius, ImU32 col, bool fill) +{ + const ImDrawListSharedData* data = draw_list->_Data; + ImTextureID tex_id = data->Font->ContainerAtlas->TexID; + IM_ASSERT(tex_id == draw_list->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + const int rad = (int)radius; + IM_ASSERT(rad <= data->Font->ContainerAtlas->RoundCornersMaxSize); + + const ImVec4& uvs = (*(fill ? data->TexUvRoundCornerFilled : data->TexUvRoundCornerStroked))[rad - 1]; + const ImVec2 corner_uv[3] = + { + ImVec2(uvs.x, uvs.y), + fill ? ImVec2(uvs.x, uvs.w) : ImVec2(uvs.z, uvs.y), + ImVec2(uvs.z, uvs.w), + }; + + const ImVec2& c = centre; + ImVec2 tl = ImVec2(c.x - rad, c.y - rad); + ImVec2 br = ImVec2(c.x + rad, c.y + rad); + + // NOTE: test performance using locals instead of array + const ImVec2 circle_vt[9] = + { + c, + tl, + ImVec2(c.x, tl.y), + ImVec2(br.x, tl.y), + ImVec2(br.x, c.y), + br, + ImVec2(c.x, br.y), + ImVec2(tl.x, br.y), + ImVec2(tl.x, c.y), + }; + + #define IDX_WRITE_TRI(d, idx0, idx1, idx2) \ + draw_list->_IdxWritePtr[d+0] = (ImDrawIdx)(idx+idx0); \ + draw_list->_IdxWritePtr[d+1] = (ImDrawIdx)(idx+idx1); \ + draw_list->_IdxWritePtr[d+2] = (ImDrawIdx)(idx+idx2) + + #define VTX_WRITE(d, i) \ + draw_list->_VtxWritePtr[d].pos = circle_vt[d]; \ + draw_list->_VtxWritePtr[d].uv = corner_uv[i]; \ + draw_list->_VtxWritePtr[d].col = col + + draw_list->PrimReserve(24, 9); + ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx; + IDX_WRITE_TRI( 0, 0, 1, 2); + IDX_WRITE_TRI( 3, 0, 3, 2); + IDX_WRITE_TRI( 6, 0, 3, 4); + IDX_WRITE_TRI( 9, 0, 5, 4); + IDX_WRITE_TRI(12, 0, 5, 6); + IDX_WRITE_TRI(15, 0, 7, 6); + IDX_WRITE_TRI(18, 0, 7, 8); + IDX_WRITE_TRI(21, 0, 1, 8); + + VTX_WRITE(1, 2); VTX_WRITE(2, 1); VTX_WRITE(3, 2); + VTX_WRITE(8, 1); VTX_WRITE(0, 0); VTX_WRITE(4, 1); + VTX_WRITE(7, 2); VTX_WRITE(6, 1); VTX_WRITE(5, 2); + + draw_list->_VtxWritePtr += 9; + draw_list->_VtxCurrentIdx += 9; + draw_list->_IdxWritePtr += 24; + +#undef IDX_WRITE_TRI +#undef VTX_WRITE +} + void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments, float thickness) { if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; + if (ImGui::GetIO().KeyShift) + { + AddRoundCornerCircle(this, centre, radius, col, false); + return; + } + // 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; PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments - 1); @@ -1083,6 +1348,12 @@ if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; + if (ImGui::GetIO().KeyShift) + { + AddRoundCornerCircle(this, centre, radius, col, true); + return; + } + // 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; PathArcTo(centre, radius, 0.0f, a_max, num_segments - 1); @@ -1495,6 +1766,8 @@ TexWidth = TexHeight = 0; TexUvScale = ImVec2(0.0f, 0.0f); TexUvWhitePixel = ImVec2(0.0f, 0.0f); + + RoundCornersMaxSize = 60; for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++) CustomRectIds[n] = -1; } @@ -1526,6 +1799,7 @@ CustomRects.clear(); for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++) CustomRectIds[n] = -1; + RoundCornersRectIds.clear(); } void ImFontAtlas::ClearTexData() @@ -1537,6 +1811,8 @@ IM_FREE(TexPixelsRGBA32); TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; + TexUvRoundCornerFilled.clear(); + TexUvRoundCornerStroked.clear(); } void ImFontAtlas::ClearFonts() @@ -1840,6 +2116,7 @@ IM_ASSERT(atlas->ConfigData.Size > 0); ImFontAtlasBuildRegisterDefaultCustomRects(atlas); + ImFontAtlasBuildRegisterRoundCornersCustomRects(atlas); // Clear atlas atlas->TexID = (ImTextureID)NULL; @@ -2173,11 +2450,103 @@ atlas->TexUvWhitePixel = ImVec2((r.X + 0.5f) * atlas->TexUvScale.x, (r.Y + 0.5f) * atlas->TexUvScale.y); } +const unsigned int FONT_ATLAS_ROUNDED_CORNER_TEX_DATA_ID = 0x80000001; +const int FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING = 2; + +void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas) +{ + if (atlas->RoundCornersRectIds.size() > 0) + return; + + if ((atlas->Flags & ImFontAtlasFlags_NoRoundCorners)) + return; + + const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING; + const int max = atlas->RoundCornersMaxSize; + + // Filled + for (int n = 0; n < max; n++) + atlas->RoundCornersRectIds.push_back(atlas->AddCustomRectRegular(FONT_ATLAS_ROUNDED_CORNER_TEX_DATA_ID + n, + n + 1 + pad * 2, n + 1 + pad * 2)); + + // Stroked + for (int n = 0; n < max; n++) + atlas->RoundCornersRectIds.push_back(atlas->AddCustomRectRegular(FONT_ATLAS_ROUNDED_CORNER_TEX_DATA_ID + max + n, + n + 1 + pad * 2, n + 1 + pad * 2)); +} + +static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas) +{ + IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); + IM_ASSERT(atlas->TexUvRoundCornerFilled.size() == 0); + IM_ASSERT(atlas->TexUvRoundCornerStroked.size() == 0); + + if ((atlas->Flags & ImFontAtlasFlags_NoRoundCorners)) + return; + + const int w = atlas->TexWidth; + const unsigned int max = atlas->RoundCornersMaxSize; + + // Filled + for (unsigned int stage = 0; stage < 2; stage++) + { + bool filled = stage == 0; + for (unsigned int n = 0; n < max; n++) + { + const unsigned int id = (filled ? 0 : max) + n; + IM_ASSERT(atlas->RoundCornersRectIds.size() > (int) n); + ImFontAtlas::CustomRect& r = atlas->CustomRects[atlas->RoundCornersRectIds[id]]; + IM_ASSERT(r.ID == FONT_ATLAS_ROUNDED_CORNER_TEX_DATA_ID + id); + IM_ASSERT(r.IsPacked()); + + const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING; + + IM_ASSERT(r.Width == n + 1 + pad * 2 && r.Height == n + 1 + pad * 2); + + const int radius = (int)(r.Width - pad * 2); + const float stroke_width = 1.0f; + + for (int y = -pad; y < (int) (radius); y++) + for (int x = (filled ? -pad : y); x < (int)(filled ? y + pad : radius); x++) + { + const float dist = ImSqrt((float)(x*x+y*y)) - (float)(radius - (filled ? 0 : stroke_width)); + + float alpha = 0.0f; + if (filled) + { + alpha = ImClamp(-dist, 0.0f, 1.0f); + } + else + { + const float alpha1 = ImClamp(dist + stroke_width, 0.0f, 1.0f); + const float alpha2 = ImClamp(dist, 0.0f, 1.0f); + alpha = alpha1 - alpha2; + } + + const unsigned int offset = (int)(r.X + pad + x) + (int)(r.Y + pad + y) * w; + atlas->TexPixelsAlpha8[offset] = (unsigned char)(0xFF * ImSaturate(alpha)); + } + + ImVec2 uv0, uv1; + r.X += pad; + r.Y += pad; + r.Width -= pad * 2; + r.Height -= pad * 2; + atlas->CalcCustomRectUV(&r, &uv0, &uv1); + ImVector& uvs = (filled ? atlas->TexUvRoundCornerFilled : atlas->TexUvRoundCornerStroked); + uvs.push_back(ImVec4(uv0.x, uv0.y, uv1.x, uv1.y)); + } + } +} + void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { // Render into our custom data block ImFontAtlasBuildRenderDefaultTexData(atlas); + // Render into our rounded corner data block + ImFontAtlasBuildRenderRoundCornersTexData(atlas); + // Register custom rectangle glyphs for (int i = 0; i < atlas->CustomRects.Size; i++) { diff --git a/examples/example_sdl_opengl3/Makefile b/examples/example_sdl_opengl3/Makefile index 76601a1..9ac59b0 100644 --- a/examples/example_sdl_opengl3/Makefile +++ b/examples/example_sdl_opengl3/Makefile @@ -22,7 +22,7 @@ UNAME_S := $(shell uname -s) CXXFLAGS = -I../ -I../../ -CXXFLAGS += -g -Wall -Wformat +CXXFLAGS += -g -Wall -Wformat -std=c++03 LIBS = ##--------------------------------------------------------------------- diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 38b9ebd..6aaf12c 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -22,6 +22,20 @@ #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM #endif +void GetVtxIdxDelta(ImDrawList* dl, int* vtx, int *idx) +{ + static int vtx_n, idx_n; + static int vtx_o, idx_o; + vtx_n = dl->VtxBuffer.Size; + idx_n = dl->IdxBuffer.Size; + + *vtx = vtx_n - vtx_o; + *idx = idx_n - idx_o; + + vtx_o = vtx_n; + idx_o = idx_n; +} + int main(int, char**) { // Setup SDL @@ -83,6 +97,8 @@ // Setup Dear ImGui style ImGui::StyleColorsDark(); + + ImGuiStyle& style = ImGui::GetStyle(); //ImGui::StyleColorsClassic(); // Setup Platform/Renderer bindings @@ -96,6 +112,8 @@ // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->RoundCornersMaxSize = 60; + style.WindowRounding = io.Fonts->RoundCornersMaxSize; //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); @@ -143,6 +161,95 @@ ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + // Rounded corners demo start + + ImGui::TextUnformatted("Press alt to toggle quads (hold to see them)."); + ImGui::TextUnformatted(io.KeyAlt ? "ALT ON -- Rasterized quad circle! w00t! OPTIMIZATION!" + : "ALT OFF -- Regular, boring circle with PathArcToFast."); + + static int r = io.Fonts->RoundCornersMaxSize / 2; + ImGui::SliderInt("radius", &r, 0, io.Fonts->RoundCornersMaxSize); + + ImGui::BeginGroup(); + + static int s = 20; + ImGui::PushItemWidth(120); + ImGui::SliderInt("segments", &s, 3, 100); + ImGui::PopItemWidth(); + + int vtx = 0, idx = 0; + ImDrawList* dl = ImGui::GetWindowDrawList(); + + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircleFilled(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircleFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + GetVtxIdxDelta(dl, &vtx, &idx); + ImVec2 min = ImGui::GetItemRectMin(); + ImVec2 size = ImGui::GetItemRectSize(); + dl->AddCircle(ImVec2(min.x + size.x / 2, min.y + size.y / 2), r, 0xFFFF00FF, s); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddCircle\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + + static bool tl = true, tr = true, bl = true, br = true; + int flags = 0; + ImGui::Checkbox("TL", &tl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("TR", &tr); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BL", &bl); + ImGui::SameLine(0, 12); + ImGui::Checkbox("BR", &br); + + flags |= tl ? ImDrawCornerFlags_TopLeft : 0; + flags |= tr ? ImDrawCornerFlags_TopRight : 0; + flags |= bl ? ImDrawCornerFlags_BotLeft : 0; + flags |= br ? ImDrawCornerFlags_BotRight : 0; + + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRectFilled(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRectFilled\n %d vtx, %d idx", vtx, idx); + } + { + ImGui::Button("", ImVec2(200, 200)); + ImVec2 r_min = ImGui::GetItemRectMin(); + ImVec2 r_max = ImGui::GetItemRectMax(); + + GetVtxIdxDelta(dl, &vtx, &idx); + dl->AddRect(r_min, r_max, 0xFFFF00FF, r, flags); + GetVtxIdxDelta(dl, &vtx, &idx); + ImGui::Text("AddRect\n %d vtx, %d idx", vtx, idx); + } + + ImGui::EndGroup(); + + ImGui::Separator(); + + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + + // Rounded corners demo end + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 49b2ff8..d4c05eb 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -21,14 +21,6 @@ Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 - {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.ActiveCfg = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|Win32.Build.0 = Debug|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Debug|x64.ActiveCfg = Debug|x64 @@ -37,22 +29,6 @@ {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|Win32.Build.0 = Release|Win32 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.ActiveCfg = Release|x64 {4165A294-21F2-44CA-9B38-E3F935ABADF5}.Release|x64.Build.0 = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 - {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 - {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.ActiveCfg = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|Win32.Build.0 = Debug|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Debug|x64.ActiveCfg = Debug|x64 @@ -61,6 +37,30 @@ {345A953E-A004-4648-B442-DC5F9F11068C}.Release|Win32.Build.0 = Release|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.ActiveCfg = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.Build.0 = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.ActiveCfg = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|Win32.Build.0 = Debug|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.ActiveCfg = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Debug|x64.Build.0 = Debug|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.ActiveCfg = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 + {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.Build.0 = Debug|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.ActiveCfg = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|Win32.Build.0 = Release|Win32 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.ActiveCfg = Release|x64 + {9CDA7840-B7A5-496D-A527-E95571496D18}.Release|x64.Build.0 = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.ActiveCfg = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|Win32.Build.0 = Debug|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.ActiveCfg = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Debug|x64.Build.0 = Debug|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.ActiveCfg = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 + {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/imgui.cpp b/imgui.cpp index 72ea25e..425c63f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4876,16 +4876,62 @@ ImVec2 CornerPosN; ImVec2 InnerDir; int AngleMin12, AngleMax12; + ImDrawCornerFlags CornerFlags; }; static const ImGuiResizeGripDef resize_grip_def[4] = { - { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right - { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left - { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left - { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right + { ImVec2(1,1), ImVec2(-1,-1), 0, 3, ImDrawCornerFlags_BotRight }, // Lower right + { ImVec2(0,1), ImVec2(+1,-1), 3, 6, ImDrawCornerFlags_BotLeft }, // Lower left + { ImVec2(0,0), ImVec2(+1,+1), 6, 9, ImDrawCornerFlags_TopLeft }, // Upper left + { ImVec2(1,0), ImVec2(-1,+1), 9,12, ImDrawCornerFlags_TopRight }, // Upper right }; +static void AddResizeGrip(ImDrawList* dl, const ImVec2& corner, unsigned int rad, int rounding_corners_flags, ImU32 col) +{ + ImTextureID tex = dl->_Data->Font->ContainerAtlas->TexID; + IM_ASSERT(tex == dl->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + switch (rounding_corners_flags) + { + case ImDrawCornerFlags_TopLeft: + case ImDrawCornerFlags_TopRight: + case ImDrawCornerFlags_BotRight: + case ImDrawCornerFlags_BotLeft: + break; + default: + { + IM_ASSERT("Invalid ImDrawCornerFlags for corner quad. {Top,Bot}{Left,Right} pick exactly one of each!"); + return; + } + } + + const ImVec4& uvs = (*dl->_Data->TexUvRoundCornerFilled)[rad - 1]; + + // NOTE: test performance using locals instead of array + const ImVec2 uv[] = + { + ImVec2(ImLerp(uvs.x, uvs.z, 0.5f), ImLerp(uvs.y, uvs.w, 0.5f)), + ImVec2(uvs.x, uvs.w), + ImVec2(uvs.z, uvs.w), + }; + + ImVec2 in_x = corner, in_y = corner; + if (rounding_corners_flags & ImDrawCornerFlags_Top) + in_y.y += rad; + else if (rounding_corners_flags & ImDrawCornerFlags_Bot) + in_y.y -= rad; + if (rounding_corners_flags & ImDrawCornerFlags_Left) + in_x.x += rad; + else if (rounding_corners_flags & ImDrawCornerFlags_Right) + in_x.x -= rad; + + const ImVec2 mid = ImVec2(ImLerp(in_x.x, in_y.x, 0.5f), ImLerp(in_x.y, in_y.y, 0.5f)); + + dl->PrimReserve(6, 4); + dl->PrimQuadUV(mid, in_y, corner, in_x, uv[0], uv[1], uv[2], uv[1], col); +} + static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) { ImRect rect = window->Rect(); @@ -5129,10 +5175,20 @@ { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); - window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); - window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + if (g.IO.KeyAlt) + { + ImVec2 grip_corner = corner; + grip_corner.x += grip.InnerDir.x * window_border_size; + grip_corner.y += grip.InnerDir.y * window_border_size; + AddResizeGrip(window->DrawList, grip_corner, (unsigned int)window_rounding, grip.CornerFlags, resize_grip_col[resize_grip_n]); + } + else + { + //window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); + window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); + window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + } } } @@ -6085,6 +6141,8 @@ ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; + g.DrawListSharedData.TexUvRoundCornerFilled = &atlas->TexUvRoundCornerFilled; + g.DrawListSharedData.TexUvRoundCornerStroked = &atlas->TexUvRoundCornerStroked; g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.FontSize = g.FontSize; } diff --git a/imgui.h b/imgui.h index d5de110..6d79c77 100644 --- a/imgui.h +++ b/imgui.h @@ -1961,6 +1961,7 @@ inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } IMGUI_API void UpdateClipRect(); IMGUI_API void UpdateTextureID(); + }; // All draw data to render a Dear ImGui frame @@ -2043,7 +2044,8 @@ { 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 + ImFontAtlasFlags_NoRoundCorners = 1 << 2 // Don't use software rendered quads for round corners }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: @@ -2156,6 +2158,12 @@ ImVector ConfigData; // Internal data int CustomRectIds[1]; // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList + // FIXME-ROUND-SHAPES: WIP + int RoundCornersMaxSize; // Max pixel size of round corner textures to generate + ImVector RoundCornersRectIds; // Ids of custom rects for round corners indexed by size [0] is 1px, [n] is (n+1)px (index up to RoundCornersMaxSize - 1). + ImVector TexUvRoundCornerFilled; // Texture coordinates to filled round corner quads + ImVector TexUvRoundCornerStroked;// Texture coordinates to stroked round corner quads + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ #endif diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 8d0c31d..021db91 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -978,15 +978,193 @@ PathStroke(col, false, thickness); } +inline void AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags, bool fill) +{ + const ImDrawListSharedData* data = draw_list->_Data; + const int rad = (int)rounding; + IM_ASSERT(rad <= data->Font->ContainerAtlas->RoundCornersMaxSize); + + ImTextureID tex_id = data->Font->ContainerAtlas->TexID; + IM_ASSERT(tex_id == draw_list->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + const ImVec4& uvs = (*(fill ? data->TexUvRoundCornerFilled : data->TexUvRoundCornerStroked))[rad - 1]; + const ImVec2 corner_uv[3] = + { + ImVec2(uvs.x, uvs.y), + fill ? ImVec2(uvs.x, uvs.w) : ImVec2(uvs.z, uvs.y), + ImVec2(uvs.z, uvs.w), + }; + + const bool ba = (rounding_corners_flags & ImDrawCornerFlags_TopLeft) != 0; + const bool bb = (rounding_corners_flags & ImDrawCornerFlags_TopRight) != 0; + const bool bc = (rounding_corners_flags & ImDrawCornerFlags_BotRight) != 0; + const bool bd = (rounding_corners_flags & ImDrawCornerFlags_BotLeft) != 0; + + // TODO: fix "D" shaped stroked rects + + const int rad_l = (ba || bd) ? rad : 0; + const int rad_t = (ba || bb) ? rad : 0; + const int rad_r = (bc || bb) ? rad : 0; + const int rad_b = (bc || bd) ? rad : 0; + + const ImVec2 ca(a.x, a.y), cb(b.x, a.y); + const ImVec2 ma1(ca.x + rad_l, ca.y), mb1(cb.x - rad_r, cb.y); + const ImVec2 ma2(ca.x, ca.y + rad_t), mb2(cb.x, cb.y + rad_t); + const ImVec2 ia(ma1.x, ma2.y), ib(mb1.x, mb2.y); + + const ImVec2 cc(b.x, b.y), cd(a.x, b.y); + const ImVec2 md3(cd.x, cd.y - rad_b), mc3(cc.x, cc.y - rad_b); + const ImVec2 md4(cd.x + rad_l, cd.y), mc4(cc.x - rad_r, cc.y); + const ImVec2 id(md4.x, md3.y), ic(mc4.x, mc3.y); + + const int vtcs = 16; + const int idcs = 54; + draw_list->PrimReserve(idcs, vtcs); + + const ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx; + + #define VTX_WRITE(d, p, i) \ + draw_list->_VtxWritePtr[d].pos = (p); \ + draw_list->_VtxWritePtr[d].uv = corner_uv[(i)]; \ + draw_list->_VtxWritePtr[d].col = col + + const int vca = 0, vcb = 1, vcc = 2, vcd = 3; + VTX_WRITE(vca, ca, ba ? 2 : 1); + VTX_WRITE(vcb, cb, bb ? 2 : 1); + VTX_WRITE(vcc, cc, bc ? 2 : 1); + VTX_WRITE(vcd, cd, bd ? 2 : 1); + + int dv = 4; + + int vya = vca, vxa = vca, via = vca; + int vyb = vcb, vxb = vcb, vib = vcb; + int vyc = vcc, vxc = vcc, vic = vcc; + int vyd = vcd, vxd = vcd, vid = vcd; + + // FIXME-ROUND_SHAPES: TODO: find a way of saving vertices/triangles here? + // currently it's the same cost regardless of how many corners are rounded + + if (ba || 1) + { + vya = dv; + vxa = dv + 1; + via = dv + 2; + VTX_WRITE(vya, ma1, 1); + VTX_WRITE(vxa, ma2, 1); + VTX_WRITE(via, ia, 0); + dv += 3; + } + + if (bb || 1) + { + vyb = dv; + vxb = dv + 1; + vib = dv + 2; + VTX_WRITE(vyb, mb1, 1); + VTX_WRITE(vxb, mb2, 1); + VTX_WRITE(vib, ib, 0); + dv += 3; + } + + if (bc || 1) + { + vyc = dv; + vxc = dv + 1; + vic = dv + 2; + VTX_WRITE(vyc, mc4, 1); + VTX_WRITE(vxc, mc3, 1); + VTX_WRITE(vic, ic, 0); + dv += 3; + } + + if (bd || 1) + { + vyd = dv; + vxd = dv + 1; + vid = dv + 2; + VTX_WRITE(vyd, md4, 1); + VTX_WRITE(vxd, md3, 1); + VTX_WRITE(vid, id, 0); + dv += 3; + } + + int di = 0; + #define IDX_WRITE_TRI(idx0, idx1, idx2) \ + draw_list->_IdxWritePtr[di] = (ImDrawIdx)(idx+(idx0)); \ + draw_list->_IdxWritePtr[di+1] = (ImDrawIdx)(idx+(idx1)); \ + draw_list->_IdxWritePtr[di+2] = (ImDrawIdx)(idx+(idx2)); \ + di += 3 + + // Inner + if (fill) + { + IDX_WRITE_TRI(via, vic, vib); + IDX_WRITE_TRI(via, vic, vid); + } + + if (ba || 1) + { + IDX_WRITE_TRI(vca, vya, via); + IDX_WRITE_TRI(vca, vxa, via); + + IDX_WRITE_TRI(vib, vya, via); + IDX_WRITE_TRI(vid, vxa, via); + } + + if (bb || 1) + { + IDX_WRITE_TRI(vcb, vyb, vib); + IDX_WRITE_TRI(vcb, vxb, vib); + + IDX_WRITE_TRI(vya, vyb, vib); + IDX_WRITE_TRI(vic, vxb, vib); + } + + if (bc || 1) + { + IDX_WRITE_TRI(vcc, vyc, vic); + IDX_WRITE_TRI(vcc, vxc, vic); + + IDX_WRITE_TRI(vxb, vxc, vic); + IDX_WRITE_TRI(vyd, vyc, vic); + } + + if (bd || 1) + { + IDX_WRITE_TRI(vcd, vyd, vid); + IDX_WRITE_TRI(vcd, vxd, vid); + + IDX_WRITE_TRI(vic, vyd, vid); + IDX_WRITE_TRI(vxa, vxd, vid); + } + + draw_list->_VtxWritePtr += dv; + draw_list->_VtxCurrentIdx += dv; + draw_list->_IdxWritePtr += di; + + draw_list->PrimReserve(di - idcs, dv - vtcs); // FIXME-OPT + + #undef IDX_WRITE_TRI + #undef VTX_WRITE +} + // a: upper-left, b: lower-right. we don't render 1 px sized rectangles properly. void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; + + rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((rounding_corners_flags & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners_flags & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f) - 1.0f); + rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((rounding_corners_flags & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners_flags & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f) - 1.0f); + + // FIXME-ROUND-SHAPES: NOTE HACK TODO figure out why it's broken on small rounding + if (ImGui::GetIO().KeyShift && rounding > 3) + return AddRoundCornerRect(this, a, b, col, rounding, rounding_corners_flags, /* fill */ false); + if (Flags & ImDrawListFlags_AntiAliasedLines) - PathRect(a + ImVec2(0.5f,0.5f), b - ImVec2(0.50f,0.50f), rounding, rounding_corners_flags); + PathRect(a + ImVec2(0.5f, 0.5f), b - ImVec2(0.50f, 0.50f), rounding, rounding_corners_flags); else - PathRect(a + ImVec2(0.5f,0.5f), b - ImVec2(0.49f,0.49f), rounding, rounding_corners_flags); // Better looking lower-right corner and rounded non-AA shapes. + PathRect(a + ImVec2(0.5f, 0.5f), b - ImVec2(0.49f, 0.49f), rounding, rounding_corners_flags); // Better looking lower-right corner and rounded non-AA shapes. PathStroke(col, true, thickness); } @@ -994,10 +1172,23 @@ { if ((col & IM_COL32_A_MASK) == 0) return; - if (rounding > 0.0f) + + rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((rounding_corners_flags & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners_flags & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f ) - 1.0f); + rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((rounding_corners_flags & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners_flags & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f ) - 1.0f); + + if (rounding > 0.0f && rounding_corners_flags != 0) { - PathRect(a, b, rounding, rounding_corners_flags); - PathFillConvex(col); + // // FIXME-ROUND-SHAPES: NOTE HACK TODO figure out why it's broken on small rounding + if (ImGui::GetIO().KeyShift && rounding > 3) + { + AddRoundCornerRect(this, a, b, col, rounding, rounding_corners_flags, /* fill */ true); + return; + } + else + { + PathRect(a, b, rounding, rounding_corners_flags); + PathFillConvex(col); + } } else { @@ -1067,11 +1258,85 @@ PathFillConvex(col); } +inline void AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& centre, float radius, ImU32 col, bool fill) +{ + const ImDrawListSharedData* data = draw_list->_Data; + ImTextureID tex_id = data->Font->ContainerAtlas->TexID; + IM_ASSERT(tex_id == draw_list->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + const int rad = (int)radius; + IM_ASSERT(rad <= data->Font->ContainerAtlas->RoundCornersMaxSize); + + const ImVec4& uvs = (*(fill ? data->TexUvRoundCornerFilled : data->TexUvRoundCornerStroked))[rad - 1]; + const ImVec2 corner_uv[3] = + { + ImVec2(uvs.x, uvs.y), + fill ? ImVec2(uvs.x, uvs.w) : ImVec2(uvs.z, uvs.y), + ImVec2(uvs.z, uvs.w), + }; + + const ImVec2& c = centre; + ImVec2 tl = ImVec2(c.x - rad, c.y - rad); + ImVec2 br = ImVec2(c.x + rad, c.y + rad); + + // NOTE: test performance using locals instead of array + const ImVec2 circle_vt[9] = + { + c, + tl, + ImVec2(c.x, tl.y), + ImVec2(br.x, tl.y), + ImVec2(br.x, c.y), + br, + ImVec2(c.x, br.y), + ImVec2(tl.x, br.y), + ImVec2(tl.x, c.y), + }; + + #define IDX_WRITE_TRI(d, idx0, idx1, idx2) \ + draw_list->_IdxWritePtr[d+0] = (ImDrawIdx)(idx+idx0); \ + draw_list->_IdxWritePtr[d+1] = (ImDrawIdx)(idx+idx1); \ + draw_list->_IdxWritePtr[d+2] = (ImDrawIdx)(idx+idx2) + + #define VTX_WRITE(d, i) \ + draw_list->_VtxWritePtr[d].pos = circle_vt[d]; \ + draw_list->_VtxWritePtr[d].uv = corner_uv[i]; \ + draw_list->_VtxWritePtr[d].col = col + + draw_list->PrimReserve(24, 9); + ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx; + IDX_WRITE_TRI( 0, 0, 1, 2); + IDX_WRITE_TRI( 3, 0, 3, 2); + IDX_WRITE_TRI( 6, 0, 3, 4); + IDX_WRITE_TRI( 9, 0, 5, 4); + IDX_WRITE_TRI(12, 0, 5, 6); + IDX_WRITE_TRI(15, 0, 7, 6); + IDX_WRITE_TRI(18, 0, 7, 8); + IDX_WRITE_TRI(21, 0, 1, 8); + + VTX_WRITE(1, 2); VTX_WRITE(2, 1); VTX_WRITE(3, 2); + VTX_WRITE(8, 1); VTX_WRITE(0, 0); VTX_WRITE(4, 1); + VTX_WRITE(7, 2); VTX_WRITE(6, 1); VTX_WRITE(5, 2); + + draw_list->_VtxWritePtr += 9; + draw_list->_VtxCurrentIdx += 9; + draw_list->_IdxWritePtr += 24; + +#undef IDX_WRITE_TRI +#undef VTX_WRITE +} + void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments, float thickness) { if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; + if (ImGui::GetIO().KeyShift) + { + AddRoundCornerCircle(this, centre, radius, col, false); + return; + } + // 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; PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments - 1); @@ -1083,6 +1348,12 @@ if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; + if (ImGui::GetIO().KeyShift) + { + AddRoundCornerCircle(this, centre, radius, col, true); + return; + } + // 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; PathArcTo(centre, radius, 0.0f, a_max, num_segments - 1); @@ -1495,6 +1766,8 @@ TexWidth = TexHeight = 0; TexUvScale = ImVec2(0.0f, 0.0f); TexUvWhitePixel = ImVec2(0.0f, 0.0f); + + RoundCornersMaxSize = 60; for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++) CustomRectIds[n] = -1; } @@ -1526,6 +1799,7 @@ CustomRects.clear(); for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++) CustomRectIds[n] = -1; + RoundCornersRectIds.clear(); } void ImFontAtlas::ClearTexData() @@ -1537,6 +1811,8 @@ IM_FREE(TexPixelsRGBA32); TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; + TexUvRoundCornerFilled.clear(); + TexUvRoundCornerStroked.clear(); } void ImFontAtlas::ClearFonts() @@ -1840,6 +2116,7 @@ IM_ASSERT(atlas->ConfigData.Size > 0); ImFontAtlasBuildRegisterDefaultCustomRects(atlas); + ImFontAtlasBuildRegisterRoundCornersCustomRects(atlas); // Clear atlas atlas->TexID = (ImTextureID)NULL; @@ -2173,11 +2450,103 @@ atlas->TexUvWhitePixel = ImVec2((r.X + 0.5f) * atlas->TexUvScale.x, (r.Y + 0.5f) * atlas->TexUvScale.y); } +const unsigned int FONT_ATLAS_ROUNDED_CORNER_TEX_DATA_ID = 0x80000001; +const int FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING = 2; + +void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas) +{ + if (atlas->RoundCornersRectIds.size() > 0) + return; + + if ((atlas->Flags & ImFontAtlasFlags_NoRoundCorners)) + return; + + const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING; + const int max = atlas->RoundCornersMaxSize; + + // Filled + for (int n = 0; n < max; n++) + atlas->RoundCornersRectIds.push_back(atlas->AddCustomRectRegular(FONT_ATLAS_ROUNDED_CORNER_TEX_DATA_ID + n, + n + 1 + pad * 2, n + 1 + pad * 2)); + + // Stroked + for (int n = 0; n < max; n++) + atlas->RoundCornersRectIds.push_back(atlas->AddCustomRectRegular(FONT_ATLAS_ROUNDED_CORNER_TEX_DATA_ID + max + n, + n + 1 + pad * 2, n + 1 + pad * 2)); +} + +static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas) +{ + IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); + IM_ASSERT(atlas->TexUvRoundCornerFilled.size() == 0); + IM_ASSERT(atlas->TexUvRoundCornerStroked.size() == 0); + + if ((atlas->Flags & ImFontAtlasFlags_NoRoundCorners)) + return; + + const int w = atlas->TexWidth; + const unsigned int max = atlas->RoundCornersMaxSize; + + // Filled + for (unsigned int stage = 0; stage < 2; stage++) + { + bool filled = stage == 0; + for (unsigned int n = 0; n < max; n++) + { + const unsigned int id = (filled ? 0 : max) + n; + IM_ASSERT(atlas->RoundCornersRectIds.size() > (int) n); + ImFontAtlas::CustomRect& r = atlas->CustomRects[atlas->RoundCornersRectIds[id]]; + IM_ASSERT(r.ID == FONT_ATLAS_ROUNDED_CORNER_TEX_DATA_ID + id); + IM_ASSERT(r.IsPacked()); + + const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING; + + IM_ASSERT(r.Width == n + 1 + pad * 2 && r.Height == n + 1 + pad * 2); + + const int radius = (int)(r.Width - pad * 2); + const float stroke_width = 1.0f; + + for (int y = -pad; y < (int) (radius); y++) + for (int x = (filled ? -pad : y); x < (int)(filled ? y + pad : radius); x++) + { + const float dist = ImSqrt((float)(x*x+y*y)) - (float)(radius - (filled ? 0 : stroke_width)); + + float alpha = 0.0f; + if (filled) + { + alpha = ImClamp(-dist, 0.0f, 1.0f); + } + else + { + const float alpha1 = ImClamp(dist + stroke_width, 0.0f, 1.0f); + const float alpha2 = ImClamp(dist, 0.0f, 1.0f); + alpha = alpha1 - alpha2; + } + + const unsigned int offset = (int)(r.X + pad + x) + (int)(r.Y + pad + y) * w; + atlas->TexPixelsAlpha8[offset] = (unsigned char)(0xFF * ImSaturate(alpha)); + } + + ImVec2 uv0, uv1; + r.X += pad; + r.Y += pad; + r.Width -= pad * 2; + r.Height -= pad * 2; + atlas->CalcCustomRectUV(&r, &uv0, &uv1); + ImVector& uvs = (filled ? atlas->TexUvRoundCornerFilled : atlas->TexUvRoundCornerStroked); + uvs.push_back(ImVec4(uv0.x, uv0.y, uv1.x, uv1.y)); + } + } +} + void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { // Render into our custom data block ImFontAtlasBuildRenderDefaultTexData(atlas); + // Render into our rounded corner data block + ImFontAtlasBuildRenderRoundCornersTexData(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 f82225e..66e06ff 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -726,17 +726,21 @@ // Data shared between all ImDrawList instances struct IMGUI_API ImDrawListSharedData { - ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas - ImFont* Font; // Current/default font (optional, for simplified AddText overload) - float FontSize; // Current/default font size (optional, for simplified AddText overload) + ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas + ImFont* Font; // Current/default font (optional, for simplified AddText overload) + float FontSize; // Current/default font size (optional, for simplified AddText overload) float CurveTessellationTol; - ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() - ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) + ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() + ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) // Const data // FIXME: Bake rounded corners fill/borders in atlas ImVec2 CircleVtx12[12]; + // FIXME-ROUNDSHAPES: WIP + need to remove CircleVtx12 before PR + ImVector* TexUvRoundCornerFilled; // UV of filled round corner quad in the atlas + ImVector* TexUvRoundCornerStroked; // UV of stroked round corner quad in the atlas + ImDrawListSharedData(); }; @@ -1654,6 +1658,7 @@ // ImFontAtlas internals IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildRegisterRoundCornersCustomRects(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 ImFontAtlasBuildFinish(ImFontAtlas* atlas);