- DUP-01: les tres branques del switch d'extensions que saltaven el mateix (bloc fix + sub-blocs) es substitueixen per una crida a skipExtensionBlock(); com que totes les extensions comparteixen layout, el switch desapareix i el codi es redueix a una línia. - DUP-06: el constructor Surface(file_path) llegia el fitxer dues voltes (una via loadSurface per pixels i una altra via loadPaletteFromGif per la paleta); ara carrega el buffer una sola vegada i n'extrau pixels + paleta en passades separades sobre la memòria. Surface::loadSurface passa a no existir públicament (no tenia callers externs). - HC-03: les màscares i talls màgics del decoder LZW i del Screen Descriptor passen a constants nominals (LZW_MIN/MAX_CODE_LENGTH, LZW_MASK_INITIAL, LZW_MASK_OVERFLOW, SCREEN_GCT_FLAG, SCREEN_GCT_SIZE_MASK, GCT_BYTES_PER_ENTRY). - HC-10: processImageDescriptor tenia gct i resolution_bits [[maybe_unused]] vestigials; se'n treu la signatura. L'struct RGB, que només servia per a aquests args, també s'elimina. - PATCH-05: el comentari "reemplaçar macro READ" (migració parcial des de C) passa a ser un comentari neutre sobre el que fa readBytes. Build + smoke 5s xvfb OK (exit 124). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
121 lines
4.9 KiB
C++
121 lines
4.9 KiB
C++
#include "core/rendering/surface.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
#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<std::uint32_t> 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<SurfaceData>(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<Uint8> 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<Uint8[]>(new Uint8[raw_pixels.size()], std::default_delete<Uint8[]>());
|
|
std::memcpy(pixels.get(), raw_pixels.data(), raw_pixels.size());
|
|
surface_data_ = std::make_shared<SurfaceData>(static_cast<int>(w), static_cast<int>(h), pixels);
|
|
initializeSubPalette(sub_palette_);
|
|
|
|
std::vector<std::uint32_t> 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<std::size_t>(surface_data_->width) * static_cast<std::size_t>(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<int>(src_rect->x) : 0;
|
|
const int SY = (src_rect != nullptr) ? static_cast<int>(src_rect->y) : 0;
|
|
int sw = (src_rect != nullptr) ? static_cast<int>(src_rect->w) : surface_data_->width;
|
|
int sh = (src_rect != nullptr) ? static_cast<int>(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];
|
|
}
|
|
}
|
|
}
|