libcaca  0.99.beta19
About: libcaca is a graphics library that outputs text instead of pixels, so that it can work on older video cards or text terminals (something like an advanced AAlib library).
  Fossies Dox: libcaca-0.99.beta19.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

dirty.c
Go to the documentation of this file.
1 /*
2  * libcaca Colour ASCII-Art library
3  * Copyright (c) 2002-2012 Sam Hocevar <sam@hocevar.net>
4  * All Rights Reserved
5  *
6  * This library is free software. It comes without any warranty, to
7  * the extent permitted by applicable law. You can redistribute it
8  * and/or modify it under the terms of the Do What the Fuck You Want
9  * to Public License, Version 2, as published by Sam Hocevar. See
10  * http://www.wtfpl.net/ for more details.
11  */
12 
13 /*
14  * This file contains the dirty rectangle handling functions.
15  *
16  *
17  * About dirty rectangles:
18  *
19  * * Dirty rectangles MUST NOT be larger than the canvas. If the user
20  * provides a large rectangle through caca_add_dirty_rect(), or if the
21  * canvas changes size to become smaller, all dirty rectangles MUST
22  * immediately be clipped to the canvas size.
23  */
24 
25 #include "config.h"
26 
27 #if !defined(__KERNEL__)
28 # include <stdio.h>
29 # include <string.h>
30 #endif
31 
32 #include "caca.h"
33 #include "caca_internals.h"
34 
35 static void merge_new_rect(caca_canvas_t *cv, int n);
36 
52 {
53  cv->dirty_disabled++;
54 
55  return 0;
56 }
57 
70 {
71  if(cv->dirty_disabled <= 0)
72  {
73  seterrno(EINVAL);
74  return -1;
75  }
76 
77  cv->dirty_disabled--;
78 
79  return 0;
80 }
81 
99 {
100  return cv->ndirty;
101 }
102 
126  int *x, int *y, int *width, int *height)
127 {
128  if(r < 0 || r >= cv->ndirty)
129  {
130  seterrno(EINVAL);
131  return -1;
132  }
133 
134  *x = cv->dirty[r].xmin;
135  *y = cv->dirty[r].ymin;
136  *width = cv->dirty[r].xmax - cv->dirty[r].xmin + 1;
137  *height = cv->dirty[r].ymax - cv->dirty[r].ymin + 1;
138 
139  debug("dirty #%i: %ix%i at (%i,%i)", r, *width, *height, *x, *y);
140 
141  return 0;
142 }
143 
164 int caca_add_dirty_rect(caca_canvas_t *cv, int x, int y, int width, int height)
165 {
166  debug("new dirty: %ix%i at (%i,%i)", width, height, x, y);
167 
168  /* Clip arguments to canvas */
169  if(x < 0) { width += x; x = 0; }
170 
171  if(x + width > cv->width)
172  width = cv->width - x;
173 
174  if(y < 0) { height += y; y = 0; }
175 
176  if(y + height > cv->height)
177  height = cv->height - y;
178 
179  /* Ignore empty and out-of-canvas rectangles */
180  if(width <= 0 || height <= 0)
181  {
182  seterrno(EINVAL);
183  return -1;
184  }
185 
186  /* Add the new rectangle to the list; it works even if cv->ndirty
187  * is MAX_DIRTY_COUNT because there's an extra cell in the array. */
188  cv->dirty[cv->ndirty].xmin = x;
189  cv->dirty[cv->ndirty].ymin = y;
190  cv->dirty[cv->ndirty].xmax = x + width - 1;
191  cv->dirty[cv->ndirty].ymax = y + height - 1;
192  cv->ndirty++;
193 
194  /* Try to merge the new rectangle with existing ones. This also ensures
195  * that cv->ndirty is brought back below MAX_DIRTY_COUNT. */
196  merge_new_rect(cv, cv->ndirty - 1);
197 
198  return 0;
199 }
200 
220  int width, int height)
221 {
222  /* Clip arguments to canvas size */
223  if(x < 0) { width += x; x = 0; }
224 
225  if(x + width > cv->width)
226  width = cv->width - x;
227 
228  if(y < 0) { height += y; y = 0; }
229 
230  if(y + height > cv->height)
231  height = cv->height - y;
232 
233  /* Ignore empty and out-of-canvas rectangles */
234  if(width <= 0 || height <= 0)
235  {
236  seterrno(EINVAL);
237  return -1;
238  }
239 
240  /* FIXME: implement this function. It's OK to have it do nothing,
241  * since we take a conservative approach in dirty rectangle handling,
242  * but we ought to help the rendering eventually. */
243 
244  return 0;
245 }
246 
257 {
258  cv->ndirty = 0;
259 
260  return 0;
261 }
262 
263 /*
264  * XXX: the following functions are local.
265  */
266 
267 static inline int int_min(int a, int b) { return a < b ? a : b; }
268 static inline int int_max(int a, int b) { return a > b ? a : b; }
269 
270 /* Merge a newly added rectangle, if necessary. */
271 static void merge_new_rect(caca_canvas_t *cv, int n)
272 {
273  int wasted[MAX_DIRTY_COUNT + 1];
274  int i, sn, best, best_score;
275 
276  best = -1;
277  best_score = cv->width * cv->height;
278 
279  sn = (cv->dirty[n].xmax - cv->dirty[n].xmin + 1)
280  * (cv->dirty[n].ymax - cv->dirty[n].ymin + 1);
281 
282  /* Check whether the new rectangle can be merged with an existing one. */
283  for(i = 0; i < cv->ndirty; i++)
284  {
285  int si, sf, xmin, ymin, xmax, ymax;
286 
287  if(i == n)
288  continue;
289 
290  xmin = int_min(cv->dirty[i].xmin, cv->dirty[n].xmin);
291  ymin = int_min(cv->dirty[i].ymin, cv->dirty[n].ymin);
292  xmax = int_max(cv->dirty[i].xmax, cv->dirty[n].xmax);
293  ymax = int_max(cv->dirty[i].ymax, cv->dirty[n].ymax);
294 
295  sf = (xmax - xmin + 1) * (ymax - ymin + 1);
296 
297  /* Shortcut: if the current rectangle is inside the new rectangle,
298  * we remove the current rectangle and continue trying merges. */
299  if(sf == sn)
300  {
301  memmove(&cv->dirty[i], &cv->dirty[i + 1],
302  (cv->ndirty - i) * sizeof(cv->dirty[0]));
303  cv->ndirty--;
304 
305  if(i < n)
306  n--;
307  else
308  i--;
309 
310  continue;
311  }
312 
313  si = (cv->dirty[i].xmax - cv->dirty[i].xmin + 1)
314  * (cv->dirty[i].ymax - cv->dirty[i].ymin + 1);
315 
316  /* Shortcut: if the new rectangle is inside the current rectangle,
317  * we get rid of the new rectangle and bail out. */
318  if(sf == si)
319  {
320  cv->ndirty--;
321  memmove(&cv->dirty[n], &cv->dirty[n + 1],
322  (cv->ndirty - n) * sizeof(cv->dirty[0]));
323  return;
324  }
325 
326  /* We store approximately how many bytes were wasted. FIXME: this is
327  * not an exact computation, we need to be more precise. */
328  wasted[i] = sf - si - sn;
329 
330  if(wasted[i] < best_score)
331  {
332  best = i;
333  best_score = wasted[i];
334  }
335  }
336 
337  /* FIXME: we only try to merge the current rectangle, ignoring
338  * potentially better merges. */
339 
340  /* If no acceptable score was found and the dirty rectangle list is
341  * not full, we bail out. */
342  if(best_score > 0 && cv->ndirty < MAX_DIRTY_COUNT)
343  return;
344 
345  /* Otherwise, merge the rectangle with the best candidate */
346  cv->dirty[best].xmin = int_min(cv->dirty[best].xmin, cv->dirty[n].xmin);
347  cv->dirty[best].ymin = int_min(cv->dirty[best].ymin, cv->dirty[n].ymin);
348  cv->dirty[best].xmax = int_max(cv->dirty[best].xmax, cv->dirty[n].xmax);
349  cv->dirty[best].ymax = int_max(cv->dirty[best].ymax, cv->dirty[n].ymax);
350 
351  memmove(&cv->dirty[n], &cv->dirty[n + 1],
352  (cv->ndirty - n) * sizeof(cv->dirty[0]));
353  cv->ndirty--;
354 
355  if(best < n)
356  merge_new_rect(cv, best);
357  else
358  merge_new_rect(cv, best - 1);
359 }
360 
361 /* Clip all dirty rectangles in case they're larger than the canvas */
363 {
364  int i;
365 
366  for(i = 0; i < cv->ndirty; i++)
367  {
368  if(cv->dirty[i].xmin < 0)
369  cv->dirty[i].xmin = 0;
370 
371  if(cv->dirty[i].ymin < 0)
372  cv->dirty[i].ymin = 0;
373 
374  if(cv->dirty[i].xmax >= cv->width)
375  cv->dirty[i].xmax = cv->width - 1;
376 
377  if(cv->dirty[i].ymax >= cv->height)
378  cv->dirty[i].ymax = cv->height - 1;
379  }
380 }
381 
caca_canvas::ymax
int ymax
Definition: caca_internals.h:66
caca_remove_dirty_rect
int caca_remove_dirty_rect(caca_canvas_t *cv, int x, int y, int width, int height)
Remove an area from the dirty rectangle list.
Definition: dirty.c:219
y
static int y
Definition: cacadraw.c:27
caca_enable_dirty_rect
int caca_enable_dirty_rect(caca_canvas_t *cv)
Enable dirty rectangles.
Definition: dirty.c:69
caca_canvas::xmax
int xmax
Definition: caca_internals.h:66
int_min
static int int_min(int a, int b)
Definition: dirty.c:267
caca_clear_dirty_rect_list
int caca_clear_dirty_rect_list(caca_canvas_t *cv)
Clear a canvas's dirty rectangle list.
Definition: dirty.c:256
caca_canvas::ymin
int ymin
Definition: caca_internals.h:66
caca_canvas::xmin
int xmin
Definition: caca_internals.h:66
caca_get_dirty_rect
int caca_get_dirty_rect(caca_canvas_t *cv, int r, int *x, int *y, int *width, int *height)
Get a canvas's dirty rectangle.
Definition: dirty.c:125
int_max
static int int_max(int a, int b)
Definition: dirty.c:268
seterrno
#define seterrno(x)
Definition: caca_stubs.h:27
caca_canvas::ndirty
int ndirty
Definition: caca_internals.h:63
caca_add_dirty_rect
int caca_add_dirty_rect(caca_canvas_t *cv, int x, int y, int width, int height)
Add an area to the canvas's dirty rectangle list.
Definition: dirty.c:164
caca_disable_dirty_rect
int caca_disable_dirty_rect(caca_canvas_t *cv)
Disable dirty rectangles.
Definition: dirty.c:51
cv
caca_canvas_t * cv
Definition: cacaview.c:45
caca_canvas::dirty_disabled
int dirty_disabled
Definition: caca_internals.h:63
caca_canvas::dirty
struct caca_canvas::@4 dirty[8+1]
merge_new_rect
static void merge_new_rect(caca_canvas_t *cv, int n)
Definition: dirty.c:271
caca_internals.h
caca_canvas::width
int width
Definition: caca_internals.h:71
caca_get_dirty_rect_count
int caca_get_dirty_rect_count(caca_canvas_t *cv)
Get the number of dirty rectangles in the canvas.
Definition: dirty.c:98
_caca_clip_dirty_rect_list
void _caca_clip_dirty_rect_list(caca_canvas_t *cv)
Definition: dirty.c:362
caca.h
The libcaca public header.
config.h
MAX_DIRTY_COUNT
#define MAX_DIRTY_COUNT
Definition: caca_internals.h:26
caca_canvas::height
int height
Definition: caca_internals.h:71
debug
#define debug(format,...)
Definition: caca_debug.h:36
caca_canvas
Definition: caca_internals.h:47
x
static int x
Definition: cacadraw.c:27