353 lines
12 KiB
C++
353 lines
12 KiB
C++
#include "textrenderer.h"
|
|
#include <SDL3/SDL.h>
|
|
#include <SDL3_ttf/SDL_ttf.h>
|
|
#include "../resource_manager.h"
|
|
|
|
TextRenderer::TextRenderer() : renderer_(nullptr), font_(nullptr), font_size_(0), use_antialiasing_(true), font_data_buffer_(nullptr) {
|
|
}
|
|
|
|
TextRenderer::~TextRenderer() {
|
|
cleanup();
|
|
}
|
|
|
|
bool TextRenderer::init(SDL_Renderer* renderer, const char* font_path, int font_size, bool use_antialiasing) {
|
|
renderer_ = renderer;
|
|
font_size_ = font_size;
|
|
use_antialiasing_ = use_antialiasing;
|
|
font_path_ = font_path; // Guardar ruta para reinitialize()
|
|
|
|
// Inicializar SDL_ttf si no está inicializado
|
|
if (!TTF_WasInit()) {
|
|
if (!TTF_Init()) {
|
|
SDL_Log("Error al inicializar SDL_ttf: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Intentar cargar la fuente desde ResourceManager (pack o disco)
|
|
unsigned char* fontData = nullptr;
|
|
size_t fontDataSize = 0;
|
|
|
|
if (ResourceManager::loadResource(font_path, fontData, fontDataSize)) {
|
|
// Crear SDL_IOStream desde memoria
|
|
SDL_IOStream* fontIO = SDL_IOFromConstMem(fontData, static_cast<size_t>(fontDataSize));
|
|
if (fontIO != nullptr) {
|
|
// Cargar fuente desde IOStream
|
|
font_ = TTF_OpenFontIO(fontIO, true, font_size); // true = cerrar stream automáticamente
|
|
|
|
if (font_ == nullptr) {
|
|
SDL_Log("Error al cargar fuente desde memoria '%s': %s", font_path, SDL_GetError());
|
|
delete[] fontData; // Liberar solo si falla la carga
|
|
return false;
|
|
}
|
|
|
|
// CRÍTICO: NO eliminar fontData aquí - SDL_ttf necesita estos datos en memoria
|
|
// mientras la fuente esté abierta. Se liberará en cleanup()
|
|
font_data_buffer_ = fontData;
|
|
SDL_Log("Fuente cargada desde ResourceManager: %s (%lu bytes)", font_path, (unsigned long)fontDataSize);
|
|
return true;
|
|
} else {
|
|
delete[] fontData;
|
|
}
|
|
}
|
|
|
|
// Fallback final: intentar cargar directamente desde disco (por si falla ResourceManager)
|
|
font_ = TTF_OpenFont(font_path, font_size);
|
|
if (font_ == nullptr) {
|
|
SDL_Log("Error al cargar fuente '%s': %s", font_path, SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TextRenderer::reinitialize(int new_font_size) {
|
|
// Verificar que tenemos todo lo necesario
|
|
if (renderer_ == nullptr || font_path_.empty()) {
|
|
SDL_Log("Error: TextRenderer no inicializado correctamente para reinitialize()");
|
|
return false;
|
|
}
|
|
|
|
// Si el tamaño es el mismo, no hacer nada
|
|
if (new_font_size == font_size_) {
|
|
return true;
|
|
}
|
|
|
|
// Cerrar fuente actual y liberar buffer previo
|
|
if (font_ != nullptr) {
|
|
TTF_CloseFont(font_);
|
|
font_ = nullptr;
|
|
}
|
|
if (font_data_buffer_ != nullptr) {
|
|
delete[] font_data_buffer_;
|
|
font_data_buffer_ = nullptr;
|
|
}
|
|
|
|
// Intentar cargar la fuente desde ResourceManager con el nuevo tamaño
|
|
unsigned char* fontData = nullptr;
|
|
size_t fontDataSize = 0;
|
|
|
|
if (ResourceManager::loadResource(font_path_, fontData, fontDataSize)) {
|
|
SDL_IOStream* fontIO = SDL_IOFromConstMem(fontData, static_cast<size_t>(fontDataSize));
|
|
if (fontIO != nullptr) {
|
|
font_ = TTF_OpenFontIO(fontIO, true, new_font_size);
|
|
|
|
if (font_ == nullptr) {
|
|
SDL_Log("Error al recargar fuente '%s' con tamaño %d: %s",
|
|
font_path_.c_str(), new_font_size, SDL_GetError());
|
|
delete[] fontData; // Liberar solo si falla
|
|
return false;
|
|
}
|
|
|
|
// Mantener buffer en memoria (NO eliminar)
|
|
font_data_buffer_ = fontData;
|
|
font_size_ = new_font_size;
|
|
SDL_Log("Fuente recargada desde ResourceManager: %s (tamaño %d)", font_path_.c_str(), new_font_size);
|
|
return true;
|
|
} else {
|
|
delete[] fontData;
|
|
}
|
|
}
|
|
|
|
// Fallback: cargar directamente desde disco
|
|
font_ = TTF_OpenFont(font_path_.c_str(), new_font_size);
|
|
if (font_ == nullptr) {
|
|
SDL_Log("Error al recargar fuente '%s' con tamaño %d: %s",
|
|
font_path_.c_str(), new_font_size, SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
font_size_ = new_font_size;
|
|
return true;
|
|
}
|
|
|
|
void TextRenderer::cleanup() {
|
|
if (font_ != nullptr) {
|
|
TTF_CloseFont(font_);
|
|
font_ = nullptr;
|
|
}
|
|
if (font_data_buffer_ != nullptr) {
|
|
delete[] font_data_buffer_;
|
|
font_data_buffer_ = nullptr;
|
|
}
|
|
renderer_ = nullptr;
|
|
}
|
|
|
|
void TextRenderer::print(int x, int y, const char* text, uint8_t r, uint8_t g, uint8_t b) {
|
|
if (!isInitialized() || text == nullptr || text[0] == '\0') {
|
|
return;
|
|
}
|
|
|
|
// Crear superficie con el texto renderizado
|
|
SDL_Color color = {r, g, b, 255};
|
|
SDL_Surface* text_surface = nullptr;
|
|
|
|
if (use_antialiasing_) {
|
|
// Con antialiasing (suave, mejor calidad)
|
|
text_surface = TTF_RenderText_Blended(font_, text, strlen(text), color);
|
|
} else {
|
|
// Sin antialiasing (píxeles nítidos, estilo retro)
|
|
text_surface = TTF_RenderText_Solid(font_, text, strlen(text), color);
|
|
}
|
|
|
|
if (text_surface == nullptr) {
|
|
SDL_Log("Error al renderizar texto: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
// Crear textura desde la superficie
|
|
SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer_, text_surface);
|
|
|
|
if (text_texture == nullptr) {
|
|
SDL_Log("Error al crear textura: %s", SDL_GetError());
|
|
SDL_DestroySurface(text_surface);
|
|
return;
|
|
}
|
|
|
|
// Preparar rectángulo de destino
|
|
SDL_FRect dest_rect;
|
|
dest_rect.x = static_cast<float>(x);
|
|
dest_rect.y = static_cast<float>(y);
|
|
dest_rect.w = static_cast<float>(text_surface->w);
|
|
dest_rect.h = static_cast<float>(text_surface->h);
|
|
|
|
// Renderizar la textura
|
|
SDL_RenderTexture(renderer_, text_texture, nullptr, &dest_rect);
|
|
|
|
// Limpiar recursos
|
|
SDL_DestroyTexture(text_texture);
|
|
SDL_DestroySurface(text_surface);
|
|
}
|
|
|
|
void TextRenderer::print(int x, int y, const std::string& text, uint8_t r, uint8_t g, uint8_t b) {
|
|
print(x, y, text.c_str(), r, g, b);
|
|
}
|
|
|
|
void TextRenderer::printPhysical(int logical_x, int logical_y, const char* text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y) {
|
|
if (!isInitialized() || text == nullptr || text[0] == '\0') {
|
|
return;
|
|
}
|
|
|
|
// Convertir coordenadas lógicas a físicas
|
|
int physical_x = static_cast<int>(logical_x * scale_x);
|
|
int physical_y = static_cast<int>(logical_y * scale_y);
|
|
|
|
// Crear superficie con el texto renderizado
|
|
SDL_Color color = {r, g, b, 255};
|
|
SDL_Surface* text_surface = nullptr;
|
|
|
|
if (use_antialiasing_) {
|
|
text_surface = TTF_RenderText_Blended(font_, text, strlen(text), color);
|
|
} else {
|
|
text_surface = TTF_RenderText_Solid(font_, text, strlen(text), color);
|
|
}
|
|
|
|
if (text_surface == nullptr) {
|
|
SDL_Log("Error al renderizar texto: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
// Crear textura desde la superficie
|
|
SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer_, text_surface);
|
|
|
|
if (text_texture == nullptr) {
|
|
SDL_Log("Error al crear textura: %s", SDL_GetError());
|
|
SDL_DestroySurface(text_surface);
|
|
return;
|
|
}
|
|
|
|
// Renderizar en coordenadas físicas (bypass presentación lógica)
|
|
// Usar SDL_RenderTexture con coordenadas absolutas de ventana
|
|
SDL_FRect dest_rect;
|
|
dest_rect.x = static_cast<float>(physical_x);
|
|
dest_rect.y = static_cast<float>(physical_y);
|
|
dest_rect.w = static_cast<float>(text_surface->w);
|
|
dest_rect.h = static_cast<float>(text_surface->h);
|
|
|
|
// Deshabilitar temporalmente presentación lógica para renderizar en píxeles físicos
|
|
int logical_w = 0, logical_h = 0;
|
|
SDL_RendererLogicalPresentation presentation_mode;
|
|
SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &presentation_mode);
|
|
|
|
// Renderizar sin presentación lógica (coordenadas absolutas)
|
|
SDL_SetRenderLogicalPresentation(renderer_, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
|
|
SDL_RenderTexture(renderer_, text_texture, nullptr, &dest_rect);
|
|
|
|
// Restaurar presentación lógica
|
|
SDL_SetRenderLogicalPresentation(renderer_, logical_w, logical_h, presentation_mode);
|
|
|
|
// Limpiar recursos
|
|
SDL_DestroyTexture(text_texture);
|
|
SDL_DestroySurface(text_surface);
|
|
}
|
|
|
|
void TextRenderer::printPhysical(int logical_x, int logical_y, const std::string& text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y) {
|
|
printPhysical(logical_x, logical_y, text.c_str(), r, g, b, scale_x, scale_y);
|
|
}
|
|
|
|
void TextRenderer::printAbsolute(int physical_x, int physical_y, const char* text, SDL_Color color) {
|
|
if (!isInitialized() || text == nullptr || text[0] == '\0') {
|
|
return;
|
|
}
|
|
|
|
// Crear superficie con el texto renderizado
|
|
SDL_Surface* text_surface = nullptr;
|
|
|
|
if (use_antialiasing_) {
|
|
text_surface = TTF_RenderText_Blended(font_, text, strlen(text), color);
|
|
} else {
|
|
text_surface = TTF_RenderText_Solid(font_, text, strlen(text), color);
|
|
}
|
|
|
|
if (text_surface == nullptr) {
|
|
SDL_Log("Error al renderizar texto: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
// Crear textura desde la superficie
|
|
SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer_, text_surface);
|
|
|
|
if (text_texture == nullptr) {
|
|
SDL_Log("Error al crear textura: %s", SDL_GetError());
|
|
SDL_DestroySurface(text_surface);
|
|
return;
|
|
}
|
|
|
|
// Configurar alpha blending si el color tiene transparencia
|
|
if (color.a < 255) {
|
|
SDL_SetTextureBlendMode(text_texture, SDL_BLENDMODE_BLEND);
|
|
SDL_SetTextureAlphaModFloat(text_texture, color.a / 255.0f);
|
|
}
|
|
|
|
// Obtener viewport ANTES de deshabilitar presentación lógica
|
|
// En modo letterbox (F3), SDL crea un viewport con offset para centrar la imagen
|
|
SDL_Rect viewport;
|
|
SDL_GetRenderViewport(renderer_, &viewport);
|
|
|
|
// Preparar rectángulo de destino en coordenadas físicas absolutas
|
|
// Aplicar offset del viewport para que el texto se pinte dentro del área visible
|
|
SDL_FRect dest_rect;
|
|
dest_rect.x = static_cast<float>(physical_x + viewport.x);
|
|
dest_rect.y = static_cast<float>(physical_y + viewport.y);
|
|
dest_rect.w = static_cast<float>(text_surface->w);
|
|
dest_rect.h = static_cast<float>(text_surface->h);
|
|
|
|
// Deshabilitar temporalmente presentación lógica para renderizar en píxeles físicos
|
|
int logical_w = 0, logical_h = 0;
|
|
SDL_RendererLogicalPresentation presentation_mode;
|
|
SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &presentation_mode);
|
|
|
|
// Renderizar sin presentación lógica (coordenadas absolutas con offset de viewport)
|
|
SDL_SetRenderLogicalPresentation(renderer_, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
|
|
SDL_RenderTexture(renderer_, text_texture, nullptr, &dest_rect);
|
|
|
|
// Restaurar presentación lógica
|
|
SDL_SetRenderLogicalPresentation(renderer_, logical_w, logical_h, presentation_mode);
|
|
|
|
// Limpiar recursos
|
|
SDL_DestroyTexture(text_texture);
|
|
SDL_DestroySurface(text_surface);
|
|
}
|
|
|
|
void TextRenderer::printAbsolute(int physical_x, int physical_y, const std::string& text, SDL_Color color) {
|
|
printAbsolute(physical_x, physical_y, text.c_str(), color);
|
|
}
|
|
|
|
int TextRenderer::getTextWidth(const char* text) {
|
|
if (!isInitialized() || text == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
int width = 0;
|
|
int height = 0;
|
|
if (!TTF_GetStringSize(font_, text, strlen(text), &width, &height)) {
|
|
return 0;
|
|
}
|
|
return width;
|
|
}
|
|
|
|
int TextRenderer::getTextWidthPhysical(const char* text) {
|
|
// Retorna el ancho REAL en píxeles físicos (sin escalado lógico)
|
|
// Idéntico a getTextWidth() pero semánticamente diferente:
|
|
// - Este método se usa cuando se necesita el ancho REAL de la fuente
|
|
// - Útil para calcular dimensiones de UI en coordenadas físicas absolutas
|
|
if (!isInitialized() || text == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
int width = 0;
|
|
int height = 0;
|
|
if (!TTF_GetStringSize(font_, text, strlen(text), &width, &height)) {
|
|
return 0;
|
|
}
|
|
return width; // Ancho real de la textura generada por TTF
|
|
}
|
|
|
|
int TextRenderer::getTextHeight() {
|
|
if (!isInitialized()) {
|
|
return 0;
|
|
}
|
|
|
|
return TTF_GetFontHeight(font_);
|
|
}
|