Source: src/ex_drs.cc


Annotated List
Files
Globals
Hierarchy
Index
/*
 * 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.