Fix untextured draws, implement custom rectangles.

This commit is contained in:
Skyth 2024-12-11 15:14:48 +03:00
parent 26fa3cea58
commit bdebd6408f
10 changed files with 232 additions and 49 deletions

View file

@ -2,12 +2,165 @@
#include <msdf-atlas-gen/msdf-atlas-gen.h>
// Taken directly from msdf-atlas-gen, modified to support custom rectangles.
struct TightAtlasPacker {
TightAtlasPacker() :
width(-1), height(-1),
spacing(0),
dimensionsConstraint(msdf_atlas::DimensionsConstraint::POWER_OF_TWO_SQUARE),
scale(-1),
minScale(1),
unitRange(0),
pxRange(0),
miterLimit(0),
pxAlignOriginX(false), pxAlignOriginY(false),
scaleMaximizationTolerance(.001)
{
}
int pack(msdf_atlas::GlyphGeometry* glyphs, int count, msdf_atlas::Rectangle* customRects, int customRectCount) {
double initialScale = scale > 0 ? scale : minScale;
if (initialScale > 0) {
if (int remaining = tryPack(glyphs, count, customRects, customRectCount, dimensionsConstraint, width, height, initialScale))
return remaining;
}
else if (width < 0 || height < 0)
return -1;
if (scale <= 0)
scale = packAndScale(glyphs, count, customRects, customRectCount);
if (scale <= 0)
return -1;
return 0;
}
int width, height;
int spacing;
msdf_atlas::DimensionsConstraint dimensionsConstraint;
double scale;
double minScale;
msdfgen::Range unitRange;
msdfgen::Range pxRange;
double miterLimit;
bool pxAlignOriginX, pxAlignOriginY;
msdf_atlas::Padding innerUnitPadding, outerUnitPadding;
msdf_atlas::Padding innerPxPadding, outerPxPadding;
double scaleMaximizationTolerance;
int tryPack(msdf_atlas::GlyphGeometry* glyphs, int count, msdf_atlas::Rectangle* customRects, int customRectCount, msdf_atlas::DimensionsConstraint dimensionsConstraint, int& width, int& height, double scale) const {
// Wrap glyphs into boxes
std::vector<msdf_atlas::Rectangle> rectangles;
std::vector<msdf_atlas::GlyphGeometry*> rectangleGlyphs;
rectangles.reserve(count + customRectCount);
rectangleGlyphs.reserve(count);
msdf_atlas::GlyphGeometry::GlyphAttributes attribs = { };
attribs.scale = scale;
attribs.range = { unitRange.lower + pxRange.lower / scale, unitRange.upper + pxRange.upper / scale };
attribs.innerPadding = innerUnitPadding + innerPxPadding / scale;
attribs.outerPadding = outerUnitPadding + outerPxPadding / scale;
attribs.miterLimit = miterLimit;
attribs.pxAlignOriginX = pxAlignOriginX;
attribs.pxAlignOriginY = pxAlignOriginY;
for (msdf_atlas::GlyphGeometry* glyph = glyphs, *end = glyphs + count; glyph < end; ++glyph) {
if (!glyph->isWhitespace()) {
msdf_atlas::Rectangle rect = { };
glyph->wrapBox(attribs);
glyph->getBoxSize(rect.w, rect.h);
if (rect.w > 0 && rect.h > 0) {
rectangles.push_back(rect);
rectangleGlyphs.push_back(glyph);
}
}
}
rectangles.insert(rectangles.end(), customRects, customRects + customRectCount);
// No non-zero size boxes?
if (rectangles.empty()) {
if (width < 0 || height < 0)
width = 0, height = 0;
return 0;
}
// Box rectangle packing
if (width < 0 || height < 0) {
std::pair<int, int> dimensions = std::make_pair(width, height);
switch (dimensionsConstraint) {
case msdf_atlas::DimensionsConstraint::POWER_OF_TWO_SQUARE:
dimensions = msdf_atlas::packRectangles<msdf_atlas::SquarePowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), spacing);
break;
case msdf_atlas::DimensionsConstraint::POWER_OF_TWO_RECTANGLE:
dimensions = msdf_atlas::packRectangles<msdf_atlas::PowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), spacing);
break;
case msdf_atlas::DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE:
dimensions = msdf_atlas::packRectangles<msdf_atlas::SquareSizeSelector<4> >(rectangles.data(), rectangles.size(), spacing);
break;
case msdf_atlas::DimensionsConstraint::EVEN_SQUARE:
dimensions = msdf_atlas::packRectangles<msdf_atlas::SquareSizeSelector<2> >(rectangles.data(), rectangles.size(), spacing);
break;
case msdf_atlas::DimensionsConstraint::SQUARE:
default:
dimensions = msdf_atlas::packRectangles<msdf_atlas::SquareSizeSelector<> >(rectangles.data(), rectangles.size(), spacing);
break;
}
if (!(dimensions.first > 0 && dimensions.second > 0))
return -1;
width = dimensions.first, height = dimensions.second;
}
else {
if (int result = packRectangles(rectangles.data(), rectangles.size(), width, height, spacing))
return result;
}
// Set glyph box placement
for (size_t i = 0; i < rectangleGlyphs.size(); ++i)
rectangleGlyphs[i]->placeBox(rectangles[i].x, height - (rectangles[i].y + rectangles[i].h));
for (int i = 0; i < customRectCount; ++i) {
customRects[i].x = rectangles[rectangleGlyphs.size() + i].x;
customRects[i].y = height - (rectangles[rectangleGlyphs.size() + i].y + rectangles[rectangleGlyphs.size() + i].h);
}
return 0;
}
double packAndScale(msdf_atlas::GlyphGeometry* glyphs, int count, msdf_atlas::Rectangle* customRects, int customRectCount) const {
bool lastResult = false;
int w = width, h = height;
#define TRY_PACK(scale) (lastResult = !tryPack(glyphs, count, customRects, customRectCount, msdf_atlas::DimensionsConstraint(), w, h, (scale)))
double minScale = 1, maxScale = 1;
if (TRY_PACK(1)) {
while (maxScale < 1e+32 && ((maxScale = 2 * minScale), TRY_PACK(maxScale)))
minScale = maxScale;
}
else {
while (minScale > 1e-32 && ((minScale = .5 * maxScale), !TRY_PACK(minScale)))
maxScale = minScale;
}
if (minScale == maxScale)
return 0;
while (minScale / maxScale < 1 - scaleMaximizationTolerance) {
double midScale = .5 * (minScale + maxScale);
if (TRY_PACK(midScale))
minScale = midScale;
else
maxScale = midScale;
}
if (!lastResult)
TRY_PACK(minScale);
return minScale;
}
};
extern void ImFontAtlasBuildInit(ImFontAtlas* atlas);
extern void ImFontAtlasBuildFinish(ImFontAtlas* atlas);
static bool FontBuilder_Build(ImFontAtlas* atlas)
{
ImFontAtlasBuildInit(atlas);
auto freeType = msdfgen::initializeFreetype();
std::vector<msdf_atlas::GlyphGeometry> glyphs;
std::vector<std::pair<size_t, size_t>> ranges;
std::vector<msdf_atlas::Rectangle> customRects;
for (auto& config : atlas->ConfigData)
{
@ -44,16 +197,25 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
for (auto& glyph : glyphs)
glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, 3.0, 0);
msdf_atlas::TightAtlasPacker packer;
packer.setMinimumScale(32.0);
packer.setMiterLimit(1.0);
packer.setPixelRange(2.0);
packer.pack(glyphs.data(), glyphs.size());
for (auto& customRect : atlas->CustomRects)
customRects.emplace_back(0, 0, int(customRect.Width), int(customRect.Height));
int width = 0, height = 0;
packer.getDimensions(width, height);
TightAtlasPacker packer;
packer.spacing = 1;
packer.minScale = 16.0;
packer.miterLimit = 1.0;
packer.pxRange = 4.0;
packer.pack(glyphs.data(), glyphs.size(), customRects.data(), customRects.size());
msdf_atlas::ImmediateAtlasGenerator<float, 4, &msdf_atlas::mtsdfGenerator, msdf_atlas::BitmapAtlasStorage<uint8_t, 4>> generator(width, height);
for (size_t i = 0; i < customRects.size(); i++)
{
auto& srcRect = customRects[i];
auto& dstRect = atlas->CustomRects[i];
dstRect.X = srcRect.x;
dstRect.Y = srcRect.y;
}
msdf_atlas::ImmediateAtlasGenerator<float, 3, &msdf_atlas::msdfGenerator, msdf_atlas::BitmapAtlasStorage<uint8_t, 3>> generator(packer.width, packer.height);
generator.generate(glyphs.data(), glyphs.size());
for (size_t i = 0; i < atlas->ConfigData.size(); i++)
@ -73,28 +235,39 @@ static bool FontBuilder_Build(ImFontAtlas* atlas)
-y1 + config.DstFont->Ascent,
x1,
-y0 + config.DstFont->Ascent,
u0 / width,
v1 / height,
u1 / width,
v0 / height,
u0 / packer.width,
v1 / packer.height,
u1 / packer.width,
v0 / packer.height,
glyph.getAdvance());
}
config.DstFont->BuildLookupTable();
}
atlas->TexReady = true;
atlas->TexPixelsUseColors = true;
atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(width * height * 4);
atlas->TexWidth = width;
atlas->TexHeight = height;
atlas->TexUvScale = { 1.0f / width, 1.0f / height };
atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(packer.width * packer.height * 4);
atlas->TexWidth = packer.width;
atlas->TexHeight = packer.height;
atlas->TexUvScale = { 1.0f / packer.width, 1.0f / packer.height };
auto bitmapRef = (msdfgen::BitmapConstRef<uint8_t, 4>)generator.atlasStorage();
memcpy(atlas->TexPixelsRGBA32, bitmapRef.pixels, width * height * 4);
auto bitmapRef = (msdfgen::BitmapConstRef<uint8_t, 3>)generator.atlasStorage();
for (int y = 0; y < packer.height; y++)
{
for (int x = 0; x < packer.width; x++)
{
auto* srcPixels = bitmapRef(x, y);
auto* dstPixels = (uint8_t*)&atlas->TexPixelsRGBA32[y * packer.width + x];
dstPixels[0] = srcPixels[0];
dstPixels[1] = srcPixels[1];
dstPixels[2] = srcPixels[2];
dstPixels[3] = 0xFF;
}
}
msdfgen::deinitializeFreetype(freeType);
ImFontAtlasBuildFinish(atlas);
return true;
}

