#include "core/rendering/screen.hpp" #include #include #include #include #include #include #include "utils/defines.hpp" namespace { auto toSdlPresentation(Defaults::ScalingMode mode) -> SDL_RendererLogicalPresentation { switch (mode) { case Defaults::ScalingMode::DISABLED: return SDL_LOGICAL_PRESENTATION_DISABLED; case Defaults::ScalingMode::STRETCH: return SDL_LOGICAL_PRESENTATION_STRETCH; case Defaults::ScalingMode::LETTERBOX: return SDL_LOGICAL_PRESENTATION_LETTERBOX; case Defaults::ScalingMode::OVERSCAN: return SDL_LOGICAL_PRESENTATION_OVERSCAN; case Defaults::ScalingMode::INTEGER: return SDL_LOGICAL_PRESENTATION_INTEGER_SCALE; } return SDL_LOGICAL_PRESENTATION_LETTERBOX; } auto toSdlScaleMode(Defaults::TextureFilter filter) -> SDL_ScaleMode { return filter == Defaults::TextureFilter::LINEAR ? SDL_SCALEMODE_LINEAR : SDL_SCALEMODE_NEAREST; } } // namespace auto Screen::get() -> Screen& { static Screen instance_; return instance_; } Screen::Screen() : canvas_width_(GameCanvas::WIDTH), canvas_height_(GameCanvas::HEIGHT), canvas_(static_cast(GameCanvas::WIDTH) * static_cast(GameCanvas::HEIGHT), 0xFF000000U) { if (!SDL_InitSubSystem(SDL_INIT_VIDEO)) { std::fprintf(stderr, "SDL_InitSubSystem(VIDEO) failed: %s\n", SDL_GetError()); std::exit(EXIT_FAILURE); } // Els recursos SDL (finestra, renderer, textura) es creen a configure(). } Screen::~Screen() { destroySdl(); SDL_QuitSubSystem(SDL_INIT_VIDEO); } void Screen::configure(const Config& config) { destroySdl(); config_ = config; createSdl(); } void Screen::destroySdl() { if (game_texture_ != nullptr) { SDL_DestroyTexture(game_texture_); game_texture_ = nullptr; } if (renderer_ != nullptr) { SDL_DestroyRenderer(renderer_); renderer_ = nullptr; } if (window_ != nullptr) { SDL_DestroyWindow(window_); window_ = nullptr; } } void Screen::createSdl() { const int WIN_W = GameCanvas::WIDTH * config_.zoom; const int WIN_H = GameCanvas::HEIGHT * config_.zoom; Uint32 win_flags = SDL_WINDOW_RESIZABLE; if (config_.fullscreen) { win_flags |= SDL_WINDOW_FULLSCREEN; } window_ = SDL_CreateWindow(Texts::WINDOW_TITLE, WIN_W, WIN_H, win_flags); if (window_ == nullptr) { std::fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError()); std::exit(EXIT_FAILURE); } renderer_ = SDL_CreateRenderer(window_, nullptr); if (renderer_ == nullptr) { std::fprintf(stderr, "SDL_CreateRenderer failed: %s\n", SDL_GetError()); std::exit(EXIT_FAILURE); } SDL_SetRenderVSync(renderer_, config_.vsync ? 1 : 0); SDL_SetRenderLogicalPresentation(renderer_, GameCanvas::WIDTH, GameCanvas::HEIGHT, toSdlPresentation(config_.scaling_mode)); // STREAMING: el framebuffer ARGB del joc s'actualitza per CPU cada frame // i s'envia a la GPU via SDL_UpdateTexture en present(). game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, GameCanvas::WIDTH, GameCanvas::HEIGHT); if (game_texture_ == nullptr) { std::fprintf(stderr, "SDL_CreateTexture failed: %s\n", SDL_GetError()); std::exit(EXIT_FAILURE); } SDL_SetTextureScaleMode(game_texture_, toSdlScaleMode(config_.texture_filter)); } void Screen::clear(std::uint8_t r, std::uint8_t g, std::uint8_t b) { const std::uint32_t COLOR = (0xFFU << 24) | (static_cast(r) << 16) | (static_cast(g) << 8) | static_cast(b); std::ranges::fill(canvas_, COLOR); } void Screen::present() { SDL_UpdateTexture(game_texture_, nullptr, canvas_.data(), canvas_width_ * static_cast(sizeof(std::uint32_t))); SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(renderer_); SDL_RenderTexture(renderer_, game_texture_, nullptr, nullptr); SDL_RenderPresent(renderer_); }