Added basic support in bitmap.c for 64-bit indexed bitmaps. As a

result, you will need a full recompile with this change, since the
bitmap formats enum was altered.

Introduced new feature -burnin, which can be used to generate a PNG
that represents the overall brightness seen during the course of
running a game. This PNG can be used as a fake "bezel" that overlays
the screen via the artwork systems (with a low alpha, say 0.1 or 0.2)
to simulate running a game with a monitor that has been burned in
from another game.

Fixed the -crsshairpath option to be spelled properly.
This commit is contained in:
Aaron Giles 2009-07-13 22:34:43 +00:00
parent ba36cda75e
commit ba6c280210
6 changed files with 232 additions and 4 deletions

View File

@ -514,9 +514,21 @@ Core state/playback options
-wavwrite <filename>
Writes the final mixer output to the given <filename> in WAV format,
producing an audio recording of the game session. The default is
producing an audio recording of the game session. The default is
NULL (no recording).
-[no]burnin
Tracks brightness of the screen during play and at the end of
emulation generates a PNG that can be used to simulate burn-in
effects on other games. The resulting PNG is created such that the
least used-areas of the screen are fully white (since burned-in areas
are darker, all other areas of the screen must be lightened a touch).
The intention is that this PNG can be loaded via an artwork file with
a low alpha (e.g, 0.1-0.2 seems to work well) and blended over the
entire screen. The PNG files are saved in the snap directory under
the gamename/burnin-<screen.name>.png. The default is OFF (-noburnin).
Core performance options

View File

@ -43,7 +43,7 @@ const options_entry mame_core_options[] =
{ "inipath", ".;ini", 0, "path to ini files" },
{ "fontpath", ".", 0, "path to font files" },
{ "cheatpath", "cheat", 0, "path to cheat files" },
{ "crsshairpath", "crsshair", 0, "path to crosshair files" },
{ "crosshairpath", "crosshair", 0, "path to crosshair files" },
/* output directory options */
{ NULL, NULL, OPTION_HEADER, "CORE OUTPUT DIRECTORY OPTIONS" },
@ -68,6 +68,7 @@ const options_entry mame_core_options[] =
{ "snapname", "%g/%i", 0, "override of the default snapshot/movie naming; %g == gamename, %i == index" },
{ "snapsize", "auto", 0, "specify snapshot/movie resolution (<width>x<height>) or 'auto' to use minimal size " },
{ "snapview", "internal", 0, "specify snapshot/movie view or 'internal' to use internal pixel-aspect views" },
{ "burnin", "0", OPTION_BOOLEAN, "create burn-in snapshots for each screen" },
/* performance options */
{ NULL, NULL, OPTION_HEADER, "CORE PERFORMANCE OPTIONS" },

View File

@ -68,6 +68,7 @@
#define OPTION_SNAPNAME "snapname"
#define OPTION_SNAPSIZE "snapsize"
#define OPTION_SNAPVIEW "snapview"
#define OPTION_BURNIN "burnin"
/* core performance options */
#define OPTION_AUTOFRAMESKIP "autoframeskip"

View File

