kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
560 wiersze
13 KiB
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
|
|
**
|
|
*/
|