From 9963da900583dc54f46c649978aa8a574a0bd508 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 9 Jan 2019 02:06:04 +0100 Subject: [PATCH] Start refactoring graphics system to have per-window backing stores. It was fun for everyone to share a single framebuffer but it was also kinda really awful. Let's move towards having a "GraphicsBitmap" as the backing store for each Window. This is going to need a lot of refactoring so let's get started. --- AK/WeakPtr.h | 2 +- Widgets/AbstractScreen.h | 2 ++ Widgets/Button.h | 3 ++- Widgets/FrameBufferSDL.cpp | 25 +++++++++++++++++++++++++ Widgets/FrameBufferSDL.h | 7 +++++++ Widgets/GraphicsBitmap.cpp | 25 +++++++++++++++++++++++++ Widgets/GraphicsBitmap.h | 23 +++++++++++++++++++++++ Widgets/Makefile | 2 ++ Widgets/Painter.cpp | 38 ++++++++++++++------------------------ Widgets/Painter.h | 5 ++++- Widgets/Rect.cpp | 21 +++++++++++++++++++++ Widgets/Rect.h | 33 +++++++++++++++++++++++---------- Widgets/RootWidget.cpp | 3 +++ Widgets/RootWidget.h | 6 ++++++ Widgets/Size.h | 6 ++++++ Widgets/Widget.cpp | 8 ++++++++ Widgets/Widget.h | 6 ++++-- Widgets/Window.cpp | 8 +++++++- Widgets/Window.h | 7 +++++++ Widgets/WindowManager.cpp | 21 ++++++++++++++++++++- Widgets/WindowManager.h | 2 ++ 21 files changed, 212 insertions(+), 41 deletions(-) create mode 100644 Widgets/GraphicsBitmap.cpp create mode 100644 Widgets/GraphicsBitmap.h create mode 100644 Widgets/Rect.cpp diff --git a/AK/WeakPtr.h b/AK/WeakPtr.h index e3c5e4b96b..d03af159db 100644 --- a/AK/WeakPtr.h +++ b/AK/WeakPtr.h @@ -35,7 +35,7 @@ public: WeakLink* leakLink() { return m_link.leakRef(); } private: - WeakPtr(RetainPtr>&& link) : m_link(std::move(link)) { } + WeakPtr(RetainPtr>&& link) : m_link(move(link)) { } RetainPtr> m_link; }; diff --git a/Widgets/AbstractScreen.h b/Widgets/AbstractScreen.h index 14e83aff13..e881874732 100644 --- a/Widgets/AbstractScreen.h +++ b/Widgets/AbstractScreen.h @@ -2,6 +2,7 @@ #include "Object.h" #include "Rect.h" +#include "Size.h" class AbstractScreen : public Object { public: @@ -12,6 +13,7 @@ public: static AbstractScreen& the(); + Size size() const { return { width(), height() }; } Rect rect() const { return { 0, 0, width(), height() }; } protected: diff --git a/Widgets/Button.h b/Widgets/Button.h index 033fb86da1..cdbc522ecf 100644 --- a/Widgets/Button.h +++ b/Widgets/Button.h @@ -2,6 +2,7 @@ #include "Widget.h" #include +#include class Button final : public Widget { public: @@ -11,7 +12,7 @@ public: String caption() const { return m_caption; } void setCaption(String&&); - std::function onClick; + Function onClick; private: virtual void paintEvent(PaintEvent&) override; diff --git a/Widgets/FrameBufferSDL.cpp b/Widgets/FrameBufferSDL.cpp index f7dce0dfc8..e449c08d3a 100644 --- a/Widgets/FrameBufferSDL.cpp +++ b/Widgets/FrameBufferSDL.cpp @@ -1,4 +1,5 @@ #include "FrameBufferSDL.h" +#include "GraphicsBitmap.h" #include FrameBufferSDL* s_the = nullptr; @@ -57,3 +58,27 @@ void FrameBufferSDL::initializeSDL() SDL_UpdateWindowSurface(m_window); } +dword* FrameBufferSDL::scanline(int y) +{ + return (dword*)(((byte*)m_surface->pixels) + (y * m_surface->pitch)); +} + +void FrameBufferSDL::blit(const Point& position, GraphicsBitmap& bitmap) +{ + Rect dst_rect(position, bitmap.size()); + + printf("blit at %d,%d %dx%d\n", dst_rect.x(), dst_rect.y(), dst_rect.width(), dst_rect.height()); + dst_rect.intersect(rect()); + printf(" -> intersection %d,%d %dx%d\n", dst_rect.x(), dst_rect.y(), dst_rect.width(), dst_rect.height()); + + for (int y = 0; y < dst_rect.height(); ++y) { + auto* framebuffer_scanline = scanline(position.y() + y); + auto* bitmap_scanline = bitmap.scanline(y); + memcpy(framebuffer_scanline + position.x(), bitmap_scanline, dst_rect.width() * 4); + } +} + +void FrameBufferSDL::flush() +{ + SDL_UpdateWindowSurface(m_window); +} diff --git a/Widgets/FrameBufferSDL.h b/Widgets/FrameBufferSDL.h index f85b633d64..dbad8f6ae3 100644 --- a/Widgets/FrameBufferSDL.h +++ b/Widgets/FrameBufferSDL.h @@ -3,6 +3,8 @@ #include "AbstractScreen.h" #include +class GraphicsBitmap; + class FrameBufferSDL final : public AbstractScreen { public: FrameBufferSDL(unsigned width, unsigned height); @@ -15,6 +17,11 @@ public: static FrameBufferSDL& the(); + dword* scanline(int y); + + void blit(const Point&, GraphicsBitmap&); + void flush(); + private: void initializeSDL(); diff --git a/Widgets/GraphicsBitmap.cpp b/Widgets/GraphicsBitmap.cpp new file mode 100644 index 0000000000..480ebc10ff --- /dev/null +++ b/Widgets/GraphicsBitmap.cpp @@ -0,0 +1,25 @@ +#include "GraphicsBitmap.h" +#include + +RetainPtr GraphicsBitmap::create(const Size& size) +{ + return adopt(*new GraphicsBitmap(size)); +} + +GraphicsBitmap::GraphicsBitmap(const Size& size) + : m_size(size) +{ + m_data = (byte*)kmalloc(size.width() * size.height() * 4); +} + +GraphicsBitmap::~GraphicsBitmap() +{ + kfree(m_data); +} + +dword* GraphicsBitmap::scanline(int y) +{ + unsigned pitch = m_size.width() * 4; + return (dword*)(((byte*)m_data) + (y * pitch)); +} + diff --git a/Widgets/GraphicsBitmap.h b/Widgets/GraphicsBitmap.h new file mode 100644 index 0000000000..a7319368d1 --- /dev/null +++ b/Widgets/GraphicsBitmap.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Size.h" +#include +#include + +class GraphicsBitmap : public Retainable { +public: + static RetainPtr create(const Size&); + ~GraphicsBitmap(); + + dword* scanline(int y); + + Size size() const { return m_size; } + int width() const { return m_size.width(); } + int height() const { return m_size.height(); } + +private: + explicit GraphicsBitmap(const Size&); + + Size m_size; + byte* m_data { nullptr }; +}; diff --git a/Widgets/Makefile b/Widgets/Makefile index 32add4ee28..25052d25b4 100644 --- a/Widgets/Makefile +++ b/Widgets/Makefile @@ -11,6 +11,7 @@ VFS_OBJS = \ FrameBufferSDL.o \ EventLoop.o \ EventLoopSDL.o \ + Rect.o \ Object.o \ Widget.o \ RootWidget.o \ @@ -28,6 +29,7 @@ VFS_OBJS = \ ListBox.o \ TextBox.o \ MsgBox.o \ + GraphicsBitmap.o \ test.o OBJS = $(AK_OBJS) $(VFS_OBJS) diff --git a/Widgets/Painter.cpp b/Widgets/Painter.cpp index 1ff7728a40..2c2574bf38 100644 --- a/Widgets/Painter.cpp +++ b/Widgets/Painter.cpp @@ -11,28 +11,18 @@ Painter::Painter(Widget& widget) : m_widget(widget) , m_font(&widget.font()) { - if (auto* window = widget.window()) { - m_translation = window->position(); - m_translation.moveBy(widget.relativePosition()); - } else { - m_translation.setX(widget.x()); - m_translation.setY(widget.y()); - } - + m_target = widget.backing(); + ASSERT(m_target); + m_window = widget.window(); + m_translation.moveBy(widget.relativePosition()); m_clipRect.setWidth(AbstractScreen::the().width()); m_clipRect.setHeight(AbstractScreen::the().height()); } Painter::~Painter() { - int rc = SDL_UpdateWindowSurface(FrameBufferSDL::the().window()); - ASSERT(rc == 0); -} - -static dword* scanline(int y) -{ - auto& surface = *FrameBufferSDL::the().surface(); - return (dword*)(((byte*)surface.pixels) + (y * surface.pitch)); + if (m_window) + m_window->did_paint(); } void Painter::fillRect(const Rect& rect, Color color) @@ -41,7 +31,7 @@ void Painter::fillRect(const Rect& rect, Color color) r.moveBy(m_translation); for (int y = max(r.top(), m_clipRect.top()); y < min(r.bottom(), m_clipRect.bottom()); ++y) { - dword* bits = scanline(y); + dword* bits = m_target->scanline(y); for (int x = max(r.left(), m_clipRect.left()); x < min(r.right(), m_clipRect.right()); ++x) { bits[x] = color.value(); } @@ -54,7 +44,7 @@ void Painter::drawRect(const Rect& rect, Color color) r.moveBy(m_translation); for (int y = max(r.top(), m_clipRect.top()); y < min(r.bottom(), m_clipRect.bottom()); ++y) { - dword* bits = scanline(y); + dword* bits = m_target->scanline(y); if (y == r.top() || y == (r.bottom() - 1)) { for (int x = max(r.left(), m_clipRect.left()); x < min(r.right(), m_clipRect.right()); ++x) { bits[x] = color.value(); @@ -74,7 +64,7 @@ void Painter::xorRect(const Rect& rect, Color color) r.moveBy(m_translation); for (int y = max(r.top(), m_clipRect.top()); y < min(r.bottom(), m_clipRect.bottom()); ++y) { - dword* bits = scanline(y); + dword* bits = m_target->scanline(y); if (y == r.top() || y == (r.bottom() - 1)) { for (int x = max(r.left(), m_clipRect.left()); x < min(r.right(), m_clipRect.right()); ++x) { bits[x] ^= color.value(); @@ -96,7 +86,7 @@ void Painter::drawBitmap(const Point& p, const CBitmap& bitmap, Color color) int y = point.y() + row; if (y < m_clipRect.top() || y >= m_clipRect.bottom()) break; - dword* bits = scanline(y); + dword* bits = m_target->scanline(y); for (unsigned j = 0; j < bitmap.width(); ++j) { int x = point.x() + j; if (x < m_clipRect.left() || x >= m_clipRect.right()) @@ -143,7 +133,7 @@ void Painter::drawPixel(const Point& p, Color color) point.moveBy(m_translation); if (!m_clipRect.contains(point)) return; - scanline(point.y())[point.x()] = color.value(); + m_target->scanline(point.y())[point.x()] = color.value(); } void Painter::drawLine(const Point& p1, const Point& p2, Color color) @@ -162,7 +152,7 @@ void Painter::drawLine(const Point& p1, const Point& p2, Color color) if (point1.y() > point2.y()) std::swap(point1, point2); for (int y = max(point1.y(), m_clipRect.top()); y <= min(point2.y(), m_clipRect.bottom()); ++y) - scanline(y)[x] = color.value(); + m_target->scanline(y)[x] = color.value(); return; } @@ -176,7 +166,7 @@ void Painter::drawLine(const Point& p1, const Point& p2, Color color) return; if (point1.x() > point2.x()) std::swap(point1, point2); - auto* pixels = scanline(point1.y()); + auto* pixels = m_target->scanline(point1.y()); for (int x = max(point1.x(), m_clipRect.left()); x <= min(point2.x(), m_clipRect.right()); ++x) pixels[x] = color.value(); return; @@ -193,7 +183,7 @@ void Painter::drawLine(const Point& p1, const Point& p2, Color color) int y = point1.y(); for (int x = point1.x(); x <= point2.x(); ++x) { - scanline(y)[x] = color.value(); + m_target->scanline(y)[x] = color.value(); error += deltaError; if (error >= 0.5) { y = (double)y + yStep; diff --git a/Widgets/Painter.h b/Widgets/Painter.h index 2e104aef2d..b4fc0b1254 100644 --- a/Widgets/Painter.h +++ b/Widgets/Painter.h @@ -7,8 +7,10 @@ #include class CBitmap; +class GraphicsBitmap; class Font; class Widget; +class Window; class Painter { public: @@ -32,6 +34,7 @@ private: Widget& m_widget; const Font* m_font; Point m_translation; - Rect m_clipRect; + RetainPtr m_target; + Window* m_window { nullptr }; }; diff --git a/Widgets/Rect.cpp b/Widgets/Rect.cpp new file mode 100644 index 0000000000..fe7acb1629 --- /dev/null +++ b/Widgets/Rect.cpp @@ -0,0 +1,21 @@ +#include "Rect.h" +#include + +void Rect::intersect(const Rect& other) +{ + int l = max(left(), other.left()); + int r = min(right(), other.right()); + int t = max(top(), other.top()); + int b = min(bottom(), other.bottom()); + + if (l >= r || t >= b) { + m_location = { }; + m_size = { }; + return; + } + + m_location.setX(l); + m_location.setY(t); + m_size.setWidth(r - l); + m_size.setHeight(b - t); +} diff --git a/Widgets/Rect.h b/Widgets/Rect.h index cd3fbc5411..924f661624 100644 --- a/Widgets/Rect.h +++ b/Widgets/Rect.h @@ -1,14 +1,19 @@ #pragma once #include "Point.h" +#include "Size.h" class Rect { public: Rect() { } Rect(int x, int y, int width, int height) : m_location(x, y) - , m_width(width) - , m_height(height) + , m_size(width, height) + { + } + Rect(const Point& location, const Size& size) + : m_location(location) + , m_size(size) { } @@ -85,25 +90,33 @@ public: int x() const { return location().x(); } int y() const { return location().y(); } - int width() const { return m_width; } - int height() const { return m_height; } + int width() const { return m_size.width(); } + int height() const { return m_size.height(); } void setX(int x) { m_location.setX(x); } void setY(int y) { m_location.setY(y); } - void setWidth(int width) { m_width = width; } - void setHeight(int height) { m_height = height; } + void setWidth(int width) { m_size.setWidth(width); } + void setHeight(int height) { m_size.setHeight(height); } Point location() const { return m_location; } + Size size() const { return m_size; } bool operator==(const Rect& other) const { return m_location == other.m_location - && m_width == other.m_width - && m_height == other.m_height; + && m_size == other.m_size; + } + + void intersect(const Rect&); + + static Rect intersection(const Rect& a, const Rect& b) + { + Rect r(a); + r.intersect(b); + return a; } private: Point m_location; - int m_width { 0 }; - int m_height { 0 }; + Size m_size; }; diff --git a/Widgets/RootWidget.cpp b/Widgets/RootWidget.cpp index 971840bf64..f28bb16a61 100644 --- a/Widgets/RootWidget.cpp +++ b/Widgets/RootWidget.cpp @@ -1,3 +1,5 @@ +#include "AbstractScreen.h" +#include "GraphicsBitmap.h" #include "RootWidget.h" #include "Painter.h" #include "WindowManager.h" @@ -5,6 +7,7 @@ RootWidget::RootWidget() { + m_backing = GraphicsBitmap::create(AbstractScreen::the().size()); } RootWidget::~RootWidget() diff --git a/Widgets/RootWidget.h b/Widgets/RootWidget.h index 06524f4f8a..30dfbe91f4 100644 --- a/Widgets/RootWidget.h +++ b/Widgets/RootWidget.h @@ -2,6 +2,8 @@ #include "Widget.h" +class GraphicsBitmap; + class RootWidget final : public Widget { public: RootWidget(); @@ -10,4 +12,8 @@ public: private: virtual void paintEvent(PaintEvent&) override; virtual void mouseMoveEvent(MouseEvent&) override; + + virtual GraphicsBitmap* backing() override { return m_backing.ptr(); } + + RetainPtr m_backing; }; diff --git a/Widgets/Size.h b/Widgets/Size.h index 9a86829639..6fd8a21f6f 100644 --- a/Widgets/Size.h +++ b/Widgets/Size.h @@ -13,6 +13,12 @@ public: void setWidth(int w) { m_width = w; } void setHeight(int h) { m_height = h; } + bool operator==(const Size& other) const + { + return m_width == other.m_width && + m_height == other.m_height; + } + private: int m_width { 0 }; int m_height { 0 }; diff --git a/Widgets/Widget.cpp b/Widgets/Widget.cpp index 97c860a8a8..f55bb0a5c2 100644 --- a/Widgets/Widget.cpp +++ b/Widgets/Widget.cpp @@ -1,6 +1,7 @@ #include "Widget.h" #include "Event.h" #include "EventLoop.h" +#include "GraphicsBitmap.h" #include "WindowManager.h" #include "Window.h" #include "Painter.h" @@ -155,3 +156,10 @@ void Widget::setFont(RetainPtr&& font) else m_font = std::move(font); } + +GraphicsBitmap* Widget::backing() +{ + if (auto* w = window()) + return w->backing(); + return nullptr; +} diff --git a/Widgets/Widget.h b/Widgets/Widget.h index 132baa595c..90dd8850a2 100644 --- a/Widgets/Widget.h +++ b/Widgets/Widget.h @@ -6,8 +6,8 @@ #include "Color.h" #include "Font.h" #include -#include +class GraphicsBitmap; class Window; class Widget : public Object { @@ -15,7 +15,7 @@ public: explicit Widget(Widget* parent = nullptr); virtual ~Widget(); - virtual void event(Event&); + virtual void event(Event&) override; virtual void paintEvent(PaintEvent&); virtual void showEvent(ShowEvent&); virtual void hideEvent(HideEvent&); @@ -83,6 +83,8 @@ public: const Font& font() const { return *m_font; } void setFont(RetainPtr&&); + virtual GraphicsBitmap* backing(); + private: Window* m_window { nullptr }; diff --git a/Widgets/Window.cpp b/Widgets/Window.cpp index 2aa53b4b1d..2bdd4a9a9d 100644 --- a/Widgets/Window.cpp +++ b/Widgets/Window.cpp @@ -32,7 +32,7 @@ void Window::setTitle(String&& title) if (m_title == title) return; - m_title = std::move(title); + m_title = move(title); WindowManager::the().notifyTitleChanged(*this); } @@ -42,6 +42,7 @@ void Window::setRect(const Rect& rect) return; auto oldRect = m_rect; m_rect = rect; + m_backing = GraphicsBitmap::create(m_rect.size()); WindowManager::the().notifyRectChanged(*this, oldRect, m_rect); } @@ -95,6 +96,11 @@ void Window::event(Event& event) return Object::event(event); } +void Window::did_paint() +{ + WindowManager::the().did_paint(*this); +} + bool Window::isActive() const { return WindowManager::the().activeWindow() == this; diff --git a/Widgets/Window.h b/Widgets/Window.h index 6ab83ba425..d81599e656 100644 --- a/Widgets/Window.h +++ b/Widgets/Window.h @@ -2,6 +2,7 @@ #include "Object.h" #include "Rect.h" +#include "GraphicsBitmap.h" #include #include @@ -50,6 +51,10 @@ public: void close(); + GraphicsBitmap* backing() { return m_backing.ptr(); } + + void did_paint(); + private: String m_title; Rect m_rect; @@ -57,5 +62,7 @@ private: bool m_isBeingDragged { false }; WeakPtr m_focusedWidget; + + RetainPtr m_backing; }; diff --git a/Widgets/WindowManager.cpp b/Widgets/WindowManager.cpp index f91983b129..c17c6d5180 100644 --- a/Widgets/WindowManager.cpp +++ b/Widgets/WindowManager.cpp @@ -5,6 +5,7 @@ #include "AbstractScreen.h" #include "TerminalWidget.h" #include "EventLoop.h" +#include "FrameBufferSDL.h" extern TerminalWidget* g_tw; @@ -131,6 +132,17 @@ void WindowManager::repaint() handlePaintEvent(*make()); } +void WindowManager::did_paint(Window& window) +{ + auto& framebuffer = FrameBufferSDL::the(); + framebuffer.blit({ 0, 0 }, *m_rootWidget->backing()); + for (auto* window : m_windows) { + ASSERT(window->backing()); + framebuffer.blit(window->position(), *window->backing()); + } + framebuffer.flush(); +} + void WindowManager::removeWindow(Window& window) { if (!m_windows.contains(&window)) @@ -239,7 +251,6 @@ void WindowManager::processMouseEvent(MouseEvent& event) return; } } - } void WindowManager::handlePaintEvent(PaintEvent& event) @@ -255,6 +266,14 @@ void WindowManager::handlePaintEvent(PaintEvent& event) for (auto* window : m_windows) window->event(event); + + auto& framebuffer = FrameBufferSDL::the(); + framebuffer.blit({ 0, 0 }, *m_rootWidget->backing()); + for (auto* window : m_windows) { + ASSERT(window->backing()); + framebuffer.blit(window->position(), *window->backing()); + } + framebuffer.flush(); } void WindowManager::event(Event& event) diff --git a/Widgets/WindowManager.h b/Widgets/WindowManager.h index 02073ba3cb..a20c8a464c 100644 --- a/Widgets/WindowManager.h +++ b/Widgets/WindowManager.h @@ -29,6 +29,8 @@ public: bool isVisible(Window&) const; + void did_paint(Window&); + void repaint(); private: