mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-10-31 21:21:06 +01:00 
			
		
		
		
	Add version of PathArcTo() and PathArcToFast() with adaptive rendering quality. (#3491)
This commit is contained in:
		| @@ -75,9 +75,13 @@ Other Changes: | ||||
|   This can currently only ever be set by the Freetype renderer. | ||||
| - imgui_freetype: Added ImGuiFreeTypeBuilderFlags_Bitmap flag to request Freetype loading bitmap data. | ||||
|   This may have an effect on size and must be called with correct size values. (#3879) [@metarutaiga] | ||||
| - ImDrawList: PathArcTo() now supports "int num_segments = 0" (new default) and adaptively tesselate. | ||||
|   The adapative tesselation uses look up tables, tends to be faster than old PathArcTo() while maintaining | ||||
|   quality for large arcs (tesselation quality derived from "style.CircleTessellationMaxError") (#3491) [@thedmd] | ||||
| - ImDrawList: PathArcToFast() also adaptively tesselate efficiently. This means that large rounded corners | ||||
|   in e.g. hi-dpi settings will generally look better. (#3491) [@thedmd] | ||||
| - ImDrawList: AddCircle, AddCircleFilled(): Tweaked default segment count calculation to honor MaxError | ||||
|   with more accuracy. Made default segment count always even for better looking result. (#3808) [@thedmd] | ||||
| - ImDrawList: AddCircle, AddCircleFilled(): New default for style. | ||||
| - Backends: Android: Added native Android backend. (#3446) [@duddel] | ||||
| - Backends: Win32: Added ImGui_ImplWin32_EnableAlphaCompositing() to facilitate experimenting with | ||||
|   alpha compositing and transparent windows. (#2766, #3447 etc.). | ||||
|   | ||||
							
								
								
									
										4
									
								
								imgui.h
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								imgui.h
									
									
									
									
									
								
							| @@ -2436,7 +2436,7 @@ struct ImDrawList | ||||
|     inline    void  PathLineToMergeDuplicate(const ImVec2& pos)                 { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); } | ||||
|     inline    void  PathFillConvex(ImU32 col)                                   { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }  // Note: Anti-aliased filling requires points to be in clockwise order. | ||||
|     inline    void  PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; } | ||||
|     IMGUI_API void  PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 10); | ||||
|     IMGUI_API void  PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0); | ||||
|     IMGUI_API void  PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12);                // Use precomputed angles for a 12 steps circle | ||||
|     IMGUI_API void  PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) | ||||
|     IMGUI_API void  PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0);               // Quadratic Bezier (3 control points) | ||||
| @@ -2482,6 +2482,8 @@ struct ImDrawList | ||||
|     IMGUI_API void  _OnChangedTextureID(); | ||||
|     IMGUI_API void  _OnChangedVtxOffset(); | ||||
|     IMGUI_API int   _CalcCircleAutoSegmentCount(float radius) const; | ||||
|     IMGUI_API void  _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step); | ||||
|     IMGUI_API void  _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments); | ||||
| }; | ||||
|  | ||||
| // All draw data to render a Dear ImGui frame | ||||
|   | ||||
							
								
								
									
										144
									
								
								imgui_draw.cpp
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								imgui_draw.cpp
									
									
									
									
									
								
							| @@ -374,18 +374,22 @@ ImDrawListSharedData::ImDrawListSharedData() | ||||
