Merge branch 'features/tex_antialiased_lines' (#3245)

This commit is contained in:
omar 2020-07-08 20:19:57 +02:00
commit 66336528c8
6 changed files with 158 additions and 33 deletions

View File

@ -35,6 +35,15 @@ HOW TO UPDATE?
VERSION 1.78 WIP (In Progress) VERSION 1.78 WIP (In Progress)
----------------------------------------------------------------------- -----------------------------------------------------------------------
Other Changes:
- ImDrawList: Thick anti-aliased strokes (> 1.0f) with integer thickness now use a texture-based
path, reducing the amount of vertices/indices and CPU/GPU usage. (#3245) [@Shironekoben]
- This change will facilitate the wider use of thick borders in future style changes.
- Requires an extra bit of texture space (~64x64 by default), relies on GPU bilinear filtering.
- Clear io.AntiAliasedLinesUseTex = false; to disable rendering using this method.
- Clear ImFontAtlasFlags_NoBakedLines in ImFontAtlas::Flags to disable baking data in texture.
----------------------------------------------------------------------- -----------------------------------------------------------------------
VERSION 1.77 (Released 2020-06-29) VERSION 1.77 (Released 2020-06-29)

View File

@ -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. 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. 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. AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require back-end to render with bilinear filtering.
AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). 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. 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. 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,6 +3689,8 @@ void ImGui::NewFrame()
g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
if (g.Style.AntiAliasedLines) if (g.Style.AntiAliasedLines)
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
if (g.Style.AntiAliasedFill) if (g.Style.AntiAliasedFill)
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
@ -6158,6 +6161,7 @@ void ImGui::SetCurrentFont(ImFont* font)
ImFontAtlas* atlas = g.Font->ContainerAtlas; ImFontAtlas* atlas = g.Font->ContainerAtlas;
g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.Font = g.Font;
g.DrawListSharedData.FontSize = g.FontSize; g.DrawListSharedData.FontSize = g.FontSize;
} }

24
imgui.h
View File

@ -60,7 +60,7 @@ Index of this file:
// Version // Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
#define IMGUI_VERSION "1.78 WIP" #define IMGUI_VERSION "1.78 WIP"
#define IMGUI_VERSION_NUM 17702 #define IMGUI_VERSION_NUM 17703
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
// Define attributes of all API symbols declarations (e.g. for DLL under Windows) // Define attributes of all API symbols declarations (e.g. for DLL under Windows)
@ -1438,8 +1438,9 @@ struct ImGuiStyle
ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! 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. 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 AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require back-end to render with bilinear filtering. Latched at the beginning of the frame (copied to ImDrawList).
bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. float 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. float CircleSegmentMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
ImVec4 Colors[ImGuiCol_COUNT]; ImVec4 Colors[ImGuiCol_COUNT];
@ -1896,6 +1897,11 @@ struct ImColor
// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking.
#ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX
#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX (63)
#endif
// ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h] // ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h]
// NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering, // NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering,
// you can poke into the draw list for that! Draw callback may be useful for example to: // you can poke into the draw list for that! Draw callback may be useful for example to:
@ -1992,12 +1998,15 @@ enum ImDrawCornerFlags_
ImDrawCornerFlags_All = 0xF // In your function calls you may use ~0 (= all bits sets) instead of ImDrawCornerFlags_All, as a convenience ImDrawCornerFlags_All = 0xF // In your function calls you may use ~0 (= all bits sets) instead of ImDrawCornerFlags_All, as a convenience
}; };
// Flags for ImDrawList. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly.
// It is however possible to temporarily alter flags between calls to ImDrawList:: functions.
enum ImDrawListFlags_ enum ImDrawListFlags_
{ {
ImDrawListFlags_None = 0, 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_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles)
ImDrawListFlags_AntiAliasedFill = 1 << 1, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require back-end to render with bilinear filtering.
ImDrawListFlags_AllowVtxOffset = 1 << 2 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles).
ImDrawListFlags_AllowVtxOffset = 1 << 3 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled.
}; };
// Draw command list // Draw command list
@ -2216,7 +2225,8 @@ enum ImFontAtlasFlags_
{ {
ImFontAtlasFlags_None = 0, ImFontAtlasFlags_None = 0,
ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two 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_NoBakedLines = 1 << 2 // Don't build thick line textures into the atlas (save a little texture memory). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU).
}; };
// Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding:
@ -2317,9 +2327,11 @@ struct ImFontAtlas
ImVector<ImFont*> Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. ImVector<ImFont*> Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font.
ImVector<ImFontAtlasCustomRect> CustomRects; // Rectangles for packing custom texture data into the atlas. ImVector<ImFontAtlasCustomRect> CustomRects; // Rectangles for packing custom texture data into the atlas.
ImVector<ImFontConfig> ConfigData; // Configuration data ImVector<ImFontConfig> ConfigData; // Configuration data
ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines
// [Internal] Packing data // [Internal] Packing data
int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors
int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+

View File

@ -3831,6 +3831,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
{ {
ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); 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::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex);
ImGui::SameLine(); HelpMarker("Faster lines using texture data. Require back-end to render with bilinear filtering (not point/nearest filtering).");
ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
ImGui::PushItemWidth(100); ImGui::PushItemWidth(100);
ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");

