Files
esqueleto/source/core/rendering/sprites/dissolve_sprite.cpp
2026-03-24 21:59:22 +01:00

189 lines
6.4 KiB
C++

#include "core/rendering/sprites/dissolve_sprite.hpp"
#include <algorithm> // Para min
#include <cstdint> // Para uint32_t
#include "core/rendering/surface.hpp" // Para Surface
// Hash 2D estable per a dithering (rank aleatori per posició de píxel)
static auto pixelRank(int col, int row) -> float {
auto h = (static_cast<uint32_t>(col) * 2246822519U) ^ (static_cast<uint32_t>(row) * 2654435761U);
h ^= (h >> 13);
h *= 1274126177U;
h ^= (h >> 16);
return static_cast<float>(h & 0xFFFFU) / 65536.0F;
}
// Rang per a un píxel tenint en compte direcció (70% direccional + 30% aleatori)
auto DissolveSprite::computePixelRank(int col, int row, int frame_h, DissolveDirection dir) -> float {
const float RANDOM = pixelRank(col, row);
if (dir == DissolveDirection::NONE || frame_h <= 0) {
return RANDOM;
}
float y_factor = 0.0F;
if (dir == DissolveDirection::DOWN) {
y_factor = static_cast<float>(row) / static_cast<float>(frame_h);
} else {
y_factor = static_cast<float>(frame_h - 1 - row) / static_cast<float>(frame_h);
}
return (y_factor * 0.7F) + (RANDOM * 0.3F);
}
// Constructor per a surface directa (sense AnimationResource)
DissolveSprite::DissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
: AnimatedSprite(std::move(surface), pos) {
if (surface_) {
const int W = static_cast<int>(surface_->getWidth());
const int H = static_cast<int>(surface_->getHeight());
surface_display_ = std::make_shared<Surface>(W, H);
surface_display_->setTransparentColor(surface_->getTransparentColor());
surface_display_->clear(surface_->getTransparentColor());
}
}
// Constructor
DissolveSprite::DissolveSprite(const AnimationResource& data)
: AnimatedSprite(data) {
if (surface_) {
const int W = static_cast<int>(surface_->getWidth());
const int H = static_cast<int>(surface_->getHeight());
surface_display_ = std::make_shared<Surface>(W, H);
surface_display_->setTransparentColor(surface_->getTransparentColor());
// Inicialitza tots els píxels com a transparents
surface_display_->clear(surface_->getTransparentColor());
}
}
// Reconstrueix la surface_display_ filtrant píxels per progress_
void DissolveSprite::rebuildDisplaySurface() {
if (!surface_ || !surface_display_) {
return;
}
const SDL_FRect CLIP = clip_;
const int SX = static_cast<int>(CLIP.x);
const int SY = static_cast<int>(CLIP.y);
const int SW = static_cast<int>(CLIP.w);
const int SH = static_cast<int>(CLIP.h);
if (SW <= 0 || SH <= 0) {
return;
}
auto src_data = surface_->getSurfaceData();
auto dst_data = surface_display_->getSurfaceData();
const int SRC_W = static_cast<int>(src_data->width);
const int DST_W = static_cast<int>(dst_data->width);
const Uint8 TRANSPARENT = surface_->getTransparentColor();
// Esborra frame anterior si ha canviat
if (prev_clip_.w > 0 && prev_clip_.h > 0 &&
(prev_clip_.x != CLIP.x || prev_clip_.y != CLIP.y ||
prev_clip_.w != CLIP.w || prev_clip_.h != CLIP.h)) {
surface_display_->fillRect(&prev_clip_, TRANSPARENT);
}
// Esborra la zona del frame actual (reconstrucció neta)
surface_display_->fillRect(&CLIP, TRANSPARENT);
// Copia píxels filtrats per progress_
for (int row = 0; row < SH; ++row) {
for (int col = 0; col < SW; ++col) {
const Uint8 COLOR = src_data->data[((SY + row) * SRC_W) + (SX + col)];
if (COLOR == TRANSPARENT) {
continue;
}
const float RANK = computePixelRank(col, row, SH, direction_);
if (RANK >= progress_) {
const Uint8 OUT = (COLOR == source_color_) ? target_color_ : COLOR;
dst_data->data[((SY + row) * DST_W) + (SX + col)] = OUT;
}
}
}
prev_clip_ = CLIP;
needs_rebuild_ = false;
}
// Actualitza animació, moviment i transició temporal
void DissolveSprite::update(float delta_time) {
const SDL_FRect OLD_CLIP = clip_;
AnimatedSprite::update(delta_time);
// Detecta canvi de frame d'animació
if (clip_.x != OLD_CLIP.x || clip_.y != OLD_CLIP.y ||
clip_.w != OLD_CLIP.w || clip_.h != OLD_CLIP.h) {
needs_rebuild_ = true;
}
// Actualitza transició temporal si activa
if (transition_mode_ != TransitionMode::NONE) {
transition_elapsed_ += delta_time * 1000.0F;
const float T = std::min(transition_elapsed_ / transition_duration_, 1.0F);
progress_ = (transition_mode_ == TransitionMode::DISSOLVING) ? T : (1.0F - T);
needs_rebuild_ = true;
if (T >= 1.0F) {
transition_mode_ = TransitionMode::NONE;
}
}
if (needs_rebuild_) {
rebuildDisplaySurface();
}
}
// Renderitza: usa surface_display_ (amb color replace) si disponible
void DissolveSprite::render() {
if (!surface_display_) {
AnimatedSprite::render();
return;
}
surface_display_->render(static_cast<int>(pos_.x), static_cast<int>(pos_.y), &clip_, flip_);
}
// Estableix el progrés manualment
void DissolveSprite::setProgress(float progress) {
progress_ = std::min(std::max(progress, 0.0F), 1.0F);
needs_rebuild_ = true;
}
// Inicia dissolució temporal (visible → invisible)
void DissolveSprite::startDissolve(float duration_ms, DissolveDirection dir) {
direction_ = dir;
transition_mode_ = TransitionMode::DISSOLVING;
transition_duration_ = duration_ms;
transition_elapsed_ = 0.0F;
progress_ = 0.0F;
needs_rebuild_ = true;
}
// Inicia generació temporal (invisible → visible)
void DissolveSprite::startGenerate(float duration_ms, DissolveDirection dir) {
direction_ = dir;
transition_mode_ = TransitionMode::GENERATING;
transition_duration_ = duration_ms;
transition_elapsed_ = 0.0F;
progress_ = 1.0F;
needs_rebuild_ = true;
}
// Atura la transició temporal
void DissolveSprite::stopTransition() {
transition_mode_ = TransitionMode::NONE;
}
// Retorna si la transició ha acabat
auto DissolveSprite::isTransitionDone() const -> bool {
return transition_mode_ == TransitionMode::NONE;
}
// Configura substitució de color per a la reconstrucció
void DissolveSprite::setColorReplace(Uint8 source, Uint8 target) {
source_color_ = source;
target_color_ = target;
needs_rebuild_ = true;
}