This commit is contained in:
2025-10-27 17:39:23 +01:00
parent aacb14149f
commit b1dca32a5b
18 changed files with 774 additions and 565 deletions

View File

@@ -14,14 +14,89 @@ inline void readBytes(const uint8_t*& buffer, void* dst, size_t size) {
buffer += size;
}
// Inicializa el diccionario LZW con los valores iniciales
inline void initializeDictionary(std::vector<DictionaryEntry>& dictionary, int code_length, int& dictionary_ind) {
int size = 1 << code_length;
dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < size; dictionary_ind++) {
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
dictionary[dictionary_ind].prev = -1;
dictionary[dictionary_ind].len = 1;
}
dictionary_ind += 2; // Reservamos espacio para clear y stop codes
}
// Lee los próximos bits del stream de entrada para formar un código
inline int readNextCode(const uint8_t*& input, int& input_length, unsigned int& mask, int code_length) {
int code = 0;
for (int i = 0; i < (code_length + 1); i++) {
if (input_length <= 0) {
throw std::runtime_error("Unexpected end of input in decompress");
}
int bit = ((*input & mask) != 0) ? 1 : 0;
mask <<= 1;
if (mask == 0x100) {
mask = 0x01;
input++;
input_length--;
}
code |= (bit << i);
}
return code;
}
// Encuentra el primer byte de una cadena del diccionario
inline uint8_t findFirstByte(const std::vector<DictionaryEntry>& dictionary, int code) {
int ptr = code;
while (dictionary[ptr].prev != -1) {
ptr = dictionary[ptr].prev;
}
return dictionary[ptr].byte;
}
// Agrega una nueva entrada al diccionario
inline void addDictionaryEntry(std::vector<DictionaryEntry>& dictionary, int& dictionary_ind,
int& code_length, int prev, int code) {
uint8_t first_byte;
if (code == dictionary_ind) {
first_byte = findFirstByte(dictionary, prev);
} else {
first_byte = findFirstByte(dictionary, code);
}
dictionary[dictionary_ind].byte = first_byte;
dictionary[dictionary_ind].prev = prev;
dictionary[dictionary_ind].len = dictionary[prev].len + 1;
dictionary_ind++;
if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) {
code_length++;
dictionary.resize(1 << (code_length + 1));
}
}
// Escribe la cadena decodificada al buffer de salida
inline int writeDecodedString(const std::vector<DictionaryEntry>& dictionary, int code, uint8_t*& out) {
int cur_code = code;
int match_len = dictionary[cur_code].len;
while (cur_code != -1) {
out[dictionary[cur_code].len - 1] = dictionary[cur_code].byte;
if (dictionary[cur_code].prev == cur_code) {
std::cerr << "Internal error; self-reference detected." << std::endl;
throw std::runtime_error("Internal error in decompress: self-reference");
}
cur_code = dictionary[cur_code].prev;
}
out += match_len;
return match_len;
}
void Gif::decompress(int code_length, const uint8_t* input, int input_length, uint8_t* out) {
// Verifica que el code_length tenga un rango razonable.
if (code_length < 2 || code_length > 12) {
throw std::runtime_error("Invalid LZW code length");
}
int i;
int bit;
int prev = -1;
std::vector<DictionaryEntry> dictionary;
int dictionary_ind;
@@ -29,48 +104,22 @@ void Gif::decompress(int code_length, const uint8_t* input, int input_length, ui
int reset_code_length = code_length;
int clear_code = 1 << code_length;
int stop_code = clear_code + 1;
int match_len = 0;
// Inicializamos el diccionario con el tamaño correspondiente.
dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
dictionary[dictionary_ind].prev = -1;
dictionary[dictionary_ind].len = 1;
}
dictionary_ind += 2; // Reservamos espacio para clear y stop codes
initializeDictionary(dictionary, code_length, dictionary_ind);
// Bucle principal: procesar el stream comprimido.
while (input_length > 0) {
int code = 0;
// Lee (code_length + 1) bits para formar el código.
for (i = 0; i < (code_length + 1); i++) {
if (input_length <= 0) {
throw std::runtime_error("Unexpected end of input in decompress");
}
bit = ((*input & mask) != 0) ? 1 : 0;
mask <<= 1;
if (mask == 0x100) {
mask = 0x01;
input++;
input_length--;
}
code |= (bit << i);
}
int code = readNextCode(input, input_length, mask, code_length);
if (code == clear_code) {
// Reinicia el diccionario.
code_length = reset_code_length;
dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
dictionary[dictionary_ind].prev = -1;
dictionary[dictionary_ind].len = 1;
}
dictionary_ind += 2;
initializeDictionary(dictionary, code_length, dictionary_ind);
prev = -1;
continue;
}
if (code == stop_code) {
break;
}
@@ -82,28 +131,7 @@ void Gif::decompress(int code_length, const uint8_t* input, int input_length, ui
throw std::runtime_error("LZW error: code exceeds dictionary_ind.");
}
int ptr;
if (code == dictionary_ind) {
ptr = prev;
while (dictionary[ptr].prev != -1) {
ptr = dictionary[ptr].prev;
}
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
} else {
ptr = code;
while (dictionary[ptr].prev != -1) {
ptr = dictionary[ptr].prev;
}
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
}
dictionary[dictionary_ind].prev = prev;
dictionary[dictionary_ind].len = dictionary[prev].len + 1;
dictionary_ind++;
if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) {
code_length++;
dictionary.resize(1 << (code_length + 1));
}
addDictionaryEntry(dictionary, dictionary_ind, code_length, prev, code);
}
prev = code;
@@ -115,18 +143,7 @@ void Gif::decompress(int code_length, const uint8_t* input, int input_length, ui
throw std::runtime_error("LZW error: invalid code encountered");
}
int cur_code = code; // Variable temporal para recorrer la cadena.
match_len = dictionary[cur_code].len;
while (cur_code != -1) {
// Se asume que dictionary[curCode].len > 0.
out[dictionary[cur_code].len - 1] = dictionary[cur_code].byte;
if (dictionary[cur_code].prev == cur_code) {
std::cerr << "Internal error; self-reference detected." << std::endl;
throw std::runtime_error("Internal error in decompress: self-reference");
}
cur_code = dictionary[cur_code].prev;
}
out += match_len;
writeDecodedString(dictionary, code, out);
}
}

