From 1d3c3070d8462b6ca2d84e3ca0f05d28896018ec Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Mon, 13 Jan 2020 14:24:55 +0900 Subject: [PATCH] Texture-based thick lines: Initial version of AA line drawing using textures (press SHIFT to enable) --- imgui.cpp | 6 ++ imgui.h | 10 ++- imgui_demo.cpp | 57 +++++++++++++++++ imgui_draw.cpp | 156 +++++++++++++++++++++++++++++++++++++++-------- imgui_internal.h | 4 ++ 5 files changed, 206 insertions(+), 27 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d09d61ed..34b63566 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -935,6 +935,7 @@ ImGuiStyle::ImGuiStyle() DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. + TexturedAntiAliasedLines= true; // Draw anti-aliased lines using textures where possible. AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. @@ -3688,10 +3689,14 @@ void ImGui::NewFrame() g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; if (g.Style.AntiAliasedLines) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; + if (g.Style.TexturedAntiAliasedLines) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_TexturedAALines; if (g.Style.AntiAliasedFill) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; + if ((g.Style.TexturedAntiAliasedLines) && (!(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_TexturedAALines; g.BackgroundDrawList._ResetForNewFrame(); g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); @@ -6158,6 +6163,7 @@ void ImGui::SetCurrentFont(ImFont* font) ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; + g.DrawListSharedData.TexUvAALines = &atlas->TexUvAALines; g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.FontSize = g.FontSize; } diff --git a/imgui.h b/imgui.h index adb2ab1c..09d92309 100644 --- a/imgui.h +++ b/imgui.h @@ -1439,6 +1439,7 @@ struct ImGuiStyle ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. + bool TexturedAntiAliasedLines; // Draw anti-aliased lines using textures where possible. bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. float CircleSegmentMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. @@ -1997,7 +1998,8 @@ enum ImDrawListFlags_ ImDrawListFlags_None = 0, ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles) ImDrawListFlags_AntiAliasedFill = 1 << 1, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). - ImDrawListFlags_AllowVtxOffset = 1 << 2 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. + ImDrawListFlags_AllowVtxOffset = 1 << 2, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. + ImDrawListFlags_TexturedAALines = 1 << 3 // Should anti-aliased lines be drawn using textures where possible? }; // Draw command list @@ -2216,7 +2218,8 @@ enum ImFontAtlasFlags_ { ImFontAtlasFlags_None = 0, ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two - ImFontAtlasFlags_NoMouseCursors = 1 << 1 // Don't build software mouse cursors into the atlas (save a little texture memory) + ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory) + ImFontAtlasFlags_NoAALines = 1 << 2 // Don't build anti-aliased line textures into the atlas }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: @@ -2320,6 +2323,9 @@ struct ImFontAtlas // [Internal] Packing data int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors + int AALineMaxWidth; // Maximum line width to build anti-aliased textures for + ImVector AALineRectIds; // Custom texture rectangle IDs for anti-aliased lines + ImVector TexUvAALines; // UVs for anti-aliased line textures #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b481c305..f06d8526 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -297,6 +297,62 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); + // Test lines + + if (ImGui::Begin("Lines")) + { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + const int num_cols = 16; + const int num_rows = 3; + const float line_len = 64.0f; + const float line_spacing = 128.0f; + + static float base_rot = 0.0f; + ImGui::SliderFloat("Base rotation", &base_rot, 0.0f, 360.0f); + static float line_width = 1.0f; + ImGui::SliderFloat("Line width", &line_width, 1.0f, 10.0f); + + ImVec2 window_pos = ImGui::GetWindowPos(); + ImVec2 cursor_pos = ImGui::GetCursorPos(); + ImVec2 base_pos(window_pos.x + cursor_pos.x + (line_spacing * 0.5f), window_pos.y + cursor_pos.y); + + for (int i = 0; i < num_rows; i++) + { + const char* name = ""; + switch (i) + { + case 0: name = "No AA"; draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; break; + case 1: name = "AA no texturing"; draw_list->Flags |= ImDrawListFlags_AntiAliasedLines; draw_list->Flags &= ~ImDrawListFlags_TexturedAALines; break; + case 2: name = "AA with texturing"; draw_list->Flags |= ImDrawListFlags_AntiAliasedLines; draw_list->Flags |= ImDrawListFlags_TexturedAALines; break; + } + + int initial_vtx_count = draw_list->VtxBuffer.Size; + int initial_idx_count = draw_list->IdxBuffer.Size; + + for (int j = 0; j < num_cols; j++) + { + const float pi = 3.14159265359f; + float r = (base_rot * pi / 180.0f) + ((j * pi * 0.5f) / (num_cols - 1)); + + ImVec2 center = ImVec2(base_pos.x + (line_spacing * (j * 0.5f)), base_pos.y + (line_spacing * (i + 0.5f))); + ImVec2 start = ImVec2(center.x + (sinf(r) * line_len * 0.5f), center.y + (cosf(r) * line_len * 0.5f)); + ImVec2 end = ImVec2(center.x - (sinf(r) * line_len * 0.5f), center.y - (cosf(r) * line_len * 0.5f)); + + draw_list->AddLine(start, end, IM_COL32(255, 255, 255, 255), line_width); + } + + ImGui::SetCursorPosY(cursor_pos.y + (i * line_spacing)); + ImGui::Text("%s - %d vertices, %d indices", name, draw_list->VtxBuffer.Size - initial_vtx_count, draw_list->IdxBuffer.Size - initial_idx_count); + } + + ImGui::SetCursorPosY(cursor_pos.y + (num_rows * line_spacing)); + + //ImGui::Spacing(); ImGui::Spacing(); ImGui::Spacing(); + //ImGui::Image(ImGui::GetFont()->ContainerAtlas->TexID, ImVec2((float)ImGui::GetFont()->ContainerAtlas->TexWidth, (float)ImGui::GetFont()->ContainerAtlas->TexHeight)); + } + ImGui::End(); + // Main body of the Demo window starts here. if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags)) { @@ -3831,6 +3887,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) { ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); + ImGui::Checkbox("Use textures for anti-aliased lines", &style.TexturedAntiAliasedLines); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); ImGui::PushItemWidth(100); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 30b9b9fe..7c845f94 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -667,6 +667,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const ImVec2 opaque_uv = _Data->TexUvWhitePixel; const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw + const bool thick_line = (thickness > 1.0f); if (Flags & ImDrawListFlags_AntiAliasedLines) @@ -675,13 +676,24 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const float AA_SIZE = 1.0f; const ImU32 col_trans = col & ~IM_COL32_A_MASK; - const int idx_count = thick_line ? count * 18 : count * 12; - const int vtx_count = thick_line ? points_count * 4 : points_count * 3; + const int integer_thickness = (int)thickness; + + // Do we want to draw this line using a texture? + bool use_textures = (Flags & ImDrawListFlags_TexturedAALines) && + (integer_thickness >= 1) && + (integer_thickness <= _Data->Font->ContainerAtlas->AALineMaxWidth) && + ImGui::GetIO().KeyShift; // FIXME-AALINES: Remove this debug code + + // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_TexturedAALines unless ImFontAtlasFlags_NoAALines is off + IM_ASSERT_PARANOID((!use_textures) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); + + const int idx_count = use_textures ? (count * 6) : (thick_line ? count * 18 : count * 12); + const int vtx_count = use_textures ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3); PrimReserve(idx_count, vtx_count); // Temporary buffer // The first items are normals at each line point, then after that there are either 2 or 4 temp points for each line point - ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); //-V630 + ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((thick_line && !use_textures) ? 5 : 3) * sizeof(ImVec2)); //-V630 ImVec2* temp_points = temp_normals + points_count; // Calculate normals (tangents) for each line segment @@ -697,14 +709,19 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 if (!closed) temp_normals[points_count - 1] = temp_normals[points_count - 2]; - if (!thick_line) + // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point + if ((!thick_line) || (use_textures)) { + // The width of the geometry we need to draw + const float half_draw_size = (!thick_line) ? AA_SIZE : (AA_SIZE + (thickness * 0.5f)); + + // If line is not closed, the first and last points need to be generated differently as there are no normals to blend if (!closed) { - temp_points[0] = points[0] + temp_normals[0] * AA_SIZE; - temp_points[1] = points[0] - temp_normals[0] * AA_SIZE; - temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * AA_SIZE; - temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * AA_SIZE; + temp_points[0] = points[0] + temp_normals[0] * half_draw_size; + temp_points[1] = points[0] - temp_normals[0] * half_draw_size; + temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * half_draw_size; + temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * half_draw_size; } // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges @@ -713,15 +730,15 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment { - const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; - const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : idx1 + 3; + const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment + unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_textures ? 2 : 3)); // Vertex index for end of segment // Average normals float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; IM_FIXNORMAL2F(dm_x, dm_y); - dm_x *= AA_SIZE; - dm_y *= AA_SIZE; + dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area + dm_y *= half_draw_size; // Add temporary vertexes for the outer edges ImVec2* out_vtx = &temp_points[i2 * 2]; @@ -730,28 +747,54 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 out_vtx[1].x = points[i2].x - dm_x; out_vtx[1].y = points[i2].y - dm_y; - // Add indexes for four triangles - _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); - _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+0); - _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); - _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10]= (ImDrawIdx)(idx2+0); _IdxWritePtr[11]= (ImDrawIdx)(idx2+1); - _IdxWritePtr += 12; + if (use_textures) + { + // Add indices for two triangles + _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri + _IdxWritePtr[3] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[4] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Left tri + _IdxWritePtr += 6; + } + else + { + // Add indexes for four triangles + _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2); // Right tri 1 + _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Right tri 2 + _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); // Left tri 1 + _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); // Left tri 2 + _IdxWritePtr += 12; + } idx1 = idx2; } // Add vertexes for each point on the line - for (int i = 0; i < points_count; i++) + if (use_textures) { - _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; - _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; - _VtxWritePtr += 3; + // If we're using textures we only need to emit the left/right edge vertices + const ImVec4 tex_uvs = (*_Data->TexUvAALines)[integer_thickness - 1]; + + for (int i = 0; i < points_count; i++) + { + _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = ImVec2(tex_uvs.x, tex_uvs.y); _VtxWritePtr[0].col = col; // Left-side outer edge + _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = ImVec2(tex_uvs.z, tex_uvs.y); _VtxWritePtr[1].col = col; // Right-side outer edge + _VtxWritePtr += 2; + } + } + else + { + // If we're not using a texture, we need the centre vertex as well + for (int i = 0; i < points_count; i++) + { + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; // Centre of line + _VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge + _VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge + _VtxWritePtr += 3; + } } } else { - // Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point + // For untextured lines that are greater than a pixel in width, we need to draw the solid line core and thus require four vertices per point const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; // If line is not closed, the first and last points need to be generated differently as there are no normals to blend @@ -1664,6 +1707,8 @@ ImFontAtlas::ImFontAtlas() TexUvScale = ImVec2(0.0f, 0.0f); TexUvWhitePixel = ImVec2(0.0f, 0.0f); PackIdMouseCursors = -1; + + AALineMaxWidth = 8; } ImFontAtlas::~ImFontAtlas() @@ -2010,6 +2055,7 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) IM_ASSERT(atlas->ConfigData.Size > 0); ImFontAtlasBuildInit(atlas); + ImFontAtlasBuildRegisterAALineCustomRects(atlas); // Clear atlas atlas->TexID = (ImTextureID)NULL; @@ -2331,7 +2377,6 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); } - // Note: this is called / shared by both the stb_truetype and the FreeType builder void ImFontAtlasBuildInit(ImFontAtlas* atlas) { @@ -2346,12 +2391,73 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) } // This is called/shared by both the stb_truetype and the FreeType builder. +const unsigned int FONT_ATLAS_AA_LINE_TEX_HEIGHT = 1; // Technically we only need 1 pixel in the ideal case but this can be increased if necessary to give a border to avoid sampling artifacts + +void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) +{ + if (atlas->AALineRectIds.size() > 0) + return; + + if ((atlas->Flags & ImFontAtlasFlags_NoAALines)) + return; + + const int max = atlas->AALineMaxWidth; + + for (int n = 0; n < max; n++) + { + const int width = n + 1; // The line width this entry corresponds to + // The "width + 3" here is interesting - +2 is to give space for the end caps, but the remaining +1 is because (empirically) to match the behaviour of the untextured render path we need to draw lines one pixel wider + atlas->AALineRectIds.push_back(atlas->AddCustomRectRegular(width + 3, FONT_ATLAS_AA_LINE_TEX_HEIGHT)); + } +} + +void ImFontAtlasBuildAALinesTexData(ImFontAtlas* atlas) +{ + IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); + IM_ASSERT(atlas->TexUvAALines.size() == 0); + + if (atlas->Flags & ImFontAtlasFlags_NoAALines) + return; + + const int w = atlas->TexWidth; + const unsigned int max = atlas->AALineMaxWidth; + + for (unsigned int n = 0; n < max; n++) + { + IM_ASSERT(atlas->AALineRectIds.size() > (int)n); + ImFontAtlas::CustomRect& r = atlas->CustomRects[atlas->AALineRectIds[n]]; + IM_ASSERT(r.IsPacked()); + + // We fill as many lines as we were given, to allow for >1 lines being used to work around sampling weirdness + for (unsigned int y = 0; y < r.Height; y++) + { + unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r.X + ((r.Y + y) * w)]; + + // Each line consists of two empty pixels at the ends, with a line of solid pixels in the middle + *(write_ptr++) = 0; + for (unsigned short x = 0; x < (r.Width - 2U); x++) + { + *(write_ptr++) = 0xFF; + } + *(write_ptr++) = 0; + } + + ImVec2 uv0, uv1; + atlas->CalcCustomRectUV(&r, &uv0, &uv1); + float halfV = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the texture as we want a horizontal slice (with some padding either side to avoid sampling artifacts) + atlas->TexUvAALines.push_back(ImVec4(uv0.x, halfV, uv1.x, halfV)); + } +} + void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { // Render into our custom data blocks IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); ImFontAtlasBuildRenderDefaultTexData(atlas); + // Render anti-aliased line textures + ImFontAtlasBuildAALinesTexData(atlas); + // Register custom rectangle glyphs for (int i = 0; i < atlas->CustomRects.Size; i++) { diff --git a/imgui_internal.h b/imgui_internal.h index b57b01b5..2035f324 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -545,6 +545,8 @@ struct IMGUI_API ImDrawListSharedData ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead) + ImVector* TexUvAALines; // UV of anti-aliased lines in the atlas + ImDrawListSharedData(); void SetCircleSegmentMaxError(float max_error); }; @@ -2018,8 +2020,10 @@ namespace ImGui // ImFontAtlas internals IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); +IMGUI_API void ImFontAtlasBuildAALinesTexData(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);