Sam Lantinga wrote:
Anyway, in doom, you could only have ONE dirty rectangle [0,0,320,200]
You’d still have tearing because the copy wouldn’t be synchronized with
the vertical retrace.
My dirty rect. routines are supposed to optimize the rectangle list before it is
copied, so (almost) nothing is copied twice or more…
I’ll upload it somewhere soon if anyone wants to see it…
Cool.
See ya!
-Sam Lantinga (slouken at devolution.com)
here it is (I don’t really think it’s worth the bandwidth…)
–8<–cut to the end of the message–8<–
/*
! not tested yet !
( I don’t have anything to make the screen dirty with yet )
anyone to do this in 3d ?? :-)))
*/
#include <stdlib.h> // NULL
#define X1 0
#define Y1 1
#define X2 2
#define Y2 3
typedef struct DirtyRect
{
int x1;
int y1;
int x2;
int y2;
struct DirtyRect
*prev;
struct DirtyRect
*next;
} DirtyRect;
enum
{
lineclip_none_left,
lineclip_none_right,
lineclip_left,
lineclip_right,
lineclip_inside, // b contains a
lineclip_outside // a contains b
};
enum
{
lineclip_none_up,
lineclip_none_down,
lineclip_up,
lineclip_down
/* … */
};
static DirtyRect
*allocated = NULL; // array, not a linked list here
static int
default_allocate = 2000,
allocated_count = 0;
static DirtyRect
*free_rect = NULL;
void dirty_init()
{
int
i;
allocated = ( DirtyRect* ) malloc( sizeof( DirtyRect ) * default_allocate );
allocated_count = default_allocate;
if ( allocated_count==0 )
return;
// how silly...
free_rect = allocated;
allocated[0].next = &allocated[1];
allocated[0].prev = NULL;
for ( i=1; i<allocated_count - 1; i++ )
{
allocated[i].next = &allocated[i+1];
allocated[i].prev = &allocated[i-1];
}
allocated[allocated_count].prev = &allocated[allocated_count-1]; //
allocated_count-1 == i
allocated[allocated_count].next = NULL;
}
void dirty_exit()
{
free( ( void* ) allocated );
free_rect = NULL;
allocated_count = 0;
}
DirtyRect *dirty_new()
{
DirtyRect
* d;
if ( free_rect==NULL )
return NULL;
// FIXME allocate more (and transfer all
// older dirty rects into the newly allocated area)
else
{
d = free_rect;
free_rect = free_rect->next;
free_rect->prev = NULL;
d->next = NULL;
return d;
}
}
void dirty_free( DirtyRect *d )
{
d->next = free_rect;
free_rect->prev = d;
d->prev = NULL;
free_rect = d;
}
static int lineclip( int a1, int a2, int b1, int b2 )
/* a1 >= a2
b1 >= b2 */
{
if ( a1 < b1 )
{
if ( a2 < b1 )
return lineclip_none_left;
else
{
if ( a2 < b2 )
return lineclip_left;
else
return lineclip_outside;
}
}
else
{
if ( a1 < b2 )
{
if ( a2 < b2 )
return lineclip_inside;
else
return lineclip_right;
} else
return lineclip_none_right;
}
}
/* this is horrible but it should work /
/ it’s not pixel perfect yet (some overlaps remain) */
void dirty_optimize( DirtyRect *d, DirtyRect *dlist )
{
DirtyRect
*d2, *diter;
diter = dlist;
while ( ( d != NULL ) && ( diter != NULL ) )
{
switch ( lineclip( d->x1, d->x2, diter->x1, diter->x2 ) )
{
case lineclip_none_left:
case lineclip_none_right:
// no contact, quit
break;
case lineclip_inside:
switch ( lineclip( d->y1, d->y2, diter->y1, diter->y2 ) )
{
case lineclip_none_up:
case lineclip_none_down:
// no contact, quit
break;
case lineclip_inside:
d = NULL;
break;
case lineclip_outside:
d2 = dirty_new();
d2->x1 = d->x1;
d2->x2 = d->x2;
d2->y1 = d->y1;
d2->y2 = diter->y1;
d->y1 = diter->y2;
dirty_optimize( d2, diter );// yes, start at diter
// and go on with d now
break;
case lineclip_up:
d->y2 = diter->y1;
break;
case lineclip_down:
d->y1 = diter->y2;
break;
}
break;
case lineclip_outside:
switch ( lineclip( d->y1, d->y2, diter->y1, diter->y2 ) )
{
case lineclip_none_up:
case lineclip_none_down:
// no contact, quit
break;
case lineclip_inside:
d2 = dirty_new();
d2->x1 = d->x1;
d2->x2 = diter->x1;
d2->y1 = d->y1;
d2->y2 = d->y2;
dirty_optimize( d2, diter ); // yes, start at diter
// and go on with d now
break;
case lineclip_outside:
if ( diter->next != NULL )
diter->next->prev = diter->prev;
if ( diter->prev != NULL )
diter->prev->next = diter->next;
break;
case lineclip_up:
diter->y1 = d->y2;
break;
case lineclip_down:
diter->y2 = d->y1;
break;
}
break;
case lineclip_left:
switch ( lineclip( d->y1, d->y2, diter->y1, diter->y2 ) )
{
case lineclip_none_left:
case lineclip_none_right:
// no contact, quit
break;
case lineclip_inside:
d->x2 = diter->x1;
break;
case lineclip_outside:
diter->x1 = d->x2;
break;
case lineclip_up:
// two equal ways how to split the shit
d2 = dirty_new();
d2->x1 = d->x1;
d2->x2 = diter->x1;
d2->y1 = d->y1;
d2->y2 = d->y2;
d->x1 = diter->x1;
d->y2 = diter->y1;
dirty_optimize( d2, diter ); // yes, start at diter
// and go on with d now
break;
case lineclip_down:
// two equal ways how to split the shit
d2 = dirty_new();
d2->x1 = d->x1;
d2->x2 = diter->x1;
d2->y1 = d->y1;
d2->y2 = d->y2;
d->x1 = diter->x1;
d->y1 = diter->y2;
dirty_optimize( d2, diter ); // yes, start at diter
// and go on with d now
break;
}
break;
case lineclip_right:
switch ( lineclip( d->y1, d->y2, diter->y1, diter->y2 ) )
{
case lineclip_none_left:
case lineclip_none_right:
// no contact, quit
break;
case lineclip_inside:
d->x1 = diter->x2;
// FIXME
break;
case lineclip_outside:
diter->x2 = d->x1;
// FIXME
break;
case lineclip_up:
// FIXME
break;
case lineclip_down:
// FIXME
break;
}
break;
}
diter = diter->next;
}
}