Implement outlines.

This commit is contained in:
Skyth 2024-12-11 19:17:00 +03:00
parent bbe869c25f
commit 82aee0951a
10 changed files with 55 additions and 66 deletions

View file

@ -17,6 +17,7 @@ enum class ImGuiCallback : int32_t
SetOrigin = -3,
SetScale = -4,
SetMarqueeFade = -5,
SetOutline = -6,
// -8 is ImDrawCallback_ResetRenderState, don't use!
};
@ -50,6 +51,11 @@ union ImGuiCallbackData
float boundsMin[2];
float boundsMax[2];
} setMarqueeFade;
struct
{
float outline;
} setOutline;
};
extern ImGuiCallbackData* AddImGuiCallback(ImGuiCallback callback);

View file

@ -205,7 +205,7 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
packer.dimensionsConstraint = msdf_atlas::DimensionsConstraint::POWER_OF_TWO_RECTANGLE;
packer.minScale = 24.0;
packer.miterLimit = 1.0;
packer.pxRange = 4.0;
packer.pxRange = 8.0;
packer.pack(glyphs.data(), glyphs.size(), customRects.data(), customRects.size());
for (size_t i = 0; i < customRects.size(); i++)

View file

@ -13,6 +13,7 @@ struct PushConstants
float2 InverseDisplaySize;
float2 Origin;
float2 Scale;
float Outline;
};
Texture2D<float4> g_Texture2DDescriptorHeap[] : register(t0, space0);

View file

@ -73,21 +73,6 @@ float4 PixelAntialiasing(float2 uvTexspace)
return SampleLinear(uvTexspace);
}
uint GetTexture2DDescriptorIndex()
{
return g_PushConstants.Texture2DDescriptorIndex & 0x7FFFFFFF;
}
float ComputeScreenPixelRange(float2 texCoord)
{
uint width, height;
g_Texture2DDescriptorHeap[GetTexture2DDescriptorIndex()].GetDimensions(width, height);
float2 unitRange = 4.0 / float2(width, height);
float2 screenTextureSize = 1.0 / fwidth(texCoord);
return max(0.5 * dot(unitRange, screenTextureSize), 1.0);
}
float median(float r, float g, float b)
{
return max(min(r, g), min(max(r, g), b));
@ -100,17 +85,28 @@ float4 main(in Interpolators interpolators) : SV_Target
if (g_PushConstants.Texture2DDescriptorIndex != 0)
{
float4 texture = g_Texture2DDescriptorHeap[GetTexture2DDescriptorIndex()].Sample(g_SamplerDescriptorHeap[0], interpolators.UV);
Texture2D<float4> texture = g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex & 0x7FFFFFFF];
float4 textureColor = texture.Sample(g_SamplerDescriptorHeap[0], interpolators.UV);
if ((g_PushConstants.Texture2DDescriptorIndex & 0x80000000) != 0)
{
float sd = median(texture.r, texture.g, texture.b) - 0.5;
float screenPixelDistance = ComputeScreenPixelRange(interpolators.UV) * sd;
color.a *= saturate(screenPixelDistance + 0.5);
color.a *= texture.a;
uint width, height;
texture.GetDimensions(width, height);
float pxRange = 8.0;
float2 unitRange = pxRange / float2(width, height);
float2 screenTexSize = 1.0 / fwidth(interpolators.UV);
float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0);
float sd = median(textureColor.r, textureColor.g, textureColor.b) - 0.5;
float screenPxDistance = screenPxRange * (sd + g_PushConstants.Outline / (pxRange * 2.0));
color.a *= saturate(screenPxDistance + 0.5);
color.a *= textureColor.a;
}
else
{
color *= texture;
color *= textureColor;
}
}

View file

@ -1108,6 +1108,7 @@ struct ImGuiPushConstants
ImVec2 inverseDisplaySize{};
ImVec2 origin{ 0.0f, 0.0f };
ImVec2 scale{ 1.0f, 1.0f };
float outline{};
};
extern ImFontBuilderIO g_fontBuilderIO;
@ -1952,6 +1953,9 @@ static void ProcDrawImGui(const RenderCommand& cmd)
case ImGuiCallback::SetMarqueeFade:
setPushConstants(&pushConstants.boundsMin, &callbackData->setMarqueeFade, sizeof(callbackData->setMarqueeFade));
break;
case ImGuiCallback::SetOutline:
setPushConstants(&pushConstants.outline, &callbackData->setOutline, sizeof(callbackData->setOutline));
break;
default:
assert(false && "Unknown ImGui callback type.");
break;

View file

