diff --git a/CGame.cpp b/CGame.cpp index 3e1e4f7..18f90ed 100644 --- a/CGame.cpp +++ b/CGame.cpp @@ -13,6 +13,7 @@ #include "s25util/file_handle.h" #include #include +#include #include #include #include @@ -43,6 +44,11 @@ CGame::CGame(Extent GameResolution_, bool fullscreen_) CGame::~CGame() { + if(glContext_) + { + SDL_GL_DeleteContext(glContext_); + glContext_ = nullptr; + } global::s2 = nullptr; } @@ -69,12 +75,15 @@ int CGame::Execute() return 0; } -void CGame::RenderPresent() const +void CGame::RenderPresent() { - SDL_UpdateTexture(displayTexture_.get(), nullptr, Surf_Display->pixels, Surf_Display->w * sizeof(Uint32)); - SDL_RenderClear(renderer_.get()); - SDL_RenderCopy(renderer_.get(), displayTexture_.get(), nullptr, nullptr); - SDL_RenderPresent(renderer_.get()); + displayTexture_.upload(Surf_Display->pixels); + displayTexture_.Draw(Rect(0, 0, GameResolution.x, GameResolution.y)); + + const auto& cursorImg = Cursor.clicked ? (Cursor.button.right ? cross_ : cursorClicked_) : cursor_; + cursorImg.Draw(Cursor.pos); + + SDL_GL_SwapWindow(window_.get()); } CMenu* CGame::RegisterMenu(std::unique_ptr Menu) diff --git a/CGame.h b/CGame.h index 26b32d4..f4a78e5 100644 --- a/CGame.h +++ b/CGame.h @@ -7,6 +7,7 @@ #include "CIO/CFont.h" #include "SdlSurface.h" +#include "Texture.h" #include #include #include @@ -27,8 +28,8 @@ class CGame bool Running; bool showLoadScreen; SdlSurface Surf_Display; - SdlTexture displayTexture_; - SdlRenderer renderer_; + Texture displayTexture_; + SDL_GLContext glContext_ = nullptr; SdlWindow window_; private: @@ -43,7 +44,14 @@ class CGame CFont lastFps; Uint32 lastFrameTime = 0; - unsigned suppressResizeEvents_ = 0; + Extent appliedResolution_ = Extent{0, 0}; ///< Last resolution we applied to the window/display + bool appliedFullscreen_ = false; ///< Last fullscreen state we applied + + // Textures for splash screen and cursor + Texture splashBg_; + Texture cursor_; + Texture cursorClicked_; + Texture cross_; // structure for mouse cursor struct @@ -67,9 +75,13 @@ class CGame std::unique_ptr MapObj; void SetAppIcon(); - void RecreateDisplayResources(); + void setGLViewport(); + bool CreateWindow(); public: + // Apply current GameResolution and fullscreen settings to the window/display. + void ApplyWindowChanges(); + void LoadSettings(); void SaveSettings() const; @@ -79,7 +91,6 @@ class CGame int Execute(); bool Init(); - bool ReCreateWindow(); void UpdateDisplaySize(const Extent& newSize); void EventHandling(SDL_Event* Event); @@ -88,7 +99,7 @@ class CGame void Render(); - void RenderPresent() const; + void RenderPresent(); CMenu* RegisterMenu(std::unique_ptr Menu); bool UnregisterMenu(CMenu* Menu); diff --git a/CGame_Event.cpp b/CGame_Event.cpp index ed9570c..7839c79 100644 --- a/CGame_Event.cpp +++ b/CGame_Event.cpp @@ -68,6 +68,7 @@ void CGame::EventHandling(SDL_Event* Event) if(Event->key.keysym.mod & KMOD_ALT) { fullscreen = !fullscreen; + ApplyWindowChanges(); SaveSettings(); } break; @@ -388,12 +389,16 @@ void CGame::EventHandling(SDL_Event* Event) { if(Event->window.event == SDL_WINDOWEVENT_RESIZED) { - if(suppressResizeEvents_ > 0) - { - suppressResizeEvents_--; - break; // Skip stale event from our own window recreation - } - UpdateDisplaySize(Extent(Event->window.data1, Event->window.data2)); + // In fullscreen the compositor (e.g. Wayland) may report a size + // different from the one we requested. We already applied the + // resolution ourselves, so don't let the event override it. + if(fullscreen) + break; + const Extent newSize(Event->window.data1, Event->window.data2); + // Ignore events matching the resolution we already applied. + if(newSize == appliedResolution_) + break; + UpdateDisplaySize(newSize); } break; } diff --git a/CGame_Init.cpp b/CGame_Init.cpp index f367224..7f437db 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -8,49 +8,131 @@ #include "CIO/CMenu.h" #include "CIO/CWindow.h" #include "CMap.h" -#include "CSurface.h" #include "callbacks.h" #include "globals.h" #include "lua/GameDataLoader.h" +#include #include -#include -bool CGame::ReCreateWindow() +bool CGame::CreateWindow() { - suppressResizeEvents_ = 3; - displayTexture_.reset(); - renderer_.reset(); - window_.reset(); + if(window_) + return false; + window_.reset(SDL_CreateWindow("Return to the Roots Map editor [BETA]", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, GameResolution.x, GameResolution.y, - fullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE)); + SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE)); if(!window_) return false; - renderer_.reset(SDL_CreateRenderer(window_.get(), -1, 0)); - if(!renderer_) + + glContext_ = SDL_GL_CreateContext(window_.get()); + if(!glContext_ || !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) return false; - RecreateDisplayResources(); - if(!displayTexture_ || !Surf_Display) + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); + glClearColor(0, 0, 0, 1); + + SDL_ShowWindow(window_.get()); + + ApplyWindowChanges(); + if(!displayTexture_.isValid() || !Surf_Display) return false; SetAppIcon(); + return true; } -void CGame::RecreateDisplayResources() +void CGame::ApplyWindowChanges() { - displayTexture_.reset(); - displayTexture_ = makeSdlTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, GameResolution.x, - GameResolution.y); - Surf_Display = makeRGBSurface(GameResolution.x, GameResolution.y, true); + if(!window_) + return; + + if(fullscreen) + { + SDL_DisplayMode dm; + SDL_zero(dm); + dm.w = static_cast(GameResolution.x); + dm.h = static_cast(GameResolution.y); + dm.format = 0; // let SDL pick a supported format + dm.refresh_rate = 0; + if(SDL_SetWindowDisplayMode(window_.get(), &dm) != 0) + { + std::cerr << "SDL_SetWindowDisplayMode failed: " << SDL_GetError() << std::endl; + return; + } + + const Uint32 flags = SDL_GetWindowFlags(window_.get()); + if(!(flags & SDL_WINDOW_FULLSCREEN)) + { + if(SDL_SetWindowFullscreen(window_.get(), SDL_WINDOW_FULLSCREEN) != 0) + { + std::cerr << "SDL_SetWindowFullscreen failed: " << SDL_GetError() << std::endl; + return; + } + } else if(GameResolution != appliedResolution_) + { + // Already fullscreen and the resolution changed. Toggle fullscreen off and + // back on so SDL/Wayland actually applies the new display mode. + if(SDL_SetWindowFullscreen(window_.get(), 0) != 0) + { + std::cerr << "SDL_SetWindowFullscreen(0) failed: " << SDL_GetError() << std::endl; + return; + } + SDL_SetWindowSize(window_.get(), GameResolution.x, GameResolution.y); + if(SDL_SetWindowFullscreen(window_.get(), SDL_WINDOW_FULLSCREEN) != 0) + { + std::cerr << "SDL_SetWindowFullscreen failed: " << SDL_GetError() << std::endl; + return; + } + } + } else + { + if(SDL_SetWindowFullscreen(window_.get(), 0) != 0) + { + std::cerr << "SDL_SetWindowFullscreen failed: " << SDL_GetError() << std::endl; + return; + } + SDL_SetWindowSize(window_.get(), GameResolution.x, GameResolution.y); + SDL_SetWindowPosition(window_.get(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + } + + UpdateDisplaySize(GameResolution); +} + +void CGame::setGLViewport() +{ + if(!window_) + return; + int w = 0, h = 0; + SDL_GL_GetDrawableSize(window_.get(), &w, &h); + if(w == 0 || h == 0) + return; + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, GameResolution.x, GameResolution.y, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); } void CGame::UpdateDisplaySize(const Extent& newSize) { GameResolution = newSize; - RecreateDisplayResources(); + appliedResolution_ = GameResolution; + appliedFullscreen_ = fullscreen; + + Surf_Display = makeRGBSurface(GameResolution.x, GameResolution.y, true); + displayTexture_.createEmpty(GameResolution); + + setGLViewport(); for(auto& menu : Menus) + { menu->resetSurface(); + } for(auto& wnd : Windows) wnd->resetSurface(); } @@ -62,7 +144,7 @@ bool CGame::Init() SDL_ShowCursor(SDL_DISABLE); std::cout << "Create Window..."; - if(!ReCreateWindow()) + if(!CreateWindow()) { std::cout << "failure"; return false; @@ -94,10 +176,14 @@ bool CGame::Init() } } + // Create texture for splash background + splashBg_.load(global::bmpArray[SPLASHSCREEN_LOADING_S2SCREEN].surface.get(), true); + // std::cout << "\nShow loading screen..."; showLoadScreen = true; - CSurface::DrawStretched(Surf_Display, global::bmpArray[SPLASHSCREEN_LOADING_S2SCREEN].surface); - RenderPresent(); + glClear(GL_COLOR_BUFFER_BIT); + splashBg_.Draw(Rect(0, 0, GameResolution.x, GameResolution.y)); + SDL_GL_SwapWindow(window_.get()); GameDataLoader gdLoader(global::worldDesc); if(!gdLoader.Load()) @@ -252,5 +338,10 @@ bool CGame::Init() // create the mainmenu callback::mainmenu(INITIALIZING_CALL); + // Create textures for cursor + cursor_.load(global::bmpArray[CURSOR].surface.get()); + cursorClicked_.load(global::bmpArray[CURSOR_CLICKED].surface.get()); + cross_.load(global::bmpArray[CROSS].surface.get()); + return true; } diff --git a/CGame_Render.cpp b/CGame_Render.cpp index 3fc316c..93da104 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -10,6 +10,7 @@ #include "CMap.h" #include "CSurface.h" #include "globals.h" +#include #ifdef _WIN32 # include "s25editResource.h" # ifndef WIN32_LEAN_AND_MEAN @@ -38,18 +39,14 @@ void CGame::SetAppIcon() void CGame::Render() { - suppressResizeEvents_ = 0; - if(Extent(Surf_Display->w, Surf_Display->h) != GameResolution - || fullscreen != ((SDL_GetWindowFlags(window_.get()) & SDL_WINDOW_FULLSCREEN) != 0)) - { - ReCreateWindow(); - } + glClear(GL_COLOR_BUFFER_BIT); + SDL_FillRect(Surf_Display.get(), nullptr, SDL_MapRGBA(Surf_Display->format, 0, 0, 0, 0)); // if the S2 loading screen is shown, render only this until user clicks a mouse button if(showLoadScreen) { - CSurface::DrawStretched(Surf_Display, global::bmpArray[SPLASHSCREEN_LOADING_S2SCREEN].surface); - RenderPresent(); + splashBg_.Draw(Rect(0, 0, GameResolution.x, GameResolution.y)); + SDL_GL_SwapWindow(window_.get()); return; } @@ -103,16 +100,6 @@ void CGame::Render() } } - // render mouse cursor - if(Cursor.clicked) - { - if(Cursor.button.right) - CSurface::Draw(Surf_Display, global::bmpArray[CROSS].surface, Cursor.pos); - else - CSurface::Draw(Surf_Display, global::bmpArray[CURSOR_CLICKED].surface, Cursor.pos); - } else - CSurface::Draw(Surf_Display, global::bmpArray[CURSOR].surface, Cursor.pos); - #ifdef _ADMINMODE FrameCounter++; #endif diff --git a/CIO/CFile.cpp b/CIO/CFile.cpp index 023d89f..078d1d0 100644 --- a/CIO/CFile.cpp +++ b/CIO/CFile.cpp @@ -291,6 +291,7 @@ bool CFile::read_bbm(FILE* fp) CHECK_READ(libendian::read(&(color.r), 1, fp)); CHECK_READ(libendian::read(&(color.g), 1, fp)); CHECK_READ(libendian::read(&(color.b), 1, fp)); + color.a = 255; } palArray++; @@ -1250,6 +1251,7 @@ bool CFile::read_bob05(FILE* fp) CHECK_READ(libendian::read(&(color.r), 1, fp)); CHECK_READ(libendian::read(&(color.g), 1, fp)); CHECK_READ(libendian::read(&(color.b), 1, fp)); + color.a = 255; } palArray++; diff --git a/CIO/CMenu.cpp b/CIO/CMenu.cpp index fc8da5e..af68a28 100644 --- a/CIO/CMenu.cpp +++ b/CIO/CMenu.cpp @@ -5,7 +5,7 @@ #include "CMenu.h" #include "../CGame.h" -#include "../CSurface.h" +#include "../Texture.h" #include "../globals.h" CMenu::CMenu(int pic_background) : CControlContainer(pic_background) {} @@ -15,19 +15,30 @@ bool CMenu::render() if(getBackground() < 0) return false; - // if we don't need to render, all is up to date, return true + if(!bgTexture_) + { + const int picIdx = getBackground(); + if(picIdx >= 0 && picIdx < static_cast(global::bmpArray.size()) && global::bmpArray[picIdx].surface) + { + bgTexture_ = std::make_unique(); + bgTexture_->load(global::bmpArray[picIdx].surface.get(), true); + } + } + + if(bgTexture_) + bgTexture_->Draw(Rect(0, 0, global::s2->getRes().x, global::s2->getRes().y)); + if(!needRender) return true; needRender = false; // if we need a new surface if(!surface) { - surface = makeRGBSurface(global::s2->getRes().x, global::s2->getRes().y); + surface = makeRGBSurface(global::s2->getRes().x, global::s2->getRes().y, true); if(!surface) return false; } - CSurface::DrawStretched(surface, global::bmpArray[getBackground()].surface); renderElements(); return true; } diff --git a/CIO/CMenu.h b/CIO/CMenu.h index b03832a..59d8400 100644 --- a/CIO/CMenu.h +++ b/CIO/CMenu.h @@ -6,11 +6,15 @@ #pragma once #include "CControlContainer.h" +#include + +class Texture; class CMenu final : public CControlContainer { // if active is false, the menu will not be render within the game loop bool active = true; + mutable std::unique_ptr bgTexture_; bool render() final; diff --git a/CMakeLists.txt b/CMakeLists.txt index a6e3788..524f7d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ ELSE() ENDIF() add_executable(s25edit ${MAIN_SOURCES} ${CIO_SOURCES} ${icon_RC}) -target_link_libraries(s25edit PRIVATE SGE rttrConfig s25Common gamedata siedler2 endian::static Boost::nowide PUBLIC Boost::disable_autolinking Boost::program_options) +target_link_libraries(s25edit PRIVATE SGE rttrConfig s25Common gamedata siedler2 endian::static glad Boost::nowide PUBLIC Boost::disable_autolinking Boost::program_options) target_include_directories(s25edit PRIVATE include) target_compile_features(s25edit PRIVATE cxx_std_17) diff --git a/CSurface.cpp b/CSurface.cpp index 18cc1ce..d032a13 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -184,27 +184,6 @@ bool CSurface::Draw(SDL_Surface* Surf_Dest, SdlSurface& Surf_Src, Position dest, return Draw(Surf_Dest, Surf_Src.get(), dest, srcOffset, srcSize); } -void CSurface::DrawStretched(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src) -{ - if(!Surf_Dest || !Surf_Src) - return; - - // Convert to 32-bit RGB to handle 8-bit paletted sources and drop any - // implicit alpha from LBM palette entries (which have a=0). - SDL_Surface* converted = SDL_ConvertSurfaceFormat(Surf_Src, SDL_PIXELFORMAT_RGB888, 0); - if(!converted) - return; - - // Stretch full source to fill full destination - SDL_BlitScaled(converted, nullptr, Surf_Dest, nullptr); - SDL_FreeSurface(converted); -} - -void CSurface::DrawStretched(SdlSurface& Surf_Dest, SdlSurface& Surf_Src) -{ - DrawStretched(Surf_Dest.get(), Surf_Src.get()); -} - // this is the example function from the SDL-documentation to draw pixels void CSurface::DrawPixel_Color(SDL_Surface* screen, Position pos, Uint32 color) { diff --git a/CSurface.h b/CSurface.h index 6b32021..3677579 100644 --- a/CSurface.h +++ b/CSurface.h @@ -45,9 +45,6 @@ class CSurface { return Draw(Surf_Dest.get(), Surf_Src.get(), X, Y, X2, Y2, W, H); } - // stretches Surf_Src to fill entire Surf_Dest - static void DrawStretched(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src); - static void DrawStretched(SdlSurface& Surf_Dest, SdlSurface& Surf_Src); static void DrawPixel_Color(SDL_Surface* screen, Position pos, Uint32 color); static void DrawPixel_RGB(SDL_Surface* screen, Position pos, Uint8 R, Uint8 G, Uint8 B); static void DrawPixel_RGB(SdlSurface& screen, Position pos, Uint8 R, Uint8 G, Uint8 B) diff --git a/Texture.cpp b/Texture.cpp new file mode 100644 index 0000000..5ff6a56 --- /dev/null +++ b/Texture.cpp @@ -0,0 +1,155 @@ +// Copyright (C) 2026 - 2026 Settlers Freaks +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "Texture.h" +#include +#include +#include + +Texture::~Texture() +{ + if(texture_) + glDeleteTextures(1, &texture_); +} + +Texture::Texture(Texture&& other) noexcept + : texture_(std::exchange(other.texture_, 0)), size_(std::exchange(other.size_, {0, 0})) +{} + +Texture& Texture::operator=(Texture&& other) noexcept +{ + if(this != &other) + { + if(texture_) + glDeleteTextures(1, &texture_); + texture_ = std::exchange(other.texture_, 0); + size_ = std::exchange(other.size_, {0, 0}); + } + return *this; +} + +void Texture::load(const void* bgraPixels, Extent size, bool filterLinear) +{ + if(texture_) + glDeleteTextures(1, &texture_); + texture_ = 0; + size_ = {0, 0}; + + glGenTextures(1, &texture_); + glBindTexture(GL_TEXTURE_2D, texture_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterLinear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterLinear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_BGRA, GL_UNSIGNED_BYTE, bgraPixels); + size_ = size; +} + +void Texture::createEmpty(Extent size, bool filterLinear) +{ + load(nullptr, size, filterLinear); +} + +void Texture::upload(const void* bgraPixels) +{ + if(!texture_) + return; + glBindTexture(GL_TEXTURE_2D, texture_); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size_.x, size_.y, GL_BGRA, GL_UNSIGNED_BYTE, bgraPixels); +} + +bool Texture::load(SDL_Surface* surface, bool filterLinear) +{ + if(!surface) + return false; + + // For 8-bit paletted surfaces, honour colorkey if set. + if(surface->format->palette) + { + const int w = surface->w, h = surface->h; + std::vector pixels(static_cast(w) * h); + + SDL_Palette* pal = surface->format->palette; + Uint32 ck; + const bool hasCK = SDL_GetColorKey(surface, &ck) == 0; + const Uint8 ckIdx = hasCK ? static_cast(ck & 0xFF) : 0; + + SDL_LockSurface(surface); + for(int row = 0; row < h; row++) + { + const auto* src = (const Uint8*)surface->pixels + row * surface->pitch; + for(int col = 0; col < w; col++) + { + const Uint8 idx = src[col]; + if(hasCK && idx == ckIdx) + pixels[row * w + col] = 0; // transparent + else + { + const SDL_Color& c = pal->colors[idx]; + // BGRA layout: A<<24 | R<<16 | G<<8 | B (little-endian GL_BGRA) + pixels[row * w + col] = (0xFFu << 24) | (Uint32(c.r) << 16) | (Uint32(c.g) << 8) | Uint32(c.b); + } + } + } + SDL_UnlockSurface(surface); + + load(pixels.data(), Extent(w, h), filterLinear); + return true; + } + + // 32-bit surface: convert to destination format (BGRA), force full opacity + SDL_Surface* converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0); + if(!converted) + return false; + + // Force alpha to opaque (LBM palette entries often have alpha=0) + SDL_LockSurface(converted); + for(int y = 0; y < converted->h; y++) + { + auto* row = (Uint32*)((Uint8*)converted->pixels + y * converted->pitch); + for(int x = 0; x < converted->w; x++) + row[x] |= 0xFF000000u; // set alpha bits + } + SDL_UnlockSurface(converted); + + load(converted->pixels, Extent(converted->w, converted->h), filterLinear); + SDL_FreeSurface(converted); + return true; +} + +void Texture::Draw(const Rect& destRect) const +{ + if(!texture_) + return; + + glBindTexture(GL_TEXTURE_2D, texture_); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2i(destRect.left, destRect.top); + glTexCoord2f(1, 0); + glVertex2i(destRect.right, destRect.top); + glTexCoord2f(1, 1); + glVertex2i(destRect.right, destRect.bottom); + glTexCoord2f(0, 1); + glVertex2i(destRect.left, destRect.bottom); + glEnd(); +} + +void Texture::Draw(Position pos) const +{ + if(!texture_) + return; + + glBindTexture(GL_TEXTURE_2D, texture_); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2i(pos.x, pos.y); + glTexCoord2f(1, 0); + glVertex2i(pos.x + size_.x, pos.y); + glTexCoord2f(1, 1); + glVertex2i(pos.x + size_.x, pos.y + size_.y); + glTexCoord2f(0, 1); + glVertex2i(pos.x, pos.y + size_.y); + glEnd(); +} diff --git a/Texture.h b/Texture.h new file mode 100644 index 0000000..5c19b8c --- /dev/null +++ b/Texture.h @@ -0,0 +1,50 @@ +// Copyright (C) 2026 - 2026 Settlers Freaks +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "Rect.h" +#include +#include + +/// Wraps a texture with RAII and provides draw methods. +class Texture +{ +public: + Texture() = default; + ~Texture(); + + Texture(Texture&&) noexcept; + Texture& operator=(Texture&&) noexcept; + + // No copy + Texture(const Texture&) = delete; + Texture& operator=(const Texture&) = delete; + + /// Load from a 32-bit or 8-bit paletted SDL surface. + /// For 8-bit surfaces, optional colorkey is respected (keyed pixels become transparent). + bool load(SDL_Surface* surface, bool filterLinear = false); + + /// Create an empty texture of the given size (for use as a render-target). + void createEmpty(Extent size, bool filterLinear = false); + + /// Upload new pixel data to an existing texture (glTexSubImage2D). + void upload(const void* bgraPixels); + + /// Draw the texture stretched to fill the given rect. + void Draw(const Rect& destRect) const; + + /// Draw the texture at native size at the given position. + void Draw(Position pos) const; + + /// Returns true if the texture has been created. + bool isValid() const { return texture_ != 0; } + +private: + unsigned int texture_ = 0; + Extent size_; + + /// Internal: create or recreate texture from raw BGRA pixel data. + void load(const void* bgraPixels, Extent size, bool filterLinear); +}; diff --git a/callbacks.cpp b/callbacks.cpp index 7c69194..6e16659 100644 --- a/callbacks.cpp +++ b/callbacks.cpp @@ -319,6 +319,7 @@ void callback::submenuOptions(int Param) case GRAPHICS_CHANGE: assert(SubMenu); + global::s2->ApplyWindowChanges(); global::s2->SaveSettings(); SubMenu->setWaste(); TextResolution = nullptr;