canviat Options de struct a namespace

This commit is contained in:
2025-10-26 14:01:08 +01:00
parent 8f49e442de
commit df4965a84b
59 changed files with 1470 additions and 1533 deletions

View File

@@ -1,316 +1,275 @@
#include "core/rendering/gif.hpp"
#include <iostream> // Para std::cout
#include <cstring> // Para memcpy, size_t
#include <stdexcept> // Para runtime_error
#include <string> // Para allocator, char_traits, operator==, basic_string
namespace GIF
{
#include <cstring> // Para memcpy, size_t
#include <iostream> // Para std::cout
#include <stdexcept> // Para runtime_error
#include <string> // Para allocator, char_traits, operator==, basic_string
// Función inline para reemplazar el macro READ.
// Actualiza el puntero 'buffer' tras copiar 'size' bytes a 'dst'.
inline void readBytes(const uint8_t *&buffer, void *dst, size_t size)
{
std::memcpy(dst, buffer, size);
buffer += size;
namespace GIF {
// Función inline para reemplazar el macro READ.
// Actualiza el puntero 'buffer' tras copiar 'size' bytes a 'dst'.
inline void readBytes(const uint8_t*& buffer, void* dst, size_t size) {
std::memcpy(dst, buffer, size);
buffer += size;
}
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");
}
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, bit;
int prev = -1;
std::vector<DictionaryEntry> dictionary;
int dictionary_ind;
unsigned int mask = 0x01;
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
// 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 i, bit;
int prev = -1;
std::vector<DictionaryEntry> dictionary;
int dictionary_ind;
unsigned int mask = 0x01;
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;
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;
prev = -1;
continue;
} else if (code == stop_code) {
break;
}
dictionary_ind += 2; // Reservamos espacio para clear y stop codes
// 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);
if (prev > -1 && code_length < 12) {
if (code > dictionary_ind) {
std::cerr << "code = " << std::hex << code
<< ", but dictionary_ind = " << dictionary_ind << std::endl;
throw std::runtime_error("LZW error: code exceeds dictionary_ind.");
}
if (code == clear_code)
{
// Reinicia el diccionario.
code_length = reset_code_length;
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));
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;
prev = -1;
continue;
}
else if (code == stop_code)
{
break;
}
if (prev > -1 && code_length < 12)
{
if (code > dictionary_ind)
{
std::cerr << "code = " << std::hex << code
<< ", but dictionary_ind = " << dictionary_ind << std::endl;
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));
}
}
prev = code;
// Verifica que 'code' sea un índice válido antes de usarlo.
if (code < 0 || static_cast<size_t>(code) >= dictionary.size())
{
std::cerr << "Invalid LZW code " << code
<< ", dictionary size " << dictionary.size() << std::endl;
throw std::runtime_error("LZW error: invalid code encountered");
}
int curCode = code; // Variable temporal para recorrer la cadena.
match_len = dictionary[curCode].len;
while (curCode != -1)
{
// Se asume que dictionary[curCode].len > 0.
out[dictionary[curCode].len - 1] = dictionary[curCode].byte;
if (dictionary[curCode].prev == curCode)
{
std::cerr << "Internal error; self-reference detected." << std::endl;
throw std::runtime_error("Internal error in decompress: self-reference");
}
curCode = dictionary[curCode].prev;
}
out += match_len;
}
}
std::vector<uint8_t> Gif::readSubBlocks(const uint8_t *&buffer)
{
std::vector<uint8_t> data;
uint8_t block_size = *buffer;
prev = code;
// Verifica que 'code' sea un índice válido antes de usarlo.
if (code < 0 || static_cast<size_t>(code) >= dictionary.size()) {
std::cerr << "Invalid LZW code " << code
<< ", dictionary size " << dictionary.size() << std::endl;
throw std::runtime_error("LZW error: invalid code encountered");
}
int curCode = code; // Variable temporal para recorrer la cadena.
match_len = dictionary[curCode].len;
while (curCode != -1) {
// Se asume que dictionary[curCode].len > 0.
out[dictionary[curCode].len - 1] = dictionary[curCode].byte;
if (dictionary[curCode].prev == curCode) {
std::cerr << "Internal error; self-reference detected." << std::endl;
throw std::runtime_error("Internal error in decompress: self-reference");
}
curCode = dictionary[curCode].prev;
}
out += match_len;
}
}
std::vector<uint8_t> Gif::readSubBlocks(const uint8_t*& buffer) {
std::vector<uint8_t> data;
uint8_t block_size = *buffer;
buffer++;
while (block_size != 0) {
data.insert(data.end(), buffer, buffer + block_size);
buffer += block_size;
block_size = *buffer;
buffer++;
while (block_size != 0)
{
data.insert(data.end(), buffer, buffer + block_size);
buffer += block_size;
block_size = *buffer;
buffer++;
}
return data;
}
std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t*& buffer, const std::vector<RGB>& gct, int resolution_bits) {
ImageDescriptor image_descriptor;
// Lee 9 bytes para el image descriptor.
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
uint8_t lzw_code_size;
readBytes(buffer, &lzw_code_size, sizeof(uint8_t));
std::vector<uint8_t> compressed_data = readSubBlocks(buffer);
int uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height;
std::vector<uint8_t> uncompressed_data(uncompressed_data_length);
decompress(lzw_code_size, compressed_data.data(), static_cast<int>(compressed_data.size()), uncompressed_data.data());
return uncompressed_data;
}
std::vector<uint32_t> Gif::loadPalette(const uint8_t* buffer) {
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
ScreenDescriptor screen_descriptor;
std::memcpy(&screen_descriptor, buffer, sizeof(ScreenDescriptor));
buffer += sizeof(ScreenDescriptor);
std::vector<uint32_t> global_color_table;
if (screen_descriptor.fields & 0x80) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
for (int i = 0; i < global_color_table_size; ++i) {
uint8_t r = buffer[0];
uint8_t g = buffer[1];
uint8_t b = buffer[2];
global_color_table[i] = (r << 16) | (g << 8) | b;
buffer += 3;
}
return data;
}
return global_color_table;
}
std::vector<uint8_t> Gif::processGifStream(const uint8_t* buffer, uint16_t& w, uint16_t& h) {
// Leer la cabecera de 6 bytes ("GIF87a" o "GIF89a")
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
// Opcional: Validar header
std::string headerStr(reinterpret_cast<char*>(header), 6);
if (headerStr != "GIF87a" && headerStr != "GIF89a") {
throw std::runtime_error("Formato de archivo GIF inválido.");
}
std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits)
{
ImageDescriptor image_descriptor;
// Lee 9 bytes para el image descriptor.
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
// Leer el Screen Descriptor (7 bytes, empaquetado sin padding)
ScreenDescriptor screen_descriptor;
readBytes(buffer, &screen_descriptor, sizeof(ScreenDescriptor));
uint8_t lzw_code_size;
readBytes(buffer, &lzw_code_size, sizeof(uint8_t));
// Asigna ancho y alto
w = screen_descriptor.width;
h = screen_descriptor.height;
std::vector<uint8_t> compressed_data = readSubBlocks(buffer);
int uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height;
std::vector<uint8_t> uncompressed_data(uncompressed_data_length);
decompress(lzw_code_size, compressed_data.data(), static_cast<int>(compressed_data.size()), uncompressed_data.data());
return uncompressed_data;
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
std::vector<RGB> global_color_table;
if (screen_descriptor.fields & 0x80) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size);
buffer += 3 * global_color_table_size;
}
std::vector<uint32_t> Gif::loadPalette(const uint8_t *buffer)
{
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
ScreenDescriptor screen_descriptor;
std::memcpy(&screen_descriptor, buffer, sizeof(ScreenDescriptor));
buffer += sizeof(ScreenDescriptor);
std::vector<uint32_t> global_color_table;
if (screen_descriptor.fields & 0x80)
// Supongamos que 'buffer' es el puntero actual y TRAILER es 0x3B
uint8_t block_type = *buffer++;
while (block_type != TRAILER) {
if (block_type == EXTENSION_INTRODUCER) // 0x21
{
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
for (int i = 0; i < global_color_table_size; ++i)
{
uint8_t r = buffer[0];
uint8_t g = buffer[1];
uint8_t b = buffer[2];
global_color_table[i] = (r << 16) | (g << 8) | b;
buffer += 3;
}
}
return global_color_table;
}
std::vector<uint8_t> Gif::processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h)
{
// Leer la cabecera de 6 bytes ("GIF87a" o "GIF89a")
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
// Opcional: Validar header
std::string headerStr(reinterpret_cast<char *>(header), 6);
if (headerStr != "GIF87a" && headerStr != "GIF89a")
{
throw std::runtime_error("Formato de archivo GIF inválido.");
}
// Leer el Screen Descriptor (7 bytes, empaquetado sin padding)
ScreenDescriptor screen_descriptor;
readBytes(buffer, &screen_descriptor, sizeof(ScreenDescriptor));
// Asigna ancho y alto
w = screen_descriptor.width;
h = screen_descriptor.height;
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
std::vector<RGB> global_color_table;
if (screen_descriptor.fields & 0x80)
{
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size);
buffer += 3 * global_color_table_size;
}
// Supongamos que 'buffer' es el puntero actual y TRAILER es 0x3B
uint8_t block_type = *buffer++;
while (block_type != TRAILER)
{
if (block_type == EXTENSION_INTRODUCER) // 0x21
{
// Se lee la etiqueta de extensión, la cual indica el tipo de extensión.
uint8_t extension_label = *buffer++;
switch (extension_label)
{
case GRAPHIC_CONTROL: // 0xF9
// Se lee la etiqueta de extensión, la cual indica el tipo de extensión.
uint8_t extension_label = *buffer++;
switch (extension_label) {
case GRAPHIC_CONTROL: // 0xF9
{
// Procesar Graphic Control Extension:
uint8_t blockSize = *buffer++; // Normalmente, blockSize == 4
buffer += blockSize; // Saltamos los 4 bytes del bloque fijo
uint8_t blockSize = *buffer++; // Normalmente, blockSize == 4
buffer += blockSize; // Saltamos los 4 bytes del bloque fijo
// Saltar los sub-bloques
uint8_t subBlockSize = *buffer++;
while (subBlockSize != 0)
{
while (subBlockSize != 0) {
buffer += subBlockSize;
subBlockSize = *buffer++;
}
break;
}
case APPLICATION_EXTENSION: // 0xFF
case COMMENT_EXTENSION: // 0xFE
case PLAINTEXT_EXTENSION: // 0x01
case APPLICATION_EXTENSION: // 0xFF
case COMMENT_EXTENSION: // 0xFE
case PLAINTEXT_EXTENSION: // 0x01
{
// Para estas extensiones, saltamos el bloque fijo y los sub-bloques.
uint8_t blockSize = *buffer++;
buffer += blockSize;
uint8_t subBlockSize = *buffer++;
while (subBlockSize != 0)
{
while (subBlockSize != 0) {
buffer += subBlockSize;
subBlockSize = *buffer++;
}
break;
}
default:
{
default: {
// Si la etiqueta de extensión es desconocida, saltarla también:
uint8_t blockSize = *buffer++;
buffer += blockSize;
uint8_t subBlockSize = *buffer++;
while (subBlockSize != 0)
{
while (subBlockSize != 0) {
buffer += subBlockSize;
subBlockSize = *buffer++;
}
break;
}
}
}
else if (block_type == IMAGE_DESCRIPTOR)
{
// Procesar el Image Descriptor y retornar los datos de imagen
return processImageDescriptor(buffer, global_color_table, color_resolution_bits);
}
else
{
std::cerr << "Unrecognized block type " << std::hex << static_cast<int>(block_type) << std::endl;
return std::vector<uint8_t>{};
}
block_type = *buffer++;
} else if (block_type == IMAGE_DESCRIPTOR) {
// Procesar el Image Descriptor y retornar los datos de imagen
return processImageDescriptor(buffer, global_color_table, color_resolution_bits);
} else {
std::cerr << "Unrecognized block type " << std::hex << static_cast<int>(block_type) << std::endl;
return std::vector<uint8_t>{};
}
return std::vector<uint8_t>{};
block_type = *buffer++;
}
std::vector<uint8_t> Gif::loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h)
{
return processGifStream(buffer, w, h);
}
return std::vector<uint8_t>{};
}
} // namespace GIF
std::vector<uint8_t> Gif::loadGif(const uint8_t* buffer, uint16_t& w, uint16_t& h) {
return processGifStream(buffer, w, h);
}
} // namespace GIF

