/* +-------------------------------------------------------+ ____/ \____ /| 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 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 blended; if (_wrap.has_value()) { blended = std::shared_ptr{TTF_RenderUTF8_Blended_Wrapped(_font.get(), _content.c_str(), _foreground, _wrap.value()), SDL_FreeSurface}; } else { blended = std::shared_ptr{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 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 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 << ""; return message.str(); } std::ostream& sb::operator<<(std::ostream& out, const Text& text) { out << std::string(text); return out; }