MCUME/MCUME_teensy/teensynofrendo/vid_drv.c

560 wiersze
13 KiB
C

/*
** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
**
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of version 2 of the GNU Library General
** Public License as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Library General Public License for more details. To obtain a
** copy of the GNU Library General Public License, write to the Free
** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** Any permitted reproduction of these routines, in whole or in part,
** must bear this legend.
**
**
** vid_drv.c
**
** Video driver
** $Id: vid_drv.c,v 1.2 2001/04/27 14:37:11 neil Exp $
*/
#include <string.h>
#include "noftypes.h"
#include "log.h"
#include "bitmap.h"
#include "vid_drv.h"
#include "osd.h"
/* hardware surface */
static bitmap_t *screen = NULL;
/* primary / backbuffer surfaces */
static bitmap_t *primary_buffer = NULL; //, *back_buffer = NULL;
static viddriver_t *driver = NULL;
/* fast automagic loop unrolling */
#define DUFFS_DEVICE(transfer, count) \
{ \
register int n = (count + 7) / 8; \
switch (count % 8) \
{ \
case 0: do { { transfer; } \
case 7: { transfer; } \
case 6: { transfer; } \
case 5: { transfer; } \
case 4: { transfer; } \
case 3: { transfer; } \
case 2: { transfer; } \
case 1: { transfer; } \
} while (--n > 0); \
} \
}
/* some system dependent replacement routines (for speed) */
INLINE int vid_memcmp(const void *p1, const void *p2, int len)
{
/* check for 32-bit aligned data */
if (0 == (((uint32) p1 & 3) | ((uint32) p2 & 3)))
{
uint32 *dw1 = (uint32 *) p1;
uint32 *dw2 = (uint32 *) p2;
len >>= 2;
DUFFS_DEVICE(if (*dw1++ != *dw2++) return -1, len);
}
else
/* fall back to 8-bit compares */
{
uint8 *b1 = (uint8 *) p1;
uint8 *b2 = (uint8 *) p2;
DUFFS_DEVICE(if (*b1++ != *b2++) return -1, len);
}
return 0;
}
/* super-dooper assembly memcpy (thanks, SDL!) */
#if defined(__GNUC__) && defined(i386)
#define vid_memcpy(dest, src, len) \
{ \
int u0, u1, u2; \
__asm__ __volatile__ ( \
" cld \n" \
" rep \n" \
" movsl \n" \
" testb $2,%b4 \n" \
" je 1f \n" \
" movsw \n" \
"1: \n" \
" testb $1,%b4 \n" \
" je 2f \n" \
" movsb \n" \
"2: \n" \
: "=&c" (u0), "=&D" (u1), "=&S" (u2) \
: "0" ((len)/4), "q" (len), "1" (dest), "2" (src) \
: "memory"); \
}
#else /* !(defined(__GNUC__) && defined(i386)) */
INLINE void vid_memcpy(void *dest, const void *src, int len)
{
uint32 *s = (uint32 *) src;
uint32 *d = (uint32 *) dest;
ASSERT(0 == ((len & 3) | ((uint32) src & 3) | ((uint32) dest & 3)));
len >>= 2;
DUFFS_DEVICE(*d++ = *s++, len);
}
#endif /* !(defined(__GNUC__) && defined(i386)) */
extern bitmap_t *lock_write(void);
/* TODO: any way to remove this filth (GUI needs it)? */
bitmap_t *vid_getbuffer(void)
{
return primary_buffer;
}
void vid_setpalette(rgb_t *p)
{
ASSERT(driver);
ASSERT(p);
driver->set_palette(p);
}
/* blits a bitmap onto primary buffer */
void vid_blit(bitmap_t *bitmap, int src_x, int src_y, int dest_x, int dest_y,
int width, int height)
{
int bitmap_pitch, primary_pitch;
uint8 *dest_ptr, *src_ptr;
ASSERT(bitmap);
/* clip to source */
if (src_y >= bitmap->height)
return;
if (src_y + height > bitmap->height)
height = bitmap->height - src_y;
if (src_x >= bitmap->width)
return;
if (src_x + width > bitmap->width)
width = bitmap->width - src_x;
/* clip to dest */
if (dest_y + height <= 0)
{
return;
}
else if (dest_y < 0)
{
height += dest_y;
src_y -= dest_y;
dest_y = 0;
}
if (dest_y >= primary_buffer->height)
return;
if (dest_y + height > primary_buffer->height)
height = primary_buffer->height - dest_y;
if (dest_x + width <= 0)
{
return;
}
else if (dest_x < 0)
{
width += dest_x;
src_x -= dest_x;
dest_x = 0;
}
if (dest_x >= primary_buffer->width)
return;
if (dest_x + width > primary_buffer->width)
width = primary_buffer->width - dest_x;
src_ptr = bitmap->line[src_y] + src_x;
dest_ptr = primary_buffer->line[dest_y] + dest_x;
/* Avoid doing unnecessary indexed lookups */
bitmap_pitch = bitmap->pitch;
primary_pitch = primary_buffer->pitch;
/* do the copy */
while (height--)
{
vid_memcpy(dest_ptr, src_ptr, width);
src_ptr += bitmap_pitch;
dest_ptr += primary_pitch;
}
}
static void vid_blitscreen(int num_dirties, rect_t *dirty_rects)
{
int src_x, src_y, dest_x, dest_y;
int blit_width, blit_height;
screen = driver->lock_write();
/* center in y direction */
if (primary_buffer->height <= screen->height)
{
src_y = 0;
blit_height = primary_buffer->height;
dest_y = (screen->height - blit_height) >> 1;
}
else
{
src_y = (primary_buffer->height - screen->height) >> 1;
blit_height = screen->height;
dest_y = 0;
}
/* and in x */
if (primary_buffer->width <= screen->width)
{
src_x = 0;
blit_width = primary_buffer->width;
dest_x = (screen->width - blit_width) >> 1;
}
else
{
src_x = (primary_buffer->width - screen->width) >> 1;
blit_width = screen->width;
dest_x = 0;
}
/* should we just copy the entire screen? */
if (-1 == num_dirties)
{
uint8 *dest, *src;
src = primary_buffer->line[src_y] + src_x;
dest = screen->line[dest_y] + dest_x;
while (blit_height--)
{
vid_memcpy(dest, src, primary_buffer->width);
src += primary_buffer->pitch;
dest += screen->pitch;
}
}
else
{
/* we need to blit just a bunch of dirties */
int i, j, height;
rect_t *rects = dirty_rects;
for (i = 0; i < num_dirties && blit_height; i++)
{
height = rects->h;
if (blit_height < height)
height = blit_height;
j = 0;
DUFFS_DEVICE(
{
vid_memcpy(screen->line[dest_y + rects->y + j] + rects->x + dest_x,
primary_buffer->line[src_y + rects->y + j] + rects->x + src_x,
rects->w);
j++;
blit_height--;
}, height);
rects++;
}
}
if (driver->free_write)
driver->free_write(num_dirties, dirty_rects);
}
/* TODO: this code is sickly */
#define CHUNK_WIDTH 256
#define CHUNK_HEIGHT 16
#define MAX_DIRTIES ((256 / CHUNK_WIDTH) * (240 / CHUNK_HEIGHT))
#define DIRTY_CUTOFF ((3 * MAX_DIRTIES) / 4)
#if 0
INLINE int calc_dirties(rect_t *list)
{
bool dirty;
int num_dirties = 0;
int i = 0, j, line_offset = 0;
int iterations = primary_buffer->height / CHUNK_HEIGHT;
for (i = 0; i < iterations; i++)
{
dirty = false;
j = line_offset;
DUFFS_DEVICE(
{
if (vid_memcmp(back_buffer->line[j], primary_buffer->line[j],
CHUNK_WIDTH))
{
dirty = true;
break;
}
j++;
}, CHUNK_HEIGHT);
if (true == dirty)
{
list->h = CHUNK_HEIGHT;
list->w = CHUNK_WIDTH;
list->x = 0;
list->y = line_offset;
list++;
/* totally arbitrary at this point */
if (++num_dirties > DIRTY_CUTOFF)
return -1;
}
line_offset += CHUNK_HEIGHT;
}
return num_dirties;
}
#endif
void vid_flush(void)
{
bitmap_t *temp;
int num_dirties;
rect_t dirty_rects[MAX_DIRTIES];
ASSERT(driver);
if (true == driver->invalidate)
{
driver->invalidate = false;
num_dirties = -1;
}
else
{
//num_dirties = calc_dirties(dirty_rects);
num_dirties = -1;
}
if (driver->custom_blit)
driver->custom_blit(primary_buffer, num_dirties, dirty_rects);
else
vid_blitscreen(num_dirties, dirty_rects);
/* Swap pointers to the main/back buffers */
// temp = back_buffer;
// back_buffer = primary_buffer;
// primary_buffer = temp;
}
/* emulated machine tells us which resolution it wants */
int vid_setmode(int width, int height)
{
if (NULL != primary_buffer)
bmp_destroy(&primary_buffer);
// if (NULL != back_buffer)
// bmp_destroy(&back_buffer);
primary_buffer = bmp_create(width, height, 0); /* no overdraw */
if (NULL == primary_buffer)
return -1;
return 0;
}
static int vid_findmode(int width, int height, viddriver_t *osd_driver)
{
if (osd_driver->init(width, height))
{
driver = NULL;
return -1; /* mode not available! */
}
/* we got our driver */
driver = osd_driver;
/* re-assert dimensions, clear the surface */
screen = driver->lock_write();
/* release surface */
if (driver->free_write)
driver->free_write(-1, NULL);
log_printf("video driver: %s at %dx%d\n", driver->name,
screen->width, screen->height);
return 0;
}
/* This is the interface to the drivers, used in nofrendo.c */
int vid_init(int width, int height, viddriver_t *osd_driver)
{
if (vid_findmode(width, height, osd_driver))
{
log_printf("video initialization failed for %s at %dx%d\n",
osd_driver->name, width, height);
return -1;
}
log_printf("vid_init done\n");
return 0;
}
void vid_shutdown(void)
{
if (NULL == driver)
return;
if (NULL != primary_buffer)
bmp_destroy(&primary_buffer);
#if 0
if (NULL != back_buffer)
bmp_destroy(&back_buffer);
#endif
if (driver && driver->shutdown)
driver->shutdown();
}
/*
** $Log: vid_drv.c,v $
** Revision 1.2 2001/04/27 14:37:11 neil
** wheeee
**
** Revision 1.1.1.1 2001/04/27 07:03:54 neil
** initial
**
** Revision 1.40 2000/11/25 20:26:42 matt
** not much
**
** Revision 1.39 2000/11/16 14:27:27 matt
** even more crash-proofness
**
** Revision 1.38 2000/11/16 14:11:18 neil
** Better *not* to crash in case of catastrophic failure in the driver
**
** Revision 1.37 2000/11/13 00:55:16 matt
** no dirties seems to be faster (!?!?)
**
** Revision 1.36 2000/11/06 05:16:18 matt
** minor clipping bug
**
** Revision 1.35 2000/11/06 02:16:26 matt
** cleanups
**
** Revision 1.34 2000/11/05 22:53:13 matt
** only one video driver per system, please
**
** Revision 1.33 2000/11/05 16:37:18 matt
** rolled rgb.h into bitmap.h
**
** Revision 1.32 2000/11/05 06:23:41 matt
** thinlib spawns changes
**
** Revision 1.31 2000/10/10 13:58:14 matt
** stroustrup squeezing his way in the door
**
** Revision 1.30 2000/10/10 13:03:53 matt
** Mr. Clean makes a guest appearance
**
** Revision 1.29 2000/10/08 17:58:23 matt
** lock_read() should not allow us to clear the bitmap
**
** Revision 1.28 2000/09/18 02:06:48 matt
** -pedantic is your friend
**
** Revision 1.27 2000/08/16 02:53:05 matt
** changed init() interface a wee bit
**
** Revision 1.26 2000/08/14 02:45:59 matt
** fixed nasty bug in vid_blitscreen
**
** Revision 1.24 2000/08/11 01:44:37 matt
** clipping bugfix
**
** Revision 1.23 2000/07/31 04:28:47 matt
** one million cleanups
**
** Revision 1.22 2000/07/28 07:25:49 neil
** Video driver has an invalidate flag, telling vid_drv not to calculate dirties for the next frame
**
** Revision 1.21 2000/07/28 03:51:45 matt
** lock_read used instead of lock_write in some places
**
** Revision 1.20 2000/07/28 01:24:05 matt
** dirty rectangle support
**
** Revision 1.19 2000/07/27 23:49:52 matt
** no more getmode
**
** Revision 1.18 2000/07/27 04:30:37 matt
** change to get_mode api
**
** Revision 1.17 2000/07/27 04:05:58 matt
** changed place where name goes
**
** Revision 1.16 2000/07/27 01:16:22 matt
** api changes for new main and dirty rects
**
** Revision 1.15 2000/07/26 21:36:13 neil
** Big honkin' change -- see the mailing list
**
** Revision 1.14 2000/07/24 04:33:57 matt
** skeleton of dirty rectangle code in place
**
** Revision 1.13 2000/07/23 14:35:39 matt
** cleanups
**
** Revision 1.12 2000/07/18 19:41:26 neil
** use screen->pitch in blitscreen, not screen_width
**
** Revision 1.11 2000/07/11 04:30:16 matt
** overdraw unnecessary!
**
** Revision 1.10 2000/07/10 19:07:57 matt
** added custom clear() member call to video driver
**
** Revision 1.9 2000/07/10 03:06:49 matt
** my dependency file is broken... *snif*
**
** Revision 1.8 2000/07/10 03:04:15 matt
** removed scanlines, backbuffer from custom blit
**
** Revision 1.7 2000/07/10 01:03:20 neil
** New video scheme allows for custom blitters to be determined by the driver at runtime
**
** Revision 1.6 2000/07/09 03:34:46 matt
** temporary cleanup
**
** Revision 1.5 2000/07/08 23:48:29 neil
** Another assumption GGI kills: pitch == width for hardware bitmaps
**
** Revision 1.4 2000/07/07 20:18:03 matt
** added overdraw, fixed some bugs in blitters
**
** Revision 1.3 2000/07/07 18:33:55 neil
** no need to lock for reading just to get the dimensions
**
** Revision 1.2 2000/07/07 18:11:37 neil
** Generalizing the video driver
**
** Revision 1.1 2000/07/06 16:48:47 matt
** initial revision
**
*/