View File

@@ -1,102 +1,92 @@
#pragma once
#include <cstdint> // Para uint8_t, uint16_t, uint32_t
#include <vector> // Para vector
#include <cstdint> // Para uint8_t, uint16_t, uint32_t
#include <vector> // Para vector
namespace GIF
{
namespace GIF {
// Constantes definidas con constexpr, en lugar de macros
constexpr uint8_t EXTENSION_INTRODUCER = 0x21;
constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C;
constexpr uint8_t TRAILER = 0x3B;
constexpr uint8_t GRAPHIC_CONTROL = 0xF9;
constexpr uint8_t APPLICATION_EXTENSION = 0xFF;
constexpr uint8_t COMMENT_EXTENSION = 0xFE;
constexpr uint8_t PLAINTEXT_EXTENSION = 0x01;
// Constantes definidas con constexpr, en lugar de macros
constexpr uint8_t EXTENSION_INTRODUCER = 0x21;
constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C;
constexpr uint8_t TRAILER = 0x3B;
constexpr uint8_t GRAPHIC_CONTROL = 0xF9;
constexpr uint8_t APPLICATION_EXTENSION = 0xFF;
constexpr uint8_t COMMENT_EXTENSION = 0xFE;
constexpr uint8_t PLAINTEXT_EXTENSION = 0x01;
#pragma pack(push, 1)
struct ScreenDescriptor
{
struct ScreenDescriptor {
uint16_t width;
uint16_t height;
uint8_t fields;
uint8_t background_color_index;
uint8_t pixel_aspect_ratio;
};
};
struct RGB
{
struct RGB {
uint8_t r, g, b;
};
};
struct ImageDescriptor
{
struct ImageDescriptor {
uint16_t image_left_position;
uint16_t image_top_position;
uint16_t image_width;
uint16_t image_height;
uint8_t fields;
};
};
#pragma pack(pop)
struct DictionaryEntry
{
struct DictionaryEntry {
uint8_t byte;
int prev;
int len;
};
};
struct Extension
{
struct Extension {
uint8_t extension_code;
uint8_t block_size;
};
};
struct GraphicControlExtension
{
struct GraphicControlExtension {
uint8_t fields;
uint16_t delay_time;
uint8_t transparent_color_index;
};
};
struct ApplicationExtension
{
struct ApplicationExtension {
uint8_t application_id[8];
uint8_t version[3];
};
};
struct PlaintextExtension
{
struct PlaintextExtension {
uint16_t left, top, width, height;
uint8_t cell_width, cell_height;
uint8_t foreground_color, background_color;
};
};
class Gif
{
class Gif {
public:
// Descompone (uncompress) el bloque comprimido usando LZW.
// Este método puede lanzar std::runtime_error en caso de error.
void decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out);
void decompress(int code_length, const uint8_t* input, int input_length, uint8_t* out);
// Carga la paleta (global color table) a partir de un buffer,
// retornándola en un vector de uint32_t (cada color se compone de R, G, B).
std::vector<uint32_t> loadPalette(const uint8_t *buffer);
std::vector<uint32_t> loadPalette(const uint8_t* buffer);
// Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y
// asigna el ancho y alto mediante referencias.
std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h);
std::vector<uint8_t> loadGif(const uint8_t* buffer, uint16_t& w, uint16_t& h);
private:
// Lee los sub-bloques de datos y los acumula en un std::vector<uint8_t>.
std::vector<uint8_t> readSubBlocks(const uint8_t *&buffer);
std::vector<uint8_t> readSubBlocks(const uint8_t*& buffer);
// Procesa el Image Descriptor y retorna el vector de datos sin comprimir.
std::vector<uint8_t> processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits);
std::vector<uint8_t> processImageDescriptor(const uint8_t*& buffer, const std::vector<RGB>& gct, int resolution_bits);
// Procesa el stream completo del GIF y devuelve los datos sin comprimir.
std::vector<uint8_t> processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h);
};
std::vector<uint8_t> processGifStream(const uint8_t* buffer, uint16_t& w, uint16_t& h);
};
} // namespace GIF
} // namespace GIF

