|
|
/* * drs.c --- dirty rectangles system * * This file is gift-ware. This file is given to you freely * as a gift. You may use, modify, redistribute, and generally hack * it about in any way you like, and you do not have to give anyone * anything in return. * * I do not accept any responsibility for any effects, adverse or * otherwise, that this code may have on just about anything that * you can think of. Use it at your own risk. * * Copyright (C) 1998, 1999 Michael Bukin */ /* * Almost general purpose dirty-rectangles system. * Ideas borrowed from Shawn Hargreaves, George Foot, Owen Embury, Ove Kaaven * and me, of course. */ #include#include #include #include "ex_drs.h" /* Rectangle. */ typedef struct __DRS_rect_t { int x, y, w, h; } __DRS_rect_t; /* Span of x. */ typedef struct __DRS_span_t { int x1, x2; } __DRS_span_t; /* Offscreen (double-buffer) bitmap. */ BITMAP* DRS_work = 0; /* Background bitmap and color. */ static BITMAP* __DRS_bbmp = 0; static int __DRS_bcol = 0; /* Update the whole screen. */ static int __DRS_full = 0; /* Use double-buffer mode. */ static int __DRS_dbuf = 0; /* Wait for vsync before screen update. */ static int __DRS_vsync = 0; /* Viewport offset in background. */ static int __DRS_xoff = 0; static int __DRS_yoff = 0; /* List of bank starts. */ static int* __DRS_bank = 0; /* List of spans. */ static __DRS_span_t* __DRS_span = 0; /* Size of rect's list. */ static int __DRS_size = 0; /* Old rectangles list and its size. */ static __DRS_rect_t* __DRS_omem = 0; static __DRS_rect_t* __DRS_obuf = 0; static int __DRS_onum = 0; /* New rectangles list and its size. */ static __DRS_rect_t* __DRS_nmem = 0; static __DRS_rect_t* __DRS_nbuf = 0; static int __DRS_nnum = 0; /* * Initialize DR system. */ int DRS_init (const int _size, BITMAP* _bbmp, const int _bcol) { /* Free internal data (if any). */ DRS_exit (); /* Sanity check. */ if (_size < 0) return -1; __DRS_size = 2 * _size; /* Allocate memory. */ DRS_work = create_bitmap (SCREEN_W, SCREEN_H); __DRS_bank = (int*) malloc (sizeof (int) * SCREEN_H); __DRS_span = (__DRS_span_t*) malloc (sizeof (__DRS_span_t) * SCREEN_W); __DRS_omem = (__DRS_rect_t*) malloc (sizeof (__DRS_rect_t) * __DRS_size); __DRS_nmem = (__DRS_rect_t*) malloc (sizeof (__DRS_rect_t) * __DRS_size); /* Test allocation results. */ if ((DRS_work == 0) || (__DRS_bank == 0) || (__DRS_span == 0) || (__DRS_omem == 0) || (__DRS_nmem == 0)) { /* Free internal data. */ DRS_exit (); return -1; } /* Save parameters. */ __DRS_bbmp = _bbmp; __DRS_bcol = _bcol; __DRS_xoff = 0; __DRS_yoff = 0; /* Initial lists and sizes. */ __DRS_obuf = __DRS_omem; __DRS_onum = 0; __DRS_nbuf = __DRS_nmem; __DRS_nnum = 0; /* Trigger full update. */ __DRS_full = 1; /* Calculate bank starts. */ { int i; int next_bank_line = SCREEN_H; __DRS_bank[SCREEN_H - 1] = SCREEN_H; for (i = SCREEN_H - 2; i >= 0; i--) { if (screen->line[i] >= screen->line[i + 1]) next_bank_line = i + 1; __DRS_bank[i] = next_bank_line; } } return 0; } /* * Shutdown DR system. */ void DRS_exit (void) { /* Free internal data. */ if (DRS_work != 0) { destroy_bitmap (DRS_work); DRS_work = 0; } if (__DRS_bank != 0) { free (__DRS_bank); __DRS_bank = 0; } if (__DRS_span != 0) { free (__DRS_span); __DRS_span = 0; } if (__DRS_omem != 0) { free (__DRS_omem); __DRS_omem = 0; } if (__DRS_nmem != 0) { free (__DRS_nmem); __DRS_nmem = 0; } } /* * Set background bitmap and color. */ void DRS_set_background (BITMAP* _bbmp, const int _bcol) { /* Trigger full update on background change. */ if ((__DRS_bbmp != 0) || (_bbmp != 0) || (__DRS_bcol != _bcol)) { __DRS_full = 1; /* Change background. */ __DRS_bbmp = _bbmp; __DRS_bcol = _bcol; } } /* * Set viewport offset in background bitmap. */ void DRS_set_offset (const int _x, const int _y) { /* Trigger full update on viewport shift. */ if ((__DRS_xoff != _x) || (__DRS_yoff != _y)) { /* Update everything iff background is from bitmap. */ if (__DRS_bbmp != 0) __DRS_full = 1; /* Change viewport. */ __DRS_xoff = _x; __DRS_yoff = _y; } } /* * Restart DR system. */ void DRS_restart (void) { /* Clear rectangles lists and trigger full update. */ __DRS_obuf = __DRS_omem; __DRS_nbuf = __DRS_nmem; __DRS_onum = 0; __DRS_nnum = 0; __DRS_full = 1; /* Shift viewport to the top-left corner of background image. */ __DRS_xoff = 0; __DRS_yoff = 0; } /* * Compare rectangles by y coordinate (and then by x coordinate). */ static int __DRS_cmpy (const void* _p1, const void* _p2) { const __DRS_rect_t *r1 = (__DRS_rect_t *) _p1; const __DRS_rect_t *r2 = (__DRS_rect_t *) _p2; const int result = r1->y - r2->y; return ((result != 0) ? result : (r1->x - r2->x)); } /* * Swap two rectangles. */ static void __DRS_swap (__DRS_rect_t* _p1, __DRS_rect_t* _p2) { __DRS_rect_t tmp; tmp = *_p1; *_p1 = *_p2; *_p2 = tmp; } /* * Start frame (make work bitmap identical to background bitmap using work->background list). */ void DRS_start_frame (void) { /* Make work bitmap identical to background bitmap. */ if (__DRS_full != 0) { /* Update everything. */ if (__DRS_bbmp == 0) clear_to_color (DRS_work, __DRS_bcol); else blit (__DRS_bbmp, DRS_work, __DRS_xoff, __DRS_yoff, 0, 0, SCREEN_W, SCREEN_H); __DRS_nnum = 0; } else { /* Restore rectangles. */ __DRS_rect_t* rbeg = __DRS_nbuf; __DRS_rect_t* rmax = rbeg + __DRS_nnum; __DRS_rect_t* rend; __DRS_rect_t* rcur; __DRS_span_t* scur; __DRS_span_t* smax; int ybeg, yend; /* Sort by y. */ qsort (__DRS_nbuf, __DRS_nnum, sizeof (__DRS_rect_t), __DRS_cmpy); /* Split on strips in y coordinate. */ yend = 0; while (rbeg < rmax) { rend = rbeg + 1; ybeg = (rbeg->y <= yend) ? yend : rbeg->y; yend = rbeg->y + rbeg->h; if (ybeg >= SCREEN_H) break; /* Add more rectangles to the strip. */ while ((rend < rmax) && (rend->y <= ybeg)) { const int y = rend->y + rend->h; if (y < yend) yend = y; rend++; } /* Found rectangle from next strip (limit this strip height). */ if ((rend < rmax) && (rend->y < yend)) yend = rend->y; /* Split on spans in x coordinate. */ smax = __DRS_span; for (rcur = rbeg; rcur < rend; rcur++) { int xbeg = rcur->x; int xend = xbeg + rcur->w; /* Combine current rectangle with other spans. */ for (scur = __DRS_span; scur < smax;) { const int x1 = scur->x1; const int x2 = scur->x2; if ((xbeg > x2) || (xend < x1)) { /* Outside of current span. */ scur++; continue; } /* Covers part of current span (combine and remove old span). */ if (x1 < xbeg) xbeg = x1; if (x2 > xend) xend = x2; *scur = *(--smax); } /* Add new span. */ smax->x1 = xbeg; smax->x2 = xend; smax++; if ((rcur->y + rcur->h) <= yend) { /* Remove rectangle which ends in this strip. */ if (rbeg < rcur) __DRS_swap (rcur, rbeg); rbeg++; } } /* Clear spans. */ if (__DRS_bbmp == 0) { /* With background color. */ for (scur = __DRS_span; scur < smax; scur++) rectfill (DRS_work, scur->x1, ybeg, scur->x2 - 1, yend - 1, __DRS_bcol); } else { /* With rectangles from background bitmap. */ for (scur = __DRS_span; scur < smax; scur++) blit (__DRS_bbmp, DRS_work, __DRS_xoff + scur->x1, __DRS_yoff + ybeg, scur->x1, ybeg, scur->x2 - scur->x1, yend - ybeg); } } } /* * Swap rectangles lists. * Move list of changed rectangles to the work->screen list. * Clear work->background list. */ if (__DRS_obuf == __DRS_omem) { __DRS_obuf = __DRS_nmem; __DRS_nbuf = __DRS_omem; } else { __DRS_obuf = __DRS_omem; __DRS_nbuf = __DRS_nmem; } __DRS_onum = __DRS_nnum; __DRS_nnum = 0; } /* * Add rectangle for changes in work bitmap (in background coordinates). */ void DRS_add_rectangle (const int _x, const int _y, const int _w, const int _h) { const int x = _x - __DRS_xoff; const int y = _y - __DRS_yoff; if (__DRS_dbuf != 0) return; else if ((_w > 0) && (x < SCREEN_W) && ((x + _w) > 0) && (_h > 0) && (y < SCREEN_H) && ((y + _h) > 0)) { /* Rectangle is visible on screen. */ if ((__DRS_onum >= __DRS_size) || (__DRS_nnum >= __DRS_size)) { /* Do full update on overflow of rectangles list. */ __DRS_full = 1; } else { /* Add rectangle to the work->screen list. */ __DRS_obuf[__DRS_onum].x = x; __DRS_obuf[__DRS_onum].y = y; __DRS_obuf[__DRS_onum].w = _w; __DRS_obuf[__DRS_onum].h = _h; __DRS_onum++; /* Add rectangle to the work->background list. */ __DRS_nbuf[__DRS_nnum].x = x; __DRS_nbuf[__DRS_nnum].y = y; __DRS_nbuf[__DRS_nnum].w = _w; __DRS_nbuf[__DRS_nnum].h = _h; __DRS_nnum++; } } } /* * Add rectangle for changes in background bitmap (in background coordinates). */ void DRS_background_changed (const int _x, const int _y, const int _w, const int _h) { const int x = _x - __DRS_xoff; const int y = _y - __DRS_yoff; if (__DRS_dbuf != 0) return; else if ((_w > 0) && (x < SCREEN_W) && ((x + _w) > 0) && (_h > 0) && (y < SCREEN_H) && ((y + _h) > 0)) { /* Rectangle is visible on screen. */ if (__DRS_nnum >= __DRS_size) { /* Do full update on overflow of rectangles list. */ __DRS_full = 1; } else { /* Add rectangle to the work->background list. */ __DRS_nbuf[__DRS_nnum].x = x; __DRS_nbuf[__DRS_nnum].y = y; __DRS_nbuf[__DRS_nnum].w = _w; __DRS_nbuf[__DRS_nnum].h = _h; __DRS_nnum++; } } } /* * Add rectangle for changes in background and offscreen bitmaps. */ void DRS_buffer_changed (const int _x, const int _y, const int _w, const int _h) { const int x = _x - __DRS_xoff; const int y = _y - __DRS_yoff; if (__DRS_dbuf != 0) return; else if ((_w > 0) && (x < SCREEN_W) && ((x + _w) > 0) && (_h > 0) && (y < SCREEN_H) && ((y + _h) > 0)) { /* Rectangle is visible on screen. */ if (__DRS_onum >= __DRS_size) { /* Do full update on overflow of rectangles list. */ __DRS_full = 1; } else { /* Add rectangle to the work->screen list. */ __DRS_obuf[__DRS_onum].x = x; __DRS_obuf[__DRS_onum].y = y; __DRS_obuf[__DRS_onum].w = _w; __DRS_obuf[__DRS_onum].h = _h; __DRS_onum++; } } } /* * Update screen using work->screen list. */ void DRS_update_screen (void) { if (__DRS_full) { /* Wait for vsync if necessary. */ if (__DRS_vsync) vsync (); /* Update the whole screen. */ blit (DRS_work, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); /* If double-buffering, then do full update always. */ __DRS_full = __DRS_dbuf; } else { __DRS_rect_t* rbeg = __DRS_obuf; __DRS_rect_t* rmax = rbeg + __DRS_onum; __DRS_rect_t* rend; __DRS_rect_t* rcur; __DRS_span_t* scur; __DRS_span_t* smax; int ybeg, yend; /* Sort by y. */ qsort (__DRS_obuf, __DRS_onum, sizeof (__DRS_rect_t), __DRS_cmpy); /* Wait for vsync if necessary. */ if (__DRS_vsync) vsync (); /* Split on strips in y coordinate. */ yend = 0; while (rbeg < rmax) { rend = rbeg + 1; ybeg = (rbeg->y <= yend) ? yend : rbeg->y; yend = rbeg->y + rbeg->h; if (ybeg >= SCREEN_H) break; /* Limit height of the strip to fit in one bank. */ if (__DRS_bank[ybeg] < yend) yend = __DRS_bank[ybeg]; /* Add more rectangles to the strip. */ while ((rend < rmax) && (rend->y <= ybeg)) { const int y = rend->y + rend->h; if (y < yend) yend = y; rend++; } /* Found rectangle from next strip (limit this strip height). */ if ((rend < rmax) && (rend->y < yend)) yend = rend->y; /* Split on spans in x coordinate. */ smax = __DRS_span; for (rcur = rbeg; rcur < rend; rcur++) { int xbeg = rcur->x; int xend = xbeg + rcur->w; /* Combine current rectangle with other spans. */ for (scur = __DRS_span; scur < smax;) { const int x1 = scur->x1; const int x2 = scur->x2; if ((xbeg > x2) || (xend < x1)) { /* Outside of current span. */ scur++; continue; } /* Covers part of current span (combine and remove old span). */ if (x1 < xbeg) xbeg = x1; if (x2 > xend) xend = x2; *scur = *(--smax); } /* Add new span. */ smax->x1 = xbeg; smax->x2 = xend; smax++; if ((rcur->y + rcur->h) <= yend) { /* Remove rectangle which ends in this strip. */ if (rbeg < rcur) __DRS_swap (rcur, rbeg); rbeg++; } } /* Move spans. */ for (scur = __DRS_span; scur < smax; scur++) blit (DRS_work, screen, scur->x1, ybeg, scur->x1, ybeg, scur->x2 - scur->x1, yend - ybeg); } } /* Clear work->screen list. */ __DRS_onum = 0; } /* * Set double-buffer mode. */ void DRS_use_double_buffer (const int _flag) { if ((__DRS_dbuf != 0) || (_flag != 0)) { /* Trigger full update. */ __DRS_onum = 0; __DRS_nnum = 0; __DRS_full = 1; /* Set double-buffer mode. */ __DRS_dbuf = _flag; } } /* * Set 'wait for vsync' mode. */ void DRS_wait_for_vsync (const int _flag) { __DRS_vsync = _flag; } /* * drs.c ends here */
Generated by: georgik on armada on Sat Jul 24 07:07:15 2004, using kdoc 2.0a54. |