Files
vibe3_physics/source/logo_scaler.cpp
Sergio Valor ad3f5a00e4 feat: Sistema de pre-escalado de logos con stb_image_resize2
Implementa pre-escalado de alta calidad para eliminar artefactos de
escalado dinámico de SDL y mejorar la nitidez visual de los logos.

Características:
- 4 texturas pre-escaladas (2 logos × 2 resoluciones: base + nativa)
- Detección automática de resolución nativa del monitor
- Switching dinámico entre texturas al cambiar resolución (F4)
- Renderizado 1:1 sin escalado adicional (máxima calidad)
- Algoritmo Mitchell en espacio sRGB (balance calidad/velocidad)
- Todo en RAM, sin archivos temporales

Archivos nuevos:
- source/external/stb_image_resize2.h: Biblioteca de escalado stb
- source/logo_scaler.h/cpp: Clase helper para pre-escalado

Cambios en AppLogo:
- Reemplazadas shared_ptr<Texture> por SDL_Texture* raw pointers
- initialize(): Pre-escala logos a 2 resoluciones al inicio
- updateScreenSize(): Cambia entre texturas según resolución
- render(): Simplificado, siempre usa renderWithGeometry()
- ~AppLogo(): Libera 4 texturas SDL manualmente

El sistema detecta la resolución nativa al inicio y crea versiones
optimizadas. Al presionar F4, cambia automáticamente a la textura
nativa para calidad perfecta en fullscreen.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 10:36:26 +02:00

145 lines
5.3 KiB
C++

#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "logo_scaler.h"
#include <SDL3/SDL_error.h> // Para SDL_GetError
#include <SDL3/SDL_log.h> // Para SDL_Log
#include <SDL3/SDL_pixels.h> // Para SDL_PixelFormat
#include <SDL3/SDL_render.h> // Para SDL_CreateTexture
#include <SDL3/SDL_surface.h> // Para SDL_CreateSurfaceFrom
#include <SDL3/SDL_video.h> // Para SDL_GetDisplays
#include <cstdlib> // Para free()
#include <iostream> // Para std::cout
#include "external/stb_image.h" // Para stbi_load, stbi_image_free
#include "external/stb_image_resize2.h" // Para stbir_resize_uint8_srgb
// ============================================================================
// Detectar resolución nativa del monitor principal
// ============================================================================
bool LogoScaler::detectNativeResolution(int& native_width, int& native_height) {
int num_displays = 0;
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
if (displays == nullptr || num_displays == 0) {
SDL_Log("Error al obtener displays: %s", SDL_GetError());
return false;
}
// Obtener resolución del display principal (displays[0])
const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
if (dm == nullptr) {
SDL_Log("Error al obtener modo del display: %s", SDL_GetError());
SDL_free(displays);
return false;
}
native_width = dm->w;
native_height = dm->h;
SDL_free(displays);
std::cout << "Resolución nativa detectada: " << native_width << "x" << native_height << std::endl;
return true;
}
// ============================================================================
// Cargar PNG y escalar al tamaño especificado
// ============================================================================
unsigned char* LogoScaler::loadAndScale(const std::string& path,
int target_width, int target_height,
int& out_width, int& out_height) {
// 1. Cargar imagen original con stb_image
int orig_width, orig_height, orig_channels;
unsigned char* orig_data = stbi_load(path.c_str(), &orig_width, &orig_height, &orig_channels, STBI_rgb_alpha);
if (orig_data == nullptr) {
SDL_Log("Error al cargar imagen %s: %s", path.c_str(), stbi_failure_reason());
return nullptr;
}
std::cout << "Imagen cargada: " << path << " (" << orig_width << "x" << orig_height << ")" << std::endl;
// 2. Calcular tamaño final manteniendo aspect ratio
// El alto está fijado por target_height (APPLOGO_HEIGHT_PERCENT)
// Calcular ancho proporcional al aspect ratio original
float aspect_ratio = static_cast<float>(orig_width) / static_cast<float>(orig_height);
out_width = static_cast<int>(target_height * aspect_ratio);
out_height = target_height;
std::cout << " Escalando a: " << out_width << "x" << out_height << std::endl;
// 3. Alocar buffer para imagen escalada (RGBA = 4 bytes por píxel)
unsigned char* scaled_data = static_cast<unsigned char*>(malloc(out_width * out_height * 4));
if (scaled_data == nullptr) {
SDL_Log("Error al alocar memoria para imagen escalada");
stbi_image_free(orig_data);
return nullptr;
}
// 4. Escalar con stb_image_resize2 (algoritmo Mitchell, espacio sRGB)
// La función devuelve el puntero de salida, o nullptr si falla
unsigned char* result = stbir_resize_uint8_srgb(
orig_data, orig_width, orig_height, 0, // Input
scaled_data, out_width, out_height, 0, // Output
STBIR_RGBA // Formato píxel
);
// Liberar imagen original (ya no la necesitamos)
stbi_image_free(orig_data);
if (result == nullptr) {
SDL_Log("Error al escalar imagen");
free(scaled_data);
return nullptr;
}
std::cout << " Escalado completado correctamente" << std::endl;
return scaled_data;
}
// ============================================================================
// Crear textura SDL desde buffer RGBA
// ============================================================================
SDL_Texture* LogoScaler::createTextureFromBuffer(SDL_Renderer* renderer,
unsigned char* data,
int width, int height) {
if (renderer == nullptr || data == nullptr || width <= 0 || height <= 0) {
SDL_Log("Parámetros inválidos para createTextureFromBuffer");
return nullptr;
}
// 1. Crear surface SDL desde buffer RGBA
int pitch = width * 4; // 4 bytes por píxel (RGBA)
SDL_PixelFormat pixel_format = SDL_PIXELFORMAT_RGBA32;
SDL_Surface* surface = SDL_CreateSurfaceFrom(
width, height,
pixel_format,
data,
pitch
);
if (surface == nullptr) {
SDL_Log("Error al crear surface: %s", SDL_GetError());
return nullptr;
}
// 2. Crear textura desde surface
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
if (texture == nullptr) {
SDL_Log("Error al crear textura: %s", SDL_GetError());
SDL_DestroySurface(surface);
return nullptr;
}
// 3. Liberar surface (la textura ya tiene los datos)
SDL_DestroySurface(surface);
return texture;
}