Compare commits
2 Commits
c91cb1ca56
...
7609b9ef5c
| Author | SHA1 | Date | |
|---|---|---|---|
| 7609b9ef5c | |||
| ad3f5a00e4 |
@@ -1,77 +1,194 @@
|
||||
#include "app_logo.h"
|
||||
|
||||
#include <SDL3/SDL_render.h> // for SDL_SCALEMODE_LINEAR, SDL_RenderGeometry
|
||||
#include <SDL3/SDL_render.h> // for SDL_DestroyTexture, SDL_RenderGeometry, SDL_SetTextureAlphaMod
|
||||
#include <cmath> // for powf, sinf, cosf
|
||||
#include <cstdlib> // for free()
|
||||
#include <iostream> // for std::cout
|
||||
|
||||
#include "external/sprite.h" // for Sprite
|
||||
#include "external/texture.h" // for Texture
|
||||
#include "logo_scaler.h" // for LogoScaler
|
||||
#include "defines.h" // for APPLOGO_HEIGHT_PERCENT, getResourcesDirectory
|
||||
|
||||
// ============================================================================
|
||||
// Destructor - Liberar las 4 texturas SDL
|
||||
// ============================================================================
|
||||
|
||||
AppLogo::~AppLogo() {
|
||||
if (logo1_base_texture_) {
|
||||
SDL_DestroyTexture(logo1_base_texture_);
|
||||
logo1_base_texture_ = nullptr;
|
||||
}
|
||||
if (logo1_native_texture_) {
|
||||
SDL_DestroyTexture(logo1_native_texture_);
|
||||
logo1_native_texture_ = nullptr;
|
||||
}
|
||||
if (logo2_base_texture_) {
|
||||
SDL_DestroyTexture(logo2_base_texture_);
|
||||
logo2_base_texture_ = nullptr;
|
||||
}
|
||||
if (logo2_native_texture_) {
|
||||
SDL_DestroyTexture(logo2_native_texture_);
|
||||
logo2_native_texture_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Inicialización - Pre-escalar logos a 2 resoluciones (base y nativa)
|
||||
// ============================================================================
|
||||
|
||||
bool AppLogo::initialize(SDL_Renderer* renderer, int screen_width, int screen_height) {
|
||||
renderer_ = renderer;
|
||||
base_screen_width_ = screen_width;
|
||||
base_screen_height_ = screen_height;
|
||||
screen_width_ = screen_width;
|
||||
screen_height_ = screen_height;
|
||||
|
||||
std::string resources_dir = getResourcesDirectory();
|
||||
|
||||
// ========================================================================
|
||||
// Cargar LOGO1 desde data/logo/logo.png
|
||||
// 1. Detectar resolución nativa del monitor
|
||||
// ========================================================================
|
||||
if (!LogoScaler::detectNativeResolution(native_screen_width_, native_screen_height_)) {
|
||||
std::cout << "No se pudo detectar resolución nativa, usando solo base" << std::endl;
|
||||
// Fallback: usar resolución base como nativa
|
||||
native_screen_width_ = screen_width;
|
||||
native_screen_height_ = screen_height;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 2. Calcular alturas finales para ambas resoluciones
|
||||
// ========================================================================
|
||||
int logo_base_target_height = static_cast<int>(base_screen_height_ * APPLOGO_HEIGHT_PERCENT);
|
||||
int logo_native_target_height = static_cast<int>(native_screen_height_ * APPLOGO_HEIGHT_PERCENT);
|
||||
|
||||
std::cout << "Pre-escalando logos:" << std::endl;
|
||||
std::cout << " Base: " << base_screen_width_ << "x" << base_screen_height_
|
||||
<< " (altura logo: " << logo_base_target_height << "px)" << std::endl;
|
||||
std::cout << " Nativa: " << native_screen_width_ << "x" << native_screen_height_
|
||||
<< " (altura logo: " << logo_native_target_height << "px)" << std::endl;
|
||||
|
||||
// ========================================================================
|
||||
// 3. Cargar y escalar LOGO1 (data/logo/logo.png) a 2 versiones
|
||||
// ========================================================================
|
||||
std::string logo1_path = resources_dir + "/data/logo/logo.png";
|
||||
logo1_texture_ = std::make_shared<Texture>(renderer, logo1_path);
|
||||
if (logo1_texture_->getWidth() == 0 || logo1_texture_->getHeight() == 0) {
|
||||
// Error al cargar textura logo1
|
||||
|
||||
// 3a. Versión BASE de logo1
|
||||
unsigned char* logo1_base_data = LogoScaler::loadAndScale(
|
||||
logo1_path,
|
||||
0, // width calculado automáticamente por aspect ratio
|
||||
logo_base_target_height,
|
||||
logo1_base_width_,
|
||||
logo1_base_height_
|
||||
);
|
||||
if (logo1_base_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo1 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configurar filtrado LINEAR para suavizado
|
||||
logo1_texture_->setScaleMode(SDL_SCALEMODE_LINEAR);
|
||||
logo1_base_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo1_base_data, logo1_base_width_, logo1_base_height_
|
||||
);
|
||||
free(logo1_base_data); // Liberar buffer temporal
|
||||
|
||||
// Crear sprite con la textura
|
||||
logo1_sprite_ = std::make_unique<Sprite>(logo1_texture_);
|
||||
if (logo1_base_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo1 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configurar el clip para que use toda la textura
|
||||
float logo1_width = static_cast<float>(logo1_texture_->getWidth());
|
||||
float logo1_height = static_cast<float>(logo1_texture_->getHeight());
|
||||
logo1_sprite_->setClip({0.0f, 0.0f, logo1_width, logo1_height});
|
||||
// Habilitar alpha blending
|
||||
SDL_SetTextureBlendMode(logo1_base_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// 3b. Versión NATIVA de logo1
|
||||
unsigned char* logo1_native_data = LogoScaler::loadAndScale(
|
||||
logo1_path,
|
||||
0, // width calculado automáticamente
|
||||
logo_native_target_height,
|
||||
logo1_native_width_,
|
||||
logo1_native_height_
|
||||
);
|
||||
if (logo1_native_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo1 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo1_native_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo1_native_data, logo1_native_width_, logo1_native_height_
|
||||
);
|
||||
free(logo1_native_data);
|
||||
|
||||
if (logo1_native_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo1 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetTextureBlendMode(logo1_native_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// ========================================================================
|
||||
// Cargar LOGO2 desde data/logo/logo2.png
|
||||
// 4. Cargar y escalar LOGO2 (data/logo/logo2.png) a 2 versiones
|
||||
// ========================================================================
|
||||
std::string logo2_path = resources_dir + "/data/logo/logo2.png";
|
||||
logo2_texture_ = std::make_shared<Texture>(renderer, logo2_path);
|
||||
if (logo2_texture_->getWidth() == 0 || logo2_texture_->getHeight() == 0) {
|
||||
// Error al cargar textura logo2
|
||||
|
||||
// 4a. Versión BASE de logo2
|
||||
unsigned char* logo2_base_data = LogoScaler::loadAndScale(
|
||||
logo2_path,
|
||||
0,
|
||||
logo_base_target_height,
|
||||
logo2_base_width_,
|
||||
logo2_base_height_
|
||||
);
|
||||
if (logo2_base_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo2 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configurar filtrado LINEAR para suavizado
|
||||
logo2_texture_->setScaleMode(SDL_SCALEMODE_LINEAR);
|
||||
logo2_base_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo2_base_data, logo2_base_width_, logo2_base_height_
|
||||
);
|
||||
free(logo2_base_data);
|
||||
|
||||
// Crear sprite con la textura
|
||||
logo2_sprite_ = std::make_unique<Sprite>(logo2_texture_);
|
||||
if (logo2_base_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo2 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configurar el clip para que use toda la textura
|
||||
float logo2_width = static_cast<float>(logo2_texture_->getWidth());
|
||||
float logo2_height = static_cast<float>(logo2_texture_->getHeight());
|
||||
logo2_sprite_->setClip({0.0f, 0.0f, logo2_width, logo2_height});
|
||||
SDL_SetTextureBlendMode(logo2_base_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// 4b. Versión NATIVA de logo2
|
||||
unsigned char* logo2_native_data = LogoScaler::loadAndScale(
|
||||
logo2_path,
|
||||
0,
|
||||
logo_native_target_height,
|
||||
logo2_native_width_,
|
||||
logo2_native_height_
|
||||
);
|
||||
if (logo2_native_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo2 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo2_native_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo2_native_data, logo2_native_width_, logo2_native_height_
|
||||
);
|
||||
free(logo2_native_data);
|
||||
|
||||
if (logo2_native_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo2 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetTextureBlendMode(logo2_native_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// ========================================================================
|
||||
// Calcular tamaño base (asumimos mismo tamaño para ambos logos)
|
||||
// El logo debe tener una altura de APPLOGO_HEIGHT_PERCENT (40%) de la pantalla
|
||||
// 5. Inicialmente usar texturas BASE (la resolución de inicio)
|
||||
// ========================================================================
|
||||
float target_height = screen_height_ * APPLOGO_HEIGHT_PERCENT;
|
||||
float scale = target_height / logo1_height;
|
||||
logo1_current_texture_ = logo1_base_texture_;
|
||||
logo1_current_width_ = logo1_base_width_;
|
||||
logo1_current_height_ = logo1_base_height_;
|
||||
|
||||
base_width_ = logo1_width * scale;
|
||||
base_height_ = target_height; // = logo1_height * scale
|
||||
|
||||
// Aplicar escala inicial a ambos sprites
|
||||
logo1_sprite_->setSize(base_width_, base_height_);
|
||||
logo2_sprite_->setSize(base_width_, base_height_);
|
||||
|
||||
// Posicionar ambos logos en el centro del cuadrante inferior derecho (superpuestos)
|
||||
updateLogoPosition();
|
||||
logo2_current_texture_ = logo2_base_texture_;
|
||||
logo2_current_width_ = logo2_base_width_;
|
||||
logo2_current_height_ = logo2_base_height_;
|
||||
|
||||
std::cout << "Logos pre-escalados exitosamente (4 texturas creadas)" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -80,7 +197,8 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
if (current_mode == AppMode::SANDBOX) {
|
||||
state_ = AppLogoState::HIDDEN;
|
||||
timer_ = 0.0f;
|
||||
current_alpha_ = 0;
|
||||
logo1_alpha_ = 0;
|
||||
logo2_alpha_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -93,22 +211,27 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
if (timer_ >= APPLOGO_DISPLAY_INTERVAL) {
|
||||
state_ = AppLogoState::FADE_IN;
|
||||
timer_ = 0.0f;
|
||||
current_alpha_ = 0;
|
||||
// Elegir animaciones de entrada aleatorias (independientes para cada logo)
|
||||
logo1_entry_animation_ = getRandomAnimation();
|
||||
logo2_entry_animation_ = getRandomAnimation();
|
||||
logo1_alpha_ = 0;
|
||||
logo2_alpha_ = 0;
|
||||
// Elegir UNA animación aleatoria (misma para ambos logos, misma entrada y salida)
|
||||
current_animation_ = getRandomAnimation();
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoState::FADE_IN:
|
||||
// Fade in: alpha de 0 a 255, animaciones independientes para logo1 y logo2
|
||||
// Fade in: alpha de 0 a 255, con Logo 2 retrasado 0.25s
|
||||
{
|
||||
float fade_progress = timer_ / APPLOGO_FADE_DURATION;
|
||||
if (fade_progress >= 1.0f) {
|
||||
// Fade in completado
|
||||
// Calcular progreso de cada logo (Logo 2 con retraso)
|
||||
float fade_progress_logo1 = timer_ / APPLOGO_FADE_DURATION;
|
||||
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_FADE_DURATION);
|
||||
|
||||
// Verificar si fade in completado (cuando logo2 también termina)
|
||||
if (fade_progress_logo2 >= 1.0f) {
|
||||
// Fade in completado para ambos logos
|
||||
state_ = AppLogoState::VISIBLE;
|
||||
timer_ = 0.0f;
|
||||
current_alpha_ = 255;
|
||||
logo1_alpha_ = 255;
|
||||
logo2_alpha_ = 255;
|
||||
// Resetear variables de ambos logos
|
||||
logo1_scale_ = 1.0f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
@@ -119,59 +242,24 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
} else {
|
||||
// Interpolar alpha linealmente (0 → 255) - compartido
|
||||
current_alpha_ = static_cast<int>(fade_progress * 255.0f);
|
||||
// Interpolar alpha con retraso + easing para suavidad
|
||||
float eased_prog1 = easeInOutQuad(std::min(1.0f, fade_progress_logo1));
|
||||
float eased_prog2 = easeInOutQuad(std::min(1.0f, fade_progress_logo2));
|
||||
logo1_alpha_ = static_cast<int>(eased_prog1 * 255.0f);
|
||||
logo2_alpha_ = static_cast<int>(eased_prog2 * 255.0f);
|
||||
|
||||
// ================================================================
|
||||
// Aplicar animación de LOGO1 según logo1_entry_animation_
|
||||
// Aplicar MISMA animación (current_animation_) a ambos logos
|
||||
// con sus respectivos progresos
|
||||
// ================================================================
|
||||
switch (logo1_entry_animation_) {
|
||||
switch (current_animation_) {
|
||||
case AppLogoAnimationType::ZOOM_ONLY:
|
||||
logo1_scale_ = 1.2f - (fade_progress * 0.2f);
|
||||
logo1_scale_ = 1.2f - (std::min(1.0f, fade_progress_logo1) * 0.2f);
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ELASTIC_STICK:
|
||||
{
|
||||
float elastic_t = easeOutElastic(fade_progress);
|
||||
logo1_scale_ = 1.2f - (elastic_t * 0.2f);
|
||||
float squash_t = easeOutBack(fade_progress);
|
||||
logo1_squash_y_ = 0.6f + (squash_t * 0.4f);
|
||||
logo1_stretch_x_ = 1.0f + (1.0f - logo1_squash_y_) * 0.5f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ROTATE_SPIRAL:
|
||||
{
|
||||
float ease_t = easeInOutQuad(fade_progress);
|
||||
logo1_scale_ = 0.3f + (ease_t * 0.7f);
|
||||
logo1_rotation_ = (1.0f - fade_progress) * 6.28f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::BOUNCE_SQUASH:
|
||||
{
|
||||
float bounce_t = easeOutBounce(fade_progress);
|
||||
logo1_scale_ = 1.0f;
|
||||
float squash_amount = (1.0f - bounce_t) * 0.3f;
|
||||
logo1_squash_y_ = 1.0f - squash_amount;
|
||||
logo1_stretch_x_ = 1.0f + squash_amount * 0.5f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Aplicar animación de LOGO2 según logo2_entry_animation_
|
||||
// ================================================================
|
||||
switch (logo2_entry_animation_) {
|
||||
case AppLogoAnimationType::ZOOM_ONLY:
|
||||
logo2_scale_ = 1.2f - (fade_progress * 0.2f);
|
||||
logo2_scale_ = 1.2f - (std::min(1.0f, fade_progress_logo2) * 0.2f);
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
@@ -179,10 +267,19 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
|
||||
case AppLogoAnimationType::ELASTIC_STICK:
|
||||
{
|
||||
float elastic_t = easeOutElastic(fade_progress);
|
||||
logo2_scale_ = 1.2f - (elastic_t * 0.2f);
|
||||
float squash_t = easeOutBack(fade_progress);
|
||||
logo2_squash_y_ = 0.6f + (squash_t * 0.4f);
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float elastic_t1 = easeOutElastic(prog1);
|
||||
logo1_scale_ = 1.2f - (elastic_t1 * 0.2f);
|
||||
float squash_t1 = easeOutBack(prog1);
|
||||
logo1_squash_y_ = 0.6f + (squash_t1 * 0.4f);
|
||||
logo1_stretch_x_ = 1.0f + (1.0f - logo1_squash_y_) * 0.5f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float elastic_t2 = easeOutElastic(prog2);
|
||||
logo2_scale_ = 1.2f - (elastic_t2 * 0.2f);
|
||||
float squash_t2 = easeOutBack(prog2);
|
||||
logo2_squash_y_ = 0.6f + (squash_t2 * 0.4f);
|
||||
logo2_stretch_x_ = 1.0f + (1.0f - logo2_squash_y_) * 0.5f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
}
|
||||
@@ -190,9 +287,17 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
|
||||
case AppLogoAnimationType::ROTATE_SPIRAL:
|
||||
{
|
||||
float ease_t = easeInOutQuad(fade_progress);
|
||||
logo2_scale_ = 0.3f + (ease_t * 0.7f);
|
||||
logo2_rotation_ = (1.0f - fade_progress) * 6.28f;
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float ease_t1 = easeInOutQuad(prog1);
|
||||
logo1_scale_ = 0.3f + (ease_t1 * 0.7f);
|
||||
logo1_rotation_ = (1.0f - prog1) * 6.28f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float ease_t2 = easeInOutQuad(prog2);
|
||||
logo2_scale_ = 0.3f + (ease_t2 * 0.7f);
|
||||
logo2_rotation_ = (1.0f - prog2) * 6.28f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
}
|
||||
@@ -200,11 +305,20 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
|
||||
case AppLogoAnimationType::BOUNCE_SQUASH:
|
||||
{
|
||||
float bounce_t = easeOutBounce(fade_progress);
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float bounce_t1 = easeOutBounce(prog1);
|
||||
logo1_scale_ = 1.0f;
|
||||
float squash_amount1 = (1.0f - bounce_t1) * 0.3f;
|
||||
logo1_squash_y_ = 1.0f - squash_amount1;
|
||||
logo1_stretch_x_ = 1.0f + squash_amount1 * 0.5f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float bounce_t2 = easeOutBounce(prog2);
|
||||
logo2_scale_ = 1.0f;
|
||||
float squash_amount = (1.0f - bounce_t) * 0.3f;
|
||||
logo2_squash_y_ = 1.0f - squash_amount;
|
||||
logo2_stretch_x_ = 1.0f + squash_amount * 0.5f;
|
||||
float squash_amount2 = (1.0f - bounce_t2) * 0.3f;
|
||||
logo2_squash_y_ = 1.0f - squash_amount2;
|
||||
logo2_stretch_x_ = 1.0f + squash_amount2 * 0.5f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
@@ -218,22 +332,26 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
if (timer_ >= APPLOGO_DISPLAY_DURATION) {
|
||||
state_ = AppLogoState::FADE_OUT;
|
||||
timer_ = 0.0f;
|
||||
current_alpha_ = 255;
|
||||
// Elegir animaciones de salida aleatorias (independientes para cada logo)
|
||||
logo1_exit_animation_ = getRandomAnimation();
|
||||
logo2_exit_animation_ = getRandomAnimation();
|
||||
logo1_alpha_ = 255;
|
||||
logo2_alpha_ = 255;
|
||||
// NO elegir nueva animación - reutilizar current_animation_ (simetría entrada/salida)
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoState::FADE_OUT:
|
||||
// Fade out: alpha de 255 a 0, animaciones independientes para logo1 y logo2
|
||||
// Fade out: alpha de 255 a 0, con Logo 2 retrasado 0.25s (misma animación que entrada)
|
||||
{
|
||||
float fade_progress = timer_ / APPLOGO_FADE_DURATION;
|
||||
if (fade_progress >= 1.0f) {
|
||||
// Calcular progreso de cada logo (Logo 2 con retraso)
|
||||
float fade_progress_logo1 = timer_ / APPLOGO_FADE_DURATION;
|
||||
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_FADE_DURATION);
|
||||
|
||||
// Verificar si fade out completado (cuando logo2 también termina)
|
||||
if (fade_progress_logo2 >= 1.0f) {
|
||||
// Fade out completado, volver a HIDDEN
|
||||
state_ = AppLogoState::HIDDEN;
|
||||
timer_ = 0.0f;
|
||||
current_alpha_ = 0;
|
||||
logo1_alpha_ = 0;
|
||||
logo2_alpha_ = 0;
|
||||
// Resetear variables de ambos logos
|
||||
logo1_scale_ = 1.0f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
@@ -244,94 +362,88 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
} else {
|
||||
// Interpolar alpha linealmente (255 → 0) - compartido
|
||||
current_alpha_ = static_cast<int>((1.0f - fade_progress) * 255.0f);
|
||||
// Interpolar alpha con retraso + easing para suavidad (255 → 0)
|
||||
float eased_prog1 = easeInOutQuad(std::min(1.0f, fade_progress_logo1));
|
||||
float eased_prog2 = easeInOutQuad(std::min(1.0f, fade_progress_logo2));
|
||||
logo1_alpha_ = static_cast<int>((1.0f - eased_prog1) * 255.0f);
|
||||
logo2_alpha_ = static_cast<int>((1.0f - eased_prog2) * 255.0f);
|
||||
|
||||
// ================================================================
|
||||
// Aplicar animación de LOGO1 según logo1_exit_animation_
|
||||
// Aplicar MISMA animación (current_animation_) de forma invertida
|
||||
// ================================================================
|
||||
switch (logo1_exit_animation_) {
|
||||
switch (current_animation_) {
|
||||
case AppLogoAnimationType::ZOOM_ONLY:
|
||||
logo1_scale_ = 1.0f + (fade_progress * 0.2f);
|
||||
logo1_scale_ = 1.0f + (std::min(1.0f, fade_progress_logo1) * 0.2f);
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
logo2_scale_ = 1.0f + (std::min(1.0f, fade_progress_logo2) * 0.2f);
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ELASTIC_STICK:
|
||||
logo1_scale_ = 1.0f + (fade_progress * fade_progress * 0.2f);
|
||||
logo1_squash_y_ = 1.0f + (fade_progress * 0.3f);
|
||||
logo1_stretch_x_ = 1.0f - (fade_progress * 0.2f);
|
||||
logo1_rotation_ = fade_progress * 0.1f;
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
logo1_scale_ = 1.0f + (prog1 * prog1 * 0.2f);
|
||||
logo1_squash_y_ = 1.0f + (prog1 * 0.3f);
|
||||
logo1_stretch_x_ = 1.0f - (prog1 * 0.2f);
|
||||
logo1_rotation_ = prog1 * 0.1f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
logo2_scale_ = 1.0f + (prog2 * prog2 * 0.2f);
|
||||
logo2_squash_y_ = 1.0f + (prog2 * 0.3f);
|
||||
logo2_stretch_x_ = 1.0f - (prog2 * 0.2f);
|
||||
logo2_rotation_ = prog2 * 0.1f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ROTATE_SPIRAL:
|
||||
{
|
||||
float ease_t = easeInOutQuad(fade_progress);
|
||||
logo1_scale_ = 1.0f - (ease_t * 0.7f);
|
||||
logo1_rotation_ = fade_progress * 6.28f;
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float ease_t1 = easeInOutQuad(prog1);
|
||||
logo1_scale_ = 1.0f - (ease_t1 * 0.7f);
|
||||
logo1_rotation_ = prog1 * 6.28f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float ease_t2 = easeInOutQuad(prog2);
|
||||
logo2_scale_ = 1.0f - (ease_t2 * 0.7f);
|
||||
logo2_rotation_ = prog2 * 6.28f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::BOUNCE_SQUASH:
|
||||
{
|
||||
if (fade_progress < 0.2f) {
|
||||
float squash_t = fade_progress / 0.2f;
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
if (prog1 < 0.2f) {
|
||||
float squash_t = prog1 / 0.2f;
|
||||
logo1_squash_y_ = 1.0f - (squash_t * 0.3f);
|
||||
logo1_stretch_x_ = 1.0f + (squash_t * 0.2f);
|
||||
} else {
|
||||
float jump_t = (fade_progress - 0.2f) / 0.8f;
|
||||
float jump_t = (prog1 - 0.2f) / 0.8f;
|
||||
logo1_squash_y_ = 0.7f + (jump_t * 0.5f);
|
||||
logo1_stretch_x_ = 1.2f - (jump_t * 0.2f);
|
||||
}
|
||||
logo1_scale_ = 1.0f + (fade_progress * 0.3f);
|
||||
logo1_scale_ = 1.0f + (prog1 * 0.3f);
|
||||
logo1_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Aplicar animación de LOGO2 según logo2_exit_animation_
|
||||
// ================================================================
|
||||
switch (logo2_exit_animation_) {
|
||||
case AppLogoAnimationType::ZOOM_ONLY:
|
||||
logo2_scale_ = 1.0f + (fade_progress * 0.2f);
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ELASTIC_STICK:
|
||||
logo2_scale_ = 1.0f + (fade_progress * fade_progress * 0.2f);
|
||||
logo2_squash_y_ = 1.0f + (fade_progress * 0.3f);
|
||||
logo2_stretch_x_ = 1.0f - (fade_progress * 0.2f);
|
||||
logo2_rotation_ = fade_progress * 0.1f;
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ROTATE_SPIRAL:
|
||||
{
|
||||
float ease_t = easeInOutQuad(fade_progress);
|
||||
logo2_scale_ = 1.0f - (ease_t * 0.7f);
|
||||
logo2_rotation_ = fade_progress * 6.28f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::BOUNCE_SQUASH:
|
||||
{
|
||||
if (fade_progress < 0.2f) {
|
||||
float squash_t = fade_progress / 0.2f;
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
if (prog2 < 0.2f) {
|
||||
float squash_t = prog2 / 0.2f;
|
||||
logo2_squash_y_ = 1.0f - (squash_t * 0.3f);
|
||||
logo2_stretch_x_ = 1.0f + (squash_t * 0.2f);
|
||||
} else {
|
||||
float jump_t = (fade_progress - 0.2f) / 0.8f;
|
||||
float jump_t = (prog2 - 0.2f) / 0.8f;
|
||||
logo2_squash_y_ = 0.7f + (jump_t * 0.5f);
|
||||
logo2_stretch_x_ = 1.2f - (jump_t * 0.2f);
|
||||
}
|
||||
logo2_scale_ = 1.0f + (fade_progress * 0.3f);
|
||||
logo2_scale_ = 1.0f + (prog2 * 0.3f);
|
||||
logo2_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
@@ -340,60 +452,14 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Aplicar alpha a ambos logos (compartido - sincronizado)
|
||||
if (logo1_texture_) {
|
||||
logo1_texture_->setAlpha(current_alpha_);
|
||||
}
|
||||
if (logo2_texture_) {
|
||||
logo2_texture_->setAlpha(current_alpha_);
|
||||
}
|
||||
|
||||
// Aplicar escala animada INDEPENDIENTE a cada logo
|
||||
if (logo1_sprite_) {
|
||||
float scaled_width = base_width_ * logo1_scale_;
|
||||
float scaled_height = base_height_ * logo1_scale_;
|
||||
logo1_sprite_->setSize(scaled_width, scaled_height);
|
||||
}
|
||||
|
||||
if (logo2_sprite_) {
|
||||
float scaled_width = base_width_ * logo2_scale_;
|
||||
float scaled_height = base_height_ * logo2_scale_;
|
||||
logo2_sprite_->setSize(scaled_width, scaled_height);
|
||||
}
|
||||
|
||||
// Recentrar ambos logos (están superpuestos, misma posición)
|
||||
updateLogoPosition();
|
||||
}
|
||||
|
||||
void AppLogo::render() {
|
||||
// Renderizar si NO está en estado HIDDEN (incluye FADE_IN, VISIBLE, FADE_OUT)
|
||||
if (state_ != AppLogoState::HIDDEN) {
|
||||
// Determinar animaciones actuales para cada logo
|
||||
AppLogoAnimationType logo1_anim = (state_ == AppLogoState::FADE_IN) ? logo1_entry_animation_ : logo1_exit_animation_;
|
||||
AppLogoAnimationType logo2_anim = (state_ == AppLogoState::FADE_IN) ? logo2_entry_animation_ : logo2_exit_animation_;
|
||||
|
||||
// ====================================================================
|
||||
// Renderizar LOGO1 primero (fondo)
|
||||
// ====================================================================
|
||||
if (logo1_anim != AppLogoAnimationType::ZOOM_ONLY) {
|
||||
// Usar renderizado con geometría para deformaciones/rotación
|
||||
// Renderizar LOGO1 primero (fondo), luego LOGO2 (encima)
|
||||
renderWithGeometry(1);
|
||||
} else if (logo1_sprite_) {
|
||||
// Usar renderizado simple con Sprite (solo ZOOM_ONLY)
|
||||
logo1_sprite_->render();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Renderizar LOGO2 después (encima de logo1)
|
||||
// ====================================================================
|
||||
if (logo2_anim != AppLogoAnimationType::ZOOM_ONLY) {
|
||||
// Usar renderizado con geometría para deformaciones/rotación
|
||||
renderWithGeometry(2);
|
||||
} else if (logo2_sprite_) {
|
||||
// Usar renderizado simple con Sprite (solo ZOOM_ONLY)
|
||||
logo2_sprite_->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,59 +467,37 @@ void AppLogo::updateScreenSize(int screen_width, int screen_height) {
|
||||
screen_width_ = screen_width;
|
||||
screen_height_ = screen_height;
|
||||
|
||||
// Recalcular tamaño base para la nueva resolución (asumimos mismo tamaño para ambos logos)
|
||||
if (logo1_sprite_ && logo1_texture_) {
|
||||
float logo_width = static_cast<float>(logo1_texture_->getWidth());
|
||||
float logo_height = static_cast<float>(logo1_texture_->getHeight());
|
||||
// ========================================================================
|
||||
// Detectar si coincide con resolución nativa o base, cambiar texturas
|
||||
// ========================================================================
|
||||
bool is_native = (screen_width == native_screen_width_ && screen_height == native_screen_height_);
|
||||
|
||||
// El logo debe tener una altura de APPLOGO_HEIGHT_PERCENT (40%) de la pantalla
|
||||
float target_height = screen_height_ * APPLOGO_HEIGHT_PERCENT;
|
||||
float scale = target_height / logo_height;
|
||||
if (is_native) {
|
||||
// Cambiar a texturas nativas (F4 fullscreen)
|
||||
logo1_current_texture_ = logo1_native_texture_;
|
||||
logo1_current_width_ = logo1_native_width_;
|
||||
logo1_current_height_ = logo1_native_height_;
|
||||
|
||||
// Recalcular tamaño base
|
||||
base_width_ = logo_width * scale;
|
||||
base_height_ = target_height; // = logo_height * scale
|
||||
logo2_current_texture_ = logo2_native_texture_;
|
||||
logo2_current_width_ = logo2_native_width_;
|
||||
logo2_current_height_ = logo2_native_height_;
|
||||
|
||||
// Aplicar escala actual a AMBOS logos (respeta la animación en curso)
|
||||
if (logo1_sprite_) {
|
||||
float scaled_width = base_width_ * logo1_scale_;
|
||||
float scaled_height = base_height_ * logo1_scale_;
|
||||
logo1_sprite_->setSize(scaled_width, scaled_height);
|
||||
std::cout << "AppLogo: Cambiado a texturas NATIVAS" << std::endl;
|
||||
} else {
|
||||
// Cambiar a texturas base (ventana redimensionable)
|
||||
logo1_current_texture_ = logo1_base_texture_;
|
||||
logo1_current_width_ = logo1_base_width_;
|
||||
logo1_current_height_ = logo1_base_height_;
|
||||
|
||||
logo2_current_texture_ = logo2_base_texture_;
|
||||
logo2_current_width_ = logo2_base_width_;
|
||||
logo2_current_height_ = logo2_base_height_;
|
||||
|
||||
std::cout << "AppLogo: Cambiado a texturas BASE" << std::endl;
|
||||
}
|
||||
|
||||
if (logo2_sprite_) {
|
||||
float scaled_width = base_width_ * logo2_scale_;
|
||||
float scaled_height = base_height_ * logo2_scale_;
|
||||
logo2_sprite_->setSize(scaled_width, scaled_height);
|
||||
}
|
||||
|
||||
// Reposicionar ambos logos
|
||||
updateLogoPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void AppLogo::updateLogoPosition() {
|
||||
// Calcular padding desde bordes derecho e inferior
|
||||
float padding_x = screen_width_ * APPLOGO_PADDING_PERCENT;
|
||||
float padding_y = screen_height_ * APPLOGO_PADDING_PERCENT;
|
||||
|
||||
// Posicionar LOGO1 (anclado a esquina inferior derecha con padding)
|
||||
if (logo1_sprite_) {
|
||||
float logo1_width = base_width_ * logo1_scale_;
|
||||
float logo1_height = base_height_ * logo1_scale_;
|
||||
float pos_x = screen_width_ - logo1_width - padding_x;
|
||||
float pos_y = screen_height_ - logo1_height - padding_y;
|
||||
logo1_sprite_->setPos({pos_x, pos_y});
|
||||
}
|
||||
|
||||
// Posicionar LOGO2 (anclado a esquina inferior derecha con padding, superpuesto a logo1)
|
||||
if (logo2_sprite_) {
|
||||
float logo2_width = base_width_ * logo2_scale_;
|
||||
float logo2_height = base_height_ * logo2_scale_;
|
||||
float pos_x = screen_width_ - logo2_width - padding_x;
|
||||
float pos_y = screen_height_ - logo2_height - padding_y;
|
||||
logo2_sprite_->setPos({pos_x, pos_y});
|
||||
}
|
||||
// Nota: No es necesario recalcular escalas porque las texturas están pre-escaladas
|
||||
// al tamaño exacto de pantalla. Solo renderizamos al 100% (o con deformaciones de animación).
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -528,26 +572,31 @@ AppLogoAnimationType AppLogo::getRandomAnimation() {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Renderizado con geometría deformada (para animación ELASTIC_STICK)
|
||||
// Renderizado con geometría (para todos los logos, con deformaciones)
|
||||
// ============================================================================
|
||||
|
||||
void AppLogo::renderWithGeometry(int logo_index) {
|
||||
if (!renderer_) return;
|
||||
|
||||
// Seleccionar variables según el logo_index (1 = logo1, 2 = logo2)
|
||||
std::shared_ptr<Texture> texture;
|
||||
SDL_Texture* texture;
|
||||
int base_width, base_height;
|
||||
float scale, squash_y, stretch_x, rotation;
|
||||
|
||||
if (logo_index == 1) {
|
||||
if (!logo1_texture_) return;
|
||||
texture = logo1_texture_;
|
||||
if (!logo1_current_texture_) return;
|
||||
texture = logo1_current_texture_;
|
||||
base_width = logo1_current_width_;
|
||||
base_height = logo1_current_height_;
|
||||
scale = logo1_scale_;
|
||||
squash_y = logo1_squash_y_;
|
||||
stretch_x = logo1_stretch_x_;
|
||||
rotation = logo1_rotation_;
|
||||
} else if (logo_index == 2) {
|
||||
if (!logo2_texture_) return;
|
||||
texture = logo2_texture_;
|
||||
if (!logo2_current_texture_) return;
|
||||
texture = logo2_current_texture_;
|
||||
base_width = logo2_current_width_;
|
||||
base_height = logo2_current_height_;
|
||||
scale = logo2_scale_;
|
||||
squash_y = logo2_squash_y_;
|
||||
stretch_x = logo2_stretch_x_;
|
||||
@@ -556,9 +605,14 @@ void AppLogo::renderWithGeometry(int logo_index) {
|
||||
return; // Índice inválido
|
||||
}
|
||||
|
||||
// Aplicar alpha específico de cada logo (con retraso para logo2)
|
||||
int alpha = (logo_index == 1) ? logo1_alpha_ : logo2_alpha_;
|
||||
SDL_SetTextureAlphaMod(texture, static_cast<Uint8>(alpha));
|
||||
|
||||
// Calcular tamaño con escala y deformaciones aplicadas
|
||||
float width = base_width_ * scale * stretch_x;
|
||||
float height = base_height_ * scale * squash_y;
|
||||
// (base_width y base_height ya están pre-escalados al tamaño correcto de pantalla)
|
||||
float width = base_width * scale * stretch_x;
|
||||
float height = base_height * scale * squash_y;
|
||||
|
||||
// Calcular padding desde bordes derecho e inferior
|
||||
float padding_x = screen_width_ * APPLOGO_PADDING_PERCENT;
|
||||
@@ -631,5 +685,5 @@ void AppLogo::renderWithGeometry(int logo_index) {
|
||||
int indices[6] = {0, 1, 2, 2, 3, 0};
|
||||
|
||||
// Renderizar con la textura del logo correspondiente
|
||||
SDL_RenderGeometry(renderer_, texture->getSDLTexture(), vertices, 4, indices, 6);
|
||||
SDL_RenderGeometry(renderer_, texture, vertices, 4, indices, 6);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ enum class AppLogoAnimationType {
|
||||
class AppLogo {
|
||||
public:
|
||||
AppLogo() = default;
|
||||
~AppLogo() = default;
|
||||
~AppLogo(); // Necesario para liberar las 4 texturas SDL
|
||||
|
||||
// Inicializar textura y sprite del logo
|
||||
bool initialize(SDL_Renderer* renderer, int screen_width, int screen_height);
|
||||
@@ -43,22 +43,42 @@ class AppLogo {
|
||||
void updateScreenSize(int screen_width, int screen_height);
|
||||
|
||||
private:
|
||||
// Texturas y sprites (x2 - logo1 y logo2 superpuestos)
|
||||
std::shared_ptr<Texture> logo1_texture_; // Textura del logo1 (data/logo/logo.png)
|
||||
std::unique_ptr<Sprite> logo1_sprite_; // Sprite para renderizar logo1
|
||||
std::shared_ptr<Texture> logo2_texture_; // Textura del logo2 (data/logo/logo2.png)
|
||||
std::unique_ptr<Sprite> logo2_sprite_; // Sprite para renderizar logo2
|
||||
// ====================================================================
|
||||
// Texturas pre-escaladas (4 texturas: 2 logos × 2 resoluciones)
|
||||
// ====================================================================
|
||||
SDL_Texture* logo1_base_texture_ = nullptr; // Logo1 para resolución base
|
||||
SDL_Texture* logo1_native_texture_ = nullptr; // Logo1 para resolución nativa (F4)
|
||||
SDL_Texture* logo2_base_texture_ = nullptr; // Logo2 para resolución base
|
||||
SDL_Texture* logo2_native_texture_ = nullptr; // Logo2 para resolución nativa (F4)
|
||||
|
||||
// Dimensiones pre-calculadas para cada textura
|
||||
int logo1_base_width_ = 0, logo1_base_height_ = 0;
|
||||
int logo1_native_width_ = 0, logo1_native_height_ = 0;
|
||||
int logo2_base_width_ = 0, logo2_base_height_ = 0;
|
||||
int logo2_native_width_ = 0, logo2_native_height_ = 0;
|
||||
|
||||
// Texturas actualmente en uso (apuntan a base o native según resolución)
|
||||
SDL_Texture* logo1_current_texture_ = nullptr;
|
||||
SDL_Texture* logo2_current_texture_ = nullptr;
|
||||
int logo1_current_width_ = 0, logo1_current_height_ = 0;
|
||||
int logo2_current_width_ = 0, logo2_current_height_ = 0;
|
||||
|
||||
// Resoluciones conocidas
|
||||
int base_screen_width_ = 0, base_screen_height_ = 0; // Resolución inicial
|
||||
int native_screen_width_ = 0, native_screen_height_ = 0; // Resolución nativa (F4)
|
||||
|
||||
// ====================================================================
|
||||
// Variables COMPARTIDAS (sincronización de ambos logos)
|
||||
// ====================================================================
|
||||
AppLogoState state_ = AppLogoState::HIDDEN; // Estado actual de la máquina de estados
|
||||
float timer_ = 0.0f; // Contador de tiempo para estado actual
|
||||
int current_alpha_ = 0; // Alpha actual (0-255)
|
||||
|
||||
// Animaciones INDEPENDIENTES para cada logo
|
||||
AppLogoAnimationType logo1_entry_animation_ = AppLogoAnimationType::ZOOM_ONLY;
|
||||
AppLogoAnimationType logo1_exit_animation_ = AppLogoAnimationType::ZOOM_ONLY;
|
||||
AppLogoAnimationType logo2_entry_animation_ = AppLogoAnimationType::ZOOM_ONLY;
|
||||
AppLogoAnimationType logo2_exit_animation_ = AppLogoAnimationType::ZOOM_ONLY;
|
||||
// Alpha INDEPENDIENTE para cada logo (Logo 2 con retraso)
|
||||
int logo1_alpha_ = 0; // Alpha de Logo 1 (0-255)
|
||||
int logo2_alpha_ = 0; // Alpha de Logo 2 (0-255, con retraso)
|
||||
|
||||
// Animación COMPARTIDA (misma para ambos logos, misma entrada y salida)
|
||||
AppLogoAnimationType current_animation_ = AppLogoAnimationType::ZOOM_ONLY;
|
||||
|
||||
// Variables de deformación INDEPENDIENTES para logo1
|
||||
float logo1_scale_ = 1.0f; // Escala actual de logo1 (1.0 = 100%)
|
||||
|
||||
@@ -294,6 +294,7 @@ constexpr float APPLOGO_DISPLAY_DURATION = 5.0f; // Duración de visibilidad
|
||||
constexpr float APPLOGO_FADE_DURATION = 0.5f; // Duración del fade in/out (segundos)
|
||||
constexpr float APPLOGO_HEIGHT_PERCENT = 0.4f; // Altura del logo = 40% de la altura de pantalla
|
||||
constexpr float APPLOGO_PADDING_PERCENT = 0.1f; // Padding desde esquina inferior-derecha = 10%
|
||||
constexpr float APPLOGO_LOGO2_DELAY = 0.25f; // Retraso de Logo 2 respecto a Logo 1 (segundos)
|
||||
|
||||
// Configuración de Modo BOIDS (comportamiento de enjambre)
|
||||
// TIME-BASED CONVERSION (frame-based → time-based):
|
||||
|
||||
10630
source/external/stb_image_resize2.h
vendored
Normal file
10630
source/external/stb_image_resize2.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
144
source/logo_scaler.cpp
Normal file
144
source/logo_scaler.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
#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;
|
||||
}
|
||||
61
source/logo_scaler.h
Normal file
61
source/logo_scaler.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_render.h> // Para SDL_Renderer, SDL_Texture
|
||||
#include <SDL3/SDL_video.h> // Para SDL_DisplayID, SDL_GetDisplays
|
||||
|
||||
#include <string> // Para std::string
|
||||
|
||||
/**
|
||||
* @brief Helper class para pre-escalar logos usando stb_image_resize2
|
||||
*
|
||||
* Proporciona funciones para:
|
||||
* - Detectar resolución nativa del monitor
|
||||
* - Cargar PNG y escalar a tamaño específico con algoritmos de alta calidad
|
||||
* - Crear texturas SDL desde buffers escalados
|
||||
*
|
||||
* Usado por AppLogo para pre-generar versiones de logos al tamaño exacto
|
||||
* de pantalla, eliminando el escalado dinámico de SDL y mejorando calidad visual.
|
||||
*/
|
||||
class LogoScaler {
|
||||
public:
|
||||
/**
|
||||
* @brief Detecta la resolución nativa del monitor principal
|
||||
*
|
||||
* @param native_width [out] Ancho nativo del display en píxeles
|
||||
* @param native_height [out] Alto nativo del display en píxeles
|
||||
* @return true si se pudo detectar, false si hubo error
|
||||
*/
|
||||
static bool detectNativeResolution(int& native_width, int& native_height);
|
||||
|
||||
/**
|
||||
* @brief Carga un PNG y lo escala al tamaño especificado
|
||||
*
|
||||
* Usa stb_image para cargar y stb_image_resize2 para escalar con
|
||||
* algoritmo Mitchell (balance calidad/velocidad) en espacio sRGB.
|
||||
*
|
||||
* @param path Ruta al archivo PNG (ej: "data/logo/logo.png")
|
||||
* @param target_width Ancho destino en píxeles
|
||||
* @param target_height Alto destino en píxeles
|
||||
* @param out_width [out] Ancho real de la imagen escalada
|
||||
* @param out_height [out] Alto real de la imagen escalada
|
||||
* @return Buffer RGBA (4 bytes por píxel) o nullptr si falla
|
||||
* IMPORTANTE: El caller debe liberar con free() cuando termine
|
||||
*/
|
||||
static unsigned char* loadAndScale(const std::string& path,
|
||||
int target_width, int target_height,
|
||||
int& out_width, int& out_height);
|
||||
|
||||
/**
|
||||
* @brief Crea una textura SDL desde un buffer RGBA
|
||||
*
|
||||
* @param renderer Renderizador SDL activo
|
||||
* @param data Buffer RGBA (4 bytes por píxel)
|
||||
* @param width Ancho del buffer en píxeles
|
||||
* @param height Alto del buffer en píxeles
|
||||
* @return Textura SDL creada o nullptr si falla
|
||||
* IMPORTANTE: El caller debe destruir con SDL_DestroyTexture()
|
||||
*/
|
||||
static SDL_Texture* createTextureFromBuffer(SDL_Renderer* renderer,
|
||||
unsigned char* data,
|
||||
int width, int height);
|
||||
};
|
||||
Reference in New Issue
Block a user