From 10719139e641edc2b015886b92cf829db80574e5 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Fri, 24 Jul 2009 06:48:35 +0000 Subject: [PATCH] Store dirty rectangles in a structure list. For now there is still only one dirty rectangle, but this is the first step to improvement. --- caca/caca_internals.h | 10 +++- caca/canvas.c | 12 +++-- caca/dirty.c | 122 +++++++++++++++++++++++++++++------------- 3 files changed, 102 insertions(+), 42 deletions(-) diff --git a/caca/caca_internals.h b/caca/caca_internals.h index 5196d6e..267c08f 100644 --- a/caca/caca_internals.h +++ b/caca/caca_internals.h @@ -23,6 +23,7 @@ typedef struct caca_figfont caca_figfont_t; #if !defined(_DOXYGEN_SKIP_ME) # define EVENTBUF_LEN 10 +# define MAX_DIRTY_COUNT 1 #endif struct caca_frame @@ -60,7 +61,11 @@ struct caca_canvas /* Dirty rectangles */ int ndirty; - int dirty_xmin, dirty_xmax, dirty_ymin, dirty_ymax; + struct + { + int xmin, xmax, ymin, ymax; + } + dirty[MAX_DIRTY_COUNT]; /* Shortcut to the active frame information */ int width, height; @@ -216,6 +221,9 @@ struct caca_display } events; }; +/* Dirty rectangle functions */ +extern void _caca_clip_dirty_rect_list(caca_canvas_t *); + /* Colour functions */ extern uint32_t _caca_attr_to_rgb24fg(uint32_t); extern uint32_t _caca_attr_to_rgb24bg(uint32_t); diff --git a/caca/canvas.c b/caca/canvas.c index 3f070a5..6483bc3 100644 --- a/caca/canvas.c +++ b/caca/canvas.c @@ -370,10 +370,17 @@ int caca_resize(caca_canvas_t *cv, int width, int height) _caca_save_frame_info(cv); + /* Preload new width and height values into the canvas to optimise + * dirty rectangle handling */ cv->width = width; cv->height = height; new_size = width * height; + /* If width or height is smaller (or both), we have the opportunity to + * reduce or even remove dirty rectangles */ + if(width < old_width || height < old_height) + _caca_clip_dirty_rect_list(cv); + /* Step 1: if new area is bigger, resize the memory area now. */ if(new_size > old_size) { @@ -470,9 +477,8 @@ int caca_resize(caca_canvas_t *cv, int width, int height) caca_add_dirty_rect(cv, 0, old_height, old_width, height - old_height); } - /* XXX: technically we should not worry about the dirty rectangle in - * the bottom-right corner, because we only handle one dirty rectangle, - * but in case the API changes later, we make sure this is handled. */ + /* If both width and height are larger, there is a new dirty rectangle + * that needs to be created in the lower right corner. */ if(width > old_width && height > old_height) caca_add_dirty_rect(cv, old_width, old_height, width - old_width, height - old_height); diff --git a/caca/dirty.c b/caca/dirty.c index d44ac88..91f7b64 100644 --- a/caca/dirty.c +++ b/caca/dirty.c @@ -14,6 +14,14 @@ /* * This file contains the dirty rectangle handling functions. + * + * + * About dirty rectangles: + * + * * Dirty rectangles MUST NOT be larger than the canvas. If the user + * provides a large rectangle through caca_add_dirty_rect(), or if the + * canvas changes size to become smaller, all dirty rectangles MUST + * immediately be clipped to the canvas size. */ #include "config.h" @@ -77,23 +85,12 @@ int caca_get_dirty_rect(caca_canvas_t *cv, int r, return -1; } - /* Normalise dirty rectangle so that the values can be directly used. */ - if(cv->dirty_xmin < 0) - cv->dirty_xmin = 0; - - if(cv->dirty_xmax > cv->width - 1) - cv->dirty_xmax = cv->width - 1; + *x = cv->dirty[r].xmin; + *y = cv->dirty[r].ymin; + *width = cv->dirty[r].xmax - cv->dirty[r].xmin + 1; + *height = cv->dirty[r].ymax - cv->dirty[r].ymin + 1; - if(cv->dirty_ymin < 0) - cv->dirty_ymin = 0; - - if(cv->dirty_ymax > cv->height - 1) - cv->dirty_ymax = cv->height - 1; - - *x = cv->dirty_xmin; - *y = cv->dirty_ymin; - *width = cv->dirty_xmax - cv->dirty_xmin + 1; - *height = cv->dirty_ymax - cv->dirty_ymin + 1; + debug("dirty #%i: %ix%i at (%i,%i)\n", r, *width, *height, *x, *y); return 0; } @@ -118,35 +115,49 @@ int caca_get_dirty_rect(caca_canvas_t *cv, int r, * \param height The height of the additional dirty rectangle. * \return 0 in case of success, -1 if an error occurred. */ -int caca_add_dirty_rect(caca_canvas_t *cv, int x, int y, - int width, int height) +int caca_add_dirty_rect(caca_canvas_t *cv, int x, int y, int width, int height) { - /* Ignore empty and out-of-bounds rectangles */ - if(width <= 0 || height <= 0 || x + width <= 0 || x >= cv->width - || y + height <= 0 || y >= cv->height) + debug("new dirty: %ix%i at (%i,%i)\n", width, height, x, y); + + /* Clip arguments to canvas */ + if(x < 0) { width += x; x = 0; } + + if(x + width > cv->width) + width = cv->width - x; + + if(y < 0) { height += y; y = 0; } + + if(y + height > cv->height) + height = cv->height - y; + + /* Ignore empty and out-of-canvas rectangles */ + if(width <= 0 || height <= 0) { seterrno(EINVAL); return -1; } - if(cv->ndirty == 0) + /* Add dirty rectangle to list. Current strategy: if there is room + * for rectangles, just append it to the list. Otherwise, merge the + * new rectangle with the first in the list. */ + if(cv->ndirty < MAX_DIRTY_COUNT) { - cv->ndirty = 1; - cv->dirty_xmin = x; - cv->dirty_xmax = x + width - 1; - cv->dirty_ymin = y; - cv->dirty_ymax = y + height - 1; + cv->dirty[cv->ndirty].xmin = x; + cv->dirty[cv->ndirty].xmax = x + width - 1; + cv->dirty[cv->ndirty].ymin = y; + cv->dirty[cv->ndirty].ymax = y + height - 1; + cv->ndirty++; } else { - if(x < cv->dirty_xmin) - cv->dirty_xmin = x; - if(x + width - 1 > cv->dirty_xmax) - cv->dirty_xmax = x + width - 1; - if(y < cv->dirty_ymin) - cv->dirty_ymin = y; - if(y + height - 1 > cv->dirty_ymax) - cv->dirty_ymax = y + height - 1; + if(x < cv->dirty[0].xmin) + cv->dirty[0].xmin = x; + if(x + width - 1 > cv->dirty[0].xmax) + cv->dirty[0].xmax = x + width - 1; + if(y < cv->dirty[0].ymin) + cv->dirty[0].ymin = y; + if(y + height - 1 > cv->dirty[0].ymax) + cv->dirty[0].ymax = y + height - 1; } return 0; @@ -173,9 +184,19 @@ int caca_add_dirty_rect(caca_canvas_t *cv, int x, int y, int caca_remove_dirty_rect(caca_canvas_t *cv, int x, int y, int width, int height) { - /* Ignore empty and out-of-bounds rectangles */ - if(width <= 0 || height <= 0 || x + width <= 0 || x >= cv->width - || y + height <= 0 || y >= cv->height) + /* Clip arguments to canvas size */ + if(x < 0) { width += x; x = 0; } + + if(x + width > cv->width) + width = cv->width - x; + + if(y < 0) { height += y; y = 0; } + + if(y + height > cv->height) + height = cv->height - y; + + /* Ignore empty and out-of-canvas rectangles */ + if(width <= 0 || height <= 0) { seterrno(EINVAL); return -1; @@ -204,3 +225,28 @@ int caca_clear_dirty_rect_list(caca_canvas_t *cv) return 0; } +/* + * XXX: the following functions are local. + */ + +/* Clip all dirty rectangles in case they're larger than the canvas */ +void _caca_clip_dirty_rect_list(caca_canvas_t *cv) +{ + int i; + + for(i = 0; i < cv->ndirty; i++) + { + if(cv->dirty[i].xmin < 0) + cv->dirty[i].xmin = 0; + + if(cv->dirty[i].ymin < 0) + cv->dirty[i].ymin = 0; + + if(cv->dirty[i].xmax >= cv->width) + cv->dirty[i].xmax = cv->width - 1; + + if(cv->dirty[i].ymax >= cv->height) + cv->dirty[i].ymax = cv->height - 1; + } +} +