diff --git a/CGame.cpp b/CGame.cpp index 18f90ed..e399ac8 100644 --- a/CGame.cpp +++ b/CGame.cpp @@ -77,11 +77,8 @@ int CGame::Execute() void CGame::RenderPresent() { - 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); + cursorImg.draw(Cursor.pos); SDL_GL_SwapWindow(window_.get()); } diff --git a/CGame.h b/CGame.h index f4a78e5..d62ea65 100644 --- a/CGame.h +++ b/CGame.h @@ -6,7 +6,6 @@ #pragma once #include "CIO/CFont.h" -#include "SdlSurface.h" #include "Texture.h" #include #include @@ -27,8 +26,6 @@ class CGame bool Running; bool showLoadScreen; - SdlSurface Surf_Display; - Texture displayTexture_; SDL_GLContext glContext_ = nullptr; SdlWindow window_; @@ -52,6 +49,7 @@ class CGame Texture cursor_; Texture cursorClicked_; Texture cross_; + Texture mapTex_; ///< Texture for the map/terrain (Surf_Map may be 8-bit) // structure for mouse cursor struct @@ -111,6 +109,5 @@ class CGame CMap* getMapObj(); void delMapObj(); void enterEditor(const boost::filesystem::path& filepath); - SDL_Surface* getDisplaySurface() const { return Surf_Display.get(); }; auto getRes() const { return GameResolution; } }; diff --git a/CGame_Event.cpp b/CGame_Event.cpp index 7839c79..93ed86c 100644 --- a/CGame_Event.cpp +++ b/CGame_Event.cpp @@ -8,6 +8,7 @@ #include "CIO/CWindow.h" #include "CMap.h" #include "CSurface.h" +#include "CollisionDetection.h" #include "callbacks.h" #include "globals.h" @@ -195,9 +196,7 @@ void CGame::EventHandling(SDL_Event* Event) if(!Window->isWaste() && Window->getPriority() == actualPriority) { // is the cursor INSIDE the window or does the user move or resize the window? - if(((Event->motion.x >= Window->getX()) && (Event->motion.x < Window->getX() + Window->getW()) - && (Event->motion.y >= Window->getY()) - && (Event->motion.y < Window->getY() + Window->getH())) + if(IsPointInRect(Event->motion.x, Event->motion.y, Rect(Window->getPos(), Window->getSize())) || Window->isMoving() || Window->isResizing()) { // Windows[i]->setActive(); @@ -275,9 +274,7 @@ void CGame::EventHandling(SDL_Event* Event) if(!Window->isWaste() && Window->getPriority() == actualPriority) { // is the cursor INSIDE the window? - if((Event->button.x >= Window->getX()) && (Event->button.x < Window->getX() + Window->getW()) - && (Event->button.y >= Window->getY()) - && (Event->button.y < Window->getY() + Window->getH())) + if(IsPointInRect(Event->button.x, Event->button.y, Rect(Window->getPos(), Window->getSize()))) { Window->setActive(); Window->setPriority(highestPriority + 1); @@ -338,9 +335,7 @@ void CGame::EventHandling(SDL_Event* Event) if(!Window->isWaste() && Window->getPriority() == actualPriority) { // is the cursor INSIDE the window? - if((Event->button.x >= Window->getX()) && (Event->button.x < Window->getX() + Window->getW()) - && (Event->button.y >= Window->getY()) - && (Event->button.y < Window->getY() + Window->getH())) + if(IsPointInRect(Event->button.x, Event->button.y, Rect(Window->getPos(), Window->getSize()))) { // Windows[i]->setActive(); // Windows[i]->setPriority(highestPriority+1); diff --git a/CGame_Init.cpp b/CGame_Init.cpp index 7f437db..c386977 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -38,8 +38,6 @@ bool CGame::CreateWindow() SDL_ShowWindow(window_.get()); ApplyWindowChanges(); - if(!displayTexture_.isValid() || !Surf_Display) - return false; SetAppIcon(); @@ -125,16 +123,7 @@ void CGame::UpdateDisplaySize(const Extent& newSize) 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(); } bool CGame::Init() @@ -182,7 +171,7 @@ bool CGame::Init() // std::cout << "\nShow loading screen..."; showLoadScreen = true; glClear(GL_COLOR_BUFFER_BIT); - splashBg_.Draw(Rect(0, 0, GameResolution.x, GameResolution.y)); + splashBg_.draw(Rect(0, 0, GameResolution.x, GameResolution.y)); SDL_GL_SwapWindow(window_.get()); GameDataLoader gdLoader(global::worldDesc); diff --git a/CGame_Render.cpp b/CGame_Render.cpp index 93da104..3fb5b4b 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -9,6 +9,7 @@ #include "CIO/CWindow.h" #include "CMap.h" #include "CSurface.h" +#include "Texture.h" #include "globals.h" #include #ifdef _WIN32 @@ -40,12 +41,13 @@ void CGame::SetAppIcon() void CGame::Render() { glClear(GL_COLOR_BUFFER_BIT); - SDL_FillRect(Surf_Display.get(), nullptr, SDL_MapRGBA(Surf_Display->format, 0, 0, 0, 0)); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // if the S2 loading screen is shown, render only this until user clicks a mouse button if(showLoadScreen) { - splashBg_.Draw(Rect(0, 0, GameResolution.x, GameResolution.y)); + splashBg_.draw(Rect(0, 0, GameResolution.x, GameResolution.y)); SDL_GL_SwapWindow(window_.get()); return; } @@ -53,33 +55,35 @@ void CGame::Render() // render the map if active if(MapObj && MapObj->isActive()) { - CSurface::Draw(Surf_Display, MapObj->getSurface(), 0, 0); - std::array textBuffer; - // text for x and y of vertex (shown in upper left corner) - std::snprintf(textBuffer.data(), textBuffer.size(), "%d %d", MapObj->getVertexX(), MapObj->getVertexY()); - CFont::writeText(Surf_Display, textBuffer.data(), Position(20, 20)); - // text for MinReduceHeight and MaxRaiseHeight - std::snprintf(textBuffer.data(), textBuffer.size(), - "min. height: %#04x/0x3C max. height: %#04x/0x3C NormalNull: 0x0A", - MapObj->getMinReduceHeight(), MapObj->getMaxRaiseHeight()); - CFont::writeText(Surf_Display, textBuffer.data(), Position(100, 20)); - // text for MovementLocked - if(MapObj->isHorizontalMovementLocked() && MapObj->isVerticalMovementLocked()) - CFont::writeText(Surf_Display, "Movement locked (F9 or F10 to unlock)", Position(20, 40), FontSize::Large, - FontColor::Orange); - else if(MapObj->isHorizontalMovementLocked()) - CFont::writeText(Surf_Display, "Horizontal movement locked (F9 to unlock)", Position(20, 40), - FontSize::Large, FontColor::Orange); - else if(MapObj->isVerticalMovementLocked()) - CFont::writeText(Surf_Display, "Vertical movement locked (F10 to unlock)", Position(20, 40), - FontSize::Large, FontColor::Orange); + if(auto* mapSurf = MapObj->getSurface()) + { + std::array textBuffer; + std::snprintf(textBuffer.data(), textBuffer.size(), "%d %d", MapObj->getVertexX(), MapObj->getVertexY()); + CFont::writeText(mapSurf, textBuffer.data(), 20, 20); + std::snprintf(textBuffer.data(), textBuffer.size(), + "min. height: %#04x/0x3C max. height: %#04x/0x3C NormalNull: 0x0A", + MapObj->getMinReduceHeight(), MapObj->getMaxRaiseHeight()); + CFont::writeText(mapSurf, textBuffer.data(), 100, 20); + if(MapObj->isHorizontalMovementLocked() && MapObj->isVerticalMovementLocked()) + CFont::writeText(mapSurf, "Movement locked (F9 or F10 to unlock)", 20, 40, FontSize::Large, + FontColor::Orange); + else if(MapObj->isHorizontalMovementLocked()) + CFont::writeText(mapSurf, "Horizontal movement locked (F9 to unlock)", 20, 40, FontSize::Large, + FontColor::Orange); + else if(MapObj->isVerticalMovementLocked()) + CFont::writeText(mapSurf, "Vertical movement locked (F10 to unlock)", 20, 40, FontSize::Large, + FontColor::Orange); + + mapTex_.load(mapSurf); + mapTex_.draw(Rect(0, 0, GameResolution.x, GameResolution.y)); + } } // render active menus for(auto& Menu : Menus) { if(Menu->isActive()) - CSurface::Draw(Surf_Display, Menu->getSurface(), 0, 0); + Menu->draw(Position(0, 0)); } // render windows ordered by priority @@ -96,7 +100,7 @@ void CGame::Render() for(auto& Window : Windows) { if(Window->getPriority() == actualPriority) - CSurface::Draw(Surf_Display, Window->getSurface(), Window->getX(), Window->getY()); + Window->draw(Position(0, 0)); } } @@ -105,7 +109,6 @@ void CGame::Render() #endif ++framesPassedSinceLastFps; - const auto curTicks = SDL_GetTicks(); const auto diffTicks = curTicks - lastFpsTick; if(diffTicks > 1000) @@ -114,9 +117,16 @@ void CGame::Render() framesPassedSinceLastFps = 0; lastFpsTick = curTicks; } - CSurface::Draw(Surf_Display, lastFps.getSurface(), 0, 0); + lastFps.draw(Position(0, 0)); + + const auto& cursorImg = Cursor.clicked ? (Cursor.button.right ? cross_ : cursorClicked_) : cursor_; + cursorImg.draw(Cursor.pos); - RenderPresent(); + SDL_GL_SwapWindow(window_.get()); + +#ifdef _ADMINMODE + FrameCounter++; +#endif if(msWait) SDL_Delay(msWait); diff --git a/CIO/CButton.cpp b/CIO/CButton.cpp index f5f2c7b..cb25536 100644 --- a/CIO/CButton.cpp +++ b/CIO/CButton.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "CButton.h" -#include "../CSurface.h" +#include "../Texture.h" #include "../globals.h" #include "CFont.h" #include "CollisionDetection.h" @@ -23,19 +23,16 @@ CButton::CButton(void callback(int), int clickedParam, Position pos, Extent size this->clickedParam = clickedParam; motionEntryParam = -1; motionLeaveParam = -1; - needRender = true; } void CButton::setButtonPicture(int picture) { this->button_picture = picture; - needRender = true; } void CButton::setButtonText(const char* text) { button_text = text; - needRender = true; } void CButton::setColor(int color) @@ -84,14 +81,12 @@ void CButton::setColor(int color) pic_background = BUTTON_GREY_BACKGROUND; break; } - - needRender = true; } void CButton::setMouseData(const SDL_MouseMotionEvent& motion) { // cursor is on the button (and mouse button not pressed while moving on the button) - if(IsPointInRect(Position(motion.x, motion.y), Rect(pos_, size_))) + if(IsPointInRect(motion.x, motion.y, Rect(pos_, size_))) { if(motion.state == SDL_RELEASED) { @@ -106,7 +101,6 @@ void CButton::setMouseData(const SDL_MouseMotionEvent& motion) callback_(motionLeaveParam); marked = false; } - needRender = true; } void CButton::setMouseData(const SDL_MouseButtonEvent& button) @@ -115,7 +109,7 @@ void CButton::setMouseData(const SDL_MouseButtonEvent& button) if(button.button == SDL_BUTTON_LEFT) { // if mouse button is pressed ON the button, set marked=true - if((button.state == SDL_PRESSED) && IsPointInRect(Position(button.x, button.y), Rect(pos_, size_))) + if(button.state == SDL_PRESSED && IsPointInRect(button.x, button.y, Rect(pos_, size_))) { marked = true; clicked = true; @@ -127,191 +121,28 @@ void CButton::setMouseData(const SDL_MouseButtonEvent& button) callback_(clickedParam); } } - needRender = true; } -bool CButton::render() +void CButton::draw(Position parentOrigin) const { - // position in the Surface 'Surf_Button' - Position pos{0, 0}; - // width and height of the button color source picture - Extent pic{0, 0}; - // foreground of the button --> marked or unmarked, NOT the picture - int foreground; - - // if we don't need to render, all is up to date, return true - if(!needRender) - return true; - needRender = false; - // if we need a new surface - if(!Surf_Button) - { - if((Surf_Button = makeRGBSurface(size_.x, size_.y)) == nullptr) - return false; - } - - // at first completly fill the background (not the fastest way, but simplier) - if(size_.x <= global::bmpArray[pic_background].w) - pic.x = size_.x; - else - pic.x = global::bmpArray[pic_background].w; - - if(size_.y <= global::bmpArray[pic_background].h) - pic.y = size_.y; - else - pic.y = global::bmpArray[pic_background].h; - - while(pos.x + pic.x <= static_cast(Surf_Button->w)) - { - while(pos.y + pic.y <= static_cast(Surf_Button->h)) - { - CSurface::Draw(Surf_Button, global::bmpArray[pic_background].surface, pos, Position(0, 0), pic); - pos.y += pic.y; - } - - if(pos.y < Surf_Button->h) - CSurface::Draw(Surf_Button, global::bmpArray[pic_background].surface, pos, Position(0, 0), - Extent(pic.x, static_cast(Surf_Button->h - pos.y))); - - pos.y = 0; - pos.x += pic.x; - } - - if(pos.x < Surf_Button->w) - { - while(pos.y + pic.y <= static_cast(Surf_Button->h)) - { - CSurface::Draw(Surf_Button, global::bmpArray[pic_background].surface, pos, Position(0, 0), - Extent(static_cast(Surf_Button->w - pos.x), pic.y)); - pos.y += pic.y; - } - - if(pos.y < Surf_Button->h) - CSurface::Draw( - Surf_Button, global::bmpArray[pic_background].surface, pos, Position(0, 0), - Extent(static_cast(Surf_Button->w - pos.x), static_cast(Surf_Button->h - pos.y))); - } - - // draw partial black frame - if(clicked) - { - // black frame is left and up - // draw vertical line - pos.x = 0; - for(unsigned y = 0; y < size_.y; y++) - CSurface::DrawPixel_RGB(Surf_Button, Position(pos.x, y), 0, 0, 0); - - // draw vertical line - pos.x = 1; - for(unsigned y = 0; y < size_.y - 1; y++) - CSurface::DrawPixel_RGB(Surf_Button, Position(pos.x, y), 0, 0, 0); - - // draw horizontal line - pos.y = 0; - for(unsigned x = 0; x < size_.x; x++) - CSurface::DrawPixel_RGB(Surf_Button, Position(x, pos.y), 0, 0, 0); - - // draw horizontal line - pos.y = 1; - for(unsigned x = 0; x < size_.x - 1; x++) - CSurface::DrawPixel_RGB(Surf_Button, Position(x, pos.y), 0, 0, 0); - } else - { - // black frame is right and down - // draw vertical line - pos.x = size_.x - 1; - for(unsigned y = 0; y < size_.y; y++) - CSurface::DrawPixel_RGB(Surf_Button, Position(pos.x, y), 0, 0, 0); + const Position absPos = parentOrigin + pos_; - // draw vertical line - pos.x = size_.x - 2; - for(unsigned y = 1; y < size_.y; y++) - CSurface::DrawPixel_RGB(Surf_Button, Position(pos.x, y), 0, 0, 0); + // Draw 3D button box + const int foreground = (marked && !clicked) ? pic_marked : pic_normal; + drawButtonBox(Rect(absPos, size_), clicked, pic_background, foreground); - // draw horizontal line - pos.y = size_.y - 1; - for(unsigned x = 0; x < size_.x; x++) - CSurface::DrawPixel_RGB(Surf_Button, Position(x, pos.y), 0, 0, 0); - - // draw horizontal line - pos.y = size_.y - 2; - for(unsigned x = 1; x < size_.x; x++) - CSurface::DrawPixel_RGB(Surf_Button, Position(x, pos.y), 0, 0, 0); - } - - // draw the foreground --> at first the color (marked or unmarked) and then the picture or text - if(size_.x <= global::bmpArray[pic_normal].w) - pic.x = size_.x; - else - pic.x = global::bmpArray[pic_normal].w; - - if(size_.y <= global::bmpArray[pic_normal].h) - pic.y = size_.y; - else - pic.y = global::bmpArray[pic_normal].h; - - // beware overdrawing the left and upper frame - pos.x = 2; - pos.y = 2; - - // decide if button lights or not - if(marked && !clicked) - foreground = pic_marked; - else - foreground = pic_normal; - - // '-2' follows a few times, this means: beware overdrawing the right and lower frame - while(pos.x + pic.x <= static_cast(Surf_Button->w - 2)) - { - while(pos.y + pic.y <= static_cast(Surf_Button->h - 2)) - { - CSurface::Draw(Surf_Button, global::bmpArray[foreground].surface, pos, Position(0, 0), pic); - pos.y += pic.y; - } - - if(pos.y + 2 < Surf_Button->h) - CSurface::Draw(Surf_Button, global::bmpArray[foreground].surface, pos, Position(0, 0), - Extent(pic.x, static_cast(Surf_Button->h - pos.y))); - - pos.y = 2; - pos.x += pic.x; - } - - if(pos.x + 2 < Surf_Button->w) - { - while(pos.y + pic.y <= static_cast(Surf_Button->h - 2)) - { - CSurface::Draw(Surf_Button, global::bmpArray[foreground].surface, pos, Position(0, 0), - Extent(static_cast(Surf_Button->w - 2 - pos.x), pic.y)); - pos.y += pic.y; - } - - if(pos.y + 2 < Surf_Button->h) - CSurface::Draw(Surf_Button, global::bmpArray[foreground].surface, pos, Position(0, 0), - Extent(static_cast(Surf_Button->w - 2 - pos.x), - static_cast(Surf_Button->h - 2 - pos.y))); - } - - // positioning the picture or write text + // 4. Draw picture or text centered inside the button if(button_picture >= 0) { - // picture may not be bigger than the button - if(size_.x <= static_cast(Surf_Button->w) && size_.y <= static_cast(Surf_Button->h)) - { - // get coordinates of the left upper corner where to positionate the picture - Position leftup = Position(Surf_Button->w, Surf_Button->h) / 2 - - Position(global::bmpArray[button_picture].w, global::bmpArray[button_picture].h) / 2; - // blit it - CSurface::Draw(Surf_Button, global::bmpArray[button_picture].surface, leftup); - } else - { - button_picture = -1; - button_text = "PIC"; - } + auto& picTex = getBmpTexture(button_picture); + const Position picPos = absPos + size_ / 2 - Position(picTex.getSize()) / 2; + picTex.draw(picPos); } else if(button_text) - CFont::writeText(Surf_Button, button_text, - Position(static_cast(size_.x / 2), static_cast((size_.y - 11) / 2)), - FontSize::Medium, button_text_color, FontAlign::Middle); - - return true; + { + // Draw text centered (using native-size texture drawing for each character) + const Extent textSize(CFont::getTextWidth(button_text, FontSize::Medium), + static_cast(FontSize::Medium)); + const Position textPos = absPos + (Position(size_) - textSize) / 2; + CFont::draw(button_text, textPos, FontSize::Medium, button_text_color, FontAlign::Left); + } } diff --git a/CIO/CButton.h b/CIO/CButton.h index d9b54e7..27fe79d 100644 --- a/CIO/CButton.h +++ b/CIO/CButton.h @@ -5,7 +5,6 @@ #pragma once -#include "SdlSurface.h" #include "defines.h" class CButton @@ -13,8 +12,6 @@ class CButton friend class CDebug; private: - SdlSurface Surf_Button; - bool needRender; Position pos_; Extent size_; int pic_normal; @@ -36,26 +33,17 @@ class CButton // Access int getX() const { return pos_.x; }; int getY() const { return pos_.y; }; - const Position& getPos() const { return pos_; } - const Extent& getSize() const { return size_; }; + Position getPos() const { return pos_; } + Extent getSize() const { return size_; }; void setX(int x) { pos_.x = x; }; void setY(int y) { pos_.y = y; }; void setButtonPicture(int picture); void setButtonText(const char* text); void setMouseData(const SDL_MouseMotionEvent& motion); void setMouseData(const SDL_MouseButtonEvent& button); - bool render(); - SDL_Surface* getSurface() - { - render(); - return Surf_Button.get(); - }; + void draw(Position parentOrigin) const; void setColor(int color); - void setTextColor(FontColor color) - { - button_text_color = color; - needRender = true; - }; + void setTextColor(FontColor color) { button_text_color = color; }; void setMotionParams(int entry, int leave) { motionEntryParam = entry; diff --git a/CIO/CControlContainer.cpp b/CIO/CControlContainer.cpp index e839f38..df98926 100644 --- a/CIO/CControlContainer.cpp +++ b/CIO/CControlContainer.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "CControlContainer.h" -#include "../CSurface.h" +#include "../Texture.h" #include "../globals.h" #include "CButton.h" #include "CFont.h" @@ -13,11 +13,9 @@ #include "CTextfield.h" #include "helpers/containerUtils.h" -CControlContainer::CControlContainer(int pic_background) - : CControlContainer(pic_background, Extent::all(0), Extent::all(0)) -{} -CControlContainer::CControlContainer(int pic_background, Extent borderBeginSize, Extent borderEndSize) - : borderBeginSize(borderBeginSize), borderEndSize(borderEndSize), pic_background(pic_background) +CControlContainer::CControlContainer(int pic_background) : CControlContainer(pic_background, BorderSizes{}) {} +CControlContainer::CControlContainer(int pic_background, BorderSizes border) + : border(border), pic_background(pic_background) {} CControlContainer::~CControlContainer() noexcept = default; @@ -25,7 +23,6 @@ CControlContainer::~CControlContainer() noexcept = default; void CControlContainer::setBackgroundPicture(int pic_background) { this->pic_background = pic_background; - needRender = true; } void CControlContainer::setMouseData(const SDL_MouseMotionEvent motion) @@ -42,7 +39,6 @@ void CControlContainer::setMouseData(const SDL_MouseMotionEvent motion) { selectbox->setMouseData(motion); } - needRender = true; } void CControlContainer::setMouseData(const SDL_MouseButtonEvent button) @@ -63,7 +59,6 @@ void CControlContainer::setMouseData(const SDL_MouseButtonEvent button) { selectbox->setMouseData(button); } - needRender = true; } void CControlContainer::setKeyboardData(const SDL_KeyboardEvent& key) @@ -81,7 +76,6 @@ bool CControlContainer::eraseElement(T& collection, const U* element) if(it != collection.end()) { collection.erase(it); - needRender = true; return true; } return false; @@ -90,10 +84,9 @@ bool CControlContainer::eraseElement(T& collection, const U* element) CButton* CControlContainer::addButton(void callback(int), int clickedParam, Position pos, Extent size, int color, const char* text, int picture) { - pos = pos + borderBeginSize; + pos = pos + Position(border.left, border.top); buttons.emplace_back(std::make_unique(callback, clickedParam, pos, size, color, text, picture)); - needRender = true; return buttons.back().get(); } @@ -104,10 +97,9 @@ bool CControlContainer::delButton(CButton* ButtonToDelete) CFont* CControlContainer::addText(std::string string, Position pos, FontSize fontsize, FontColor color) { - pos = pos + borderBeginSize; + pos = pos + Position(border.left, border.top); texts.emplace_back(std::make_unique(std::move(string), pos, fontsize, color)); - needRender = true; return texts.back().get(); } @@ -118,10 +110,9 @@ bool CControlContainer::delText(CFont* TextToDelete) CPicture* CControlContainer::addPicture(void callback(int), int clickedParam, Position pos, int picture) { - pos = pos + borderBeginSize; + pos = pos + Position(border.left, border.top); pictures.emplace_back(std::make_unique(callback, clickedParam, pos, picture)); - needRender = true; return pictures.back().get(); } @@ -134,11 +125,10 @@ int CControlContainer::addStaticPicture(Position pos, int picture) { if(picture < 0) return -1; - pos = pos + borderBeginSize; + pos = pos + Position(border.left, border.top); unsigned id = static_pictures.empty() ? 0u : static_pictures.back().id + 1u; static_pictures.emplace_back(Picture{pos, picture, id}); - needRender = true; return id; } @@ -151,7 +141,6 @@ bool CControlContainer::delStaticPicture(int picId) if(it != static_pictures.end()) { static_pictures.erase(it); - needRender = true; return true; } return false; @@ -160,11 +149,10 @@ bool CControlContainer::delStaticPicture(int picId) CTextfield* CControlContainer::addTextfield(Position pos, Uint16 cols, Uint16 rows, FontSize fontsize, FontColor text_color, int bg_color, bool button_style) { - pos = pos + borderBeginSize; + pos = pos + Position(border.left, border.top); textfields.emplace_back( std::make_unique(pos, cols, rows, fontsize, text_color, bg_color, button_style)); - needRender = true; return textfields.back().get(); } @@ -176,10 +164,9 @@ bool CControlContainer::delTextfield(CTextfield* TextfieldToDelete) CSelectBox* CControlContainer::addSelectBox(Position pos, Extent size, FontSize fontsize, FontColor text_color, int bg_color) { - pos += Position(borderBeginSize); + pos += Position(border.left, border.top); selectboxes.emplace_back(std::make_unique(pos, size, fontsize, text_color, bg_color)); - needRender = true; return selectboxes.back().get(); } @@ -188,18 +175,20 @@ bool CControlContainer::delSelectBox(CSelectBox* SelectBoxToDelete) return eraseElement(selectboxes, SelectBoxToDelete); } -void CControlContainer::renderElements() +void CControlContainer::drawChildren(Position origin) { for(const auto& picture : pictures) - CSurface::Draw(surface, picture->getSurface(), picture->getX(), picture->getY()); + picture->draw(origin); for(const auto& text : texts) - CSurface::Draw(surface, text->getSurface(), text->getX(), text->getY()); + text->draw(origin); for(const auto& textfield : textfields) - CSurface::Draw(surface, textfield->getSurface(), textfield->getX(), textfield->getY()); + textfield->draw(origin); for(const auto& selectbox : selectboxes) - CSurface::Draw(surface, selectbox->getSurface(), selectbox->getPos()); + selectbox->draw(origin); for(const auto& button : buttons) - CSurface::Draw(surface, button->getSurface(), button->getX(), button->getY()); + button->draw(origin); for(const auto& static_picture : static_pictures) - CSurface::Draw(surface, global::bmpArray[static_picture.pic].surface, static_picture.pos); + { + getBmpTexture(static_picture.pic).draw(origin + static_picture.pos); + } } diff --git a/CIO/CControlContainer.h b/CIO/CControlContainer.h index dacc91f..2f945dd 100644 --- a/CIO/CControlContainer.h +++ b/CIO/CControlContainer.h @@ -15,6 +15,15 @@ class CPicture; class CTextfield; class CSelectBox; +/// Pixel thickness of a window's frame border on each edge. +struct BorderSizes +{ + int left = 0; + int top = 0; + int right = 0; + int bottom = 0; +}; + class CControlContainer { friend class CDebug; @@ -28,7 +37,7 @@ class CControlContainer }; // if waste is true, the menu will be delete within the game loop - Extent borderBeginSize, borderEndSize; // Width and height of border at left/top and right/bottom + BorderSizes border; bool waste = false; int pic_background; std::vector> buttons; @@ -40,39 +49,34 @@ class CControlContainer template bool eraseElement(T& collection, const U* element); - virtual bool render() = 0; protected: - SdlSurface surface; - bool needRender = true; + /// Draw the container's background and child elements + /// @param parentOrigin Absolute position of the parent container. + virtual void draw(Position parentOrigin) = 0; + /// Draw children at the given origin (calls each child's Draw). + void drawChildren(Position origin); - void renderElements(); auto& getTextFields() { return textfields; } const auto& getTextFields() const { return textfields; } int getBackground() const { return pic_background; } public: CControlContainer(int pic_background); - CControlContainer(int pic_background, Extent borderBeginSize, Extent borderEndSize); - ~CControlContainer() noexcept; + CControlContainer(int pic_background, BorderSizes border); + virtual ~CControlContainer() noexcept; // Access - Extent getBorderSize() const { return borderBeginSize + borderEndSize; } + BorderSizes getBorderSizes() const { return border; } + Extent getBorderSize() const + { + return {static_cast(border.left + border.right), static_cast(border.top + border.bottom)}; + } void setBackgroundPicture(int pic_background); virtual void setMouseData(SDL_MouseMotionEvent motion); virtual void setMouseData(SDL_MouseButtonEvent button); void setKeyboardData(const SDL_KeyboardEvent& key); - SDL_Surface* getSurface() - { - render(); - return surface.get(); - } void setWaste() { waste = true; } bool isWaste() const { return waste; } - void resetSurface() - { - surface.reset(); - needRender = true; - } // Methods CButton* addButton(void callback(int), int clickedParam, Position pos = {0, 0}, Extent size = {20, 20}, int color = BUTTON_GREY, const char* text = nullptr, int picture = -1); diff --git a/CIO/CFont.cpp b/CIO/CFont.cpp index d7b421a..3fac602 100644 --- a/CIO/CFont.cpp +++ b/CIO/CFont.cpp @@ -5,37 +5,30 @@ #include "CFont.h" #include "../CSurface.h" +#include "../Texture.h" #include "../globals.h" +#include "CollisionDetection.h" #include CFont::CFont(std::string text, Position pos, FontSize fontsize, FontColor color) - : x_(pos.x), y_(pos.y), string_(std::move(text)), fontsize_(fontsize), color_(color), initialColor_(color), - clickedParam(0) -{} + : pos_(pos), string_(std::move(text)), fontsize_(fontsize), color_(color), initialColor_(color), clickedParam(0) +{ + size_ = Extent(getTextWidth(string_, fontsize_), getLineHeight(fontsize_)); +} void CFont::setPos(Position pos) { - if(pos != Position(x_, y_)) - { - x_ = pos.x; - y_ = pos.y; - Surf_Font.reset(); - } + pos_ = pos; } void CFont::setFontsize(FontSize fontsize) { - if(fontsize != fontsize_) - { - fontsize_ = fontsize; - Surf_Font.reset(); - } + fontsize_ = fontsize; + size_ = Extent(getTextWidth(string_, fontsize_), getLineHeight(fontsize_)); } void CFont::setColor(FontColor color) { - if(color != color_) - Surf_Font.reset(); initialColor_ = color_ = color; } @@ -43,8 +36,8 @@ void CFont::setText(std::string text) { if(text == string_) return; - Surf_Font.reset(); this->string_ = std::move(text); + size_ = Extent(getTextWidth(string_, fontsize_), getLineHeight(fontsize_)); } void CFont::setMouseData(SDL_MouseButtonEvent button) @@ -54,10 +47,10 @@ void CFont::setMouseData(SDL_MouseButtonEvent button) // left button is pressed if(button.button == SDL_BUTTON_LEFT) { - if((button.x >= x_) && (button.x < x_ + w) && (button.y >= y_) && (button.y < y_ + h)) + if(IsPointInRect(button.x, button.y, Rect(pos_, size_))) { // if mouse button is pressed ON the text - if((button.state == SDL_PRESSED) && getColor() == initialColor_) + if(button.state == SDL_PRESSED && getColor() == initialColor_) { const auto tmpInitialColor = initialColor_; setColor(FontColor::Orange); @@ -71,19 +64,10 @@ void CFont::setMouseData(SDL_MouseButtonEvent button) } } -SDL_Surface* CFont::getSurface() -{ - if(!Surf_Font) - writeText(); - return Surf_Font.get(); -} - namespace { unsigned getIndexForChar(uint8_t c) { // subtract 32 shows that we start by spacebar as 'zero-position' - // subtract another value after subtracting 32 means the skipped chiffres in ansi in compare to our enumeration - // (cause we dont have all ansi-values as pictures) if(c >= 32 && c <= 90) return c - 32; /* \ */ @@ -197,97 +181,47 @@ unsigned getIndexForChar(uint8_t c, FontSize fontsize, FontColor color) } unsigned getCharWidth(uint8_t c, FontSize fontsize, FontColor color) -{ // NOTE: there is a bug in the ansi 236 'ì' at fontsize 9, the width is 39, this is not useable, we will use the width - // of ansi 237 - // 'í' instead +{ + // NOTE: there is a bug in the ansi 236 'ì' at fontsize 9, the width is 39, this is not useable, we will use the + // width of ansi 237 'í' instead if(fontsize == FontSize::Small && c == 236) c = 109; return global::bmpArray[getIndexForChar(c, fontsize, color)].w; } } // namespace -void CFont::writeText() +void CFont::draw(Position parentOrigin) const { if(string_.empty()) return; - // data for counting pixels to create the surface - unsigned pixel_ctr_w = 0; - unsigned pixel_ctr_w_tmp = 0; - const unsigned lineHeight = getLineHeight(fontsize_); - auto pixel_ctr_h = static_cast(fontsize_); - bool pixel_count_loop = true; - // counter for the drawed pixels (cause we dont want to draw outside of the surface) - Position pos{0, 0}; - - // now lets draw the chiffres - auto chiffre = string_.begin(); - while(chiffre != string_.end()) - { - const auto charW = getCharWidth(*chiffre, fontsize_, color_); - // if we only count pixels in this round - if(pixel_count_loop) - { - if(*chiffre == '\n') - { - pixel_ctr_h += lineHeight; - if(pixel_ctr_w_tmp > pixel_ctr_w) - pixel_ctr_w = pixel_ctr_w_tmp; - pixel_ctr_w_tmp = 0; - ++chiffre; - } else - { - pixel_ctr_w_tmp += charW; - ++chiffre; - } - - // if this was the last chiffre setup width, create surface and go in normal mode to write text to the - // surface - if(chiffre == string_.end()) - { - if(pixel_ctr_w_tmp > pixel_ctr_w) - pixel_ctr_w = pixel_ctr_w_tmp; - w = pixel_ctr_w; - h = pixel_ctr_h; - Surf_Font = makeRGBSurface(w, h); - if(!Surf_Font) - return; - SDL_SetColorKey(Surf_Font.get(), SDL_TRUE, SDL_MapRGB(Surf_Font->format, 0, 0, 0)); - chiffre = string_.begin(); - pixel_count_loop = false; - continue; - } else - continue; - } - - // now we have our index and can use global::bmpArray[chiffre_index] to get the picture - - // test for new line - if(*chiffre == '\n') - { - pos.x = 0; - pos.y += lineHeight; - ++chiffre; - continue; - } - - // if right end of surface is reached, stop drawing chiffres - if(Surf_Font->w < static_cast(pos.x + charW)) - break; - - const auto chiffre_index = getIndexForChar(*chiffre, fontsize_, color_); + draw(string_, parentOrigin + pos_, fontsize_, color_, FontAlign::Left); +} - // if lower end of surface is reached, stop drawing chiffres - if(Surf_Font->h < static_cast(pos.y + global::bmpArray[chiffre_index].h)) - break; +void CFont::draw(const std::string& string, Position pos, FontSize fontsize, FontColor color, FontAlign align) +{ + if(string.empty()) + return; - // draw the chiffre to the destination - CSurface::Draw(Surf_Font, global::bmpArray[chiffre_index].surface, pos); + // Measure text width for alignment + unsigned totalWidth = 0; + for(unsigned char c : string) + totalWidth += getCharWidth(c, fontsize, color); - // set position for next chiffre - pos.x += charW; + // Apply alignment + switch(align) + { + case FontAlign::Middle: pos.x -= static_cast(totalWidth / 2); break; + case FontAlign::Right: pos.x -= static_cast(totalWidth); break; + case FontAlign::Left: break; // no adjustment + } - // go to next chiffre - ++chiffre; + // Draw each character as a textured quad + Position curPos = pos; + for(unsigned char c : string) + { + auto& tex = getBmpTexture(getIndexForChar(c, fontsize, color)); + tex.draw(Rect(curPos, tex.getSize())); + curPos.x += tex.getSize().x; } } @@ -362,3 +296,12 @@ bool CFont::writeText(SDL_Surface* Surf_Dest, const std::string& string, unsigne return true; } + +unsigned CFont::getTextWidth(const std::string& string, FontSize fontsize) +{ + unsigned w = 0; + for(unsigned char c : string) + w += getCharWidth(c, fontsize, FontColor::Yellow); // width is same for all colors + + return w; +} diff --git a/CIO/CFont.h b/CIO/CFont.h index e73e480..2448235 100644 --- a/CIO/CFont.h +++ b/CIO/CFont.h @@ -16,28 +16,21 @@ class CFont friend class CDebug; private: - SdlSurface Surf_Font; - int x_; - int y_; - Uint16 w; - Uint16 h; + Position pos_; ///< Position of the text (top-left) + Extent size_; ///< Pixel extent of the rendered text std::string string_; FontSize fontsize_; FontColor color_, initialColor_; std::function callback; int clickedParam; - void writeText(); - public: CFont(std::string text, Position pos = {0, 0}, FontSize fontsize = FontSize::Small, FontColor color = FontColor::Yellow); // Access - int getX() const { return x_; }; - int getY() const { return y_; }; + Position getPos() const { return pos_; } + Extent getSize() const { return size_; } void setPos(Position pos); - unsigned getW() const { return w; }; - unsigned getH() const { return static_cast(fontsize_); }; void setFontsize(FontSize fontsize); void setColor(FontColor color); FontColor getColor() const { return color_; } @@ -53,10 +46,16 @@ class CFont clickedParam = 0; } void setMouseData(SDL_MouseButtonEvent button); - SDL_Surface* getSurface(); - // Methods - // fontsize can be 9, 11 or 14 (otherwise it will be set to 9) ---- '\n' is possible - // this function can be used as CFont::writeText to write text directly to a surface without creating an object + + /// Draw this font's text at the given absolute position + void draw(Position parentOrigin) const; + + /// Draw text directly + /// @param pos Absolute position (top-left of the text, adjusted for alignment). + static void draw(const std::string& string, Position pos, FontSize fontsize = FontSize::Small, + FontColor color = FontColor::Yellow, FontAlign align = FontAlign::Left); + + /// Static helper: draw text onto an SDL surface (for terrain / minimap rendering). static bool writeText(SDL_Surface* Surf_Dest, const std::string& string, unsigned x = 0, unsigned y = 0, FontSize fontsize = FontSize::Small, FontColor color = FontColor::Yellow, FontAlign align = FontAlign::Left); @@ -66,4 +65,7 @@ class CFont { return writeText(Surf_Dest.get(), string, pos.x, pos.y, fontsize, color, align); } + + /// Compute the pixel width of a string without drawing it. + static unsigned getTextWidth(const std::string& string, FontSize fontsize); }; diff --git a/CIO/CMenu.cpp b/CIO/CMenu.cpp index af68a28..fe1d3fb 100644 --- a/CIO/CMenu.cpp +++ b/CIO/CMenu.cpp @@ -10,35 +10,11 @@ CMenu::CMenu(int pic_background) : CControlContainer(pic_background) {} -bool CMenu::render() +void CMenu::draw(Position /*parentOrigin*/) { - if(getBackground() < 0) - return false; + // Draw full-screen background texture + const auto res = global::s2->getRes(); + getBmpTexture(getBackground(), true).draw(Rect(0, 0, res.x, res.y)); - 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, true); - if(!surface) - return false; - } - - renderElements(); - return true; + drawChildren(Position(0, 0)); } diff --git a/CIO/CMenu.h b/CIO/CMenu.h index 59d8400..0b4fedb 100644 --- a/CIO/CMenu.h +++ b/CIO/CMenu.h @@ -6,21 +6,17 @@ #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; public: CMenu(int pic_background); - void setActive() { active = true; }; - void setInactive() { active = false; }; - bool isActive() const { return active; }; + void setActive() { active = true; } + void setInactive() { active = false; } + bool isActive() const { return active; } + + void draw(Position parentOrigin) override; }; diff --git a/CIO/CMinimapWindow.cpp b/CIO/CMinimapWindow.cpp new file mode 100644 index 0000000..6b7f4e5 --- /dev/null +++ b/CIO/CMinimapWindow.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2026 - 2026 Settlers Freaks +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "CMinimapWindow.h" +#include "../CGame.h" +#include "../CMap.h" +#include "../Texture.h" +#include "../globals.h" +#include "CFont.h" + +void CMinimapWindow::draw(Position /*parentOrigin*/) +{ + // Draw window chrome (frame, title, close button, background, child elements) + CWindow::draw(pos_); + + // Compute content area (inside the frames) + const auto& b = getBorderSizes(); + const Position contentPos = pos_ + Position(b.left, b.top); + const auto contentSize = getSize() - getBorderSize(); + if(static_cast(contentSize.x) <= 0 || static_cast(contentSize.y) <= 0) + return; + + auto* map = global::s2->getMapObj(); + if(!map) + return; + + // Fill pixel buffer with minimap terrain + int scale = 1; + map->drawMinimap(pixels_, static_cast(contentSize.x), static_cast(contentSize.y), scale); + + // Upload to texture and draw + if(!minimapTex_.isValid() || minimapTex_.getSize() != contentSize) + minimapTex_.createEmpty(contentSize); + minimapTex_.upload(pixels_.data()); + minimapTex_.draw(Rect(contentPos, contentSize)); + + // Draw player flags and numbers on top + for(int i = 0; i < MAXPLAYERS; i++) + { + const auto hqX = map->getPlayerHQx()[i]; + const auto hqY = map->getPlayerHQy()[i]; + if(hqX == 0xFFFF || hqY == 0xFFFF) + continue; + + const int flagIdx = FLAG_BLUE_DARK + i % 7; + const Position hqPos = Position(hqX, hqY) / scale; + const Position flagOffset(global::bmpArray[flagIdx].nx, global::bmpArray[flagIdx].ny); + getBmpTexture(flagIdx).draw(contentPos + hqPos - flagOffset); + + // Player number + CFont::draw(std::to_string(i + 1), contentPos + hqPos, FontSize::Small, FontColor::MintGreen); + } + + // Draw the position arrow + { + const int arrowIdx = MAPPIC_ARROWCROSS_ORANGE; + const auto& dispRect = map->getDisplayRect(); + const Position arrowCenter = dispRect.getOrigin() + dispRect.getSize() / 2u; + const Position arrowPos = contentPos + arrowCenter / Position(triangleWidth, triangleHeight) / scale + - Position(global::bmpArray[arrowIdx].nx, global::bmpArray[arrowIdx].ny); + getBmpTexture(arrowIdx).draw(arrowPos); + } +} diff --git a/CIO/CMinimapWindow.h b/CIO/CMinimapWindow.h new file mode 100644 index 0000000..715442b --- /dev/null +++ b/CIO/CMinimapWindow.h @@ -0,0 +1,20 @@ +// Copyright (C) 2026 - 2026 Settlers Freaks +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "../Texture.h" +#include "CWindow.h" +#include + +class CMinimapWindow final : public CWindow +{ + std::vector pixels_; ///< Pixel buffer for minimap terrain + Texture minimapTex_; + + void draw(Position parentOrigin) override; + +public: + using CWindow::CWindow; +}; diff --git a/CIO/CPicture.cpp b/CIO/CPicture.cpp index 7130a1c..abe2f41 100644 --- a/CIO/CPicture.cpp +++ b/CIO/CPicture.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "CPicture.h" -#include "../CSurface.h" +#include "../Texture.h" #include "../globals.h" #include "CollisionDetection.h" @@ -22,13 +22,12 @@ CPicture::CPicture(void callback(int), int clickedParam, Position pos, int pictu this->clickedParam = clickedParam; motionEntryParam = -1; motionLeaveParam = -1; - needRender = true; } void CPicture::setMouseData(const SDL_MouseMotionEvent& motion) { // cursor is on the picture - if(IsPointInRect(Position(motion.x, motion.y), Rect(pos_, size_))) + if(IsPointInRect(motion.x, motion.y, Rect(pos_, size_))) { if(motion.state == SDL_RELEASED) { @@ -43,7 +42,6 @@ void CPicture::setMouseData(const SDL_MouseMotionEvent& motion) callback(motionLeaveParam); marked = false; } - needRender = true; } void CPicture::setMouseData(const SDL_MouseButtonEvent& button) @@ -52,7 +50,7 @@ void CPicture::setMouseData(const SDL_MouseButtonEvent& button) if(button.button == SDL_BUTTON_LEFT) { // if mouse button is pressed ON the button, set marked=true - if((button.state == SDL_PRESSED) && IsPointInRect(Position(button.x, button.y), Rect(pos_, size_))) + if(button.state == SDL_PRESSED && IsPointInRect(button.x, button.y, Rect(pos_, size_))) { marked = true; clicked = true; @@ -64,25 +62,9 @@ void CPicture::setMouseData(const SDL_MouseButtonEvent& button) callback(clickedParam); } } - needRender = true; } -bool CPicture::render() +void CPicture::draw(Position parentOrigin) const { - // if we don't need to render, all is up to date, return true - if(!needRender) - return true; - needRender = false; - // if we need a new surface - if(!Surf_Picture) - { - Surf_Picture = makeRGBSurface(size_.x, size_.y); - if(!Surf_Picture) - return false; - SDL_SetColorKey(Surf_Picture.get(), SDL_TRUE, SDL_MapRGB(Surf_Picture->format, 0, 0, 0)); - } - - CSurface::Draw(Surf_Picture, global::bmpArray[picture_].surface); - - return true; + getBmpTexture(picture_).draw(parentOrigin + pos_); } diff --git a/CIO/CPicture.h b/CIO/CPicture.h index 6b648c2..5a88a57 100644 --- a/CIO/CPicture.h +++ b/CIO/CPicture.h @@ -6,15 +6,13 @@ #pragma once #include "Point.h" -#include "SdlSurface.h" +#include "defines.h" class CPicture { friend class CDebug; private: - SdlSurface Surf_Picture; - bool needRender; Position pos_; Extent size_; int picture_; @@ -30,17 +28,13 @@ class CPicture // Access int getX() const { return pos_.x; }; int getY() const { return pos_.y; }; - const Extent& getSize() const { return size_; }; + Position getPos() const { return pos_; } + Extent getSize() const { return size_; }; void setX(int x) { pos_.x = x; }; void setY(int y) { pos_.y = y; }; void setMouseData(const SDL_MouseMotionEvent& motion); void setMouseData(const SDL_MouseButtonEvent& button); - bool render(); - SDL_Surface* getSurface() - { - render(); - return Surf_Picture.get(); - }; + void draw(Position parentOrigin) const; void setMotionParams(int entry, int leave) { motionEntryParam = entry; diff --git a/CIO/CSelectBox.cpp b/CIO/CSelectBox.cpp index 04da9b4..a8cc946 100644 --- a/CIO/CSelectBox.cpp +++ b/CIO/CSelectBox.cpp @@ -4,10 +4,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "CSelectBox.h" -#include "../CSurface.h" +#include "../CGame.h" +#include "../Texture.h" #include "../globals.h" #include "CButton.h" #include "CFont.h" +#include CSelectBox::CSelectBox(Position pos, Extent size, FontSize fontsize, FontColor text_color, int bg_color) : pos_(pos), size_(size), fontsize(fontsize), text_color(text_color) @@ -33,16 +35,6 @@ void CSelectBox::addOption(const std::string& string, std::function c last_text_pos_y += row_height; } -bool CSelectBox::hasRendered() -{ - if(rendered) - { - rendered = false; - return true; - } else - return false; -} - void CSelectBox::setColor(int color) { switch(color) @@ -82,8 +74,6 @@ void CSelectBox::setColor(int color) pic_background = -1; break; } - - needRender = true; } void CSelectBox::setMouseData(SDL_MouseMotionEvent motion) @@ -94,7 +84,6 @@ void CSelectBox::setMouseData(SDL_MouseMotionEvent motion) motion.y -= pos_.y; ScrollUpButton->setMouseData(motion); ScrollDownButton->setMouseData(motion); - needRender = true; } void CSelectBox::setMouseData(SDL_MouseButtonEvent button) @@ -147,11 +136,11 @@ void CSelectBox::setMouseData(SDL_MouseButtonEvent button) if((button.x > pos_.x + static_cast(size_.x) - 20) && (button.y < pos_.y + 20)) { // test if first entry is on the most upper position - if(!Entries.empty() && Entries.front()->getY() < 10) + if(!Entries.empty() && Entries.front()->getPos().y < 10) { for(auto& entry : Entries) { - entry->setPos(Position(entry->getX(), entry->getY() + 10)); + entry->setPos(Position(entry->getPos().x, entry->getPos().y + 10)); } } } @@ -163,11 +152,11 @@ void CSelectBox::setMouseData(SDL_MouseButtonEvent button) && (button.y > pos_.y + static_cast(size_.y) - 20)) { // test if last entry is on the most lower position - if(!Entries.empty() && Entries.back()->getY() > static_cast(size_.y) - 10) + if(!Entries.empty() && Entries.back()->getPos().y > static_cast(size_.y) - 10) { for(auto& entry : Entries) { - entry->setPos(Position(entry->getX(), entry->getY() - 10)); + entry->setPos(Position(entry->getPos().x, entry->getPos().y - 10)); } } } @@ -199,8 +188,6 @@ void CSelectBox::setMouseData(SDL_MouseButtonEvent button) ScrollUpButton->setMouseData(button); ScrollDownButton->setMouseData(button); } - - needRender = true; } void CSelectBox::setSize(Extent size) @@ -208,8 +195,6 @@ void CSelectBox::setSize(Extent size) if(size_ != size) { size_ = size; - Surf_SelectBox.reset(); - needRender = true; // update scroll down button position ScrollDownButton->setY(size_.y - 1 - 20); } @@ -218,84 +203,38 @@ void CSelectBox::setSize(Extent size) void CSelectBox::setPos(Position pos) { pos_ = pos; - needRender = true; } -bool CSelectBox::render() +void CSelectBox::draw(Position parentOrigin) { - // position in the Surface 'Surf_SelectBox' - Position pos{0, 0}; - // width and height of the button color source picture - Extent pic{0, 0}; + const Position absPos = parentOrigin + pos_; + const Rect area(absPos, size_); - // if we don't need to render, all is up to date, return true - if(!needRender) - return true; - needRender = false; - // if we need a new surface - if(!Surf_SelectBox) - { - if((Surf_SelectBox = makeRGBSurface(size_.x, size_.y)) == nullptr) - return false; - } - - // draw the pictures for background and foreground or, if not set, fill with black color + // Draw background if(pic_background >= 0 && pic_foreground >= 0) { - // at first completly fill the background (not the fastest way, but simplier) - if(size_.x <= global::bmpArray[pic_foreground].w) - pic.x = size_.x; - else - pic.x = global::bmpArray[pic_foreground].w; - - if(size_.y <= global::bmpArray[pic_foreground].h) - pic.y = size_.y; - else - pic.y = global::bmpArray[pic_foreground].h; - - while(pos.x + pic.x <= static_cast(Surf_SelectBox->w)) - { - while(pos.y + pic.y <= static_cast(Surf_SelectBox->h)) - { - CSurface::Draw(Surf_SelectBox, global::bmpArray[pic_foreground].surface, pos, Position(0, 0), pic); - pos.y += pic.y; - } - - if(pos.y < Surf_SelectBox->h) - CSurface::Draw(Surf_SelectBox, global::bmpArray[pic_foreground].surface, pos.x, pos.y, 0, 0, pic.x, - static_cast(Surf_SelectBox->h - pos.y)); - - pos.y = 0; - pos.x += pic.x; - } - - if(pos.x < Surf_SelectBox->w) - { - while(pos.y + pic.y <= static_cast(Surf_SelectBox->h)) - { - CSurface::Draw(Surf_SelectBox, global::bmpArray[pic_foreground].surface, pos.x, pos.y, 0, 0, - static_cast(Surf_SelectBox->w - pos.x), pic.y); - pos.y += pic.y; - } - - if(pos.y < Surf_SelectBox->h) - CSurface::Draw(Surf_SelectBox, global::bmpArray[pic_foreground].surface, pos.x, pos.y, 0, 0, - static_cast(Surf_SelectBox->w - pos.x), - static_cast(Surf_SelectBox->h - pos.y)); - } + getBmpTexture(pic_foreground).drawTiled(area); } else - SDL_FillRect(Surf_SelectBox.get(), nullptr, SDL_MapRGB(Surf_SelectBox->format, 0, 0, 0)); - - for(auto& entry : Entries) { - CSurface::Draw(Surf_SelectBox, entry->getSurface(), entry->getX(), entry->getY()); + // Fill with black + drawRect(area, 0xFF000000); } - CSurface::Draw(Surf_SelectBox, ScrollUpButton->getSurface(), static_cast(size_.x) - 1 - 20, 0); - CSurface::Draw(Surf_SelectBox, ScrollDownButton->getSurface(), static_cast(size_.x) - 1 - 20, - static_cast(size_.y) - 1 - 20); + // Clip entries to the select box area + const auto viewH = global::s2->getRes().y; + glEnable(GL_SCISSOR_TEST); + glScissor(area.left, viewH - (area.top + static_cast(size_.y)), static_cast(size_.x), + static_cast(size_.y)); + + // Draw entries + for(const auto& entry : Entries) + { + entry->draw(absPos); + } - rendered = true; + glDisable(GL_SCISSOR_TEST); - return true; + // Draw scroll buttons (on top, within the select box) + ScrollUpButton->draw(absPos); + ScrollDownButton->draw(absPos); } diff --git a/CIO/CSelectBox.h b/CIO/CSelectBox.h index 8a62d13..7b149d5 100644 --- a/CIO/CSelectBox.h +++ b/CIO/CSelectBox.h @@ -5,7 +5,6 @@ #pragma once -#include "SdlSurface.h" #include "defines.h" #include #include @@ -19,7 +18,6 @@ class CSelectBox friend class CDebug; private: - SdlSurface Surf_SelectBox; std::vector> Entries; Position pos_; Extent size_; @@ -30,30 +28,17 @@ class CSelectBox std::unique_ptr ScrollUpButton; std::unique_ptr ScrollDownButton; Uint16 last_text_pos_y = 10; - // we need this to say the window if it needs to render, otherwise no chiffre are shown - bool rendered = false; - bool needRender = true; public: CSelectBox(Position pos, Extent size, FontSize fontsize = FontSize::Large, FontColor text_color = FontColor::Yellow, int bg_color = -1); - const Position& getPos() const { return pos_; } - const Extent& getSize() const { return size_; } - bool hasRendered(); + Position getPos() const { return pos_; } + Extent getSize() const { return size_; } void setMouseData(SDL_MouseButtonEvent button); void setMouseData(SDL_MouseMotionEvent motion); - bool render(); - SdlSurface& getSurface() - { - render(); - return Surf_SelectBox; - } + void draw(Position parentOrigin); void setColor(int color); - void setTextColor(FontColor color) - { - text_color = color; - needRender = true; - } + void setTextColor(FontColor color) { text_color = color; } void addOption(const std::string& string, std::function callback = nullptr, int param = 0); void setSize(Extent size); void setPos(Position pos); diff --git a/CIO/CTextfield.cpp b/CIO/CTextfield.cpp index 81a7c49..365ab56 100644 --- a/CIO/CTextfield.cpp +++ b/CIO/CTextfield.cpp @@ -4,10 +4,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "CTextfield.h" -#include "../CSurface.h" +#include "../Texture.h" #include "../globals.h" #include "CFont.h" #include "CollisionDetection.h" +#include +#include CTextfield::CTextfield(Position pos, Uint16 cols, Uint16 rows, FontSize fontsize, FontColor text_color, int bg_color, bool button_style) @@ -24,30 +26,18 @@ CTextfield::CTextfield(Position pos, Uint16 cols, Uint16 rows, FontSize fontsize // allocate memory for the text: chiffres (cols) + '\n' for each line * rows + blinking chiffre + '\0' text_.resize((this->cols + 1) * this->rows + 2); - needRender = true; - rendered = false; this->button_style = button_style; textObj = std::make_unique("", pos, fontsize, text_color); } -int CTextfield::getX() const +Position CTextfield::getPos() const { - return textObj->getX(); + return textObj->getPos(); } -int CTextfield::getY() const +void CTextfield::setPos(Position pos) { - return textObj->getY(); -} - -bool CTextfield::hasRendered() -{ - if(rendered) - { - rendered = false; - return true; - } else - return false; + textObj->setPos(pos); } void CTextfield::setColor(int color) @@ -89,24 +79,11 @@ void CTextfield::setColor(int color) pic_background = -1; break; } - - needRender = true; } void CTextfield::setTextColor(FontColor color) { textObj->setColor(color); - needRender = true; -} - -void CTextfield::setX(int x) -{ - textObj->setPos(Position(x, getY())); -} - -void CTextfield::setY(int y) -{ - textObj->setPos(Position(getX(), y)); } void CTextfield::setText(const std::string& text) @@ -145,10 +122,9 @@ void CTextfield::setMouseData(SDL_MouseButtonEvent button) // if mouse button is pressed ON the textfield, set active=true if(button.state == SDL_PRESSED) { - active = IsPointInRect(Position(button.x, button.y), Rect(Position(getX(), getY()), size_)); + active = IsPointInRect(button.x, button.y, Rect(getPos(), size_)); } } - needRender = true; } void CTextfield::setKeyboardData(const SDL_KeyboardEvent& key) @@ -239,213 +215,57 @@ void CTextfield::setKeyboardData(const SDL_KeyboardEvent& key) } break; } - needRender = true; } } -bool CTextfield::render() +void CTextfield::draw(Position parentOrigin) { - // position in the Surface 'Surf_Text' - Position pos{0, 0}; - // width and height of the picture tile - Extent pic{0, 0}; - // we save the time to let a chiffre blink - static Uint32 currentTime; - static Uint32 lastTime = SDL_GetTicks(); - static bool blinking_chiffre = false; - // if the textfield is active, we need to render to show the blinking chiffre + const Position absPos = parentOrigin + getPos(); + const Rect area(absPos, size_); + + // Update cursor blink state + static Uint32 lastTime = 0; // Shared timer is OK here if(active) { - currentTime = SDL_GetTicks(); + const Uint32 currentTime = SDL_GetTicks(); + if(lastTime == 0) + lastTime = currentTime; if(currentTime - lastTime > 500) { lastTime = currentTime; - blinking_chiffre = !blinking_chiffre; } - needRender = true; - } - - // if we don't need to render, all is up to date, return true - if(!needRender) - return true; - needRender = false; - // if we need a new surface - if(!Surf_Text) + } else { - Surf_Text = makeRGBSurface(size_.x, size_.y); - if(!Surf_Text) - return false; + blinking_chiffre = false; } - // draw the pictures for background and foreground or, if not set, fill with black color + // Draw the background / foreground if(pic_background >= 0 && pic_foreground >= 0) { - // in case the textfield should look like a button, we do it, otherwise we use pic_foreground for the background - const int bmpIdx = button_style ? pic_background : pic_foreground; - - // at first completly fill the background (not the fastest way, but simplier) - if(size_.x <= global::bmpArray[bmpIdx].w) - pic.x = size_.x; - else - pic.x = global::bmpArray[bmpIdx].w; - - if(size_.y <= global::bmpArray[bmpIdx].h) - pic.y = size_.y; - else - pic.y = global::bmpArray[bmpIdx].h; - - while(pos.x + pic.x <= static_cast(Surf_Text->w)) - { - while(pos.y + pic.y <= static_cast(Surf_Text->h)) - { - CSurface::Draw(Surf_Text, global::bmpArray[bmpIdx].surface, pos, Position(0, 0), pic); - pos.y += pic.y; - } - - if(pos.y < Surf_Text->h) - CSurface::Draw(Surf_Text, global::bmpArray[bmpIdx].surface, pos.x, pos.y, 0, 0, pic.x, - static_cast(Surf_Text->h - pos.y)); - - pos.y = 0; - pos.x += pic.x; - } - - if(pos.x < Surf_Text->w) - { - while(pos.y + pic.y <= static_cast(Surf_Text->h)) - { - CSurface::Draw(Surf_Text, global::bmpArray[bmpIdx].surface, pos.x, pos.y, 0, 0, - static_cast(Surf_Text->w - pos.x), pic.y); - pos.y += pic.y; - } - - if(pos.y < Surf_Text->h) - CSurface::Draw(Surf_Text, global::bmpArray[bmpIdx].surface, pos.x, pos.y, 0, 0, - static_cast(Surf_Text->w - pos.x), - static_cast(Surf_Text->h - pos.y)); - } - - // if not button_style, we are finished, otherwise continue drawing if(button_style) - { - // draw partial black frame - if(active) - { - // black frame is left and up - // draw vertical line - for(unsigned y = 0; y < size_.y; y++) - CSurface::DrawPixel_RGB(Surf_Text, Position(0, y), 0, 0, 0); - - // draw vertical line - for(unsigned y = 0; y < size_.y - 1; y++) - CSurface::DrawPixel_RGB(Surf_Text, Position(1, y), 0, 0, 0); - - // draw horizontal line - for(unsigned x = 0; x < size_.x; x++) - CSurface::DrawPixel_RGB(Surf_Text, Position(x, 0), 0, 0, 0); - - // draw horizontal line - for(unsigned x = 0; x < size_.x - 1; x++) - CSurface::DrawPixel_RGB(Surf_Text, Position(x, 1), 0, 0, 0); - } else - { - // black frame is right and down - // draw vertical line - for(unsigned y = 0; y < size_.y; y++) - CSurface::DrawPixel_RGB(Surf_Text, Position(size_.x - 1, y), 0, 0, 0); - - // draw vertical line - for(unsigned y = 1; y < size_.y; y++) - CSurface::DrawPixel_RGB(Surf_Text, Position(size_.x - 2, y), 0, 0, 0); - - // draw horizontal line - for(unsigned x = 0; x < size_.x; x++) - CSurface::DrawPixel_RGB(Surf_Text, Position(x, size_.y - 1), 0, 0, 0); - - // draw horizontal line - for(unsigned x = 1; x < size_.x; x++) - CSurface::DrawPixel_RGB(Surf_Text, Position(x, size_.y - 2), 0, 0, 0); - } - - // draw the foreground --> at first the color (marked or unmarked) and then the picture or text - if(size_.x <= global::bmpArray[pic_foreground].w) - pic.x = size_.x; - else - pic.x = global::bmpArray[pic_foreground].w; - - if(size_.y <= global::bmpArray[pic_foreground].h) - pic.y = size_.y; - else - pic.y = global::bmpArray[pic_foreground].h; - - // beware overdrawing the left and upper frame - pos.x = 2; - pos.y = 2; - - // '-2' follows a few times, this means: beware overdrawing the right and lower frame - while(pos.x + pic.x <= static_cast(Surf_Text->w - 2)) - { - while(pos.y + pic.y <= static_cast(Surf_Text->h - 2)) - { - CSurface::Draw(Surf_Text, global::bmpArray[pic_foreground].surface, pos.x, pos.y, 0, 0, pic.x, - pic.y); - pos.y += pic.y; - } - - if(pos.y + 2 < Surf_Text->h) - CSurface::Draw(Surf_Text, global::bmpArray[pic_foreground].surface, pos.x, pos.y, 0, 0, pic.x, - static_cast(Surf_Text->h - 2 - pos.y)); - - pos.y = 2; - pos.x += pic.x; - } - - if(pos.x + 2 < Surf_Text->w) - { - while(pos.y + pic.y <= static_cast(Surf_Text->h - 2)) - { - CSurface::Draw(Surf_Text, global::bmpArray[pic_foreground].surface, pos.x, pos.y, 0, 0, - static_cast(Surf_Text->w - 2 - pos.x), pic.y); - pos.y += pic.y; - } - - if(pos.y + 2 < Surf_Text->h) - CSurface::Draw(Surf_Text, global::bmpArray[pic_foreground].surface, pos.x, pos.y, 0, 0, - static_cast(Surf_Text->w - 2 - pos.x), - static_cast(Surf_Text->h - 2 - pos.y)); - } - } + drawButtonBox(area, active, pic_background, pic_foreground); + else + getBmpTexture(pic_foreground).drawTiled(area); } else - SDL_FillRect(Surf_Text.get(), nullptr, SDL_MapRGB(Surf_Text->format, 0, 0, 0)); - - char* txtPtr = text_.data(); - - // go to '\0' - while(*txtPtr != '\0') - txtPtr++; - // add blinking chiffre if necessary - if(blinking_chiffre && active) { - *txtPtr = '>'; - txtPtr++; - *txtPtr = '\0'; + // Fill with black + drawRect(area, 0xFF000000); } - // write text - textObj->setText(text_.data()); + // Prepare text with cursor + std::string displayText = getText(); - // delete blinking chiffre (otherwise it could be written between user input chiffres) + // Add blinking cursor if active if(blinking_chiffre && active) { - txtPtr--; - *txtPtr = '\0'; + displayText += '>'; } - // blit text surface - CSurface::Draw(Surf_Text, textObj->getSurface(), 2, 2); - - rendered = true; - - return true; + // Draw the text + if(!displayText.empty()) + { + textObj->setText(displayText); + textObj->draw(Position(parentOrigin.x + 2, parentOrigin.y + 4)); + } } diff --git a/CIO/CTextfield.h b/CIO/CTextfield.h index 73761b1..1b10415 100644 --- a/CIO/CTextfield.h +++ b/CIO/CTextfield.h @@ -7,6 +7,7 @@ #include "defines.h" #include +#include #include class CFont; @@ -16,9 +17,7 @@ class CTextfield friend class CDebug; private: - SdlSurface Surf_Text; std::unique_ptr textObj; - bool needRender; Extent size_; Uint16 cols; Uint16 rows; @@ -27,36 +26,28 @@ class CTextfield std::vector text_; // if active, keyboard data will be delivered and the cursor is blinking bool active; - // we need this to say the window if it needs to render, otherwise no blinking cursor and no chiffres are shown - bool rendered; // if true, the textfield looks like a button bool button_style; + // Cursor blink state + bool blinking_chiffre = false; public: // Constructor - Destructor CTextfield(Position pos = {0, 0}, Uint16 cols = 10, Uint16 rows = 1, FontSize fontsize = FontSize::Large, FontColor text_color = FontColor::Yellow, int bg_color = -1, bool button_style = false); // Access - int getX() const; - int getY() const; - const Extent& getSize() const { return size_; }; + Position getPos() const; + void setPos(Position pos); + Extent getSize() const { return size_; }; int getCols() const { return cols; } int getRows() const { return rows; } - void setX(int x); - void setY(int y); void setText(const std::string& text); void setActive() { active = true; } void setInactive() { active = false; } bool isActive() const { return active; } - bool hasRendered(); void setMouseData(SDL_MouseButtonEvent button); void setKeyboardData(const SDL_KeyboardEvent& key); - bool render(); - SDL_Surface* getSurface() - { - render(); - return Surf_Text.get(); - } + void draw(Position parentOrigin); void setColor(int color); void setTextColor(FontColor color); std::string getText() const { return text_.data(); } diff --git a/CIO/CWindow.cpp b/CIO/CWindow.cpp index 66146a5..7696e9d 100644 --- a/CIO/CWindow.cpp +++ b/CIO/CWindow.cpp @@ -5,7 +5,7 @@ #include "CWindow.h" #include "../CGame.h" -#include "../CSurface.h" +#include "../Texture.h" #include "../globals.h" #include "CButton.h" #include "CFont.h" @@ -14,25 +14,16 @@ #include "CTextfield.h" #include "CollisionDetection.h" #include "helpers/containerUtils.h" +#include #include CWindow::CWindow(void callback(int), int callbackQuitMessage, Position pos, Extent size, const char* title, int color, Uint8 flags) - : CControlContainer(color, {global::bmpArray[WINDOW_LEFT_FRAME].w, global::bmpArray[WINDOW_UPPER_FRAME].h}, - {global::bmpArray[WINDOW_RIGHT_FRAME].w, global::bmpArray[WINDOW_LOWER_FRAME].h}), - x_(pos.x), y_(pos.y), w_(size.x), h_(size.y), title(title), callback_(callback), - callbackQuitMessage(callbackQuitMessage) + : CControlContainer(color, {global::bmpArray[WINDOW_LEFT_FRAME].w, global::bmpArray[WINDOW_UPPER_FRAME].h, + global::bmpArray[WINDOW_RIGHT_FRAME].w, global::bmpArray[WINDOW_LOWER_FRAME].h}), + pos_(pos), size_(size), title(title), callback_(callback), callbackQuitMessage(callbackQuitMessage) { assert(callback); - // ensure window is big enough to take all basic pictures needed - // if ( w < (global::bmpArray[WINDOW_LEFT_UPPER_CORNER].w + global::bmpArray[WINDOW_UPPER_FRAME].w + - // global::bmpArray[WINDOW_RIGHT_UPPER_CORNER].w) ) - // this->w = global::bmpArray[WINDOW_LEFT_UPPER_CORNER].w + global::bmpArray[WINDOW_UPPER_FRAME].w + - // global::bmpArray[WINDOW_RIGHT_UPPER_CORNER].w; - // else - // if ( h < (global::bmpArray[WINDOW_UPPER_FRAME].h + global::bmpArray[WINDOW_CORNER_RECTANGLE].h) ) - // this->h = global::bmpArray[WINDOW_UPPER_FRAME].h + global::bmpArray[WINDOW_CORNER_RECTANGLE].h; - // else canMove = (flags & WINDOW_MOVE) != 0; canClose = (flags & WINDOW_CLOSE) != 0; canMinimize = (flags & WINDOW_MINIMIZE) != 0; @@ -42,7 +33,7 @@ CWindow::CWindow(void callback(int), int callbackQuitMessage, Position pos, Exte static Position makePos(WindowPos pos, Extent size) { if(pos == WindowPos::Center) - return Position(global::s2->getDisplaySurface()->w, global::s2->getDisplaySurface()->h) / 2 - size / 2; + return (global::s2->getRes() - size) / 2; else return {}; } @@ -55,7 +46,6 @@ CWindow::CWindow(void callback(int), int callbackQuitMessage, WindowPos pos, Ext void CWindow::setTitle(const char* title) { this->title = title; - needRender = true; } void CWindow::setColor(int color) @@ -71,10 +61,11 @@ bool CWindow::hasActiveInputElement() void CWindow::setMouseData(SDL_MouseMotionEvent motion) { // cursor is on the title frame (+/-2 and +/-4 are only for a good optic) - const Position titleFrameLT = Position(x_, y_) + Position(global::bmpArray[WINDOW_LEFT_UPPER_CORNER].w + 2, 4); - const Position titleFrameRB = Position(x_ + w_ - global::bmpArray[WINDOW_RIGHT_UPPER_CORNER].w - 2, - y_ + global::bmpArray[WINDOW_UPPER_FRAME].h - 4); - if(IsPointInRect(Position(motion.x, motion.y), Rect(titleFrameLT, Extent(titleFrameRB - titleFrameLT)))) + const Position titleFrameLT = pos_ + Position(global::bmpArray[WINDOW_LEFT_UPPER_CORNER].w + 2, 4); + const Position titleFrameRB = + Position(pos_.x + static_cast(size_.x) - global::bmpArray[WINDOW_RIGHT_UPPER_CORNER].w - 2, + pos_.y + global::bmpArray[WINDOW_UPPER_FRAME].h - 4); + if(IsPointInRect(motion.x, motion.y, Rect(titleFrameLT, Extent(titleFrameRB - titleFrameLT)))) { // left button was pressed while moving if(clicked) @@ -86,40 +77,38 @@ void CWindow::setMouseData(SDL_MouseMotionEvent motion) moving = false; if(moving && canMove) { - x_ += motion.xrel; - y_ += motion.yrel; - // make sure to not move the window outside the display surface - if(x_ < 0) - x_ = 0; - if(x_ + w_ >= global::s2->getDisplaySurface()->w) //-V807 - x_ = global::s2->getDisplaySurface()->w - w_ - 1; - if(y_ < 0) - y_ = 0; - if(y_ + h_ >= global::s2->getDisplaySurface()->h) - y_ = global::s2->getDisplaySurface()->h - h_ - 1; + pos_ += Position(motion.xrel, motion.yrel); + // Clamp window position so it stays on screen + const auto res = global::s2->getRes(); + const auto maxPos = res - elMin(size_, res); + pos_ = elMin(elMax(pos_, Position(0, 0)), Position(maxPos)); } // check whats happen to the close button if(canClose) { // cursor is on the button (+/-2 is only for the optic) - canClose_marked = (motion.x >= x_ + 2) && (motion.x < x_ + global::bmpArray[WINDOW_BUTTON_CLOSE].w - 2) - && (motion.y >= y_ + 2) && (motion.y < y_ + global::bmpArray[WINDOW_BUTTON_CLOSE].h - 2); + canClose_marked = (motion.x >= pos_.x + 2) && (motion.x < pos_.x + global::bmpArray[WINDOW_BUTTON_CLOSE].w - 2) + && (motion.y >= pos_.y + 2) + && (motion.y < pos_.y + global::bmpArray[WINDOW_BUTTON_CLOSE].h - 2); } // check whats happen to the minimize button if(canMinimize) { // cursor is on the button (+/-2 is only for the optic) - canMinimize_marked = (motion.x >= x_ + w_ - global::bmpArray[WINDOW_BUTTON_MINIMIZE].w + 2) - && (motion.x < x_ + w_ - 2) && (motion.y >= y_ + 2) - && (motion.y < y_ + global::bmpArray[WINDOW_BUTTON_MINIMIZE].h - 2); + canMinimize_marked = + (motion.x >= pos_.x + static_cast(size_.x) - global::bmpArray[WINDOW_BUTTON_MINIMIZE].w + 2) + && (motion.x < pos_.x + static_cast(size_.x) - 2) && (motion.y >= pos_.y + 2) + && (motion.y < pos_.y + global::bmpArray[WINDOW_BUTTON_MINIMIZE].h - 2); } // check whats happen to the resize button if(canResize) { // cursor is on the button (+/-2 is only for the optic) - if((motion.x >= x_ + w_ - global::bmpArray[WINDOW_BUTTON_RESIZE].w + 2) && (motion.x < x_ + w_ - 2) - && (motion.y >= y_ + h_ - global::bmpArray[WINDOW_BUTTON_RESIZE].h + 2) && (motion.y < y_ + h_ - 2)) + if((motion.x >= pos_.x + static_cast(size_.x) - global::bmpArray[WINDOW_BUTTON_RESIZE].w + 2) + && (motion.x < pos_.x + static_cast(size_.x) - 2) + && (motion.y >= pos_.y + static_cast(size_.y) - global::bmpArray[WINDOW_BUTTON_RESIZE].h + 2) + && (motion.y < pos_.y + static_cast(size_.y) - 2)) { // left button was pressed while moving if(SDL_GetMouseState(nullptr, nullptr) & SDL_BUTTON(SDL_BUTTON_LEFT)) @@ -135,32 +124,25 @@ void CWindow::setMouseData(SDL_MouseMotionEvent motion) // only resize if not minimized if(!minimized) { - w_ += motion.xrel; - h_ += motion.yrel; - - // MISSING: we have to test if window size is under minimum - - // the window has resized, so we need a new surface - surface.reset(); - - // notify the callback that the window has been resized + size_ = Extent(static_cast(size_.x) + motion.xrel, static_cast(size_.y) + motion.yrel); + const auto res = global::s2->getRes(); + const auto maxSize = Extent(elMax(Position(res) - pos_, Position(1, 1))); + size_ = elMin(elMax(size_, Extent::all(1u)), maxSize); callback_(WINDOW_RESIZED_CALL); } } } // deliver mouse data to the content objects of the window (if mouse cursor is inside the window) - if(IsPointInRect(Position(motion.x, motion.y), Rect(getPos(), getSize()))) + if(IsPointInRect(motion.x, motion.y, Rect(getPos(), getSize()))) { // IMPORTANT: we use the left upper corner of the window as (x,y)=(0,0), so we have to manipulate // the motion-structure before give it to buttons, pictures....: x_absolute - x_window, y_absolute - // y_window - motion.x -= x_; - motion.y -= y_; + motion.x -= pos_.x; + motion.y -= pos_.y; CControlContainer::setMouseData(motion); } - - needRender = true; } void CWindow::setMouseData(SDL_MouseButtonEvent button) @@ -177,22 +159,22 @@ void CWindow::setMouseData(SDL_MouseButtonEvent button) // will not happen) static int maximized_h = global::bmpArray[WINDOW_UPPER_FRAME].h + global::bmpArray[WINDOW_CORNER_RECTANGLE].h; if(!minimized) - maximized_h = h_; + maximized_h = static_cast(size_.y); // left button is pressed if(button.button == SDL_BUTTON_LEFT) { // cursor is on the title frame (+/-2 and +/-4 are only for a good optic) - if((button.x >= x_ + global::bmpArray[WINDOW_LEFT_UPPER_CORNER].w + 2) - && (button.x < x_ + w_ - global::bmpArray[WINDOW_RIGHT_UPPER_CORNER].w - 2) && (button.y >= y_ + 4) - && (button.y < y_ + +global::bmpArray[WINDOW_UPPER_FRAME].h - 4)) + if((button.x >= pos_.x + global::bmpArray[WINDOW_LEFT_UPPER_CORNER].w + 2) + && (button.x < pos_.x + static_cast(size_.x) - global::bmpArray[WINDOW_RIGHT_UPPER_CORNER].w - 2) + && (button.y >= pos_.y + 4) + && (button.y < pos_.y + static_cast(global::bmpArray[WINDOW_UPPER_FRAME].h) - 4)) { marked = true; clicked = true; } // pressed inside the window - if((button.state == SDL_PRESSED) && (button.x >= x_) && (button.x <= x_ + w_) && (button.y >= y_) - && (button.y <= y_ + h_)) + if(button.state == SDL_PRESSED && IsPointInRect(button.x, button.y, Rect(pos_, size_))) marked = true; // else pressed outside of the window else if(button.state == SDL_PRESSED) @@ -225,15 +207,11 @@ void CWindow::setMouseData(SDL_MouseButtonEvent button) { if(minimized) // maximize now { - h_ = maximized_h; - // the window has resized, so we need a new surface - surface.reset(); + size_.y = static_cast(maximized_h); minimized = false; } else // minimize now { - h_ = global::bmpArray[WINDOW_UPPER_FRAME].h + global::bmpArray[WINDOW_CORNER_RECTANGLE].h; - // the window has resized, so we need a new surface - surface.reset(); + size_.y = global::bmpArray[WINDOW_UPPER_FRAME].h + global::bmpArray[WINDOW_CORNER_RECTANGLE].h; minimized = true; } } @@ -247,97 +225,45 @@ void CWindow::setMouseData(SDL_MouseButtonEvent button) } // deliver mouse data to the content objects of the window (if mouse cursor is inside the window) - if(IsPointInRect(Position(button.x, button.y), Rect(getPos(), getSize()))) + if(IsPointInRect(button.x, button.y, Rect(getPos(), getSize()))) { // IMPORTANT: we use the left upper corner of the window as (x,y)=(0,0), so we have to manipulate // the motion-structure before give it to buttons, pictures....: x_absolute - x_window, y_absolute - // y_window - button.x -= x_; - button.y -= y_; + button.x -= pos_.x; + button.y -= pos_.y; CControlContainer::setMouseData(button); } // at least call the callback callback_(WINDOW_CLICKED_CALL); - - needRender = true; } -bool CWindow::render() +void CWindow::draw(Position /*parentOrigin*/) { - // position in the Surface 'surface' - Position pos = {0, 0}; - // width and height of the window background color source picture - Uint16 pic_w = 0; - Uint16 pic_h = 0; - // upper frame (can be marked, clicked or normal) - int upperframe; - // close button (can be marked, clicked or normal) - int closebutton = WINDOW_BUTTON_CLOSE; - // minimize button (can be marked, clicked or normal) - int minimizebutton = WINDOW_BUTTON_MINIMIZE; - // resize button (can be marked, clicked or normal) - int resizebutton = WINDOW_BUTTON_RESIZE; - - // test if a textfield has changed - needRender |= helpers::contains_if(getTextFields(), [](const auto& textfield) { return textfield->hasRendered(); }); - - // if we don't need to render, all is up to date, return true - if(!needRender) - return true; - needRender = false; - // if we need a new surface - if(!surface) - { - if(!(surface = makeRGBSurface(w_, h_))) - return false; - } - - // at first completly fill the background (not the fastest way, but simpler) + // 1. Background fill (tiled) if(getBackground() != WINDOW_NOTHING) - { - pic_w = std::min(w_, global::bmpArray[getBackground()].w); - pic_h = std::min(h_, global::bmpArray[getBackground()].h); - - while(pos.x + pic_w <= surface->w) - { - while(pos.y + pic_h <= surface->h) - { - CSurface::Draw(surface.get(), global::bmpArray[getBackground()].surface, pos.x, pos.y, 0, 0, pic_w, - pic_h); - pos.y += pic_h; - } - - if(surface->h - pos.y > 0) - CSurface::Draw(surface.get(), global::bmpArray[getBackground()].surface, pos.x, pos.y, 0, 0, pic_w, - surface->h - pos.y); - - pos.y = 0; - pos.x += pic_w; - } - - if(surface->w - pos.x > 0) - { - while(pos.y + pic_h <= surface->h) - { - CSurface::Draw(surface.get(), global::bmpArray[getBackground()].surface, pos.x, pos.y, 0, 0, - surface->w - pos.x, pic_h); - pos.y += pic_h; - } + getBmpTexture(getBackground()).drawTiled(getRect()); - if(surface->h - pos.y > 0) - CSurface::Draw(surface.get(), global::bmpArray[getBackground()].surface, pos.x, pos.y, 0, 0, - surface->w - pos.x, surface->h - pos.y); - } - } - - // if not minimized, draw the content now (this stands here to prevent the frames and corners from being overdrawn) + // 2. Content (if not minimized) — clipped to the area inside frames if(!minimized) { - renderElements(); + const auto viewH = global::s2->getRes().y; + const auto& b = getBorderSizes(); + const auto contentOrigin = pos_ + Position(b.left, b.top); + const auto contentSize = getSize() - getBorderSize(); + if(static_cast(contentSize.x) > 0 && static_cast(contentSize.y) > 0) + { + glEnable(GL_SCISSOR_TEST); + glScissor(contentOrigin.x, viewH - (contentOrigin.y + static_cast(contentSize.y)), + static_cast(contentSize.x), static_cast(contentSize.y)); + drawChildren(pos_); + glDisable(GL_SCISSOR_TEST); + } } - // now draw the upper frame to the top + // 3. Upper frame + int upperframe; if(clicked) upperframe = WINDOW_UPPER_FRAME_CLICKED; else if(marked) @@ -345,110 +271,94 @@ bool CWindow::render() else upperframe = WINDOW_UPPER_FRAME; - pic_w = std::min(w_, global::bmpArray[upperframe].w); - - pos.x = 0; - pos.y = 0; - while(pos.x + pic_w <= surface->w) + // Draw upper frame tile across the top of the window { - CSurface::Draw(surface, global::bmpArray[upperframe].surface, pos); - pos.x += pic_w; + const Rect upperFrameRect(pos_, Extent(size_.x, getBmpTexture(upperframe).getSize().y)); + getBmpTexture(upperframe).drawTiled(upperFrameRect); } - if(surface->w - pos.x > 0) - CSurface::Draw(surface.get(), global::bmpArray[upperframe].surface, pos.x, pos.y, 0, 0, surface->w - pos.x, - pic_h); - // write text in the upper frame + // 4. Title text if(title) - CFont::writeText(surface.get(), title, (int)w_ / 2, (int)((global::bmpArray[WINDOW_UPPER_FRAME].h - 9) / 2), - FontSize::Small, FontColor::Yellow, FontAlign::Middle); - - // now draw the other frames (left, right, down) - // down - pic_w = std::min(w_, global::bmpArray[WINDOW_LOWER_FRAME].w); - pic_h = global::bmpArray[WINDOW_LOWER_FRAME].h; - pos.x = 0; - pos.y = h_ - global::bmpArray[WINDOW_LOWER_FRAME].h; - while(pos.x + pic_w <= surface->w) { - CSurface::Draw(surface, global::bmpArray[WINDOW_LOWER_FRAME].surface, pos); - pos.x += pic_w; + const int titleY = pos_.y + (getBmpTexture(WINDOW_UPPER_FRAME).getSize().y - 9) / 2; + CFont::draw(title, Position(pos_.x + static_cast(size_.x) / 2, titleY), FontSize::Small, FontColor::Yellow, + FontAlign::Middle); } - if(surface->w - pos.x > 0) - CSurface::Draw(surface.get(), global::bmpArray[WINDOW_LOWER_FRAME].surface, pos.x, pos.y, 0, 0, - surface->w - pos.x, pic_h); - // left - pic_h = std::min(h_, global::bmpArray[WINDOW_LEFT_FRAME].h); - pos.x = 0; - pos.y = 0; - while(pos.y + pic_h <= surface->h) + + // 5. Lower frame (tiled across bottom) { - CSurface::Draw(surface, global::bmpArray[WINDOW_LEFT_FRAME].surface, pos); - pos.y += pic_h; + const int lowerH = getBmpTexture(WINDOW_LOWER_FRAME).getSize().y; + const Rect lowerFrameRect(Position(pos_.x, pos_.y + static_cast(size_.y) - lowerH), + Extent(size_.x, lowerH)); + getBmpTexture(WINDOW_LOWER_FRAME).drawTiled(lowerFrameRect); } - if(surface->w - pos.x > 0) - CSurface::Draw(surface.get(), global::bmpArray[WINDOW_LEFT_FRAME].surface, pos.x, pos.y, 0, 0, - surface->w - pos.x, pic_h); - // right - pic_h = std::min(h_, global::bmpArray[WINDOW_RIGHT_FRAME].h); - pos.x = w_ - global::bmpArray[WINDOW_RIGHT_FRAME].w; - pos.y = 0; - while(pos.y + pic_h <= surface->h) + + // 6. Left frame (tiled down left side) { - CSurface::Draw(surface, global::bmpArray[WINDOW_RIGHT_FRAME].surface, pos); - pos.y += pic_h; + const Rect leftFrameRect(pos_, Extent(getBmpTexture(WINDOW_LEFT_FRAME).getSize().x, size_.y)); + getBmpTexture(WINDOW_LEFT_FRAME).drawTiled(leftFrameRect); } - if(surface->w - pos.x > 0) - CSurface::Draw(surface.get(), global::bmpArray[WINDOW_RIGHT_FRAME].surface, pos.x, pos.y, 0, 0, - surface->w - pos.x, pic_h); - - // now draw the corners - CSurface::Draw(surface, global::bmpArray[WINDOW_LEFT_UPPER_CORNER].surface); - CSurface::Draw(surface, global::bmpArray[WINDOW_RIGHT_UPPER_CORNER].surface, - Position(w_ - global::bmpArray[WINDOW_RIGHT_UPPER_CORNER].w, 0)); - CSurface::Draw(surface, global::bmpArray[WINDOW_CORNER_RECTANGLE].surface, - Position(0, h_ - global::bmpArray[WINDOW_CORNER_RECTANGLE].h)); - CSurface::Draw( - surface, global::bmpArray[WINDOW_CORNER_RECTANGLE].surface, - Position(w_ - global::bmpArray[WINDOW_CORNER_RECTANGLE].w, h_ - global::bmpArray[WINDOW_CORNER_RECTANGLE].h)); - // now the corner buttons - // close + + // 7. Right frame (tiled down right side) + { + const int rightW = getBmpTexture(WINDOW_RIGHT_FRAME).getSize().x; + const Rect rightFrameRect(Position(pos_.x + static_cast(size_.x) - rightW, pos_.y), + Extent(rightW, size_.y)); + getBmpTexture(WINDOW_RIGHT_FRAME).drawTiled(rightFrameRect); + } + + // 8. Corners + { + getBmpTexture(WINDOW_LEFT_UPPER_CORNER).draw(pos_); + + const int ruW = getBmpTexture(WINDOW_RIGHT_UPPER_CORNER).getSize().x; + getBmpTexture(WINDOW_RIGHT_UPPER_CORNER).draw(pos_ + Position(static_cast(size_.x) - ruW, 0)); + + const int crW = getBmpTexture(WINDOW_CORNER_RECTANGLE).getSize().x; + const int crH = getBmpTexture(WINDOW_CORNER_RECTANGLE).getSize().y; + getBmpTexture(WINDOW_CORNER_RECTANGLE).draw(pos_ + Position(0, static_cast(size_.y) - crH)); + getBmpTexture(WINDOW_CORNER_RECTANGLE).draw(pos_ + size_ - Position(crW, crH)); + } + + // 9. Close button if(canClose) { + int closebutton; if(canClose_clicked) closebutton = WINDOW_BUTTON_CLOSE_CLICKED; else if(canClose_marked) closebutton = WINDOW_BUTTON_CLOSE_MARKED; else closebutton = WINDOW_BUTTON_CLOSE; - CSurface::Draw(surface, global::bmpArray[closebutton].surface); + getBmpTexture(closebutton).draw(pos_); } - // minimize + + // 10. Minimize button if(canMinimize) { + int minimizebutton; if(canMinimize_clicked) minimizebutton = WINDOW_BUTTON_MINIMIZE_CLICKED; else if(canMinimize_marked) minimizebutton = WINDOW_BUTTON_MINIMIZE_MARKED; else minimizebutton = WINDOW_BUTTON_MINIMIZE; - CSurface::Draw(surface, global::bmpArray[minimizebutton].surface, - Position(w_ - global::bmpArray[minimizebutton].w, 0)); + getBmpTexture(minimizebutton) + .draw(pos_ + Position(static_cast(size_.x) - getBmpTexture(minimizebutton).getSize().x, 0)); } - // resize + + // 11. Resize button if(canResize) { + int resizebutton; if(canResize_clicked) resizebutton = WINDOW_BUTTON_RESIZE_CLICKED; else if(canResize_marked) resizebutton = WINDOW_BUTTON_RESIZE_MARKED; else resizebutton = WINDOW_BUTTON_RESIZE; - CSurface::Draw(surface, global::bmpArray[resizebutton].surface, - Position(w_, h_) - global::bmpArray[resizebutton].getSize()); + getBmpTexture(resizebutton).draw(pos_ + size_ - getBmpTexture(resizebutton).getSize()); } - - return true; } void CWindow::setInactive() @@ -456,7 +366,6 @@ void CWindow::setInactive() active = false; clicked = false; marked = false; - needRender = true; for(auto& textfield : getTextFields()) { diff --git a/CIO/CWindow.h b/CIO/CWindow.h index db92045..eb0ed8b 100644 --- a/CIO/CWindow.h +++ b/CIO/CWindow.h @@ -12,17 +12,18 @@ enum class WindowPos Center }; -class CWindow final : public CControlContainer +class CWindow : public CControlContainer { friend class CDebug; -private: // if active is false, the window will be render behind the active windows within the game loop bool active = true; - Sint16 x_; - Sint16 y_; - Uint16 w_; - Uint16 h_; + +protected: + Position pos_; + Extent size_; + +private: const char* title; bool marked = true; bool clicked = false; @@ -43,21 +44,18 @@ class CWindow final : public CControlContainer void (*callback_)(int); int callbackQuitMessage; - bool render() final; - public: + /// Draw the window (frame and children). + void draw(Position parentOrigin) override; + CWindow(void callback(int), int callbackQuitMessage, Position pos, Extent size, const char* title = nullptr, int color = WINDOW_GREEN1, Uint8 flags = 0); CWindow(void callback(int), int callbackQuitMessage, WindowPos pos, Extent size, const char* title = nullptr, int color = WINDOW_GREEN1, Uint8 flags = 0); // Access - Position getPos() const { return {x_, y_}; } - Extent getSize() const { return {w_, h_}; } - int getX() const { return x_; }; - int getY() const { return y_; }; - int getW() const { return w_; }; - int getH() const { return h_; }; - Rect getRect() const { return Rect(x_, y_, w_, h_); } + Position getPos() const { return pos_; } + Extent getSize() const { return size_; } + Rect getRect() const { return Rect(pos_, size_); } int getPriority() const { return priority; } void setPriority(int priority) { this->priority = priority; } void setTitle(const char* title); @@ -67,14 +65,13 @@ class CWindow final : public CControlContainer { active = true; marked = true; - needRender = true; } void setInactive(); bool isActive() const { return active; } bool isMoving() const { return moving; } bool isResizing() const { return resizing; } bool isMarked() const { return marked; } - void setDirty() { needRender = true; } + void setDirty() {} // we can not trust this information, cause if minimized is false, it is possible, that we still have the old // minimized surface bool isMinimized() { return minimized; }; we need an information if a input-element (textfield // etc.) is active to not deliver the input to other gui-element in the event system diff --git a/CMap.cpp b/CMap.cpp index dd63715..1865da5 100644 --- a/CMap.cpp +++ b/CMap.cpp @@ -1328,35 +1328,37 @@ static void getTriangleColor(const bobMAP& map, Uint8 rawTextureId, Sint16& r, S b = 128; } -void CMap::drawMinimap(SDL_Surface* Window) +void CMap::drawMinimap(std::vector& pixels, int w, int h, int& scale) { - // this variables are needed to reduce the size of minimap-windows of big maps - int num_x = (map->width > 256 ? map->width / 256 : 1); - int num_y = (map->height > 256 ? map->height / 256 : 1); + // Scale factor to keep minimap within a reasonable size + int sx = (map->width > 256 ? map->width / 256 : 1); + int sy = (map->height > 256 ? map->height / 256 : 1); - // make sure the minimap has the same proportions as the "real" map, so scale the same rate - num_x = (num_x > num_y ? num_x : num_y); - num_y = (num_x > num_y ? num_x : num_y); + // Keep aspect ratio uniform + scale = (sx > sy ? sx : sy); - // if (Window->w < map->width || Window->h < map->height) - // return; + // Ensure pixel buffer is the right size + pixels.assign(static_cast(w) * h, 0); for(int y = 0; y < map->height; y++) { - if(y % num_y != 0) + if(y % scale != 0) continue; for(int x = 0; x < map->width; x++) { - if(x % num_x != 0) + if(x % scale != 0) continue; Sint16 r, g, b; getTriangleColor(*map, map->getVertex(x, y).rsuTexture, r, g, b); - Uint32* row = (Uint32*)Window->pixels + (y / num_y + 20) * Window->pitch / 4; //-V206 - //+6 because of the left window frame - Uint32* pixel = row + x / num_x + 6; + const int py = y / scale; + const int px = x / scale; + if(py >= h || px >= w) + continue; + + auto& pixel = pixels[static_cast(py) * w + px]; Sint32 vertexLighting = map->getVertex(x, y).i; r = ((r * vertexLighting) >> 16); @@ -1365,32 +1367,9 @@ void CMap::drawMinimap(SDL_Surface* Window) const auto r8 = (Uint8)(r > 255 ? 255 : (r < 0 ? 0 : r)); const auto g8 = (Uint8)(g > 255 ? 255 : (g < 0 ? 0 : g)); const auto b8 = (Uint8)(b > 255 ? 255 : (b < 0 ? 0 : b)); - *pixel = ((r8 << Window->format->Rshift) + (g8 << Window->format->Gshift) + (b8 << Window->format->Bshift)); - } - } - - // draw the player flags - for(int i = 0; i < MAXPLAYERS; i++) - { - if(PlayerHQx[i] != 0xFFFF && PlayerHQy[i] != 0xFFFF) - { - // draw flag - //%7 cause in the original game there are only 7 players and 7 different flags - CSurface::Draw(Window, global::bmpArray[FLAG_BLUE_DARK + i % 7].surface, - 6 + PlayerHQx[i] / num_x - global::bmpArray[FLAG_BLUE_DARK + i % 7].nx, - 20 + PlayerHQy[i] / num_y - global::bmpArray[FLAG_BLUE_DARK + i % 7].ny); - // write player number - CFont::writeText(Window, std::to_string(i + 1), 6 + PlayerHQx[i] / num_x, 20 + PlayerHQy[i] / num_y, - FontSize::Small, FontColor::MintGreen); + pixel = (0xFFu << 24) | (r8 << 16) | (g8 << 8) | b8; } } - - // draw the arrow --> 6px is width of left window frame and 20px is the height of the upper window frame - CSurface::Draw(Window, global::bmpArray[MAPPIC_ARROWCROSS_ORANGE].surface, - 6 + (displayRect.left + displayRect.getSize().x / 2) / triangleWidth / num_x - - global::bmpArray[MAPPIC_ARROWCROSS_ORANGE].nx, - 20 + (displayRect.top + displayRect.getSize().y / 2) / triangleHeight / num_y - - global::bmpArray[MAPPIC_ARROWCROSS_ORANGE].ny); } void CMap::modifyVertex() diff --git a/CMap.h b/CMap.h index 431f2c3..fbb3352 100644 --- a/CMap.h +++ b/CMap.h @@ -161,7 +161,7 @@ class CMap std::string getAuthor() const { return map->getAuthor(); } void setAuthor(const std::string& author) { map->setAuthor(author); } - void drawMinimap(SDL_Surface* Window); + void drawMinimap(std::vector& pixels, int w, int h, int& scale); void render(); // get and set some variables necessary for cursor behavior void setHexagonMode(bool HexagonMode) diff --git a/Texture.cpp b/Texture.cpp index 5ff6a56..bac0849 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -3,7 +3,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "Texture.h" +#include "defines.h" +#include "globals.h" #include +#include #include #include @@ -109,7 +112,7 @@ bool Texture::load(SDL_Surface* surface, bool filterLinear) { auto* row = (Uint32*)((Uint8*)converted->pixels + y * converted->pitch); for(int x = 0; x < converted->w; x++) - row[x] |= 0xFF000000u; // set alpha bits + row[x] |= 0xFF000000u; } SDL_UnlockSurface(converted); @@ -118,7 +121,7 @@ bool Texture::load(SDL_Surface* surface, bool filterLinear) return true; } -void Texture::Draw(const Rect& destRect) const +void Texture::draw(const Rect& destRect) const { if(!texture_) return; @@ -136,7 +139,7 @@ void Texture::Draw(const Rect& destRect) const glEnd(); } -void Texture::Draw(Position pos) const +void Texture::draw(Position pos) const { if(!texture_) return; @@ -153,3 +156,96 @@ void Texture::Draw(Position pos) const glVertex2i(pos.x, pos.y + size_.y); glEnd(); } + +void Texture::drawTiled(const Rect& destRect) const +{ + if(!texture_) + return; + + const Extent tileSize = getSize(); + if(tileSize.x == 0 || tileSize.y == 0) + return; + + glBindTexture(GL_TEXTURE_2D, texture_); + glBegin(GL_QUADS); + for(int y = destRect.top; y < destRect.bottom; y += static_cast(tileSize.y)) + { + const int rowH = std::min(static_cast(tileSize.y), destRect.bottom - y); + const float v1 = float(rowH) / float(tileSize.y); + for(int x = destRect.left; x < destRect.right; x += static_cast(tileSize.x)) + { + const int colW = std::min(static_cast(tileSize.x), destRect.right - x); + const float u1 = float(colW) / float(tileSize.x); + + glTexCoord2f(0, 0); + glVertex2i(x, y); + glTexCoord2f(u1, 0); + glVertex2i(x + colW, y); + glTexCoord2f(u1, v1); + glVertex2i(x + colW, y + rowH); + glTexCoord2f(0, v1); + glVertex2i(x, y + rowH); + } + } + glEnd(); +} + +void drawRect(const Rect& rect, unsigned color) +{ + glDisable(GL_TEXTURE_2D); + glColor4ub((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, (color >> 24) & 0xFF); + glBegin(GL_QUADS); + glVertex2i(rect.left, rect.top); + glVertex2i(rect.right, rect.top); + glVertex2i(rect.right, rect.bottom); + glVertex2i(rect.left, rect.bottom); + glEnd(); + glEnable(GL_TEXTURE_2D); + glColor4f(1, 1, 1, 1); +} + +Texture& getBmpTexture(unsigned idx, bool filterLinear) +{ + static std::vector cache; + static std::vector linearFlags; + if(idx >= global::bmpArray.size()) + { + static Texture dummy; + return dummy; + } + if(idx >= cache.size()) + { + cache.resize(static_cast(idx) + 1); + linearFlags.resize(static_cast(idx) + 1, false); + } + if(!cache[idx].isValid() || linearFlags[idx] != filterLinear) + { + linearFlags[idx] = filterLinear; + auto& bmp = global::bmpArray[idx]; + if(bmp.surface) + cache[idx].load(bmp.surface.get(), filterLinear); + } + return cache[idx]; +} + +void drawButtonBox(const Rect& area, bool pressed, unsigned baseTex, unsigned faceTex) +{ + getBmpTexture(baseTex).drawTiled(area); + + const auto sz = area.getSize(); + + // 2px black frame: left+top if pressed, right+bottom otherwise + if(pressed) + { + drawRect(Rect(area.getOrigin(), 2, sz.y), 0xFF000000); + drawRect(Rect(area.getOrigin(), sz.x, 2), 0xFF000000); + } else + { + drawRect(Rect(area.right - 2, area.top, 2, sz.y), 0xFF000000); + drawRect(Rect(area.left, area.bottom - 2, sz.x, 2), 0xFF000000); + } + + // Foreground inset by 2px + const Rect fgRect(area.getOrigin() + Position(2, 2), sz - Extent(4, 4)); + getBmpTexture(faceTex).drawTiled(fgRect); +} diff --git a/Texture.h b/Texture.h index 5c19b8c..b306c67 100644 --- a/Texture.h +++ b/Texture.h @@ -33,10 +33,19 @@ class Texture void upload(const void* bgraPixels); /// Draw the texture stretched to fill the given rect. - void Draw(const Rect& destRect) const; + void draw(const Rect& destRect) const; /// Draw the texture at native size at the given position. - void Draw(Position pos) const; + void draw(Position pos) const; + + /// Tile the texture to fill the given rectangle. + void drawTiled(const Rect& destRect) const; + + /// Returns the raw GL texture name (for use with glBindTexture). + unsigned getHandle() const { return texture_; } + + /// Size in pixels. + Extent getSize() const { return size_; } /// Returns true if the texture has been created. bool isValid() const { return texture_ != 0; } @@ -48,3 +57,15 @@ class Texture /// Internal: create or recreate texture from raw BGRA pixel data. void load(const void* bgraPixels, Extent size, bool filterLinear); }; + +/// Draw a filled rectangle with a 32-bit ARGB colour. +void drawRect(const Rect& rect, unsigned color); + +/// Draw a 3D-style button box: tiled background, 2px black frame (sunken if pressed, raised otherwise), +/// and tiled foreground inset by 2px. +void drawButtonBox(const Rect& area, bool pressed, unsigned baseTex, unsigned faceTex); + +/// Get or create the cached OpenGL texture for a bitmap index. +/// The texture is loaded from the SDL surface on first access. +/// @param filterLinear Whether to use linear filtering (for scaled backgrounds). +Texture& getBmpTexture(int idx, bool filterLinear = false); diff --git a/callbacks.cpp b/callbacks.cpp index 6e16659..338891e 100644 --- a/callbacks.cpp +++ b/callbacks.cpp @@ -10,15 +10,18 @@ #include "CIO/CFile.h" #include "CIO/CFont.h" #include "CIO/CMenu.h" +#include "CIO/CMinimapWindow.h" #include "CIO/CPicture.h" #include "CIO/CSelectBox.h" #include "CIO/CTextfield.h" #include "CIO/CWindow.h" #include "CMap.h" #include "CSurface.h" +#include "CollisionDetection.h" #include "globals.h" #include "helpers/format.hpp" #include "s25util/strAlgos.h" +#include #include #include #include @@ -51,8 +54,8 @@ void callback::PleaseWait(int Param) WNDWait->addText("Please wait ...", Position(10, 10), FontSize::Large); // we need to render this window NOW, cause the render loop will do it too late (when the operation // is done and we don't need the "Please wait"-window anymore) - CSurface::Draw(global::s2->getDisplaySurface(), WNDWait->getSurface(), - global::s2->getDisplaySurface()->w / 2 - 106, global::s2->getDisplaySurface()->h / 2 - 35); + glClear(GL_COLOR_BUFFER_BIT); + WNDWait->draw(Position(0, 0)); global::s2->RenderPresent(); break; @@ -813,8 +816,8 @@ void callback::EditorLoadMenu(int Param) int borderT = global::bmpArray[WINDOW_UPPER_FRAME].h; int borderB = global::bmpArray[WINDOW_LOWER_FRAME].h; - int window_w = WNDLoad->getW(); - int window_h = WNDLoad->getH(); + int window_w = static_cast(WNDLoad->getSize().x); + int window_h = static_cast(WNDLoad->getSize().y); int client_w = window_w - borderL - borderR; int client_h = window_h - borderT - borderB; @@ -928,10 +931,10 @@ void callback::EditorSaveMenu(int Param) TXTF_Filename = WNDSave->addTextfield(Position(10, 13), 21, 1); const bfs::path filePath = MapObj->getFilepath().empty() ? "MyMap" : MapObj->getFilepath(); TXTF_Filename->setText(filePath.filename().string()); - WNDSave->addText("Mapname", Position(98, 38), FontSize::Small); + WNDSave->addText("Mapname", Position(100, 38), FontSize::Small); TXTF_Mapname = WNDSave->addTextfield(Position(10, 50), 21, 1); TXTF_Mapname->setText(MapObj->getMapname()); - WNDSave->addText("Author", Position(110, 75), FontSize::Medium); + WNDSave->addText("Author", Position(100, 75), FontSize::Small); TXTF_Author = WNDSave->addTextfield(Position(10, 87), 21, 1); TXTF_Author->setText(MapObj->getAuthor()); WNDSave->addButton(EditorSaveMenu, SAVEMAP, Position(170, 120), Extent(90, 20), BUTTON_GREY, "Save"); @@ -2820,7 +2823,6 @@ void callback::MinimapMenu(int Param) { static CWindow* WNDMinimap = nullptr; static CMap* MapObj = nullptr; - static SDL_Surface* WndSurface = nullptr; static int scaleNum = 1; // only in case INITIALIZING_CALL needed to create the window int width; @@ -2849,31 +2851,24 @@ void callback::MinimapMenu(int Param) height = map->height / scaleNum; //--> 12px is width of left and right window frame and 30px is height of the upper and lower window // frame - if((global::s2->getDisplaySurface()->w - 12 < width) - || (global::s2->getDisplaySurface()->h - 30 < height)) + if((static_cast(global::s2->getRes().x) - 12 < width) + || (static_cast(global::s2->getRes().y) - 30 < height)) break; - WNDMinimap = global::s2->RegisterWindow( - std::make_unique(MinimapMenu, WINDOWQUIT, WindowPos::Center, Extent(width + 12, height + 30), - "Overview", WINDOW_NOTHING, WINDOW_CLOSE | WINDOW_MOVE)); + WNDMinimap = global::s2->RegisterWindow(std::make_unique( + MinimapMenu, WINDOWQUIT, WindowPos::Center, Extent(width + 12, height + 30), "Overview", + WINDOW_NOTHING, WINDOW_CLOSE | WINDOW_MOVE)); global::s2->RegisterCallback(MinimapMenu); - WndSurface = WNDMinimap->getSurface(); } break; - case CALL_FROM_GAMELOOP: - if(MapObj && WndSurface) - MapObj->drawMinimap(WndSurface); - break; - case WINDOW_CLICKED_CALL: if(MapObj) { Position mouse; if(SDL_GetMouseState(&mouse.x, &mouse.y) & SDL_BUTTON(1)) { - if(mouse.x > (WNDMinimap->getX() + 6) && mouse.x < (WNDMinimap->getX() + WNDMinimap->getW() - 6) - && mouse.y > (WNDMinimap->getY() + 20) - && mouse.y < (WNDMinimap->getY() + WNDMinimap->getH() - 10)) + if(IsPointInRect( + mouse, Rect(WNDMinimap->getPos() + Position(6, 20), WNDMinimap->getSize() - Extent(12, 30)))) { DisplayRectangle displayRect = MapObj->getDisplayRect(); displayRect.setOrigin((mouse - WNDMinimap->getRect().getOrigin() - Position(6, 20) @@ -2894,7 +2889,6 @@ void callback::MinimapMenu(int Param) WNDMinimap = nullptr; } MapObj = nullptr; - WndSurface = nullptr; global::s2->UnregisterCallback(MinimapMenu); break; @@ -3105,8 +3099,7 @@ void callback::submenu1(int Param) "Create window"); picObject = SubMenu->addPicture(submenu1, PICOBJECT, Position(200, 30), MIS0BOBS_SHIP); picObject->setMotionParams(PICOBJECTENTRY, PICOBJECTLEAVE); - // text block with \n - SubMenu->addText("\nTextblock:\n\nNeue Zeile\nNoch eine neue Zeile", Position(400, 200), FontSize::Large); + SubMenu->addText("Textblock", Position(400, 200), FontSize::Large); testTextfield = SubMenu->addTextfield(Position(400, 300), 10, 3); testSelectBox = SubMenu->addSelectBox(Position(500, 500), Extent(300, 200)); testSelectBox->addOption("Erste Option", submenu1, SELECTBOX_OPTION1);