View File

@@ -1,6 +1,7 @@
#include "core/rendering/opengl/opengl_shader.hpp"
#include <SDL3/SDL.h>
#include <cstring>
#include <stdexcept>
#include <vector>
@@ -40,12 +41,12 @@ bool OpenGLShader::initGLExtensions() {
glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)SDL_GL_GetProcAddress("glEnableVertexAttribArray");
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog &&
glUseProgram && glDeleteProgram && glGetUniformLocation && glUniform2f &&
glGenVertexArrays && glBindVertexArray && glDeleteVertexArrays &&
glGenBuffers && glBindBuffer && glBufferData && glDeleteBuffers &&
glVertexAttribPointer && glEnableVertexAttribArray;
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog &&
glUseProgram && glDeleteProgram && glGetUniformLocation && glUniform2f &&
glGenVertexArrays && glBindVertexArray && glDeleteVertexArrays &&
glGenBuffers && glBindBuffer && glBufferData && glDeleteBuffers &&
glVertexAttribPointer && glEnableVertexAttribArray;
}
#endif
@@ -53,14 +54,16 @@ void OpenGLShader::checkGLError(const char* operation) {
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error OpenGL en %s: 0x%x", operation, error);
"Error OpenGL en %s: 0x%x",
operation,
error);
}
}
GLuint OpenGLShader::compileShader(const std::string& source, GLenum shader_type) {
if (source.empty()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ERROR: El código fuente del shader está vacío");
"ERROR: El código fuente del shader está vacío");
return 0;
}
@@ -83,14 +86,15 @@ GLuint OpenGLShader::compileShader(const std::string& source, GLenum shader_type
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &compiled);
if (compiled != GL_TRUE) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error en compilación del shader");
"Error en compilación del shader");
GLint log_length;
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
std::vector<char> log(log_length);
glGetShaderInfoLog(shader_id, log_length, &log_length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Log de compilación: %s", log.data());
"Log de compilación: %s",
log.data());
}
glDeleteShader(shader_id);
return 0;
@@ -103,7 +107,7 @@ GLuint OpenGLShader::linkProgram(GLuint vertex_shader, GLuint fragment_shader) {
GLuint program = glCreateProgram();
if (program == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error al crear programa de shaders");
"Error al crear programa de shaders");
return 0;
}
@@ -120,14 +124,15 @@ GLuint OpenGLShader::linkProgram(GLuint vertex_shader, GLuint fragment_shader) {
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (linked != GL_TRUE) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error al enlazar programa");
"Error al enlazar programa");
GLint log_length;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
std::vector<char> log(log_length);
glGetProgramInfoLog(program, log_length, &log_length, log.data());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Log de enlace: %s", log.data());
"Log de enlace: %s",
log.data());
}
glDeleteProgram(program);
return 0;
@@ -144,16 +149,32 @@ void OpenGLShader::createQuadGeometry() {
// Formato: x, y, u, v
float vertices[] = {
// Posición // TexCoords
-1.0f, -1.0f, 0.0f, 0.0f, // Inferior izquierda
1.0f, -1.0f, 1.0f, 0.0f, // Inferior derecha
1.0f, 1.0f, 1.0f, 1.0f, // Superior derecha
-1.0f, 1.0f, 0.0f, 1.0f // Superior izquierda
-1.0f,
-1.0f,
0.0f,
0.0f, // Inferior izquierda
1.0f,
-1.0f,
1.0f,
0.0f, // Inferior derecha
1.0f,
1.0f,
1.0f,
1.0f, // Superior derecha
-1.0f,
1.0f,
0.0f,
1.0f // Superior izquierda
};
// Índices para dibujar el quad con dos triángulos
unsigned int indices[] = {
0, 1, 2, // Primer triángulo
2, 3, 0 // Segundo triángulo
0,
1,
2, // Primer triángulo
2,
3,
0 // Segundo triángulo
};
// Generar y configurar VAO
@@ -207,7 +228,7 @@ GLuint OpenGLShader::getTextureID(SDL_Texture* texture) {
if (texture_id == 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"No se pudo obtener ID de textura OpenGL, usando 1 por defecto");
"No se pudo obtener ID de textura OpenGL, usando 1 por defecto");
texture_id = 1;
}
@@ -215,16 +236,16 @@ GLuint OpenGLShader::getTextureID(SDL_Texture* texture) {
}
bool OpenGLShader::init(SDL_Window* window,
SDL_Texture* texture,
const std::string& vertex_source,
const std::string& fragment_source) {
SDL_Texture* texture,
const std::string& vertex_source,
const std::string& fragment_source) {
window_ = window;
back_buffer_ = texture;
renderer_ = SDL_GetRenderer(window);
if (!renderer_) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error: No se pudo obtener el renderer");
"Error: No se pudo obtener el renderer");
return false;
}
@@ -233,14 +254,18 @@ bool OpenGLShader::init(SDL_Window* window,
SDL_GetTextureSize(back_buffer_, &texture_width_, &texture_height_);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Inicializando shaders: ventana=%dx%d, textura=%.0fx%.0f",
window_width_, window_height_, texture_width_, texture_height_);
"Inicializando shaders: ventana=%dx%d, textura=%.0fx%.0f",
window_width_,
window_height_,
texture_width_,
texture_height_);
// Verificar que es OpenGL
const char* renderer_name = SDL_GetRendererName(renderer_);
if (!renderer_name || strncmp(renderer_name, "opengl", 6) != 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Renderer no es OpenGL: %s", renderer_name ? renderer_name : "unknown");
"Renderer no es OpenGL: %s",
renderer_name ? renderer_name : "unknown");
return false;
}
@@ -248,7 +273,7 @@ bool OpenGLShader::init(SDL_Window* window,
// Inicializar extensiones OpenGL en Windows/Linux
if (!initGLExtensions()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error al inicializar extensiones OpenGL");
"Error al inicializar extensiones OpenGL");
return false;
}
#endif
@@ -265,7 +290,7 @@ bool OpenGLShader::init(SDL_Window* window,
if (vertex_shader == 0 || fragment_shader == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error al compilar shaders");
"Error al compilar shaders");
if (vertex_shader != 0) glDeleteShader(vertex_shader);
if (fragment_shader != 0) glDeleteShader(fragment_shader);
return false;
@@ -280,7 +305,7 @@ bool OpenGLShader::init(SDL_Window* window,
if (program_id_ == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error al crear programa de shaders");
"Error al crear programa de shaders");
return false;
}
@@ -292,19 +317,20 @@ bool OpenGLShader::init(SDL_Window* window,
texture_size_location_ = glGetUniformLocation(program_id_, "TextureSize");
if (texture_size_location_ != -1) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Configurando TextureSize uniform: %.0fx%.0f",
texture_width_, texture_height_);
"Configurando TextureSize uniform: %.0fx%.0f",
texture_width_,
texture_height_);
glUniform2f(texture_size_location_, texture_width_, texture_height_);
checkGLError("glUniform2f(TextureSize)");
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Uniform 'TextureSize' no encontrado en shader");
"Uniform 'TextureSize' no encontrado en shader");
}
glUseProgram(0);
is_initialized_ = true;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"** OpenGL 3.3 Shader Backend inicializado correctamente");
"** OpenGL 3.3 Shader Backend inicializado correctamente");
return true;
}
@@ -459,4 +485,4 @@ void OpenGLShader::cleanup() {
back_buffer_ = nullptr;
}
} // namespace Rendering
} // namespace Rendering

