From 494e7218597a6cd94902b3ae6f827e74b41c00b0 Mon Sep 17 00:00:00 2001
From: Bent Bisballe Nyeng <deva@aasimon.org>
Date: Sun, 12 Mar 2017 17:25:04 +0100
Subject: Fix redrawing of underlying exposed area on Widget moves/resizing.

---
 plugingui/pixelbuffer.cc | 18 +++++++++---------
 plugingui/pixelbuffer.h  | 37 +++++++++++++++++++++++-------------
 plugingui/widget.cc      | 49 +++++++++++++++++++++++++++++++++++++++++-------
 plugingui/window.cc      | 43 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 118 insertions(+), 29 deletions(-)

(limited to 'plugingui')

diff --git a/plugingui/pixelbuffer.cc b/plugingui/pixelbuffer.cc
index bbef84c..360ad06 100644
--- a/plugingui/pixelbuffer.cc
+++ b/plugingui/pixelbuffer.cc
@@ -33,7 +33,7 @@
 namespace GUI
 {
 
-PixelBuffer::PixelBuffer(size_t width, size_t height)
+PixelBuffer::PixelBuffer(std::size_t width, std::size_t height)
 	: buf(nullptr)
 {
 	realloc(width, height);
@@ -44,7 +44,7 @@ PixelBuffer::~PixelBuffer()
 	free(buf);
 }
 
-void PixelBuffer::realloc(size_t width, size_t height)
+void PixelBuffer::realloc(std::size_t width, std::size_t height)
 {
 	free(buf);
 	buf = (unsigned char *)calloc(width * height, 3);
@@ -53,7 +53,7 @@ void PixelBuffer::realloc(size_t width, size_t height)
 }
 
 #define PX(k) ((x + y * width) * 3 + k)
-void PixelBuffer::setPixel(size_t x, size_t y,
+void PixelBuffer::setPixel(std::size_t x, std::size_t y,
                            unsigned char red,
                            unsigned char green,
                            unsigned char blue,
@@ -84,7 +84,7 @@ void PixelBuffer::setPixel(size_t x, size_t y,
 	}
 }
 
-PixelBufferAlpha::PixelBufferAlpha(size_t width, size_t height)
+PixelBufferAlpha::PixelBufferAlpha(std::size_t width, std::size_t height)
 	: managed(true)
 	, buf(nullptr)
 	, x(0)
@@ -101,7 +101,7 @@ PixelBufferAlpha::~PixelBufferAlpha()
 	}
 }
 
