From bee9b650db0dcb1d1a0d886f41e5851ba4844e12 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sun, 19 Apr 2026 09:30:29 +0200 Subject: [PATCH] =?UTF-8?q?Paleta=20UI=20compartida=20(sega-cube.pal)=20i?= =?UTF-8?q?=20constants=20sem=C3=A0ntiques=20UiColor=20(v0.7.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + data/palettes/sega-cube.pal | 259 +++++++++++++++++++++++ source/core/rendering/render_info.cpp | 15 +- source/core/rendering/surface.cpp | 8 - source/core/rendering/surface.hpp | 3 - source/core/resources/palette_loader.cpp | 65 ++++++ source/core/resources/palette_loader.hpp | 22 ++ source/core/resources/resource_cache.cpp | 16 ++ source/core/resources/resource_cache.hpp | 5 + source/core/resources/resource_types.hpp | 5 + source/game/ui/ui_palette.hpp | 14 ++ source/utils/defines.hpp | 2 +- 12 files changed, 396 insertions(+), 19 deletions(-) create mode 100644 data/palettes/sega-cube.pal create mode 100644 source/core/resources/palette_loader.cpp create mode 100644 source/core/resources/palette_loader.hpp create mode 100644 source/game/ui/ui_palette.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index dddb314..54837f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ set(APP_SOURCES source/core/rendering/sdl3gpu/pipelines/crtpi_pipeline.cpp source/core/resources/resource_helper.cpp source/core/resources/resource_cache.cpp + source/core/resources/palette_loader.cpp source/utils/utils.cpp source/game/options.cpp source/game/scenes/game_scene.cpp diff --git a/data/palettes/sega-cube.pal b/data/palettes/sega-cube.pal new file mode 100644 index 0000000..e5ddd58 --- /dev/null +++ b/data/palettes/sega-cube.pal @@ -0,0 +1,259 @@ +JASC-PAL +0100 +256 +0 0 0 +0 36 0 +0 72 0 +0 109 0 +0 145 0 +0 182 0 +0 218 0 +0 255 0 +85 0 0 +85 36 0 +85 72 0 +85 109 0 +85 145 0 +85 182 0 +85 218 0 +85 255 0 +170 0 0 +170 36 0 +170 72 0 +170 109 0 +170 145 0 +170 182 0 +170 218 0 +170 255 0 +255 0 0 +255 36 0 +255 72 0 +255 109 0 +255 145 0 +255 182 0 +255 218 0 +255 255 0 +0 0 36 +0 36 36 +0 72 36 +0 109 36 +0 145 36 +0 182 36 +0 218 36 +0 255 36 +85 0 36 +85 36 36 +85 72 36 +85 109 36 +85 145 36 +85 182 36 +85 218 36 +85 255 36 +170 0 36 +170 36 36 +170 72 36 +170 109 36 +170 145 36 +170 182 36 +170 218 36 +170 255 36 +255 0 36 +255 36 36 +255 72 36 +255 109 36 +255 145 36 +255 182 36 +255 218 36 +255 255 36 +0 0 72 +0 36 72 +0 72 72 +0 109 72 +0 145 72 +0 182 72 +0 218 72 +0 255 72 +85 0 72 +85 36 72 +85 72 72 +85 109 72 +85 145 72 +85 182 72 +85 218 72 +85 255 72 +170 0 72 +170 36 72 +170 72 72 +170 109 72 +170 145 72 +170 182 72 +170 218 72 +170 255 72 +255 0 72 +255 36 72 +255 72 72 +255 109 72 +255 145 72 +255 182 72 +255 218 72 +255 255 72 +0 0 109 +0 36 109 +0 72 109 +0 109 109 +0 145 109 +0 182 109 +0 218 109 +0 255 109 +85 0 109 +85 36 109 +85 72 109 +85 109 109 +85 145 109 +85 182 109 +85 218 109 +85 255 109 +170 0 109 +170 36 109 +170 72 109 +170 109 109 +170 145 109 +170 182 109 +170 218 109 +170 255 109 +255 0 109 +255 36 109 +255 72 109 +255 109 109 +255 145 109 +255 182 109 +255 218 109 +255 255 109 +0 0 145 +0 36 145 +0 72 145 +0 109 145 +0 145 145 +0 182 145 +0 218 145 +0 255 145 +85 0 145 +85 36 145 +85 72 145 +85 109 145 +85 145 145 +85 182 145 +85 218 145 +85 255 145 +170 0 145 +170 36 145 +170 72 145 +170 109 145 +170 145 145 +170 182 145 +170 218 145 +170 255 145 +255 0 145 +255 36 145 +255 72 145 +255 109 145 +255 145 145 +255 182 145 +255 218 145 +255 255 145 +0 0 182 +0 36 182 +0 72 182 +0 109 182 +0 145 182 +0 182 182 +0 218 182 +0 255 182 +85 0 182 +85 36 182 +85 72 182 +85 109 182 +85 145 182 +85 182 182 +85 218 182 +85 255 182 +170 0 182 +170 36 182 +170 72 182 +170 109 182 +170 145 182 +170 182 182 +170 218 182 +170 255 182 +255 0 182 +255 36 182 +255 72 182 +255 109 182 +255 145 182 +255 182 182 +255 218 182 +255 255 182 +0 0 218 +0 36 218 +0 72 218 +0 109 218 +0 145 218 +0 182 218 +0 218 218 +0 255 218 +85 0 218 +85 36 218 +85 72 218 +85 109 218 +85 145 218 +85 182 218 +85 218 218 +85 255 218 +170 0 218 +170 36 218 +170 72 218 +170 109 218 +170 145 218 +170 182 218 +170 218 218 +170 255 218 +255 0 218 +255 36 218 +255 72 218 +255 109 218 +255 145 218 +255 182 218 +255 218 218 +255 255 218 +0 0 255 +0 36 255 +0 72 255 +0 109 255 +0 145 255 +0 182 255 +0 218 255 +0 255 255 +85 0 255 +85 36 255 +85 72 255 +85 109 255 +85 145 255 +85 182 255 +85 218 255 +85 255 255 +170 0 255 +170 36 255 +170 72 255 +170 109 255 +170 145 255 +170 182 255 +170 218 255 +170 255 255 +255 0 255 +255 36 255 +255 72 255 +255 109 255 +255 145 255 +255 182 255 +255 218 255 +255 255 255 diff --git a/source/core/rendering/render_info.cpp b/source/core/rendering/render_info.cpp index 78c23bc..9bc250c 100644 --- a/source/core/rendering/render_info.cpp +++ b/source/core/rendering/render_info.cpp @@ -8,14 +8,11 @@ #include "core/rendering/text.hpp" #include "core/resources/resource_cache.hpp" #include "game/options.hpp" +#include "game/ui/ui_palette.hpp" #include "utils/defines.hpp" namespace { - // La paleta de 8bithud.gif només té 2 entrades (0=negre, 1=blanc); per a - // poder colorar el text lliurement caldrà una paleta compartida, com al - // projecte_2026. De moment ens quedem amb blanc. - constexpr Uint8 TEXT_COLOR = 1; constexpr int TEXT_SIZE = 8; constexpr int PADDING_V = (RenderInfo::HEIGHT - TEXT_SIZE) / 2; @@ -35,10 +32,14 @@ auto RenderInfo::get() -> RenderInfo& { RenderInfo::~RenderInfo() = default; void RenderInfo::init() { - // El fitxer s'indexa al Cache perquè també el puguen reutilitzar altres - // HUDs que vulguen la mateixa font sense recarregar-la. + // Paleta compartida de UI: qualsevol HUD que la vulga només ha de cridar + // Cache::getPalette("ui") i aplicar-la a la seua surface. El .gif propi + // de la font (2 colors) es descarta a favor d'aquesta. + Resource::Cache::get().loadPalette("ui", "data/palettes/sega-cube.pal"); + Resource::Cache::get().loadSurface("8bithud.gif", "data/font/8bithud.gif"); auto font_surface = Resource::Cache::get().getSurface("8bithud.gif"); + font_surface->setPalette(Resource::Cache::get().getPalette("ui")); text_ = std::make_unique(font_surface, "data/font/8bithud.fnt"); // Restaura l'estat guardat a config.yaml (video.render_info). @@ -93,7 +94,7 @@ void RenderInfo::render() { static_cast(y_) + PADDING_V, line, 1, - TEXT_COLOR); + UiColor::RENDER_INFO_TEXT); } void RenderInfo::toggle() { diff --git a/source/core/rendering/surface.cpp b/source/core/rendering/surface.cpp index b55ebf4..d3ecd48 100644 --- a/source/core/rendering/surface.cpp +++ b/source/core/rendering/surface.cpp @@ -63,14 +63,6 @@ auto Surface::loadSurface(const std::string& file_path) -> SurfaceData { return {static_cast(w), static_cast(h), pixels}; } -void Surface::loadPalette(const std::string& file_path) { - palette_ = loadPaletteFromGif(file_path); -} - -void Surface::loadPalette(const Palette& palette) { - palette_ = palette; -} - void Surface::clear(Uint8 color) { const std::size_t TOTAL = static_cast(surface_data_->width) * static_cast(surface_data_->height); std::fill(surface_data_->data.get(), surface_data_->data.get() + TOTAL, color); diff --git a/source/core/rendering/surface.hpp b/source/core/rendering/surface.hpp index d9806c8..0a9ef16 100644 --- a/source/core/rendering/surface.hpp +++ b/source/core/rendering/surface.hpp @@ -47,9 +47,6 @@ class Surface { static auto loadSurface(const std::string& file_path) -> SurfaceData; - void loadPalette(const std::string& file_path); - void loadPalette(const Palette& palette); - // Vobla la regió indicada al framebuffer ARGB del joc usant la paleta propia. void render(int x, int y, const SDL_FRect* src_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE) const; // Igual que render() però substituint els píxels amb valor source_color per diff --git a/source/core/resources/palette_loader.cpp b/source/core/resources/palette_loader.cpp new file mode 100644 index 0000000..3ea6caf --- /dev/null +++ b/source/core/resources/palette_loader.cpp @@ -0,0 +1,65 @@ +#include "core/resources/palette_loader.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "core/rendering/surface.hpp" +#include "core/resources/resource_helper.hpp" + +namespace Resource::PaletteLoader { + + auto parseJasc(const std::vector& bytes) -> Palette { + std::string content(bytes.begin(), bytes.end()); + std::istringstream stream(content); + std::string line; + + // Capçalera: "JASC-PAL" / "0100" / count. + std::getline(stream, line); + if (!line.starts_with("JASC-PAL")) { + throw std::runtime_error("PaletteLoader: fitxer no és JASC-PAL"); + } + std::getline(stream, line); // versió, ignorada per ara + std::getline(stream, line); + int declared_count = 0; + try { + declared_count = std::stoi(line); + } catch (...) { + throw std::runtime_error("PaletteLoader: count JASC-PAL invàlid"); + } + + Palette palette{}; + const std::size_t MAX = std::min(static_cast(declared_count), palette.size()); + std::size_t i = 0; + while (i < MAX && std::getline(stream, line)) { + if (line.empty()) { continue; } + std::istringstream ls(line); + int r = 0; + int g = 0; + int b = 0; + ls >> r >> g >> b; + palette[i] = 0xFF000000U | (static_cast(r & 0xFF) << 16) | (static_cast(g & 0xFF) << 8) | static_cast(b & 0xFF); + ++i; + } + return palette; + } + + auto loadFromFile(const std::string& file_path) -> Palette { + auto ext = std::filesystem::path(file_path).extension().string(); + std::ranges::transform(ext, ext.begin(), [](unsigned char c) { return std::tolower(c); }); + + auto bytes = Resource::Helper::loadFile(file_path); + if (bytes.empty()) { + throw std::runtime_error("PaletteLoader: no s'ha pogut llegir " + file_path); + } + + if (ext == ".pal") { return parseJasc(bytes); } + if (ext == ".gif") { return loadPaletteFromGif(file_path); } + throw std::runtime_error("PaletteLoader: format de paleta no suportat: " + ext); + } + +} // namespace Resource::PaletteLoader diff --git a/source/core/resources/palette_loader.hpp b/source/core/resources/palette_loader.hpp new file mode 100644 index 0000000..79ec8b4 --- /dev/null +++ b/source/core/resources/palette_loader.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +#include "core/rendering/surface.hpp" + +namespace Resource::PaletteLoader { + + // Detecta el format pel sufix i retorna una Palette (256 entrades ARGB). + // Actualment suporta: + // - ".pal" → JASC-PAL versió 0100 (plaintext "R G B" per línia). + // - ".gif" → paleta global del GIF (reutilitza el decoder existent). + // Cridar-lo amb un altre sufix llança std::runtime_error. + auto loadFromFile(const std::string& file_path) -> Palette; + + // Parser pur del format JASC-PAL, útil si ja es té el contingut en memòria + // (p.ex. des d'un resources.pack). No fa I/O. + auto parseJasc(const std::vector& bytes) -> Palette; + +} // namespace Resource::PaletteLoader diff --git a/source/core/resources/resource_cache.cpp b/source/core/resources/resource_cache.cpp index 4ba7def..47ca4c2 100644 --- a/source/core/resources/resource_cache.cpp +++ b/source/core/resources/resource_cache.cpp @@ -4,6 +4,7 @@ #include #include +#include "core/resources/palette_loader.hpp" #include "core/resources/resource_helper.hpp" #include "utils/utils.hpp" @@ -28,6 +29,12 @@ namespace Resource { printWithDots("Animation : ", getFileName(file_path), "[ LOADED ]"); } + void Cache::loadPalette(const std::string& name, const std::string& file_path) { + auto palette = PaletteLoader::loadFromFile(file_path); + palettes_.emplace_back(PaletteResource{.name = name, .palette = palette}); + printWithDots("Palette : ", getFileName(file_path), "[ LOADED ]"); + } + auto Cache::getSurface(const std::string& name) -> std::shared_ptr { auto it = std::ranges::find_if(surfaces_, [&name](const auto& s) { return s.name == name; }); if (it == surfaces_.end()) { @@ -46,4 +53,13 @@ namespace Resource { return *it; } + auto Cache::getPalette(const std::string& name) -> const Palette& { + auto it = std::ranges::find_if(palettes_, [&name](const auto& p) { return p.name == name; }); + if (it == palettes_.end()) { + std::cerr << "Cache: palette not found: " << name << '\n'; + throw std::runtime_error("Palette not found: " + name); + } + return it->palette; + } + } // namespace Resource diff --git a/source/core/resources/resource_cache.hpp b/source/core/resources/resource_cache.hpp index 135fc30..018e703 100644 --- a/source/core/resources/resource_cache.hpp +++ b/source/core/resources/resource_cache.hpp @@ -25,9 +25,13 @@ namespace Resource { void loadSurface(const std::string& name, const std::string& file_path); // Llig el YAML d'animació en cru i el registra amb el nom donat. void loadAnimation(const std::string& name, const std::string& file_path); + // Carrega una paleta (.pal JASC o .gif) i la registra amb el nom donat. + // Pensat per a paletes compartides (UI, fonts, tilesets reciclats). + void loadPalette(const std::string& name, const std::string& file_path); auto getSurface(const std::string& name) -> std::shared_ptr; auto getAnimationData(const std::string& name) -> const AnimationResource&; + auto getPalette(const std::string& name) -> const Palette&; private: Cache() = default; @@ -35,6 +39,7 @@ namespace Resource { std::vector surfaces_; std::vector animations_; + std::vector palettes_; }; } // namespace Resource diff --git a/source/core/resources/resource_types.hpp b/source/core/resources/resource_types.hpp index 79d19f0..c2d3525 100644 --- a/source/core/resources/resource_types.hpp +++ b/source/core/resources/resource_types.hpp @@ -16,3 +16,8 @@ struct AnimationResource { std::string name; std::vector yaml_data; }; + +struct PaletteResource { + std::string name; + Palette palette; +}; diff --git a/source/game/ui/ui_palette.hpp b/source/game/ui/ui_palette.hpp new file mode 100644 index 0000000..be018cd --- /dev/null +++ b/source/game/ui/ui_palette.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +// Constants semàntiques que mapegen cada element de la UI al seu color dins +// de la paleta compartida (registre "ui" a Resource::Cache, carregada des de +// data/palettes/sega-cube.pal). Canviar l'aparença d'un component només +// requereix canviar la seua constant ací — no cal buscar l'índex pel codi. +namespace UiColor { + + // Color per al text de l'overlay de debug RenderInfo (F3). + constexpr Uint8 RENDER_INFO_TEXT = 9; + +} // namespace UiColor diff --git a/source/utils/defines.hpp b/source/utils/defines.hpp index b488ada..e9f7360 100644 --- a/source/utils/defines.hpp +++ b/source/utils/defines.hpp @@ -3,7 +3,7 @@ namespace Texts { constexpr const char* WINDOW_TITLE = "© 2026 Aventures en Egipte — JailDesigner"; constexpr const char* COPYRIGHT = "© 2026 JailDesigner"; - constexpr const char* VERSION = "0.7.0"; // Single source of truth — Makefile el grep + constexpr const char* VERSION = "0.7.1"; // Single source of truth — Makefile el grep } // namespace Texts namespace GameCanvas {