@ -141,14 +141,14 @@ static void DrawHeaderContainer(const char* text)
SetTextSkew((min.y + max.y) / 2.0f, Scale(3.0f));
// TODO: Apply bevel.
DrawTextWithOutline<float>
DrawTextWithOutline
(
g_fntNewRodinUB,
fontSize,
{ /* X */ min.x + textMarginX, /* Y */ CENTRE_TEXT_VERT(min, max, textSize) - Scale(5) },
IM_COL32(255, 255, 255, 255 * alpha),
text,
1.65f,
4,
IM_COL32(0, 0, 0, 255 * alpha)
);
@ -349,7 +349,7 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen
);
// Draw timestamp text.
DrawTextWithOutline<int>
DrawTextWithOutline
(
g_fntNewRodinDB,
fontSize,
@ -523,7 +523,7 @@ static void DrawAchievementTotal(ImVec2 min, ImVec2 max)
auto fontSize = Scale(20);
auto textSize = g_fntNewRodinDB->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str());
DrawTextWithOutline<int>
DrawTextWithOutline
(
g_fntNewRodinDB,
fontSize,

View file

@ -188,7 +188,7 @@ static void DrawGuide(float* offset, ImVec2 regionMin, ImVec2 regionMax, EButton
ImVec2 textPosition = { textMarginX, textMarginY };
DrawTextWithOutline<int>(font, fontSize, textPosition, IM_COL32_WHITE, text, 2, IM_COL32_BLACK);
DrawTextWithOutline(font, fontSize, textPosition, IM_COL32_WHITE, text, 2, IM_COL32_BLACK);
// Add extra luminance to low quality text.
if (fontQuality == EFontQuality::Low)

View file

@ -83,6 +83,17 @@ static void ResetMarqueeFade()
SetScale({ 1.0f, 1.0f });
}
static void SetOutline(float outline)
{
auto callbackData = AddImGuiCallback(ImGuiCallback::SetOutline);
callbackData->setOutline.outline = outline;
}
static void ResetOutline()
{
SetOutline(0.0f);
}
// Aspect ratio aware.
static float Scale(float size)
{
@ -179,41 +190,13 @@ static void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2
drawList->PopClipRect();
}
template<typename T>
static void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, T outlineSize, ImU32 outlineColor)
static void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, float outlineSize, ImU32 outlineColor)
{
auto drawList = ImGui::GetForegroundDrawList();
outlineSize = Scale(outlineSize);
if constexpr (std::is_same_v<float, T> || std::is_same_v<double, T>)
{
auto step = outlineSize / 2.0f;
// TODO: This is still very inefficient!
for (float i = -outlineSize; i <= outlineSize; i += step)
{
for (float j = -outlineSize; j <= outlineSize; j += step)
{
if (i == 0.0f && j == 0.0f)
continue;
drawList->AddText(font, fontSize, { pos.x + i, pos.y + j }, outlineColor, text);
}
}
}
else if constexpr (std::is_integral_v<T>)
{
// TODO: This is very inefficient!
for (int32_t i = -outlineSize + 1; i < outlineSize; i++)
{
for (int32_t j = -outlineSize + 1; j < outlineSize; j++)
{
if (i != 0 || j != 0)
drawList->AddText(font, fontSize, { pos.x + i, pos.y + j }, outlineColor, text);
}
}
}
SetOutline(outlineSize);
drawList->AddText(font, fontSize, pos, outlineColor, text);
ResetOutline();
drawList->AddText(font, fontSize, pos, color, text);
}
@ -223,9 +206,8 @@ static void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2&
auto drawList = ImGui::GetForegroundDrawList();
offset = Scale(offset);
radius = Scale(radius);
DrawTextWithOutline<float>(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text, radius, shadowColour);
DrawTextWithOutline(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text, radius, shadowColour);
drawList->AddText(font, fontSize, pos, colour, text, nullptr);
}

View file

@ -559,7 +559,7 @@ static void DrawScanlineBars()
// Installer text
const std::string &headerText = Localise(g_currentPage == WizardPage::Installing ? "Installer_Header_Installing" : "Installer_Header_Installer");
int textAlpha = std::lround(255.0f * ComputeMotionInstaller(g_appearTime, g_disappearTime, TITLE_ANIMATION_TIME, TITLE_ANIMATION_DURATION));
DrawTextWithOutline<int>(g_dfsogeistdFont, Scale(42.0f), { Scale(285.0f), Scale(57.0f) }, IM_COL32(255, 195, 0, textAlpha), headerText.c_str(), 4, IM_COL32(0, 0, 0, textAlpha));
DrawTextWithOutline(g_dfsogeistdFont, Scale(42.0f), { Scale(285.0f), Scale(57.0f) }, IM_COL32(255, 195, 0, textAlpha), headerText.c_str(), 4, IM_COL32(0, 0, 0, textAlpha));
// Top bar line
drawList->AddLine
@ -761,7 +761,7 @@ static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sour
IM_COL32(baser + 128, baseg + 170, 0, 255)
);
DrawTextWithOutline<int>
DrawTextWithOutline
(
font,
size,

View file

@ -138,7 +138,7 @@ static void DrawScanlineBars()
// Options text
// TODO: localise this.
DrawTextWithOutline<int>(g_dfsogeistdFont, Scale(48.0f), { Scale(122.0f), Scale(56.0f) }, IM_COL32(255, 195, 0, 255), "OPTIONS", 4, IM_COL32_BLACK);
DrawTextWithOutline(g_dfsogeistdFont, Scale(48.0f), { Scale(122.0f), Scale(56.0f) }, IM_COL32(255, 195, 0, 255), "OPTIONS", 4, IM_COL32_BLACK);
// Top bar line
drawList->AddLine
@ -389,7 +389,7 @@ static bool DrawCategories()
IM_COL32(255, 192, 0, alpha)
);
DrawTextWithOutline<int>
DrawTextWithOutline
(
g_dfsogeistdFont,
size,
@ -755,7 +755,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
IM_COL32(128, 170, 0, 255)
);
DrawTextWithOutline<int>
DrawTextWithOutline
(
g_newRodinFont,
size,