View file

@ -252,12 +252,12 @@ void ImFontAtlasSnapshot::GenerateGlyphRanges()
g_glyphRanges.push_back(0);
}
ImFont* ImFontAtlasSnapshot::GetFont(const char* name, float size)
ImFont* ImFontAtlasSnapshot::GetFont(const char* name)
{
auto fontAtlas = ImGui::GetIO().Fonts;
for (auto& configData : fontAtlas->ConfigData)
{
if (strstr(configData.Name, name) != nullptr && abs(configData.SizePixels - size) < 0.001f)
if (strstr(configData.Name, name) != nullptr)
{
assert(configData.DstFont != nullptr);
return configData.DstFont;
@ -268,5 +268,5 @@ ImFont* ImFontAtlasSnapshot::GetFont(const char* name, float size)
assert(false && "Unable to locate equivalent font in the atlas file.");
#endif
return fontAtlas->AddFontFromFileTTF(name, size, nullptr, g_glyphRanges.data());
return fontAtlas->AddFontFromFileTTF(name, 1.0f, nullptr, g_glyphRanges.data());
}

View file

@ -58,5 +58,5 @@ struct ImFontAtlasSnapshot
static void GenerateGlyphRanges();
// When ENABLE_IM_FONT_ATLAS_SNAPSHOT is undefined, this creates the font runtime instead.
static ImFont* GetFont(const char* name, float size);
static ImFont* GetFont(const char* name);
};

View file

@ -73,13 +73,34 @@ float4 PixelAntialiasing(float2 uvTexspace)
return SampleLinear(uvTexspace);
}
float ComputeScreenPixelRange(float2 texCoord)
{
uint width, height;
g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex].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));
}
float4 main(in Interpolators interpolators) : SV_Target
{
float4 color = interpolators.Color;
color *= PixelAntialiasing(interpolators.Position.xy - 0.5);
if (g_PushConstants.Texture2DDescriptorIndex != 0)
color *= g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex].Sample(g_SamplerDescriptorHeap[0], interpolators.UV);
{
float4 msd = g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex].Sample(g_SamplerDescriptorHeap[0], interpolators.UV);
float sd = median(msd.r, msd.g, msd.b) - 0.5;
float screenPixelDistance = ComputeScreenPixelRange(interpolators.UV) * sd;
color.a *= saturate(screenPixelDistance + 0.5);
color.a *= msd.a;
}
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_MARQUEE_FADE)
{

View file

@ -765,11 +765,9 @@ void AchievementMenu::Init()
{
auto& io = ImGui::GetIO();
constexpr float FONT_SCALE = 2.0f;
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE);
g_fntNewRodinDB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE);
g_fntNewRodinUB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf", 20.0f * FONT_SCALE);
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
g_fntNewRodinDB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf");
g_fntNewRodinUB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf");
g_upTrophyIcon = LOAD_ZSTD_TEXTURE(g_trophy);
g_upSelectionCursor = LOAD_ZSTD_TEXTURE(g_select_fill);