|         const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx); | ||||
|         ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a)); | ||||
|     } | ||||
|     ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); | ||||
| } | ||||
|  | ||||
| void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) | ||||
| { | ||||
|     if (CircleSegmentMaxError == max_error) | ||||
|         return; | ||||
|  | ||||
|     IM_ASSERT(max_error > 0.0f); | ||||
|     CircleSegmentMaxError = max_error; | ||||
|     for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++) | ||||
|     { | ||||
|         const float radius = (float)i; | ||||
|         CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : 0); | ||||
|     } | ||||
|     ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); | ||||
| } | ||||
|  | ||||
| // Initialize before use in a new frame. We always have a command ready in the buffer. | ||||
| @@ -1026,32 +1030,86 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun | ||||
|     } | ||||
| } | ||||
|  | ||||
| // 0: East, 3: South, 6: West, 9: North, 12: East | ||||
| void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12) | ||||
| void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step) | ||||
| { | ||||
|     if (radius <= 0.0f) | ||||
|     { | ||||
|         _Path.push_back(center); | ||||
|         return; | ||||
|     } | ||||
|     IM_ASSERT(a_min_of_12 <= a_max_of_12); | ||||
|     IM_ASSERT(a_min_sample <= a_max_sample); | ||||
|  | ||||
|     // For legacy reason the PathArcToFast() always takes angles where 2*PI is represented by 12, | ||||
|     // but it is possible to set IM_DRAWLIST_ARCFAST_TESSELATION_MULTIPLIER to a higher value. This should compile to a no-op otherwise. | ||||
| #if IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER != 1 | ||||
|     a_min_of_12 *= IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER; | ||||
|     a_max_of_12 *= IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER; | ||||
| #endif | ||||
|     // Calculate arc auto segment step size | ||||
|     if (a_step <= 0) | ||||
|         a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(radius); | ||||
|  | ||||
|     _Path.reserve(_Path.Size + (a_max_of_12 - a_min_of_12 + 1)); | ||||
|     for (int a = a_min_of_12; a <= a_max_of_12; a++) | ||||
|     // Make sure we never do steps larger than one quarter of the circle | ||||
|     a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4); | ||||
|  | ||||
|     // Normalize a_min_sample to always start lie in [0..IM_DRAWLIST_ARCFAST_SAMPLE_MAX] range. | ||||
|     if (a_min_sample < 0) | ||||
|     { | ||||
|         const ImVec2& c = _Data->ArcFastVtx[a % IM_ARRAYSIZE(_Data->ArcFastVtx)]; | ||||
|         _Path.push_back(ImVec2(center.x + c.x * radius, center.y + c.y * radius)); | ||||
|         int normalized_sample = a_min_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; | ||||
|         if (normalized_sample < 0) | ||||
|             normalized_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; | ||||
|         a_max_sample += (normalized_sample - a_min_sample); | ||||
|         a_min_sample = normalized_sample; | ||||
|     } | ||||
|  | ||||
|     const int sample_range = a_max_sample - a_min_sample; | ||||
|     const int a_next_step = a_step; | ||||
|  | ||||
|     int samples = sample_range + 1; | ||||
|     bool extra_max_sample = false; | ||||
|     if (a_step > 1) | ||||
|     { | ||||
|         samples            = sample_range / a_step + 1; | ||||
|         const int overstep = sample_range % a_step; | ||||
|  | ||||
|         if (overstep > 0) | ||||
|         { | ||||
|             extra_max_sample = true; | ||||
|             samples++; | ||||
|  | ||||
|             // When we have overstep to avoid awkwardly looking one long line and one tiny one at the end, | ||||
|             // distribute first step range evenly between them by reducing first step size. | ||||
|             if (sample_range > 0) | ||||
|                 a_step -= (a_step - overstep) / 2; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _Path.resize(_Path.Size + samples); | ||||
|     ImVec2* out_ptr = _Path.Data + (_Path.Size - samples); | ||||
|  | ||||
|     int sample_index = a_min_sample; | ||||
|     for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step) | ||||
|     { | ||||
|         // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more | ||||
|         if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) | ||||
|             sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX; | ||||
|  | ||||
|         const ImVec2 s = _Data->ArcFastVtx[sample_index]; | ||||
|         out_ptr->x = center.x + s.x * radius; | ||||
|         out_ptr->y = center.y + s.y * radius; | ||||
|         out_ptr++; | ||||
|     } | ||||
|  | ||||
|     if (extra_max_sample) | ||||
|     { | ||||
|         int normalized_max_sample = a_max_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; | ||||
|         if (normalized_max_sample < 0) | ||||
|             normalized_max_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; | ||||
|  | ||||
|         const ImVec2 s = _Data->ArcFastVtx[normalized_max_sample]; | ||||
|         out_ptr->x = center.x + s.x * radius; | ||||
|         out_ptr->y = center.y + s.y * radius; | ||||
|         out_ptr++; | ||||
|     } | ||||
|  | ||||
|     IM_ASSERT_PARANOID(_Path.Data + _Path.Size == out_ptr); | ||||
| } | ||||
|  | ||||
| void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) | ||||
| void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) | ||||
| { | ||||
|     if (radius <= 0.0f) | ||||
|     { | ||||
| @@ -1070,6 +1128,64 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa | ||||
|     } | ||||
| } | ||||
|  | ||||
| // 0: East, 3: South, 6: West, 9: North, 12: East | ||||
| void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12) | ||||
| { | ||||
|     if (radius <= 0.0f) | ||||
|     { | ||||
|         _Path.push_back(center); | ||||
|         return; | ||||
|     } | ||||
|     IM_ASSERT(a_min_of_12 <= a_max_of_12); | ||||
|     _PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0); | ||||
| } | ||||
|  | ||||
| void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) | ||||
| { | ||||
|     if (radius <= 0.0f) | ||||
|     { | ||||
|         _Path.push_back(center); | ||||
|         return; | ||||
|     } | ||||
|     IM_ASSERT(a_min <= a_max); | ||||
|  | ||||
|     if (num_segments > 0) | ||||
|     { | ||||
|         _PathArcToN(center, radius, a_min, a_max, num_segments); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Automatic segment count | ||||
|     if (radius <= _Data->ArcFastRadiusCutoff) | ||||
|     { | ||||
|         // We are going to use precomputed values for mid samples. | ||||
|         // Determine first and last sample in lookup table that belong to the arc. | ||||
|         const int a_min_sample = (int)ImCeil(IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f)); | ||||
|         const int a_max_sample = (int)(      IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f)); | ||||
|         const int a_mid_samples = ImMax(a_max_sample - a_min_sample, 0); | ||||
|  | ||||
|         const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; | ||||
|         const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; | ||||
|         const bool a_emit_start = (a_min_segment_angle - a_min) > 0.0f; | ||||
|         const bool a_emit_end = (a_max - a_max_segment_angle) > 0.0f; | ||||
|  | ||||
|         _Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0))); | ||||
|         if (a_emit_start) | ||||
|             _Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius)); | ||||
|         if (a_max_sample >= a_min_sample) | ||||
|             _PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0); | ||||
|         if (a_emit_end) | ||||
|             _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius)); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         const float arc_length = a_max - a_min; | ||||
|         const int circle_segment_count = _CalcCircleAutoSegmentCount(radius); | ||||
|         const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length)); | ||||
|         _PathArcToN(center, radius, a_min, a_max, arc_segment_count); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t) | ||||
| { | ||||
|     float u = 1.0f - t; | ||||
|   | ||||
| @@ -636,10 +636,15 @@ struct IMGUI_API ImChunkStream | ||||
| #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX                     512 | ||||
| #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR)    ImClamp(IM_ROUNDUP_TO_EVEN((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD)))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX) | ||||
|  | ||||
| // ImDrawList: You may set this to higher values (e.g. 2 or 3) to increase tessellation of fast rounded corners path. | ||||
| #ifndef IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER | ||||
| #define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER             1 | ||||
| // Raw equation from IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC rewritten for 'r' and 'error'. | ||||
| #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR)    ((_MAXERROR) / (1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI)))) | ||||
| #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD)     ((1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))) / (_RAD)) | ||||
|  | ||||
| // ImDrawList: Lookup table size for adaptive arc drawing, cover full circle. | ||||
| #ifndef IM_DRAWLIST_ARCFAST_TABLE_SIZE | ||||
| #define IM_DRAWLIST_ARCFAST_TABLE_SIZE                          48 // Number of samples in lookup table. | ||||
| #endif | ||||
| #define IM_DRAWLIST_ARCFAST_SAMPLE_MAX                          IM_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle. | ||||
|  | ||||
| // Data shared between all ImDrawList instances | ||||
| // You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure. | ||||
| @@ -654,7 +659,8 @@ struct IMGUI_API ImDrawListSharedData | ||||
|     ImDrawListFlags InitialFlags;               // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) | ||||
|  | ||||
|     // [Internal] Lookup tables | ||||
|     ImVec2          ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER];  // FIXME: Bake rounded corners fill/borders in atlas | ||||
|     ImVec2          ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle. | ||||
|     float           ArcFastRadiusCutoff;                        // Cutoff radius after which arc drawing will fallback to slower PathArcTo() | ||||
|     ImU8            CircleSegmentCounts[64];    // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead) | ||||
|     const ImVec4*   TexUvLines;                 // UV of anti-aliased lines in the atlas | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user