summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2009-07-13 22:34:43 +0000
committer Aaron Giles <aaron@aarongiles.com>2009-07-13 22:34:43 +0000
commitba6c28021093ff02002fb89cacd26c5d9b3fe111 (patch)
tree7a2aeeba1d9a9f4827a551b0479c0a6a132f1db0
parentba36cda75e4d6d4cd4a9cdda8711af32d05a0f03 (diff)
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.
-rw-r--r--docs/config.txt14
-rw-r--r--src/emu/emuopts.c3
-rw-r--r--src/emu/emuopts.h1
-rw-r--r--src/emu/video.c187
-rw-r--r--src/lib/util/bitmap.c29
-rw-r--r--src/lib/util/bitmap.h2
6 files changed, 232 insertions, 4 deletions
diff --git a/docs/config.txt b/docs/config.txt
index a3ca21a1fac..19614fb24f3 100644
--- a/docs/config.txt
+++ b/docs/config.txt
@@ -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
diff --git a/src/emu/emuopts.c b/src/emu/emuopts.c
index 24d134044dc..90179d8bc1f 100644
--- a/src/emu/emuopts.c
+++ b/src/emu/emuopts.c
@@ -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" },
diff --git a/src/emu/emuopts.h b/src/emu/emuopts.h
index b7f57b5a7b8..03edd5e6824 100644
--- a/src/emu/emuopts.h
+++ b/src/emu/emuopts.h
@@ -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"
diff --git a/src/emu/video.c b/src/emu/video.c
index de27d64c592..9c400875d9c 100644
--- a/src/emu/video.c
+++ b/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 */
@@ -2587,6 +2610,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
***************************************************************************/
diff --git a/src/lib/util/bitmap.c b/src/lib/util/bitmap.c
index 5ffe709654d..32bddd397e8 100644
--- a/src/lib/util/bitmap.c
+++ b/src/lib/util/bitmap.c
@@ -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;
diff --git a/src/lib/util/bitmap.h b/src/lib/util/bitmap.h
index 95c4e4476ef..ea2fd6389d5 100644
--- a/src/lib/util/bitmap.h
+++ b/src/lib/util/bitmap.h
@@ -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)