diff --git a/src/blitter/32bpp_anim.cpp b/src/blitter/32bpp_anim.cpp index 24d22658a3..38cc577cda 100644 --- a/src/blitter/32bpp_anim.cpp +++ b/src/blitter/32bpp_anim.cpp @@ -509,7 +509,7 @@ void Blitter_32bppAnim::CopyToBuffer(const void *video, void *dst, int width, in } } -void Blitter_32bppAnim::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) +void Blitter_32bppAnim::ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) { assert(!_screen_disable_anim); assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch); diff --git a/src/blitter/32bpp_anim.hpp b/src/blitter/32bpp_anim.hpp index 9a8582089a..1ff745632c 100644 --- a/src/blitter/32bpp_anim.hpp +++ b/src/blitter/32bpp_anim.hpp @@ -44,7 +44,7 @@ public: void DrawRect(void *video, int width, int height, uint8 colour) override; void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override; - void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; + void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) override; int BufferSize(int width, int height) override; void PaletteAnimate(const Palette &palette) override; Blitter::PaletteAnimation UsePaletteAnimation() override; diff --git a/src/blitter/32bpp_base.cpp b/src/blitter/32bpp_base.cpp index 344a8c315e..1dc7e444fb 100644 --- a/src/blitter/32bpp_base.cpp +++ b/src/blitter/32bpp_base.cpp @@ -101,7 +101,7 @@ void Blitter_32bppBase::CopyImageToBuffer(const void *video, void *dst, int widt } } -void Blitter_32bppBase::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) +void Blitter_32bppBase::ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) { const uint32 *src; uint32 *dst; diff --git a/src/blitter/32bpp_base.hpp b/src/blitter/32bpp_base.hpp index 07a345469c..4992c3e022 100644 --- a/src/blitter/32bpp_base.hpp +++ b/src/blitter/32bpp_base.hpp @@ -28,7 +28,7 @@ public: void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override; - void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; + void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) override; int BufferSize(int width, int height) override; void PaletteAnimate(const Palette &palette) override; Blitter::PaletteAnimation UsePaletteAnimation() override; diff --git a/src/blitter/8bpp_base.cpp b/src/blitter/8bpp_base.cpp index cc7396b99d..ed91ed1d20 100644 --- a/src/blitter/8bpp_base.cpp +++ b/src/blitter/8bpp_base.cpp @@ -90,7 +90,7 @@ void Blitter_8bppBase::CopyImageToBuffer(const void *video, void *dst, int width } } -void Blitter_8bppBase::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) +void Blitter_8bppBase::ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) { const uint8 *src; uint8 *dst; diff --git a/src/blitter/8bpp_base.hpp b/src/blitter/8bpp_base.hpp index e13493691a..a51e17d590 100644 --- a/src/blitter/8bpp_base.hpp +++ b/src/blitter/8bpp_base.hpp @@ -25,7 +25,7 @@ public: void CopyFromBuffer(void *video, const void *src, int width, int height) override; void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override; - void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; + void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) override; int BufferSize(int width, int height) override; void PaletteAnimate(const Palette &palette) override; Blitter::PaletteAnimation UsePaletteAnimation() override; diff --git a/src/blitter/base.hpp b/src/blitter/base.hpp index 946dcddbf8..f956459951 100644 --- a/src/blitter/base.hpp +++ b/src/blitter/base.hpp @@ -198,7 +198,7 @@ public: * @param scroll_x How much to scroll in X. * @param scroll_y How much to scroll in Y. */ - virtual void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) = 0; + virtual void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) = 0; /** * Calculate how much memory there is needed for an image of this size in the video-buffer. diff --git a/src/blitter/null.hpp b/src/blitter/null.hpp index ea21e999dc..187afcf166 100644 --- a/src/blitter/null.hpp +++ b/src/blitter/null.hpp @@ -28,7 +28,7 @@ public: void CopyFromBuffer(void *video, const void *src, int width, int height) override {}; void CopyToBuffer(const void *video, void *dst, int width, int height) override {}; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override {}; - void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override {}; + void ScrollBuffer(void *video, int left, int top, int width, int height, int scroll_x, int scroll_y) override {}; int BufferSize(int width, int height) override { return 0; }; void PaletteAnimate(const Palette &palette) override { }; Blitter::PaletteAnimation UsePaletteAnimation() override { return Blitter::PALETTE_ANIMATION_NONE; }; diff --git a/src/gfx.cpp b/src/gfx.cpp index 1a634c246d..acb0715058 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -79,21 +79,6 @@ static uint _dirty_bytes_per_line = 0; static byte *_dirty_blocks = nullptr; extern uint _dirty_block_colour; -void GfxScroll(int left, int top, int width, int height, int xo, int yo) -{ - Blitter *blitter = BlitterFactory::GetCurrentBlitter(); - - if (xo == 0 && yo == 0) return; - - if (_cursor.visible) UndrawMouseCursor(); - - if (_networking) NetworkUndrawChatMessage(); - - blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo); - /* This part of the screen is now dirty. */ - VideoDriver::GetInstance()->MakeDirty(left, top, width, height); -} - /** * Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen. diff --git a/src/gfx_func.h b/src/gfx_func.h index 494b8ad1b5..d6d8107e74 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -86,7 +86,6 @@ void UndrawMouseCursor(); static const int DRAW_STRING_BUFFER = 2048; void RedrawScreenRect(int left, int top, int right, int bottom); -void GfxScroll(int left, int top, int width, int height, int xo, int yo); Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr); diff --git a/src/viewport.cpp b/src/viewport.cpp index 606e406bc0..88b421da73 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -105,6 +105,7 @@ #include "gui.h" #include "core/container_func.hpp" #include "tunnelbridge_map.h" +#include "video/video_driver.hpp" #include #include @@ -391,9 +392,15 @@ void InitializeWindowViewport(Window *w, int x, int y, static Point _vp_move_offs; -static void DoSetViewportPosition(const Window *w, int left, int top, int width, int height) +struct ViewportRedrawRegion { + Rect coords; +}; + +static std::vector _vp_redraw_regions; + +static void DoViewportRedrawRegions(const Window *w, int left, int top, int width, int height) { - IncrementWindowUpdateNumber(); + if (width <= 0 || height <= 0) return; FOR_ALL_WINDOWS_FROM_BACK_FROM(w, w) { if (left + width > w->left && @@ -402,26 +409,26 @@ static void DoSetViewportPosition(const Window *w, int left, int top, int width, w->top + w->height > top) { if (left < w->left) { - DoSetViewportPosition(w, left, top, w->left - left, height); - DoSetViewportPosition(w, left + (w->left - left), top, width - (w->left - left), height); + DoViewportRedrawRegions(w, left, top, w->left - left, height); + DoViewportRedrawRegions(w, left + (w->left - left), top, width - (w->left - left), height); return; } if (left + width > w->left + w->width) { - DoSetViewportPosition(w, left, top, (w->left + w->width - left), height); - DoSetViewportPosition(w, left + (w->left + w->width - left), top, width - (w->left + w->width - left), height); + DoViewportRedrawRegions(w, left, top, (w->left + w->width - left), height); + DoViewportRedrawRegions(w, left + (w->left + w->width - left), top, width - (w->left + w->width - left), height); return; } if (top < w->top) { - DoSetViewportPosition(w, left, top, width, (w->top - top)); - DoSetViewportPosition(w, left, top + (w->top - top), width, height - (w->top - top)); + DoViewportRedrawRegions(w, left, top, width, (w->top - top)); + DoViewportRedrawRegions(w, left, top + (w->top - top), width, height - (w->top - top)); return; } if (top + height > w->top + w->height) { - DoSetViewportPosition(w, left, top, width, (w->top + w->height - top)); - DoSetViewportPosition(w, left, top + (w->top + w->height - top), width, height - (w->top + w->height - top)); + DoViewportRedrawRegions(w, left, top, width, (w->top + w->height - top)); + DoViewportRedrawRegions(w, left, top + (w->top + w->height - top), width, height - (w->top + w->height - top)); return; } @@ -429,31 +436,132 @@ static void DoSetViewportPosition(const Window *w, int left, int top, int width, } } - { - int xo = _vp_move_offs.x; - int yo = _vp_move_offs.y; + _vp_redraw_regions.push_back({ { left, top, left + width, top + height } }); +} + +static void DoSetViewportPositionFillRegion(int left, int top, int width, int height, int xo, int yo) { + int src_left = left - xo; + int src_top = top - yo; + int src_right = src_left + width; + int src_bottom = src_top + height; + for (const auto ®ion : _vp_redraw_regions) { + if (region.coords.left < src_right && + region.coords.right > src_left && + region.coords.top < src_bottom && + region.coords.bottom > src_top) { + /* can use this region as a source */ + if (src_left < region.coords.left) { + DoSetViewportPositionFillRegion(src_left + xo, src_top + yo, region.coords.left - src_left, height, xo, yo); + src_left = region.coords.left; + width = src_right - src_left; + } + if (src_top < region.coords.top) { + DoSetViewportPositionFillRegion(src_left + xo, src_top + yo, width, region.coords.top - src_top, xo, yo); + src_top = region.coords.top; + height = src_bottom - src_top; + } + if (src_right > region.coords.right) { + DoSetViewportPositionFillRegion(region.coords.right + xo, src_top + yo, src_right - region.coords.right, height, xo, yo); + src_right = region.coords.right; + width = src_right - src_left; + } + if (src_bottom > region.coords.bottom) { + DoSetViewportPositionFillRegion(src_left + xo, region.coords.bottom + yo, width, src_bottom - region.coords.bottom, xo, yo); + src_bottom = region.coords.bottom; + height = src_bottom - src_top; + } + + if (xo >= 0) { + /* scrolling left, moving pixels right */ + width += xo; + } else { + /* scrolling right, moving pixels left */ + src_left += xo; + width -= xo; + } + if (yo >= 0) { + /* scrolling down, moving pixels up */ + height += yo; + } else { + /* scrolling up, moving pixels down */ + src_top += yo; + height -= yo; + } + BlitterFactory::GetCurrentBlitter()->ScrollBuffer(_screen.dst_ptr, src_left, src_top, width, height, xo, yo); - if (abs(xo) >= width || abs(yo) >= height) { - /* fully_outside */ - RedrawScreenRect(left, top, left + width, top + height); return; } + } + DrawOverlappedWindowForAll(left, top, left + width, top + height); +}; - GfxScroll(left, top, width, height, xo, yo); +static void DoSetViewportPosition(const Window *w, const int left, const int top, const int width, const int height) +{ + const int xo = _vp_move_offs.x; + const int yo = _vp_move_offs.y; + if (xo == 0 && yo == 0) return; - if (xo > 0) { - RedrawScreenRect(left, top, xo + left, top + height); - left += xo; - width -= xo; - } else if (xo < 0) { - RedrawScreenRect(left + width + xo, top, left + width, top + height); - width += xo; + + IncrementWindowUpdateNumber(); + + _vp_redraw_regions.clear(); + DoViewportRedrawRegions(w, left, top, width, height); + + if (abs(xo) >= width || abs(yo) >= height) { + /* fully outside */ + for (ViewportRedrawRegion &vrr : _vp_redraw_regions) { + RedrawScreenRect(vrr.coords.left, vrr.coords.top, vrr.coords.right, vrr.coords.bottom); } + return; + } - if (yo > 0) { - RedrawScreenRect(left, top, width + left, top + yo); - } else if (yo < 0) { - RedrawScreenRect(left, top + height + yo, width + left, top + height); + Blitter *blitter = BlitterFactory::GetCurrentBlitter(); + + if (_cursor.visible) UndrawMouseCursor(); + + if (_networking) NetworkUndrawChatMessage(); + + std::sort(_vp_redraw_regions.begin(), _vp_redraw_regions.end(), [&](const ViewportRedrawRegion &a, const ViewportRedrawRegion &b) { + if (a.coords.right <= b.coords.left) return xo > 0; + if (a.coords.left >= b.coords.right) return xo < 0; + if (a.coords.bottom <= b.coords.top) return yo > 0; + if (a.coords.top >= b.coords.bottom) return yo < 0; + NOT_REACHED(); + }); + + while (!_vp_redraw_regions.empty()) { + const Rect &rect = _vp_redraw_regions.back().coords; + int left = rect.left; + int top = rect.top; + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + _vp_redraw_regions.pop_back(); + VideoDriver::GetInstance()->MakeDirty(left, top, width, height); + int fill_width = abs(xo); + int fill_height = abs(yo); + if (fill_width < width && fill_height < height) { + blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo); + } else { + if (width < fill_width) fill_width = width; + if (height < fill_height) fill_height = height; + } + if (xo < 0) { + /* scrolling right, moving pixels left, fill in on right */ + width -= fill_width; + DoSetViewportPositionFillRegion(left + width, top, fill_width, height, xo, yo); + } else if (xo > 0) { + /* scrolling left, moving pixels right, fill in on left */ + DoSetViewportPositionFillRegion(left, top, fill_width, height, xo, yo); + width -= fill_width; + left += fill_width; + } + if (yo < 0 && width > 0) { + /* scrolling down, moving pixels up, fill in at bottom */ + height -= fill_height; + DoSetViewportPositionFillRegion(left, top + height, width, fill_height, xo, yo); + } else if (yo > 0 && width > 0) { + /* scrolling up, moving pixels down, fill in at top */ + DoSetViewportPositionFillRegion(left, top, width, fill_height, xo, yo); } } }