@ -59,6 +59,7 @@ struct _screen_state
/* textures and bitmaps */
render_texture * texture[2]; /* 2x textures for the screen bitmap */
bitmap_t * bitmap[2]; /* 2x bitmaps for rendering */
bitmap_t * burnin; /* burn-in bitmap */
UINT8 curbitmap; /* current bitmap index */
UINT8 curtexture; /* current texture index */
INT32 texture_format; /* texture format of bitmap for this screen */
@ -203,6 +204,10 @@ static file_error mame_fopen_next(running_machine *machine, const char *pathopti
static void video_mng_record_frame(running_machine *machine);
static void video_avi_record_frame(running_machine *machine);
/* burn-in generation */
static void video_update_burnin(running_machine *machine);
static void video_finalize_burnin(const device_config *screen);
/* software rendering */
static void rgb888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
@ -363,7 +368,7 @@ void video_init(running_machine *machine)
/* extract snap resolution if present */
if (sscanf(options_get_string(mame_options(), OPTION_SNAPSIZE), "%dx%d", &global.snap_width, &global.snap_height) != 2)
global.snap_width = global.snap_height = 0;
/* start recording movie if specified */
filename = options_get_string(mame_options(), OPTION_MNGWRITE);
if (filename[0] != 0)
@ -1197,6 +1202,18 @@ static DEVICE_START( video_screen )
if (screen->machine->config->video_attributes & VIDEO_UPDATE_SCANLINE)
timer_adjust_oneshot(state->scanline_timer, video_screen_get_time_until_pos(screen, 0, 0), 0);
/* create burn-in bitmap */
if (options_get_int(mame_options(), OPTION_BURNIN) > 0)
{
int width, height;
if (sscanf(options_get_string(mame_options(), OPTION_SNAPSIZE), "%dx%d", &width, &height) != 2 || width == 0 || height == 0)
width = height = 300;
state->burnin = bitmap_alloc(width, height, BITMAP_FORMAT_INDEXED64);
if (state->burnin == NULL)
fatalerror("Error allocating burn-in bitmap for screen at (%dx%d)\n", width, height);
bitmap_fill(state->burnin, NULL, 0);
}
state_save_register_device_item(screen, 0, state->width);
state_save_register_device_item(screen, 0, state->height);
state_save_register_device_item(screen, 0, state->visarea.min_x);
@ -1248,6 +1265,11 @@ static DEVICE_STOP( video_screen )
bitmap_free(state->bitmap[0]);
if (state->bitmap[1] != NULL)
bitmap_free(state->bitmap[1]);
if (state->burnin != NULL)
{
video_finalize_burnin(screen);
bitmap_free(state->burnin);
}
}
@ -1523,11 +1545,12 @@ static int finish_screen_updates(running_machine *machine)
state->changed = FALSE;
}
/* update our movie recording state */
/* update our movie recording and burn-in state */
if (!mame_is_paused(machine))
{
video_mng_record_frame(machine);
video_avi_record_frame(machine);
video_update_burnin(machine);
}
/* draw any crosshairs */
@ -2586,6 +2609,166 @@ void video_avi_add_sound(running_machine *machine, const INT16 *sound, int numsa
/***************************************************************************
BURN-IN GENERATION
***************************************************************************/
/*-------------------------------------------------
video_update_burnin - update the burnin bitmap
for all screens
-------------------------------------------------*/
#undef rand
static void video_update_burnin(running_machine *machine)
{
const device_config *screen;
/* iterate over screens and update the burnin for the ones that care */
for (screen = video_screen_first(machine->config); screen != NULL; screen = video_screen_next(screen))
{
screen_state *state = get_safe_token(screen);
if (state->burnin != NULL)
{
bitmap_t *srcbitmap = state->bitmap[state->curtexture];
int srcwidth = srcbitmap->width;
int srcheight = srcbitmap->height;
int dstwidth = state->burnin->width;
int dstheight = state->burnin->height;
int xstep = (srcwidth << 16) / dstwidth;
int ystep = (srcheight << 16) / dstheight;
int xstart = (rand() % 32767) * xstep / 32767;
int ystart = (rand() % 32767) * ystep / 32767;
int srcx, srcy;
int x, y;
/* iterate over rows in the destination */
for (y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep)
{
UINT64 *dst = BITMAP_ADDR64(state->burnin, y, 0);
/* handle the 16-bit palettized case */
if (srcbitmap->format == BITMAP_FORMAT_INDEXED16)
{
const UINT16 *src = BITMAP_ADDR16(srcbitmap, srcy >> 16, 0);
const rgb_t *palette = palette_entry_list_adjusted(machine->palette);
for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep)
{
rgb_t pixel = palette[src[srcx >> 16]];
dst[x] += RGB_GREEN(pixel) + RGB_RED(pixel) + RGB_BLUE(pixel);
}
}
/* handle the 15-bit RGB case */
else if (srcbitmap->format == BITMAP_FORMAT_RGB15)
{
const UINT16 *src = BITMAP_ADDR16(srcbitmap, srcy >> 16, 0);
for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep)
{
rgb15_t pixel = src[srcx >> 16];
dst[x] += ((pixel >> 10) & 0x1f) + ((pixel >> 5) & 0x1f) + ((pixel >> 0) & 0x1f);
}
}
/* handle the 32-bit RGB case */
else if (srcbitmap->format == BITMAP_FORMAT_RGB32)
{
const UINT32 *src = BITMAP_ADDR32(srcbitmap, srcy >> 16, 0);
for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep)
{
rgb_t pixel = src[srcx >> 16];
dst[x] += RGB_GREEN(pixel) + RGB_RED(pixel) + RGB_BLUE(pixel);
}
}
}
}
}
}
/*-------------------------------------------------
video_finalize_burnin - finalize the burnin
bitmaps for all screens
-------------------------------------------------*/
static void video_finalize_burnin(const device_config *screen)
{
screen_state *state = get_safe_token(screen);
if (state->burnin != NULL)
{
astring *fname = astring_alloc();
rectangle scaledvis;
bitmap_t *finalmap;
UINT64 minval = ~(UINT64)0;
UINT64 maxval = 0;
file_error filerr;
mame_file *file;
int x, y;
/* compute the scaled visible region */
scaledvis.min_x = state->visarea.min_x * state->burnin->width / state->width;
scaledvis.max_x = state->visarea.max_x * state->burnin->width / state->width;
scaledvis.min_y = state->visarea.min_y * state->burnin->height / state->height;
scaledvis.max_y = state->visarea.max_y * state->burnin->height / state->height;
/* wrap a bitmap around the subregion we care about */
finalmap = bitmap_alloc(scaledvis.max_x + 1 - scaledvis.min_x,
scaledvis.max_y + 1 - scaledvis.min_y,
BITMAP_FORMAT_ARGB32);
/* find the maximum value */
for (y = 0; y < finalmap->height; y++)
{
UINT64 *src = BITMAP_ADDR64(state->burnin, y, 0);
for (x = 0; x < finalmap->width; x++)
{
minval = MIN(minval, src[x]);
maxval = MAX(maxval, src[x]);
}
}
/* now normalize and convert to RGB */
for (y = 0; y < finalmap->height; y++)
{
UINT64 *src = BITMAP_ADDR64(state->burnin, y, 0);
UINT32 *dst = BITMAP_ADDR32(finalmap, y, 0);
for (x = 0; x < finalmap->width; x++)
{
int brightness = (UINT64)(maxval - src[x]) * 255 / (maxval - minval);
dst[x] = MAKE_ARGB(0xff, brightness, brightness, brightness);
}
}
/* write the final PNG */
/* compute the name and create the file */
astring_printf(fname, "%s" PATH_SEPARATOR "burnin-%s.png", screen->machine->basename, screen->tag);
filerr = mame_fopen(SEARCHPATH_SCREENSHOT, astring_c(fname), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file);
if (filerr == FILERR_NONE)
{
png_info pnginfo = { 0 };
png_error pngerr;
char text[256];
/* add two text entries describing the image */
sprintf(text, APPNAME " %s", build_version);
png_add_text(&pnginfo, "Software", text);
sprintf(text, "%s %s", screen->machine->gamedrv->manufacturer, screen->machine->gamedrv->description);
png_add_text(&pnginfo, "System", text);
/* now do the actual work */
pngerr = png_write_bitmap(mame_core_file(file), &pnginfo, finalmap, 0, NULL);
/* free any data allocated */
png_free(&pnginfo);
mame_fclose(file);
}
astring_free(fname);
bitmap_free(finalmap);
}
}
/***************************************************************************
CONFIGURATION HELPERS
***************************************************************************/

View File

@ -243,6 +243,32 @@ void bitmap_fill(bitmap_t *dest, const rectangle *cliprect, UINT32 color)
}
}
break;
case 64:
/* 64bpp can use memset if the bytes are equal */
if ((UINT8)(color >> 8) == (UINT8)color && (UINT16)(color >> 16) == (UINT16)color)
{
for (y = fill.min_y; y <= fill.max_y; y++)
memset(BITMAP_ADDR64(dest, y, fill.min_x), (UINT8)color, (fill.max_x + 1 - fill.min_x) * 4);
}
else
{
UINT64 *destrow, *destrow0;
/* Fill the first line the hard way */
destrow = BITMAP_ADDR64(dest, fill.min_y, 0);
for (x = fill.min_x; x <= fill.max_x; x++)
destrow[x] = (UINT64)color;
/* For the other lines, just copy the first one */
destrow0 = BITMAP_ADDR64(dest, fill.min_y, fill.min_x);
for (y = fill.min_y + 1; y <= fill.max_y; y++)
{
destrow = BITMAP_ADDR64(dest, y, fill.min_x);
memcpy(destrow, destrow0, (fill.max_x + 1 - fill.min_x) * 4);
}
}
break;
}
}
@ -274,6 +300,9 @@ int bitmap_format_to_bpp(bitmap_format format)
case BITMAP_FORMAT_RGB32:
case BITMAP_FORMAT_ARGB32:
return 32;
case BITMAP_FORMAT_INDEXED64:
return 64;
default:
break;

View File

@ -29,6 +29,7 @@ enum _bitmap_format
BITMAP_FORMAT_INDEXED8, /* 8bpp indexed */
BITMAP_FORMAT_INDEXED16, /* 16bpp indexed */
BITMAP_FORMAT_INDEXED32, /* 32bpp indexed */
BITMAP_FORMAT_INDEXED64, /* 64bpp indexed */
BITMAP_FORMAT_RGB15, /* 15bpp 5-5-5 RGB */
BITMAP_FORMAT_RGB32, /* 32bpp 8-8-8 RGB */
BITMAP_FORMAT_ARGB32, /* 32bpp 8-8-8-8 ARGB */
@ -77,6 +78,7 @@ struct _bitmap_t
#define BITMAP_ADDR8(bitmap, y, x) BITMAP_ADDR(bitmap, UINT8, y, x)
#define BITMAP_ADDR16(bitmap, y, x) BITMAP_ADDR(bitmap, UINT16, y, x)
#define BITMAP_ADDR32(bitmap, y, x) BITMAP_ADDR(bitmap, UINT32, y, x)
#define BITMAP_ADDR64(bitmap, y, x) BITMAP_ADDR(bitmap, UINT64, y, x)