From bd2faabe993e0c07a52bf21d3cb279451805ec93 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Fri, 1 Jun 2012 16:39:09 -0300 Subject: [PATCH] graphics optimization feature inspirated by diablo3 engine * the rendering now consits of two panes - the background pane (for animated stuff like the map) - the foreground pane (for steady stuff, like UI) each pane has it own max FPS and works idependently this may increase graphics performance on many platforms --- TODO | 1 + modules/client_options/graphics.otui | 52 +++++++- modules/client_topmenu/topmenu.otui | 12 +- modules/core_lib/ui/tooltip.lua | 3 +- modules/core_lib/widgets/uiscrollbar.lua | 10 +- modules/core_styles/styles/labels.otui | 4 - src/framework/CMakeLists.txt | 2 +- src/framework/application.cpp | 82 ++++++++++--- src/framework/application.h | 17 ++- src/framework/core/adaptativeframecounter.cpp | 113 ++++++++++++++++++ src/framework/core/adaptativeframecounter.h | 74 ++++++++++++ src/framework/core/clock.h | 2 +- src/framework/graphics/framebuffer.cpp | 22 ++-- src/framework/graphics/framebuffer.h | 1 + src/framework/graphics/graphics.cpp | 1 - src/framework/graphics/painter.cpp | 7 +- src/framework/graphics/painter.h | 1 + src/framework/graphics/particle.cpp | 3 +- src/framework/graphics/texture.cpp | 6 + src/framework/graphics/texture.h | 4 +- src/framework/luafunctions.cpp | 22 ++-- src/framework/platform/win32window.cpp | 3 +- src/framework/platform/x11window.cpp | 2 + src/framework/sound/streamsoundsource.h | 2 +- src/framework/stdext/stdext.h | 1 + .../{ui/uiframecounter.cpp => stdext/time.h} | 45 ++----- src/framework/stdext/types.h | 4 +- src/framework/ui/declarations.h | 2 - src/framework/ui/ui.h | 1 - src/framework/ui/uimanager.cpp | 5 +- src/framework/ui/uimanager.h | 2 +- src/framework/ui/uitextedit.cpp | 5 +- src/framework/ui/uitextedit.h | 2 +- src/framework/ui/uiwidget.cpp | 19 +-- src/framework/ui/uiwidget.h | 6 +- src/otclient/core/creature.cpp | 4 +- src/otclient/core/mapview.cpp | 9 +- src/otclient/ui/uicreature.cpp | 7 +- src/otclient/ui/uicreature.h | 2 +- src/otclient/ui/uiitem.cpp | 7 +- src/otclient/ui/uiitem.h | 2 +- src/otclient/ui/uimap.cpp | 26 ++-- src/otclient/ui/uimap.h | 2 +- 43 files changed, 460 insertions(+), 137 deletions(-) create mode 100644 src/framework/core/adaptativeframecounter.cpp create mode 100644 src/framework/core/adaptativeframecounter.h rename src/framework/{ui/uiframecounter.cpp => stdext/time.h} (52%) diff --git a/TODO b/TODO index aed39d5c..86adebb6 100644 --- a/TODO +++ b/TODO @@ -36,6 +36,7 @@ scrolling multiline edit mouse wheel lineedits onMapKnown event save different account/pasword/server for each otclient protocol version +dont store textures upsidedown == OPTIMIZATIONS AND REWORKS * game diff --git a/modules/client_options/graphics.otui b/modules/client_options/graphics.otui index d2ed442c..944bc767 100644 --- a/modules/client_options/graphics.otui +++ b/modules/client_options/graphics.otui @@ -13,20 +13,60 @@ Panel !text: tr('Fullscreen') Label - !text: tr('Frame rate limit') + id: backgroundFrameRateLimitLabel + !text: tr('Background pane framerate limit: %s', 'max') anchors.left: parent.left anchors.right: parent.right anchors.top: prev.bottom margin-top: 6 HorizontalScrollBar - id: frameRateScrollBar + id: backgroundFrameRateScrollBar anchors.left: parent.left anchors.right: parent.right anchors.top: prev.bottom margin-top: 3 - minimum: 0 - maximum: 50 - value: 0 + minimum: 10 + maximum: 201 + value: 201 step: 1 - @onValueChange: g_app.setFrameSleep(self:getValue()) + @onValueChange: | + local value = self:getValue() + local text = value + if value == self:getMaximum() then + text = 'max' + value = 0 + end + + self:getParent():getChildById('backgroundFrameRateLimitLabel'):setText(tr('Background pane framerate limit: %s', text)) + g_app.setBackgroundPaneMaxFps(value) + + Label + id: foregroundFrameRateLimitLabel + !text: tr('Foreground pane framerate limit: %s', '8') + anchors.left: parent.left + anchors.right: parent.right + anchors.top: prev.bottom + margin-top: 6 + + HorizontalScrollBar + id: foregroundFrameRateScrollBar + anchors.left: parent.left + anchors.right: parent.right + anchors.top: prev.bottom + margin-top: 3 + minimum: 1 + maximum: 61 + value: 8 + step: 1 + @onValueChange: | + local value = self:getValue() + local text = value + if value == self:getMaximum() then + text = 'max' + value = 0 + end + + self:getParent():getChildById('foregroundFrameRateLimitLabel'):setText(tr('Foreground pane framerate limit: %s', text)) + g_app.setForegroundPaneMaxFps(value) + diff --git a/modules/client_topmenu/topmenu.otui b/modules/client_topmenu/topmenu.otui index 835117d7..35d34315 100644 --- a/modules/client_topmenu/topmenu.otui +++ b/modules/client_topmenu/topmenu.otui @@ -69,9 +69,19 @@ TopPanel anchors.bottom: parent.bottom anchors.right: parent.right - FrameCounter + UILabel + size: 68 16 + text-align: right + color: white id: frameCounter anchors.top: parent.top anchors.right: prev.left margin-top: 8 margin-right: 5 + @onSetup: | + local updateFunc + updateFunc = function() + self:setText('FPS: ' .. g_app.getBackgroundPaneFps()) + scheduleEvent(updateFunc, 250) + end + updateFunc() diff --git a/modules/core_lib/ui/tooltip.lua b/modules/core_lib/ui/tooltip.lua index 2e717e8e..cd53ad36 100644 --- a/modules/core_lib/ui/tooltip.lua +++ b/modules/core_lib/ui/tooltip.lua @@ -6,7 +6,7 @@ local currentHoveredWidget -- private functions local function moveToolTip(tooltip) - if not tooltip:isVisible() then return end + if not tooltip:isVisible() or tooltip:getOpacity() < 0.1 then return end local pos = g_window.getMousePosition() pos.y = pos.y + 1 @@ -49,6 +49,7 @@ function ToolTip.init() toolTipLabel:setId('toolTip') toolTipLabel:setBackgroundColor('#111111cc') toolTipLabel:setTextAlign(AlignCenter) + toolTipLabel:hide() toolTipLabel.onMouseMove = moveToolTip end) end diff --git a/modules/core_lib/widgets/uiscrollbar.lua b/modules/core_lib/widgets/uiscrollbar.lua index 5b8ab0e0..90132807 100644 --- a/modules/core_lib/widgets/uiscrollbar.lua +++ b/modules/core_lib/widgets/uiscrollbar.lua @@ -89,8 +89,8 @@ function UIScrollBar.create() local scrollbar = UIScrollBar.internalCreate() scrollbar:setFocusable(false) scrollbar.value = 0 - scrollbar.minimum = 0 - scrollbar.maximum = 0 + scrollbar.minimum = -999999 + scrollbar.maximum = 999999 scrollbar.step = 1 scrollbar.orientation = 'vertical' scrollbar.pixelsScroll = false @@ -98,6 +98,8 @@ function UIScrollBar.create() end function UIScrollBar:onSetup() + self.setupDone = true + signalcall(self.onValueChange, self, self.value) addEvent(function() Mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:decrement() end) Mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:increment() end) @@ -163,7 +165,9 @@ function UIScrollBar:setValue(value) local delta = value - self.value self.value = value updateSlider(self) - signalcall(self.onValueChange, self, value, delta) + if self.setupDone then + signalcall(self.onValueChange, self, value, delta) + end end function UIScrollBar:setStep(step) diff --git a/modules/core_styles/styles/labels.otui b/modules/core_styles/styles/labels.otui index 9dc62cd0..fdd8e639 100644 --- a/modules/core_styles/styles/labels.otui +++ b/modules/core_styles/styles/labels.otui @@ -20,7 +20,3 @@ FlatLabel < UILabel GameLabel < UILabel font: verdana-11px-antialised color: #bbbbbb - -FrameCounter < UIFrameCounter - size: 68 16 - align: right diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index cc40436b..7c3bafae 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -173,6 +173,7 @@ SET(framework_SOURCES ${framework_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/util/color.cpp # framework core + ${CMAKE_CURRENT_LIST_DIR}/core/adaptativeframecounter.cpp ${CMAKE_CURRENT_LIST_DIR}/core/logger.cpp ${CMAKE_CURRENT_LIST_DIR}/core/configmanager.cpp ${CMAKE_CURRENT_LIST_DIR}/core/resourcemanager.cpp @@ -250,7 +251,6 @@ SET(framework_SOURCES ${framework_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/ui/uigridlayout.cpp ${CMAKE_CURRENT_LIST_DIR}/ui/uianchorlayout.cpp ${CMAKE_CURRENT_LIST_DIR}/ui/uilayout.cpp - ${CMAKE_CURRENT_LIST_DIR}/ui/uiframecounter.cpp ${CMAKE_CURRENT_LIST_DIR}/ui/uitranslator.cpp # framework third party diff --git a/src/framework/application.cpp b/src/framework/application.cpp index afbffc90..22093ca7 100644 --- a/src/framework/application.cpp +++ b/src/framework/application.cpp @@ -57,7 +57,7 @@ Application::Application(const std::string& appName) { g_app = this; m_appName = appName; - m_frameSleep = 0; + m_foregroundFrameCounter.setMaxFps(60); } Application::~Application() @@ -157,6 +157,8 @@ void Application::terminate() // terminate script environment g_lua.terminate(); + m_foreground = nullptr; + logInfo("Application ended successfully."); } @@ -182,19 +184,70 @@ void Application::run() poll(); if(g_window.isVisible()) { - g_graphics.beginRender(); - render(); - g_graphics.endRender(); + // the otclient's screen consists of two panes + // background pane - high updated and animated pane (where the game are stuff happens) + // foreground pane - steady pane with few animated stuff (UI) + bool redraw = false; + bool updateForeground = false; + + if(m_backgroundFrameCounter.shouldProcessNextFrame()) { + redraw = true; + + if(m_mustRepaint || m_foregroundFrameCounter.shouldProcessNextFrame()) { + m_mustRepaint = false; + updateForeground = true; + } + } + + if(redraw) { + g_graphics.beginRender(); + + Rect viewportRect(0, 0, g_graphics.getViewportSize()); + + // draw the foreground into a texture + if(updateForeground) { + m_foregroundFrameCounter.processNextFrame(); + + // draw foreground + g_ui.render(true); + + // copy the foreground to a texture + m_foreground->copyFromScreen(viewportRect); + } + + // draw background (animated stuff) + m_backgroundFrameCounter.processNextFrame(); + g_ui.render(false); + + // transform projection matrix to render upside down + Matrix3 projectionMatrix = g_painter->getProjectionMatrix(); + projectionMatrix(2,2) *= -1.0f; + projectionMatrix(3,2) *= -1.0f; + + // draw the foreground (steady stuff) + g_painter->saveAndResetState(); + g_painter->setProjectionMatrix(projectionMatrix); + g_painter->drawTexturedRect(viewportRect, m_foreground, viewportRect); + g_painter->restoreSavedState(); + + g_graphics.endRender(); + + // update screen pixels + g_window.swapBuffers(); + } + + m_backgroundFrameCounter.update(); + m_foregroundFrameCounter.update(); + + int sleepMicros = std::min(m_backgroundFrameCounter.getMaximumSleepMicros(), m_foregroundFrameCounter.getMaximumSleepMicros()); + if(sleepMicros >= AdaptativeFrameCounter::MINIMUM_MICROS_SLEEP) { + stdext::microsleep(AdaptativeFrameCounter::MINIMUM_MICROS_SLEEP); + } - // update screen pixels - g_window.swapBuffers(); } else { // sleeps until next poll to avoid massive cpu usage g_clock.sleep(POLL_CYCLE_DELAY+1); } - - if(m_frameSleep > 0) - g_clock.sleep(m_frameSleep); } m_stopping = false; @@ -227,20 +280,15 @@ void Application::close() m_onInputEvent = false; } -void Application::render() -{ - // everything is rendered by UI components, even the game - g_ui.render(); - - //g_particleManager.render(); -} - void Application::resize(const Size& size) { m_onInputEvent = true; g_graphics.resize(size); g_ui.resize(size); m_onInputEvent = false; + + m_foreground = TexturePtr(new Texture(size.width(), size.height())); + m_mustRepaint = true; } void Application::inputEvent(const InputEvent& event) diff --git a/src/framework/application.h b/src/framework/application.h index b865b01a..d66c1575 100644 --- a/src/framework/application.h +++ b/src/framework/application.h @@ -24,6 +24,8 @@ #define APPLICATION_H #include +#include +#include class Application { @@ -43,33 +45,40 @@ public: virtual void poll(); virtual void close(); - void setFrameSleep(int delay) { m_frameSleep = delay; } + bool willRepaint() { return m_mustRepaint; } + void repaint() { m_mustRepaint = true; } + + void setForegroundPaneMaxFps(int maxFps) { m_foregroundFrameCounter.setMaxFps(maxFps); } + void setBackgroundPaneMaxFps(int maxFps) { m_backgroundFrameCounter.setMaxFps(maxFps); } bool isRunning() { return m_running; } bool isStopping() { return m_stopping; } bool isOnInputEvent() { return m_onInputEvent; } - int getFrameSleep() { return m_frameSleep; } const std::string& getName() { return m_appName; } const std::string& getVersion() { return m_appVersion; } + int getForegroundPaneFps() { return m_foregroundFrameCounter.getLastFps(); } + int getBackgroundPaneFps() { return m_backgroundFrameCounter.getLastFps(); } std::string getBuildCompiler() { return BUILD_COMPILER; } std::string getBuildDate() { return BUILD_DATE; } std::string getBuildRevision() { return BUILD_REVISION; } std::string getBuildType() { return BUILD_TYPE; } protected: - virtual void render(); virtual void resize(const Size& size); virtual void inputEvent(const InputEvent& event); std::string m_appName; std::string m_appVersion; std::string m_appBuildDate; - int m_frameSleep; Boolean m_initialized; Boolean m_running; Boolean m_stopping; Boolean m_onInputEvent; + Boolean m_mustRepaint; + AdaptativeFrameCounter m_backgroundFrameCounter; + AdaptativeFrameCounter m_foregroundFrameCounter; + TexturePtr m_foreground; }; extern Application *g_app; diff --git a/src/framework/core/adaptativeframecounter.cpp b/src/framework/core/adaptativeframecounter.cpp new file mode 100644 index 00000000..649daebe --- /dev/null +++ b/src/framework/core/adaptativeframecounter.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010-2012 OTClient + * + * 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. + */ + +#include "adaptativeframecounter.h" + +AdaptativeFrameCounter::AdaptativeFrameCounter() +{ + m_frames = 0; + m_partialFrames = 0; + m_partialFps = 0; + m_maxPartialFps = 0; + m_frameDelaySum = 0; + m_mediumFrameDelay = 0; + m_lastFps = 0; + m_lastFrame = 0; + m_maxFps = 0; + m_sleepMicros = 0; + m_mediumFrameDelay = 0; + m_lastFpsUpdate = stdext::micros(); + m_lastPartialFpsUpdate = stdext::micros(); +} + +bool AdaptativeFrameCounter::shouldProcessNextFrame() +{ + if(m_maxFps == 0) + return true; + + ticks_t now = stdext::micros(); + if(now - m_lastFrame < m_bestFrameDelay) + return false; + return true; +} + +void AdaptativeFrameCounter::processNextFrame() +{ + ticks_t now = stdext::micros(); + m_frames++; + m_partialFrames++; + m_frameDelaySum += now - m_lastFrame; + m_lastFrame = now ; +} + +void AdaptativeFrameCounter::update() +{ + ticks_t now = stdext::micros(); + ticks_t delta = now - m_lastPartialFpsUpdate; + if(delta > 41000 && m_partialFrames > 0) { + m_partialFps = m_partialFrames / (delta / 1000000.0f); + m_lastPartialFpsUpdate = now; + m_partialFrames = 0; + } + + delta = now - m_lastFpsUpdate; + if(delta >= 1000000) { + m_lastFps = m_frames; + if(m_frames > 0) + m_mediumFrameDelay = m_frameDelaySum / m_frames; + else + m_mediumFrameDelay = 0; + m_lastFpsUpdate = now; + m_frames = 0; + m_frameDelaySum = 0; + + //dump << stdext::format("FPS => %d Partial FPS => %d Frame Delay Hit => %.2f%%", m_lastFps, (int)m_partialFps, getFrameDelayHit()); + } +} + +void AdaptativeFrameCounter::setMaxFps(int maxFps) +{ + maxFps = std::max(std::min(maxFps, 1000), 0); + + if(maxFps != 0) { + m_bestFrameDelay = 1000000 / maxFps; + } else { + m_maxPartialFps = 0; + m_bestFrameDelay = 0; + } + m_maxFps = maxFps; +} + +int AdaptativeFrameCounter::getMaximumSleepMicros() +{ + if(m_maxFps == 0) + return 0; + return m_lastFrame + m_bestFrameDelay - stdext::micros(); +} + +float AdaptativeFrameCounter::getFrameDelayHit() +{ + if(m_bestFrameDelay > 0) + return ((m_bestFrameDelay - std::abs(m_bestFrameDelay - m_mediumFrameDelay)) * 100.0f) / (float)m_bestFrameDelay; + else + return 100.0f; +} diff --git a/src/framework/core/adaptativeframecounter.h b/src/framework/core/adaptativeframecounter.h new file mode 100644 index 00000000..553d7a62 --- /dev/null +++ b/src/framework/core/adaptativeframecounter.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010-2012 OTClient + * + * 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. + */ + +#ifndef ADAPTATIVEFRAMECOUNTER_H +#define ADAPTATIVEFRAMECOUNTER_H + +#include + +/** + * Class that help counting and limiting frames per second in a application, + * to make fps limit work as desired, this class requires 2 platforms prerequisites: + * - the platform must have timer with a precision of at least 1ms + * - the platform sleep must have a precision of at least 4ms + */ +class AdaptativeFrameCounter +{ +public: + enum { + // 4ms because most platforms has kernel timer of 250Hz + MINIMUM_MICROS_SLEEP = 4000 + }; + + AdaptativeFrameCounter(); + + bool shouldProcessNextFrame(); + void processNextFrame(); + void update(); + void setMaxFps(int maxFps); + bool isFpsLimitActive() { return m_maxFps != 0; } + + int getMaximumSleepMicros(); + float getFrameDelayHit(); + int getLastFps() { return m_lastFps; } + int getPartialFps() { return (int)m_partialFps; } + int getMaxFps() { return m_maxFps; } + float getMediumFrameDelay() { return m_mediumFrameDelay; } + +private: + int m_frames; + int m_partialFrames; + ticks_t m_partialFpsDelta; + float m_partialFps; + float m_maxPartialFps; + ticks_t m_frameDelaySum; + ticks_t m_mediumFrameDelay; + ticks_t m_lastFrame; + int m_lastFps; + int m_maxFps; + ticks_t m_bestFrameDelay; + ticks_t m_lastFpsUpdate; + ticks_t m_lastPartialFpsUpdate; + float m_sleepMicros; +}; + +#endif diff --git a/src/framework/core/clock.h b/src/framework/core/clock.h index b303351e..e7cda832 100644 --- a/src/framework/core/clock.h +++ b/src/framework/core/clock.h @@ -35,7 +35,7 @@ public: ticks_t asyncTicks(); ticks_t ticks() { return m_currentTicks; } - ticks_t ticksElapsed(long prevTicks) { return m_currentTicks - prevTicks; } + ticks_t ticksElapsed(ticks_t prevTicks) { return m_currentTicks - prevTicks; } ticks_t ticksFor(int delay) { return m_currentTicks + delay; } float asyncTime() { return asyncTicks()/1000.0f; } diff --git a/src/framework/graphics/framebuffer.cpp b/src/framework/graphics/framebuffer.cpp index 6d3247fd..fe4fc76e 100644 --- a/src/framework/graphics/framebuffer.cpp +++ b/src/framework/graphics/framebuffer.cpp @@ -75,7 +75,7 @@ void FrameBuffer::resize(const Size& size) logFatal("Unable to setup framebuffer object"); internalRelease(); } else { - m_screenBackup = TexturePtr(new Texture(size.width(), size.height(), 4)); + m_screenBackup = TexturePtr(new Texture(size.width(), size.height())); } } @@ -113,6 +113,12 @@ void FrameBuffer::release() g_graphics.setViewportSize(m_oldViewportSize); } +void FrameBuffer::draw() +{ + Rect rect(0,0, getSize()); + g_painter->drawTexturedRect(rect, m_texture, rect); +} + void FrameBuffer::draw(const Rect& dest, const Rect& src) { g_painter->drawTexturedRect(dest, m_texture, src); @@ -132,9 +138,7 @@ void FrameBuffer::internalBind() boundFbo = m_fbo; } else { // backup screen color buffer into a texture - m_screenBackup->bind(); - Size size = getSize(); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.width(), size.height()); + m_screenBackup->copyFromScreen(Rect(0, 0, getSize())); } } @@ -145,16 +149,14 @@ void FrameBuffer::internalRelease() glBindFramebuffer(GL_FRAMEBUFFER, m_prevBoundFbo); boundFbo = m_prevBoundFbo; } else { - Size size = getSize(); + Rect screenRect(0, 0, getSize()); // copy the drawn color buffer into the framebuffer texture - m_texture->bind(); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.width(), size.height()); + m_texture->copyFromScreen(screenRect); // restore screen original content - glDisable(GL_BLEND); - g_painter->drawTexturedRect(Rect(0, 0, size), m_screenBackup, Rect(0, 0, size)); - glEnable(GL_BLEND); + g_painter->setCompositionMode(Painter::CompositionMode_Replace); + g_painter->drawTexturedRect(screenRect, m_screenBackup, screenRect); } } diff --git a/src/framework/graphics/framebuffer.h b/src/framework/graphics/framebuffer.h index 846e9c9c..3afbe925 100644 --- a/src/framework/graphics/framebuffer.h +++ b/src/framework/graphics/framebuffer.h @@ -37,6 +37,7 @@ public: void bind(); void clear(const Color& color = Color::black, const Rect& rect = Rect()); void release(); + void draw(); void draw(const Rect& dest); void draw(const Rect& dest, const Rect& src); diff --git a/src/framework/graphics/graphics.cpp b/src/framework/graphics/graphics.cpp index b54e6cc2..f3cdf857 100644 --- a/src/framework/graphics/graphics.cpp +++ b/src/framework/graphics/graphics.cpp @@ -88,7 +88,6 @@ void Graphics::init() selectPainterEngine(m_prefferedPainterEngine); m_emptyTexture = TexturePtr(new Texture); - } void Graphics::terminate() diff --git a/src/framework/graphics/painter.cpp b/src/framework/graphics/painter.cpp index f68a7ee9..41eaa905 100644 --- a/src/framework/graphics/painter.cpp +++ b/src/framework/graphics/painter.cpp @@ -53,7 +53,7 @@ void Painter::refreshState() updateGlTexture(); } -void Painter::saveAndResetState() +void Painter::saveState() { assert(m_oldStateIndex<10); m_olderStates[m_oldStateIndex].projectionMatrix = m_projectionMatrix; @@ -65,6 +65,11 @@ void Painter::saveAndResetState() m_olderStates[m_oldStateIndex].shaderProgram = m_shaderProgram; m_olderStates[m_oldStateIndex].texture = m_texture; m_oldStateIndex++; +} + +void Painter::saveAndResetState() +{ + saveState(); resetState(); } diff --git a/src/framework/graphics/painter.h b/src/framework/graphics/painter.h index c2991a33..ea4d00cf 100644 --- a/src/framework/graphics/painter.h +++ b/src/framework/graphics/painter.h @@ -62,6 +62,7 @@ public: void resetState(); virtual void refreshState(); + void saveState(); void saveAndResetState(); void restoreSavedState(); diff --git a/src/framework/graphics/particle.cpp b/src/framework/graphics/particle.cpp index 4f1bfa13..2a277601 100644 --- a/src/framework/graphics/particle.cpp +++ b/src/framework/graphics/particle.cpp @@ -50,9 +50,10 @@ void Particle::render() if(!m_texture) g_painter->drawFilledRect(m_rect); else { + g_painter->saveState(); g_painter->setCompositionMode(m_compositionMode); g_painter->drawTexturedRect(m_rect, m_texture); - g_painter->setCompositionMode(Painter::CompositionMode_Normal); + g_painter->restoreSavedState(); } } diff --git a/src/framework/graphics/texture.cpp b/src/framework/graphics/texture.cpp index 9b9aae0b..d96c2bf1 100644 --- a/src/framework/graphics/texture.cpp +++ b/src/framework/graphics/texture.cpp @@ -129,6 +129,12 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int return id; } +void Texture::copyFromScreen(const Rect& screenRect) +{ + bind(); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screenRect.x(), screenRect.y(), screenRect.width(), screenRect.height()); +} + void Texture::bind() { // must reset painter texture state diff --git a/src/framework/graphics/texture.h b/src/framework/graphics/texture.h index 30eb759d..38838826 100644 --- a/src/framework/graphics/texture.h +++ b/src/framework/graphics/texture.h @@ -30,9 +30,11 @@ class Texture : public std::enable_shared_from_this public: Texture(); Texture(const ImagePtr& image); - Texture(int width, int height, int channels, uchar* pixels = NULL); + Texture(int width, int height, int channels = 4, uchar* pixels = NULL); virtual ~Texture(); + void copyFromScreen(const Rect& screenRect); + void bind(); /// Tries to generate mipmaps via hardware, otherwise fallback to software implementation diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index 3d0153ad..fbe2a28e 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -390,11 +390,6 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("isShiftNavigation", &UITextEdit::isShiftNavigation); g_lua.bindClassMemberFunction("isMultiline", &UITextEdit::isMultiline); - // UIFrameCounter - g_lua.registerClass(); - g_lua.bindClassStaticFunction("create", []{ return UIFrameCounterPtr(new UIFrameCounter); } ); - g_lua.bindClassMemberFunction("getFrameCount", &UIFrameCounter::getFrameCount); - // Protocol g_lua.registerClass(); //g_lua.bindClassMemberFunction("connect", &Protocol::connect); @@ -457,10 +452,23 @@ void Application::registerLuaFunctions() // Application g_lua.registerStaticClass("g_app"); g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, g_app)); - g_lua.bindClassStaticFunction("g_app", "setFrameSleep", std::bind(&Application::setFrameSleep, g_app, std::placeholders::_1)); + g_lua.bindClassStaticFunction("g_app", "setForegroundPaneMaxFps", std::bind(&Application::setForegroundPaneMaxFps, g_app, std::placeholders::_1)); + g_lua.bindClassStaticFunction("g_app", "setBackgroundPaneMaxFps", std::bind(&Application::setBackgroundPaneMaxFps, g_app, std::placeholders::_1)); + g_lua.bindClassStaticFunction("g_app", "isRunning", std::bind(&Application::isRunning, g_app)); + g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app)); + g_lua.bindClassStaticFunction("g_app", "isOnInputEvent", std::bind(&Application::isOnInputEvent, g_app)); + g_lua.bindClassStaticFunction("g_app", "getName", std::bind(&Application::getName, g_app)); + g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, g_app)); + g_lua.bindClassStaticFunction("g_app", "getForegroundPaneFps", std::bind(&Application::getForegroundPaneFps, g_app)); + g_lua.bindClassStaticFunction("g_app", "getBackgroundPaneFps", std::bind(&Application::getBackgroundPaneFps, g_app)); + g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app)); + g_lua.bindClassStaticFunction("g_app", "getBuildDate", std::bind(&Application::getBuildDate, g_app)); + g_lua.bindClassStaticFunction("g_app", "getBuildRevision", std::bind(&Application::getBuildRevision, g_app)); + g_lua.bindClassStaticFunction("g_app", "getBuildType", std::bind(&Application::getBuildType, g_app)); + + g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, g_app)); g_lua.bindClassStaticFunction("g_app", "isRunning", std::bind(&Application::isRunning, g_app)); g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app)); - g_lua.bindClassStaticFunction("g_app", "getFrameSleep", std::bind(&Application::getFrameSleep, g_app)); g_lua.bindClassStaticFunction("g_app", "getName", std::bind(&Application::getName, g_app)); g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, g_app)); g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app)); diff --git a/src/framework/platform/win32window.cpp b/src/framework/platform/win32window.cpp index 70bf5aa8..6811bff9 100644 --- a/src/framework/platform/win32window.cpp +++ b/src/framework/platform/win32window.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #define HSB_BIT_SET(p, n) (p[(n)/8] |= (128 >>((n)%8))) @@ -305,7 +304,7 @@ void WIN32Window::internalChooseGLVisual() PFD_TYPE_RGBA, 32, // Select Our Color Depth 0, 0, 0, 0, 0, 0, // Color Bits Ignored - 0, // No Alpha Buffer + 1, // Alpha Buffer Bits 0, // Shift Bit Ignored 0, // No Accumulation Buffer 0, 0, 0, 0, // Accumulation Bits Ignored diff --git a/src/framework/platform/x11window.cpp b/src/framework/platform/x11window.cpp index 8df61040..7c659d2b 100644 --- a/src/framework/platform/x11window.cpp +++ b/src/framework/platform/x11window.cpp @@ -371,6 +371,7 @@ void X11Window::internalChooseGLVisual() #else EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, #endif + EGL_ALPHA_SIZE, 1, EGL_NONE }; @@ -386,6 +387,7 @@ void X11Window::internalChooseGLVisual() static int attrList[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DOUBLEBUFFER, True, + GLX_ALPHA_SIZE, 1, None }; diff --git a/src/framework/sound/streamsoundsource.h b/src/framework/sound/streamsoundsource.h index 5c756706..676b3699 100644 --- a/src/framework/sound/streamsoundsource.h +++ b/src/framework/sound/streamsoundsource.h @@ -28,7 +28,7 @@ class StreamSoundSource : public SoundSource { enum { - STREAM_BUFFER_SIZE = 1024 * 500, + STREAM_BUFFER_SIZE = 1024 * 100, STREAM_FRAGMENTS = 5, STREAM_FRAGMENT_SIZE = STREAM_BUFFER_SIZE / STREAM_FRAGMENTS }; diff --git a/src/framework/stdext/stdext.h b/src/framework/stdext/stdext.h index 44fe9421..7800d36a 100644 --- a/src/framework/stdext/stdext.h +++ b/src/framework/stdext/stdext.h @@ -31,5 +31,6 @@ #include "math.h" #include "string.h" #include "dumper.h" +#include "time.h" #endif diff --git a/src/framework/ui/uiframecounter.cpp b/src/framework/stdext/time.h similarity index 52% rename from src/framework/ui/uiframecounter.cpp rename to src/framework/stdext/time.h index 49a9416e..236e3e0c 100644 --- a/src/framework/ui/uiframecounter.cpp +++ b/src/framework/stdext/time.h @@ -20,43 +20,22 @@ * THE SOFTWARE. */ -#include "uiframecounter.h" -#include "uitranslator.h" -#include -#include -#include -#include +#ifndef STDEXT_TIME_H +#define STDEXT_TIME_H -UIFrameCounter::UIFrameCounter() -{ - m_focusable = false; - m_phantom = true; - m_align = Fw::AlignLeft; - m_lastFrameTicks = g_clock.ticks(); - m_frameCount = 0; -} +#include "types.h" +#include +#include -void UIFrameCounter::drawSelf() -{ - UIWidget::drawSelf(); +namespace stdext { - if(g_clock.ticksElapsed(m_lastFrameTicks) >= 1000) { - m_fpsText = stdext::format("FPS: %d", m_frameCount); - m_lastFrameTicks = g_clock.ticks(); - m_frameCount = 0; - } - m_frameCount++; +const static std::chrono::system_clock::time_point startup_time = std::chrono::high_resolution_clock::now(); +inline ticks_t millis() { return std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - startup_time).count(); } +inline ticks_t micros() { return std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - startup_time).count(); } +inline void millisleep(uint32 ms) { usleep(ms * 1000); }; +inline void microsleep(uint32 us) { usleep(us); }; - g_painter->setColor(Color::white); - m_font->drawText(m_fpsText, m_rect, m_align); } -void UIFrameCounter::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode) -{ - UIWidget::onStyleApply(styleName, styleNode); +#endif - for(const OTMLNodePtr& node : styleNode->children()) { - if(node->tag() == "align") - setAlign(Fw::translateAlignment(node->value())); - } -} \ No newline at end of file diff --git a/src/framework/stdext/types.h b/src/framework/stdext/types.h index eae7ce00..2fbd17c8 100644 --- a/src/framework/stdext/types.h +++ b/src/framework/stdext/types.h @@ -39,8 +39,6 @@ typedef int32_t int32; typedef int16_t int16; typedef int8_t int8; -// note that on 32 bit platforms the max ticks will overflow for values above 2,147,483,647 -// thus this means that the app may cause unknown behavior after running 24 days without restarting -typedef long ticks_t; +typedef int64_t ticks_t; #endif diff --git a/src/framework/ui/declarations.h b/src/framework/ui/declarations.h index 74adaa5c..10635fb4 100644 --- a/src/framework/ui/declarations.h +++ b/src/framework/ui/declarations.h @@ -28,7 +28,6 @@ class UIManager; class UIWidget; class UITextEdit; -class UIFrameCounter; class UILayout; class UIBoxLayout; class UIHorizontalLayout; @@ -40,7 +39,6 @@ typedef std::shared_ptr UIWidgetPtr; typedef std::weak_ptr UIWidgetWeakPtr; typedef std::shared_ptr UITextEditPtr; -typedef std::shared_ptr UIFrameCounterPtr; typedef std::shared_ptr UILayoutPtr; typedef std::shared_ptr UIBoxLayoutPtr; typedef std::shared_ptr UIHorizontalLayoutPtr; diff --git a/src/framework/ui/ui.h b/src/framework/ui/ui.h index bfbb4007..01af51ee 100644 --- a/src/framework/ui/ui.h +++ b/src/framework/ui/ui.h @@ -26,7 +26,6 @@ #include "uimanager.h" #include "uiwidget.h" #include "uitextedit.h" -#include "uiframecounter.h" #include "uilayout.h" #include "uihorizontallayout.h" #include "uiverticallayout.h" diff --git a/src/framework/ui/uimanager.cpp b/src/framework/ui/uimanager.cpp index 295779ab..7e67c3b2 100644 --- a/src/framework/ui/uimanager.cpp +++ b/src/framework/ui/uimanager.cpp @@ -27,6 +27,7 @@ #include #include #include +#include UIManager g_ui; @@ -51,9 +52,9 @@ void UIManager::terminate() m_pressedWidget = nullptr; } -void UIManager::render() +void UIManager::render(bool foregroundPane) { - m_rootWidget->draw(m_rootWidget->getRect()); + m_rootWidget->draw(m_rootWidget->getRect(), foregroundPane); } void UIManager::resize(const Size& size) diff --git a/src/framework/ui/uimanager.h b/src/framework/ui/uimanager.h index b949e287..820e2445 100644 --- a/src/framework/ui/uimanager.h +++ b/src/framework/ui/uimanager.h @@ -33,7 +33,7 @@ public: void init(); void terminate(); - void render(); + void render(bool foregroundPane); void resize(const Size& size); void inputEvent(const InputEvent& event); diff --git a/src/framework/ui/uitextedit.cpp b/src/framework/ui/uitextedit.cpp index adf2e5bb..f8ff901a 100644 --- a/src/framework/ui/uitextedit.cpp +++ b/src/framework/ui/uitextedit.cpp @@ -40,8 +40,11 @@ UITextEdit::UITextEdit() blinkCursor(); } -void UITextEdit::drawSelf() +void UITextEdit::drawSelf(bool foregroundPane) { + if(!foregroundPane) + return; + drawBackground(m_rect); drawBorder(m_rect); drawImage(m_rect); diff --git a/src/framework/ui/uitextedit.h b/src/framework/ui/uitextedit.h index 2c7437fb..58c5582a 100644 --- a/src/framework/ui/uitextedit.h +++ b/src/framework/ui/uitextedit.h @@ -30,7 +30,7 @@ class UITextEdit : public UIWidget public: UITextEdit(); - virtual void drawSelf(); + void drawSelf(bool foregroundPane); private: void update(); diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 0a74ea95..61d99471 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -30,6 +30,7 @@ #include #include #include +#include UIWidget::UIWidget() { @@ -51,26 +52,29 @@ UIWidget::~UIWidget() #endif } -void UIWidget::draw(const Rect& visibleRect) +void UIWidget::draw(const Rect& visibleRect, bool foregroundPane) { if(m_clipping) g_painter->setClipRect(visibleRect); - drawSelf(); + drawSelf(foregroundPane); if(m_children.size() > 0) { if(m_clipping) g_painter->setClipRect(visibleRect.intersection(getClippingRect())); - drawChildren(visibleRect); + drawChildren(visibleRect, foregroundPane); } if(m_clipping) g_painter->resetClipRect(); } -void UIWidget::drawSelf() +void UIWidget::drawSelf(bool foregroundPane) { + if(!foregroundPane) + return; + // draw style components in order if(m_backgroundColor.aF() > Fw::MIN_ALPHA) { Rect backgroundDestRect = m_rect; @@ -84,7 +88,7 @@ void UIWidget::drawSelf() drawText(m_rect); } -void UIWidget::drawChildren(const Rect& visibleRect) +void UIWidget::drawChildren(const Rect& visibleRect, bool foregroundPane) { // draw children for(const UIWidgetPtr& child : m_children) { @@ -103,10 +107,10 @@ void UIWidget::drawChildren(const Rect& visibleRect) if(child->getOpacity() < oldOpacity) g_painter->setOpacity(child->getOpacity()); - child->draw(childVisibleRect); + child->draw(childVisibleRect, foregroundPane); // debug draw box - if(g_ui.isDrawingDebugBoxes()) { + if(foregroundPane && g_ui.isDrawingDebugBoxes()) { g_painter->setColor(Color::green); g_painter->drawBoundingRect(child->getRect()); } @@ -807,6 +811,7 @@ bool UIWidget::setRect(const Rect& rect) if(rect == oldRect) return false; + g_app->repaint(); m_rect = rect; // updates own layout diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index 9c345a73..8e07d071 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -52,9 +52,9 @@ public: virtual ~UIWidget(); protected: - virtual void draw(const Rect& visibleRect); - virtual void drawSelf(); - virtual void drawChildren(const Rect& visibleRect); + virtual void draw(const Rect& visibleRect, bool foregroundPane); + virtual void drawSelf(bool foregroundPane); + virtual void drawChildren(const Rect& visibleRect, bool foregroundPane); friend class UIManager; diff --git a/src/otclient/core/creature.cpp b/src/otclient/core/creature.cpp index 8a3913f9..5953f6cb 100644 --- a/src/otclient/core/creature.cpp +++ b/src/otclient/core/creature.cpp @@ -148,6 +148,7 @@ void Creature::internalDrawOutfit(const Point& dest, float scaleFactor, bool ani Point p = dest + (-Point(w,h)*Otc::TILE_PIXELS)*scaleFactor; if(getLayers() > 1) { + g_painter->saveState(); g_painter->setCompositionMode(Painter::CompositionMode_Multiply); g_painter->setColor(m_outfit.getHeadColor()); @@ -159,8 +160,7 @@ void Creature::internalDrawOutfit(const Point& dest, float scaleFactor, bool ani g_painter->setColor(m_outfit.getFeetColor()); m_type->drawMask(p, scaleFactor, w, h, xPattern, yPattern, zPattern, 1, animationPhase, ThingType::BlueMask); - g_painter->resetColor(); - g_painter->resetCompositionMode(); + g_painter->restoreSavedState(); } } } diff --git a/src/otclient/core/mapview.cpp b/src/otclient/core/mapview.cpp index 98d86b15..c5e594d2 100644 --- a/src/otclient/core/mapview.cpp +++ b/src/otclient/core/mapview.cpp @@ -77,11 +77,13 @@ void MapView::draw(const Rect& rect) Rect clearRect = Rect(0, 0, m_drawDimension * m_tileSize); // drawing a black rect is actually faster than FrameBuffer::clear() + /* g_painter->setColor(Color::black); g_painter->drawFilledRect(clearRect); g_painter->setColor(Color::white); + */ - // m_framebuffer->clear(Color::black); + m_framebuffer->clear(Color::black); } auto it = m_cachedVisibleTiles.begin(); @@ -131,6 +133,7 @@ void MapView::draw(const Rect& rect) drawOffset.y += (srcVisible.height() - srcSize.height()) / 2; Rect srcRect = Rect(drawOffset, srcSize); + g_painter->saveState(); g_painter->setColor(Color::white); g_painter->setCompositionMode(Painter::CompositionMode_Replace); #if 0 @@ -146,8 +149,8 @@ void MapView::draw(const Rect& rect) m_framebuffer->draw(rect, srcRect); #endif - g_painter->resetCompositionMode(); - g_painter->resetShaderProgram(); + g_painter->restoreSavedState(); + //g_painter->resetShaderProgram(); // this could happen if the player position is not known yet if(!cameraPosition.isValid()) diff --git a/src/otclient/ui/uicreature.cpp b/src/otclient/ui/uicreature.cpp index ae83bb19..6000e8b7 100644 --- a/src/otclient/ui/uicreature.cpp +++ b/src/otclient/ui/uicreature.cpp @@ -24,9 +24,12 @@ #include #include -void UICreature::drawSelf() +void UICreature::drawSelf(bool foregroundPane) { - UIWidget::drawSelf(); + if(!foregroundPane) + return; + + UIWidget::drawSelf(foregroundPane); if(m_creature) { g_painter->setColor(Color::white); diff --git a/src/otclient/ui/uicreature.h b/src/otclient/ui/uicreature.h index 2a98e0dc..1cfd0c86 100644 --- a/src/otclient/ui/uicreature.h +++ b/src/otclient/ui/uicreature.h @@ -30,7 +30,7 @@ class UICreature : public UIWidget { public: - void drawSelf(); + void drawSelf(bool foregroundPane); void setCreature(const CreaturePtr& creature) { m_creature = creature; } void setFixedCreatureSize(bool fixed) { m_fixedCreatureSize = fixed; } diff --git a/src/otclient/ui/uiitem.cpp b/src/otclient/ui/uiitem.cpp index ccd285cb..f90ffada 100644 --- a/src/otclient/ui/uiitem.cpp +++ b/src/otclient/ui/uiitem.cpp @@ -30,9 +30,12 @@ UIItem::UIItem() m_dragable = true; } -void UIItem::drawSelf() +void UIItem::drawSelf(bool foregroundPane) { - UIWidget::drawSelf(); + if(!foregroundPane) + return; + + UIWidget::drawSelf(foregroundPane); if(m_item) { Rect drawRect = getClippingRect(); diff --git a/src/otclient/ui/uiitem.h b/src/otclient/ui/uiitem.h index a4108048..975f59a3 100644 --- a/src/otclient/ui/uiitem.h +++ b/src/otclient/ui/uiitem.h @@ -31,7 +31,7 @@ class UIItem : public UIWidget { public: UIItem(); - void drawSelf(); + void drawSelf(bool foregroundPane); void setItemId(int id); void setItemCount(int count) { if(m_item) m_item->setCount(count); } diff --git a/src/otclient/ui/uimap.cpp b/src/otclient/ui/uimap.cpp index c957e188..3f26fcb8 100644 --- a/src/otclient/ui/uimap.cpp +++ b/src/otclient/ui/uimap.cpp @@ -45,16 +45,24 @@ UIMap::~UIMap() g_map.removeMapView(m_mapView); } -void UIMap::drawSelf() +void UIMap::drawSelf(bool foregroundPane) { - UIWidget::drawSelf(); - - // draw map border - g_painter->setColor(Color::black); - g_painter->drawBoundingRect(m_mapRect.expanded(1)); - - g_painter->setColor(Color::white); - m_mapView->draw(m_mapRect); + if(foregroundPane) { + UIWidget::drawSelf(foregroundPane); + + // draw map border + g_painter->setColor(Color::black); + g_painter->drawBoundingRect(m_mapRect.expanded(1)); + + g_painter->saveState(); + g_painter->setCompositionMode(Painter::CompositionMode_Replace); + g_painter->setColor(Color::alpha); + g_painter->drawFilledRect(m_mapRect); + g_painter->restoreSavedState(); + } else { + g_painter->setColor(Color::white); + m_mapView->draw(m_mapRect); + } } bool UIMap::setZoom(int zoom) diff --git a/src/otclient/ui/uimap.h b/src/otclient/ui/uimap.h index 2cb0ff59..00ff08df 100644 --- a/src/otclient/ui/uimap.h +++ b/src/otclient/ui/uimap.h @@ -35,7 +35,7 @@ public: UIMap(); ~UIMap(); - void drawSelf(); + void drawSelf(bool foregroundPane); bool setZoom(int zoom); bool zoomIn();