mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-10-30 20:51:06 +01:00 
			
		
		
		
	Texture-based thick lines: Allow interpolation between textures for non-integer line widths
This commit is contained in:
		
							
								
								
									
										2
									
								
								imgui.h
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								imgui.h
									
									
									
									
									
								
							| @@ -2323,7 +2323,7 @@ struct ImFontAtlas | |||||||
|  |  | ||||||
|     // [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 | ||||||
|     ImVector<int>               AALineRectIds;      // Custom texture rectangle IDs for anti-aliased lines |     int                         AALineRectId;       // Custom texture rectangle ID for anti-aliased lines | ||||||
|     ImVector<ImVec4>            TexUvAALines;       // UVs for anti-aliased line textures |     ImVector<ImVec4>            TexUvAALines;       // UVs for anti-aliased line textures | ||||||
|  |  | ||||||
| #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS | #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS | ||||||
|   | |||||||
| @@ -677,11 +677,12 @@ 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; | ||||||
|  |  | ||||||
|         // The -0.5f here is to better match the geometry-based code, and also shift the transition point from one width texture to another off the integer values (where it will be less noticeable) |         // The thick_line test is an attempt to compensate for the way half_draw_size gets calculated later, which special-cases 1.0f width lines | ||||||
|         const int integer_thickness = ImMax((int)(thickness - 0.5f), 1); |         const int integer_thickness = thick_line ? ImMax((int)(thickness), 1) : 2; | ||||||
|  |         const float fractional_thickness = thick_line ? (thickness) - integer_thickness : 0.0f; | ||||||
|  |  | ||||||
|         // Do we want to draw this line using a texture? |         // Do we want to draw this line using a texture? | ||||||
|         const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTexData) && (integer_thickness <= IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); |         const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTexData) && (integer_thickness < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX); | ||||||
|  |  | ||||||
|         // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTexData unless ImFontAtlasFlags_NoAALines is off |         // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTexData unless ImFontAtlasFlags_NoAALines is off | ||||||
|         IM_ASSERT_PARANOID((!use_texture) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); |         IM_ASSERT_PARANOID((!use_texture) || (!(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoAALines))); | ||||||
| @@ -711,8 +712,9 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 | |||||||
|         // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point |         // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point | ||||||
|         if (!thick_line || use_texture) |         if (!thick_line || use_texture) | ||||||
|         { |         { | ||||||
|             // The width of the geometry we need to draw |             // The width of the geometry we need to draw - this is essentially <thickness> pixels for the line itself, plus one pixel for AA | ||||||
|             const float half_draw_size = (!thick_line) ? AA_SIZE : (AA_SIZE + (thickness * 0.5f)); |             // We don't use AA_SIZE here because the +1 is tied to the generated texture and so alternate values won't work without changes to that code | ||||||
|  |             const float half_draw_size = (thickness * 0.5f) + 1; | ||||||
|  |  | ||||||
|             // If line is not closed, the first and last points need to be generated differently as there are no normals to blend |             // If 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) | ||||||
| @@ -770,7 +772,18 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 | |||||||
|             if (use_texture) |             if (use_texture) | ||||||
|             { |             { | ||||||
|                 // If we're using textures we only need to emit the left/right edge vertices |                 // If we're using textures we only need to emit the left/right edge vertices | ||||||
|                 const ImVec4 tex_uvs = (*_Data->TexUvAALines)[integer_thickness - 1]; |  | ||||||
|  |                 ImVec4 tex_uvs; | ||||||
|  |  | ||||||
|  |                 if (fractional_thickness == 0.0f) // Fast path for pure integer widths | ||||||
|  |                     tex_uvs = (*_Data->TexUvAALines)[integer_thickness]; | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     // Calculate UV by interpolating between the two nearest integer line widths | ||||||
|  |                     const ImVec4 tex_uvs_0 = (*_Data->TexUvAALines)[integer_thickness]; | ||||||
|  |                     const ImVec4 tex_uvs_1 = (*_Data->TexUvAALines)[integer_thickness + 1]; | ||||||
|  |                     tex_uvs = ImLerp(tex_uvs_0, tex_uvs_1, fractional_thickness); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 for (int i = 0; i < points_count; i++) |                 for (int i = 0; i < points_count; i++) | ||||||
|                 { |                 { | ||||||
| @@ -2373,24 +2386,14 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) | |||||||
|     atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); |     atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); | ||||||
| } | } | ||||||
|  |  | ||||||
| // This is called/shared by both the stb_truetype and the FreeType builder. |  | ||||||
| const unsigned int FONT_ATLAS_AA_LINE_TEX_HEIGHT = 1; // Technically we only need 1 pixel in the ideal case but this can be increased if necessary to give a border to avoid sampling artifacts |  | ||||||
|  |  | ||||||
| static void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) | static void ImFontAtlasBuildRegisterAALineCustomRects(ImFontAtlas* atlas) | ||||||
| { | { | ||||||
|     if (atlas->AALineRectIds.Size > 0) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     if ((atlas->Flags & ImFontAtlasFlags_NoAALines)) |     if ((atlas->Flags & ImFontAtlasFlags_NoAALines)) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     for (int n = 0; n < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; n++) |     const int max_width = IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; // The maximum line width we want to generate | ||||||
|     { |     // The "max_width + 2" here is to give space for the end caps, whilst height (IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX+1) is to accommodate the fact we have a zero-width row | ||||||
|         // The "width + 3" here is interesting. +2 is to give space for the end caps, but the remaining +1 is |     atlas->AALineRectId = atlas->AddCustomRectRegular(max_width + 2, IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1); | ||||||
|         // because (empirically) to match the behavior of the untextured render path we need to draw lines one pixel wider. |  | ||||||
|         const int width = n + 1; // The line width this entry corresponds to |  | ||||||
|         atlas->AALineRectIds.push_back(atlas->AddCustomRectRegular(width + 3, FONT_ATLAS_AA_LINE_TEX_HEIGHT)); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) | static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) | ||||||
| @@ -2401,27 +2404,42 @@ static void ImFontAtlasBuildRenderAALinesTexData(ImFontAtlas* atlas) | |||||||
|     if (atlas->Flags & ImFontAtlasFlags_NoAALines) |     if (atlas->Flags & ImFontAtlasFlags_NoAALines) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     const int w = atlas->TexWidth; |     ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->AALineRectId]; | ||||||
|     for (unsigned int n = 0; n < IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX; n++) |     IM_ASSERT(r.IsPacked()); | ||||||
|     { |  | ||||||
|         IM_ASSERT(atlas->AALineRectIds.Size > (int)n); |  | ||||||
|         ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->AALineRectIds[n]]; |  | ||||||
|         IM_ASSERT(r.IsPacked()); |  | ||||||
|  |  | ||||||
|         // We fill as many lines as we were given, to allow for >1 lines being used to work around sampling weirdness |     // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them | ||||||
|         for (unsigned int y = 0; y < r.Height; y++) |     const int w = atlas->TexWidth; | ||||||
|         { |     for (unsigned int n = 0; n < (IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX + 1); n++) // +1 because of the zero-width row | ||||||
|             // Each line consists of two empty pixels at the ends, with a line of solid pixels in the middle |     { | ||||||
|             unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r.X + ((r.Y + y) * w)]; |         unsigned int y = n; | ||||||
|  |         unsigned int line_width = n; | ||||||
|  |         // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle | ||||||
|  |         unsigned int pad_left = (r.Width - line_width) / 2; | ||||||
|  |         unsigned int pad_right = r.Width - (pad_left + line_width); | ||||||
|  |  | ||||||
|  |         // Make sure we're inside the texture bounds before we start writing pixels | ||||||
|  |         IM_ASSERT_PARANOID(pad_left + line_width + pad_right == r.Width); | ||||||
|  |         IM_ASSERT_PARANOID(y < r.Height); | ||||||
|  |  | ||||||
|  |         // Write each slice | ||||||
|  |         unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r.X + ((r.Y + y) * w)]; | ||||||
|  |         for (unsigned int x = 0; x < pad_left; x++) | ||||||
|             *(write_ptr++) = 0; |             *(write_ptr++) = 0; | ||||||
|             for (unsigned short x = 0; x < (r.Width - 2U); x++) |         for (unsigned int x = 0; x < line_width; x++) | ||||||
|                 *(write_ptr++) = 0xFF; |             *(write_ptr++) = 0xFF; | ||||||
|  |         for (unsigned int x = 0; x < pad_right; x++) | ||||||
|             *(write_ptr++) = 0; |             *(write_ptr++) = 0; | ||||||
|         } |  | ||||||
|  |         // Calculate UVs for this line | ||||||
|  |         ImFontAtlasCustomRect line_rect = r; | ||||||
|  |         line_rect.X += (unsigned short)(pad_left - 1); | ||||||
|  |         line_rect.Width = (unsigned short)(line_width + 2); | ||||||
|  |         line_rect.Y += (unsigned short)y; | ||||||
|  |         line_rect.Height = 1; | ||||||
|  |  | ||||||
|         ImVec2 uv0, uv1; |         ImVec2 uv0, uv1; | ||||||
|         atlas->CalcCustomRectUV(&r, &uv0, &uv1); |         atlas->CalcCustomRectUV(&line_rect, &uv0, &uv1); | ||||||
|         float halfV = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the texture as we want a horizontal slice (with some padding either side to avoid sampling artifacts) |         float halfV = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts | ||||||
|         atlas->TexUvAALines.push_back(ImVec4(uv0.x, halfV, uv1.x, halfV)); |         atlas->TexUvAALines.push_back(ImVec4(uv0.x, halfV, uv1.x, halfV)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -529,7 +529,7 @@ struct IMGUI_API ImChunkStream | |||||||
| #define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER             1 | #define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER             1 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // The maximum line width to build anti-aliased textures for | // The maximum line width to build anti-aliased textures for (note that this needs to be one greater than the maximum line width you want to be able to draw using the textured path) | ||||||
| #define IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX                      65 | #define IM_DRAWLIST_TEX_AA_LINES_WIDTH_MAX                      65 | ||||||
|  |  | ||||||
| // Data shared between all ImDrawList instances | // Data shared between all ImDrawList instances | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user