diff --git a/src/sago/SagoDataHolder.cpp b/src/sago/SagoDataHolder.cpp index 47ef639..3f8d338 100644 --- a/src/sago/SagoDataHolder.cpp +++ b/src/sago/SagoDataHolder.cpp @@ -24,7 +24,6 @@ SOFTWARE. #include "SagoDataHolder.hpp" #include -#include #include #include #include @@ -114,8 +113,12 @@ SDL_Texture* SagoDataHolder::getTexturePtr(const std::string& textureName) const printFileWeLoad(path); } if (!PHYSFS_exists(path.c_str())) { - std::cerr << "getTextureFailed - Texture does not exist: " << path << "\n"; - return getTexturePtr("fallback"); + // We did not find the png file. Try to see if there are a jpg file. + std::string jpg_path = "textures/"+textureName+".jpg"; + if (!PHYSFS_exists(jpg_path.c_str())) { + sago::SagoFatalErrorF("getTextureFailed - Texture does not exist: %s", path.c_str()); + } + path = jpg_path; } unsigned int m_size = 0; std::unique_ptr m_data; @@ -253,7 +256,7 @@ TextureHandler::TextureHandler(const SagoDataHolder* holder, const std::string& this->data = nullptr; } -SDL_Texture* TextureHandler::get() { +SDL_Texture* TextureHandler::get() const { if (version != holder->getVersion()) { //The holder has been invalidated this->data = this->holder->getTexturePtr(textureName); diff --git a/src/sago/SagoDataHolder.hpp b/src/sago/SagoDataHolder.hpp index 377b51b..339d111 100644 --- a/src/sago/SagoDataHolder.hpp +++ b/src/sago/SagoDataHolder.hpp @@ -40,11 +40,11 @@ class TextureHandler { public: TextureHandler() {}; TextureHandler(const SagoDataHolder* holder, const std::string& textureName); - SDL_Texture* get(); + SDL_Texture* get() const; private: std::string textureName; const SagoDataHolder* holder = nullptr; - SDL_Texture* data = nullptr; + mutable SDL_Texture* data = nullptr; Uint64 version = 0; }; diff --git a/src/sago/SagoLogicalResize.hpp b/src/sago/SagoLogicalResize.hpp new file mode 100644 index 0000000..53db267 --- /dev/null +++ b/src/sago/SagoLogicalResize.hpp @@ -0,0 +1,219 @@ +/* +Copyright (c) 2025 Poul Sander + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#pragma once +#include +#include "SDL.h" + + +namespace sago { + +/** + * @brief Provides coordinate transformation between logical and physical screen coordinates. + * + * This class helps create resolution-independent layouts by defining coordinates in a fixed + * "logical" resolution and automatically converting them to the actual "physical" screen size. + * It maintains aspect ratio by adding letterboxing/pillarboxing margins when needed. + * + * Key concepts: + * - Logical coordinates: Fixed coordinate system you design for (e.g., 1920x1080) + * - Physical coordinates: Actual screen/window size (e.g., 1280x720, 3840x2160, etc.) + * - Scale factor: Calculated to fit logical size into physical size while preserving aspect ratio + * - Margins: Black bars added to maintain aspect ratio (letterboxing/pillarboxing) + * - Tile alignment: As long as tiles are resized with LogicalToPhysical, they will align correctly. No gaps or overlaps. + * + * Example usage: + * @code + * // Design your UI for 1920x1080 + * SagoLogicalResize resize(1920, 1080); + * resize.SetPhysicalSize(window_width, window_height); + * + * // Draw at logical position (100, 100) + * int phys_x, phys_y; + * resize.LogicalToPhysical(100, 100, phys_x, phys_y); + * DrawSprite(renderer, phys_x, phys_y); + * + * // Handle mouse input + * int log_x, log_y; + * resize.PhysicalToLogical(mouse_x, mouse_y, log_x, log_y); + * if (log_x > 100 && log_x < 200) { ... } + * @endcode + */ +class SagoLogicalResize { +public: + /** + * @brief Default constructor with minimal 1x1 logical size. + */ + SagoLogicalResize() : logical_width_(1), logical_height_(1), physical_width_(1), physical_height_(1) { + SetScaleFactor(); + } + + /** + * @brief Constructor with specified logical resolution. + * @param logical_width The width of the logical coordinate system (minimum 1) + * @param logical_height The height of the logical coordinate system (minimum 1) + */ + SagoLogicalResize(int logical_width, int logical_height) + : logical_width_(std::max(1, logical_width)), logical_height_(std::max(1, logical_height)), physical_width_(1), physical_height_(1) { + SetScaleFactor(); + } + + /** + * @brief Updates the physical (actual) screen size and recalculates scaling. + * + * Call this when the window is resized or when initializing the physical display size. + * This recalculates the scale factor and margins to fit the logical size into the physical size. + * + * @param physical_width The actual width of the screen/window (minimum 1) + * @param physical_height The actual height of the screen/window (minimum 1) + */ + void SetPhysicalSize(int physical_width, int physical_height) { + //Physical size must be at least 1. Less than 1 is not drawn anyway and it prevents division by zero. + physical_width_ = std::max(1, physical_width); + physical_height_ = std::max(1, physical_height); + SetScaleFactor(); + } + + /** + * @brief Converts logical coordinates to physical screen coordinates. + * + * This applies the scale factor and adds the appropriate margins. + * Use this to convert positions where you want to draw elements on screen. + * + * @param logical_x The x coordinate in logical space + * @param logical_y The y coordinate in logical space + * @param physical_x Output: the x coordinate in physical screen space + * @param physical_y Output: the y coordinate in physical screen space + */ + void LogicalToPhysical(int logical_x, int logical_y, int& physical_x, int& physical_y) const { + physical_x = logical_x; + physical_y = logical_y; + LogicalToPhysical(&physical_x, &physical_y); + } + + /** + * @brief Converts logical coordinates to physical screen coordinates (pointer version). + * + * This applies the scale factor and adds the appropriate margins. + * Null pointers are safely ignored. + * + * @param x Pointer to x coordinate (will be converted in place), can be null + * @param y Pointer to y coordinate (will be converted in place), can be null + */ + void LogicalToPhysical(int* x, int* y) const { + if (x) { + *x = *x * scale_factor_ + left_margin_; + } + if (y) { + *y = *y * scale_factor_ + top_margin_; + } + } + + /** + * @brief Converts a logical rectangle to physical screen coordinates. + * + * This converts both the position AND size of a rectangle. The rectangle + * is transformed in place. Use this for converting areas/regions rather than + * just single points. + * + * Note: This properly handles the size conversion by converting the bottom-right + * corner and calculating the new width/height from the difference. + * + * @param inout The rectangle in logical coordinates (input), converted to physical coordinates (output) + */ + void LogicalToPhysical(SDL_Rect& inout) const { + SDL_Rect input = inout; + LogicalToPhysical(&inout.x, &inout.y); + LogicalToPhysical(input.x + input.w + 1, input.y + input.h + 1, inout.w, inout.h); + inout.w -= inout.x - 1; + inout.h -= inout.y - 1; + } + + /** + * @brief Converts physical screen coordinates to logical coordinates. + * + * Use this to convert mouse input or other physical positions back to your + * logical coordinate system for hit detection and input handling. + * This removes margins and applies inverse scaling. + * + * @param physical_x The x coordinate in physical screen space + * @param physical_y The y coordinate in physical screen space + * @param logical_x Output: the x coordinate in logical space + * @param logical_y Output: the y coordinate in logical space + */ + void PhysicalToLogical(int physical_x, int physical_y, int& logical_x, int& logical_y) const { + logical_x = (physical_x - left_margin_) / scale_factor_; + logical_y = (physical_y - top_margin_) / scale_factor_; + } + + /** + * @brief Gets the top margin (letterboxing/pillarboxing offset). + * + * This is the black bar size at the top when the aspect ratio requires vertical margins. + * Useful for debugging or custom rendering that needs to know the drawable area. + * + * @return The top margin in physical pixels + */ + int GetTopMargin() const { + return top_margin_; + } + + /** + * @brief Gets the left margin (letterboxing/pillarboxing offset). + * + * This is the black bar size on the left when the aspect ratio requires horizontal margins. + * Useful for debugging or custom rendering that needs to know the drawable area. + * + * @return The left margin in physical pixels + */ + int GetLeftMargin() const { + return left_margin_; + } + +private: + /** + * @brief Calculates the scale factor and margins based on current logical and physical sizes. + * + * The scale factor is chosen to fit the logical size into the physical size while + * maintaining aspect ratio. The margins center the content on screen. + * + * Scale factor = min(physical_width/logical_width, physical_height/logical_height) + * This ensures the content fits in both dimensions without distortion. + */ + void SetScaleFactor() { + scale_factor_ = std::min(physical_width_ / logical_width_, physical_height_ / logical_height_); + left_margin_ = (physical_width_ - logical_width_ * scale_factor_) / 2; + top_margin_ = (physical_height_ - logical_height_ * scale_factor_) / 2; + } + + double scale_factor_; ///< Multiplier to convert logical size to physical size + int top_margin_; ///< Vertical offset for centering (letterboxing) + int left_margin_; ///< Horizontal offset for centering (pillarboxing) + double logical_width_; ///< Width of the logical coordinate system + double logical_height_; ///< Height of the logical coordinate system + double physical_width_; ///< Actual width of the screen/window + double physical_height_; ///< Actual height of the screen/window +}; + +} // namespace sago \ No newline at end of file diff --git a/src/sago/SagoMisc.cpp b/src/sago/SagoMisc.cpp index ce21e09..08c92d6 100644 --- a/src/sago/SagoMisc.cpp +++ b/src/sago/SagoMisc.cpp @@ -80,7 +80,8 @@ std::string GetFileContent(const char* filename) { std::unique_ptr m_data; ReadBytesFromFile(filename, m_data, m_size); //Now create a std::string - ret = std::string(m_data.get(), m_data.get()+m_size); + char* inbuf = m_data.get(); + ret = std::string(inbuf, inbuf+m_size); return ret; } diff --git a/src/sago/SagoSprite.cpp b/src/sago/SagoSprite.cpp index c116e3e..e097660 100644 --- a/src/sago/SagoSprite.cpp +++ b/src/sago/SagoSprite.cpp @@ -32,61 +32,39 @@ SOFTWARE. namespace sago { -struct SagoSprite::SagoSpriteData { - TextureHandler tex; - SDL_Rect imgCord = {}; - SDL_Rect origin = {}; - int aniFrames = 0; - int aniFrameTime = 0; -}; SagoSprite::SagoSprite() { - data = new SagoSpriteData(); } SagoSprite::SagoSprite(const SagoDataHolder& texHolder, const std::string& texture,const SDL_Rect& initImage,const int animationFrames, const int animationFrameLength) { - data = new SagoSpriteData(); - data->tex = texHolder.getTextureHandler(texture); - data->imgCord = initImage; - data->aniFrames = animationFrames; - data->aniFrameTime = animationFrameLength; + tex = texHolder.getTextureHandler(texture); + imgCord = initImage; + aniFrames = animationFrames; + aniFrameTime = animationFrameLength; } -SagoSprite::SagoSprite(const SagoSprite& base) : data(new SagoSpriteData(*base.data)) { - -} - -SagoSprite& SagoSprite::operator=(const SagoSprite& base) { - *data = *base.data; - return *this; -} - -SagoSprite::~SagoSprite() { - delete data; +void SagoSprite::Draw(SDL_Renderer* target, Sint32 frameTime, int x, int y, SagoLogicalResize* resize) const { + DrawScaled(target, frameTime, x, y, imgCord.w, imgCord.h, resize); } -void SagoSprite::Draw(SDL_Renderer* target, Sint32 frameTime, int x, int y) const { - DrawScaled(target, frameTime, x, y, data->imgCord.w, data->imgCord.h); +void SagoSprite::DrawRotated(SDL_Renderer* target, Sint32 frameTime, int x, int y, const double angleRadian, SagoLogicalResize* resize) const { + SDL_Point center = {this->origin.x, this->origin.y}; + DrawScaledAndRotated(target, frameTime, x, y, imgCord.w, imgCord.h, angleRadian, ¢er, SDL_FLIP_NONE, resize); } -void SagoSprite::DrawRotated(SDL_Renderer* target, Sint32 frameTime, int x, int y, const double angleRadian) const { - SDL_Point center = {this->data->origin.x, this->data->origin.y}; - DrawScaledAndRotated(target, frameTime, x, y, data->imgCord.w, data->imgCord.h, angleRadian, ¢er, SDL_FLIP_NONE); +void SagoSprite::DrawScaled(SDL_Renderer* target, Sint32 frameTime, int x, int y, int w, int h, SagoLogicalResize* resize) const { + DrawScaledAndRotated(target, frameTime, x, y, w, h, 0.0, nullptr, SDL_FLIP_NONE, resize); } -void SagoSprite::DrawScaled(SDL_Renderer* target, Sint32 frameTime, int x, int y, int w, int h) const { - DrawScaledAndRotated(target, frameTime, x, y, w, h, 0.0, nullptr, SDL_FLIP_NONE); -} - -void SagoSprite::DrawScaledAndRotated(SDL_Renderer* target, Sint32 frameTime, int x, int y, int w, int h, const double angleRadian, const SDL_Point* center, const SDL_RendererFlip flip) const { - if (!data->tex.get()) { +void SagoSprite::DrawScaledAndRotated(SDL_Renderer* target, Sint32 frameTime, int x, int y, int w, int h, const double angleRadian, const SDL_Point* center, const SDL_RendererFlip flip, SagoLogicalResize* resize) const { + if (!tex.get()) { std::cerr << "Texture is null!\n"; } - SDL_Rect rect = data->imgCord; - rect.x+=rect.w*((frameTime/data->aniFrameTime)%data->aniFrames); + SDL_Rect rect = imgCord; + rect.x+=rect.w*((frameTime/aniFrameTime)%aniFrames); SDL_Rect pos = rect; - pos.x = x - this->data->origin.x; - pos.y = y - this->data->origin.y; + pos.x = x - this->origin.x; + pos.y = y - this->origin.y; if (w > 0) { pos.w = w; } @@ -94,31 +72,37 @@ void SagoSprite::DrawScaledAndRotated(SDL_Renderer* target, Sint32 frameTime, in pos.h = h; } double angleDegress = angleRadian/M_PI*180.0; - SDL_RenderCopyEx(target, data->tex.get(), &rect, &pos, angleDegress, center, flip); + if (resize) { + resize->LogicalToPhysical(pos); + } + SDL_RenderCopyEx(target, tex.get(), &rect, &pos, angleDegress, center, flip); } -void SagoSprite::Draw(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& part) const { - SDL_Rect rect = data->imgCord; - rect.x+=rect.w*((frameTime/data->aniFrameTime)%data->aniFrames); +void SagoSprite::Draw(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& part, SagoLogicalResize* resize) const { + SDL_Rect rect = imgCord; + rect.x+=rect.w*((frameTime/aniFrameTime)%aniFrames); rect.x += part.x; rect.y += part.y; rect.w = part.w; rect.h = part.h; SDL_Rect pos = rect; - pos.x = x - this->data->origin.x; - pos.y = y - this->data->origin.y; - SDL_RenderCopy(target, data->tex.get(), &rect, &pos); + pos.x = x - this->origin.x; + pos.y = y - this->origin.y; + if (resize) { + resize->LogicalToPhysical(pos); + } + SDL_RenderCopy(target, tex.get(), &rect, &pos); } -void SagoSprite::DrawProgressive(SDL_Renderer* target, float progress, int x, int y) const { - Sint32 frameNumber = progress*data->aniFrames; - Sint32 frameTime = frameNumber*data->aniFrameTime; - Draw(target, frameTime, x, y); +void SagoSprite::DrawProgressive(SDL_Renderer* target, float progress, int x, int y, SagoLogicalResize* resize) const { + Sint32 frameNumber = progress*aniFrames; + Sint32 frameTime = frameNumber*aniFrameTime; + Draw(target, frameTime, x, y, resize); } -void SagoSprite::DrawBounded(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& bounds) const { - SDL_Rect rect = data->imgCord; - rect.x+=rect.w*((frameTime/data->aniFrameTime)%data->aniFrames); +void SagoSprite::DrawBounded(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& bounds, SagoLogicalResize* resize) const { + SDL_Rect rect = imgCord; + rect.x+=rect.w*((frameTime/aniFrameTime)%aniFrames); SDL_Rect pos = rect; pos.x = x; pos.y = y; @@ -159,18 +143,21 @@ void SagoSprite::DrawBounded(SDL_Renderer* target, Sint32 frameTime, int x, int rect.h -= absDiff; } - SDL_RenderCopy(target, data->tex.get(), &rect, &pos); + if (resize) { + resize->LogicalToPhysical(pos); + } + SDL_RenderCopy(target, tex.get(), &rect, &pos); } void SagoSprite::SetOrigin(const SDL_Rect& newOrigin) { - data->origin = newOrigin; + origin = newOrigin; } int SagoSprite::GetWidth() const { - return data->imgCord.w; + return imgCord.w; } int SagoSprite::GetHeight() const { - return data->imgCord.h; + return imgCord.h; } } //namespace sago diff --git a/src/sago/SagoSprite.hpp b/src/sago/SagoSprite.hpp index 57c4e17..025d6b6 100644 --- a/src/sago/SagoSprite.hpp +++ b/src/sago/SagoSprite.hpp @@ -26,6 +26,7 @@ SOFTWARE. #define SAGOSPRITE_HPP #include "SagoDataHolder.hpp" +#include "SagoLogicalResize.hpp" namespace sago { @@ -41,7 +42,7 @@ public: * @param x Place to draw the sprite * @param y Place to draw the sprite */ - void Draw(SDL_Renderer* target, Sint32 frameTime, int x, int y) const; + void Draw(SDL_Renderer* target, Sint32 frameTime, int x, int y, SagoLogicalResize* resize = nullptr) const; /** * Draws the sprite to a given render window @@ -51,7 +52,7 @@ public: * @param y Place to draw the sprite * @param angleRadian Angle to rotate the sprite around origin before drawing */ - void DrawRotated(SDL_Renderer* target, Sint32 frameTime, int x, int y, const double angleRadian) const; + void DrawRotated(SDL_Renderer* target, Sint32 frameTime, int x, int y, const double angleRadian, SagoLogicalResize* resize = nullptr) const; /** * Draws part of the sprite to a given render window @@ -61,7 +62,7 @@ public: * @param y Place to draw the sprite * @param part the part of the sprite that should be drawn. */ - void Draw(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& part) const; + void Draw(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& part, SagoLogicalResize* resize = nullptr) const; /** * Draws the wprite to the given renderer but makes sure to not draw outside th bounds given @@ -71,7 +72,7 @@ public: * @param y Place to draw the sprite * @param bounds A recagular area that we must not draw outside. */ - void DrawBounded(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& bounds) const; + void DrawBounded(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& bounds, SagoLogicalResize* resize = nullptr) const; /** * Draws the sprite to a given render window @@ -80,25 +81,41 @@ public: * @param x Place to draw the sprite * @param y Place to draw the sprite */ - void DrawProgressive(SDL_Renderer* target, float progress, int x, int y) const; + void DrawProgressive(SDL_Renderer* target, float progress, int x, int y, SagoLogicalResize* resize = nullptr) const; - void DrawScaled(SDL_Renderer* target, Sint32 frameTime, int x, int y, int w, int h) const; + void DrawScaled(SDL_Renderer* target, Sint32 frameTime, int x, int y, int w, int h, SagoLogicalResize* resize = nullptr) const; void DrawScaledAndRotated(SDL_Renderer* target, Sint32 frameTime, int x, int y, int w, int h, - const double angleRadian, const SDL_Point* center, const SDL_RendererFlip flip) const; + const double angleRadian, const SDL_Point* center, const SDL_RendererFlip flip, SagoLogicalResize* resize = nullptr) const; /** * Set a different origin. Normally it is the top left cornor. But in some cases you might want to center the origin or tranform it for other reasons * @param newOrigin the coordinates that should be the new origin. Call with {0,0} to reset to default */ void SetOrigin(const SDL_Rect& newOrigin); - SagoSprite(const SagoSprite& base); - SagoSprite& operator=(const SagoSprite& base); + SagoSprite(const SagoSprite& base) = default; + SagoSprite& operator=(const SagoSprite& base) = default; int GetWidth() const; int GetHeight() const; - ~SagoSprite(); + ~SagoSprite() = default; + + const TextureHandler& GetTextureHandler() const { + return this->tex; + } + + const SDL_Rect& GetImageCord() const { + return this->imgCord; + } + + const SDL_Rect& GetOrigin() const { + return this->origin; + } + private: - struct SagoSpriteData; - SagoSpriteData* data; + TextureHandler tex; + SDL_Rect imgCord = {}; + SDL_Rect origin = {}; + int aniFrames = 0; + int aniFrameTime = 0; }; } diff --git a/src/sago/SagoSpriteHolder.cpp b/src/sago/SagoSpriteHolder.cpp index 94c4c1c..88047d4 100644 --- a/src/sago/SagoSpriteHolder.cpp +++ b/src/sago/SagoSpriteHolder.cpp @@ -148,6 +148,7 @@ const sago::SagoSprite& SagoSpriteHolder::GetSprite(const std::string& spritenam } } + const SagoDataHolder& SagoSpriteHolder::GetDataHolder() const { return *data->tex; } diff --git a/src/sago/SagoTextBox.cpp b/src/sago/SagoTextBox.cpp index 8ad56fb..41a1c6e 100644 --- a/src/sago/SagoTextBox.cpp +++ b/src/sago/SagoTextBox.cpp @@ -177,14 +177,14 @@ void SagoTextBox::UpdateCache() { data->renderedText = data->text; } -void SagoTextBox::Draw(SDL_Renderer* target, int x, int y, SagoTextField::Alignment alignment ) { +void SagoTextBox::Draw(SDL_Renderer* target, int x, int y, SagoTextField::Alignment alignment, SagoLogicalResize* resize ) { if (data->text != data->renderedText) { UpdateCache(); } TTF_Font* font = data->tex->getFontPtr(data->fontName, data->fontSize); int lineSkip = TTF_FontLineSkip(font); for (size_t i = 0; i < data->lines.size(); ++i) { - data->lines[i].Draw(target, x, y+i*lineSkip, alignment); + data->lines[i].Draw(target, x, y+i*lineSkip, alignment, sago::SagoTextField::VerticalAlignment::top, resize); } } diff --git a/src/sago/SagoTextBox.hpp b/src/sago/SagoTextBox.hpp index 6882006..4532a4a 100644 --- a/src/sago/SagoTextBox.hpp +++ b/src/sago/SagoTextBox.hpp @@ -57,7 +57,7 @@ public: */ void SetMaxWidth(int width); const std::string& GetText() const; - void Draw(SDL_Renderer* target, int x, int y, SagoTextField::Alignment alignment = SagoTextField::Alignment::left); + void Draw(SDL_Renderer* target, int x, int y, SagoTextField::Alignment alignment = SagoTextField::Alignment::left, SagoLogicalResize* resize = nullptr); void UpdateCache(); private: void AppendLineToCache(const std::string& text); diff --git a/src/sago/SagoTextField.cpp b/src/sago/SagoTextField.cpp index 85e63fa..eef904a 100644 --- a/src/sago/SagoTextField.cpp +++ b/src/sago/SagoTextField.cpp @@ -193,7 +193,7 @@ void SagoTextField::GetRenderedSize(const char* text, int* w, int* h) { } } -void SagoTextField::Draw(SDL_Renderer* target, int x, int y, Alignment alignment, VerticalAlignment verticalAlignment) { +void SagoTextField::Draw(SDL_Renderer* target, int x, int y, Alignment alignment, VerticalAlignment verticalAlignment, SagoLogicalResize* resize) { if (data->text.empty()) { return; } @@ -224,8 +224,14 @@ void SagoTextField::Draw(SDL_Renderer* target, int x, int y, Alignment alignment int outlineTexH = 0; SDL_QueryTexture(data->outlineTexture, NULL, NULL, &outlineTexW, &outlineTexH); SDL_Rect dstrectOutline = { x-(data->outline), y-(data->outline), outlineTexW, outlineTexH }; + if (resize) { + resize->LogicalToPhysical(dstrectOutline); + } SDL_RenderCopy(target, data->outlineTexture, NULL, &dstrectOutline); } + if (resize) { + resize->LogicalToPhysical(dstrect); + } SDL_RenderCopy(target, data->texture, NULL, &dstrect); } diff --git a/src/sago/SagoTextField.hpp b/src/sago/SagoTextField.hpp index c97846f..9230ce5 100644 --- a/src/sago/SagoTextField.hpp +++ b/src/sago/SagoTextField.hpp @@ -26,6 +26,7 @@ SOFTWARE. #define SAGOTEXTFIELD_HPP #include "SagoDataHolder.hpp" +#include "SagoLogicalResize.hpp" namespace sago { @@ -109,7 +110,7 @@ public: enum class Alignment { left = 0, right=1, center = 2 }; enum class VerticalAlignment { top = 0, center = 1, bottom = 2}; - void Draw(SDL_Renderer* target, int x, int y, Alignment alignment = Alignment::left, VerticalAlignment verticalAlignment = VerticalAlignment::top); + void Draw(SDL_Renderer* target, int x, int y, Alignment alignment = Alignment::left, VerticalAlignment verticalAlignment = VerticalAlignment::top, SagoLogicalResize* resize = nullptr); /** * Updates the cache.