#include "core/rendering/surface.hpp" #include #include #include #include #include #include "core/rendering/canvas.hpp" #include "core/rendering/gif.hpp" #include "core/resources/resource_helper.hpp" #include "utils/utils.hpp" auto loadPaletteFromGif(const std::string& file_path) -> Palette { auto buffer = Resource::Helper::loadFile(file_path); if (buffer.empty()) { throw std::runtime_error("Surface: cannot open palette file " + file_path); } std::vector rgb = GIF::Gif::loadPalette(buffer.data()); if (rgb.empty()) { throw std::runtime_error("Surface: no palette in GIF " + file_path); } Palette palette = {}; // Convertim RGB → ARGB amb canal alpha 0xFF. const std::size_t COUNT = std::min(rgb.size(), palette.size()); for (std::size_t i = 0; i < COUNT; ++i) { palette[i] = 0xFF000000U | rgb[i]; } return palette; } Surface::Surface(int w, int h) : surface_data_(std::make_shared(w, h)) { initializeSubPalette(sub_palette_); } Surface::Surface(const std::string& file_path) { // Carreguem el fitxer una única vegada i n'extraiem pixels i paleta de // passades independents sobre el mateix buffer en memòria — abans es // parsejava dues voltes (loadSurface + loadPaletteFromGif). auto buffer = Resource::Helper::loadFile(file_path); if (buffer.empty()) { throw std::runtime_error("Surface: cannot open " + file_path); } Uint16 w = 0; Uint16 h = 0; std::vector raw_pixels = GIF::Gif::loadGif(buffer.data(), w, h); if (raw_pixels.empty()) { throw std::runtime_error("Surface: failed to decode GIF " + file_path); } auto pixels = std::shared_ptr(new Uint8[raw_pixels.size()], std::default_delete()); std::memcpy(pixels.get(), raw_pixels.data(), raw_pixels.size()); surface_data_ = std::make_shared(static_cast(w), static_cast(h), pixels); initializeSubPalette(sub_palette_); std::vector rgb = GIF::Gif::loadPalette(buffer.data()); const std::size_t COUNT = std::min(rgb.size(), palette_.size()); for (std::size_t i = 0; i < COUNT; ++i) { palette_[i] = 0xFF000000U | rgb[i]; } printWithDots("Surface : ", getFileName(file_path), "[ LOADED ]"); } 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); } void Surface::putPixel(int x, int y, Uint8 color) { if (x < 0 || y < 0 || x >= surface_data_->width || y >= surface_data_->height) { return; } surface_data_->data.get()[x + (y * surface_data_->width)] = color; } auto Surface::getPixel(int x, int y) const -> Uint8 { return surface_data_->data.get()[x + (y * surface_data_->width)]; } void Surface::render(int x, int y, const SDL_FRect* src_rect, SDL_FlipMode flip) const { // Delegem a la versió amb remap: fent source==target==transparent_color_, // qualsevol píxel opaC tindrà COLOR != source_color (els transparents ja // s'han filtrat), així que REMAPPED = sub_palette_[COLOR] — idèntic al // camí sense remap. Això evita duplicar el bucle de blit. renderWithColorReplace(x, y, transparent_color_, transparent_color_, src_rect, flip); } void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 target_color, const SDL_FRect* src_rect, SDL_FlipMode flip) const { const auto CANVAS = Rendering::activeCanvas(); Uint32* dst = CANVAS.pixels; const int DST_W = CANVAS.width; const int DST_H = CANVAS.height; const int SX = (src_rect != nullptr) ? static_cast(src_rect->x) : 0; const int SY = (src_rect != nullptr) ? static_cast(src_rect->y) : 0; int sw = (src_rect != nullptr) ? static_cast(src_rect->w) : surface_data_->width; int sh = (src_rect != nullptr) ? static_cast(src_rect->h) : surface_data_->height; sw = std::min(sw, surface_data_->width - SX); sh = std::min(sh, surface_data_->height - SY); const Uint8* src = surface_data_->data.get(); for (int iy = 0; iy < sh; ++iy) { for (int ix = 0; ix < sw; ++ix) { const int SRC_X = (flip == SDL_FLIP_HORIZONTAL) ? (SX + sw - 1 - ix) : (SX + ix); const int SRC_Y = (flip == SDL_FLIP_VERTICAL) ? (SY + sh - 1 - iy) : (SY + iy); const int DST_X = x + ix; const int DST_Y = y + iy; if (DST_X < 0 || DST_X >= DST_W || DST_Y < 0 || DST_Y >= DST_H) { continue; } const Uint8 COLOR = src[SRC_X + (SRC_Y * surface_data_->width)]; if (COLOR == transparent_color_) { continue; } const Uint8 REMAPPED = (COLOR == source_color) ? target_color : sub_palette_[COLOR]; dst[DST_X + (DST_Y * DST_W)] = palette_[REMAPPED]; } } }