View file

@ -76,9 +76,7 @@ void AchievementOverlay::Init()
{
auto& io = ImGui::GetIO();
constexpr float FONT_SCALE = 2.0f;
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE);
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window);
}

View file

@ -214,10 +214,8 @@ void ButtonGuide::Init()
{
auto& io = ImGui::GetIO();
constexpr float FONT_SCALE = 2.0f;
g_fntNewRodin = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf", 24.0f * FONT_SCALE);
g_fntNewRodinLQ = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf", 19.0f);
g_fntNewRodin = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf");
g_fntNewRodinLQ = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf");
g_upControllerIcons = LOAD_ZSTD_TEXTURE(g_controller);
g_upKBMIcons = LOAD_ZSTD_TEXTURE(g_kbm);

View file

@ -1334,10 +1334,9 @@ static void DrawMessagePrompt()
void InstallerWizard::Init()
{
auto &io = ImGui::GetIO();
constexpr float FONT_SCALE = 2.0f;
g_seuratFont = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE);
g_dfsogeistdFont = ImFontAtlasSnapshot::GetFont("DFSoGeiStd-W7.otf", 48.0f * FONT_SCALE);
g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE);
g_seuratFont = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
g_dfsogeistdFont = ImFontAtlasSnapshot::GetFont("DFSoGeiStd-W7.otf");
g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf");
g_installTextures[0] = LOAD_ZSTD_TEXTURE(g_install_001);
g_installTextures[1] = LOAD_ZSTD_TEXTURE(g_install_002);
g_installTextures[2] = LOAD_ZSTD_TEXTURE(g_install_003);

View file

@ -256,9 +256,7 @@ void MessageWindow::Init()
{
auto& io = ImGui::GetIO();
constexpr float FONT_SCALE = 2.0f;
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE);
g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
g_upSelectionCursor = LOAD_ZSTD_TEXTURE(g_select_fade);
g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window);

View file

@ -990,11 +990,9 @@ void OptionsMenu::Init()
{
auto& io = ImGui::GetIO();
constexpr float FONT_SCALE = 2.0f;
g_seuratFont = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE);
g_dfsogeistdFont = ImFontAtlasSnapshot::GetFont("DFSoGeiStd-W7.otf", 48.0f * FONT_SCALE);
g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE);
g_seuratFont = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
g_dfsogeistdFont = ImFontAtlasSnapshot::GetFont("DFSoGeiStd-W7.otf");
g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf");
LoadThumbnails();
}