diff options
| -rw-r--r-- | plugingui/guievent.h | 5 | ||||
| -rw-r--r-- | plugingui/pixelbuffer.cc | 141 | ||||
| -rw-r--r-- | plugingui/pixelbuffer.h | 5 | ||||
| -rw-r--r-- | plugingui/window.cc | 136 | ||||
| -rw-r--r-- | test/uitests/benchmarktest.cc | 28 | 
5 files changed, 182 insertions, 133 deletions
| diff --git a/plugingui/guievent.h b/plugingui/guievent.h index fa42483..4ad0798 100644 --- a/plugingui/guievent.h +++ b/plugingui/guievent.h @@ -202,6 +202,11 @@ struct Rect  	std::size_t y1;  	std::size_t x2;  	std::size_t y2; + +	bool empty() const +	{ +		return x1 == x2 && y1 == y2; +	}  };  } // GUI:: diff --git a/plugingui/pixelbuffer.cc b/plugingui/pixelbuffer.cc index 1df1d5a..827d2fc 100644 --- a/plugingui/pixelbuffer.cc +++ b/plugingui/pixelbuffer.cc @@ -104,6 +104,147 @@ void PixelBuffer::writeLine(std::size_t x, std::size_t y,  	}  } +Rect PixelBuffer::updateBuffer(std::vector<PixelBufferAlpha*>& pixel_buffers) +{ +	bool has_dirty_rect{false}; +	Rect dirty_rect; + +	for(const auto& pixel_buffer : pixel_buffers) +	{ +		if(pixel_buffer->dirty) +		{ +			auto x1 = (std::size_t)pixel_buffer->x; +			auto x2 = (std::size_t)(pixel_buffer->x + pixel_buffer->width); +			auto y1 = (std::size_t)pixel_buffer->y; +			auto y2 = (std::size_t)(pixel_buffer->y + pixel_buffer->height); + +			pixel_buffer->dirty = 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(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 {}; +	} + +	for(const auto& pixel_buffer : pixel_buffers) +	{ +		if(!pixel_buffer->visible) +		{ +			continue; +		} + +		int update_width = pixel_buffer->width; +		int update_height = pixel_buffer->height; + +		// Skip buffer if not inside window. +		if(((int)width < pixel_buffer->x) || +		   ((int)height < pixel_buffer->y)) +		{ +			continue; +		} + +		if(update_width > ((int)width - pixel_buffer->x)) +		{ +			update_width = ((int)width - pixel_buffer->x); +		} + +		if(update_height > ((int)height - pixel_buffer->y)) +		{ +			update_height = ((int)height - pixel_buffer->y); +		} + +		auto from_x  = (int)dirty_rect.x1 - pixel_buffer->x; +		from_x = std::max(0, from_x); +		auto from_y  = (int)dirty_rect.y1 - pixel_buffer->y; +		from_y = std::max(0, from_y); + +		auto to_x = (int)dirty_rect.x2 - pixel_buffer->x; +		to_x = std::min(to_x, (int)update_width); +		auto to_y = (int)dirty_rect.y2 - pixel_buffer->y; +		to_y = std::min(to_y, (int)update_height); + +		if(to_x < from_x) +		{ +			continue; +		} + +		for(int y = from_y; y < to_y; y++) +		{ +			writeLine(pixel_buffer->x + from_x, +			          pixel_buffer->y + y, +			          pixel_buffer->getLine(from_x, y), +			          to_x - from_x); +		} +	} + +	dirty_rect.x2 = std::min(width, dirty_rect.x2); +	dirty_rect.y2 = std::min(height, dirty_rect.y2); + +	// Make sure we don't try to paint a rect backwards. +	if(dirty_rect.x1 > dirty_rect.x2) +	{ +		std::swap(dirty_rect.x1, dirty_rect.x2); +	} + +	if(dirty_rect.y1 > dirty_rect.y2) +	{ +		std::swap(dirty_rect.y1, dirty_rect.y2); +	} + +	return dirty_rect; +} +  PixelBufferAlpha::PixelBufferAlpha(std::size_t width, std::size_t height)  	: managed(true) diff --git a/plugingui/pixelbuffer.h b/plugingui/pixelbuffer.h index 499940d..81921e5 100644 --- a/plugingui/pixelbuffer.h +++ b/plugingui/pixelbuffer.h @@ -29,6 +29,9 @@  #include "colour.h"  #include <cstddef> +#include <vector> + +#include "guievent.h"  namespace GUI  { @@ -46,6 +49,8 @@ public:  	void writeLine(std::size_t x, std::size_t y,  	               const std::uint8_t* line, std::size_t len); +	Rect updateBuffer(std::vector<class PixelBufferAlpha*>& pixel_buffers); +  	std::uint8_t* buf{nullptr};  	std::size_t width{0};  	std::size_t height{0}; diff --git a/plugingui/window.cc b/plugingui/window.cc index acde008..5e0ad31 100644 --- a/plugingui/window.cc +++ b/plugingui/window.cc @@ -250,144 +250,14 @@ bool Window::updateBuffer()  		return false;  	} -	bool has_dirty_rect{false}; -	Rect dirty_rect; -  	auto pixel_buffers = getPixelBuffers(); -	for(auto& pixel_buffer : pixel_buffers) -	{ -		if(pixel_buffer->dirty) -		{ -			auto x1 = (std::size_t)pixel_buffer->x; -			auto x2 = (std::size_t)(pixel_buffer->x + pixel_buffer->width); -			auto y1 = (std::size_t)pixel_buffer->y; -			auto y2 = (std::size_t)(pixel_buffer->y + pixel_buffer->height); - -			pixel_buffer->dirty = 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(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; - -		// Skip buffer if not inside window. -		if(((int)wpixbuf.width < pixel_buffer->x) || -		   ((int)wpixbuf.height < pixel_buffer->y)) -		{ -			continue; -		} - -		if(update_width > ((int)wpixbuf.width - pixel_buffer->x)) -		{ -			update_width = ((int)wpixbuf.width - pixel_buffer->x); -		} - -		if(update_height > ((int)wpixbuf.height - pixel_buffer->y)) -		{ -			update_height = ((int)wpixbuf.height - pixel_buffer->y); -		} - -		auto from_x  = (int)dirty_rect.x1 - pixel_buffer->x; -		from_x = std::max(0, from_x); -		auto from_y  = (int)dirty_rect.y1 - pixel_buffer->y; -		from_y = std::max(0, from_y); - -		auto to_x = (int)dirty_rect.x2 - pixel_buffer->x; -		to_x = std::min(to_x, (int)update_width); -		auto to_y = (int)dirty_rect.y2 - pixel_buffer->y; -		to_y = std::min(to_y, (int)update_height); - -		if(to_x < from_x) -		{ -			continue; -		} - -		for(int y = from_y; y < to_y; y++) -		{ -			wpixbuf.writeLine(pixel_buffer->x + from_x, -			                  pixel_buffer->y + y, -			                  pixel_buffer->getLine(from_x, y), -			                  to_x - from_x); -		} -	} +	auto dirty_rect = wpixbuf.updateBuffer(pixel_buffers); -	dirty_rect.x2 = std::min(wpixbuf.width, dirty_rect.x2); -	dirty_rect.y2 = std::min(wpixbuf.height, dirty_rect.y2); - -	// Make sure we don't try to paint a rect backwards. -	if(dirty_rect.x1 > dirty_rect.x2) +	if(!dirty_rect.empty())  	{ -		std::swap(dirty_rect.x1, dirty_rect.x2); +		native->redraw(dirty_rect);  	} - -	if(dirty_rect.y1 > dirty_rect.y2) -	{ -		std::swap(dirty_rect.y1, dirty_rect.y2); -	} - -	native->redraw(dirty_rect);  	needs_redraw = false;  	return true; diff --git a/test/uitests/benchmarktest.cc b/test/uitests/benchmarktest.cc index b240388..33defc6 100644 --- a/test/uitests/benchmarktest.cc +++ b/test/uitests/benchmarktest.cc @@ -116,6 +116,34 @@ int main()  	}  	{ +		GUI::PixelBuffer wpixbuf(800, 600); +		std::vector<GUI::PixelBufferAlpha*> children; +		for(int i = 0; i < 100; ++i) +		{ +			auto child = new GUI::PixelBufferAlpha(300, 300); +			child->x = i * 2; +			child->y = i * 2; +			children.push_back(child); +		} + +		TimedScope timed("Buffer flattening", 100); +		for(int i = 0; i < 100; ++i) +		{ +			for(auto child : children) +			{ +				child->dirty = true; +			} + +			wpixbuf.updateBuffer(children); +		} + +		for(auto child : children) +		{ +			delete child; +		} +	} + +	{  		TimedScope timed("Scaled 1:1 no alpha", 1000);  		for(int i = 0; i < 1000; ++i)  		{ | 
