1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 04:27:44 +00:00

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.
This commit is contained in:
Andreas Kling 2019-01-09 02:06:04 +01:00
parent 2735b7e50d
commit 9963da9005
21 changed files with 212 additions and 41 deletions

View file

@ -35,7 +35,7 @@ public:
WeakLink<T>* leakLink() { return m_link.leakRef(); }
private:
WeakPtr(RetainPtr<WeakLink<T>>&& link) : m_link(std::move(link)) { }
WeakPtr(RetainPtr<WeakLink<T>>&& link) : m_link(move(link)) { }
RetainPtr<WeakLink<T>> m_link;
};

View file

@ -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:

View file

@ -2,6 +2,7 @@
#include "Widget.h"
#include <AK/AKString.h>
#include <AK/Function.h>
class Button final : public Widget {
public:
@ -11,7 +12,7 @@ public:
String caption() const { return m_caption; }
void setCaption(String&&);
std::function<void(Button&)> onClick;
Function<void(Button&)> onClick;
private:
virtual void paintEvent(PaintEvent&) override;

View file

@ -1,4 +1,5 @@
#include "FrameBufferSDL.h"
#include "GraphicsBitmap.h"
#include <AK/Assertions.h>
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);
}

View file

@ -3,6 +3,8 @@
#include "AbstractScreen.h"
#include <SDL.h>
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();

View file

@ -0,0 +1,25 @@
#include "GraphicsBitmap.h"
#include <AK/kmalloc.h>
RetainPtr<GraphicsBitmap> 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));
}

23
Widgets/GraphicsBitmap.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include "Size.h"
#include <AK/Retainable.h>
#include <AK/RetainPtr.h>
class GraphicsBitmap : public Retainable<GraphicsBitmap> {
public:
static RetainPtr<GraphicsBitmap> 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 };
};

View file

@ -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)

View file

@ -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_target = widget.backing();
ASSERT(m_target);
m_window = widget.window();
m_translation.moveBy(widget.relativePosition());
} else {
m_translation.setX(widget.x());
m_translation.setY(widget.y());
}
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;

View file

@ -7,8 +7,10 @@
#include <AK/AKString.h>
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<GraphicsBitmap> m_target;
Window* m_window { nullptr };
};

21
Widgets/Rect.cpp Normal file
View file

@ -0,0 +1,21 @@
#include "Rect.h"
#include <AK/StdLibExtras.h>
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);
}

View file

@ -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;
};

View file

@ -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()

View file

@ -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<GraphicsBitmap> m_backing;
};

View file

@ -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 };

View file

@ -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>&& font)
else
m_font = std::move(font);
}
GraphicsBitmap* Widget::backing()
{
if (auto* w = window())
return w->backing();
return nullptr;
}

View file

@ -6,8 +6,8 @@
#include "Color.h"
#include "Font.h"
#include <AK/AKString.h>
#include <functional>
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<Font>&&);
virtual GraphicsBitmap* backing();
private:
Window* m_window { nullptr };

View file

@ -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;

View file

@ -2,6 +2,7 @@
#include "Object.h"
#include "Rect.h"
#include "GraphicsBitmap.h"
#include <AK/AKString.h>
#include <AK/WeakPtr.h>
@ -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<Widget> m_focusedWidget;
RetainPtr<GraphicsBitmap> m_backing;
};

View file

@ -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<PaintEvent>());
}
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)

View file

@ -29,6 +29,8 @@ public:
bool isVisible(Window&) const;
void did_paint(Window&);
void repaint();
private: