mirror of
https://github.com/holub/mame
synced 2025-06-04 03:46:29 +03:00
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:
parent
ba36cda75e
commit
ba6c280210
@ -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
|
||||
|
@ -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" },
|
||||
|
@ -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"
|
||||
|
187
src/emu/video.c
187
src/emu/video.c
@ -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
|
||||
***************************************************************************/
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user