View File

@@ -19,80 +19,80 @@ namespace Rendering {
* - Shaders GLSL #version 330 core
*/
class OpenGLShader : public ShaderBackend {
public:
OpenGLShader() = default;
~OpenGLShader() override;
public:
OpenGLShader() = default;
~OpenGLShader() override;
bool init(SDL_Window* window,
SDL_Texture* texture,
const std::string& vertex_source,
const std::string& fragment_source) override;
bool init(SDL_Window* window,
SDL_Texture* texture,
const std::string& vertex_source,
const std::string& fragment_source) override;
void render() override;
void setTextureSize(float width, float height) override;
void cleanup() override;
bool isHardwareAccelerated() const override { return is_initialized_; }
void render() override;
void setTextureSize(float width, float height) override;
void cleanup() override;
bool isHardwareAccelerated() const override { return is_initialized_; }
private:
// Funciones auxiliares
bool initGLExtensions();
GLuint compileShader(const std::string& source, GLenum shader_type);
GLuint linkProgram(GLuint vertex_shader, GLuint fragment_shader);
void createQuadGeometry();
GLuint getTextureID(SDL_Texture* texture);
void checkGLError(const char* operation);
private:
// Funciones auxiliares
bool initGLExtensions();
GLuint compileShader(const std::string& source, GLenum shader_type);
GLuint linkProgram(GLuint vertex_shader, GLuint fragment_shader);
void createQuadGeometry();
GLuint getTextureID(SDL_Texture* texture);
void checkGLError(const char* operation);
// Estado SDL
SDL_Window* window_ = nullptr;
SDL_Renderer* renderer_ = nullptr;
SDL_Texture* back_buffer_ = nullptr;
// Estado SDL
SDL_Window* window_ = nullptr;
SDL_Renderer* renderer_ = nullptr;
SDL_Texture* back_buffer_ = nullptr;
// Estado OpenGL
GLuint program_id_ = 0;
GLuint vao_ = 0; // Vertex Array Object
GLuint vbo_ = 0; // Vertex Buffer Object
GLuint ebo_ = 0; // Element Buffer Object
// Estado OpenGL
GLuint program_id_ = 0;
GLuint vao_ = 0; // Vertex Array Object
GLuint vbo_ = 0; // Vertex Buffer Object
GLuint ebo_ = 0; // Element Buffer Object
// Ubicaciones de uniforms
GLint texture_size_location_ = -1;
// Ubicaciones de uniforms
GLint texture_size_location_ = -1;
// Tamaños
int window_width_ = 0;
int window_height_ = 0;
float texture_width_ = 0.0f;
float texture_height_ = 0.0f;
// Tamaños
int window_width_ = 0;
int window_height_ = 0;
float texture_width_ = 0.0f;
float texture_height_ = 0.0f;
// Estado
bool is_initialized_ = false;
// Estado
bool is_initialized_ = false;
#ifndef __APPLE__
// Punteros a funciones OpenGL en Windows/Linux
PFNGLCREATESHADERPROC glCreateShader = nullptr;
PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
PFNGLCOMPILESHADERPROC glCompileShader = nullptr;
PFNGLGETSHADERIVPROC glGetShaderiv = nullptr;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr;
PFNGLDELETESHADERPROC glDeleteShader = nullptr;
PFNGLATTACHSHADERPROC glAttachShader = nullptr;
PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr;
PFNGLLINKPROGRAMPROC glLinkProgram = nullptr;
PFNGLVALIDATEPROGRAMPROC glValidateProgram = nullptr;
PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr;
PFNGLUSEPROGRAMPROC glUseProgram = nullptr;
PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr;
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr;
PFNGLUNIFORM2FPROC glUniform2f = nullptr;
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr;
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr;
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr;
PFNGLGENBUFFERSPROC glGenBuffers = nullptr;
PFNGLBINDBUFFERPROC glBindBuffer = nullptr;
PFNGLBUFFERDATAPROC glBufferData = nullptr;
PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr;
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr;
// Punteros a funciones OpenGL en Windows/Linux
PFNGLCREATESHADERPROC glCreateShader = nullptr;
PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
PFNGLCOMPILESHADERPROC glCompileShader = nullptr;
PFNGLGETSHADERIVPROC glGetShaderiv = nullptr;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr;
PFNGLDELETESHADERPROC glDeleteShader = nullptr;
PFNGLATTACHSHADERPROC glAttachShader = nullptr;
PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr;
PFNGLLINKPROGRAMPROC glLinkProgram = nullptr;
PFNGLVALIDATEPROGRAMPROC glValidateProgram = nullptr;
PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr;
PFNGLUSEPROGRAMPROC glUseProgram = nullptr;
PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr;
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr;
PFNGLUNIFORM2FPROC glUniform2f = nullptr;
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr;
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr;
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr;
PFNGLGENBUFFERSPROC glGenBuffers = nullptr;
PFNGLBINDBUFFERPROC glBindBuffer = nullptr;
PFNGLBUFFERDATAPROC glBufferData = nullptr;
PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr;
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr;
#endif
};
} // namespace Rendering
} // namespace Rendering

