From 6a2490c9360e7bf73af1ef20badac9564c12c6fa Mon Sep 17 00:00:00 2001 From: Simon Weis Date: Wed, 1 Nov 2023 07:44:08 +0100 Subject: [PATCH 1/2] Add Outline functionality to all Drawables. Fix Rotation orientation --- impl/jamtemplate/common/animation.cpp | 49 ++++++++++++++----- impl/jamtemplate/common/animation.hpp | 3 ++ impl/jamtemplate/common/bar.cpp | 34 +++++++++++++ impl/jamtemplate/common/bar.hpp | 3 ++ .../common/color/palette_builder.cpp | 4 ++ .../common/graphics/drawable_impl.cpp | 32 +++++++++++- .../common/graphics/drawable_impl.hpp | 10 ++++ .../common/graphics/drawable_interface.hpp | 35 ++++++++++--- .../common/graphics/outline_impl.cpp | 35 +++++++++++++ .../common/graphics/outline_impl.hpp | 34 +++++++++++++ .../common/graphics/shadow_impl.cpp | 11 +++-- .../jamtemplate/common/tilemap/tile_layer.cpp | 13 +++++ .../jamtemplate/common/tilemap/tile_layer.hpp | 4 ++ impl/jamtemplate/sdl/line.cpp | 44 +++++++++++++++-- impl/jamtemplate/sdl/line.hpp | 19 ++++++- impl/jamtemplate/sdl/shape.cpp | 26 ++++++++-- impl/jamtemplate/sdl/shape.hpp | 7 ++- impl/jamtemplate/sdl/sprite.cpp | 24 +++++++-- impl/jamtemplate/sdl/sprite.hpp | 7 ++- impl/jamtemplate/sdl/text.cpp | 35 +++++++++---- impl/jamtemplate/sdl/text.hpp | 12 ++--- impl/jamtemplate/sfml/line.cpp | 26 +++++++++- impl/jamtemplate/sfml/line.hpp | 12 +++++ impl/jamtemplate/sfml/render_window_lib.cpp | 5 +- impl/jamtemplate/sfml/shape.cpp | 20 +++++++- impl/jamtemplate/sfml/shape.hpp | 4 +- impl/jamtemplate/sfml/sprite.cpp | 23 ++++++++- impl/jamtemplate/sfml/sprite.hpp | 2 +- impl/jamtemplate/sfml/text.cpp | 39 +++++++++++---- impl/jamtemplate/sfml/text.hpp | 2 +- .../demo/move_cam/state_move_cam.cpp | 21 +++++--- .../demo/move_cam/state_move_cam.hpp | 3 +- .../common/graphics/drawables_helper_test.cpp | 1 - .../common/graphics/drawables_impl_test.cpp | 38 +++++++++++++- .../jt_test/common/graphics/line_test.cpp | 25 ++++++++++ test/unit/jt_test/common/text_test.cpp | 3 +- test/unit/jt_test/mocks/mock_drawable.hpp | 7 ++- 37 files changed, 582 insertions(+), 90 deletions(-) create mode 100644 impl/jamtemplate/common/graphics/outline_impl.cpp create mode 100644 impl/jamtemplate/common/graphics/outline_impl.hpp create mode 100644 test/unit/jt_test/common/graphics/line_test.cpp diff --git a/impl/jamtemplate/common/animation.cpp b/impl/jamtemplate/common/animation.cpp index 1e58c694..1f7d284a 100644 --- a/impl/jamtemplate/common/animation.cpp +++ b/impl/jamtemplate/common/animation.cpp @@ -64,7 +64,7 @@ void jt::Animation::add(std::string const& fileName, std::string const& animName for (auto const idx : frameIndices) { jt::Recti const rect { static_cast(idx * imageSize.x), 0, static_cast(imageSize.x), static_cast(imageSize.y) }; - Sprite::Sptr sptr = std::make_shared(fileName, rect, textureManager); + auto sptr = std::make_shared(fileName, rect, textureManager); m_frames[animName].push_back(sptr); } m_time[animName] = frameTimesInSeconds; @@ -226,12 +226,14 @@ jt::Color jt::Animation::getColor() const } void jt::Animation::setPosition(jt::Vector2f const& pos) { m_position = pos; } + jt::Vector2f jt::Animation::getPosition() const { return m_position; } jt::Rectf jt::Animation::getGlobalBounds() const { return getCurrentSprite(m_frames, m_currentAnimName, m_currentIdx)->getGlobalBounds(); } + jt::Rectf jt::Animation::getLocalBounds() const { return getCurrentSprite(m_frames, m_currentAnimName, m_currentIdx)->getLocalBounds(); @@ -245,6 +247,7 @@ void jt::Animation::setScale(jt::Vector2f const& scale) } } } + jt::Vector2f jt::Animation::getScale() const { return getCurrentSprite(m_frames, m_currentAnimName, m_currentIdx)->getScale(); @@ -259,12 +262,22 @@ void jt::Animation::setOriginInternal(jt::Vector2f const& origin) } } -void jt::Animation::setShadow(jt::Color const& col, jt::Vector2f const& offset) +void jt::Animation::setOutline(jt::Color const& color, int width) { - DrawableImpl::setShadow(col, offset); + DrawableImpl::setOutline(color, width); for (auto const& kvp : m_frames) { - for (auto const& sptr : kvp.second) { - sptr->setShadow(col, offset); + for (auto const& sprite : kvp.second) { + sprite->setOutline(color, width); + } + } +} + +void jt::Animation::setShadow(jt::Color const& color, jt::Vector2f const& offset) +{ + DrawableImpl::setShadow(color, offset); + for (auto const& kvp : m_frames) { + for (auto const& sprite : kvp.second) { + sprite->setShadow(color, offset); } } } @@ -273,14 +286,16 @@ void jt::Animation::setShadowActive(bool active) { DrawableImpl::setShadowActive(active); for (auto const& kvp : m_frames) { - for (auto const& sptr : kvp.second) { - sptr->setShadowActive(active); + for (auto const& sprite : kvp.second) { + sprite->setShadowActive(active); } } } void jt::Animation::doDrawShadow(std::shared_ptr const /*sptr*/) const { } +void jt::Animation::doDrawOutline(std::shared_ptr const /*sptr*/) const { } + void jt::Animation::doDraw(std::shared_ptr const sptr) const { if (!m_isValid) { @@ -297,8 +312,8 @@ void jt::Animation::doDrawFlash(std::shared_ptr const /*s void jt::Animation::doFlashImpl(float t, jt::Color col) { for (auto& kvp : m_frames) { - for (auto& spr : kvp.second) { - spr->flash(t, col); + for (auto& sprite : kvp.second) { + sprite->flash(t, col); } } } @@ -333,8 +348,8 @@ void jt::Animation::doUpdate(float elapsed) // update all sprites for (auto& kvp : m_frames) { - for (auto& spr : kvp.second) { - spr->update(elapsed); + for (auto& sprite : kvp.second) { + sprite->update(elapsed); } } } @@ -342,23 +357,27 @@ void jt::Animation::doUpdate(float elapsed) void jt::Animation::doRotate(float rot) { for (auto& kvp : m_frames) { - for (auto& spr : kvp.second) { - spr->setRotation(rot); + for (auto& sprite : kvp.second) { + sprite->setRotation(rot); } } } + float jt::Animation::getCurrentAnimationSingleFrameTime() const { return m_time.at(m_currentAnimName).at(m_currentIdx); } + float jt::Animation::getCurrentAnimTotalTime() const { return getCurrentAnimationSingleFrameTime() * getNumberOfFramesInCurrentAnimation(); } + std::size_t jt::Animation::getNumberOfFramesInCurrentAnimation() const { return m_frames.at(m_currentAnimName).size(); } + std::string jt::Animation::getCurrentAnimationName() const { return m_currentAnimName; } bool jt::Animation::getIsLooping() const @@ -368,6 +387,7 @@ bool jt::Animation::getIsLooping() const } return m_isLooping.at(m_currentAnimName); } + void jt::Animation::setLooping(std::string const& animName, bool isLooping) { if (!hasAnimation(animName)) { @@ -375,6 +395,7 @@ void jt::Animation::setLooping(std::string const& animName, bool isLooping) } m_isLooping[animName] = isLooping; } + std::size_t jt::Animation::getCurrentAnimationFrameIndex() const { return m_currentIdx; } void jt::Animation::setFrameTimes( @@ -389,5 +410,7 @@ void jt::Animation::setFrameTimes( } m_time[animationName] = frameTimes; } + void jt::Animation::setAnimationSpeedFactor(float factor) { m_animationplaybackSpeed = factor; } + float jt::Animation::getAnimationSpeedFactor() const { return m_animationplaybackSpeed; } diff --git a/impl/jamtemplate/common/animation.hpp b/impl/jamtemplate/common/animation.hpp index c4a44494..538adf5f 100644 --- a/impl/jamtemplate/common/animation.hpp +++ b/impl/jamtemplate/common/animation.hpp @@ -108,6 +108,8 @@ class Animation : public DrawableImpl { void setShadowActive(bool active) override; void setShadow(jt::Color const& color, jt::Vector2f const& offset) override; + void setOutline(jt::Color const& color, int width) override; + /// Get the frame time for one single frame in the current animation /// /// \return the time set in add for the currently playing animation @@ -158,6 +160,7 @@ class Animation : public DrawableImpl { float m_animationplaybackSpeed { 1.0f }; void doDrawShadow(std::shared_ptr const sptr) const override; + void doDrawOutline(std::shared_ptr const sptr) const override; void doDraw(std::shared_ptr const sptr) const override; void doDrawFlash(std::shared_ptr const /*sptr*/) const override; diff --git a/impl/jamtemplate/common/bar.cpp b/impl/jamtemplate/common/bar.cpp index f2c1b185..3e519b66 100644 --- a/impl/jamtemplate/common/bar.cpp +++ b/impl/jamtemplate/common/bar.cpp @@ -31,6 +31,7 @@ jt::Bar::Bar( } void jt::Bar::setFrontColor(jt::Color const& col) { m_shapeProgress->setColor(col); } + void jt::Bar::setBackColor(jt::Color const& col) { m_shapeFull->setColor(col); } jt::Color jt::Bar::getBackColor() const { return m_shapeFull->getColor(); } @@ -66,6 +67,7 @@ void jt::Bar::doDraw(std::shared_ptr const sptr) const } void jt::Bar::doDrawFlash(std::shared_ptr const /*sptr*/) const { } + void jt::Bar::doDrawShadow(std::shared_ptr const sptr) const { jt::Vector2f const oldPos = m_shapeFull->getPosition(); @@ -73,12 +75,36 @@ void jt::Bar::doDrawShadow(std::shared_ptr const sptr) co m_shapeFull->setPosition(oldPos + getShadowOffset()); m_shapeFull->setColor(getShadowColor()); + m_shapeFull->update(0.0f); m_shapeFull->draw(sptr); m_shapeFull->setPosition(oldPos); m_shapeFull->setColor(oldCol); } +void jt::Bar::doDrawOutline(std::shared_ptr const sptr) const +{ + jt::Vector2f const oldPos = m_shapeFull->getPosition(); + jt::Color const oldCol = m_shapeFull->getColor(); + + m_shapeFull->setColor(getOutlineColor()); + + auto const maxWidth = getOutlineWidth(); + for (auto currentWidth = 1; currentWidth != maxWidth + 1; ++currentWidth) { + for (auto i = -currentWidth; i != currentWidth; ++i) { + for (auto j = -currentWidth; j != currentWidth; ++j) { + m_shapeFull->setPosition( + oldPos + jt::Vector2f { static_cast(i), static_cast(j) }); + m_shapeFull->update(0.0f); + m_shapeFull->draw(sptr); + } + } + } + + m_shapeFull->setPosition(oldPos); + m_shapeFull->setColor(oldCol); +} + void jt::Bar::doUpdate(float elapsed) { float const value = static_cast(m_valueCurrent) / m_valueMax; @@ -101,6 +127,7 @@ void jt::Bar::doUpdate(float elapsed) void jt::Bar::doRotate(float /*rot*/) { } void jt::Bar::setColor(jt::Color const& col) { setFrontColor(col); } + jt::Color jt::Bar::getColor() const { return m_shapeProgress->getColor(); } void jt::Bar::setPosition(jt::Vector2f const& pos) { m_position = pos; } @@ -108,6 +135,7 @@ void jt::Bar::setPosition(jt::Vector2f const& pos) { m_position = pos; } jt::Vector2f jt::Bar::getPosition() const { return m_position; } jt::Rectf jt::Bar::getGlobalBounds() const { return m_shapeFull->getGlobalBounds(); } + jt::Rectf jt::Bar::getLocalBounds() const { return m_shapeFull->getLocalBounds(); } void jt::Bar::setScale(jt::Vector2f const& scale) @@ -129,3 +157,9 @@ void jt::Bar::setShadow(jt::Color const& color, jt::Vector2f const& offset) m_shapeFull->setShadow(color, offset); DrawableImpl::setShadow(color, offset); } + +void jt::Bar::setOutline(jt::Color const& color, int width) +{ + m_shapeFull->setOutline(color, width); + DrawableImpl::setOutline(color, width); +} diff --git a/impl/jamtemplate/common/bar.hpp b/impl/jamtemplate/common/bar.hpp index 260dd6a0..d6621fe8 100644 --- a/impl/jamtemplate/common/bar.hpp +++ b/impl/jamtemplate/common/bar.hpp @@ -64,6 +64,7 @@ class Bar : public jt::DrawableImpl { void setShadowActive(bool active) override; void setShadow(jt::Color const& color, jt::Vector2f const& offset) override; + void setOutline(jt::Color const& color, int width) override; private: float m_valueMax; @@ -84,6 +85,8 @@ class Bar : public jt::DrawableImpl { virtual void doDrawFlash(std::shared_ptr const sptr) const override; virtual void doDrawShadow(std::shared_ptr const sptr) const override; + void doDrawOutline(std::shared_ptr const sptr) const override; + // overwrite this method: // things to take care of: // - make sure flash object and normal object are at the same position diff --git a/impl/jamtemplate/common/color/palette_builder.cpp b/impl/jamtemplate/common/color/palette_builder.cpp index 9be63949..44f4ed29 100644 --- a/impl/jamtemplate/common/color/palette_builder.cpp +++ b/impl/jamtemplate/common/color/palette_builder.cpp @@ -62,6 +62,7 @@ PaletteBuilder& PaletteBuilder::addGradientH( m_colors.insert(m_colors.end(), colors.cbegin(), colors.cend()); return *this; } + PaletteBuilder& PaletteBuilder::addGradientS( float h, float smin, float smax, float v, std::size_t steps) { @@ -74,6 +75,7 @@ PaletteBuilder& PaletteBuilder::addGradientS( m_colors.insert(m_colors.end(), colors.cbegin(), colors.cend()); return *this; } + PaletteBuilder& PaletteBuilder::addGradientV( float h, float s, float vmin, float vmax, std::size_t steps) { @@ -87,6 +89,7 @@ PaletteBuilder& PaletteBuilder::addGradientV( m_colors.insert(m_colors.end(), colors.cbegin(), colors.cend()); return *this; } + PaletteBuilder& PaletteBuilder::addColor(jt::Color const& col) { m_colors.push_back(col); @@ -116,6 +119,7 @@ PaletteBuilder& PaletteBuilder::makeUnique() SystemHelper::remove_duplicates(m_colors.begin(), m_colors.end()), m_colors.end()); return *this; } + PaletteBuilder& PaletteBuilder::addColorsFromAseprite(std::string const& filepath) { aselib::AsepriteData ase { filepath }; diff --git a/impl/jamtemplate/common/graphics/drawable_impl.cpp b/impl/jamtemplate/common/graphics/drawable_impl.cpp index 51e7f509..9f43ab79 100644 --- a/impl/jamtemplate/common/graphics/drawable_impl.cpp +++ b/impl/jamtemplate/common/graphics/drawable_impl.cpp @@ -24,6 +24,7 @@ void jt::DrawableImpl::draw(std::shared_ptr sptr) const if (allowDrawFromFlicker()) { drawShadow(sptr); + drawOutline(sptr); doDraw(sptr); drawFlash(sptr); } @@ -49,12 +50,15 @@ void jt::DrawableImpl::update(float elapsed) } jt::Vector2f jt::DrawableImpl::getOffset() const { return m_offset; } + void jt::DrawableImpl::setOffset(jt::Vector2f const& offset) { m_offset = offset; m_offsetMode = jt::OffsetMode::MANUAL; } + jt::OffsetMode jt::DrawableImpl::getOffsetMode() const { return m_offsetMode; } + void jt::DrawableImpl::setOffset(jt::OffsetMode offset) { m_offsetMode = offset; @@ -84,15 +88,19 @@ void jt::DrawableImpl::setOrigin(jt::OriginMode origin) } setOriginInternal(m_origin); } + jt::Vector2f jt::DrawableImpl::getOrigin() const { return m_origin; } void jt::DrawableImpl::setRotation(float rot) { doSetRotation(rot); } + float jt::DrawableImpl::getRotation() const { return doGetRotation(); } void jt::DrawableImpl::setShadowActive(bool active) { doSetShadowActive(active); } bool jt::DrawableImpl::getShadowActive() const { return doGetShadowActive(); } + jt::Color jt::DrawableImpl::getShadowColor() const { return doGetShadowColor(); } + jt::Vector2f jt::DrawableImpl::getShadowOffset() const { return doGetShadowOffset(); } void jt::DrawableImpl::setIgnoreCamMovement(bool ignore) @@ -108,6 +116,8 @@ void jt::DrawableImpl::setShadow(jt::Color const& col, jt::Vector2f const& offse doSetShadow(col, offset); } +void jt::DrawableImpl::setOutline(jt::Color const& col, int width) { doSetOutline(col, width); } + jt::Vector2f jt::DrawableImpl::getShakeOffset() const { return doGetShakeOffset(); } jt::Vector2f jt::DrawableImpl::getCamOffset() const @@ -124,10 +134,12 @@ jt::Vector2f jt::DrawableImpl::getCamOffset() const bool jt::DrawableImpl::getIgnoreCamMovement() const { return m_ignoreCamMovement; } -void jt::DrawableImpl::setCamOffset(const jt::Vector2f& v) { m_CamOffset = v; } +void jt::DrawableImpl::setCamOffset(jt::Vector2f const& v) { m_CamOffset = v; } + jt::Vector2f jt::DrawableImpl::getStaticCamOffset() { return m_CamOffset; } void jt::DrawableImpl::setFlashColor(jt::Color const& col) { doSetFlashColor(col); } + jt::Color jt::DrawableImpl::getFlashColor() const { return doGetFlashColor(); } void jt::DrawableImpl::setScreenSizeHint(jt::Vector2f const& hint) { m_screenSizeHint = hint; } @@ -153,7 +165,9 @@ bool jt::DrawableImpl::isVisible() const } return true; } + void jt::DrawableImpl::setBlendMode(jt::BlendMode mode) { m_blendMode = mode; } + jt::BlendMode jt::DrawableImpl::getBlendMode() const { return m_blendMode; } jt::Vector2f jt::DrawableImpl::getScreenPosition() const @@ -161,13 +175,29 @@ jt::Vector2f jt::DrawableImpl::getScreenPosition() const auto const camOffset = getStaticCamOffset(); return getPosition() + camOffset * m_camMovementFactor; } + jt::Vector2f jt::DrawableImpl::getScreenSizeHint() const { return m_screenSizeHint; } + void jt::DrawableImpl::setCamMovementFactor(float factor) { m_camMovementFactor = factor; bool const ignoreCamMovement = m_camMovementFactor != 1.0f; m_ignoreCamMovement = ignoreCamMovement; } + float jt::DrawableImpl::getCamMovementFactor() const { return m_camMovementFactor; } + void jt::DrawableImpl::setZ(int z) { m_z = z; } + int jt::DrawableImpl::getZ() const { return m_z; } + +bool jt::DrawableImpl::getOutlineActive() const { return getOutlineWidth() != 0; } + +jt::Color jt::DrawableImpl::getOutlineColor() const { return doGetOutlineColor(); } + +int jt::DrawableImpl::getOutlineWidth() const { return doGetOutlineWidth(); } + +std::vector jt::DrawableImpl::getOutlineOffsets() const +{ + return doGetOutlineOffsets(); +} diff --git a/impl/jamtemplate/common/graphics/drawable_impl.hpp b/impl/jamtemplate/common/graphics/drawable_impl.hpp index 0145a879..f128eb70 100644 --- a/impl/jamtemplate/common/graphics/drawable_impl.hpp +++ b/impl/jamtemplate/common/graphics/drawable_impl.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ class DrawableImpl : // implementation of flash, rotation, shadow and shake functionality via mix-in private jt::FlashImpl, private jt::FlickerImpl, + private jt::OutlineImpl, private jt::RotationImpl, private jt::ShadowImpl, private jt::ShakeImpl { @@ -55,6 +57,13 @@ class DrawableImpl : void setFlashColor(Color const& col) override; Color getFlashColor() const override; + bool getOutlineActive() const override; + void setOutline(jt::Color const& col, int width) override; + Color getOutlineColor() const override; + int getOutlineWidth() const override; + + std::vector getOutlineOffsets() const; + void setShadow(jt::Color const& col, jt::Vector2f const& offset) override; void setShadowActive(bool active) override; @@ -94,6 +103,7 @@ class DrawableImpl : jt::Vector2f m_screenSizeHint { 0.0f, 0.0f }; virtual void setOriginInternal(jt::Vector2f const& /*origin*/) { } + float m_camMovementFactor { 1.0f }; jt::OriginMode m_originMode { jt::OriginMode::MANUAL }; diff --git a/impl/jamtemplate/common/graphics/drawable_interface.hpp b/impl/jamtemplate/common/graphics/drawable_interface.hpp index cabd8926..12e31ab9 100644 --- a/impl/jamtemplate/common/graphics/drawable_interface.hpp +++ b/impl/jamtemplate/common/graphics/drawable_interface.hpp @@ -115,6 +115,9 @@ class DrawableInterface { virtual void setOrigin(jt::OriginMode origin) = 0; /// Get the originMode + /// + /// The originMode is used for rotation + /// /// \return the originMode virtual jt::OriginMode getOriginMode() const = 0; @@ -134,17 +137,23 @@ class DrawableInterface { /// \return the offset virtual jt::Vector2f getOffset() const = 0; - /// Set the rotation angle of the drawable (in degree) + /// Set the rotation angle of the drawable (in degree). + /// Rotation is done counter-clockwise. /// - /// Rotation will happen around the origin + /// Rotation will happen around the origin. /// /// \param angleInDegree the rotation virtual void setRotation(float angleInDegree) = 0; - /// Get the rotation angle of the drawable (in degree) + /// Get the rotation angle of the drawable (in degree). + /// Rotation is done counter-clockwise. /// \return the rotation angle virtual float getRotation() const = 0; + /// Set the outline width of the drawable + /// \param width outline width in pixel + virtual void setOutline(jt::Color const& col, int width) = 0; + /// Set the shadow active status of the of the drawable /// \param active virtual void setShadowActive(bool active) = 0; @@ -153,14 +162,26 @@ class DrawableInterface { /// \return the shadow active status virtual bool getShadowActive() const = 0; - /// Get the shadow color of the drawable - /// \return the color + /// Get the shadow color + /// \return the shadow color virtual jt::Color getShadowColor() const = 0; /// Get the shadow offset /// \return the offset in pixel virtual jt::Vector2f getShadowOffset() const = 0; + /// Get the outline active status of the drawable + /// \return the outline active status + virtual bool getOutlineActive() const = 0; + + /// Get the outline color + /// \return the outline color + virtual jt::Color getOutlineColor() const = 0; + + /// Get the outline width + /// \return the outline width in pixel + virtual int getOutlineWidth() const = 0; + /// Set the shadow /// \param shadowColor the shadow color /// \param shadowOffset the shadow shadowOffset @@ -216,9 +237,9 @@ class DrawableInterface { virtual ~DrawableInterface() = default; // no copy, no move. Avoid slicing. - DrawableInterface(const DrawableInterface&) = delete; + DrawableInterface(DrawableInterface const&) = delete; DrawableInterface(DrawableInterface&&) = delete; - DrawableInterface& operator=(const DrawableInterface&) = delete; + DrawableInterface& operator=(DrawableInterface const&) = delete; DrawableInterface& operator=(DrawableInterface&&) = delete; protected: diff --git a/impl/jamtemplate/common/graphics/outline_impl.cpp b/impl/jamtemplate/common/graphics/outline_impl.cpp new file mode 100644 index 00000000..81a13b44 --- /dev/null +++ b/impl/jamtemplate/common/graphics/outline_impl.cpp @@ -0,0 +1,35 @@ +#include "outline_impl.hpp" + +void jt::OutlineImpl::doSetOutline(Color const& col, int width) +{ + m_outlineActive = width != 0; + m_outlineColor = col; + m_outlineWidthInPixel = width; + + m_outlineOffsets.clear(); + auto const maxWidth = doGetOutlineWidth(); + for (auto currentWidth = 1; currentWidth != maxWidth + 1; ++currentWidth) { + for (auto i = -currentWidth; i != currentWidth + 1; ++i) { + for (auto j = -currentWidth; j != currentWidth + 1; ++j) { + m_outlineOffsets.emplace_back( + jt::Vector2f { static_cast(i), static_cast(j) }); + } + } + } +} + +void jt::OutlineImpl::drawOutline(std::shared_ptr const sptr) const +{ + if (!sptr) { + return; + } + if (m_outlineActive) { + doDrawOutline(sptr); + } +} + +jt::Color jt::OutlineImpl::doGetOutlineColor() const { return m_outlineColor; } + +int jt::OutlineImpl::doGetOutlineWidth() const { return m_outlineWidthInPixel; } + +std::vector jt::OutlineImpl::doGetOutlineOffsets() const { return m_outlineOffsets; } diff --git a/impl/jamtemplate/common/graphics/outline_impl.hpp b/impl/jamtemplate/common/graphics/outline_impl.hpp new file mode 100644 index 00000000..778e207e --- /dev/null +++ b/impl/jamtemplate/common/graphics/outline_impl.hpp @@ -0,0 +1,34 @@ +#ifndef JAMTEMPLATE_OUTLINE_IMPL_HPP +#define JAMTEMPLATE_OUTLINE_IMPL_HPP + +#include +#include +#include +#include + +namespace jt { + +class OutlineImpl { +public: + void doSetOutline(jt::Color const& col, int width); + + void drawOutline(std::shared_ptr const sptr) const; + + jt::Color doGetOutlineColor() const; + int doGetOutlineWidth() const; + + std::vector doGetOutlineOffsets() const; + +private: + bool m_outlineActive { false }; + int m_outlineWidthInPixel { 0 }; + jt::Color m_outlineColor { jt::colors::Black }; + + std::vector m_outlineOffsets {}; + + virtual void doDrawOutline(std::shared_ptr const sptr) const = 0; +}; + +} // namespace jt + +#endif // JAMTEMPLATE_OUTLINE_IMPL_HPP diff --git a/impl/jamtemplate/common/graphics/shadow_impl.cpp b/impl/jamtemplate/common/graphics/shadow_impl.cpp index 67cfce30..a9d570a3 100644 --- a/impl/jamtemplate/common/graphics/shadow_impl.cpp +++ b/impl/jamtemplate/common/graphics/shadow_impl.cpp @@ -9,15 +9,18 @@ void jt::ShadowImpl::doSetShadow(jt::Color const& col, jt::Vector2f const& offse void jt::ShadowImpl::drawShadow(std::shared_ptr const sptr) const { - if (sptr) { - if (m_shadowActive) { - doDrawShadow(sptr); - } + if (!sptr) { + return; + } + if (m_shadowActive) { + doDrawShadow(sptr); } } void jt::ShadowImpl::doSetShadowActive(bool active) { m_shadowActive = active; } bool jt::ShadowImpl::doGetShadowActive() const { return m_shadowActive; } + jt::Color jt::ShadowImpl::doGetShadowColor() const { return m_shadowColor; } + jt::Vector2f jt::ShadowImpl::doGetShadowOffset() const { return m_shadowOffset; } diff --git a/impl/jamtemplate/common/tilemap/tile_layer.cpp b/impl/jamtemplate/common/tilemap/tile_layer.cpp index 4c07d2e4..7e272360 100644 --- a/impl/jamtemplate/common/tilemap/tile_layer.cpp +++ b/impl/jamtemplate/common/tilemap/tile_layer.cpp @@ -68,9 +68,17 @@ void jt::tilemap::TileLayer::doDrawFlash( std::shared_ptr const /*sptr*/) const { } + void jt::tilemap::TileLayer::doDrawShadow( std::shared_ptr const /*sptr*/) const { + // Nothing to do +} + +void jt::tilemap::TileLayer::doDrawOutline( + std::shared_ptr const /*sptr*/) const +{ + // Nothing to do } void jt::tilemap::TileLayer::doUpdate(float /*elapsed*/) { } @@ -82,9 +90,11 @@ void jt::tilemap::TileLayer::setColor(jt::Color const& col) } m_color = col; } + jt::Color jt::tilemap::TileLayer::getColor() const { return m_color; } void jt::tilemap::TileLayer::setPosition(jt::Vector2f const& pos) { m_position = pos; } + jt::Vector2f jt::tilemap::TileLayer::getPosition() const { return m_position; } jt::Rectf jt::tilemap::TileLayer::getGlobalBounds() const @@ -92,6 +102,7 @@ jt::Rectf jt::tilemap::TileLayer::getGlobalBounds() const return jt::Rectf { getPosition().x, getPosition().y, std::numeric_limits::max(), std::numeric_limits::max() }; } + jt::Rectf jt::tilemap::TileLayer::getLocalBounds() const { return jt::Rectf { getPosition().x, getPosition().y, std::numeric_limits::max(), @@ -99,6 +110,7 @@ jt::Rectf jt::tilemap::TileLayer::getLocalBounds() const } void jt::tilemap::TileLayer::setScale(jt::Vector2f const& scale) { m_scale = scale; } + jt::Vector2f jt::tilemap::TileLayer::getScale() const { return m_scale; } void jt::tilemap::TileLayer::setOriginInternal(jt::Vector2f const& origin) @@ -114,6 +126,7 @@ void jt::tilemap::TileLayer::doRotate(float rot) ts->setRotation(rot); } } + void jt::tilemap::TileLayer::setColorFunction( std::function colorFunc) { diff --git a/impl/jamtemplate/common/tilemap/tile_layer.hpp b/impl/jamtemplate/common/tilemap/tile_layer.hpp index 0f498b4c..1bd71635 100644 --- a/impl/jamtemplate/common/tilemap/tile_layer.hpp +++ b/impl/jamtemplate/common/tilemap/tile_layer.hpp @@ -37,6 +37,10 @@ class TileLayer : public DrawableImpl { void doDrawFlash(std::shared_ptr sptr) const override; void doDrawShadow(std::shared_ptr sptr) const override; +private: + void doDrawOutline(std::shared_ptr const sptr) const override; + +public: void doUpdate(float elapsed) override; void setColor(jt::Color const& col) override; diff --git a/impl/jamtemplate/sdl/line.cpp b/impl/jamtemplate/sdl/line.cpp index b11473f4..54aaa87a 100644 --- a/impl/jamtemplate/sdl/line.cpp +++ b/impl/jamtemplate/sdl/line.cpp @@ -2,12 +2,21 @@ #include #include +jt::Line::Line() + : Line { jt::Vector2f { 0.0f, 0.0f } } +{ +} + jt::Line::Line(jt::Vector2f lineVector) : m_lineVector { std::move(lineVector) } , m_color { jt::colors::White } { } +void jt::Line::setLineVector(jt::Vector2f const& lineVector) { m_lineVector = lineVector; } + +jt::Vector2f jt::Line::getLineVector() const { return m_lineVector; } + void jt::Line::doUpdate(float /*elapsed*/) { } void jt::Line::doDraw(std::shared_ptr const sptr) const @@ -32,29 +41,55 @@ void jt::Line::doDrawFlash(std::shared_ptr const sptr) co static_cast(startPosition.y), static_cast(endPosition.x), static_cast(endPosition.y)); } + void jt::Line::doDrawShadow(std::shared_ptr const sptr) const { - auto const startPosition = getPosition() + getShakeOffset() + getOffset() + getCamOffset() + getShadowOffset(); auto const endPosition = startPosition + m_lineVector + getShadowOffset(); SDL_SetRenderDrawColor( sptr.get(), getShadowColor().r, getShadowColor().g, getShadowColor().b, getShadowColor().a); - SDL_RenderDrawLine(sptr.get(), static_cast(startPosition.x), - static_cast(startPosition.y), static_cast(endPosition.x), - static_cast(endPosition.y)); + SDL_RenderDrawLine(sptr.get(), + // clang-format off + static_cast(startPosition.x), static_cast(startPosition.y), + static_cast(endPosition.x), static_cast(endPosition.y) + // clang-format on + ); +} + +void jt::Line::doDrawOutline(std::shared_ptr const sptr) const +{ + auto const startPosition + = getPosition() + getShakeOffset() + getOffset() + getCamOffset() + getShadowOffset(); + auto const endPosition = startPosition + + jt::Vector2f { m_lineVector.x * m_scale.x, m_lineVector.y * m_scale.y } + + getShadowOffset(); + + SDL_SetRenderDrawColor(sptr.get(), getOutlineColor().r, getOutlineColor().g, + getOutlineColor().b, getOutlineColor().a); + for (auto const outlineOffset : getOutlineOffsets()) { + SDL_RenderDrawLine(sptr.get(), + // clang-format off + static_cast(startPosition.x + outlineOffset.x), static_cast(startPosition.y + outlineOffset.y), + static_cast(endPosition.x + outlineOffset.x), static_cast(endPosition.y + outlineOffset.y) + // clang-format on + ); + } } void jt::Line::doRotate(float d) { m_lineVector = jt::MathHelper::rotateBy(m_lineVector, d); } void jt::Line::setColor(jt::Color const& col) { m_color = col; } + jt::Color jt::Line::getColor() const { return m_color; } void jt::Line::setPosition(jt::Vector2f const& pos) { m_position = pos; } + jt::Vector2f jt::Line::getPosition() const { return m_position; } jt::Rectf jt::Line::getGlobalBounds() const { return jt::Rectf {}; } + jt::Rectf jt::Line::getLocalBounds() const { return jt::Rectf {}; } void jt::Line::setScale(jt::Vector2f const& scale) @@ -62,4 +97,5 @@ void jt::Line::setScale(jt::Vector2f const& scale) m_scale = scale; setOriginInternal(m_origin); } + jt::Vector2f jt::Line::getScale() const { return m_scale; } diff --git a/impl/jamtemplate/sdl/line.hpp b/impl/jamtemplate/sdl/line.hpp index 83fd2fd6..b375bdd1 100644 --- a/impl/jamtemplate/sdl/line.hpp +++ b/impl/jamtemplate/sdl/line.hpp @@ -9,7 +9,21 @@ namespace jt { class Line : public DrawableImplSdl { public: using Sptr = std::shared_ptr; - Line(jt::Vector2f lineVector); + + /// Constructor + Line(); + + /// Constructor + /// \param lineVector the vector from start to end of the line + explicit Line(jt::Vector2f lineVector); + + /// Set line Vector from start to end of the line + /// \param lineVector line vector + void setLineVector(jt::Vector2f const& lineVector); + + /// Get line Vector from start to end of the line + /// \return line vector + jt::Vector2f getLineVector() const; void setColor(jt::Color const& col) override; jt::Color getColor() const override; @@ -33,10 +47,11 @@ class Line : public DrawableImplSdl { void doDraw(std::shared_ptr const sptr) const override; void doDrawFlash(std::shared_ptr const sptr) const override; void doDrawShadow(std::shared_ptr const sptr) const override; + void doDrawOutline(std::shared_ptr const sptr) const override; void doUpdate(float elapsed) override; void doRotate(float d) override; }; } // namespace jt -#endif // QUASARRUSH_LINE_HPP +#endif // JAMTEMPLATE_LINE_HPP diff --git a/impl/jamtemplate/sdl/shape.cpp b/impl/jamtemplate/sdl/shape.cpp index b9222c6b..8627271b 100644 --- a/impl/jamtemplate/sdl/shape.cpp +++ b/impl/jamtemplate/sdl/shape.cpp @@ -24,9 +24,11 @@ void Shape::makeCircle(float radius, jt::TextureManagerInterface& textureManager } void Shape::setColor(jt::Color const& col) { m_color = col; } + jt::Color Shape::getColor() const { return m_color; } void Shape::setPosition(jt::Vector2f const& pos) { m_position = pos; } + jt::Vector2f Shape::getPosition() const { return m_position; } jt::Rectf Shape::getGlobalBounds() const @@ -35,6 +37,7 @@ jt::Rectf Shape::getGlobalBounds() const static_cast(m_sourceRect.width) * m_scale.x, static_cast(m_sourceRect.height) * m_scale.y }; } + jt::Rectf Shape::getLocalBounds() const { return jt::Rectf { m_position.x, m_position.y, @@ -47,6 +50,7 @@ void Shape::setScale(jt::Vector2f const& scale) m_scale = scale; setOriginInternal(m_origin); } + jt::Vector2f Shape::getScale() const { return m_scale; } void Shape::doDraw(std::shared_ptr const sptr) const @@ -57,7 +61,7 @@ void Shape::doDraw(std::shared_ptr const sptr) const static_cast(getOrigin().y * m_scale.y) }; SDL_SetRenderDrawBlendMode(sptr.get(), getSDLBlendMode()); setSDLColor(m_color); - SDL_RenderCopyEx(sptr.get(), m_text.get(), nullptr, &destRect, -getRotation(), &p, flip); + SDL_RenderCopyEx(sptr.get(), m_text.get(), nullptr, &destRect, getRotation(), &p, flip); } void Shape::doDrawFlash(std::shared_ptr const sptr) const @@ -68,7 +72,7 @@ void Shape::doDrawFlash(std::shared_ptr const sptr) const static_cast(getOrigin().y * m_scale.y) }; SDL_SetRenderDrawBlendMode(sptr.get(), SDL_BLENDMODE_BLEND); setSDLColor(getFlashColor()); - SDL_RenderCopyEx(sptr.get(), m_text.get(), nullptr, &destRect, -getRotation(), &p, flip); + SDL_RenderCopyEx(sptr.get(), m_text.get(), nullptr, &destRect, getRotation(), &p, flip); } void Shape::doDrawShadow(std::shared_ptr const sptr) const @@ -79,10 +83,25 @@ void Shape::doDrawShadow(std::shared_ptr const sptr) cons static_cast(getOrigin().y * m_scale.y) }; SDL_SetRenderDrawBlendMode(sptr.get(), SDL_BLENDMODE_BLEND); setSDLColor(getShadowColor()); - SDL_RenderCopyEx(sptr.get(), m_text.get(), nullptr, &destRect, -getRotation(), &p, flip); + SDL_RenderCopyEx(sptr.get(), m_text.get(), nullptr, &destRect, getRotation(), &p, flip); +} + +void Shape::doDrawOutline(std::shared_ptr const sptr) const +{ + setSDLColor(getOutlineColor()); + auto const flip = jt::getFlipFromScale(m_scale); + SDL_Point const p { static_cast(getOrigin().x * m_scale.x), + static_cast(getOrigin().y * m_scale.y) }; + SDL_SetRenderDrawBlendMode(sptr.get(), SDL_BLENDMODE_BLEND); + + for (auto const& outlineOffset : getOutlineOffsets()) { + SDL_Rect const destRect = getDestRect(outlineOffset); + SDL_RenderCopyEx(sptr.get(), m_text.get(), nullptr, &destRect, getRotation(), &p, flip); + } } void Shape::doUpdate(float /*elapsed*/) { } + void Shape::doRotate(float /*rot*/) { } SDL_Rect Shape::getDestRect(jt::Vector2f const& positionOffset) const @@ -100,4 +119,5 @@ void Shape::setSDLColor(jt::Color const& col) const SDL_SetTextureColorMod(m_text.get(), col.r, col.g, col.b); SDL_SetTextureAlphaMod(m_text.get(), col.a); } + } // namespace jt diff --git a/impl/jamtemplate/sdl/shape.hpp b/impl/jamtemplate/sdl/shape.hpp index bb319740..e55a2106 100644 --- a/impl/jamtemplate/sdl/shape.hpp +++ b/impl/jamtemplate/sdl/shape.hpp @@ -4,9 +4,9 @@ #include #include #include +#include #include #include -#include #include namespace jt { @@ -36,12 +36,11 @@ class Shape : public DrawableImplSdl { jt::Recti m_sourceRect { 0, 0, 0, 0 }; jt::Color m_color { jt::colors::White }; + void doDrawShadow(std::shared_ptr const sptr) const override; + void doDrawOutline(std::shared_ptr const sptr) const override; void doDraw(std::shared_ptr const sptr) const override; - void doDrawFlash(std::shared_ptr const sptr) const override; - void doDrawShadow(std::shared_ptr const sptr) const override; - void doUpdate(float /*elapsed*/) override; void doRotate(float /*rot*/) override; diff --git a/impl/jamtemplate/sdl/sprite.cpp b/impl/jamtemplate/sdl/sprite.cpp index 2516945a..3bed2574 100644 --- a/impl/jamtemplate/sdl/sprite.cpp +++ b/impl/jamtemplate/sdl/sprite.cpp @@ -50,9 +50,11 @@ void Sprite::fromTexture(std::shared_ptr txt) } void Sprite::setPosition(jt::Vector2f const& pos) { m_position = pos; } + jt::Vector2f Sprite::getPosition() const { return m_position; } void Sprite::setColor(jt::Color const& col) { m_color = col; } + jt::Color Sprite::getColor() const { return m_color; } jt::Rectf Sprite::getGlobalBounds() const @@ -72,6 +74,7 @@ void Sprite::setScale(jt::Vector2f const& scale) m_scale = scale; setOriginInternal(m_origin); } + jt::Vector2f Sprite::getScale() const { return m_scale; } jt::Color Sprite::getColorAtPixel(jt::Vector2u pixelPos) const @@ -111,7 +114,7 @@ void Sprite::doDraw(std::shared_ptr const sptr) const static_cast(getOrigin().y * m_scale.y) }; SDL_SetRenderDrawBlendMode(sptr.get(), SDL_BLENDMODE_BLEND); setSDLColor(m_color); - SDL_RenderCopyEx(sptr.get(), m_text.get(), &sourceRect, &destRect, -getRotation(), &p, flip); + SDL_RenderCopyEx(sptr.get(), m_text.get(), &sourceRect, &destRect, getRotation(), &p, flip); } void Sprite::doDrawShadow(std::shared_ptr const sptr) const @@ -123,7 +126,22 @@ void Sprite::doDrawShadow(std::shared_ptr const sptr) con static_cast(getOrigin().y * m_scale.y) }; SDL_SetRenderDrawBlendMode(sptr.get(), SDL_BLENDMODE_BLEND); setSDLColor(getShadowColor()); - SDL_RenderCopyEx(sptr.get(), m_text.get(), &sourceRect, &destRect, -getRotation(), &p, flip); + SDL_RenderCopyEx(sptr.get(), m_text.get(), &sourceRect, &destRect, getRotation(), &p, flip); +} + +void Sprite::doDrawOutline(std::shared_ptr const sptr) const +{ + SDL_Rect const sourceRect = getSourceRect(); + auto const flip = jt::getFlipFromScale(m_scale); + SDL_Point const p { static_cast(getOrigin().x * m_scale.x), + static_cast(getOrigin().y * m_scale.y) }; + setSDLColor(getOutlineColor()); + SDL_SetRenderDrawBlendMode(sptr.get(), SDL_BLENDMODE_BLEND); + for (auto const& outlineOffset : getOutlineOffsets()) { + SDL_Rect const destRect = getDestRect(outlineOffset); + + SDL_RenderCopyEx(sptr.get(), m_text.get(), &sourceRect, &destRect, getRotation(), &p, flip); + } } void Sprite::doDrawFlash(std::shared_ptr const sptr) const @@ -138,7 +156,7 @@ void Sprite::doDrawFlash(std::shared_ptr const sptr) cons m_textFlash.get(), getFlashColor().r, getFlashColor().g, getFlashColor().b); SDL_SetTextureAlphaMod(m_textFlash.get(), getFlashColor().a); SDL_RenderCopyEx( - sptr.get(), m_textFlash.get(), &sourceRect, &destRect, -getRotation(), &p, flip); + sptr.get(), m_textFlash.get(), &sourceRect, &destRect, getRotation(), &p, flip); } void Sprite::doRotate(float /*rot*/) { } diff --git a/impl/jamtemplate/sdl/sprite.hpp b/impl/jamtemplate/sdl/sprite.hpp index a8b695e3..06f68b94 100644 --- a/impl/jamtemplate/sdl/sprite.hpp +++ b/impl/jamtemplate/sdl/sprite.hpp @@ -4,9 +4,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -57,10 +57,9 @@ class Sprite : public DrawableImplSdl { void doUpdate(float /*elapsed*/) override; - void doDraw(std::shared_ptr const sptr) const override; - + void doDrawOutline(std::shared_ptr const sptr) const override; void doDrawShadow(std::shared_ptr const sptr) const override; - + void doDraw(std::shared_ptr const sptr) const override; void doDrawFlash(std::shared_ptr const sptr) const override; void doRotate(float /*rot*/) override; diff --git a/impl/jamtemplate/sdl/text.cpp b/impl/jamtemplate/sdl/text.cpp index 15cbbca4..aec0263c 100644 --- a/impl/jamtemplate/sdl/text.cpp +++ b/impl/jamtemplate/sdl/text.cpp @@ -35,15 +35,12 @@ void Text::setText(std::string const& text) std::string Text::getText() const { return m_text; } -void Text::setOutline(float /*thickness*/, jt::Color /*col*/) -{ - std::cerr << "Font outline not supported by SDL TTF fonts" << std::endl; -} - void Text::setPosition(jt::Vector2f const& pos) { m_position = pos; } + jt::Vector2f Text::getPosition() const { return m_position; } -void Text::setColor(const jt::Color& col) { m_color = col; } +void Text::setColor(jt::Color const& col) { m_color = col; } + jt::Color Text::getColor() const { return m_color; } jt::Rectf Text::getGlobalBounds() const @@ -53,6 +50,7 @@ jt::Rectf Text::getGlobalBounds() const static_cast(m_textTextureSizeY) * m_scale.y / static_cast(getUpscaleFactor()) }; } + jt::Rectf Text::getLocalBounds() const { return jt::Rectf { 0, 0, @@ -66,6 +64,7 @@ void Text::setScale(jt::Vector2f const& scale) m_scale = scale; setOriginInternal(m_origin); } + jt::Vector2f Text::getScale() const { return m_scale; } void Text::setTextAlign(Text::TextAlign ta) @@ -75,6 +74,7 @@ void Text::setTextAlign(Text::TextAlign ta) recreateTextTexture(getRenderTarget()); } } + Text::TextAlign Text::getTextAlign() const { return m_textAlign; } void Text::doUpdate(float /*elapsed*/) @@ -91,7 +91,24 @@ void Text::doDrawShadow(std::shared_ptr const sptr) const auto col = getShadowColor(); col.a = std::min(col.a, m_color.a); setSDLColor(col); - SDL_RenderCopyEx(sptr.get(), m_textTexture.get(), nullptr, &destRect, -getRotation(), &p, flip); + SDL_RenderCopyEx(sptr.get(), m_textTexture.get(), nullptr, &destRect, getRotation(), &p, flip); +} + +void Text::doDrawOutline(std::shared_ptr const sptr) const +{ + SDL_Point const p { static_cast(getOrigin().x), static_cast(getOrigin().y) }; + + auto const flip = jt::getFlipFromScale(m_scale); + auto col = getOutlineColor(); + col.a = std::min(col.a, m_color.a); + setSDLColor(col); + + for (auto const& outlineOffset : getOutlineOffsets()) { + auto const destRect = getDestRect(outlineOffset); + + SDL_RenderCopyEx( + sptr.get(), m_textTexture.get(), nullptr, &destRect, getRotation(), &p, flip); + } } void Text::doDraw(std::shared_ptr const sptr) const @@ -100,7 +117,7 @@ void Text::doDraw(std::shared_ptr const sptr) const SDL_Point const p { static_cast(getOrigin().x), static_cast(getOrigin().y) }; setSDLColor(getColor()); - SDL_RenderCopyEx(sptr.get(), m_textTexture.get(), nullptr, &destRect, -getRotation(), &p, + SDL_RenderCopyEx(sptr.get(), m_textTexture.get(), nullptr, &destRect, getRotation(), &p, jt::getFlipFromScale(m_scale)); } @@ -110,7 +127,7 @@ void Text::doDrawFlash(std::shared_ptr const sptr) const SDL_Point const p { static_cast(getOrigin().x), static_cast(getOrigin().y) }; setSDLColor(getFlashColor()); - SDL_RenderCopyEx(sptr.get(), m_textTexture.get(), nullptr, &destRect, -getRotation(), &p, + SDL_RenderCopyEx(sptr.get(), m_textTexture.get(), nullptr, &destRect, getRotation(), &p, jt::getFlipFromScale(m_scale)); } diff --git a/impl/jamtemplate/sdl/text.hpp b/impl/jamtemplate/sdl/text.hpp index ad421d24..752f0bbc 100644 --- a/impl/jamtemplate/sdl/text.hpp +++ b/impl/jamtemplate/sdl/text.hpp @@ -24,13 +24,11 @@ class Text : public DrawableImplSdl { void setText(std::string const& text); std::string getText() const; - void setOutline(float /*thickness*/, jt::Color /*col*/); - void setPosition(jt::Vector2f const& pos) override; jt::Vector2f getPosition() const override; - void setColor(const jt::Color& col) override; + void setColor(jt::Color const& col) override; jt::Color getColor() const override; jt::Rectf getGlobalBounds() const override; @@ -62,17 +60,16 @@ class Text : public DrawableImplSdl { void doUpdate(float /*elapsed*/) override; - void doDrawShadow(std::shared_ptr const sptr) const override; - void renderOneLineOfText(std::shared_ptr const sptr, std::string text, std::size_t i, std::size_t lineCount) const; jt::Vector2u getSizeForLine( std::shared_ptr const sptr, std::string const& text) const; + void doDrawShadow(std::shared_ptr const sptr) const override; + void doDrawOutline(std::shared_ptr const sptr) const override; void doDraw(std::shared_ptr const sptr) const override; - - void doDrawFlash(std::shared_ptr const /*sptr*/) const override; + void doDrawFlash(std::shared_ptr const sptr) const override; void doRotate(float /*rot*/) override; @@ -82,6 +79,7 @@ class Text : public DrawableImplSdl { SDL_Rect getDestRect(jt::Vector2f const& positionOffset = jt::Vector2f { 0.0f, 0.0f }) const; int getUpscaleFactor() const { return 1; }; + void calculateTextTextureSize( std::shared_ptr const sptr, std::vector const& ssv); }; diff --git a/impl/jamtemplate/sfml/line.cpp b/impl/jamtemplate/sfml/line.cpp index 83a14f52..556b2ee0 100644 --- a/impl/jamtemplate/sfml/line.cpp +++ b/impl/jamtemplate/sfml/line.cpp @@ -3,13 +3,21 @@ #include #include +jt::Line::Line() + : Line { jt::Vector2f { 0.0f, 0.0f } } +{ +} + jt::Line::Line(jt::Vector2f lineVector) : m_lineVector { std::move(lineVector) } , m_color { jt::colors::White } - { } +void jt::Line::setLineVector(jt::Vector2f const& lineVector) { m_lineVector = lineVector; } + +jt::Vector2f jt::Line::getLineVector() const { return m_lineVector; } + void jt::Line::doUpdate(float /*elapsed*/) { } void jt::Line::doDraw(std::shared_ptr const sptr) const @@ -54,6 +62,22 @@ void jt::Line::doDrawShadow(std::shared_ptr const sptr) c sptr->draw(line); } +void jt::Line::doDrawOutline(std::shared_ptr const sptr) const +{ + auto const startPosition + = getPosition() + getShakeOffset() + getOffset() + getCamOffset() + getShadowOffset(); + auto const endPosition = startPosition + + jt::Vector2f { m_lineVector.x * m_scale.x, m_lineVector.y * m_scale.y } + + getShadowOffset(); + + for (auto const outlineOffset : getOutlineOffsets()) { + sf::VertexArray line { sf::Lines, 2 }; + line[0] = sf::Vertex { toLib(startPosition + outlineOffset), toLib(getOutlineColor()) }; + line[1] = sf::Vertex { toLib(endPosition + outlineOffset), toLib(getOutlineColor()) }; + sptr->draw(line); + } +} + void jt::Line::doRotate(float d) { m_lineVector = jt::MathHelper::rotateBy(m_lineVector, d); } void jt::Line::setColor(jt::Color const& col) { m_color = col; } diff --git a/impl/jamtemplate/sfml/line.hpp b/impl/jamtemplate/sfml/line.hpp index 248fad93..5d464397 100644 --- a/impl/jamtemplate/sfml/line.hpp +++ b/impl/jamtemplate/sfml/line.hpp @@ -12,10 +12,21 @@ class Line : public DrawableImplSFML { public: using Sptr = std::shared_ptr; + /// Constructor + Line(); + /// Constructor /// \param lineVector the vector from start to end of the line explicit Line(jt::Vector2f lineVector); + /// Set line Vector from start to end of the line + /// \param lineVector line vector + void setLineVector(jt::Vector2f const& lineVector); + + /// Get line Vector from start to end of the line + /// \return line vector + jt::Vector2f getLineVector() const; + void setColor(jt::Color const& col) override; jt::Color getColor() const override; @@ -37,6 +48,7 @@ class Line : public DrawableImplSFML { void doDraw(std::shared_ptr const sptr) const override; void doDrawFlash(std::shared_ptr const sptr) const override; void doDrawShadow(std::shared_ptr const sptr) const override; + void doDrawOutline(std::shared_ptr const sptr) const override; void doUpdate(float elapsed) override; void doRotate(float d) override; }; diff --git a/impl/jamtemplate/sfml/render_window_lib.cpp b/impl/jamtemplate/sfml/render_window_lib.cpp index c6d5187f..5b3e320c 100644 --- a/impl/jamtemplate/sfml/render_window_lib.cpp +++ b/impl/jamtemplate/sfml/render_window_lib.cpp @@ -9,7 +9,10 @@ jt::RenderWindow::RenderWindow(unsigned int width, unsigned int height, std::str = std::make_shared(sf::VideoMode(width, height), title, sf::Style::Close); m_window->setVerticalSyncEnabled(true); - ImGui::SFML::Init(*m_window.get()); + auto const returnValue = ImGui::SFML::Init(*m_window.get()); + if (!returnValue) { + throw std::invalid_argument { "SFML ImGui initialization failed." }; + } } jt::RenderWindow::~RenderWindow() diff --git a/impl/jamtemplate/sfml/shape.cpp b/impl/jamtemplate/sfml/shape.cpp index d34aa1b3..007b0595 100644 --- a/impl/jamtemplate/sfml/shape.cpp +++ b/impl/jamtemplate/sfml/shape.cpp @@ -105,7 +105,23 @@ void jt::Shape::doUpdate(float /*elapsed*/) void jt::Shape::doRotate(float rot) { if (m_shape) { - m_shape->setRotation(-rot); - m_flashShape->setRotation(-rot); + m_shape->setRotation(rot); + m_flashShape->setRotation(rot); } } + +void jt::Shape::doDrawOutline(std::shared_ptr const sptr) const +{ + jt::Vector2f const oldPos = fromLib(m_shape->getPosition()); + jt::Color const oldCol = fromLib(m_shape->getFillColor()); + + m_shape->setFillColor(toLib(getOutlineColor())); + + for (auto const outlineOffset : getOutlineOffsets()) { + m_shape->setPosition(toLib(oldPos + outlineOffset)); + sptr->draw(*m_shape); + } + + m_shape->setPosition(toLib(oldPos)); + m_shape->setFillColor(toLib(oldCol)); +} diff --git a/impl/jamtemplate/sfml/shape.hpp b/impl/jamtemplate/sfml/shape.hpp index fd40af4d..c7933e36 100644 --- a/impl/jamtemplate/sfml/shape.hpp +++ b/impl/jamtemplate/sfml/shape.hpp @@ -39,9 +39,11 @@ class Shape : public DrawableImplSFML { jt::Vector2f m_position { 0, 0 }; + void doDrawShadow(std::shared_ptr const sptr) const override; + void doDrawOutline(std::shared_ptr const sptr) const override; void doDraw(std::shared_ptr const sptr) const override; void doDrawFlash(std::shared_ptr const sptr) const override; - void doDrawShadow(std::shared_ptr const sptr) const override; + void doUpdate(float elapsed) override; void doRotate(float rot) override; }; diff --git a/impl/jamtemplate/sfml/sprite.cpp b/impl/jamtemplate/sfml/sprite.cpp index 60c14e96..964fbe2b 100644 --- a/impl/jamtemplate/sfml/sprite.cpp +++ b/impl/jamtemplate/sfml/sprite.cpp @@ -22,12 +22,15 @@ jt::Sprite::Sprite( void jt::Sprite::fromTexture(sf::Texture const& text) { m_sprite.setTexture(text); } void jt::Sprite::setPosition(jt::Vector2f const& pos) { m_position = pos; } + jt::Vector2f jt::Sprite::getPosition() const { return m_position; } void jt::Sprite::setColor(jt::Color const& col) { m_sprite.setColor(toLib(col)); } + jt::Color jt::Sprite::getColor() const { return fromLib(m_sprite.getColor()); } jt::Rectf jt::Sprite::getGlobalBounds() const { return fromLib(m_sprite.getGlobalBounds()); } + jt::Rectf jt::Sprite::getLocalBounds() const { return fromLib(m_sprite.getLocalBounds()); } void jt::Sprite::setScale(jt::Vector2f const& scale) @@ -84,6 +87,22 @@ void jt::Sprite::doDrawShadow(std::shared_ptr const sptr) } } +void jt::Sprite::doDrawOutline(std::shared_ptr const sptr) const +{ + jt::Vector2f const oldPos = fromLib(m_sprite.getPosition()); + jt::Color const oldCol = fromLib(m_sprite.getColor()); + + m_sprite.setColor(toLib(getOutlineColor())); + + for (auto const outlineOffset : getOutlineOffsets()) { + m_sprite.setPosition(toLib(oldPos + outlineOffset)); + sptr->draw(m_sprite); + } + + m_sprite.setPosition(toLib(oldPos)); + m_sprite.setColor(toLib(oldCol)); +} + void jt::Sprite::doDraw(std::shared_ptr const sptr) const { if (sptr) { @@ -101,8 +120,8 @@ void jt::Sprite::doDrawFlash(std::shared_ptr const sptr) void jt::Sprite::doRotate(float rot) { - m_sprite.setRotation(-rot); - m_flashSprite.setRotation(-rot); + m_sprite.setRotation(rot); + m_flashSprite.setRotation(rot); } void jt::Sprite::setOriginInternal(jt::Vector2f const& origin) diff --git a/impl/jamtemplate/sfml/sprite.hpp b/impl/jamtemplate/sfml/sprite.hpp index 7b884dd1..382c18c2 100644 --- a/impl/jamtemplate/sfml/sprite.hpp +++ b/impl/jamtemplate/sfml/sprite.hpp @@ -59,9 +59,9 @@ class Sprite : public DrawableImplSFML { void doUpdate(float /*elapsed*/) override; void doDrawShadow(std::shared_ptr const sptr) const override; + void doDrawOutline(std::shared_ptr const sptr) const override; void doDraw(std::shared_ptr const sptr) const override; void doDrawFlash(std::shared_ptr const sptr) const override; - void doRotate(float rot) override; }; diff --git a/impl/jamtemplate/sfml/text.cpp b/impl/jamtemplate/sfml/text.cpp index 232b41dc..30bddb60 100644 --- a/impl/jamtemplate/sfml/text.cpp +++ b/impl/jamtemplate/sfml/text.cpp @@ -29,21 +29,19 @@ void jt::Text::setText(std::string const& text) m_text->setString(text); m_flashText->setString(text); } -std::string jt::Text::getText() const { return m_text->getString(); } -void jt::Text::setOutline(float thickness, jt::Color col) -{ - m_text->setOutlineThickness(thickness); - m_text->setOutlineColor(toLib(col)); -} +std::string jt::Text::getText() const { return m_text->getString(); } void jt::Text::setPosition(jt::Vector2f const& pos) { m_position = pos; } + jt::Vector2f jt::Text::getPosition() const { return m_position; } -void jt::Text::setColor(const jt::Color& col) { m_text->setFillColor(toLib(col)); } +void jt::Text::setColor(jt::Color const& col) { m_text->setFillColor(toLib(col)); } + jt::Color jt::Text::getColor() const { return fromLib(m_text->getFillColor()); } jt::Rectf jt::Text::getGlobalBounds() const { return fromLib(m_text->getGlobalBounds()); } + jt::Rectf jt::Text::getLocalBounds() const { return fromLib(m_text->getLocalBounds()); } void jt::Text::setScale(jt::Vector2f const& scale) @@ -63,6 +61,7 @@ void jt::Text::setOriginInternal(jt::Vector2f const& origin) } void jt::Text::setTextAlign(jt::Text::TextAlign ta) { m_textAlign = ta; } + jt::Text::TextAlign jt::Text::getTextAlign() const { return m_textAlign; } void jt::Text::doUpdate(float /*elapsed*/) @@ -101,6 +100,28 @@ void jt::Text::doDrawShadow(std::shared_ptr const sptr) c m_text->setFillColor(toLib(oldCol)); } +void jt::Text::doDrawOutline(std::shared_ptr const sptr) const +{ + jt::Vector2f const oldPos = fromLib(m_text->getPosition()); + jt::Color const oldCol = fromLib(m_text->getFillColor()); + + m_text->setFillColor(toLib(getOutlineColor())); + + auto const maxWidth = getOutlineWidth(); + for (auto currentWidth = 1; currentWidth != maxWidth + 1; ++currentWidth) { + for (auto i = -currentWidth; i != currentWidth + 1; ++i) { + for (auto j = -currentWidth; j != currentWidth + 1; ++j) { + m_text->setPosition( + toLib(oldPos + jt::Vector2f { static_cast(i), static_cast(j) })); + sptr->draw(*m_text); + } + } + } + + m_text->setPosition(toLib(oldPos)); + m_text->setFillColor(toLib(oldCol)); +} + void jt::Text::doDraw(std::shared_ptr const sptr) const { sf::RenderStates states { getSfBlendMode() }; @@ -114,6 +135,6 @@ void jt::Text::doDrawFlash(std::shared_ptr const sptr) co void jt::Text::doRotate(float rot) { - m_text->setRotation(-rot); - m_flashText->setRotation(-rot); + m_text->setRotation(rot); + m_flashText->setRotation(rot); } diff --git a/impl/jamtemplate/sfml/text.hpp b/impl/jamtemplate/sfml/text.hpp index 7e6e73a1..a6ce02ff 100644 --- a/impl/jamtemplate/sfml/text.hpp +++ b/impl/jamtemplate/sfml/text.hpp @@ -24,7 +24,6 @@ class Text : public DrawableImplSFML { void setText(std::string const& text); std::string getText() const; - void setOutline(float thickness, jt::Color col); void setPosition(jt::Vector2f const& pos) override; jt::Vector2f getPosition() const override; @@ -55,6 +54,7 @@ class Text : public DrawableImplSFML { void doUpdate(float /*elapsed*/) override; void doDrawShadow(std::shared_ptr const sptr) const override; + void doDrawOutline(std::shared_ptr const sptr) const override; void doDraw(std::shared_ptr const sptr) const override; void doDrawFlash(std::shared_ptr const sptr) const override; diff --git a/test/integration/demo/move_cam/state_move_cam.cpp b/test/integration/demo/move_cam/state_move_cam.cpp index 35b2af75..7dad4089 100644 --- a/test/integration/demo/move_cam/state_move_cam.cpp +++ b/test/integration/demo/move_cam/state_move_cam.cpp @@ -35,6 +35,7 @@ void StateMoveCam::onCreate() m_spriteDino = std::make_shared( "assets/test/integration/demo/dino_salto.aseprite", textureManager()); m_spriteDino->setPosition({ 150, 232 }); + m_spriteDino->setOutline(jt::colors::Black, 1); m_spriteDino->setScale({ 2.0f, 2.0f }); m_animMiner = std::make_shared(); @@ -56,14 +57,19 @@ void StateMoveCam::onCreate() m_text_center_aligned = jt::dh::createText(renderTarget(), "center aligned", 16); m_text_center_aligned->setTextAlign(jt::Text::TextAlign::CENTER); + m_text_center_aligned->setOutline(jt::Color { 30, 30, 30, 255 }, 1); m_text_center_aligned->setPosition(jt::Vector2f { 300, 130 }); m_text_right_aligned = jt::dh::createText(renderTarget(), "right aligned", 16); m_text_right_aligned->setTextAlign(jt::Text::TextAlign::RIGHT); m_text_right_aligned->setPosition(jt::Vector2f { 300, 160 }); - m_line = std::make_shared(jt::Vector2f { 100, 50 }); - m_line->setPosition(jt::Vector2f { 20, 200 }); + m_line1 = std::make_shared(jt::Vector2f { 100, 50 }); + m_line1->setPosition(jt::Vector2f { 20, 200 }); + + m_line2 = std::make_shared(jt::Vector2f { -50, 100 }); + m_line2->setOutline(jt::colors::Black, 2); + m_line2->setPosition(jt::Vector2f { 20, 200 } + jt::Vector2f { 100, -50 }); float const scrollSpeed = 50.0f; getGame()->input().keyboard()->setCommandPressed({ jt::KeyCode::W, jt::KeyCode::Up }, @@ -114,12 +120,12 @@ void StateMoveCam::onUpdate(float const elapsed) } m_background->update(elapsed); - m_shape1->setRotation(getAge() * 90); + m_shape1->setRotation(getAge() * 90.0f); m_shape1->update(elapsed); m_shape2->update(elapsed); - m_sprite->setRotation(-getAge() * 90); + m_sprite->setRotation(-getAge() * 90.0f); m_sprite->update(elapsed); m_spriteCircle->update(elapsed); @@ -132,7 +138,8 @@ void StateMoveCam::onUpdate(float const elapsed) m_text_left_aligned->update(elapsed); m_text_center_aligned->update(elapsed); m_text_right_aligned->update(elapsed); - m_line->update(elapsed); + m_line1->update(elapsed); + m_line2->update(elapsed); } void StateMoveCam::onDraw() const @@ -152,6 +159,8 @@ void StateMoveCam::onDraw() const m_text_center_aligned->draw(renderTarget()); m_text_right_aligned->draw(renderTarget()); - m_line->draw(renderTarget()); + m_line1->draw(renderTarget()); + m_line2->draw(renderTarget()); } + std::string StateMoveCam::getName() const { return "Move Cam"; } diff --git a/test/integration/demo/move_cam/state_move_cam.hpp b/test/integration/demo/move_cam/state_move_cam.hpp index 4a2a87ab..d46fece3 100644 --- a/test/integration/demo/move_cam/state_move_cam.hpp +++ b/test/integration/demo/move_cam/state_move_cam.hpp @@ -26,7 +26,8 @@ class StateMoveCam : public jt::GameState { jt::Text::Sptr m_text_center_aligned; jt::Text::Sptr m_text_right_aligned; - jt::Line::Sptr m_line; + jt::Line::Sptr m_line1; + jt::Line::Sptr m_line2; void onCreate() override; void onEnter() override; diff --git a/test/unit/jt_test/common/graphics/drawables_helper_test.cpp b/test/unit/jt_test/common/graphics/drawables_helper_test.cpp index 6cae1ee5..80516bf5 100644 --- a/test/unit/jt_test/common/graphics/drawables_helper_test.cpp +++ b/test/unit/jt_test/common/graphics/drawables_helper_test.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include diff --git a/test/unit/jt_test/common/graphics/drawables_impl_test.cpp b/test/unit/jt_test/common/graphics/drawables_impl_test.cpp index 23743c06..417a994b 100644 --- a/test/unit/jt_test/common/graphics/drawables_impl_test.cpp +++ b/test/unit/jt_test/common/graphics/drawables_impl_test.cpp @@ -1,7 +1,6 @@ #include "drawables_impl_test.hpp" #include #include -#include #include #include @@ -10,7 +9,9 @@ class DrawableImplTestFixture protected: std::shared_ptr drawable; jt::TextureManagerInterface& tm { getTextureManager() }; + void SetUp() override { drawable = GetParam()->createDrawable(tm); } + void TearDown() override { // reset static cam offset just to make sure @@ -197,6 +198,13 @@ TEST_P(DrawableImplTestFixture, DrawWithShadow) drawable->draw(getRenderTargetContainer()); } +TEST_P(DrawableImplTestFixture, DrawWithOutline) +{ + drawable->setOutline(jt::colors::Green, 4); + drawable->update(0.1f); + drawable->draw(getRenderTargetContainer()); +} + TEST_P(DrawableImplTestFixture, GetColorAfterSetColor) { drawable->setColor(jt::colors::Red); @@ -317,6 +325,34 @@ TEST_P(DrawableImplTestFixture, GetShadowColorAfterSet) ASSERT_EQ(drawable->getShadowColor(), jt::colors::Yellow); } +TEST_P(DrawableImplTestFixture, GetOutlineActiveIsFalseByDefault) +{ + ASSERT_FALSE(drawable->getOutlineActive()); +} + +TEST_P(DrawableImplTestFixture, GetOutlineActiveIsTrueAfterSetOutline) +{ + drawable->setOutline(jt::colors::Yellow, 1); + ASSERT_TRUE(drawable->getOutlineActive()); +} + +TEST_P(DrawableImplTestFixture, GetOutlineColorReturnsBlackByDefault) +{ + ASSERT_EQ(drawable->getOutlineColor(), jt::colors::Black); +} + +TEST_P(DrawableImplTestFixture, GetOutlineColorAfterSet) +{ + drawable->setOutline(jt::colors::Yellow, 2); + ASSERT_EQ(drawable->getOutlineColor(), jt::colors::Yellow); +} + +TEST_P(DrawableImplTestFixture, GetOutlineWidthAfterSet) +{ + drawable->setOutline(jt::colors::Yellow, 5); + ASSERT_EQ(drawable->getOutlineWidth(), 5); +} + TEST_P(DrawableImplTestFixture, GetSetBlendModeMul) { auto const expectedBlendMode = jt::BlendMode::MUL; diff --git a/test/unit/jt_test/common/graphics/line_test.cpp b/test/unit/jt_test/common/graphics/line_test.cpp new file mode 100644 index 00000000..c7da750d --- /dev/null +++ b/test/unit/jt_test/common/graphics/line_test.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +TEST(LineTest, LineVectorIsZeroByDefault) +{ + auto const line = jt::Line {}; + jt::Vector2f const expectedResult { 0.0f, 0.0f }; + ASSERT_EQ(line.getLineVector(), expectedResult); +} + +TEST(LineTest, LineVectorIsSetCorrectlyByConstructor) +{ + jt::Vector2f const expectedResult { 15.4f, -22.0f }; + auto const line = jt::Line { expectedResult }; + ASSERT_EQ(line.getLineVector(), expectedResult); +} + +TEST(LineTest, SetLineVector) +{ + auto line = jt::Line {}; + jt::Vector2f const expectedResult { -20.0f, -40.0f }; + line.setLineVector(expectedResult); + ASSERT_EQ(line.getLineVector(), expectedResult); +} diff --git a/test/unit/jt_test/common/text_test.cpp b/test/unit/jt_test/common/text_test.cpp index e2a467a4..927c1e80 100644 --- a/test/unit/jt_test/common/text_test.cpp +++ b/test/unit/jt_test/common/text_test.cpp @@ -25,6 +25,7 @@ TEST(TextTest, InitialValues) ASSERT_FLOAT_EQ(t->getLocalBounds().width, 0.0f); ASSERT_FLOAT_EQ(t->getLocalBounds().height, 0.0f); } + TEST(TextTest, Destructor) { { @@ -156,7 +157,7 @@ TEST(TextTest, Update) TEST(TextTest, SetOutline) { auto t = getText(); - t->setOutline(2.0f, jt::colors::Red); + t->setOutline(jt::colors::Red, 2); } TEST(TextTest, InitialRotation) diff --git a/test/unit/jt_test/mocks/mock_drawable.hpp b/test/unit/jt_test/mocks/mock_drawable.hpp index 900d189e..9776171c 100644 --- a/test/unit/jt_test/mocks/mock_drawable.hpp +++ b/test/unit/jt_test/mocks/mock_drawable.hpp @@ -53,12 +53,17 @@ class MockDrawable : public jt::DrawableInterface { MOCK_METHOD(jt::Vector2f, getShadowOffset, (), (const, override)); MOCK_METHOD(void, setShadow, (jt::Color const&, jt::Vector2f const&), (override)); + MOCK_METHOD(bool, getOutlineActive, (), (const, override)); + MOCK_METHOD(void, setOutline, (jt::Color const&, int), (override)); + MOCK_METHOD(jt::Color, getOutlineColor, (), (const, override)); + MOCK_METHOD(int, getOutlineWidth, (), (const, override)); + MOCK_METHOD(void, setIgnoreCamMovement, (bool), (override)); MOCK_METHOD(bool, getIgnoreCamMovement, (), (const, override)); MOCK_METHOD(void, setScreenSizeHint, (jt::Vector2f const& hint), (override)); MOCK_METHOD(jt::Vector2f, getScreenSizeHint, (), (const, override)); - + MOCK_METHOD(void, setBlendMode, (jt::BlendMode), (override)); MOCK_METHOD(jt::BlendMode, getBlendMode, (), (const, override)); From fef25c3859c7aa0cbecb236edf09f666b6c8f064 Mon Sep 17 00:00:00 2001 From: Simon Weis Date: Wed, 1 Nov 2023 08:36:46 +0100 Subject: [PATCH 2/2] Add missing include --- impl/jamtemplate/common/graphics/outline_impl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/impl/jamtemplate/common/graphics/outline_impl.hpp b/impl/jamtemplate/common/graphics/outline_impl.hpp index 778e207e..4f238e09 100644 --- a/impl/jamtemplate/common/graphics/outline_impl.hpp +++ b/impl/jamtemplate/common/graphics/outline_impl.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace jt {