mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-22 11:57:00 +00:00
Shadows: Added experimental texture-based shadows (stripped of dynamic tex config and back-end code)
(merged 10 commits, removing dynamic tex config, moved tex config to internal structs, removed back-end changes) Shadows: Added IMGUI_HAS_SHADOWS
This commit is contained in:
parent
c71a50deb5
commit
b306474a2f
14
imgui.cpp
14
imgui.cpp
@ -1072,6 +1072,9 @@ ImGuiStyle::ImGuiStyle()
|
||||
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.
|
||||
CircleTessellationMaxError = 0.30f; // 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.
|
||||
WindowShadowSize = 100.0f; // Size (in pixels) of window shadows.
|
||||
WindowShadowOffsetDist = 0.0f; // Offset distance (in pixels) of window shadows from casting window.
|
||||
WindowShadowOffsetAngle = IM_PI * 0.25f; // Offset angle of window shadows from casting window (0.0f = left, 0.5f*PI = bottom, 1.0f*PI = right, 1.5f*PI = top).
|
||||
|
||||
// Default theme
|
||||
ImGui::StyleColorsDark(this);
|
||||
@ -2941,6 +2944,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
|
||||
case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
|
||||
case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
|
||||
case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
|
||||
case ImGuiCol_WindowShadow: return "WindowShadow";
|
||||
}
|
||||
IM_ASSERT(0);
|
||||
return "Unknown";
|
||||
@ -5759,6 +5763,14 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
|
||||
window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
|
||||
}
|
||||
|
||||
// Draw window shadow
|
||||
if (style.WindowShadowSize > 0.0f && (!(flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_Popup)))
|
||||
{
|
||||
float shadow_size = style.WindowShadowSize;
|
||||
ImVec2 shadow_offset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist;
|
||||
window->DrawList->AddShadowRect(window->Pos, window->Pos + window->Size, shadow_size, shadow_offset, GetColorU32(ImGuiCol_WindowShadow), window_rounding);
|
||||
}
|
||||
|
||||
// Title bar
|
||||
if (!(flags & ImGuiWindowFlags_NoTitleBar))
|
||||
{
|
||||
@ -6813,6 +6825,8 @@ void ImGui::SetCurrentFont(ImFont* font)
|
||||
g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
|
||||
g.DrawListSharedData.Font = g.Font;
|
||||
g.DrawListSharedData.FontSize = g.FontSize;
|
||||
g.DrawListSharedData.ShadowRectId = atlas->ShadowRectId;
|
||||
g.DrawListSharedData.ShadowRectUvs = &atlas->ShadowRectUvs[0];
|
||||
}
|
||||
|
||||
void ImGui::PushFont(ImFont* font)
|
||||
|
29
imgui.h
29
imgui.h
@ -1579,6 +1579,7 @@ enum ImGuiCol_
|
||||
ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB
|
||||
ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active
|
||||
ImGuiCol_ModalWindowDimBg, // Darken/colorize entire screen behind a modal window, when one is active
|
||||
ImGuiCol_WindowShadow, // Window shadows
|
||||
ImGuiCol_COUNT
|
||||
};
|
||||
|
||||
@ -1877,6 +1878,9 @@ struct ImGuiStyle
|
||||
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 CircleTessellationMaxError; // 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 WindowShadowSize; // Size (in pixels) of window shadows. Set this to zero to disable shadows.
|
||||
float WindowShadowOffsetDist; // Offset distance (in pixels) of window shadows from casting window.
|
||||
float WindowShadowOffsetAngle; // Offset angle of window shadows from casting window (0.0f = left, 0.5f*PI = bottom, 1.0f*PI = right, 1.5f*PI = top).
|
||||
ImVec4 Colors[ImGuiCol_COUNT];
|
||||
|
||||
IMGUI_API ImGuiStyle();
|
||||
@ -2562,6 +2566,12 @@ struct ImDrawList
|
||||
IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE);
|
||||
IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0);
|
||||
|
||||
// Shadows primitives
|
||||
// [BETA] API
|
||||
// FIXME-SHADOWS: high-level api to draw shadow without a hole?
|
||||
#define IMGUI_HAS_SHADOWS 1
|
||||
IMGUI_API void AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All); // Add a shadow for a rectangular object, with min-max giving the object extents, and offset giving an offset to shift the shadow by. Rounding parameters refer to the object itself, not the shadow.
|
||||
|
||||
// Stateful path API, add points then finish with PathFillConvex() or PathStroke()
|
||||
inline void PathClear() { _Path.Size = 0; }
|
||||
inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); }
|
||||
@ -2644,6 +2654,20 @@ struct ImDrawData
|
||||
// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// [Internal] Shadow texture baking config
|
||||
struct ImFontAtlasShadowTexConfig
|
||||
{
|
||||
int TexCornerSize; // Size of the corner areas.
|
||||
int TexEdgeSize; // Size of the edge areas (and by extension the center). Changing this is normally unnecessary.
|
||||
float TexFalloffPower; // The power factor for the shadow falloff curve.
|
||||
float TexDistanceFieldOffset; // How much to offset the distance field by (allows over/under-shadowing, potentially useful for accommodating rounded corners on the "casting" shape).
|
||||
bool TexBlur; // Do we want to Gaussian blur the shadow texture?
|
||||
|
||||
IMGUI_API ImFontAtlasShadowTexConfig();
|
||||
int GetPadding() const { return 2; } // Number of pixels of padding to add to avoid sampling artifacts at the edges.
|
||||
int CalcTexSize() const { return TexCornerSize + TexEdgeSize + GetPadding(); } // The size of the texture area required for the actual 2x2 shadow texture (after the redundant corners have been removed). Padding is required here to avoid sampling artifacts at the edge adjoining the removed corners.
|
||||
};
|
||||
|
||||
struct ImFontConfig
|
||||
{
|
||||
void* FontData; // // TTF/OTF data
|
||||
@ -2832,6 +2856,11 @@ struct ImFontAtlas
|
||||
int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors
|
||||
int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines
|
||||
|
||||
// [Internal] Shadow data
|
||||
int ShadowRectId; // ID of rect for shadow texture
|
||||
ImVec4 ShadowRectUvs[9]; // UV coordinates for shadow texture.
|
||||
ImFontAtlasShadowTexConfig ShadowTexConfig; // Shadow texture baking config
|
||||
|
||||
// [Obsolete]
|
||||
//typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
|
||||
//typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+
|
||||
|
@ -6328,6 +6328,22 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Shadows"))
|
||||
{
|
||||
ImGui::Text("Window shadows:");
|
||||
ImGui::ColorEdit4("Color", (float*)&style.Colors[ImGuiCol_WindowShadow], ImGuiColorEditFlags_AlphaBar);
|
||||
ImGui::SameLine();
|
||||
HelpMarker("Same as 'WindowShadow' in Colors tab.");
|
||||
|
||||
ImGui::SliderFloat("Size", &style.WindowShadowSize, 0.0f, 128.0f, "%.1f");
|
||||
ImGui::SameLine();
|
||||
HelpMarker("Set shadow size to zero to disable shadows.");
|
||||
ImGui::SliderFloat("Offset distance", &style.WindowShadowOffsetDist, 0.0f, 64.0f, "%.0f");
|
||||
ImGui::SliderAngle("Offset angle", &style.WindowShadowOffsetAngle);
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
|
627
imgui_draw.cpp
627
imgui_draw.cpp
@ -8,9 +8,11 @@ Index of this file:
|
||||
// [SECTION] STB libraries implementation
|
||||
// [SECTION] Style functions
|
||||
// [SECTION] ImDrawList
|
||||
// [SECTION] ImDrawList Shadow Primitives
|
||||
// [SECTION] ImDrawListSplitter
|
||||
// [SECTION] ImDrawData
|
||||
// [SECTION] Helpers ShadeVertsXXX functions
|
||||
// [SECTION] ImFontAtlasShadowTexConfig
|
||||
// [SECTION] ImFontConfig
|
||||
// [SECTION] ImFontAtlas
|
||||
// [SECTION] ImFontAtlas glyph ranges helpers
|
||||
@ -245,6 +247,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
colors[ImGuiCol_WindowShadow] = ImVec4(0.08f, 0.08f, 0.08f, 0.35f);
|
||||
}
|
||||
|
||||
void ImGui::StyleColorsClassic(ImGuiStyle* dst)
|
||||
@ -305,6 +308,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
|
||||
colors[ImGuiCol_WindowShadow] = ImVec4(0.08f, 0.08f, 0.08f, 0.35f);
|
||||
}
|
||||
|
||||
// Those light colors are better suited with a thicker font than the default one + FrameBorder
|
||||
@ -366,6 +370,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);
|
||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
|
||||
colors[ImGuiCol_WindowShadow] = ImVec4(0.08f, 0.08f, 0.08f, 0.35f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -381,6 +386,7 @@ ImDrawListSharedData::ImDrawListSharedData()
|
||||
ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));
|
||||
}
|
||||
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
|
||||
ShadowRectId = -1;
|
||||
}
|
||||
|
||||
void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
|
||||
@ -1666,6 +1672,458 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImDrawList Shadow Primitives
|
||||
//-----------------------------------------------------------------------------
|
||||
// - AddSubtractedRect() [Internal]
|
||||
// - ClipPolygonShape() [Internal]
|
||||
// - AddSubtractedRect() [Internal]
|
||||
// - AddRectShadow()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Adds a rectangle (A) with another rectangle (B) subtracted from it (i.e. the portion of A covered by B is not drawn). Does not handle rounded corners (use the version that takes a convex polygon for that).
|
||||
static void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const ImVec2& a_max, const ImVec2& a_min_uv, const ImVec2& a_max_uv, ImVec2 b_min, ImVec2 b_max, ImU32 col)
|
||||
{
|
||||
// Early out without drawing anything if A is zero-size
|
||||
if ((a_min.x >= a_max.x) || (a_min.y >= a_max.y))
|
||||
return;
|
||||
|
||||
// Early out without drawing anything if B covers A entirely
|
||||
if ((a_min.x >= b_min.x) && (a_max.x <= b_max.x) && (a_min.y >= b_min.y) && (a_max.y <= b_max.y))
|
||||
return;
|
||||
|
||||
// First clip the extents of B to A
|
||||
b_min = ImMax(b_min, a_min);
|
||||
b_max = ImMin(b_max, a_max);
|
||||
|
||||
if ((b_min.x >= b_max.x) || (b_min.y >= b_max.y))
|
||||
{
|
||||
// B is entirely outside A, so just draw A as-is
|
||||
draw_list->PrimReserve(6, 4);
|
||||
draw_list->PrimRectUV(a_min, a_max, a_min_uv, a_max_uv, col);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we need to emit (up to) four quads to cover the visible area...
|
||||
//
|
||||
// Our layout looks like this (numbers are vertex indices, letters are quads):
|
||||
//
|
||||
// 0---8------9-----1
|
||||
// | | B | |
|
||||
// + 4------5 +
|
||||
// | A |xxxxxx| C |
|
||||
// | |xxxxxx| |
|
||||
// + 7------6 +
|
||||
// | | D | |
|
||||
// 3---11-----10----2
|
||||
|
||||
const int max_verts = 12;
|
||||
const int max_indices = 6 * 4; // At most four quads
|
||||
draw_list->PrimReserve(max_indices, max_verts);
|
||||
|
||||
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
|
||||
ImDrawVert* vtx_write = draw_list->_VtxWritePtr;
|
||||
ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx;
|
||||
|
||||
// Write vertices
|
||||
vtx_write[0].pos = ImVec2(a_min.x, a_min.y); vtx_write[0].uv = ImVec2(a_min_uv.x, a_min_uv.y); vtx_write[0].col = col;
|
||||
vtx_write[1].pos = ImVec2(a_max.x, a_min.y); vtx_write[1].uv = ImVec2(a_max_uv.x, a_min_uv.y); vtx_write[1].col = col;
|
||||
vtx_write[2].pos = ImVec2(a_max.x, a_max.y); vtx_write[2].uv = ImVec2(a_max_uv.x, a_max_uv.y); vtx_write[2].col = col;
|
||||
vtx_write[3].pos = ImVec2(a_min.x, a_max.y); vtx_write[3].uv = ImVec2(a_min_uv.x, a_max_uv.y); vtx_write[3].col = col;
|
||||
|
||||
const ImVec2 pos_to_uv_scale = (a_max_uv - a_min_uv) / (a_max - a_min); // Guaranteed never to be a /0 because we check for zero-size A above
|
||||
const ImVec2 pos_to_uv_offset = (a_min_uv / pos_to_uv_scale) - a_min;
|
||||
|
||||
// Helper that generates an interpolated UV based on position
|
||||
#define LERP_UV(x_pos, y_pos) (ImVec2(((x_pos) + pos_to_uv_offset.x) * pos_to_uv_scale.x, ((y_pos) + pos_to_uv_offset.y) * pos_to_uv_scale.y))
|
||||
vtx_write[4].pos = ImVec2(b_min.x, b_min.y); vtx_write[4].uv = LERP_UV(b_min.x, b_min.y); vtx_write[4].col = col;
|
||||
vtx_write[5].pos = ImVec2(b_max.x, b_min.y); vtx_write[5].uv = LERP_UV(b_max.x, b_min.y); vtx_write[5].col = col;
|
||||
vtx_write[6].pos = ImVec2(b_max.x, b_max.y); vtx_write[6].uv = LERP_UV(b_max.x, b_max.y); vtx_write[6].col = col;
|
||||
vtx_write[7].pos = ImVec2(b_min.x, b_max.y); vtx_write[7].uv = LERP_UV(b_min.x, b_max.y); vtx_write[7].col = col;
|
||||
vtx_write[8].pos = ImVec2(b_min.x, a_min.y); vtx_write[8].uv = LERP_UV(b_min.x, a_min.y); vtx_write[8].col = col;
|
||||
vtx_write[9].pos = ImVec2(b_max.x, a_min.y); vtx_write[9].uv = LERP_UV(b_max.x, a_min.y); vtx_write[9].col = col;
|
||||
vtx_write[10].pos = ImVec2(b_max.x, a_max.y); vtx_write[10].uv = LERP_UV(b_max.x, a_max.y); vtx_write[10].col = col;
|
||||
vtx_write[11].pos = ImVec2(b_min.x, a_max.y); vtx_write[11].uv = LERP_UV(b_min.x, a_max.y); vtx_write[11].col = col;
|
||||
#undef LERP_UV
|
||||
draw_list->_VtxWritePtr += 12;
|
||||
draw_list->_VtxCurrentIdx += 12;
|
||||
|
||||
// Write indices for each quad (if it is visible)
|
||||
if (b_min.x > a_min.x) // A
|
||||
{
|
||||
idx_write[0] = (ImDrawIdx)(idx + 0); idx_write[1] = (ImDrawIdx)(idx + 8); idx_write[2] = (ImDrawIdx)(idx + 11);
|
||||
idx_write[3] = (ImDrawIdx)(idx + 0); idx_write[4] = (ImDrawIdx)(idx + 11); idx_write[5] = (ImDrawIdx)(idx + 3);
|
||||
idx_write += 6;
|
||||
}
|
||||
if (b_min.y > a_min.y) // B
|
||||
{
|
||||
idx_write[0] = (ImDrawIdx)(idx + 8); idx_write[1] = (ImDrawIdx)(idx + 9); idx_write[2] = (ImDrawIdx)(idx + 5);
|
||||
idx_write[3] = (ImDrawIdx)(idx + 8); idx_write[4] = (ImDrawIdx)(idx + 5); idx_write[5] = (ImDrawIdx)(idx + 4);
|
||||
idx_write += 6;
|
||||
}
|
||||
if (a_max.x > b_max.x) // C
|
||||
{
|
||||
idx_write[0] = (ImDrawIdx)(idx + 9); idx_write[1] = (ImDrawIdx)(idx + 1); idx_write[2] = (ImDrawIdx)(idx + 2);
|
||||
idx_write[3] = (ImDrawIdx)(idx + 9); idx_write[4] = (ImDrawIdx)(idx + 2); idx_write[5] = (ImDrawIdx)(idx + 10);
|
||||
idx_write += 6;
|
||||
}
|
||||
if (a_max.y > b_max.y) // D
|
||||
{
|
||||
idx_write[0] = (ImDrawIdx)(idx + 7); idx_write[1] = (ImDrawIdx)(idx + 6); idx_write[2] = (ImDrawIdx)(idx + 10);
|
||||
idx_write[3] = (ImDrawIdx)(idx + 7); idx_write[4] = (ImDrawIdx)(idx + 10); idx_write[5] = (ImDrawIdx)(idx + 11);
|
||||
idx_write += 6;
|
||||
}
|
||||
|
||||
const int used_indices = (int)(idx_write - draw_list->_IdxWritePtr);
|
||||
draw_list->_IdxWritePtr = idx_write;
|
||||
draw_list->PrimUnreserve(max_indices - used_indices, 0);
|
||||
}
|
||||
|
||||
// Clip a polygonal shape to a rectangle, writing the results into dest_points. The number of points emitted is returned (may be zero if the polygon was entirely outside the rectangle, or the source polygon was not valid). dest_points may still be written to even if zero was returned.
|
||||
// allocated_dest_points should contain the number of allocated points in dest_points - in general this should be the number of source points + 4 to accommodate the worst case. If this is exceeded data will be truncated and -1 returned. Stack space work area is allocated based on this value so it shouldn't be too large.
|
||||
static int ClipPolygonShape(ImVec2* src_points, int num_src_points, ImVec2* dest_points, int allocated_dest_points, ImVec2 clip_rect_min, ImVec2 clip_rect_max)
|
||||
{
|
||||
// Early-out with an empty result if clipping region is zero-sized
|
||||
if ((clip_rect_max.x <= clip_rect_min.x) || (clip_rect_max.y <= clip_rect_min.y))
|
||||
return 0;
|
||||
|
||||
// Early-out if there is no source geometry
|
||||
if (num_src_points < 3)
|
||||
return 0;
|
||||
|
||||
// The four clip planes here are indexed as:
|
||||
// 0 = X-, 1 = X+, 2 = Y-, 3 = Y+
|
||||
ImU8* outflags[2]; // Double-buffered flags for each vertex indicating which of the four clip planes it is outside of
|
||||
outflags[0] = (ImU8*)alloca(2 * allocated_dest_points * sizeof(ImU8));
|
||||
outflags[1] = outflags[0] + allocated_dest_points;
|
||||
|
||||
// Calculate initial outflags
|
||||
ImU8 outflags_anded = 0xFF;
|
||||
ImU8 outflags_ored = 0;
|
||||
for (int point_idx = 0; point_idx < num_src_points; point_idx++)
|
||||
{
|
||||
const ImVec2 pos = src_points[point_idx];
|
||||
const ImU8 point_outflags = (pos.x < clip_rect_min.x ? 1 : 0) | (pos.x > clip_rect_max.x ? 2 : 0) | (pos.y < clip_rect_min.y ? 4 : 0) | (pos.y > clip_rect_max.y ? 8 : 0);
|
||||
outflags[0][point_idx] = point_outflags; // Writing to buffer 0
|
||||
outflags_anded &= point_outflags;
|
||||
outflags_ored |= point_outflags;
|
||||
}
|
||||
if (outflags_anded != 0) // Entirely clipped by any one plane, so nothing remains
|
||||
return 0;
|
||||
|
||||
if (outflags_ored == 0) // Entirely within bounds, so trivial accept
|
||||
{
|
||||
if (allocated_dest_points < num_src_points)
|
||||
return -1; // Not sure what the caller was thinking if this happens, but we should handle it gracefully
|
||||
|
||||
memcpy(dest_points, src_points, num_src_points * sizeof(ImVec2));
|
||||
return num_src_points;
|
||||
}
|
||||
|
||||
// Shape needs clipping
|
||||
ImVec2* clip_buf[2]; // Double-buffered work area
|
||||
clip_buf[0] = (ImVec2*)alloca(2 * allocated_dest_points * sizeof(ImVec2)); //-V630
|
||||
clip_buf[1] = clip_buf[0] + allocated_dest_points;
|
||||
|
||||
memcpy(clip_buf[0], src_points, num_src_points * sizeof(ImVec2));
|
||||
int clip_buf_size = num_src_points; // Number of vertices currently in the clip buffer
|
||||
|
||||
int read_buffer_idx = 0; // The index of the clip buffer/out-flags we are reading (0 or 1)
|
||||
|
||||
for (int clip_plane = 0; clip_plane < 4; clip_plane++) // 0 = X-, 1 = X+, 2 = Y-, 3 = Y+
|
||||
{
|
||||
const int clip_plane_bit = 1 << clip_plane; // Bit mask for our current plane in out-flags
|
||||
if ((outflags_ored & clip_plane_bit) == 0)
|
||||
continue; // All vertices are inside this plane, so no need to clip
|
||||
|
||||
ImVec2* read_vert = &clip_buf[read_buffer_idx][0]; // Clip buffer vertex we are currently reading
|
||||
ImVec2* write_vert = &clip_buf[1 - read_buffer_idx][0]; // Clip buffer vertex we are currently writing
|
||||
ImVec2* write_vert_end = write_vert + allocated_dest_points; // End of the write buffer
|
||||
ImU8* read_outflags = &outflags[read_buffer_idx][0]; // Out-flag we are currently reading
|
||||
ImU8* write_outflags = &outflags[1 - read_buffer_idx][0]; // Out-flag we are currently writing
|
||||
|
||||
// Keep track of the last vertex visited, initially the last in the list
|
||||
ImVec2* last_vert = &read_vert[clip_buf_size - 1];
|
||||
ImU8 last_outflags = read_outflags[clip_buf_size - 1];
|
||||
|
||||
for (int vert = 0; vert < clip_buf_size; vert++)
|
||||
{
|
||||
ImU8 current_outflags = *(read_outflags++);
|
||||
bool out = (current_outflags & clip_plane_bit) != 0;
|
||||
if (((current_outflags ^ last_outflags) & clip_plane_bit) == 0) // We haven't crossed the clip plane
|
||||
{
|
||||
if (!out)
|
||||
{
|
||||
// Emit vertex as-is
|
||||
if (write_vert >= write_vert_end)
|
||||
return -1; // Ran out of buffer space, so abort
|
||||
*(write_vert++) = *read_vert;
|
||||
*(write_outflags++) = current_outflags;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Emit a vertex at the intersection point
|
||||
float t = 0.0f;
|
||||
ImVec2 pos0 = *last_vert;
|
||||
ImVec2 pos1 = *read_vert;
|
||||
ImVec2 intersect_pos;
|
||||
switch (clip_plane)
|
||||
{
|
||||
case 0: t = (clip_rect_min.x - pos0.x) / (pos1.x - pos0.x); intersect_pos = ImVec2(clip_rect_min.x, pos0.y + ((pos1.y - pos0.y) * t)); break; // X-
|
||||
case 1: t = (clip_rect_max.x - pos0.x) / (pos1.x - pos0.x); intersect_pos = ImVec2(clip_rect_max.x, pos0.y + ((pos1.y - pos0.y) * t)); break; // X+
|
||||
case 2: t = (clip_rect_min.y - pos0.y) / (pos1.y - pos0.y); intersect_pos = ImVec2(pos0.x + ((pos1.x - pos0.x) * t), clip_rect_min.y); break; // Y-
|
||||
case 3: t = (clip_rect_max.y - pos0.y) / (pos1.y - pos0.y); intersect_pos = ImVec2(pos0.x + ((pos1.x - pos0.x) * t), clip_rect_max.y); break; // Y+
|
||||
}
|
||||
|
||||
if (write_vert >= write_vert_end)
|
||||
return -1; // Ran out of buffer space, so abort
|
||||
|
||||
// Write new out-flags for the vertex we just emitted
|
||||
*(write_vert++) = intersect_pos;
|
||||
*(write_outflags++) = (intersect_pos.x < clip_rect_min.x ? 1 : 0) | (intersect_pos.x > clip_rect_max.x ? 2 : 0) | (intersect_pos.y < clip_rect_min.y ? 4 : 0) | (intersect_pos.y > clip_rect_max.y ? 8 : 0);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
// When coming back in, also emit the actual vertex
|
||||
if (write_vert >= write_vert_end)
|
||||
return -1; // Ran out of buffer space, so abort
|
||||
*(write_vert++) = *read_vert;
|
||||
*(write_outflags++) = current_outflags;
|
||||
}
|
||||
|
||||
last_outflags = current_outflags;
|
||||
}
|
||||
|
||||
last_vert = read_vert;
|
||||
read_vert++; // Advance to next vertex
|
||||
}
|
||||
|
||||
clip_buf_size = (int)(write_vert - &clip_buf[1 - read_buffer_idx][0]); // Update buffer size
|
||||
read_buffer_idx = 1 - read_buffer_idx; // Swap buffers
|
||||
}
|
||||
|
||||
if (clip_buf_size < 3)
|
||||
return 0; // Nothing to return
|
||||
|
||||
// Copy results to output buffer, removing any redundant vertices
|
||||
int num_out_verts = 0;
|
||||
ImVec2 last_vert = clip_buf[read_buffer_idx][clip_buf_size - 1];
|
||||
for (int i = 0; i < clip_buf_size; i++)
|
||||
{
|
||||
ImVec2 vert = clip_buf[read_buffer_idx][i];
|
||||
if (ImLengthSqr(vert - last_vert) > 0.00001f)
|
||||
{
|
||||
dest_points[num_out_verts++] = vert;
|
||||
last_vert = vert;
|
||||
}
|
||||
}
|
||||
|
||||
// Return size (IF this is still a valid shape)
|
||||
return (num_out_verts > 2) ? num_out_verts : 0;
|
||||
}
|
||||
|
||||
// Adds a rectangle (A) with a convex shape (B) subtracted from it (i.e. the portion of A covered by B is not drawn).
|
||||
static void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const ImVec2& a_max, const ImVec2& a_min_uv, const ImVec2& a_max_uv, ImVec2* b_points, int num_b_points, ImU32 col)
|
||||
{
|
||||
// Early out without drawing anything if A is zero-size
|
||||
if ((a_min.x >= a_max.x) || (a_min.y >= a_max.y))
|
||||
return;
|
||||
|
||||
// First clip B to A
|
||||
const int max_clipped_points = num_b_points + 4;
|
||||
ImVec2* clipped_b_points = (ImVec2*)alloca(max_clipped_points * sizeof(ImVec2)); //-V630
|
||||
const int num_clipped_points = ClipPolygonShape(b_points, num_b_points, clipped_b_points, max_clipped_points, a_min, a_max);
|
||||
IM_ASSERT(num_clipped_points >= 0); // -1 would indicate max_clipped_points was too small, which shouldn't happen
|
||||
|
||||
b_points = clipped_b_points;
|
||||
num_b_points = num_clipped_points;
|
||||
|
||||
if (num_clipped_points == 0)
|
||||
{
|
||||
// B is entirely outside A, so just draw A as-is
|
||||
draw_list->PrimReserve(6, 4);
|
||||
draw_list->PrimRectUV(a_min, a_max, a_min_uv, a_max_uv, col);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to generate clipped geometry
|
||||
// To do this we walk the inner polygon and connect each edge to one of the four corners of our rectangle based on the quadrant their normal points at
|
||||
const int max_verts = num_b_points + 4; // Inner points plus the four corners
|
||||
const int max_indices = (num_b_points * 3) + (4 * 3); // Worst case is one triangle per inner edge and then four filler triangles
|
||||
draw_list->PrimReserve(max_indices, max_verts);
|
||||
|
||||
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
|
||||
ImDrawVert* vtx_write = draw_list->_VtxWritePtr;
|
||||
ImDrawIdx inner_idx = (ImDrawIdx)draw_list->_VtxCurrentIdx; // Starting index for inner vertices
|
||||
|
||||
// Write inner vertices
|
||||
const ImVec2 pos_to_uv_scale = (a_max_uv - a_min_uv) / (a_max - a_min); // Guaranteed never to be a /0 because we check for zero-size A above
|
||||
const ImVec2 pos_to_uv_offset = (a_min_uv / pos_to_uv_scale) - a_min;
|
||||
|
||||
// Helper that generates an interpolated UV based on position
|
||||
#define LERP_UV(x_pos, y_pos) (ImVec2(((x_pos) + pos_to_uv_offset.x) * pos_to_uv_scale.x, ((y_pos) + pos_to_uv_offset.y) * pos_to_uv_scale.y))
|
||||
for (int i = 0; i < num_b_points; i++)
|
||||
{
|
||||
vtx_write[i].pos = b_points[i];
|
||||
vtx_write[i].uv = LERP_UV(b_points[i].x, b_points[i].y);
|
||||
vtx_write[i].col = col;
|
||||
}
|
||||
#undef LERP_UV
|
||||
|
||||
vtx_write += num_b_points;
|
||||
|
||||
// Write outer vertices
|
||||
ImDrawIdx outer_idx = (ImDrawIdx)(inner_idx + num_b_points); // Starting index for outer vertices
|
||||
|
||||
ImVec2 outer_verts[4];
|
||||
outer_verts[0] = ImVec2(a_min.x, a_min.y); // X- Y- (quadrant 0, top left)
|
||||
outer_verts[1] = ImVec2(a_max.x, a_min.y); // X+ Y- (quadrant 1, top right)
|
||||
outer_verts[2] = ImVec2(a_max.x, a_max.y); // X+ Y+ (quadrant 2, bottom right)
|
||||
outer_verts[3] = ImVec2(a_min.x, a_max.y); // X- Y+ (quadrant 3, bottom left)
|
||||
|
||||
vtx_write[0].pos = outer_verts[0]; vtx_write[0].uv = ImVec2(a_min_uv.x, a_min_uv.y); vtx_write[0].col = col;
|
||||
vtx_write[1].pos = outer_verts[1]; vtx_write[1].uv = ImVec2(a_max_uv.x, a_min_uv.y); vtx_write[1].col = col;
|
||||
vtx_write[2].pos = outer_verts[2]; vtx_write[2].uv = ImVec2(a_max_uv.x, a_max_uv.y); vtx_write[2].col = col;
|
||||
vtx_write[3].pos = outer_verts[3]; vtx_write[3].uv = ImVec2(a_min_uv.x, a_max_uv.y); vtx_write[3].col = col;
|
||||
|
||||
draw_list->_VtxCurrentIdx += num_b_points + 4;
|
||||
draw_list->_VtxWritePtr += num_b_points + 4;
|
||||
|
||||
// Now walk the inner vertices in order
|
||||
ImVec2 last_inner_vert = b_points[num_b_points - 1];
|
||||
int last_inner_vert_idx = num_b_points - 1;
|
||||
int last_outer_vert_idx = -1;
|
||||
int first_outer_vert_idx = -1;
|
||||
|
||||
// Triangle-area based check for degenerate triangles
|
||||
// Min area (0.1f) is doubled (* 2.0f) because we're calculating (area * 2) here
|
||||
#define IS_DEGENERATE(a, b, c) (ImFabs((((a).x * ((b).y - (c).y)) + ((b).x * ((c).y - (a).y)) + ((c).x * ((a).y - (b).y)))) < (0.1f * 2.0f))
|
||||
|
||||
// Check the winding order of the inner vertices using the sign of the triangle area, and set the outer vertex winding to match
|
||||
int outer_vertex_winding = (((b_points[0].x * (b_points[1].y - b_points[2].y)) + (b_points[1].x * (b_points[2].y - b_points[0].y)) + (b_points[2].x * (b_points[0].y - b_points[1].y))) < 0.0f) ? -1 : 1;
|
||||
for (int inner_vert_idx = 0; inner_vert_idx < num_b_points; inner_vert_idx++)
|
||||
{
|
||||
ImVec2 current_inner_vert = b_points[inner_vert_idx];
|
||||
|
||||
// Calculate normal (not actually normalized, as for our purposes here it doesn't need to be)
|
||||
ImVec2 normal(current_inner_vert.y - last_inner_vert.y, -(current_inner_vert.x - last_inner_vert.x));
|
||||
|
||||
// Calculate the outer vertex index based on the quadrant the normal points at (0=top left, 1=top right, 2=bottom right, 3=bottom left)
|
||||
int outer_vert_idx = (ImFabs(normal.x) > ImFabs(normal.y)) ? ((normal.x >= 0.0f) ? ((normal.y > 0.0f) ? 2 : 1) : ((normal.y > 0.0f) ? 3 : 0)) : ((normal.y >= 0.0f) ? ((normal.x > 0.0f) ? 2 : 3) : ((normal.x > 0.0f) ? 1 : 0));
|
||||
ImVec2 outer_vert = outer_verts[outer_vert_idx];
|
||||
|
||||
// Write the main triangle (connecting the inner edge to the corner)
|
||||
if (!IS_DEGENERATE(last_inner_vert, current_inner_vert, outer_vert))
|
||||
{
|
||||
idx_write[0] = (ImDrawIdx)(inner_idx + last_inner_vert_idx);
|
||||
idx_write[1] = (ImDrawIdx)(inner_idx + inner_vert_idx);
|
||||
idx_write[2] = (ImDrawIdx)(outer_idx + outer_vert_idx);
|
||||
idx_write += 3;
|
||||
}
|
||||
|
||||
// We don't initially know which outer vertex we are going to start from, so set that here when processing the first inner vertex
|
||||
if (first_outer_vert_idx == -1)
|
||||
{
|
||||
first_outer_vert_idx = outer_vert_idx;
|
||||
last_outer_vert_idx = outer_vert_idx;
|
||||
}
|
||||
|
||||
// Now walk the outer edge and write any filler triangles needed (connecting outer edges to the inner vertex)
|
||||
while (outer_vert_idx != last_outer_vert_idx)
|
||||
{
|
||||
int next_outer_vert_idx = (last_outer_vert_idx + outer_vertex_winding) & 3;
|
||||
if (!IS_DEGENERATE(outer_verts[last_outer_vert_idx], outer_verts[next_outer_vert_idx], last_inner_vert))
|
||||
{
|
||||
idx_write[0] = (ImDrawIdx)(outer_idx + last_outer_vert_idx);
|
||||
idx_write[1] = (ImDrawIdx)(outer_idx + next_outer_vert_idx);
|
||||
idx_write[2] = (ImDrawIdx)(inner_idx + last_inner_vert_idx);
|
||||
idx_write += 3;
|
||||
}
|
||||
last_outer_vert_idx = next_outer_vert_idx;
|
||||
}
|
||||
|
||||
last_inner_vert = current_inner_vert;
|
||||
last_inner_vert_idx = inner_vert_idx;
|
||||
}
|
||||
|
||||
// Write remaining filler triangles for any un-traversed outer edges
|
||||
if (first_outer_vert_idx != -1)
|
||||
{
|
||||
while (first_outer_vert_idx != last_outer_vert_idx)
|
||||
{
|
||||
int next_outer_vert_idx = (last_outer_vert_idx + outer_vertex_winding) & 3;
|
||||
if (!IS_DEGENERATE(outer_verts[last_outer_vert_idx], outer_verts[next_outer_vert_idx], last_inner_vert))
|
||||
{
|
||||
idx_write[0] = (ImDrawIdx)(outer_idx + last_outer_vert_idx);
|
||||
idx_write[1] = (ImDrawIdx)(outer_idx + next_outer_vert_idx);
|
||||
idx_write[2] = (ImDrawIdx)(inner_idx + last_inner_vert_idx);
|
||||
idx_write += 3;
|
||||
}
|
||||
last_outer_vert_idx = next_outer_vert_idx;
|
||||
}
|
||||
}
|
||||
#undef IS_DEGENERATE
|
||||
|
||||
int used_indices = (int)(idx_write - draw_list->_IdxWritePtr);
|
||||
draw_list->_IdxWritePtr = idx_write;
|
||||
draw_list->PrimUnreserve(max_indices - used_indices, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ImDrawList::AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners)
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
|
||||
ImVec2* inner_rect_points = NULL; // Points that make up the shape of the inner rectangle (used when it has rounded corners)
|
||||
int num_inner_rect_points = 0;
|
||||
|
||||
// Generate a path describing the inner rectangle and copy it to our buffer
|
||||
const bool is_rounded = (rounding > 0.0f) && (rounding_corners != ImDrawCornerFlags_None); // Do we have rounded corners?
|
||||
if (is_rounded)
|
||||
{
|
||||
_Path.Size = 0;
|
||||
PathRect(p_min, p_max, rounding, rounding_corners);
|
||||
num_inner_rect_points = _Path.Size;
|
||||
inner_rect_points = (ImVec2*)alloca(num_inner_rect_points * sizeof(ImVec2)); //-V630
|
||||
memcpy(inner_rect_points, _Path.Data, num_inner_rect_points * sizeof(ImVec2));
|
||||
_Path.Size = 0;
|
||||
}
|
||||
|
||||
// Draw the relevant chunks of the texture (the texture is split into a 3x3 grid)
|
||||
for (int x = 0; x < 3; x++)
|
||||
{
|
||||
for (int y = 0; y < 3; y++)
|
||||
{
|
||||
const int uv_index = x + (y + y + y); // y*3 formatted so as to ensure the compiler avoids an actual multiply
|
||||
const ImVec4 uvs = _Data->ShadowRectUvs[uv_index];
|
||||
|
||||
ImVec2 draw_min, draw_max;
|
||||
switch (x)
|
||||
{
|
||||
case 0: draw_min.x = p_min.x - shadow_thickness; draw_max.x = p_min.x; break;
|
||||
case 1: draw_min.x = p_min.x; draw_max.x = p_max.x; break;
|
||||
case 2: draw_min.x = p_max.x; draw_max.x = p_max.x + shadow_thickness; break;
|
||||
}
|
||||
switch (y)
|
||||
{
|
||||
case 0: draw_min.y = p_min.y - shadow_thickness; draw_max.y = p_min.y; break;
|
||||
case 1: draw_min.y = p_min.y; draw_max.y = p_max.y; break;
|
||||
case 2: draw_min.y = p_max.y; draw_max.y = p_max.y + shadow_thickness; break;
|
||||
}
|
||||
|
||||
ImVec2 uv_min(uvs.x, uvs.y);
|
||||
ImVec2 uv_max(uvs.z, uvs.w);
|
||||
if (is_rounded)
|
||||
AddSubtractedRect(this, draw_min + offset, draw_max + offset, uv_min, uv_max, inner_rect_points, num_inner_rect_points, col); // Complex path for rounded rectangles
|
||||
else
|
||||
AddSubtractedRect(this, draw_min + offset, draw_max + offset, uv_min, uv_max, p_min, p_max, col); // Simple fast path for non-rounded rectangles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImDrawListSplitter
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -1903,6 +2361,19 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImFontAtlasShadowTexConfig
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ImFontAtlasShadowTexConfig::ImFontAtlasShadowTexConfig()
|
||||
{
|
||||
TexCornerSize = 16;
|
||||
TexEdgeSize = 1;
|
||||
TexFalloffPower = 4.8f;
|
||||
TexDistanceFieldOffset = 3.8f;
|
||||
TexBlur = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImFontConfig
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -1977,6 +2448,7 @@ ImFontAtlas::ImFontAtlas()
|
||||
memset(this, 0, sizeof(*this));
|
||||
TexGlyphPadding = 1;
|
||||
PackIdMouseCursors = PackIdLines = -1;
|
||||
ShadowRectId = -1;
|
||||
}
|
||||
|
||||
ImFontAtlas::~ImFontAtlas()
|
||||
@ -2005,6 +2477,7 @@ void ImFontAtlas::ClearInputData()
|
||||
ConfigData.clear();
|
||||
CustomRects.clear();
|
||||
PackIdMouseCursors = PackIdLines = -1;
|
||||
ShadowRectId = -1;
|
||||
// Important: we leave TexReady untouched
|
||||
}
|
||||
|
||||
@ -2755,6 +3228,157 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
|
||||
}
|
||||
}
|
||||
|
||||
// Register the rectangles we need for the rounded corner images
|
||||
static void ImFontAtlasBuildRegisterShadowCustomRects(ImFontAtlas* atlas)
|
||||
{
|
||||
if (atlas->ShadowRectId >= 0)
|
||||
return;
|
||||
|
||||
// The actual size we want to reserve, including padding
|
||||
const ImFontAtlasShadowTexConfig* shadow_cfg = &atlas->ShadowTexConfig;
|
||||
const unsigned int effective_size = shadow_cfg->CalcTexSize() + shadow_cfg->GetPadding();
|
||||
atlas->ShadowRectId = atlas->AddCustomRectRegular(effective_size, effective_size);
|
||||
}
|
||||
|
||||
// Calculates the signed distance from samplePos to the nearest point on the rectangle defined by rectMin-rectMax
|
||||
static float DistanceFromRectangle(ImVec2 samplePos, ImVec2 rectMin, ImVec2 rectMax)
|
||||
{
|
||||
ImVec2 rect_centre = (rectMin + rectMax) * 0.5f;
|
||||
ImVec2 rect_half_size = (rectMax - rectMin) * 0.5f;
|
||||
|
||||
ImVec2 local_sample_pos = samplePos - rect_centre;
|
||||
|
||||
ImVec2 axis_dist = ImVec2(ImFabs(local_sample_pos.x), ImFabs(local_sample_pos.y)) - rect_half_size;
|
||||
|
||||
float out_dist = ImLength(ImVec2(ImMax(axis_dist.x, 0.0f), ImMax(axis_dist.y, 0.0f)), 0.00001f);
|
||||
float in_dist = ImMin(ImMax(axis_dist.x, axis_dist.y), 0.0f);
|
||||
|
||||
return out_dist + in_dist;
|
||||
}
|
||||
|
||||
// Perform a single Gaussian blur pass with a fixed kernel size and sigma
|
||||
static void GaussianBlurPass(float* src, float* dest, int size, bool horizontal)
|
||||
{
|
||||
// See http://dev.theomader.com/gaussian-kernel-calculator/
|
||||
const float coefficients[] = { 0.0f, 0.0f, 0.000003f, 0.000229f, 0.005977f, 0.060598f, 0.24173f, 0.382925f, 0.24173f, 0.060598f, 0.005977f, 0.000229f, 0.000003f, 0.0f, 0.0f };
|
||||
const int kernel_size = IM_ARRAYSIZE(coefficients);
|
||||
|
||||
int sample_step = horizontal ? 1 : size;
|
||||
|
||||
float* read_ptr = src;
|
||||
float* write_ptr = dest;
|
||||
|
||||
for (int y = 0; y < size; y++)
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
float result = 0;
|
||||
int current_offset = (horizontal ? x : y) - ((kernel_size - 1) >> 1);
|
||||
float* sample_ptr = read_ptr - (((kernel_size - 1) >> 1) * sample_step);
|
||||
for (int j = 0; j < kernel_size; j++)
|
||||
{
|
||||
if ((current_offset >= 0) && (current_offset < size))
|
||||
result += (*sample_ptr) * coefficients[j];
|
||||
current_offset++;
|
||||
sample_ptr += sample_step;
|
||||
}
|
||||
read_ptr++;
|
||||
*(write_ptr++) = result;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform an in-place Gaussian blur of a square array of floats with a fixed kernel size and sigma
|
||||
// Uses a stack allocation for the temporary data so potentially dangerous with large size values
|
||||
static void GaussianBlur(float* data, int size)
|
||||
{
|
||||
// Do two passes, one from data into temp and then the second back to data again
|
||||
float* temp = (float*)alloca(size * size * sizeof(float));
|
||||
GaussianBlurPass(data, temp, size, true);
|
||||
GaussianBlurPass(temp, data, size, false);
|
||||
}
|
||||
|
||||
// Generate the actual pixel data for rounded corners in the atlas
|
||||
static void ImFontAtlasBuildRenderShadowTexData(ImFontAtlas* atlas)
|
||||
{
|
||||
IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);
|
||||
IM_ASSERT(atlas->ShadowRectId >= 0);
|
||||
|
||||
// Because of the blur, we have to generate the full 3x3 texture here, and then we chop that down to just the 2x2 section we need later.
|
||||
// 'size' correspond to the our 3x3 size, whereas 'shadow_tex_size' correspond to our 2x2 version where duplicate mirrored corners are not stored.
|
||||
const ImFontAtlasShadowTexConfig* shadow_cfg = &atlas->ShadowTexConfig;
|
||||
const int size = shadow_cfg->TexCornerSize + shadow_cfg->TexEdgeSize + shadow_cfg->TexCornerSize;
|
||||
const int corner_size = shadow_cfg->TexCornerSize;
|
||||
const int edge_size = shadow_cfg->TexEdgeSize;
|
||||
|
||||
// The bounds of the rectangle we are generating the shadow from
|
||||
const ImVec2 shadow_rect_min((float)corner_size, (float)corner_size);
|
||||
const ImVec2 shadow_rect_max((float)(corner_size + edge_size), (float)(corner_size + edge_size));
|
||||
|
||||
// Render the texture
|
||||
ImFontAtlasCustomRect r = atlas->CustomRects[atlas->ShadowRectId];
|
||||
|
||||
// Remove the padding we added
|
||||
const int padding = shadow_cfg->GetPadding();
|
||||
r.X += (unsigned short)padding;
|
||||
r.Y += (unsigned short)padding;
|
||||
r.Width -= (unsigned short)padding * 2;
|
||||
r.Height -= (unsigned short)padding * 2;
|
||||
|
||||
// We draw the actual texture content by evaluating the distance field for the inner rectangle
|
||||
// Generate distance field
|
||||
float* tex_data = (float*)alloca(size * size * sizeof(float));
|
||||
for (int y = 0; y < size; y++)
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
float dist = DistanceFromRectangle(ImVec2((float)x, (float)y), shadow_rect_min, shadow_rect_max);
|
||||
float alpha = 1.0f - ImMin(ImMax(dist + shadow_cfg->TexDistanceFieldOffset, 0.0f) / ImMax(shadow_cfg->TexCornerSize + shadow_cfg->TexDistanceFieldOffset, 0.001f), 1.0f);
|
||||
alpha = ImPow(alpha, shadow_cfg->TexFalloffPower); // Apply power curve to give a nicer falloff
|
||||
tex_data[x + (y * size)] = alpha;
|
||||
}
|
||||
|
||||
// Blur
|
||||
if (shadow_cfg->TexBlur)
|
||||
GaussianBlur(tex_data, size);
|
||||
|
||||
// Copy to texture, truncating to the actual required texture size (the bottom/right of the source data is chopped off, as we don't need it - see below). The truncated size is essentially the top 2x2 of our data, plus a little bit of padding for sampling.
|
||||
const int tex_w = atlas->TexWidth;
|
||||
const int shadow_tex_size = shadow_cfg->CalcTexSize();
|
||||
for (int y = 0; y < shadow_tex_size; y++)
|
||||
for (int x = 0; x < shadow_tex_size; x++)
|
||||
{
|
||||
const float alpha = tex_data[x + (y * size)];
|
||||
const unsigned int offset = (int)(r.X + x) + (int)(r.Y + y) * tex_w;
|
||||
atlas->TexPixelsAlpha8[offset] = (unsigned char)(0xFF * alpha);
|
||||
}
|
||||
|
||||
// Generate UVs for each of the nine sections, which are arranged in a 3x3 grid starting from 0 in the top-left and going across then down
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
ImFontAtlasCustomRect sub_rect = r;
|
||||
|
||||
// The third row/column of the 3x3 grid are generated by flipping the appropriate chunks of the upper 2x2 grid.
|
||||
bool flip_h = false; // Do we need to flip the UVs horizontally?
|
||||
bool flip_v = false; // Do we need to flip the UVs vertically?
|
||||
|
||||
switch (i % 3)
|
||||
{
|
||||
case 0: sub_rect.Width = (unsigned short)corner_size; break;
|
||||
case 1: sub_rect.X += (unsigned short)corner_size; sub_rect.Width = (unsigned short)edge_size; break;
|
||||
case 2: sub_rect.Width = (unsigned short)corner_size; flip_h = true; break;
|
||||
}
|
||||
|
||||
switch (i / 3)
|
||||
{
|
||||
case 0: sub_rect.Height = (unsigned short)corner_size; break;
|
||||
case 1: sub_rect.Y += (unsigned short)corner_size; sub_rect.Height = (unsigned short)edge_size; break;
|
||||
case 2: sub_rect.Height = (unsigned short)corner_size; flip_v = true; break;
|
||||
}
|
||||
|
||||
ImVec2 uv0, uv1;
|
||||
atlas->CalcCustomRectUV(&sub_rect, &uv0, &uv1);
|
||||
atlas->ShadowRectUvs[i] = ImVec4(flip_h ? uv1.x : uv0.x, flip_v ? uv1.y : uv0.y, flip_h ? uv0.x : uv1.x, flip_v ? uv0.y : uv1.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this is called / shared by both the stb_truetype and the FreeType builder
|
||||
void ImFontAtlasBuildInit(ImFontAtlas* atlas)
|
||||
{
|
||||
@ -2774,6 +3398,8 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas)
|
||||
if (!(atlas->Flags & ImFontAtlasFlags_NoBakedLines))
|
||||
atlas->PackIdLines = atlas->AddCustomRectRegular(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1);
|
||||
}
|
||||
|
||||
ImFontAtlasBuildRegisterShadowCustomRects(atlas);
|
||||
}
|
||||
|
||||
// This is called/shared by both the stb_truetype and the FreeType builder.
|
||||
@ -2783,6 +3409,7 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
|
||||
IM_ASSERT(atlas->TexPixelsAlpha8 != NULL || atlas->TexPixelsRGBA32 != NULL);
|
||||
ImFontAtlasBuildRenderDefaultTexData(atlas);
|
||||
ImFontAtlasBuildRenderLinesTexData(atlas);
|
||||
ImFontAtlasBuildRenderShadowTexData(atlas);
|
||||
|
||||
// Register custom rectangle glyphs
|
||||
for (int i = 0; i < atlas->CustomRects.Size; i++)
|
||||
|
@ -132,6 +132,7 @@ struct ImGuiPopupData; // Storage for current popup stack
|
||||
struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file
|
||||
struct ImGuiStackSizes; // Storage of stack sizes for debugging/asserting
|
||||
struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it
|
||||
struct ImGuiStyleShadowTexConfig; // Shadow Texture baking config
|
||||
struct ImGuiTabBar; // Storage for a tab bar
|
||||
struct ImGuiTabItem; // Storage for a tab item (within a tab bar)
|
||||
struct ImGuiTable; // Storage for a table
|
||||
@ -438,6 +439,7 @@ static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t)
|
||||
static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
|
||||
static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }
|
||||
static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
|
||||
static inline float ImLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImSqrt(d); return fail_value; }
|
||||
static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }
|
||||
static inline float ImFloor(float f) { return (float)(int)(f); }
|
||||
static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
|
||||
@ -725,6 +727,9 @@ struct IMGUI_API ImDrawListSharedData
|
||||
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
|
||||
|
||||
int ShadowRectId; // ID of rect for shadow texture
|
||||
const ImVec4* ShadowRectUvs; // UV coordinates for shadow texture (9 entries)
|
||||
|
||||
ImDrawListSharedData();
|
||||
void SetCircleTessellationMaxError(float max_error);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user