spacebox/src/Text.cpp

158 lines
4.5 KiB
C++

/* +-------------------------------------------------------+
____/ \____ /| Open source game framework licensed to freely use, |
\ / / | copy, and modify, created for dank.game |
+--\ ^__^ /--+ | |
| ~/ \~ | | Download at https://open.shampoo.ooo/shampoo/spacebox |
| ~~~~~~~~~~~~ | +-------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#include "Text.hpp"
using namespace sb;
void Text::bind_textures() const
{
try
{
Model::bind_textures();
}
catch (const std::runtime_error& error)
{
std::ostringstream message;
message << "Error binding textures for " << *this << ": " << error.what();
throw std::runtime_error(message.str());
}
}
void Text::content(const std::string& content)
{
_content = content;
}
void Text::content(char content)
{
_content = "";
_content += content;
}
const std::string& Text::content() const
{
return _content;
}
void Text::foreground(const Color& foreground)
{
_foreground = foreground;
}
void Text::background(const Color& background)
{
_background = background;
}
void Text::font(std::shared_ptr<TTF_Font> font)
{
_font = font;
}
void Text::dimensions(const glm::vec2 &dimensions)
{
_dimensions = dimensions;
}
void Text::scaling_quality(GLint quality)
{
_scaling_quality = quality;
}
void Text::wrap(int width)
{
_wrap = width;
}
void Text::refresh()
{
/* Render the text with transparent background as RGBA pixel data using the SDL image library. Apply wrap if it is set. */
std::shared_ptr<SDL_Surface> blended;
if (_wrap.has_value())
{
blended = std::shared_ptr<SDL_Surface>{TTF_RenderUTF8_Blended_Wrapped(_font.get(), _content.c_str(), _foreground, _wrap.value()), SDL_FreeSurface};
}
else
{
blended = std::shared_ptr<SDL_Surface>{TTF_RenderUTF8_Blended(_font.get(), _content.c_str(), _foreground), SDL_FreeSurface};
}
/* Composite the rendered text onto a background surface. */
if (!blended)
{
Log::sdl_error("Could not create text");
}
else
{
/* Use the size of the rendered text unless dimensions has been set. */
glm::vec2 dimensions;
if (_dimensions.has_value())
{
dimensions = _dimensions.value();
}
else
{
dimensions = glm::vec2{blended->w, blended->h};
}
/* Create a container surface with the same format as the rendered text that the rendered text will be composited onto. */
std::shared_ptr<SDL_Surface> container
{
SDL_CreateRGBSurfaceWithFormat(0, dimensions.x, dimensions.y, blended->format->BitsPerPixel, blended->format->format),
SDL_FreeSurface
};
if (!container)
{
Log::sdl_error("Could not create container surface for rendered text");
}
else
{
SDL_FillRect(container.get(), nullptr, _background);
Box blended_box {0.0f, 0.0f, float(blended->w), float(blended->h), false};
Box container_box {0.0f, 0.0f, float(container->w), float(container->h), false};
blended_box.center(container_box.center());
SDL_Rect r = blended_box;
SDL_BlitSurface(blended.get(), nullptr, container.get(), &r);
blended = container;
}
/* Rotate and mirror the surface for compatibility with OpenGL */
std::shared_ptr<SDL_Surface> flipped {rotozoomSurfaceXY(blended.get(), 0, 1, -1, 0), SDL_FreeSurface};
if (!flipped)
{
Log::sdl_error("Could not flip surface");
}
else
{
/* Generate texture and create storage. Load pixels from the text rendering surface into the texture. The texture object will handle
* destroying the previous texture. The texture object is optimized so that it won't reallocate pixel memory if it was previously
* generated at the same size. */
texture(0).generate({flipped->w, flipped->h}, GL_RGBA8, _scaling_quality);
texture(0).load(flipped.get());
}
}
}
Text::operator std::string() const
{
std::ostringstream message;
message << "<sb::Text \"" << _content << "\">";
return message.str();
}
std::ostream& sb::operator<<(std::ostream& out, const Text& text)
{
out << std::string(text);
return out;
}