View File

@ -358,6 +358,7 @@ ImDrawListSharedData::ImDrawListSharedData()
ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a)); ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));
} }
memset(CircleSegmentCounts, 0, sizeof(CircleSegmentCounts)); // This will be set by SetCircleSegmentMaxError() memset(CircleSegmentCounts, 0, sizeof(CircleSegmentCounts)); // This will be set by SetCircleSegmentMaxError()
TexUvLines = NULL;
} }
void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error) void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error)
@ -675,13 +676,26 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
const float AA_SIZE = 1.0f; const float AA_SIZE = 1.0f;
const ImU32 col_trans = col & ~IM_COL32_A_MASK; const ImU32 col_trans = col & ~IM_COL32_A_MASK;
const int idx_count = thick_line ? count * 18 : count * 12; // Thicknesses <1.0 should behave like thickness 1.0
const int vtx_count = thick_line ? points_count * 4 : points_count * 3; thickness = ImMax(thickness, 1.0f);
const int integer_thickness = (int)thickness;
const float fractional_thickness = thickness - integer_thickness;
// Do we want to draw this line using a texture?
// - For now, only draw integer-width lines using textures to avoid issues with the way scaling occurs, could be improved.
// - If AA_SIZE is not 1.0f we cannot use the texture path.
const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f);
// We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off
IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));
const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12);
const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3);
PrimReserve(idx_count, vtx_count); PrimReserve(idx_count, vtx_count);
// Temporary buffer // Temporary buffer
// The first <points_count> items are normals at each line point, then after that there are either 2 or 4 temp points for each line point // The first <points_count> 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 * ((use_texture || !thick_line) ? 3 : 5) * sizeof(ImVec2)); //-V630
ImVec2* temp_points = temp_normals + points_count; ImVec2* temp_points = temp_normals + points_count;
// Calculate normals (tangents) for each line segment // Calculate normals (tangents) for each line segment
@ -697,14 +711,23 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
if (!closed) if (!closed)
temp_normals[points_count - 1] = temp_normals[points_count - 2]; 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 (use_texture || !thick_line)
{ {
// [PATH 1] Texture-based lines (thick or non-thick)
// [PATH 2] Non texture-based lines (non-thick)
// The width of the geometry we need to draw - this is essentially <thickness> pixels for the line itself, plus one pixel for AA
// We don't use AA_SIZE here because the +1 is tied to the generated texture and so alternate values won't work without changes to that code
const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : AA_SIZE;
// If line is not closed, the first and last points need to be generated differently as there are no normals to blend
if (!closed) if (!closed)
{ {
temp_points[0] = points[0] + temp_normals[0] * AA_SIZE; temp_points[0] = points[0] + temp_normals[0] * half_draw_size;
temp_points[1] = points[0] - temp_normals[0] * AA_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] * AA_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] * AA_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 // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges
@ -713,15 +736,15 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment 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 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 int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment
const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : idx1 + 3; const unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment
// Average normals // Average normals
float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; 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; float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
IM_FIXNORMAL2F(dm_x, dm_y); IM_FIXNORMAL2F(dm_x, dm_y);
dm_x *= AA_SIZE; dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area
dm_y *= AA_SIZE; dm_y *= half_draw_size;
// Add temporary vertexes for the outer edges // Add temporary vertexes for the outer edges
ImVec2* out_vtx = &temp_points[i2 * 2]; ImVec2* out_vtx = &temp_points[i2 * 2];
@ -730,28 +753,63 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
out_vtx[1].x = points[i2].x - dm_x; out_vtx[1].x = points[i2].x - dm_x;
out_vtx[1].y = points[i2].y - dm_y; out_vtx[1].y = points[i2].y - dm_y;
// Add indexes for four triangles if (use_texture)
_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); // Add indices for two triangles
_IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri
_IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10]= (ImDrawIdx)(idx2+0); _IdxWritePtr[11]= (ImDrawIdx)(idx2+1); _IdxWritePtr[3] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[4] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Left tri
_IdxWritePtr += 12; _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; idx1 = idx2;
} }
// Add vertexes for each point on the line // Add vertexes for each point on the line
for (int i = 0; i < points_count; i++) if (use_texture)
{ {
_VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; // If we're using textures we only need to emit the left/right edge vertices
_VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; ImVec4 tex_uvs = _Data->TexUvLines[integer_thickness];
_VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; if (fractional_thickness != 0.0f)
_VtxWritePtr += 3; {
const ImVec4 tex_uvs_1 = _Data->TexUvLines[integer_thickness + 1];
tex_uvs.x = tex_uvs.x + (tex_uvs_1.x - tex_uvs.x) * fractional_thickness; // inlined ImLerp()
tex_uvs.y = tex_uvs.y + (tex_uvs_1.y - tex_uvs.y) * fractional_thickness;
tex_uvs.z = tex_uvs.z + (tex_uvs_1.z - tex_uvs.z) * fractional_thickness;
tex_uvs.w = tex_uvs.w + (tex_uvs_1.w - tex_uvs.w) * fractional_thickness;
}
ImVec2 tex_uv0(tex_uvs.x, tex_uvs.y);
ImVec2 tex_uv1(tex_uvs.z, tex_uvs.w);
for (int i = 0; i < points_count; i++)
{
_VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = tex_uv0; _VtxWritePtr[0].col = col; // Left-side outer edge
_VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = tex_uv1; _VtxWritePtr[1].col = col; // Right-side outer edge
_VtxWritePtr += 2;
}
}
else
{
// If we're not using a texture, we need the center vertex as well
for (int i = 0; i < points_count; i++)
{
_VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; // Center of line
_VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge
_VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge
_VtxWritePtr += 3;
}
} }
} }
else else
{ {
// Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point // [PATH 2] Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point
const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f;
// If line is not closed, the first and last points need to be generated differently as there are no normals to blend // If line is not closed, the first and last points need to be generated differently as there are no normals to blend
@ -823,7 +881,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
} }
else else
{ {
// Non texture-based, Non anti-aliased lines // [PATH 4] Non texture-based, Non anti-aliased lines
const int idx_count = count * 6; const int idx_count = count * 6;
const int vtx_count = count * 4; // FIXME-OPT: Not sharing edges const int vtx_count = count * 4; // FIXME-OPT: Not sharing edges
PrimReserve(idx_count, vtx_count); PrimReserve(idx_count, vtx_count);
@ -1663,7 +1721,7 @@ ImFontAtlas::ImFontAtlas()
TexWidth = TexHeight = 0; TexWidth = TexHeight = 0;
TexUvScale = ImVec2(0.0f, 0.0f); TexUvScale = ImVec2(0.0f, 0.0f);
TexUvWhitePixel = ImVec2(0.0f, 0.0f); TexUvWhitePixel = ImVec2(0.0f, 0.0f);
PackIdMouseCursors = -1; PackIdMouseCursors = PackIdLines = -1;
} }
ImFontAtlas::~ImFontAtlas() ImFontAtlas::~ImFontAtlas()
@ -1691,7 +1749,7 @@ void ImFontAtlas::ClearInputData()
} }
ConfigData.clear(); ConfigData.clear();
CustomRects.clear(); CustomRects.clear();
PackIdMouseCursors = -1; PackIdMouseCursors = PackIdLines = -1;
} }
void ImFontAtlas::ClearTexData() void ImFontAtlas::ClearTexData()
@ -2331,6 +2389,36 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)
atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y);
} }
static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
{
if (atlas->Flags & ImFontAtlasFlags_NoBakedLines)
return;
// This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them
ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdLines);
IM_ASSERT(r->IsPacked());
for (unsigned int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row
{
// Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle
unsigned int y = n;
unsigned int line_width = n;
unsigned int pad_left = (r->Width - line_width) / 2;
unsigned int pad_right = r->Width - (pad_left + line_width);
// Write each slice
IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels
unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)];
memset(write_ptr, 0x00, pad_left);
memset(write_ptr + pad_left, 0xFF, line_width);
memset(write_ptr + pad_left + line_width, 0x00, pad_right);
// Calculate UVs for this line
ImVec2 uv0 = ImVec2((float)(r->X + pad_left - 1), (float)(r->Y + y)) * atlas->TexUvScale;
ImVec2 uv1 = ImVec2((float)(r->X + pad_left + line_width + 1), (float)(r->Y + y + 1)) * atlas->TexUvScale;
float half_v = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts
atlas->TexUvLines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v);
}
}
// Note: this is called / shared by both the stb_truetype and the FreeType builder // Note: this is called / shared by both the stb_truetype and the FreeType builder
void ImFontAtlasBuildInit(ImFontAtlas* atlas) void ImFontAtlasBuildInit(ImFontAtlas* atlas)
@ -2343,6 +2431,14 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas)
else else
atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(2, 2); atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(2, 2);
} }
// Register texture region for thick lines
// The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row
if (atlas->PackIdLines < 0)
{
if (!(atlas->Flags & ImFontAtlasFlags_NoBakedLines))
atlas->PackIdLines = atlas->AddCustomRectRegular(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1);
}
} }
// This is called/shared by both the stb_truetype and the FreeType builder. // This is called/shared by both the stb_truetype and the FreeType builder.
@ -2351,6 +2447,7 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
// Render into our custom data blocks // Render into our custom data blocks
IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);
ImFontAtlasBuildRenderDefaultTexData(atlas); ImFontAtlasBuildRenderDefaultTexData(atlas);
ImFontAtlasBuildRenderLinesTexData(atlas);
// Register custom rectangle glyphs // Register custom rectangle glyphs
for (int i = 0; i < atlas->CustomRects.Size; i++) for (int i = 0; i < atlas->CustomRects.Size; i++)

View File

@ -544,6 +544,7 @@ struct IMGUI_API ImDrawListSharedData
// [Internal] Lookup tables // [Internal] Lookup tables
ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas 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) ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead)
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas
ImDrawListSharedData(); ImDrawListSharedData();
void SetCircleSegmentMaxError(float max_error); void SetCircleSegmentMaxError(float max_error);