inner implementation still only handles one dirty rectangle, but this way we can prepare supporting applictions for the future.tags/v0.99.beta17
| @@ -23,6 +23,7 @@ libcaca_la_SOURCES = \ | |||||
| caca0.c \ | caca0.c \ | ||||
| caca0.h \ | caca0.h \ | ||||
| canvas.c \ | canvas.c \ | ||||
| dirty.c \ | |||||
| string.c \ | string.c \ | ||||
| legacy.c \ | legacy.c \ | ||||
| transform.c \ | transform.c \ | ||||
| @@ -220,10 +220,6 @@ __extern char const * caca_get_version(void); | |||||
| * | * | ||||
| * @{ */ | * @{ */ | ||||
| #define CACA_MAGIC_FULLWIDTH 0x000ffffe /**< Used to indicate that the previous character was a fullwidth glyph. */ | #define CACA_MAGIC_FULLWIDTH 0x000ffffe /**< Used to indicate that the previous character was a fullwidth glyph. */ | ||||
| __extern int caca_get_dirty_rectangle(caca_canvas_t *, int *, int *, | |||||
| int *, int *); | |||||
| __extern int caca_add_dirty_rectangle(caca_canvas_t *, int, int, int, int); | |||||
| __extern int caca_set_dirty_rectangle(caca_canvas_t *, int, int, int, int); | |||||
| __extern int caca_gotoxy(caca_canvas_t *, int, int); | __extern int caca_gotoxy(caca_canvas_t *, int, int); | ||||
| __extern int caca_get_cursor_x(caca_canvas_t const *); | __extern int caca_get_cursor_x(caca_canvas_t const *); | ||||
| __extern int caca_get_cursor_y(caca_canvas_t const *); | __extern int caca_get_cursor_y(caca_canvas_t const *); | ||||
| @@ -240,6 +236,18 @@ __extern int caca_blit(caca_canvas_t *, int, int, caca_canvas_t const *, | |||||
| __extern int caca_set_canvas_boundaries(caca_canvas_t *, int, int, int, int); | __extern int caca_set_canvas_boundaries(caca_canvas_t *, int, int, int, int); | ||||
| /* @} */ | /* @} */ | ||||
| /** \defgroup caca_dirty libcaca dirty rectangle manipulation | |||||
| * | |||||
| * These functions manipulate dirty rectangles for optimised blitting. | |||||
| * @{ */ | |||||
| __extern int caca_get_dirty_rectangle_count(caca_canvas_t *); | |||||
| __extern int caca_get_dirty_rectangle(caca_canvas_t *, int, int *, int *, | |||||
| int *, int *); | |||||
| __extern int caca_add_dirty_rectangle(caca_canvas_t *, int, int, int, int); | |||||
| __extern int caca_remove_dirty_rectangle(caca_canvas_t *, int, int, int, int); | |||||
| __extern int caca_clear_dirty_rectangle_list(caca_canvas_t *); | |||||
| /* @} */ | |||||
| /** \defgroup caca_transform libcaca canvas transformation | /** \defgroup caca_transform libcaca canvas transformation | ||||
| * | * | ||||
| * These functions perform horizontal and vertical canvas flipping. | * These functions perform horizontal and vertical canvas flipping. | ||||
| @@ -298,133 +298,6 @@ uint8_t const * caca_get_canvas_attrs(caca_canvas_t const *cv) | |||||
| return (uint8_t const *)cv->attrs; | return (uint8_t const *)cv->attrs; | ||||
| } | } | ||||
| /** \brief Get a canvas's dirty rectangle. | |||||
| * | |||||
| * Get the canvas's dirty rectangle coordinates. The dirty rectangle is | |||||
| * the smallest area containing all the cells that have changed since it | |||||
| * was last reset. | |||||
| * | |||||
| * The dirty rectangle is used internally by display drivers to optimise | |||||
| * rendering by avoiding to redraw the whole screen. Once the display driver | |||||
| * has rendered the canvas, it resets the dirty rectangle. | |||||
| * | |||||
| * Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that | |||||
| * the dirty rectangle is empty. It means that the canvas's contents have | |||||
| * not changed since the dirty rectangle was last reset. | |||||
| * | |||||
| * FIXME: having only one dirty rectangle instead of a list of rectangles | |||||
| * is a severe limitation, but the potential gain does not yet look to be | |||||
| * worth the implementation complexity of a multiple-rectangle scheme. | |||||
| * | |||||
| * This function never fails. | |||||
| * | |||||
| * \param cv A libcaca canvas. | |||||
| * \param xmin A pointer to an integer where the leftmost edge of the | |||||
| * dirty rectangle will be stored. | |||||
| * \param ymin A pointer to an integer where the topmost edge of the | |||||
| * dirty rectangle will be stored. | |||||
| * \param xmax A pointer to an integer where the rightmost edge of the | |||||
| * dirty rectangle will be stored. | |||||
| * \param ymax A pointer to an integer where the bottommost edge of the | |||||
| * dirty rectangle will be stored. | |||||
| * \return This function always returns 0. | |||||
| */ | |||||
| int caca_get_dirty_rectangle(caca_canvas_t *cv, int *xmin, int *ymin, | |||||
| int *xmax, int *ymax) | |||||
| { | |||||
| *xmin = cv->dirty_xmin; | |||||
| *xmax = cv->dirty_xmax; | |||||
| *ymin = cv->dirty_ymin; | |||||
| *ymax = cv->dirty_ymax; | |||||
| return 0; | |||||
| } | |||||
| /** \brief Add a dirty rectangle to the canvas's dirty rectangle. | |||||
| * | |||||
| * Add an invalidating zone to the canvas's dirty rectangle. For more | |||||
| * information about the dirty rectangle, see caca_get_dirty_rectangle(). | |||||
| * | |||||
| * This function may be useful to force refresh of a given zone of the | |||||
| * canvas even if the dirty rectangle tracking indicates that it is | |||||
| * unchanged. | |||||
| * | |||||
| * Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that | |||||
| * the dirty rectangle is empty. They will be silently ignored. | |||||
| * | |||||
| * This function never fails. | |||||
| * | |||||
| * \param cv A libcaca canvas. | |||||
| * \param xmin The leftmost edge of the additional dirty rectangle. | |||||
| * \param ymin The topmost edge of the additional dirty rectangle. | |||||
| * \param xmax The rightmost edge of the additional dirty rectangle. | |||||
| * \param ymax The bottommost edge of the additional dirty rectangle. | |||||
| * \return This function always returns 0. | |||||
| */ | |||||
| int caca_add_dirty_rectangle(caca_canvas_t *cv, int xmin, int ymin, | |||||
| int xmax, int ymax) | |||||
| { | |||||
| /* Ignore empty rectangles. */ | |||||
| if(xmin > xmax || ymin > ymax) | |||||
| return 0; | |||||
| /* Ignore out-of-bounds rectangles. */ | |||||
| if(xmax < 0 || xmin >= cv->width || ymax < 0 || ymin >= cv->height) | |||||
| return 0; | |||||
| if(xmin < cv->dirty_xmin) | |||||
| cv->dirty_xmin = xmin; | |||||
| if(xmax > cv->dirty_xmax) | |||||
| cv->dirty_xmax = xmax; | |||||
| if(ymin < cv->dirty_ymin) | |||||
| cv->dirty_ymin = ymin; | |||||
| if(ymax > cv->dirty_ymax) | |||||
| cv->dirty_ymax = ymax; | |||||
| return 0; | |||||
| } | |||||
| /** \brief Set a canvas's dirty rectangle. | |||||
| * | |||||
| * Set the canvas's dirty rectangle coordinates. For more information | |||||
| * about the dirty rectangle, see caca_get_dirty_rectangle(). | |||||
| * | |||||
| * Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that | |||||
| * the dirty rectangle is empty. | |||||
| * | |||||
| * This function never fails. | |||||
| * | |||||
| * \param cv A libcaca canvas. | |||||
| * \param xmin The leftmost edge of the desired dirty rectangle. | |||||
| * \param ymin The topmost edge of the desired dirty rectangle. | |||||
| * \param xmax The rightmost edge of the desired dirty rectangle. | |||||
| * \param ymax The bottommost edge of the desired dirty rectangle. | |||||
| * \return This function always returns 0. | |||||
| */ | |||||
| int caca_set_dirty_rectangle(caca_canvas_t *cv, int xmin, int ymin, | |||||
| int xmax, int ymax) | |||||
| { | |||||
| /* Normalise values indicating an empty or out-of-bounds rectangle. */ | |||||
| if(xmin > xmax || ymin > ymax || | |||||
| xmax < 0 || xmin >= cv->width || ymax < 0 || ymin >= cv->height) | |||||
| { | |||||
| xmin = cv->width; | |||||
| xmax = -1; | |||||
| ymin = cv->height; | |||||
| ymax = -1; | |||||
| } | |||||
| cv->dirty_xmin = xmin; | |||||
| cv->dirty_xmax = xmax; | |||||
| cv->dirty_ymin = ymin; | |||||
| cv->dirty_ymax = ymax; | |||||
| return 0; | |||||
| } | |||||
| /** \brief Free a \e libcaca canvas. | /** \brief Free a \e libcaca canvas. | ||||
| * | * | ||||
| * Free all resources allocated by caca_create_canvas(). The canvas | * Free all resources allocated by caca_create_canvas(). The canvas | ||||
| @@ -0,0 +1,215 @@ | |||||
| /* | |||||
| * libcaca Colour ASCII-Art library | |||||
| * Copyright (c) 2002-2009 Sam Hocevar <sam@hocevar.net> | |||||
| * All Rights Reserved | |||||
| * | |||||
| * $Id$ | |||||
| * | |||||
| * This library is free software. It comes without any warranty, to | |||||
| * the extent permitted by applicable law. You can redistribute it | |||||
| * and/or modify it under the terms of the Do What The Fuck You Want | |||||
| * To Public License, Version 2, as published by Sam Hocevar. See | |||||
| * http://sam.zoy.org/wtfpl/COPYING for more details. | |||||
| */ | |||||
| /* | |||||
| * This file contains the dirty rectangle handling functions. | |||||
| */ | |||||
| #include "config.h" | |||||
| #if !defined(__KERNEL__) | |||||
| # include <stdio.h> | |||||
| #endif | |||||
| #include "caca.h" | |||||
| #include "caca_internals.h" | |||||
| /** \brief Get the number of dirty rectangles in the canvas. | |||||
| * | |||||
| * Get the number of dirty rectangles in a canvas. Dirty rectangles are | |||||
| * areas that contain cells that have changed since the last reset. | |||||
| * | |||||
| * The dirty rectangles are used internally by display drivers to optimise | |||||
| * rendering by avoiding to redraw the whole screen. Once the display driver | |||||
| * has rendered the canvas, it resets the dirty rectangle list. | |||||
| * | |||||
| * Dirty rectangles are guaranteed not to overlap. | |||||
| * | |||||
| * This function never fails. | |||||
| * | |||||
| * \param cv A libcaca canvas. | |||||
| * \return The number of dirty rectangles in the given canvas. | |||||
| */ | |||||
| int caca_get_dirty_rectangle_count(caca_canvas_t *cv) | |||||
| { | |||||
| /* Ignore empty rectangles. */ | |||||
| if(cv->dirty_xmin > cv->dirty_xmax || cv->dirty_ymin > cv->dirty_ymax) | |||||
| return 0; | |||||
| /* Ignore out-of-bounds rectangles. */ | |||||
| if(cv->dirty_xmax < 0 || cv->dirty_xmin >= cv->width | |||||
| || cv->dirty_ymax < 0 || cv->dirty_ymin >= cv->height) | |||||
| return 0; | |||||
| return 1; | |||||
| } | |||||
| /** \brief Get a canvas's dirty rectangle. | |||||
| * | |||||
| * Get the canvas's given dirty rectangle coordinates. The index must be | |||||
| * within the dirty rectangle count. See caca_get_dirty_rectangle_count() | |||||
| * for how to compute this count. | |||||
| * | |||||
| * If an error occurs, no coordinates are written in the pointer arguments, | |||||
| * -1 is returned and \b errno is set accordingly: | |||||
| * - \c EINVAL Specified rectangle index is out of bounds. | |||||
| * | |||||
| * \param cv A libcaca canvas. | |||||
| * \param index The requested rectangle index. | |||||
| * \param xmin A pointer to an integer where the leftmost edge of the | |||||
| * dirty rectangle will be stored. | |||||
| * \param ymin A pointer to an integer where the topmost edge of the | |||||
| * dirty rectangle will be stored. | |||||
| * \param xmax A pointer to an integer where the rightmost edge of the | |||||
| * dirty rectangle will be stored. | |||||
| * \param ymax A pointer to an integer where the bottommost edge of the | |||||
| * dirty rectangle will be stored. | |||||
| * \return 0 in case of success, -1 if an error occurred. | |||||
| */ | |||||
| int caca_get_dirty_rectangle(caca_canvas_t *cv, int index, | |||||
| int *xmin, int *ymin, int *xmax, int *ymax) | |||||
| { | |||||
| /* For now, index can only be zero, and if the dirty rectangle is | |||||
| * empty, we don't return anything. */ | |||||
| if(index < 0 || index > 0 | |||||
| || cv->dirty_xmin > cv->dirty_xmax || cv->dirty_ymin > cv->dirty_ymax | |||||
| || cv->dirty_xmax < 0 || cv->dirty_xmin >= cv->width | |||||
| || cv->dirty_ymax < 0 || cv->dirty_ymin >= cv->height) | |||||
| { | |||||
| seterrno(EINVAL); | |||||
| 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; | |||||
| if(cv->dirty_ymin < 0) | |||||
| cv->dirty_ymin = 0; | |||||
| if(cv->dirty_ymax > cv->height - 1) | |||||
| cv->dirty_ymax = cv->height - 1; | |||||
| *xmin = cv->dirty_xmin; | |||||
| *xmax = cv->dirty_xmax; | |||||
| *ymin = cv->dirty_ymin; | |||||
| *ymax = cv->dirty_ymax; | |||||
| return 0; | |||||
| } | |||||
| /** \brief Add an area to the canvas's dirty rectangle list. | |||||
| * | |||||
| * Add an invalidating zone to the canvas's dirty rectangle list. For more | |||||
| * information about the dirty rectangles, see caca_get_dirty_rectangle(). | |||||
| * | |||||
| * This function may be useful to force refresh of a given zone of the | |||||
| * canvas even if the dirty rectangle tracking indicates that it is | |||||
| * unchanged. This may happen if the canvas contents were somewhat | |||||
| * directly modified. | |||||
| * | |||||
| * If an error occurs, -1 is returned and \b errno is set accordingly: | |||||
| * - \c EINVAL Specified rectangle coordinates are out of bounds. | |||||
| * | |||||
| * \param cv A libcaca canvas. | |||||
| * \param xmin The leftmost edge of the additional dirty rectangle. | |||||
| * \param ymin The topmost edge of the additional dirty rectangle. | |||||
| * \param xmax The rightmost edge of the additional dirty rectangle. | |||||
| * \param ymax The bottommost edge of the additional dirty rectangle. | |||||
| * \return 0 in case of success, -1 if an error occurred. | |||||
| */ | |||||
| int caca_add_dirty_rectangle(caca_canvas_t *cv, int xmin, int ymin, | |||||
| int xmax, int ymax) | |||||
| { | |||||
| /* Ignore empty and out-of-bounds rectangles */ | |||||
| if(xmin > xmax || ymin > ymax | |||||
| || xmax < 0 || xmin >= cv->width || ymax < 0 || ymin >= cv->height) | |||||
| { | |||||
| seterrno(EINVAL); | |||||
| return -1; | |||||
| } | |||||
| if(xmin < cv->dirty_xmin) | |||||
| cv->dirty_xmin = xmin; | |||||
| if(xmax > cv->dirty_xmax) | |||||
| cv->dirty_xmax = xmax; | |||||
| if(ymin < cv->dirty_ymin) | |||||
| cv->dirty_ymin = ymin; | |||||
| if(ymax > cv->dirty_ymax) | |||||
| cv->dirty_ymax = ymax; | |||||
| return 0; | |||||
| } | |||||
| /** \brief Remove an area from the dirty rectangle list. | |||||
| * | |||||
| * Mark a cell area in the canvas as not dirty. For more information about | |||||
| * the dirty rectangles, see caca_get_dirty_rectangle(). | |||||
| * | |||||
| * Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that | |||||
| * the dirty rectangle is empty. They will be silently ignored. | |||||
| * | |||||
| * If an error occurs, -1 is returned and \b errno is set accordingly: | |||||
| * - \c EINVAL Specified rectangle coordinates are out of bounds. | |||||
| * | |||||
| * \param cv A libcaca canvas. | |||||
| * \param xmin The leftmost edge of the clean rectangle. | |||||
| * \param ymin The topmost edge of the clean rectangle. | |||||
| * \param xmax The rightmost edge of the clean rectangle. | |||||
| * \param ymax The bottommost edge of the clean rectangle. | |||||
| * \return 0 in case of success, -1 if an error occurred. | |||||
| */ | |||||
| int caca_remove_dirty_rectangle(caca_canvas_t *cv, int xmin, int ymin, | |||||
| int xmax, int ymax) | |||||
| { | |||||
| /* Ignore empty and out-of-bounds rectangles */ | |||||
| if(xmin > xmax || ymin > ymax | |||||
| || xmax < 0 || xmin >= cv->width || ymax < 0 || ymin >= cv->height) | |||||
| { | |||||
| seterrno(EINVAL); | |||||
| return -1; | |||||
| } | |||||
| /* FIXME: implement this function. It's OK to have it do nothing, | |||||
| * since we take a conservative approach in dirty rectangle handling, | |||||
| * but we ought to help the rendering eventually. */ | |||||
| return 0; | |||||
| } | |||||
| /** \brief Clear a canvas's dirty rectangle list. | |||||
| * | |||||
| * Empty the canvas's dirty rectangle list. | |||||
| * | |||||
| * This function never fails. | |||||
| * | |||||
| * \param cv A libcaca canvas. | |||||
| * \return This function always returns 0. | |||||
| */ | |||||
| int caca_clear_dirty_rectangle_list(caca_canvas_t *cv) | |||||
| { | |||||
| cv->dirty_xmin = cv->width; | |||||
| cv->dirty_xmax = -1; | |||||
| cv->dirty_ymin = cv->height; | |||||
| cv->dirty_ymax = -1; | |||||
| return 0; | |||||
| } | |||||
| @@ -222,59 +222,61 @@ static void slang_display(caca_display_t *dp) | |||||
| { | { | ||||
| uint32_t const *cvchars = (uint32_t const *)caca_get_canvas_chars(dp->cv); | uint32_t const *cvchars = (uint32_t const *)caca_get_canvas_chars(dp->cv); | ||||
| uint32_t const *cvattrs = (uint32_t const *)caca_get_canvas_attrs(dp->cv); | uint32_t const *cvattrs = (uint32_t const *)caca_get_canvas_attrs(dp->cv); | ||||
| int x, y; | |||||
| int xmin, ymin, xmax, ymax; | |||||
| int x, y, i; | |||||
| caca_get_dirty_rectangle(dp->cv, &xmin, &ymin, &xmax, &ymax); | |||||
| if(xmin > xmax || ymin > ymax) | |||||
| return; | |||||
| for(y = ymin; y <= ymax; y++) | |||||
| for(i = 0; i < caca_get_dirty_rectangle_count(dp->cv); i++) | |||||
| { | { | ||||
| SLsmg_gotorc(y, 0); | |||||
| for(x = xmax; x >= xmin; x--) | |||||
| int xmin, ymin, xmax, ymax; | |||||
| caca_get_dirty_rectangle(dp->cv, i, &xmin, &ymin, &xmax, &ymax); | |||||
| for(y = ymin; y <= ymax; y++) | |||||
| { | { | ||||
| uint32_t ch = *cvchars++; | |||||
| SLsmg_gotorc(y, 0); | |||||
| for(x = xmax; x >= xmin; x--) | |||||
| { | |||||
| uint32_t ch = *cvchars++; | |||||
| #if defined(OPTIMISE_SLANG_PALETTE) | #if defined(OPTIMISE_SLANG_PALETTE) | ||||
| /* If foreground == background, just don't use this colour | |||||
| * pair, and print a space instead of the real character. | |||||
| * XXX: disabled, because I can't remember what it was | |||||
| * here for, and in cases where SLang does not render | |||||
| * bright backgrounds, it's just fucked up. */ | |||||
| /* If foreground == background, just don't use this colour | |||||
| * pair, and print a space instead of the real character. */ | |||||
| /* XXX: disabled, because I can't remember what it was | |||||
| * here for, and in cases where SLang does not render | |||||
| * bright backgrounds, it's just fucked up. */ | |||||
| #if 0 | #if 0 | ||||
| uint8_t fgcolor = caca_attr_to_ansi_fg(*cvattrs); | |||||
| uint8_t bgcolor = caca_attr_to_ansi_bg(*cvattrs); | |||||
| if(fgcolor >= 0x10) | |||||
| fgcolor = CACA_LIGHTGRAY; | |||||
| if(bgcolor >= 0x10) | |||||
| bgcolor = CACA_BLACK; /* FIXME: handle transparency */ | |||||
| if(fgcolor == bgcolor) | |||||
| { | |||||
| if(fgcolor == CACA_BLACK) | |||||
| fgcolor = CACA_WHITE; | |||||
| else if(fgcolor == CACA_WHITE | |||||
| || fgcolor <= CACA_LIGHTGRAY) | |||||
| fgcolor = CACA_BLACK; | |||||
| uint8_t fgcolor = caca_attr_to_ansi_fg(*cvattrs); | |||||
| uint8_t bgcolor = caca_attr_to_ansi_bg(*cvattrs); | |||||
| if(fgcolor >= 0x10) | |||||
| fgcolor = CACA_LIGHTGRAY; | |||||
| if(bgcolor >= 0x10) | |||||
| bgcolor = CACA_BLACK; /* FIXME: handle transparency */ | |||||
| if(fgcolor == bgcolor) | |||||
| { | |||||
| if(fgcolor == CACA_BLACK) | |||||
| fgcolor = CACA_WHITE; | |||||
| else if(fgcolor == CACA_WHITE | |||||
| || fgcolor <= CACA_LIGHTGRAY) | |||||
| fgcolor = CACA_BLACK; | |||||
| else | |||||
| fgcolor = CACA_WHITE; | |||||
| SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]); | |||||
| SLsmg_write_char(' '); | |||||
| cvattrs++; | |||||
| } | |||||
| else | else | ||||
| fgcolor = CACA_WHITE; | |||||
| SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]); | |||||
| SLsmg_write_char(' '); | |||||
| cvattrs++; | |||||
| } | |||||
| else | |||||
| #endif | #endif | ||||
| { | |||||
| SLsmg_set_color(slang_assoc[caca_attr_to_ansi(*cvattrs++)]); | |||||
| slang_write_utf32(ch); | |||||
| } | |||||
| { | |||||
| SLsmg_set_color(slang_assoc[caca_attr_to_ansi(*cvattrs++)]); | |||||
| slang_write_utf32(ch); | |||||
| } | |||||
| #else | #else | ||||
| SLsmg_set_color(caca_attr_to_ansi(*cvattrs++)); | |||||
| slang_write_utf32(ch); | |||||
| SLsmg_set_color(caca_attr_to_ansi(*cvattrs++)); | |||||
| slang_write_utf32(ch); | |||||
| #endif | #endif | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| SLsmg_gotorc(caca_get_cursor_y(dp->cv), caca_get_cursor_x(dp->cv)); | SLsmg_gotorc(caca_get_cursor_y(dp->cv), caca_get_cursor_x(dp->cv)); | ||||
| @@ -293,58 +293,61 @@ static void x11_display(caca_display_t *dp) | |||||
| uint32_t const *cvattrs = (uint32_t const *)caca_get_canvas_attrs(dp->cv); | uint32_t const *cvattrs = (uint32_t const *)caca_get_canvas_attrs(dp->cv); | ||||
| int width = caca_get_canvas_width(dp->cv); | int width = caca_get_canvas_width(dp->cv); | ||||
| int height = caca_get_canvas_height(dp->cv); | int height = caca_get_canvas_height(dp->cv); | ||||
| int x, y, len; | |||||
| int xmin, ymin, xmax, ymax; | |||||
| int x, y, i, len; | |||||
| caca_get_dirty_rectangle(dp->cv, &xmin, &ymin, &xmax, &ymax); | |||||
| if(xmin > xmax || ymin > ymax) | |||||
| return; | |||||
| /* First draw the background colours. Splitting the process in two | |||||
| * loops like this is actually slightly faster. */ | |||||
| for(y = ymin; y <= ymax; y++) | |||||
| for(i = 0; i < caca_get_dirty_rectangle_count(dp->cv); i++) | |||||
| { | { | ||||
| for(x = xmin; x <= xmax; x += len) | |||||
| int xmin, ymin, xmax, ymax; | |||||
| caca_get_dirty_rectangle(dp->cv, i, &xmin, &ymin, &xmax, &ymax); | |||||
| /* First draw the background colours. Splitting the process in two | |||||
| * loops like this is actually slightly faster. */ | |||||
| for(y = ymin; y <= ymax; y++) | |||||
| { | { | ||||
| uint32_t const *attrs = cvattrs + x + y * width; | |||||
| uint16_t bg = caca_attr_to_rgb12_bg(*attrs); | |||||
| len = 1; | |||||
| while(x + len < width | |||||
| && caca_attr_to_rgb12_bg(attrs[len]) == bg) | |||||
| len++; | |||||
| XSetForeground(dp->drv.p->dpy, dp->drv.p->gc, | |||||
| dp->drv.p->colors[bg]); | |||||
| XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->gc, | |||||
| x * dp->drv.p->font_width, | |||||
| y * dp->drv.p->font_height, | |||||
| len * dp->drv.p->font_width, | |||||
| dp->drv.p->font_height); | |||||
| for(x = xmin; x <= xmax; x += len) | |||||
| { | |||||
| uint32_t const *attrs = cvattrs + x + y * width; | |||||
| uint16_t bg = caca_attr_to_rgb12_bg(*attrs); | |||||
| len = 1; | |||||
| while(x + len < width | |||||
| && caca_attr_to_rgb12_bg(attrs[len]) == bg) | |||||
| len++; | |||||
| XSetForeground(dp->drv.p->dpy, dp->drv.p->gc, | |||||
| dp->drv.p->colors[bg]); | |||||
| XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap, | |||||
| dp->drv.p->gc, | |||||
| x * dp->drv.p->font_width, | |||||
| y * dp->drv.p->font_height, | |||||
| len * dp->drv.p->font_width, | |||||
| dp->drv.p->font_height); | |||||
| } | |||||
| } | } | ||||
| } | |||||
| /* Then print the foreground characters */ | |||||
| for(y = ymin; y <= ymax; y++) | |||||
| { | |||||
| int yoff = (y + 1) * dp->drv.p->font_height | |||||
| - dp->drv.p->font_offset; | |||||
| uint32_t const *chars = cvchars + y * width; | |||||
| uint32_t const *attrs = cvattrs + y * width; | |||||
| for(x = xmin; x <= xmax; x++, chars++, attrs++) | |||||
| /* Then print the foreground characters */ | |||||
| for(y = ymin; y <= ymax; y++) | |||||
| { | { | ||||
| XSetForeground(dp->drv.p->dpy, dp->drv.p->gc, | |||||
| int yoff = (y + 1) * dp->drv.p->font_height | |||||
| - dp->drv.p->font_offset; | |||||
| uint32_t const *chars = cvchars + y * width; | |||||
| uint32_t const *attrs = cvattrs + y * width; | |||||
| for(x = xmin; x <= xmax; x++, chars++, attrs++) | |||||
| { | |||||
| XSetForeground(dp->drv.p->dpy, dp->drv.p->gc, | |||||
| dp->drv.p->colors[caca_attr_to_rgb12_fg(*attrs)]); | dp->drv.p->colors[caca_attr_to_rgb12_fg(*attrs)]); | ||||
| x11_put_glyph(dp, x * dp->drv.p->font_width, | |||||
| y * dp->drv.p->font_height, yoff, | |||||
| dp->drv.p->font_width, dp->drv.p->font_height, | |||||
| *attrs, *chars); | |||||
| x11_put_glyph(dp, x * dp->drv.p->font_width, | |||||
| y * dp->drv.p->font_height, yoff, | |||||
| dp->drv.p->font_width, dp->drv.p->font_height, | |||||
| *attrs, *chars); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| /* Print the cursor if necessary */ | |||||
| /* Print the cursor if necessary. FIXME: handle dirty rectangles! */ | |||||
| if(dp->drv.p->cursor_flags) | if(dp->drv.p->cursor_flags) | ||||
| { | { | ||||
| XSetForeground(dp->drv.p->dpy, dp->drv.p->gc, | XSetForeground(dp->drv.p->dpy, dp->drv.p->gc, | ||||
| @@ -72,7 +72,7 @@ int caca_set_frame(caca_canvas_t *cv, int id) | |||||
| cv->frame = id; | cv->frame = id; | ||||
| _caca_load_frame_info(cv); | _caca_load_frame_info(cv); | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -236,7 +236,7 @@ int caca_free_frame(caca_canvas_t *cv, int id) | |||||
| { | { | ||||
| cv->frame = 0; | cv->frame = 0; | ||||
| _caca_load_frame_info(cv); | _caca_load_frame_info(cv); | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| } | } | ||||
| return 0; | return 0; | ||||
| @@ -155,7 +155,7 @@ int caca_refresh_display(caca_display_t *dp) | |||||
| dp->drv.display(dp); | dp->drv.display(dp); | ||||
| /* Invalidate the dirty rectangle */ | /* Invalidate the dirty rectangle */ | ||||
| caca_set_dirty_rectangle(dp->cv, -1, -1, -1, -1); | |||||
| caca_clear_dirty_rectangle_list(dp->cv); | |||||
| /* Once the display is finished, we can ack resizes */ | /* Once the display is finished, we can ack resizes */ | ||||
| if(dp->resize.resized) | if(dp->resize.resized) | ||||
| @@ -416,6 +416,10 @@ | |||||
| RelativePath=".\driver\conio.c" | RelativePath=".\driver\conio.c" | ||||
| > | > | ||||
| </File> | </File> | ||||
| <File | |||||
| RelativePath=".\dirty.c" | |||||
| > | |||||
| </File> | |||||
| <File | <File | ||||
| RelativePath=".\dither.c" | RelativePath=".\dither.c" | ||||
| > | > | ||||
| @@ -322,7 +322,7 @@ int caca_clear_canvas(caca_canvas_t *cv) | |||||
| cv->attrs[n] = attr; | cv->attrs[n] = attr; | ||||
| } | } | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -528,7 +528,7 @@ int caca_set_canvas_boundaries(caca_canvas_t *cv, int x, int y, int w, int h) | |||||
| _caca_load_frame_info(cv); | _caca_load_frame_info(cv); | ||||
| /* FIXME: this may be optimised somewhat */ | /* FIXME: this may be optimised somewhat */ | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -54,7 +54,7 @@ int caca_invert(caca_canvas_t *cv) | |||||
| attrs++; | attrs++; | ||||
| } | } | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -115,7 +115,7 @@ int caca_flip(caca_canvas_t *cv) | |||||
| } | } | ||||
| } | } | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -162,7 +162,7 @@ int caca_flop(caca_canvas_t *cv) | |||||
| *ctop = flopchar(*ctop); | *ctop = flopchar(*ctop); | ||||
| } | } | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -221,7 +221,7 @@ int caca_rotate_180(caca_canvas_t *cv) | |||||
| } | } | ||||
| } | } | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -340,7 +340,7 @@ int caca_rotate_left(caca_canvas_t *cv) | |||||
| /* Reset the current frame shortcuts */ | /* Reset the current frame shortcuts */ | ||||
| _caca_load_frame_info(cv); | _caca_load_frame_info(cv); | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -459,7 +459,7 @@ int caca_rotate_right(caca_canvas_t *cv) | |||||
| /* Reset the current frame shortcuts */ | /* Reset the current frame shortcuts */ | ||||
| _caca_load_frame_info(cv); | _caca_load_frame_info(cv); | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -552,7 +552,7 @@ int caca_stretch_left(caca_canvas_t *cv) | |||||
| /* Reset the current frame shortcuts */ | /* Reset the current frame shortcuts */ | ||||
| _caca_load_frame_info(cv); | _caca_load_frame_info(cv); | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -645,7 +645,7 @@ int caca_stretch_right(caca_canvas_t *cv) | |||||
| /* Reset the current frame shortcuts */ | /* Reset the current frame shortcuts */ | ||||
| _caca_load_frame_info(cv); | _caca_load_frame_info(cv); | ||||
| caca_set_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -38,21 +38,24 @@ public: | |||||
| void test_create() | void test_create() | ||||
| { | { | ||||
| caca_canvas_t *cv; | caca_canvas_t *cv; | ||||
| int xmin, xmax, ymin, ymax; | |||||
| int i, xmin, xmax, ymin, ymax; | |||||
| /* Check that the dirty rectangle contains the whole canvas | /* Check that the dirty rectangle contains the whole canvas | ||||
| * upon creation. */ | * upon creation. */ | ||||
| cv = caca_create_canvas(WIDTH, HEIGHT); | cv = caca_create_canvas(WIDTH, HEIGHT); | ||||
| caca_get_dirty_rectangle(cv, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT(xmin <= 0); | |||||
| CPPUNIT_ASSERT(ymin <= 0); | |||||
| CPPUNIT_ASSERT(xmax >= caca_get_canvas_width(cv) - 1); | |||||
| CPPUNIT_ASSERT(ymax >= caca_get_canvas_height(cv) - 1); | |||||
| i = caca_get_dirty_rectangle_count(cv); | |||||
| CPPUNIT_ASSERT_EQUAL(i, 1); | |||||
| caca_get_dirty_rectangle(cv, 0, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT_EQUAL(xmin, 0); | |||||
| CPPUNIT_ASSERT_EQUAL(ymin, 0); | |||||
| CPPUNIT_ASSERT_EQUAL(xmax, caca_get_canvas_width(cv) - 1); | |||||
| CPPUNIT_ASSERT_EQUAL(ymax, caca_get_canvas_height(cv) - 1); | |||||
| /* Invalidate the dirty rectangle and check that it stays so. */ | /* Invalidate the dirty rectangle and check that it stays so. */ | ||||
| caca_set_dirty_rectangle(cv, -1, -1, -1, -1); | |||||
| caca_get_dirty_rectangle(cv, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT(xmin > xmax || ymin > ymax); | |||||
| caca_clear_dirty_rectangle_list(cv); | |||||
| i = caca_get_dirty_rectangle_count(cv); | |||||
| CPPUNIT_ASSERT_EQUAL(i, 0); | |||||
| caca_free_canvas(cv); | caca_free_canvas(cv); | ||||
| } | } | ||||
| @@ -60,66 +63,78 @@ public: | |||||
| void test_put_char() | void test_put_char() | ||||
| { | { | ||||
| caca_canvas_t *cv; | caca_canvas_t *cv; | ||||
| int xmin, xmax, ymin, ymax; | |||||
| int i, xmin, xmax, ymin, ymax; | |||||
| cv = caca_create_canvas(WIDTH, HEIGHT); | cv = caca_create_canvas(WIDTH, HEIGHT); | ||||
| caca_set_dirty_rectangle(cv, -1, -1, -1, -1); | |||||
| caca_clear_dirty_rectangle_list(cv); | |||||
| caca_put_char(cv, 7, 3, 'x'); | caca_put_char(cv, 7, 3, 'x'); | ||||
| caca_get_dirty_rectangle(cv, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT(xmin <= 7); | |||||
| CPPUNIT_ASSERT(ymin <= 3); | |||||
| CPPUNIT_ASSERT(xmax >= 7); | |||||
| CPPUNIT_ASSERT(ymax >= 3); | |||||
| i = caca_get_dirty_rectangle_count(cv); | |||||
| CPPUNIT_ASSERT_EQUAL(i, 1); | |||||
| caca_get_dirty_rectangle(cv, 0, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT_EQUAL(xmin, 7); | |||||
| CPPUNIT_ASSERT_EQUAL(ymin, 3); | |||||
| CPPUNIT_ASSERT_EQUAL(xmax, 7); | |||||
| CPPUNIT_ASSERT_EQUAL(ymax, 3); | |||||
| caca_clear_canvas(cv); | caca_clear_canvas(cv); | ||||
| caca_set_dirty_rectangle(cv, -1, -1, -1, -1); | |||||
| caca_clear_dirty_rectangle_list(cv); | |||||
| caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | ||||
| caca_get_dirty_rectangle(cv, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT(xmin <= 7); | |||||
| CPPUNIT_ASSERT(ymin <= 3); | |||||
| CPPUNIT_ASSERT(xmax >= 8); | |||||
| CPPUNIT_ASSERT(ymax >= 3); | |||||
| i = caca_get_dirty_rectangle_count(cv); | |||||
| CPPUNIT_ASSERT_EQUAL(i, 1); | |||||
| caca_get_dirty_rectangle(cv, 0, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT_EQUAL(xmin, 7); | |||||
| CPPUNIT_ASSERT_EQUAL(ymin, 3); | |||||
| CPPUNIT_ASSERT_EQUAL(xmax, 8); | |||||
| CPPUNIT_ASSERT_EQUAL(ymax, 3); | |||||
| caca_clear_canvas(cv); | caca_clear_canvas(cv); | ||||
| caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | ||||
| caca_set_dirty_rectangle(cv, -1, -1, -1, -1); | |||||
| caca_clear_dirty_rectangle_list(cv); | |||||
| caca_put_char(cv, 7, 3, 'x'); | caca_put_char(cv, 7, 3, 'x'); | ||||
| caca_get_dirty_rectangle(cv, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT(xmin <= 7); | |||||
| CPPUNIT_ASSERT(ymin <= 3); | |||||
| CPPUNIT_ASSERT(xmax >= 8); | |||||
| CPPUNIT_ASSERT(ymax >= 3); | |||||
| i = caca_get_dirty_rectangle_count(cv); | |||||
| CPPUNIT_ASSERT_EQUAL(i, 1); | |||||
| caca_get_dirty_rectangle(cv, 0, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT_EQUAL(xmin, 7); | |||||
| CPPUNIT_ASSERT_EQUAL(ymin, 3); | |||||
| CPPUNIT_ASSERT_EQUAL(xmax, 8); | |||||
| CPPUNIT_ASSERT_EQUAL(ymax, 3); | |||||
| caca_clear_canvas(cv); | caca_clear_canvas(cv); | ||||
| caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | ||||
| caca_set_dirty_rectangle(cv, -1, -1, -1, -1); | |||||
| caca_clear_dirty_rectangle_list(cv); | |||||
| caca_put_char(cv, 8, 3, 'x'); | caca_put_char(cv, 8, 3, 'x'); | ||||
| caca_get_dirty_rectangle(cv, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT(xmin <= 7); | |||||
| CPPUNIT_ASSERT(ymin <= 3); | |||||
| CPPUNIT_ASSERT(xmax >= 8); | |||||
| CPPUNIT_ASSERT(ymax >= 3); | |||||
| i = caca_get_dirty_rectangle_count(cv); | |||||
| CPPUNIT_ASSERT_EQUAL(i, 1); | |||||
| caca_get_dirty_rectangle(cv, 0, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT_EQUAL(xmin, 7); | |||||
| CPPUNIT_ASSERT_EQUAL(ymin, 3); | |||||
| CPPUNIT_ASSERT_EQUAL(xmax, 8); | |||||
| CPPUNIT_ASSERT_EQUAL(ymax, 3); | |||||
| caca_clear_canvas(cv); | caca_clear_canvas(cv); | ||||
| caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | ||||
| caca_set_dirty_rectangle(cv, -1, -1, -1, -1); | |||||
| caca_clear_dirty_rectangle_list(cv); | |||||
| caca_put_char(cv, 6, 3, 0x2f06 /* ⼆ */); | caca_put_char(cv, 6, 3, 0x2f06 /* ⼆ */); | ||||
| caca_get_dirty_rectangle(cv, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT(xmin <= 6); | |||||
| CPPUNIT_ASSERT(ymin <= 3); | |||||
| CPPUNIT_ASSERT(xmax >= 8); | |||||
| CPPUNIT_ASSERT(ymax >= 3); | |||||
| i = caca_get_dirty_rectangle_count(cv); | |||||
| CPPUNIT_ASSERT_EQUAL(i, 1); | |||||
| caca_get_dirty_rectangle(cv, 0, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT_EQUAL(xmin, 6); | |||||
| CPPUNIT_ASSERT_EQUAL(ymin, 3); | |||||
| CPPUNIT_ASSERT_EQUAL(xmax, 8); | |||||
| CPPUNIT_ASSERT_EQUAL(ymax, 3); | |||||
| caca_clear_canvas(cv); | caca_clear_canvas(cv); | ||||
| caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | caca_put_char(cv, 7, 3, 0x2f06 /* ⼆ */); | ||||
| caca_set_dirty_rectangle(cv, -1, -1, -1, -1); | |||||
| caca_clear_dirty_rectangle_list(cv); | |||||
| caca_put_char(cv, 8, 3, 0x2f06 /* ⼆ */); | caca_put_char(cv, 8, 3, 0x2f06 /* ⼆ */); | ||||
| caca_get_dirty_rectangle(cv, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT(xmin <= 7); | |||||
| CPPUNIT_ASSERT(ymin <= 3); | |||||
| CPPUNIT_ASSERT(xmax >= 9); | |||||
| CPPUNIT_ASSERT(ymax >= 3); | |||||
| i = caca_get_dirty_rectangle_count(cv); | |||||
| CPPUNIT_ASSERT_EQUAL(i, 1); | |||||
| caca_get_dirty_rectangle(cv, 0, &xmin, &ymin, &xmax, &ymax); | |||||
| CPPUNIT_ASSERT_EQUAL(xmin, 7); | |||||
| CPPUNIT_ASSERT_EQUAL(ymin, 3); | |||||
| CPPUNIT_ASSERT_EQUAL(xmax, 9); | |||||
| CPPUNIT_ASSERT_EQUAL(ymax, 3); | |||||
| } | } | ||||
| private: | private: | ||||