#include "Text.h" #include "../Assets.h" #include "../UserInterface/Engine/Scale.h" #include "../UserInterface/Primitives.h" #include // Static member initializations - all at the top TTF_Font* Text::font = nullptr; TTF_Font* Text::iconFont = nullptr; float Text::fontScale = 0.0f; float Text::iconFontScale = 0.0f; constexpr float BaseFontPoint = 16.0f; constexpr float BaseIconFontPoint = 24.0f; void Text::Init() { EnsureFonts(); } void Text::EnsureFonts() { // fonts are rasterized at base size * ui scale so glyphs land on real // device pixels instead of getting stretched at draw time const float scale = UI::GetScale(); if (!font) { if (!TTF_Init()) { SDL_Log("TTF_Init failed: %s", SDL_GetError()); return; } font = TTF_OpenFont(Assets::Path("fonts/lexend.ttf").data(), BaseFontPoint * scale); fontScale = scale; if (!font) { SDL_Log("TTF_OpenFont failed: %s", SDL_GetError()); } } else if (fontScale != scale) { TTF_SetFontSize(font, BaseFontPoint * scale); fontScale = scale; } if (!iconFont) { iconFont = TTF_OpenFont(Assets::Path("fonts/materialSymbolsRounded.ttf").data(), BaseIconFontPoint * scale); iconFontScale = scale; if (!iconFont) { SDL_Log("TTF_OpenFont failed: %s", SDL_GetError()); } } else if (iconFontScale != scale) { TTF_SetFontSize(iconFont, BaseIconFontPoint * scale); iconFontScale = scale; } } Text::Text(double x, double y, std::string content, TTF_Font* font) : x(x), y(y), content(content), overriddenFont(font) { Init(); createTexture(); } void Text::SetText(const std::string& newContent) { if (content == newContent) { return; } content = newContent; delete texture; texture = nullptr; createTexture(); } void Text::SetPosition(double newX, double newY) { x = newX; y = newY; } void Text::createTexture() { EnsureFonts(); if (!font) return; SDL_Surface* surface = TTF_RenderText_Blended(overriddenFont ? overriddenFont : font, content.c_str(), content.length(), {255, 255, 255, 255}); if (!surface) { SDL_Log("TTF_RenderText_Blended failed: %s", SDL_GetError()); return; } SDL_Surface* converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ABGR8888); SDL_DestroySurface(surface); if (!converted) return; SDL_GPUTextureCreateInfo createInfo = {}; createInfo.type = SDL_GPU_TEXTURETYPE_2D; createInfo.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; createInfo.width = converted->w; createInfo.height = converted->h; createInfo.layer_count_or_depth = 1; createInfo.num_levels = 1; createInfo.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; texture = new Texture(createInfo); texture->Upload(converted->pixels); textureScale = UI::GetScale(); SDL_DestroySurface(converted); } void Text::SetColor(double r, double g, double b, double a) { this->r = r; this->g = g; this->b = b; this->a = a; } void Text::Render() { // the ui scale moved since this was rasterized, redo it crisp if (texture && textureScale != UI::GetScale()) { delete texture; texture = nullptr; } if (!texture) { createTexture(); } if (!texture) return; UI::Primitives::SubmitText( texture, UI::Rect( static_cast(x), static_cast(y), UI::PixelsToUnits(static_cast(texture->GetWidth())), UI::PixelsToUnits(static_cast(texture->GetHeight())) ), UI::Color(static_cast(r), static_cast(g), static_cast(b), static_cast(a)) ); } UI::Size Text::Measure(std::string content) { EnsureFonts(); if (!font) return { 0.0f, 0.0f }; int width; TTF_MeasureString(font, content.c_str(), strlen(content.c_str()), 0, &width, nullptr); return { UI::PixelsToUnits(static_cast(width)), UI::PixelsToUnits(static_cast(TTF_GetFontHeight(font))) }; } TTF_Font* Text::GetIconFont() { EnsureFonts(); return iconFont; } Text::~Text() { delete texture; }