View File

@@ -30,7 +30,7 @@ class OpenGLShader : public ShaderBackend {
void render() override;
void setTextureSize(float width, float height) override;
void cleanup() override;
void cleanup() final;
bool isHardwareAccelerated() const override { return is_initialized_; }
private:

View File

@@ -127,10 +127,9 @@ SurfaceData Surface::loadSurface(const std::string& file_path) {
}
// Crear un objeto Gif y llamar a la función loadGif
GIF::Gif gif;
Uint16 w = 0;
Uint16 h = 0;
std::vector<Uint8> raw_pixels = gif.loadGif(buffer.data(), w, h);
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 << std::endl;
throw std::runtime_error("Error loading GIF");
@@ -336,6 +335,24 @@ void Surface::render(int x, int y, SDL_FRect* src_rect, SDL_FlipMode flip) {
}
}
// 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 != 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();
@@ -370,19 +387,16 @@ void Surface::render(SDL_FRect* src_rect, SDL_FRect* dst_rect, SDL_FlipMode flip
// 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) {
// Coordenadas de origen
int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + final_width - 1 - ix) : (sx + ix);
int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + final_height - 1 - iy) : (sy + iy);
int src_x = 0;
int src_y = 0;
calculateFlippedCoords(ix, iy, sx, sy, final_width, final_height, flip, src_x, src_y);
// Coordenadas de destino
if (int dest_x = dx + ix; dest_x >= 0 && dest_x < surface_data->width) {
if (int dest_y = dy + iy; dest_y >= 0 && dest_y < surface_data->height) {
// Copiar el píxel si no es transparente
Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
if (color != transparent_color_) {
surface_data->data[dest_x + (dest_y * surface_data->width)] = sub_palette_[color];
}
}
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);
}
}
}

View File