-void PixelBufferAlpha::realloc(size_t width, size_t height)
+void PixelBufferAlpha::realloc(std::size_t width, std::size_t height)
 {
 	free(buf);
 	buf = (unsigned char *)calloc(width * height, 4);
@@ -111,7 +111,7 @@ void PixelBufferAlpha::realloc(size_t width, size_t height)
 
 #undef PX
 #define PX(k) ((x + y * width) * 4 + k)
-void PixelBufferAlpha::setPixel(size_t x, size_t y,
+void PixelBufferAlpha::setPixel(std::size_t x, std::size_t y,
                                 unsigned char red,
                                 unsigned char green,
                                 unsigned char blue,
@@ -135,7 +135,7 @@ static inline void getAlpha(unsigned char _a, unsigned char _b,
 	b *= (1 - a);
 }
 
-void PixelBufferAlpha::addPixel(size_t x, size_t y,
+void PixelBufferAlpha::addPixel(std::size_t x, std::size_t y,
                                 unsigned char red,
                                 unsigned char green,
                                 unsigned char blue,
@@ -172,13 +172,13 @@ void PixelBufferAlpha::addPixel(size_t x, size_t y,
 	}
 }
 
-void PixelBufferAlpha::addPixel(size_t x, size_t y, const Colour& c)
+void PixelBufferAlpha::addPixel(std::size_t x, std::size_t y, const Colour& c)
 {
 	addPixel(x, y,
 	         c.red() * 255, c.green() * 255, c.blue() * 255, c.alpha() * 255);
 }
 
-void PixelBufferAlpha::pixel(size_t x, size_t y,
+void PixelBufferAlpha::pixel(std::size_t x, std::size_t y,
                              unsigned char* red,
                              unsigned char* green,
                              unsigned char* blue,
diff --git a/plugingui/pixelbuffer.h b/plugingui/pixelbuffer.h
index 94c5496..71869a0 100644
--- a/plugingui/pixelbuffer.h
+++ b/plugingui/pixelbuffer.h
@@ -33,47 +33,49 @@
 namespace GUI
 {
 
-class PixelBuffer {
+class PixelBuffer
+{
 public:
-	PixelBuffer(size_t width, size_t height);
+	PixelBuffer(std::size_t width, std::size_t height);
 	~PixelBuffer();
 
-	void realloc(size_t width, size_t height);
+	void realloc(std::size_t width, std::size_t height);
 
-	void setPixel(size_t x, size_t y,
+	void setPixel(std::size_t x, std::size_t y,
 	              unsigned char red,
 	              unsigned char green,
 	              unsigned char blue,
 	              unsigned char alpha);
 
 	unsigned char* buf{nullptr};
-	size_t width{0};
-	size_t height{0};
+	std::size_t width{0};
+	std::size_t height{0};
 };
 
-class PixelBufferAlpha {
+class PixelBufferAlpha
+{
 public:
 	PixelBufferAlpha() = default;
-	PixelBufferAlpha(size_t width, size_t height);
+	PixelBufferAlpha(std::size_t width, std::size_t height);
 	~PixelBufferAlpha();
 
-	void realloc(size_t width, size_t height);
+	void realloc(std::size_t width, std::size_t height);
 
-	void setPixel(size_t x, size_t y,
+	void setPixel(std::size_t x, std::size_t y,
 	              unsigned char red,
 	              unsigned char green,
 	              unsigned char blue,
 	              unsigned char alpha);
 
-	void addPixel(size_t x, size_t y,
+	void addPixel(std::size_t x, std::size_t y,
 	              unsigned char red,
 	              unsigned char green,
 	              unsigned char blue,
 	              unsigned char alpha);
 
-	void addPixel(size_t x, size_t y, const Colour& c);
+	void addPixel(std::size_t x, std::size_t y, const Colour& c);
 
-	void pixel(size_t x, size_t y,
+	void pixel(std::size_t x, std::size_t y,
 	           unsigned char* red,
 	           unsigned char* green,
 	           unsigned char* blue,
@@ -86,6 +88,15 @@ public:
 	int x{0};
 	int y{0};
 	bool dirty{true};
+	bool visible{true};
+
+	// Add optional dirty rect that this pixelbuffer took up since it was last
+	// rendered. Make sure to update this list on resize and/or move.
+	std::size_t last_width{0};
+	std::size_t last_height{0};
+	int last_x{0};
+	int last_y{0};
+	bool has_last{false};
 };
 
 } // GUI::
diff --git a/plugingui/widget.cc b/plugingui/widget.cc
index 7d92a22..00acfbf 100644
--- a/plugingui/widget.cc
+++ b/plugingui/widget.cc
@@ -42,6 +42,9 @@ Widget::Widget(Widget* parent)
 		parent->addChild(this);
 		_window = parent->window();
 	}
+
+	pixbuf.x = translateToWindowX();
+	pixbuf.y = translateToWindowY();
 }
 
 Widget::~Widget()
@@ -64,12 +67,14 @@ void Widget::hide()
 
 void Widget::setVisible(bool visible)
 {
-	_visible = visible;
-
-	if(visible)
+	if(_visible == visible)
 	{
-		redraw();
+		return;
 	}
+
+	_visible = visible;
+	pixbuf.visible = visible;
+	redraw();
 }
 
 bool Widget::visible() const
@@ -130,7 +135,20 @@ void Widget::resize(std::size_t width, std::size_t height)
 
 	_width = width;
 	_height = height;
+
+	// Store old size/position in pixelbuffer for rendering invalidation.
+	if(!pixbuf.has_last)
+	{
+		pixbuf.last_width = pixbuf.width;
+		pixbuf.last_height = pixbuf.height;
+		pixbuf.last_x = pixbuf.x;
+		pixbuf.last_y = pixbuf.y;
+		pixbuf.has_last = true;
+	}
+
 	pixbuf.realloc(width, height);
+	pixbuf.x = translateToWindowX();
+	pixbuf.y = translateToWindowY();
 	redraw();
 	sizeChangeNotifier(width, height);
 }
@@ -145,6 +163,20 @@ void Widget::move(int x, int y)
 
 	_x = x;
 	_y = y;
+
+	// Store old size/position in pixelbuffer for rendering invalidation.
+	if(!pixbuf.has_last)
+	{
+		pixbuf.last_width = pixbuf.width;
+		pixbuf.last_height = pixbuf.height;
+		pixbuf.last_x = pixbuf.x;
+		pixbuf.last_y = pixbuf.y;
+		pixbuf.has_last = true;
+	}
+
+	//pixbuf.x = translateToWindowX();
+	//pixbuf.y = translateToWindowY();
+
 	positionChangeNotifier(x, y);
 }
 
@@ -216,11 +248,14 @@ std::vector<PixelBufferAlpha*> Widget::getPixelBuffers()
 		dirty = false;
 	}
 
-	pixelBuffers.push_back(&pixbuf);
+	if(pixbuf.dirty || visible())
+	{
+		pixelBuffers.push_back(&pixbuf);
+	}
 
-	for(auto child : children)
+	if(visible())
 	{
-		if(child->visible())
+		for(auto child : children)
 		{
 			auto childPixelBuffers = child->getPixelBuffers();
 			pixelBuffers.insert(pixelBuffers.end(),
diff --git a/plugingui/window.cc b/plugingui/window.cc
index d4046c2..8bc5c62 100644
--- a/plugingui/window.cc
+++ b/plugingui/window.cc
@@ -262,10 +262,50 @@ bool Window::updateBuffer()
 				};
 			}
 		}
+
+		if(pixel_buffer->has_last)
+		{
+			auto x1 = (std::size_t)pixel_buffer->last_x;
+			auto x2 = (std::size_t)(pixel_buffer->last_x + pixel_buffer->last_width);
+			auto y1 = (std::size_t)pixel_buffer->last_y;
+			auto y2 = (std::size_t)(pixel_buffer->last_y + pixel_buffer->last_height);
+
+			pixel_buffer->has_last = false;
+			if(!has_dirty_rect)
+			{
+				// Insert this area:
+				dirty_rect = {x1, y1, x2, y2};
+				has_dirty_rect = true;
+			}
+			else
+			{
+				// Expand existing area:
+				auto x1_0 = dirty_rect.x1;
+				auto y1_0 = dirty_rect.y1;
+				auto x2_0 = dirty_rect.x2;
+				auto y2_0 = dirty_rect.y2;
+				dirty_rect = {
+					(x1_0 < x1) ? x1_0 : x1,
+					(y1_0 < y1) ? y1_0 : y1,
+					(x2_0 > x2) ? x2_0 : x2,
+					(y2_0 > y2) ? y2_0 : y2
+				};
+			}
+		}
+	}
+
+	if(!has_dirty_rect)
+	{
+		return false;
 	}
 
 	for(auto& pixel_buffer : pixel_buffers)
 	{
+		if(!pixel_buffer->visible)
+		{
+			continue;
+		}
+
 		int update_width = pixel_buffer->width;
 		int update_height = pixel_buffer->height;
 
@@ -308,6 +348,9 @@ bool Window::updateBuffer()
 		}
 	}
 
+	dirty_rect.x2 = std::min(wpixbuf.width, dirty_rect.x2);
+	dirty_rect.y2 = std::min(wpixbuf.height, dirty_rect.y2);
+
 	native->redraw(dirty_rect);
 	needs_redraw = false;
 
-- 
cgit v1.2.3