#include "core/rendering/text.hpp" #include #include // Para size_t #include // Para basic_ifstream, basic_istream, basic_ostream #include // Para cerr #include // Para istringstream #include // Para runtime_error #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface_sprite.hpp" // Para SSprite #include "core/resources/resource_helper.hpp" // Para ResourceHelper #include "utils/utils.hpp" // Para getFileName, stringToColor, printWithDots // Llena una estructuta TextFile desde un fichero auto Text::loadTextFile(const std::string& file_path) -> std::shared_ptr { auto tf = std::make_shared(); // No es necesario inicializar - los miembros tienen valores por defecto // Load file using ResourceHelper (supports both filesystem and pack) auto file_data = Resource::Helper::loadFile(file_path); if (file_data.empty()) { std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << '\n'; throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path)); } // Convert bytes to string and parse std::string content(file_data.begin(), file_data.end()); std::istringstream stream(content); std::string buffer; // Lee los dos primeros valores del fichero std::getline(stream, buffer); // Remove Windows line ending if present if (!buffer.empty() && buffer.back() == '\r') { buffer.pop_back(); } std::getline(stream, buffer); // Remove Windows line ending if present if (!buffer.empty() && buffer.back() == '\r') { buffer.pop_back(); } tf->box_width = std::stoi(buffer); std::getline(stream, buffer); // Remove Windows line ending if present if (!buffer.empty() && buffer.back() == '\r') { buffer.pop_back(); } std::getline(stream, buffer); // Remove Windows line ending if present if (!buffer.empty() && buffer.back() == '\r') { buffer.pop_back(); } tf->box_height = std::stoi(buffer); // lee el resto de datos del fichero auto index = 32; auto line_read = 0; while (std::getline(stream, buffer)) { // Remove Windows line ending if present if (!buffer.empty() && buffer.back() == '\r') { buffer.pop_back(); } // Almacena solo las lineas impares if (line_read % 2 == 1) { tf->offset[index++].w = std::stoi(buffer); } // Limpia el buffer buffer.clear(); line_read++; }; printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]"); // Establece las coordenadas para cada caracter ascii de la cadena y su ancho for (int i = 32; i < 128; ++i) { tf->offset[i].x = ((i - 32) % 15) * tf->box_width; tf->offset[i].y = ((i - 32) / 15) * tf->box_height; } return tf; } // Constructor Text::Text(const std::shared_ptr& surface, const std::string& text_file) { // Carga los offsets desde el fichero auto tf = loadTextFile(text_file); // Inicializa variables desde la estructura box_height_ = tf->box_height; box_width_ = tf->box_width; offset_ = tf->offset; // Crea los objetos sprite_ = std::make_unique(surface, (SDL_FRect){0.0F, 0.0F, static_cast(box_width_), static_cast(box_height_)}); } // Constructor Text::Text(const std::shared_ptr& surface, const std::shared_ptr& text_file) : sprite_(std::make_unique(surface, (SDL_FRect){0.0F, 0.0F, static_cast(text_file->box_width), static_cast(text_file->box_height)})), box_width_(text_file->box_width), box_height_(text_file->box_height), offset_(text_file->offset) { } // Escribe texto en pantalla void Text::write(int x, int y, const std::string& text, int kerning, int lenght) { int shift = 0; if (lenght == -1) { lenght = text.length(); } sprite_->setY(y); for (int i = 0; i < lenght; ++i) { auto index = static_cast(text[i]); sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_); sprite_->setX(x + shift); sprite_->render(1, 15); shift += offset_[static_cast(text[i])].w + kerning; } } // Escribe el texto en una surface auto Text::writeToSurface(const std::string& text, int zoom, int kerning) -> std::shared_ptr { auto width = length(text, kerning) * zoom; auto height = box_height_ * zoom; auto surface = std::make_shared(width, height); auto previuos_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(surface); surface->clear(stringToColor("transparent")); write(0, 0, text, kerning); Screen::get()->setRendererSurface(previuos_renderer); return surface; } // Escribe el texto con extras en una surface auto Text::writeDXToSurface(Uint8 flags, const std::string& text, int kerning, Uint8 text_color, Uint8 shadow_distance, Uint8 shadow_color, int lenght) -> std::shared_ptr { auto width = Text::length(text, kerning) + shadow_distance; auto height = box_height_ + shadow_distance; auto surface = std::make_shared(width, height); auto previuos_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(surface); surface->clear(stringToColor("transparent")); writeDX(flags, 0, 0, text, kerning, text_color, shadow_distance, shadow_color, lenght); Screen::get()->setRendererSurface(previuos_renderer); return surface; } // Escribe el texto con colores void Text::writeColored(int x, int y, const std::string& text, Uint8 color, int kerning, int lenght) { int shift = 0; if (lenght == -1) { lenght = text.length(); } sprite_->setY(y); for (int i = 0; i < lenght; ++i) { auto index = static_cast(text[i]); sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_); sprite_->setX(x + shift); sprite_->render(1, color); shift += offset_[static_cast(text[i])].w + kerning; } } // Escribe el texto con sombra void Text::writeShadowed(int x, int y, const std::string& text, Uint8 color, Uint8 shadow_distance, int kerning, int lenght) { writeColored(x + shadow_distance, y + shadow_distance, text, color, kerning, lenght); write(x, y, text, kerning, lenght); } // Escribe el texto centrado en un punto x void Text::writeCentered(int x, int y, const std::string& text, int kerning, int lenght) { x -= (Text::length(text, kerning) / 2); write(x, y, text, kerning, lenght); } // Escribe texto con extras void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning, Uint8 text_color, Uint8 shadow_distance, Uint8 shadow_color, int lenght) { const auto CENTERED = ((flags & CENTER_FLAG) == CENTER_FLAG); const auto SHADOWED = ((flags & SHADOW_FLAG) == SHADOW_FLAG); const auto COLORED = ((flags & COLOR_FLAG) == COLOR_FLAG); const auto STROKED = ((flags & STROKE_FLAG) == STROKE_FLAG); if (CENTERED) { x -= (Text::length(text, kerning) / 2); } if (SHADOWED) { writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, lenght); } if (STROKED) { for (int dist = 1; dist <= shadow_distance; ++dist) { for (int dy = -dist; dy <= dist; ++dy) { for (int dx = -dist; dx <= dist; ++dx) { writeColored(x + dx, y + dy, text, shadow_color, kerning, lenght); } } } } if (COLORED) { writeColored(x, y, text, text_color, kerning, lenght); } else { writeColored(x, y, text, text_color, kerning, lenght); // write(x, y, text, kerning, lenght); } } // Obtiene la longitud en pixels de una cadena auto Text::length(const std::string& text, int kerning) const -> int { int shift = 0; for (size_t i = 0; i < text.length(); ++i) { shift += (offset_[static_cast(text[i])].w + kerning); } // Descuenta el kerning del último caracter return shift - kerning; } // Devuelve el valor de la variable auto Text::getCharacterSize() const -> int { return box_width_; } // Establece si se usa un tamaño fijo de letra void Text::setFixedWidth(bool value) { fixed_width_ = value; }