Files
jdd_opendingux/source/core/rendering/surface.cpp
Sergio Valor 5f0b4355e4 surface: hallazgo 4 — elimina render(6 floats) sin callers
La sobrecarga render(float dx, float dy, float sx, float sy, float w,
float h) no tenía un solo caller en el proyecto. Las otras dos
sobrecargas (con SDL_FRect) cubren todos los casos de uso reales.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 12:16:46 +02:00

656 lines
27 KiB
C++

// IWYU pragma: no_include <bits/std_abs.h>
#include "core/rendering/surface.hpp"
#include <SDL3/SDL.h>
#include <algorithm> // Para min, max, copy_n, fill
#include <cmath> // Para abs
#include <cstdint> // Para uint32_t
#include <cstring> // Para memcpy, size_t
#include <fstream> // Para basic_ifstream, basic_ostream, basic_ist...
#include <iostream> // Para cerr
#include <memory> // Para shared_ptr, __shared_ptr_access, default...
#include <sstream> // Para basic_istringstream
#include <stdexcept> // Para runtime_error
#include <vector> // Para vector
#include "core/rendering/gif.hpp" // Para Gif
#include "core/rendering/screen.hpp" // Para Screen
#include "core/resources/resource_helper.hpp" // Para ResourceHelper
// Carga una paleta desde un archivo .gif
auto loadPalette(const std::string& file_path) -> Palette {
// Load file using ResourceHelper (supports both filesystem and pack)
auto buffer = Resource::Helper::loadFile(file_path);
if (buffer.empty()) {
throw std::runtime_error("Error opening file: " + file_path);
}
// Cargar la paleta usando los datos del buffer
std::vector<uint32_t> pal = GIF::Gif::loadPalette(buffer.data());
if (pal.empty()) {
throw std::runtime_error("No palette found in GIF file: " + file_path);
}
// Crear la paleta y copiar los datos desde 'pal'
Palette palette = {}; // Inicializa la paleta con ceros
std::copy_n(pal.begin(), std::min(pal.size(), palette.size()), palette.begin());
// Mensaje de depuración
printWithDots("Palette : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
return palette;
}
// Carga una paleta desde un archivo .pal
auto readPalFile(const std::string& file_path) -> Palette {
Palette palette{};
palette.fill(0); // Inicializar todo con 0 (transparente por defecto)
// Load file using ResourceHelper (supports both filesystem and pack)
auto file_data = Resource::Helper::loadFile(file_path);
if (file_data.empty()) {
throw std::runtime_error("No se pudo abrir el archivo .pal: " + file_path);
}
// Convert bytes to string for parsing
std::string content(file_data.begin(), file_data.end());
std::istringstream stream(content);
std::string line;
int line_number = 0;
int color_index = 0;
while (std::getline(stream, line)) {
++line_number;
// Ignorar las tres primeras líneas del archivo
if (line_number <= 3) {
continue;
}
// Procesar las líneas restantes con valores RGB
std::istringstream ss(line);
int r;
int g;
int b;
if (ss >> r >> g >> b) {
// Construir el color ARGB (A = 255 por defecto)
Uint32 color = (255 << 24) | (r << 16) | (g << 8) | b;
palette[color_index++] = color;
// Limitar a un máximo de 256 colores (opcional)
if (color_index >= 256) {
break;
}
}
}
printWithDots("Palette : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
return palette;
}
// Constructor
Surface::Surface(int w, int h)
: surface_data_(std::make_shared<SurfaceData>(w, h)),
transparent_color_(static_cast<Uint8>(PaletteColor::TRANSPARENT)) { initializeSubPalette(sub_palette_); }
Surface::Surface(const std::string& file_path)
: transparent_color_(static_cast<Uint8>(PaletteColor::TRANSPARENT)) {
SurfaceData loaded_data = loadSurface(file_path);
surface_data_ = std::make_shared<SurfaceData>(std::move(loaded_data));
initializeSubPalette(sub_palette_);
}
// Carga una superficie desde un archivo
auto Surface::loadSurface(const std::string& file_path) -> SurfaceData {
// Load file using ResourceHelper (supports both filesystem and pack)
std::vector<Uint8> buffer = Resource::Helper::loadFile(file_path);
if (buffer.empty()) {
std::cerr << "Error opening file: " << file_path << '\n';
throw std::runtime_error("Error opening file");
}
// Crear un objeto Gif y llamar a la función loadGif
Uint16 w = 0;
Uint16 h = 0;
std::vector<Uint8> raw_pixels = GIF::Gif::loadGif(buffer.data(), w, h);
if (raw_pixels.empty()) {
std::cerr << "Error loading GIF from file: " << file_path << '\n';
throw std::runtime_error("Error loading GIF");
}
// Si el constructor de Surface espera un std::shared_ptr<Uint8[]>,
// reservamos un bloque dinámico y copiamos los datos del vector.
size_t pixel_count = raw_pixels.size();
auto pixels = std::shared_ptr<Uint8[]>(new Uint8[pixel_count], std::default_delete<Uint8[]>());
std::memcpy(pixels.get(), raw_pixels.data(), pixel_count);
// Crear y devolver directamente el objeto SurfaceData
printWithDots("Surface : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
return {static_cast<float>(w), static_cast<float>(h), pixels};
}
// Carga una paleta desde un archivo
void Surface::loadPalette(const std::string& file_path) {
palette_ = ::loadPalette(file_path);
}
// Carga una paleta desde otra paleta
void Surface::loadPalette(const Palette& palette) {
palette_ = palette;
}
// Establece un color en la paleta
void Surface::setColor(int index, Uint32 color) {
palette_.at(index) = color;
}
// Rellena la superficie con un color
void Surface::clear(Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
const size_t TOTAL_PIXELS = surface_data_->width * surface_data_->height;
Uint8* data_ptr = surface_data_->data.get();
std::fill(data_ptr, data_ptr + TOTAL_PIXELS, color);
}
// Pone un pixel en la SurfaceData
void Surface::putPixel(int x, int y, Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
if (x < 0 || y < 0 || x >= surface_data_->width || y >= surface_data_->height) {
return; // Coordenadas fuera de rango
}
const int INDEX = x + (y * surface_data_->width);
surface_data_->data.get()[INDEX] = color;
}
// Obtiene el color de un pixel de la surface_data
auto Surface::getPixel(int x, int y) -> Uint8 { return surface_data_->data.get()[x + (y * static_cast<int>(surface_data_->width))]; }
// Dibuja un rectangulo relleno
void Surface::fillRect(const SDL_FRect* rect, Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
// Limitar los valores del rectángulo al tamaño de la superficie
float x_start = std::max(0.0F, rect->x);
float y_start = std::max(0.0F, rect->y);
float x_end = std::min(rect->x + rect->w, surface_data_->width);
float y_end = std::min(rect->y + rect->h, surface_data_->height);
// Rellenar fila a fila con memset (memoria contigua por fila)
Uint8* data_ptr = surface_data_->data.get();
const int SURF_WIDTH = static_cast<int>(surface_data_->width);
const int ROW_WIDTH = static_cast<int>(x_end) - static_cast<int>(x_start);
for (int y = static_cast<int>(y_start); y < static_cast<int>(y_end); ++y) {
std::memset(data_ptr + (y * SURF_WIDTH) + static_cast<int>(x_start), color, ROW_WIDTH);
}
}
// Dibuja el borde de un rectangulo
void Surface::drawRectBorder(const SDL_FRect* rect, Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
// Limitar los valores del rectángulo al tamaño de la superficie
float x_start = std::max(0.0F, rect->x);
float y_start = std::max(0.0F, rect->y);
float x_end = std::min(rect->x + rect->w, surface_data_->width);
float y_end = std::min(rect->y + rect->h, surface_data_->height);
// Dibujar bordes horizontales con memset (líneas contiguas en memoria)
Uint8* data_ptr = surface_data_->data.get();
const int SURF_WIDTH = static_cast<int>(surface_data_->width);
const int ROW_WIDTH = static_cast<int>(x_end) - static_cast<int>(x_start);
std::memset(data_ptr + (static_cast<int>(y_start) * SURF_WIDTH) + static_cast<int>(x_start), color, ROW_WIDTH);
std::memset(data_ptr + ((static_cast<int>(y_end) - 1) * SURF_WIDTH) + static_cast<int>(x_start), color, ROW_WIDTH);
// Dibujar bordes verticales
for (int y = y_start; y < y_end; ++y) {
// Borde izquierdo
const int LEFT_INDEX = x_start + (y * surface_data_->width);
surface_data_->data.get()[LEFT_INDEX] = color;
// Borde derecho
const int RIGHT_INDEX = (x_end - 1) + (y * surface_data_->width);
surface_data_->data.get()[RIGHT_INDEX] = color;
}
}
// Dibuja una linea (Bresenham en enteros)
void Surface::drawLine(float x1, float y1, float x2, float y2, Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
int ix1 = static_cast<int>(std::lround(x1));
int iy1 = static_cast<int>(std::lround(y1));
const int IX2 = static_cast<int>(std::lround(x2));
const int IY2 = static_cast<int>(std::lround(y2));
const int DX = std::abs(IX2 - ix1);
const int DY = std::abs(IY2 - iy1);
const int SX = (ix1 < IX2) ? 1 : -1;
const int SY = (iy1 < IY2) ? 1 : -1;
const int SURF_W = static_cast<int>(surface_data_->width);
const int SURF_H = static_cast<int>(surface_data_->height);
Uint8* data_ptr = surface_data_->data.get();
int err = DX - DY;
while (true) {
if (ix1 >= 0 && ix1 < SURF_W && iy1 >= 0 && iy1 < SURF_H) {
data_ptr[ix1 + (iy1 * SURF_W)] = color;
}
if (ix1 == IX2 && iy1 == IY2) {
break;
}
int e2 = 2 * err;
if (e2 > -DY) {
err -= DY;
ix1 += SX;
}
if (e2 < DX) {
err += DX;
iy1 += SY;
}
}
}
void Surface::render(int x, int y, SDL_FRect* src_rect, SDL_FlipMode flip) { // NOLINT(readability-make-member-function-const)
auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData();
// Determina la región de origen (clip) a renderizar
float sx = (src_rect != nullptr) ? src_rect->x : 0;
float sy = (src_rect != nullptr) ? src_rect->y : 0;
float w = (src_rect != nullptr) ? src_rect->w : surface_data_->width;
float h = (src_rect != nullptr) ? src_rect->h : surface_data_->height;
// Limitar la región para evitar accesos fuera de rango (origen y destino)
w = std::min(w, surface_data_->width - sx);
h = std::min(h, surface_data_->height - sy);
w = std::min(w, surface_data_dest->width - x);
h = std::min(h, surface_data_dest->height - y);
// Renderiza píxel por píxel aplicando el flip si es necesario
const Uint8* src_ptr = surface_data_->data.get();
Uint8* dst_ptr = surface_data_dest->data.get();
for (int iy = 0; iy < h; ++iy) {
for (int ix = 0; ix < w; ++ix) {
// Coordenadas de origen
int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + w - 1 - ix) : (sx + ix);
int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + h - 1 - iy) : (sy + iy);
// Coordenadas de destino
int dest_x = x + ix;
int dest_y = y + iy;
// Verificar que las coordenadas de destino están dentro de los límites
if (dest_x >= 0 && dest_x < surface_data_dest->width && dest_y >= 0 && dest_y < surface_data_dest->height) {
// Copia el píxel si no es transparente
Uint8 color = src_ptr[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
if (color != static_cast<Uint8>(transparent_color_)) {
dst_ptr[static_cast<size_t>(dest_x + (dest_y * surface_data_dest->width))] = sub_palette_[color];
}
}
}
}
}
// Helper para calcular coordenadas con flip
void Surface::calculateFlippedCoords(int ix, int iy, float sx, float sy, float w, float h, SDL_FlipMode flip, int& src_x, int& src_y) {
src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + w - 1 - ix) : (sx + ix);
src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + h - 1 - iy) : (sy + iy);
}
// Helper para copiar un pixel si no es transparente
void Surface::copyPixelIfNotTransparent(Uint8* dest_data, int dest_x, int dest_y, int dest_width, int src_x, int src_y) const {
if (dest_x < 0 || dest_y < 0) {
return;
}
Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
if (color != static_cast<Uint8>(transparent_color_)) {
dest_data[dest_x + (dest_y * dest_width)] = sub_palette_[color];
}
}
// Copia una región de la superficie de origen a la de destino
void Surface::render(SDL_FRect* src_rect, SDL_FRect* dst_rect, SDL_FlipMode flip) {
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
// Si srcRect es nullptr, tomar toda la superficie fuente
float sx = (src_rect != nullptr) ? src_rect->x : 0;
float sy = (src_rect != nullptr) ? src_rect->y : 0;
float sw = (src_rect != nullptr) ? src_rect->w : surface_data_->width;
float sh = (src_rect != nullptr) ? src_rect->h : surface_data_->height;
// Si dstRect es nullptr, asignar las mismas dimensiones que srcRect
float dx = (dst_rect != nullptr) ? dst_rect->x : 0;
float dy = (dst_rect != nullptr) ? dst_rect->y : 0;
float dw = (dst_rect != nullptr) ? dst_rect->w : sw;
float dh = (dst_rect != nullptr) ? dst_rect->h : sh;
// Asegurarse de que srcRect y dstRect tienen las mismas dimensiones
if (sw != dw || sh != dh) {
dw = sw; // Respetar las dimensiones de srcRect
dh = sh;
}
// Limitar la región para evitar accesos fuera de rango en src y dst
sw = std::min(sw, surface_data_->width - sx);
sh = std::min(sh, surface_data_->height - sy);
dw = std::min(dw, surface_data->width - dx);
dh = std::min(dh, surface_data->height - dy);
int final_width = std::min(sw, dw);
int final_height = std::min(sh, dh);
// Renderiza píxel por píxel aplicando el flip si es necesario
for (int iy = 0; iy < final_height; ++iy) {
for (int ix = 0; ix < final_width; ++ix) {
int src_x = 0;
int src_y = 0;
calculateFlippedCoords(ix, iy, sx, sy, final_width, final_height, flip, src_x, src_y);
int dest_x = dx + ix;
int dest_y = dy + iy;
// Verificar límites de destino antes de copiar
if (dest_x >= 0 && dest_x < surface_data->width && dest_y >= 0 && dest_y < surface_data->height) {
copyPixelIfNotTransparent(surface_data->data.get(), dest_x, dest_y, surface_data->width, src_x, src_y);
}
}
}
}
// Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro
void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect, SDL_FlipMode flip) const {
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
// Determina la región de origen (clip) a renderizar
float sx = (src_rect != nullptr) ? src_rect->x : 0;
float sy = (src_rect != nullptr) ? src_rect->y : 0;
float w = (src_rect != nullptr) ? src_rect->w : surface_data_->width;
float h = (src_rect != nullptr) ? src_rect->h : surface_data_->height;
// Limitar la región para evitar accesos fuera de rango
w = std::min(w, surface_data_->width - sx);
h = std::min(h, surface_data_->height - sy);
// Renderiza píxel por píxel aplicando el flip si es necesario
for (int iy = 0; iy < h; ++iy) {
for (int ix = 0; ix < w; ++ix) {
// Coordenadas de origen
int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + w - 1 - ix) : (sx + ix);
int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + h - 1 - iy) : (sy + iy);
// Coordenadas de destino
int dest_x = x + ix;
int dest_y = y + iy;
// Verifica que las coordenadas de destino estén dentro de los límites
if (dest_x < 0 || dest_y < 0 || dest_x >= surface_data->width || dest_y >= surface_data->height) {
continue; // Saltar píxeles fuera del rango del destino
}
// Copia el píxel si no es transparente; aplica sub_palette_ como el resto de render*
Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
if (color != static_cast<Uint8>(transparent_color_)) {
surface_data->data[dest_x + (dest_y * surface_data->width)] =
(color == source_color) ? target_color : sub_palette_[color];
}
}
}
}
// Hash 2D estable per a dithering sense flickering
static auto pixelThreshold(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;
}
// Calcula la densidad de fade para un pixel en posición screen_y
static auto computeFadeDensity(int screen_y, int fade_h, int canvas_height) -> float {
if (screen_y < fade_h) {
return static_cast<float>(fade_h - screen_y) / static_cast<float>(fade_h);
}
if (screen_y >= canvas_height - fade_h) {
return static_cast<float>(screen_y - (canvas_height - fade_h)) / static_cast<float>(fade_h);
}
return 0.0F;
}
// Render amb dissolució als cantons superior/inferior (hash 2D, sense parpelleig)
void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, SDL_FRect* src_rect) const {
const int SX = (src_rect != nullptr) ? static_cast<int>(src_rect->x) : 0;
const int SY = (src_rect != nullptr) ? static_cast<int>(src_rect->y) : 0;
const int SW = (src_rect != nullptr) ? static_cast<int>(src_rect->w) : static_cast<int>(surface_data_->width);
const int SH = (src_rect != nullptr) ? static_cast<int>(src_rect->h) : static_cast<int>(surface_data_->height);
auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData();
for (int row = 0; row < SH; row++) {
const int SCREEN_Y = y + row;
if (SCREEN_Y < 0 || SCREEN_Y >= static_cast<int>(surface_data_dest->height)) {
continue;
}
const float DENSITY = computeFadeDensity(SCREEN_Y, fade_h, canvas_height);
for (int col = 0; col < SW; col++) {
const int SCREEN_X = x + col;
if (SCREEN_X < 0 || SCREEN_X >= static_cast<int>(surface_data_dest->width)) {
continue;
}
const Uint8 COLOR = surface_data_->data[((SY + row) * static_cast<int>(surface_data_->width)) + (SX + col)];
if (COLOR == static_cast<Uint8>(transparent_color_)) {
continue;
}
if (pixelThreshold(col, row) < DENSITY) {
continue; // Pixel tapat per la zona de fade
}
surface_data_dest->data[SCREEN_X + (SCREEN_Y * static_cast<int>(surface_data_dest->width))] = sub_palette_[COLOR];
}
}
}
// Idem però reemplaçant un color índex
void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect) const {
const int SX = (src_rect != nullptr) ? static_cast<int>(src_rect->x) : 0;
const int SY = (src_rect != nullptr) ? static_cast<int>(src_rect->y) : 0;
const int SW = (src_rect != nullptr) ? static_cast<int>(src_rect->w) : static_cast<int>(surface_data_->width);
const int SH = (src_rect != nullptr) ? static_cast<int>(src_rect->h) : static_cast<int>(surface_data_->height);
auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData();
for (int row = 0; row < SH; row++) {
const int SCREEN_Y = y + row;
if (SCREEN_Y < 0 || SCREEN_Y >= static_cast<int>(surface_data_dest->height)) {
continue;
}
const float DENSITY = computeFadeDensity(SCREEN_Y, fade_h, canvas_height);
for (int col = 0; col < SW; col++) {
const int SCREEN_X = x + col;
if (SCREEN_X < 0 || SCREEN_X >= static_cast<int>(surface_data_dest->width)) {
continue;
}
const Uint8 COLOR = surface_data_->data[((SY + row) * static_cast<int>(surface_data_->width)) + (SX + col)];
if (COLOR == static_cast<Uint8>(transparent_color_)) {
continue;
}
if (pixelThreshold(col, row) < DENSITY) {
continue; // Pixel tapat per la zona de fade
}
const Uint8 OUT_COLOR = (COLOR == source_color) ? target_color : sub_palette_[COLOR];
surface_data_dest->data[SCREEN_X + (SCREEN_Y * static_cast<int>(surface_data_dest->width))] = OUT_COLOR;
}
}
}
// Vuelca los píxeles como ARGB8888 a un buffer externo (sin SDL_Texture ni SDL_Renderer)
void Surface::toARGBBuffer(Uint32* buffer) const {
if (!surface_data_ || !surface_data_->data || (buffer == nullptr)) { return; }
const int WIDTH = static_cast<int>(surface_data_->width);
const int HEIGHT = static_cast<int>(surface_data_->height);
const Uint8* src = surface_data_->data.get();
// Obtenemos el tamaño de la paleta para evitar accesos fuera de rango
const size_t PAL_SIZE = palette_.size();
for (int i = 0; i < WIDTH * HEIGHT; ++i) {
Uint8 color_index = src[i];
// Verificación de seguridad: ¿El índice existe en la paleta?
if (color_index < PAL_SIZE) {
buffer[i] = palette_[color_index];
} else {
buffer[i] = 0xFF000000; // Negro opaco si el índice es erróneo
}
}
}
// Vuelca la superficie a una textura
void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture) { // NOLINT(readability-convert-member-functions-to-static)
if ((renderer == nullptr) || (texture == nullptr) || !surface_data_) {
throw std::runtime_error("Renderer or texture is null.");
}
if (surface_data_->width <= 0 || surface_data_->height <= 0 || (surface_data_->data == nullptr)) {
throw std::runtime_error("Invalid surface dimensions or data.");
}
Uint32* pixels = nullptr;
int pitch = 0;
// Bloquea la textura para modificar los píxeles directamente
if (!SDL_LockTexture(texture, nullptr, reinterpret_cast<void**>(&pixels), &pitch)) {
throw std::runtime_error("Failed to lock texture: " + std::string(SDL_GetError()));
}
// Convertir `pitch` de bytes a Uint32 (asegurando alineación correcta en hardware)
int row_stride = pitch / sizeof(Uint32);
// Cachear punteros fuera del bucle para permitir autovectorización SIMD
const Uint8* src = surface_data_->data.get();
const Uint32* pal = palette_.data();
const int WIDTH = surface_data_->width;
const int HEIGHT = surface_data_->height;
for (int y = 0; y < HEIGHT; ++y) {
const Uint8* src_row = src + (y * WIDTH);
Uint32* dst_row = pixels + (y * row_stride);
for (int x = 0; x < WIDTH; ++x) {
dst_row[x] = pal[src_row[x]];
}
}
SDL_UnlockTexture(texture); // Desbloquea la textura
// Renderiza la textura en la pantalla completa
if (!SDL_RenderTexture(renderer, texture, nullptr, nullptr)) {
throw std::runtime_error("Failed to copy texture to renderer: " + std::string(SDL_GetError()));
}
}
// Vuelca la superficie a una textura
void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_FRect* src_rect, SDL_FRect* dest_rect) { // NOLINT(readability-convert-member-functions-to-static)
if ((renderer == nullptr) || (texture == nullptr) || !surface_data_) {
throw std::runtime_error("Renderer or texture is null.");
}
if (surface_data_->width <= 0 || surface_data_->height <= 0 || (surface_data_->data == nullptr)) {
throw std::runtime_error("Invalid surface dimensions or data.");
}
Uint32* pixels = nullptr;
int pitch = 0;
SDL_Rect lock_rect;
if (dest_rect != nullptr) {
lock_rect.x = static_cast<int>(dest_rect->x);
lock_rect.y = static_cast<int>(dest_rect->y);
lock_rect.w = static_cast<int>(dest_rect->w);
lock_rect.h = static_cast<int>(dest_rect->h);
}
// Usa lockRect solo si destRect no es nulo
if (!SDL_LockTexture(texture, (dest_rect != nullptr) ? &lock_rect : nullptr, reinterpret_cast<void**>(&pixels), &pitch)) {
throw std::runtime_error("Failed to lock texture: " + std::string(SDL_GetError()));
}
int row_stride = pitch / sizeof(Uint32);
// Cachear punteros fuera del bucle para permitir autovectorización SIMD
const Uint8* src = surface_data_->data.get();
const Uint32* pal = palette_.data();
const int WIDTH = surface_data_->width;
const int HEIGHT = surface_data_->height;
for (int y = 0; y < HEIGHT; ++y) {
const Uint8* src_row = src + (y * WIDTH);
Uint32* dst_row = pixels + (y * row_stride);
for (int x = 0; x < WIDTH; ++x) {
dst_row[x] = pal[src_row[x]];
}
}
SDL_UnlockTexture(texture);
// Renderiza la textura con los rectángulos especificados
if (!SDL_RenderTexture(renderer, texture, src_rect, dest_rect)) {
throw std::runtime_error("Failed to copy texture to renderer: " + std::string(SDL_GetError()));
}
}
// Realiza un efecto de fundido en la paleta principal
auto Surface::fadePalette() -> bool { // NOLINT(readability-convert-member-functions-to-static)
static constexpr int PALETTE_SIZE = 19;
static_assert(std::tuple_size_v<Palette> >= PALETTE_SIZE, "Palette size is insufficient for fadePalette operation.");
// Desplazar colores (pares e impares)
for (int i = 18; i > 1; --i) {
palette_[i] = palette_[i - 2];
}
// Ajustar el primer color
palette_[1] = palette_[0];
// Devolver si el índice 15 coincide con el índice 0
return palette_[15] == palette_[0];
}
// Realiza un efecto de fundido en la paleta secundaria
auto Surface::fadeSubPalette(Uint32 delay) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Variable estática para almacenar el último tick
static Uint32 last_tick_ = 0;
// Obtener el tiempo actual
Uint32 current_tick = SDL_GetTicks();
// Verificar si ha pasado el tiempo de retardo
if (current_tick - last_tick_ < delay) {
return false; // No se realiza el fade
}
// Actualizar el último tick
last_tick_ = current_tick;
static constexpr int SUB_PALETTE_SIZE = 19;
static_assert(std::tuple_size_v<SubPalette> >= SUB_PALETTE_SIZE, "Sub-palette size is insufficient for fadeSubPalette operation.");
// Desplazar colores (pares e impares)
for (int i = 18; i > 1; --i) {
sub_palette_[i] = sub_palette_[i - 2];
}
// Ajustar el primer color
sub_palette_[1] = sub_palette_[0];
// Devolver si el índice 15 coincide con el índice 0
return sub_palette_[15] == sub_palette_[0];
}
// Restaura la sub paleta a su estado original
void Surface::resetSubPalette() { initializeSubPalette(sub_palette_); } // NOLINT(readability-convert-member-functions-to-static)