View File

@@ -9,13 +9,13 @@
#include <iterator> // Para istreambuf_iterator, operator==
#include <string> // Para char_traits, string, operator+, operator==
#include "core/resources/asset.hpp" // Para Asset, AssetType
#include "core/input/mouse.hpp" // Para updateCursorVisibility
#include "game/gameplay/options.hpp" // Para Options, options, OptionsVideo, Border
#include "core/input/mouse.hpp" // Para updateCursorVisibility
#include "core/rendering/opengl/opengl_shader.hpp" // Para OpenGLShader
#include "core/resources/resource.hpp" // Para Resource
#include "core/rendering/surface.hpp" // Para Surface, readPalFile
#include "core/rendering/text.hpp" // Para Text
#include "core/rendering/surface.hpp" // Para Surface, readPalFile
#include "core/rendering/text.hpp" // Para Text
#include "core/resources/asset.hpp" // Para Asset, AssetType
#include "core/resources/resource.hpp" // Para Resource
#include "game/gameplay/options.hpp" // Para Options, options, OptionsVideo, Border
#include "game/ui/notifier.hpp" // Para Notifier
// [SINGLETON]
@@ -43,40 +43,40 @@ Screen::Screen()
initSDLVideo();
// Ajusta los tamaños
game_surface_dstrect_ = {options.video.border.width, options.video.border.height, options.game.width, options.game.height};
//adjustWindowSize();
current_palette_ = findPalette(options.video.palette);
game_surface_dstrect_ = {Options::video.border.width, Options::video.border.height, Options::game.width, Options::game.height};
// adjustWindowSize();
current_palette_ = findPalette(Options::video.palette);
// Define el color del borde para el modo de pantalla completa
border_color_ = static_cast<Uint8>(PaletteColor::BLACK);
// Crea la textura donde se dibujan los graficos del juego
game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, options.game.width, options.game.height);
game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width, Options::game.height);
if (!game_texture_) {
// Registrar el error si está habilitado
if (options.console) {
if (Options::console) {
std::cerr << "Error: game_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
}
}
SDL_SetTextureScaleMode(game_texture_, SDL_SCALEMODE_NEAREST);
// Crea la textura donde se dibuja el borde que rodea el area de juego
border_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, options.game.width + options.video.border.width * 2, options.game.height + options.video.border.height * 2);
border_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width + Options::video.border.width * 2, Options::game.height + Options::video.border.height * 2);
if (!border_texture_) {
// Registrar el error si está habilitado
if (options.console) {
if (Options::console) {
std::cerr << "Error: border_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
}
}
SDL_SetTextureScaleMode(border_texture_, SDL_SCALEMODE_NEAREST);
// Crea la surface donde se dibujan los graficos del juego
game_surface_ = std::make_shared<Surface>(options.game.width, options.game.height);
game_surface_ = std::make_shared<Surface>(Options::game.width, Options::game.height);
game_surface_->setPalette(readPalFile(palettes_.at(current_palette_)));
game_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));
// Crea la surface para el borde de colores
border_surface_ = std::make_shared<Surface>(options.game.width + options.video.border.width * 2, options.game.height + options.video.border.height * 2);
border_surface_ = std::make_shared<Surface>(Options::game.width + Options::video.border.width * 2, Options::game.height + Options::video.border.height * 2);
border_surface_->setPalette(readPalFile(palettes_.at(current_palette_)));
border_surface_->clear(border_color_);
@@ -127,29 +127,29 @@ void Screen::render() {
// Establece el modo de video
void Screen::setVideoMode(bool mode) {
// Actualiza las opciones
options.video.fullscreen = mode;
Options::video.fullscreen = mode;
// Configura el modo de pantalla y ajusta la ventana
SDL_SetWindowFullscreen(window_, options.video.fullscreen);
SDL_SetWindowFullscreen(window_, Options::video.fullscreen);
adjustWindowSize();
adjustRenderLogicalSize();
}
// Camibia entre pantalla completa y ventana
void Screen::toggleVideoMode() {
options.video.fullscreen = !options.video.fullscreen;
setVideoMode(options.video.fullscreen);
Options::video.fullscreen = !Options::video.fullscreen;
setVideoMode(Options::video.fullscreen);
}
// Reduce el tamaño de la ventana
bool Screen::decWindowZoom() {
if (options.video.fullscreen == 0) {
const int PREVIOUS_ZOOM = options.window.zoom;
--options.window.zoom;
options.window.zoom = std::max(options.window.zoom, 1);
if (Options::video.fullscreen == 0) {
const int PREVIOUS_ZOOM = Options::window.zoom;
--Options::window.zoom;
Options::window.zoom = std::max(Options::window.zoom, 1);
if (options.window.zoom != PREVIOUS_ZOOM) {
setVideoMode(options.video.fullscreen);
if (Options::window.zoom != PREVIOUS_ZOOM) {
setVideoMode(Options::video.fullscreen);
return true;
}
}
@@ -159,13 +159,13 @@ bool Screen::decWindowZoom() {
// Aumenta el tamaño de la ventana
bool Screen::incWindowZoom() {
if (options.video.fullscreen == 0) {
const int PREVIOUS_ZOOM = options.window.zoom;
++options.window.zoom;
options.window.zoom = std::min(options.window.zoom, options.window.max_zoom);
if (Options::video.fullscreen == 0) {
const int PREVIOUS_ZOOM = Options::window.zoom;
++Options::window.zoom;
Options::window.zoom = std::min(Options::window.zoom, Options::window.max_zoom);
if (options.window.zoom != PREVIOUS_ZOOM) {
setVideoMode(options.video.fullscreen);
if (Options::window.zoom != PREVIOUS_ZOOM) {
setVideoMode(Options::video.fullscreen);
return true;
}
}
@@ -181,8 +181,8 @@ void Screen::setBorderColor(Uint8 color) {
// Cambia entre borde visible y no visible
void Screen::toggleBorder() {
options.video.border.enabled = !options.video.border.enabled;
setVideoMode(options.video.fullscreen);
Options::video.border.enabled = !Options::video.border.enabled;
setVideoMode(Options::video.fullscreen);
initShaders();
}
@@ -195,7 +195,7 @@ void Screen::renderNotifications() {
// Cambia el estado de los shaders
void Screen::toggleShaders() {
options.video.shaders = !options.video.shaders;
Options::video.shaders = !Options::video.shaders;
initShaders();
}
@@ -208,28 +208,28 @@ void Screen::update() {
// Calcula el tamaño de la ventana
void Screen::adjustWindowSize() {
window_width_ = options.game.width + (options.video.border.enabled ? options.video.border.width * 2 : 0);
window_height_ = options.game.height + (options.video.border.enabled ? options.video.border.height * 2 : 0);
window_width_ = Options::game.width + (Options::video.border.enabled ? Options::video.border.width * 2 : 0);
window_height_ = Options::game.height + (Options::video.border.enabled ? Options::video.border.height * 2 : 0);
// Establece el nuevo tamaño
if (options.video.fullscreen == 0) {
if (Options::video.fullscreen == 0) {
int old_width, old_height;
SDL_GetWindowSize(window_, &old_width, &old_height);
int old_pos_x, old_pos_y;
SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y);
const int NEW_POS_X = old_pos_x + (old_width - (window_width_ * options.window.zoom)) / 2;
const int NEW_POS_Y = old_pos_y + (old_height - (window_height_ * options.window.zoom)) / 2;
const int NEW_POS_X = old_pos_x + (old_width - (window_width_ * Options::window.zoom)) / 2;
const int NEW_POS_Y = old_pos_y + (old_height - (window_height_ * Options::window.zoom)) / 2;
SDL_SetWindowSize(window_, window_width_ * options.window.zoom, window_height_ * options.window.zoom);
SDL_SetWindowSize(window_, window_width_ * Options::window.zoom, window_height_ * Options::window.zoom);
SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS), std::max(NEW_POS_Y, 0));
}
}
// Ajusta el tamaño lógico del renderizador
void Screen::adjustRenderLogicalSize() {
SDL_SetRenderLogicalPresentation(renderer_, window_width_, window_height_, options.video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
SDL_SetRenderLogicalPresentation(renderer_, window_width_, window_height_, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
}
// Establece el renderizador para las surfaces
@@ -263,16 +263,16 @@ void Screen::setPalete() {
game_surface_->loadPalette(Resource::get()->getPalette(palettes_.at(current_palette_)));
border_surface_->loadPalette(Resource::get()->getPalette(palettes_.at(current_palette_)));
options.video.palette = palettes_.at(current_palette_);
Options::video.palette = palettes_.at(current_palette_);
// Eliminar ".gif"
size_t pos = options.video.palette.find(".pal");
size_t pos = Options::video.palette.find(".pal");
if (pos != std::string::npos) {
options.video.palette.erase(pos, 4);
Options::video.palette.erase(pos, 4);
}
// Convertir a mayúsculas
std::transform(options.video.palette.begin(), options.video.palette.end(), options.video.palette.begin(), ::toupper);
std::transform(Options::video.palette.begin(), Options::video.palette.end(), Options::video.palette.begin(), ::toupper);
}
// Extrae los nombres de las paletas
@@ -284,7 +284,7 @@ void Screen::processPaletteList() {
// Copia la surface a la textura
void Screen::surfaceToTexture() {
if (options.video.border.enabled) {
if (Options::video.border.enabled) {
border_surface_->copyToTexture(renderer_, border_texture_);
game_surface_->copyToTexture(renderer_, border_texture_, nullptr, &game_surface_dstrect_);
} else {
@@ -294,9 +294,9 @@ void Screen::surfaceToTexture() {
// Copia la textura al renderizador
void Screen::textureToRenderer() {
SDL_Texture* texture_to_render = options.video.border.enabled ? border_texture_ : game_texture_;
SDL_Texture* texture_to_render = Options::video.border.enabled ? border_texture_ : game_texture_;
if (options.video.shaders && shader_backend_) {
if (Options::video.shaders && shader_backend_) {
shader_backend_->render();
} else {
SDL_SetRenderTarget(renderer_, nullptr);
@@ -333,7 +333,7 @@ void Screen::renderInfo() {
// FPS
const std::string FPS_TEXT = std::to_string(fps_.lastValue) + " FPS";
text->writeColored(options.game.width - text->lenght(FPS_TEXT), 0, FPS_TEXT, color);
text->writeColored(Options::game.width - text->lenght(FPS_TEXT), 0, FPS_TEXT, color);
// Resolution
text->writeColored(0, 0, info_resolution_, color);
@@ -344,13 +344,13 @@ void Screen::renderInfo() {
void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); }
// Establece el tamaño del borde
void Screen::setBorderWidth(int width) { options.video.border.width = width; }
void Screen::setBorderWidth(int width) { Options::video.border.width = width; }
// Establece el tamaño del borde
void Screen::setBorderHeight(int height) { options.video.border.height = height; }
void Screen::setBorderHeight(int height) { Options::video.border.height = height; }
// Establece si se ha de ver el borde en el modo ventana
void Screen::setBorderEnabled(bool value) { options.video.border.enabled = value; }
void Screen::setBorderEnabled(bool value) { Options::video.border.enabled = value; }
// Muestra la ventana
void Screen::show() { SDL_ShowWindow(window_); }
@@ -366,8 +366,8 @@ void Screen::toggleDebugInfo() { show_debug_info_ = !show_debug_info_; }
// Alterna entre activar y desactivar el escalado entero
void Screen::toggleIntegerScale() {
options.video.integer_scale = !options.video.integer_scale;
SDL_SetRenderLogicalPresentation(renderer_, options.game.width, options.game.height, options.video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
Options::video.integer_scale = !Options::video.integer_scale;
SDL_SetRenderLogicalPresentation(renderer_, Options::game.width, Options::game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
}
// Getters
@@ -436,13 +436,13 @@ void Screen::loadShaders() {
// Inicializa los shaders
void Screen::initShaders() {
#ifndef __APPLE__
if (options.video.shaders) {
if (Options::video.shaders) {
loadShaders();
if (!shader_backend_) {
shader_backend_ = std::make_unique<Rendering::OpenGLShader>();
}
shader_backend_->init(window_, options.video.border.enabled ? border_texture_ : game_texture_, vertex_shader_source_, fragment_shader_source_);
//shader_backend_->init(window_, shaders_texture_, vertex_shader_source_, fragment_shader_source_);
shader_backend_->init(window_, Options::video.border.enabled ? border_texture_ : game_texture_, vertex_shader_source_, fragment_shader_source_);
// shader_backend_->init(window_, shaders_texture_, vertex_shader_source_, fragment_shader_source_);
}
#else
// En macOS, OpenGL está deprecated y rinde mal
@@ -475,23 +475,23 @@ void Screen::getDisplayInfo() {
display_monitor_.refresh_rate = static_cast<int>(dm->refresh_rate);
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
options.window.max_zoom = std::min(dm->w / options.game.width, dm->h / options.game.height);
options.window.zoom = std::min(options.window.zoom, options.window.max_zoom);
Options::window.max_zoom = std::min(dm->w / Options::game.width, dm->h / Options::game.height);
Options::window.zoom = std::min(Options::window.zoom, Options::window.max_zoom);
// Muestra información sobre el tamaño de la pantalla y de la ventana de juego
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Current display mode: %dx%d @ %dHz", static_cast<int>(dm->w), static_cast<int>(dm->h), static_cast<int>(dm->refresh_rate));
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d", static_cast<int>(options.game.width), static_cast<int>(options.game.height), options.window.zoom);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d", static_cast<int>(Options::game.width), static_cast<int>(Options::game.height), Options::window.zoom);
options.video.info = std::to_string(static_cast<int>(dm->w)) + "x" +
Options::video.info = std::to_string(static_cast<int>(dm->w)) + "x" +
std::to_string(static_cast<int>(dm->h)) + " @ " +
std::to_string(static_cast<int>(dm->refresh_rate)) + " Hz";
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
const int MAX_ZOOM = std::min(dm->w / options.game.width, (dm->h - WINDOWS_DECORATIONS) / options.game.height);
const int MAX_ZOOM = std::min(dm->w / Options::game.width, (dm->h - WINDOWS_DECORATIONS) / Options::game.height);
// Normaliza los valores de zoom
options.window.zoom = std::min(options.window.zoom, MAX_ZOOM);
Options::window.zoom = std::min(Options::window.zoom, MAX_ZOOM);
SDL_free(displays);
}
@@ -536,17 +536,17 @@ auto Screen::initSDLVideo() -> bool {
#endif
// Crear ventana
const auto WINDOW_WIDTH = options.video.border.enabled ? options.game.width + options.video.border.width * 2 : options.game.width;
const auto WINDOW_HEIGHT = options.video.border.enabled ? options.game.height + options.video.border.height * 2 : options.game.height;
const auto WINDOW_WIDTH = Options::video.border.enabled ? Options::game.width + Options::video.border.width * 2 : Options::game.width;
const auto WINDOW_HEIGHT = Options::video.border.enabled ? Options::game.height + Options::video.border.height * 2 : Options::game.height;
#ifdef __APPLE__
SDL_WindowFlags window_flags = SDL_WINDOW_METAL;
#else
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL;
#endif
if (options.video.fullscreen) {
if (Options::video.fullscreen) {
window_flags |= SDL_WINDOW_FULLSCREEN;
}
window_ = SDL_CreateWindow(options.window.caption.c_str(), WINDOW_WIDTH * options.window.zoom, WINDOW_HEIGHT * options.window.zoom, window_flags);
window_ = SDL_CreateWindow(Options::window.caption.c_str(), WINDOW_WIDTH * Options::window.zoom, WINDOW_HEIGHT * Options::window.zoom, window_flags);
if (window_ == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -569,16 +569,16 @@ auto Screen::initSDLVideo() -> bool {
}
// Configurar renderer
const int EXTRA_WIDTH = options.video.border.enabled ? options.video.border.width * 2 : 0;
const int EXTRA_HEIGHT = options.video.border.enabled ? options.video.border.height * 2 : 0;
const int EXTRA_WIDTH = Options::video.border.enabled ? Options::video.border.width * 2 : 0;
const int EXTRA_HEIGHT = Options::video.border.enabled ? Options::video.border.height * 2 : 0;
SDL_SetRenderLogicalPresentation(
renderer_,
options.game.width + EXTRA_WIDTH,
options.game.height + EXTRA_HEIGHT,
options.video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
Options::game.width + EXTRA_WIDTH,
Options::game.height + EXTRA_HEIGHT,
Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF);
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
SDL_SetRenderVSync(renderer_, options.video.vertical_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
SDL_SetRenderVSync(renderer_, Options::video.vertical_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Video system initialized successfully");
return true;

View File

@@ -1,6 +1,7 @@
#pragma once
#include <SDL3/SDL.h>
#include <string>
namespace Rendering {
@@ -12,44 +13,44 @@ namespace Rendering {
* deben cumplir (OpenGL, Metal, Vulkan, etc.)
*/
class ShaderBackend {
public:
virtual ~ShaderBackend() = default;
public:
virtual ~ShaderBackend() = default;
/**
* @brief Inicializa el backend de shaders
* @param window Ventana SDL
* @param texture Textura de backbuffer a la que aplicar shaders
* @param vertex_source Código fuente del vertex shader
* @param fragment_source Código fuente del fragment shader
* @return true si la inicialización fue exitosa
*/
virtual bool init(SDL_Window* window,
SDL_Texture* texture,
const std::string& vertex_source,
const std::string& fragment_source) = 0;
/**
* @brief Inicializa el backend de shaders
* @param window Ventana SDL
* @param texture Textura de backbuffer a la que aplicar shaders
* @param vertex_source Código fuente del vertex shader
* @param fragment_source Código fuente del fragment shader
* @return true si la inicialización fue exitosa
*/
virtual bool init(SDL_Window* window,
SDL_Texture* texture,
const std::string& vertex_source,
const std::string& fragment_source) = 0;
/**
* @brief Renderiza la textura con los shaders aplicados
*/
virtual void render() = 0;
/**
* @brief Renderiza la textura con los shaders aplicados
*/
virtual void render() = 0;
/**
* @brief Establece el tamaño de la textura como parámetro del shader
* @param width Ancho de la textura
* @param height Alto de la textura
*/
virtual void setTextureSize(float width, float height) = 0;
/**
* @brief Establece el tamaño de la textura como parámetro del shader
* @param width Ancho de la textura
* @param height Alto de la textura
*/
virtual void setTextureSize(float width, float height) = 0;
/**
* @brief Limpia y libera recursos del backend
*/
virtual void cleanup() = 0;
/**
* @brief Limpia y libera recursos del backend
*/
virtual void cleanup() = 0;
/**
* @brief Verifica si el backend está usando aceleración por hardware
* @return true si usa aceleración (OpenGL/Metal/Vulkan)
*/
virtual bool isHardwareAccelerated() const = 0;
/**
* @brief Verifica si el backend está usando aceleración por hardware
* @return true si usa aceleración (OpenGL/Metal/Vulkan)
*/
virtual bool isHardwareAccelerated() const = 0;
};
} // namespace Rendering
} // namespace Rendering

View File

@@ -8,7 +8,7 @@
#include <stdexcept> // Para runtime_error
#include "core/rendering/surface.hpp" // Para Surface
#include "utils/utils.hpp" // Para printWithDots
#include "utils/utils.hpp" // Para printWithDots
// Carga las animaciones en un vector(Animations) desde un fichero
Animations loadAnimationsFromFile(const std::string& file_path) {

View File

@@ -7,7 +7,7 @@
#include <vector> // Para vector
#include "core/rendering/surface_moving_sprite.hpp" // Para SMovingSprite
class Surface; // lines 9-9
class Surface; // lines 9-9
struct AnimationData {
std::string name; // Nombre de la animacion

View File

@@ -5,7 +5,7 @@
#include <memory> // Para shared_ptr
#include "core/rendering/surface_sprite.hpp" // Para SSprite
class Surface; // lines 8-8
class Surface; // lines 8-8
// Clase SMovingSprite. Añade movimiento y flip al sprite
class SMovingSprite : public SSprite {

View File

@@ -7,10 +7,10 @@
#include <iostream> // Para cerr
#include <stdexcept> // Para runtime_error
#include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/surface.hpp" // Para Surface
#include "core/rendering/surface_sprite.hpp" // Para SSprite
#include "core/rendering/surface.hpp" // Para Surface
#include "utils/utils.hpp" // Para getFileName, stringToColor, printWithDots
#include "utils/utils.hpp" // Para getFileName, stringToColor, printWithDots
// Llena una estructuta TextFile desde un fichero
std::shared_ptr<TextFile> loadTextFile(const std::string& file_path) {

View File

@@ -6,7 +6,7 @@
#include <string> // Para string
#include "core/rendering/surface_sprite.hpp" // Para SSprite
class Surface; // lines 8-8
class Surface; // lines 8-8
constexpr int TEXT_COLOR = 1;
constexpr int TEXT_SHADOW = 2;

View File

@@ -1,7 +1,7 @@
#include "core/rendering/texture.hpp"
#include <SDL3/SDL.h>
#include <SDL3/SDL.h>
#include <iostream> // Para basic_ostream, operator<<, endl, cout
#include <stdexcept> // Para runtime_error
@@ -64,7 +64,7 @@ bool Texture::loadFromFile(const std::string& file_path) {
SDL_Texture* newTexture = nullptr;
// Carga la imagen desde una ruta específica
auto *loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, static_cast<void *>(data), pitch);
auto* loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, static_cast<void*>(data), pitch);
if (loaded_surface == nullptr) {
std::cout << "Unable to load image " << file_path << std::endl;
} else {

View File

@@ -14,8 +14,8 @@ class Texture {
// Variables
std::string path_; // Ruta de la imagen de la textura
float width_ = 0.0F; // Ancho de la imagen
float height_ = 0.0F; // Alto de la imagen
float width_ = 0.0F; // Ancho de la imagen
float height_ = 0.0F; // Alto de la imagen
std::vector<std::vector<Uint32>> palettes_; // Vector con las diferentes paletas
// Libera la memoria de la textura