@@ -78,7 +78,7 @@ class Surface {
// Copia una región de la SurfaceData de origen a la SurfaceData de destino
void render(float dx, float dy, float sx, float sy, float w, float h);
void render(int x, int y, SDL_FRect* clip = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE);
void render(int x, int y, SDL_FRect* src_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE);
void render(SDL_FRect* src_rect = nullptr, SDL_FRect* dst_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE);
// Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro
@@ -130,4 +130,11 @@ class Surface {
// Inicializa la sub paleta
static void initializeSubPalette(SubPalette& palette) { std::iota(palette.begin(), palette.end(), 0); }
private:
// Helper para calcular coordenadas con flip
static void calculateFlippedCoords(int ix, int iy, float sx, float sy, float w, float h, SDL_FlipMode flip, int& src_x, int& src_y);
// Helper para copiar un pixel si no es transparente
void copyPixelIfNotTransparent(Uint8* dest_data, int dest_x, int dest_y, int dest_width, int src_x, int src_y) const;
};

View File

@@ -135,6 +135,96 @@ void SurfaceAnimatedSprite::resetAnimation() {
animations_[current_animation_].completed = false;
}
// Helper: Parsea los parámetros de configuración globales (frame_width, frame_height)
bool parseGlobalParameter(const std::string& line, float& frame_width, float& frame_height) {
size_t pos = line.find("=");
if (pos == std::string::npos) {
return false;
}
std::string key = line.substr(0, pos);
int value = std::stoi(line.substr(pos + 1));
if (key == "frame_width") {
frame_width = value;
return true;
}
if (key == "frame_height") {
frame_height = value;
return true;
}
std::cout << "Warning: unknown parameter " << key << std::endl;
return false;
}
// Helper: Parsea los frames de una animación desde una cadena separada por comas
void parseAnimationFrames(const std::string& value, AnimationData& animation,
float frame_width, float frame_height,
int frames_per_row, int max_tiles) {
std::stringstream ss(value);
std::string tmp;
SDL_FRect rect = {0.0F, 0.0F, frame_width, frame_height};
while (getline(ss, tmp, ',')) {
const int NUM_TILE = std::stoi(tmp);
if (NUM_TILE <= max_tiles) {
rect.x = (NUM_TILE % frames_per_row) * frame_width;
rect.y = (NUM_TILE / frames_per_row) * frame_height;
animation.frames.emplace_back(rect);
}
}
}
// Helper: Parsea un parámetro de animación individual
bool parseAnimationParameter(const std::string& key, const std::string& value,
AnimationData& animation,
float frame_width, float frame_height,
int frames_per_row, int max_tiles) {
if (key == "name") {
animation.name = value;
return true;
}
if (key == "speed") {
animation.speed = std::stoi(value);
return true;
}
if (key == "loop") {
animation.loop = std::stoi(value);
return true;
}
if (key == "frames") {
parseAnimationFrames(value, animation, frame_width, frame_height, frames_per_row, max_tiles);
return true;
}
std::cout << "Warning: unknown parameter " << key << std::endl;
return false;
}
// Helper: Parsea una animación completa
AnimationData parseAnimation(const Animations& animations, size_t& index,
float frame_width, float frame_height,
int frames_per_row, int max_tiles) {
AnimationData animation;
std::string line;
do {
index++;
line = animations.at(index);
size_t pos = line.find("=");
if (pos != std::string::npos) {
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
parseAnimationParameter(key, value, animation, frame_width, frame_height,
frames_per_row, max_tiles);
}
} while (line != "[/animation]");
return animation;
}
// Carga la animación desde un vector de cadenas
void SurfaceAnimatedSprite::setAnimations(const Animations& animations) {
float frame_width = 1.0F;
@@ -148,21 +238,7 @@ void SurfaceAnimatedSprite::setAnimations(const Animations& animations) {
// Parsea el fichero para buscar variables y valores
if (line != "[animation]") {
// Encuentra la posición del caracter '='
size_t pos = line.find("=");
// Procesa las dos subcadenas
if (pos != std::string::npos) {
std::string key = line.substr(0, pos);
int value = std::stoi(line.substr(pos + 1));
if (key == "frame_width") {
frame_width = value;
} else if (key == "frame_height") {
frame_height = value;
} else {
std::cout << "Warning: unknown parameter " << key << std::endl;
}
if (parseGlobalParameter(line, frame_width, frame_height)) {
frames_per_row = surface_->getWidth() / frame_width;
const int W = surface_->getWidth() / frame_width;
const int H = surface_->getHeight() / frame_height;
@@ -172,45 +248,8 @@ void SurfaceAnimatedSprite::setAnimations(const Animations& animations) {
// Si la linea contiene el texto [animation] se realiza el proceso de carga de una animación
if (line == "[animation]") {
AnimationData animation;
do {
index++;
line = animations.at(index);
size_t pos = line.find("=");
if (pos != std::string::npos) {
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
if (key == "name") {
animation.name = value;
} else if (key == "speed") {
animation.speed = std::stoi(value);
} else if (key == "loop") {
animation.loop = std::stoi(value);
} else if (key == "frames") {
// Se introducen los valores separados por comas en un vector
std::stringstream ss(value);
std::string tmp;
SDL_FRect rect = {0.0F, 0.0F, frame_width, frame_height};
while (getline(ss, tmp, ',')) {
// Comprueba que el tile no sea mayor que el maximo indice permitido
const int NUM_TILE = std::stoi(tmp);
if (NUM_TILE <= max_tiles) {
rect.x = (NUM_TILE % frames_per_row) * frame_width;
rect.y = (NUM_TILE / frames_per_row) * frame_height;
animation.frames.emplace_back(rect);
}
}
}
else {
std::cout << "Warning: unknown parameter " << key << std::endl;
}
}
} while (line != "[/animation]");
// Añade la animación al vector de animaciones
AnimationData animation = parseAnimation(animations, index, frame_width, frame_height,
frames_per_row, max_tiles);
animations_.emplace_back(animation);
}