making text pretty

This commit is contained in:
2026-05-05 23:51:02 +10:00
parent 62c3ded1f8
commit 6ce09c0e9c
2 changed files with 62 additions and 8 deletions

View File

@@ -68,9 +68,12 @@ constexpr GLuint kSourceHistoryTextureUnitBase = 2;
constexpr GLuint kPackedVideoTextureUnit = 2;
constexpr GLuint kGlobalParamsBindingPoint = 0;
constexpr unsigned kPrerollFrameCount = 8;
constexpr unsigned kTextTextureWidth = 1024;
constexpr unsigned kTextTextureHeight = 128;
constexpr int kTextSdfSpread = 10;
constexpr unsigned kTextTextureWidth = 2048;
constexpr unsigned kTextTextureHeight = 256;
constexpr int kTextSdfSpread = 20;
constexpr unsigned kTextSdfBlurPasses = 1;
constexpr float kTextFontPixelSize = 144.0f;
constexpr float kTextLayoutPadding = 48.0f;
const char* kVertexShaderSource =
"#version 430 core\n"
"out vec2 vTexCoord;\n"
@@ -230,6 +233,48 @@ std::vector<unsigned char> FlipTextTextureForShaderUv(const std::vector<unsigned
return flipped;
}
std::vector<unsigned char> BlurTextSdf(const std::vector<unsigned char>& pixels, unsigned width, unsigned height, unsigned passes)
{
std::vector<unsigned char> current = pixels;
std::vector<unsigned char> next(pixels.size(), 0);
for (unsigned pass = 0; pass < passes; ++pass)
{
for (unsigned y = 0; y < height; ++y)
{
for (unsigned x = 0; x < width; ++x)
{
unsigned weightedTotal = 0;
unsigned weightSum = 0;
for (int oy = -1; oy <= 1; ++oy)
{
const int sy = static_cast<int>(y) + oy;
if (sy < 0 || sy >= static_cast<int>(height))
continue;
for (int ox = -1; ox <= 1; ++ox)
{
const int sx = static_cast<int>(x) + ox;
if (sx < 0 || sx >= static_cast<int>(width))
continue;
const unsigned weight = (ox == 0 && oy == 0) ? 4u : ((ox == 0 || oy == 0) ? 2u : 1u);
const std::size_t sample = (static_cast<std::size_t>(sy) * width + sx) * 4;
weightedTotal += static_cast<unsigned>(current[sample]) * weight;
weightSum += weight;
}
}
const unsigned char value = static_cast<unsigned char>((weightedTotal + weightSum / 2) / weightSum);
const std::size_t out = (static_cast<std::size_t>(y) * width + x) * 4;
next[out + 0] = value;
next[out + 1] = value;
next[out + 2] = value;
next[out + 3] = value;
}
}
current.swap(next);
}
return current;
}
void WriteTextMaskDebugDump(const std::string& text, const std::vector<unsigned char>& alpha, const std::vector<unsigned char>& sdf, unsigned width, unsigned height)
{
try
@@ -338,15 +383,19 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font
graphics.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
graphics.Clear(Gdiplus::Color(255, 0, 0, 0));
graphics.SetCompositingMode(Gdiplus::CompositingModeSourceOver);
graphics.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAliasGridFit);
graphics.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias);
graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
Gdiplus::Font font(fontFamily, 72.0f, Gdiplus::FontStyleRegular, Gdiplus::UnitPixel);
Gdiplus::Font font(fontFamily, kTextFontPixelSize, Gdiplus::FontStyleRegular, Gdiplus::UnitPixel);
Gdiplus::SolidBrush brush(Gdiplus::Color(255, 255, 255, 255));
Gdiplus::StringFormat format;
format.SetAlignment(Gdiplus::StringAlignmentNear);
format.SetLineAlignment(Gdiplus::StringAlignmentCenter);
format.SetFormatFlags(Gdiplus::StringFormatFlagsNoWrap | Gdiplus::StringFormatFlagsMeasureTrailingSpaces);
const Gdiplus::RectF layout(24.0f, 0.0f, static_cast<Gdiplus::REAL>(kTextTextureWidth - 48), static_cast<Gdiplus::REAL>(kTextTextureHeight));
const Gdiplus::RectF layout(
kTextLayoutPadding,
0.0f,
static_cast<Gdiplus::REAL>(kTextTextureWidth) - (kTextLayoutPadding * 2.0f),
static_cast<Gdiplus::REAL>(kTextTextureHeight));
const std::wstring wideText = Utf8ToWide(text);
graphics.DrawString(wideText.c_str(), -1, &font, layout, &format, &brush);
@@ -366,6 +415,7 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font
}
}
sdf = BuildLocalSdf(alpha, kTextTextureWidth, kTextTextureHeight);
sdf = BlurTextSdf(sdf, kTextTextureWidth, kTextTextureHeight, kTextSdfBlurPasses);
sdf = FlipTextTextureForShaderUv(sdf, kTextTextureWidth, kTextTextureHeight);
WriteTextMaskDebugDump(text, alpha, sdf, kTextTextureWidth, kTextTextureHeight);
return true;

View File

@@ -20,8 +20,12 @@ float4 shadeVideo(ShaderContext context)
bool insideTextRect = textUv.x >= 0.0 && textUv.x <= 1.0 && textUv.y >= 0.0 && textUv.y <= 1.0;
float mask = insideTextRect ? sampleTitleText(textUv) : 0.0;
float fill = smoothstep(0.48, 0.54, mask);
float outline = smoothstep(0.48 - outlineWidth, 0.54 - outlineWidth, mask);
float edge = 0.5;
float aa = max(fwidth(mask) * 1.5, 0.006);
float outlineAmount = min(outlineWidth * 0.25, 0.24);
float fill = smoothstep(edge - aa, edge + aa, mask);
float outlineField = smoothstep(edge - outlineAmount - aa, edge - outlineAmount + aa, mask);
float outline = saturate(outlineField - fill);
float textAlpha = max(fill * fillColor.a, outline * outlineColor.a);
if (textAlpha <= 0.0001)
return context.sourceColor;