|
@@ -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;
|
|
|
|
- const int vtx_count = thick_line ? points_count * 4 : points_count * 3;
|
|
|
|
|
|
+ // Thicknesses <1.0 should behave like thickness 1.0
|
|
|
|
+ thickness = ImMax(thickness, 1.0f);
|
|
|
|
+ const int integer_thickness = (int)thickness;
|
|
|
|
+ const float fractional_thickness = thickness - integer_thickness;
|
|
|
|
+
|
|
|
|
+ // Do we want to draw this line using a texture?
|
|
|
|
+ // - 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[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
|
|
// 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 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
|
|
|
|
+ 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_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
|
|
// 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
|
|
|
|
- _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_texture)
|
|
|
|
+ {
|
|
|
|
+ // 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;
|
|
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)
|
|
|
|
+ {
|
|
|
|
+ // If we're using textures we only need to emit the left/right edge vertices
|
|
|
|
+ ImVec4 tex_uvs = _Data->TexUvLines[integer_thickness];
|
|
|
|
+ if (fractional_thickness != 0.0f)
|
|
|
|
+ {
|
|
|
|
+ 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
|
|
{
|
|
{
|
|
- _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;
|
|
|
|
- _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans;
|
|
|
|
- _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans;
|
|
|
|
- _VtxWritePtr += 3;
|
|
|
|
|
|
+ // If we're not using a texture, we need the 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++)
|