From c9e75ad5c8e2a58825fe7d8a85e63ad047a53476 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Mon, 3 Mar 2025 14:26:07 +0100 Subject: [PATCH] =?UTF-8?q?bfff,=20a=20casa=20a=20meitat=20enfang=C3=A0..?= =?UTF-8?q?=20be..=20Estic=20acabant=20de=20convertir=20Title?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/loading_screen.cpp | 6 +- source/resource.cpp | 2 +- source/screen.cpp | 12 ++++ source/screen.h | 8 ++- source/surface.cpp | 131 +++++++++++++++++++++++++------------- source/surface.h | 47 ++++++++------ source/title.cpp | 29 ++++----- source/utils.cpp | 35 ++++++++++ source/utils.h | 1 + 9 files changed, 190 insertions(+), 81 deletions(-) diff --git a/source/loading_screen.cpp b/source/loading_screen.cpp index d33ffab..3f96356 100644 --- a/source/loading_screen.cpp +++ b/source/loading_screen.cpp @@ -22,11 +22,11 @@ LoadingScreen::LoadingScreen() color_loading_screen_surface_(Resource::get()->getSurface("loading_screen_color.gif")), mono_loading_screen_sprite_(std::make_shared(mono_loading_screen_surface_, 0, 0, mono_loading_screen_surface_->getWidth(), mono_loading_screen_surface_->getHeight())), color_loading_screen_sprite_(std::make_shared(color_loading_screen_surface_, 0, 0, color_loading_screen_surface_->getWidth(), color_loading_screen_surface_->getHeight())), - screen_surface_(std::make_shared(Screen::get()->getSurface(), options.game.width, options.game.height)) + screen_surface_(std::make_shared(Screen::get()->getRenderSurface(), options.game.width, options.game.height)) { // Cambia el destino de las surfaces - mono_loading_screen_surface_->setSurfaceDest(screen_surface_->getSurface()); - color_loading_screen_surface_->setSurfaceDest(screen_surface_->getSurface()); + mono_loading_screen_surface_->setSurfaceDataDest(screen_surface_->getSurfaceData()); + color_loading_screen_surface_->setSurfaceDataDest(screen_surface_->getSurfaceData()); // Configura la superficie donde se van a pintar los sprites screen_surface_->setColor(0, 0xFF000000); diff --git a/source/resource.cpp b/source/resource.cpp index 7f828a8..e7aaa7a 100644 --- a/source/resource.cpp +++ b/source/resource.cpp @@ -247,7 +247,7 @@ void Resource::loadSurfaces() for (const auto &l : list) { auto name = getFileName(l); - surfaces_.emplace_back(ResourceSurface(name, std::make_shared(Screen::get()->getSurface(), l))); + surfaces_.emplace_back(ResourceSurface(name, std::make_shared(Screen::get()->getRenderSurface(), l))); updateLoadingProgress(); } } diff --git a/source/screen.cpp b/source/screen.cpp index 598140f..5b0435a 100644 --- a/source/screen.cpp +++ b/source/screen.cpp @@ -84,6 +84,12 @@ void Screen::clean(Color color) SDL_RenderClear(renderer_); } +// Limpia la pantalla +void Screen::clean(Uint8 index) +{ + surface_->clear(index); +} + // Prepara para empezar a dibujar en la textura de juego void Screen::start() { SDL_SetRenderTarget(renderer_, game_texture_); } @@ -340,3 +346,9 @@ void Screen::resetShaders() shader::init(window_, options.video.border.enabled ? border_texture_ : game_texture_, source.c_str()); } } + +// Establece el renderizador para las surfaces +void Screen::setRenderSurface(std::shared_ptr surface) +{ + (surface) ? surface_->redirectSurfaceDataTo(surface) : surface_->restoreOriginalSurfaceData(); +} \ No newline at end of file diff --git a/source/screen.h b/source/screen.h index f3b321a..9bb5cda 100644 --- a/source/screen.h +++ b/source/screen.h @@ -78,6 +78,7 @@ public: // Limpia la pantalla void clean(Color color = {0x00, 0x00, 0x00}); + void clean(Uint8 index); // Prepara para empezar a dibujar en la textura de juego void start(); @@ -132,9 +133,14 @@ public: // Obtiene el tamaño máximo de zoom posible para la ventana int getMaxZoom(); + // Establece el renderizador para las surfaces + void setRenderSurface(std::shared_ptr surface); + + // Limpia + // Getters SDL_Renderer *getRenderer() { return renderer_; } - std::shared_ptr getSurface() { return surface_->getSurface(); } + std::shared_ptr getRenderSurface() { return surface_->getSurfaceData(); } SDL_Texture *getGameTexture() { return game_texture_; }; SDL_Texture *getBorderTexture() { return border_texture_; } }; \ No newline at end of file diff --git a/source/surface.cpp b/source/surface.cpp index 485ab1d..f305d4a 100644 --- a/source/surface.cpp +++ b/source/surface.cpp @@ -1,23 +1,25 @@ #include "surface.h" -#include // for SDL_GetError -#include // for size_t -#include // for min, copy, fill -#include // for basic_ostream, basic_ifstream, basic_ios -#include // for cerr, cout -#include // for shared_ptr, __shared_ptr_access, unique_ptr -#include // for runtime_error -#include // for vector -#include "asset.h" // for Asset -#include "gif.h" // for LoadGif, LoadPalette +#include // for SDL_GetError +#include // for size_t +#include // for min, copy, fill +#include // for basic_ostream, basic_ifstream, basic_ios +#include // for cerr, cout +#include // for shared_ptr, __shared_ptr_access, unique_ptr +#include // for runtime_error +#include // for vector +#include "asset.h" // for Asset +#include "gif.h" // for LoadGif, LoadPalette Surface::Surface(std::shared_ptr surface_dest, int w, int h) - : surface_dest_(surface_dest), - surface_(std::make_shared(w, h)), + : surface_data_dest_(surface_dest), + surface_data_(std::make_shared(w, h)), + original_surface_data_(surface_data_), transparent_color_(0) {} Surface::Surface(std::shared_ptr surface_dest, const std::string &file_path) - : surface_dest_(surface_dest), - surface_(std::make_shared(loadSurface(Asset::get()->get(file_path)))), + : surface_data_dest_(surface_dest), + surface_data_(std::make_shared(loadSurface(Asset::get()->get(file_path)))), + original_surface_data_(surface_data_), transparent_color_(0) {} Surface::~Surface() {} @@ -101,8 +103,8 @@ void Surface::setColor(int index, Uint32 color) // Rellena la superficie con un color void Surface::clear(Uint8 color) { - const size_t total_pixels = surface_->width * surface_->height; - std::fill(surface_->data, surface_->data + total_pixels, color); + const size_t total_pixels = surface_data_->width * surface_data_->height; + std::fill(surface_data_->data, surface_data_->data + total_pixels, color); } // Pone un pixel en la superficie de destino @@ -113,43 +115,43 @@ void Surface::putPixel(int x, int y, Uint8 color) return; // Color transparente, no dibujar } - if (x < 0 || y < 0 || x >= surface_dest_->width || y >= surface_dest_->height) + if (x < 0 || y < 0 || x >= surface_data_dest_->width || y >= surface_data_dest_->height) { return; // Coordenadas fuera de rango } - const int index = x + y * surface_dest_->width; - surface_dest_->data[index] = color; + const int index = x + y * surface_data_dest_->width; + surface_data_dest_->data[index] = color; } // Obtiene el color de un pixel de la superficie de origen Uint8 Surface::getPixel(int x, int y) { - return surface_->data[x + y * surface_->width]; + return surface_data_->data[x + y * surface_data_->width]; } // Copia una región de la superficie de origen a la de destino void Surface::render(int dx, int dy, int sx, int sy, int w, int h) { - if (!surface_ || !surface_dest_) + if (!surface_data_ || !surface_data_dest_) { throw std::runtime_error("Surface source or destination is null."); } // Limitar la región para evitar accesos fuera de rango - w = std::min(w, surface_->width - sx); - h = std::min(h, surface_->height - sy); - w = std::min(w, surface_dest_->width - dx); - h = std::min(h, surface_dest_->height - dy); + w = std::min(w, surface_data_->width - sx); + h = std::min(h, surface_data_->height - sy); + w = std::min(w, surface_data_dest_->width - dx); + h = std::min(h, surface_data_dest_->height - dy); for (int iy = 0; iy < h; ++iy) { for (int ix = 0; ix < w; ++ix) { - Uint8 color = surface_->data[(sx + ix) + (sy + iy) * surface_->width]; - if (color != transparent_color_) // Opcional: Ignorar píxeles transparentes + Uint8 color = surface_data_->data[(sx + ix) + (sy + iy) * surface_data_->width]; + if (color != transparent_color_) { - surface_dest_->data[(dx + ix) + (dy + iy) * surface_dest_->width] = color; + surface_data_dest_->data[(dx + ix) + (dy + iy) * surface_data_dest_->width] = color; } } } @@ -158,7 +160,7 @@ void Surface::render(int dx, int dy, int sx, int sy, int w, int h) // Copia una región de la superficie de origen a la de destino void Surface::render(int x, int y, SDL_Rect *clip, SDL_RendererFlip flip) { - if (!surface_ || !surface_dest_) + if (!surface_data_ || !surface_data_dest_) { throw std::runtime_error("Surface source or destination is null."); } @@ -166,14 +168,14 @@ void Surface::render(int x, int y, SDL_Rect *clip, SDL_RendererFlip flip) // Determina la región de origen (clip) a renderizar int sx = (clip) ? clip->x : 0; int sy = (clip) ? clip->y : 0; - int w = (clip) ? clip->w : surface_->width; - int h = (clip) ? clip->h : surface_->height; + int w = (clip) ? clip->w : surface_data_->width; + int h = (clip) ? clip->h : surface_data_->height; // Limitar la región para evitar accesos fuera de rango - w = std::min(w, surface_->width - sx); - h = std::min(h, surface_->height - sy); - w = std::min(w, surface_dest_->width - x); - h = std::min(h, surface_dest_->height - y); + 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 for (int iy = 0; iy < h; ++iy) @@ -189,15 +191,58 @@ void Surface::render(int x, int y, SDL_Rect *clip, SDL_RendererFlip flip) int dest_y = y + iy; // Copia el píxel si no es transparente - Uint8 color = surface_->data[src_x + src_y * surface_->width]; - if (color != transparent_color_) // Opcional: Ignorar píxeles transparentes + Uint8 color = surface_data_->data[src_x + src_y * surface_data_->width]; + if (color != transparent_color_) { - surface_dest_->data[dest_x + dest_y * surface_dest_->width] = color; + surface_data_dest_->data[dest_x + dest_y * surface_data_dest_->width] = color; } } } } +// 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 = 0, Uint8 target_color = 0, SDL_Rect *clip = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE) +{ + if (!surface_data_ || !surface_data_dest_) + { + throw std::runtime_error("Surface source or destination is null."); + } + + // Determina la región de origen (clip) a renderizar + int sx = (clip) ? clip->x : 0; + int sy = (clip) ? clip->y : 0; + int w = (clip) ? clip->w : surface_data_->width; + int h = (clip) ? clip->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); + 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 + 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; + + // Copia el píxel si no es transparente + Uint8 color = surface_data_->data[src_x + src_y * surface_data_->width]; + if (color != transparent_color_) + { + surface_data_dest_->data[dest_x + dest_y * surface_data_dest_->width] = + (color == source_color) ? target_color : color; + } + } + } +} // Vuelca la superficie a una textura void Surface::copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture) @@ -207,7 +252,7 @@ void Surface::copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture) throw std::runtime_error("Renderer or texture is null."); } - if (surface_->width <= 0 || surface_->height <= 0 || !surface_->data) + if (surface_data_->width <= 0 || surface_data_->height <= 0 || !surface_data_->data) { throw std::runtime_error("Invalid surface dimensions or data."); } @@ -224,15 +269,15 @@ void Surface::copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture) // Convertir `pitch` de bytes a Uint32 (asegurando alineación correcta en hardware) int row_stride = pitch / sizeof(Uint32); - for (int y = 0; y < surface_->height; ++y) + for (int y = 0; y < surface_data_->height; ++y) { - for (int x = 0; x < surface_->width; ++x) + for (int x = 0; x < surface_data_->width; ++x) { // Calcular la posición correcta en la textura teniendo en cuenta el stride int texture_index = y * row_stride + x; - int surface_index = y * surface_->width + x; + int surface_index = y * surface_data_->width + x; - pixels[texture_index] = palette_[surface_->data[surface_index]]; + pixels[texture_index] = palette_[surface_data_->data[surface_index]]; } } diff --git a/source/surface.h b/source/surface.h index d5403cf..5d4bea1 100644 --- a/source/surface.h +++ b/source/surface.h @@ -8,9 +8,9 @@ struct SurfaceData { - Uint8 *data; - Uint16 width; - Uint16 height; + Uint8 *data; // Listado de indices de la paleta que conforman la imagen almacenada + Uint16 width; // Ancho de la imagen + Uint16 height; // Alto de la imagen // Constructor por defecto SurfaceData() : data(nullptr), width(0), height(0) {} @@ -60,10 +60,11 @@ struct SurfaceData class Surface { private: - std::shared_ptr surface_dest_; - std::shared_ptr surface_; - std::array palette_; - int transparent_color_; + std::shared_ptr surface_data_dest_; // Puntero a la SurfaceData remota donde copiar la información + std::shared_ptr surface_data_; // SurfaceData propia + std::shared_ptr original_surface_data_; // SurfaceData original para restauración + std::array palette_; // Paleta para volcar la SurfaceData a una Textura + int transparent_color_; // Indice de la paleta que se omite en la copia de datos // Pone un pixel en la superficie de destino void putPixel(int x, int y, Uint8 color); @@ -73,43 +74,53 @@ private: public: // Constructor - Surface(std::shared_ptr surface_dest, int w, int h); - Surface(std::shared_ptr surface_dest, const std::string &file_path); + Surface(std::shared_ptr surface_data_dest, int w, int h); + Surface(std::shared_ptr surface_data_dest, const std::string &file_path); // Destructor ~Surface(); - // Carga una superficie desde un archivo + // Carga una SurfaceData desde un archivo SurfaceData loadSurface(const std::string &file_path); // Carga una paleta desde un archivo void loadPalette(const std::string &file_path); - // Copia una región de la superficie de origen a la superficie de destino + // Copia una región de la SurfaceData de origen a la SurfaceData de destino void render(int dx, int dy, int sx, int sy, int w, int h); void render(int x, int y, SDL_Rect *clip = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE); + // Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro + void renderWithColorReplace(int x, int y, Uint8 source_color = 0, Uint8 target_color = 0, SDL_Rect *clip = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE); + // Establece un color en la paleta void setColor(int index, Uint32 color); - // Rellena la superficie con un color + // Rellena la SurfaceData con un color void clear(Uint8 color); - // Vuelca la superficie a una textura + // Vuelca la SurfaceData a una textura void copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture); // Realiza un efecto de fundido en la paleta bool fadePalette(); // Getters - std::shared_ptr getSurface() const { return surface_; } + std::shared_ptr getSurfaceData() const { return surface_data_; } int getTransparentColor() const { return transparent_color_; } - int getWidth() const { return surface_->width; } - int getHeight() const { return surface_->height; } + int getWidth() const { return surface_data_->width; } + int getHeight() const { return surface_data_->height; } // Setters void setTransparentColor(int color) { transparent_color_ = color; } - void setSurfaceDest(std::shared_ptr surface_dest) { surface_dest_ = surface_dest; } + void setSurfaceDataDest(std::shared_ptr surface_data_dest) { surface_data_dest_ = surface_data_dest; } void setPalette(const std::array &palette) { palette_ = palette; } - void setSurface(std::shared_ptr surface) { surface_ = surface; } + void setSurface(std::shared_ptr surface) { surface_data_ = surface; } + + // Permite que una Surface apunte al SurfaceData de otra Surface + void redirectSurfaceDataTo(const std::shared_ptr &newSurfaceData) { surface_data_ = newSurfaceData; } + void redirectSurfaceDataTo(const std::shared_ptr &otherSurface) { surface_data_ = otherSurface->getSurfaceData(); } + + // Método para restaurar + void restoreOriginalSurfaceData() { surface_data_ = original_surface_data_; } }; diff --git a/source/title.cpp b/source/title.cpp index 584246c..8608507 100644 --- a/source/title.cpp +++ b/source/title.cpp @@ -17,7 +17,7 @@ #include "screen.h" // for Screen #include "s_sprite.h" // for SSprite #include "text.h" // for Text, TEXT_CENTER, TEXT_COLOR -#include "surface.h" // for Texture +#include "surface.h" // for Surface #include "utils.h" // for Color, stringToColor, Palette #include "paleta.h" @@ -25,7 +25,7 @@ Title::Title() : surface_(Resource::get()->getSurface("title_logo.gif")), sprite_(std::make_shared(surface_, 0, 0, surface_->getWidth(), surface_->getHeight())), - bg_surface_(std::make_shared(Screen::get()->getSurface(), options.game.width, options.game.height)) + bg_surface_(std::make_shared(Screen::get()->getRenderSurface(), options.game.width, options.game.height)) { // Carga la surface con los gráficos de la pantalla de carga pInit(Screen::get()->getRenderer(), 256, 128); @@ -56,7 +56,6 @@ Title::Title() Title::~Title() { pDeleteSurface(loading_screen_); - SDL_DestroyTexture(bg_surface_); } // Inicializa la marquesina @@ -247,7 +246,7 @@ void Title::render() { // Prepara para empezar a dibujar en la textura de juego Screen::get()->start(); - Screen::get()->clean(stringToColor(options.video.palette, "black")); + Screen::get()->clean(stringToColor("black")); if (state_ == TitleState::SHOW_MENU) { @@ -311,15 +310,12 @@ void Title::moveCheevosList(int direction) // Rellena la textura de fondo con todos los gráficos void Title::fillSurface() { - auto renderer = Screen::get()->getRenderer(); - // Coloca el puntero del renderizador sobre la textura - SDL_SetRenderTarget(renderer, bg_surface_); + Screen::get()->setRenderSurface(bg_surface_); // Rellena la textura de color - const Color c = stringToColor(options.video.palette, "black"); - SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 0xFF); - SDL_RenderClear(renderer); + bg_surface_->setColor(255, 0xFF000000); + bg_surface_->clear(255); // Pinta el gráfico del titulo a partir del sprite sprite_->render(); @@ -334,7 +330,7 @@ void Title::fillSurface() text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 20 * TEXT_SIZE, "ESC.EXIT GAME", 1, COLOR); // Devuelve el puntero del renderizador a su sitio - SDL_SetRenderTarget(renderer, nullptr); + Screen::get()->setRenderSurface(nullptr); } // Crea y rellena la textura para mostrar los logros @@ -349,10 +345,10 @@ void Title::createCheevosTexture() constexpr int CHEEVOS_PADDING = 10; const int CHEEVO_HEIGHT = CHEEVOS_PADDING + (TEXT->getCharacterSize() * 2) + 1; const int CHEEVOS_TEXTURE_HEIGHT = (CHEEVO_HEIGHT * CHEEVOS_LIST.size()) + 2 + TEXT->getCharacterSize() + 8; - cheevos_surface_ = std::make_shared(Screen::get()->getRenderer()); - cheevos_surface_->createBlank(CHEEVOS_TEXTURE_WIDTH, CHEEVOS_TEXTURE_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); - cheevos_surface_->setAsRenderTarget(Screen::get()->getRenderer()); - cheevos_surface_->setBlendMode(SDL_BLENDMODE_BLEND); + cheevos_surface_ = std::make_shared(Screen::get()->getRenderSurface(), CHEEVOS_TEXTURE_WIDTH, CHEEVOS_TEXTURE_HEIGHT); + + // Prepara para dibujar sobre la textura + Screen::get()->setRenderSurface(cheevos_surface_); // Rellena la textura con color sólido const Color CHEEVOS_BG_COLOR = stringToColor(options.video.palette, "black"); @@ -384,6 +380,9 @@ void Title::createCheevosTexture() pos += TEXT->getCharacterSize(); } + // Restablece el RenderSurface + Screen::get()->setRenderSurface(nullptr); + // Crea el sprite para el listado de logros cheevos_sprite_ = std::make_shared(cheevos_surface_, (GAMECANVAS_WIDTH - cheevos_surface_->getWidth()) / 2, CHEEVOS_TEXTURE_POS_Y, cheevos_surface_->getWidth(), cheevos_surface_->getHeight()); cheevos_surface_view_ = {0, 0, cheevos_surface_->getWidth(), CHEEVOS_TEXTURE_VIEW_HEIGHT}; diff --git a/source/utils.cpp b/source/utils.cpp index 07dae7d..1b07999 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -428,6 +428,41 @@ Color stringToColor(Palette pal, const std::string &str) } } +// Convierte una cadena a un indice de la paleta +Uint8 stringToColor(const std::string &str) +{ + // Mapas de colores para cada paleta + static const std::unordered_map paletteMap = { + {"black", 1}, + {"bright_black", 2}, + {"blue", 3}, + {"bright_blue", 4}, + {"red", 5}, + {"bright_red", 6}, + {"magenta", 7}, + {"bright_magenta", 8}, + {"green", 9}, + {"bright_green", 10}, + {"cyan", 11}, + {"bright_cyan", 12}, + {"yellow", 13}, + {"bright_yellow", 14}, + {"white", 15}, + {"bright_white", 16}}; + + // Busca el color en el mapa + auto it = paletteMap.find(str); + if (it != paletteMap.end()) + { + return it->second; + } + else + { + // Si no se encuentra el color, devolvemos negro por defecto + return {0}; + } +} + // Convierte una cadena a un entero de forma segura int safeStoi(const std::string &value, int defaultValue) { diff --git a/source/utils.h b/source/utils.h index 24aa67d..57a1db2 100644 --- a/source/utils.h +++ b/source/utils.h @@ -98,6 +98,7 @@ void normalizeLine(LineDiagonal &l); // Devuelve un Color a partir de un string Color stringToColor(Palette pal, const std::string &str); +Uint8 stringToColor(const std::string &str); // Convierte una cadena a un entero de forma segura int safeStoi(const std::string &value, int defaultValue = 0);