From a639ae4002ffa9497e2e60b3b2382a67d618f32a Mon Sep 17 00:00:00 2001 From: Nathanael Gentry Date: Tue, 11 Aug 2020 12:54:14 -0400 Subject: [PATCH] GRAPHICS: MACGUI: Add WM direct copy mode This does not keep the current screen state cached in a surface, but re-creates the screen from its various components each time something changes. This avoids an intermediate blitting. The mode is enabled just by not providing a surface to the WM, and instead providing a width and height. --- graphics/macgui/macmenu.h | 4 ++ graphics/macgui/macwindow.cpp | 2 +- graphics/macgui/macwindow.h | 41 +++++++++++++---- graphics/macgui/macwindowmanager.cpp | 69 ++++++++++++++++++++++------ graphics/macgui/macwindowmanager.h | 11 +++++ 5 files changed, 103 insertions(+), 24 deletions(-) diff --git a/graphics/macgui/macmenu.h b/graphics/macgui/macmenu.h index b579fd21fc2..b41d081683e 100644 --- a/graphics/macgui/macmenu.h +++ b/graphics/macgui/macmenu.h @@ -52,6 +52,10 @@ public: MacMenu(int id, const Common::Rect &bounds, MacWindowManager *wm); ~MacMenu(); + virtual ManagedSurface *getBorderSurface() override { return nullptr; } + virtual const Common::Rect &getInnerDimensions() override { return _dims; } + virtual bool isDirty() override { return _contentIsDirty || _dimensionsDirty; } + static Common::StringArray *readMenuFromResource(Common::SeekableReadStream *res); static MacMenu *createMenuFromPEexe(Common::PEResources *exe, MacWindowManager *wm); diff --git a/graphics/macgui/macwindow.cpp b/graphics/macgui/macwindow.cpp index 861f8447d8c..922a9452e9a 100644 --- a/graphics/macgui/macwindow.cpp +++ b/graphics/macgui/macwindow.cpp @@ -191,7 +191,7 @@ void MacWindow::center(bool toCenter) { if (!_wm) return; - Common::Rect screen = _wm->_screen->getBounds(); + Common::Rect screen = _wm->getScreenBounds(); if (toCenter) { move((screen.width() - _dims.width()) / 2, (screen.height() - _dims.height()) / 2); diff --git a/graphics/macgui/macwindow.h b/graphics/macgui/macwindow.h index 515c5cee8b3..7f87a34f1c9 100644 --- a/graphics/macgui/macwindow.h +++ b/graphics/macgui/macwindow.h @@ -115,6 +115,28 @@ public: */ ManagedSurface *getWindowSurface() { return _composeSurface; } + /** + * Method to access the border surface of the window. + * @return A pointer to the border surface of the window. + */ + virtual ManagedSurface *getBorderSurface() = 0; + + /** + * Accessor to retrieve the dimensions of the inner surface of the window + * (i.e. without taking borders into account). + * Note that the returned dimensions' position is relative to the WM's + * screen, just like in getDimensions(). + * @return The inner dimensions of the window. + */ + virtual const Common::Rect &getInnerDimensions() = 0; + + /** + * Method called to internally draw the window. This relies on the window + * being marked as dirty unless otherwise specified. + * @param forceRedraw Its behavior depends on the subclass. + */ + virtual bool draw(bool forceRedraw = false) = 0; + /** * Method called to draw the window into the target surface. * This method is most often called by the WM, and relies on @@ -133,6 +155,11 @@ public: */ virtual bool processEvent(Common::Event &event) = 0; + /** + * Method that checks if the window is needs redrawing. + */ + virtual bool isDirty() = 0; + /** * Set the callback that will be used when an event needs to be processed. * @param callback A function pointer to a function that accepts: @@ -196,15 +223,6 @@ public: */ virtual void setDimensions(const Common::Rect &r) override; - /** - * Accessor to retrieve the dimensions of the inner surface of the window - * (i.e. without taking borders into account). - * Note that the returned dimensions' position is relative to the WM's - * screen, just like in getDimensions(). - * @return The inner dimensions of the window. - */ - const Common::Rect &getInnerDimensions() { return _innerDims; } - /** * Set a background pattern for the window. * @param pattern @@ -221,6 +239,9 @@ public: virtual bool draw(bool forceRedraw = false) override; virtual void blit(ManagedSurface *g, Common::Rect &dest) override; + virtual const Common::Rect &getInnerDimensions() override { return _innerDims; } + virtual ManagedSurface *getBorderSurface() override { return &_borderSurface; } + /** * Centers the window using the dimensions of the parent window manager, or undoes this; does * nothing if WM is null. @@ -307,6 +328,8 @@ public: void markAllDirty(); void mergeDirtyRects(); + virtual bool isDirty() override { return _borderIsDirty || _contentIsDirty; } + private: void prepareBorderSurface(ManagedSurface *g); void drawSimpleBorder(ManagedSurface *g); diff --git a/graphics/macgui/macwindowmanager.cpp b/graphics/macgui/macwindowmanager.cpp index a739787ccd0..2524c646ce4 100644 --- a/graphics/macgui/macwindowmanager.cpp +++ b/graphics/macgui/macwindowmanager.cpp @@ -156,7 +156,7 @@ static const byte macCursorCrossBar[] = { static void menuTimerHandler(void *refCon); MacWindowManager::MacWindowManager(uint32 mode, MacPatterns *patterns) { - _screen = 0; + _screen = nullptr; _screenCopy = nullptr; _desktopBmp = nullptr; _desktop = nullptr; @@ -243,6 +243,17 @@ void MacWindowManager::setScreen(ManagedSurface *screen) { drawDesktop(); } +void MacWindowManager::setScreen(int w, int h) { + if (_desktop) + _desktop->free(); + else + _desktop = new ManagedSurface(); + + _screenDims = Common::Rect(w, h); + _desktop->create(w, h, PixelFormat::createFormatCLUT8()); + drawDesktop(); +} + void MacWindowManager::setMode(uint32 mode) { _mode = mode; @@ -300,7 +311,7 @@ MacMenu *MacWindowManager::addMenu() { delete _menu; } - _menu = new MacMenu(getNextId(), _screen->getBounds(), this); + _menu = new MacMenu(getNextId(), getScreenBounds(), this); _windows[_menu->getId()] = _menu; @@ -327,6 +338,9 @@ void MacWindowManager::activateMenu() { } void MacWindowManager::activateScreenCopy() { + if (!_screen) + return; + if (!_screenCopy) _screenCopy = new ManagedSurface(*_screen); // Create a copy else @@ -463,19 +477,22 @@ void MacWindowManager::drawDesktop() { } void MacWindowManager::draw() { - assert(_screen); - removeMarked(); if (_fullRefresh) { - if (!(_mode & kWMModeNoDesktop)) { - if (_desktop->w != _screen->w || _desktop->h != _screen->h) { - _desktop->free(); - _desktop->create(_screen->w, _screen->h, PixelFormat::createFormatCLUT8()); - drawDesktop(); - } + Common::Rect screen = getScreenBounds(); + if (_desktop->w != screen.width() || _desktop->h != screen.height()) { + _desktop->free(); + _desktop->create(screen.width(), screen.height(), PixelFormat::createFormatCLUT8()); + drawDesktop(); + } + + if (_screen) { _screen->blitFrom(*_desktop, Common::Point(0, 0)); g_system->copyRectToScreen(_screen->getPixels(), _screen->pitch, 0, 0, _screen->w, _screen->h); + } else { + _screenCopyPauseToken = new PauseToken(pauseEngine()); + g_system->copyRectToScreen(_desktop->getPixels(), _desktop->pitch, 0, 0, _desktop->w, _desktop->h); } if (_redrawEngineCallback != nullptr) @@ -489,7 +506,7 @@ void MacWindowManager::draw() { continue; Common::Rect clip = w->getDimensions(); - clip.clip(_screen->getBounds()); + clip.clip(getScreenBounds()); clip.clip(Common::Rect(0, 0, g_system->getWidth() - 1, g_system->getHeight() - 1)); if (clip.isEmpty()) @@ -505,7 +522,26 @@ void MacWindowManager::draw() { } } - if (w->draw(_screen, forceRedraw)) { + if (!_screen) { + if (w->isDirty() || forceRedraw) { + w->draw(forceRedraw); + + Common::Rect dims = w->getDimensions(); + Common::Rect innerDims = w->getInnerDimensions(); + + g_system->copyRectToScreen(w->getBorderSurface()->getBasePtr(0, 0), w->getBorderSurface()->pitch, clip.left, clip.top, dims.width(), dims.height()); + + g_system->copyRectToScreen(w->getWindowSurface()->getBasePtr(MAX(clip.left - innerDims.left, 0), MAX(clip.top - innerDims.top, 0)), w->getWindowSurface()->pitch, clip.left + (-dims.left + innerDims.left), clip.top + (-dims.top + innerDims.top), innerDims.width(), innerDims.height()); + + dirtyRects.push_back(clip); + } + + if (_screenCopyPauseToken) { + _screenCopyPauseToken->clear(); + delete _screenCopyPauseToken; + _screenCopyPauseToken = nullptr; + } + } else if (w->draw(_screen, forceRedraw)) { w->setDirty(false); g_system->copyRectToScreen(_screen->getBasePtr(clip.left, clip.top), _screen->pitch, clip.left, clip.top, clip.width(), clip.height()); dirtyRects.push_back(clip); @@ -513,8 +549,13 @@ void MacWindowManager::draw() { } // Menu is drawn on top of everything and always - if (_menu && !(_mode & kWMModeFullscreen)) - _menu->draw(_screen, _fullRefresh); + if (_menu && !(_mode & kWMModeFullscreen)) { + if (_screen) { + _menu->draw(_screen, _fullRefresh); + } else { + g_system->copyRectToScreen(_menu->getWindowSurface()->getBasePtr(_menu->_dims.left, _menu->_dims.top), _menu->getWindowSurface()->pitch, _menu->_dims.left, _menu->_dims.top, _menu->_dims.width(), _menu->_dims.height()); + } + } _fullRefresh = false; } diff --git a/graphics/macgui/macwindowmanager.h b/graphics/macgui/macwindowmanager.h index e1760291101..3da91e72e73 100644 --- a/graphics/macgui/macwindowmanager.h +++ b/graphics/macgui/macwindowmanager.h @@ -147,6 +147,14 @@ public: * @param screen Surface on which the desktop will be drawn. */ void setScreen(ManagedSurface *screen); + + /** + * Mutator to indicate the dimensions of the desktop, when a backing surface is not used. + * Note that this method should be called as soon as the WM is created. + * @param screen Surface on which the desktop will be drawn. + */ + void setScreen(int w, int h); + /** * Create a window with the given parameters. * Note that this method allocates the necessary memory for the window. @@ -258,6 +266,8 @@ public: MacWidget *getActiveWidget() { return _activeWidget; } + Common::Rect getScreenBounds() { return _screen ? _screen->getBounds() : _screenDims; } + void clearWidgetRefs(MacWidget *widget); void pushCursor(MacCursorType type, Cursor *cursor = nullptr); @@ -325,6 +335,7 @@ public: ManagedSurface *_screen; ManagedSurface *_screenCopy; + Common::Rect _screenDims; private: Common::List _windowStack;