summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/uigfx.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/uigfx.c')
-rw-r--r--trunk/src/emu/uigfx.c1076
1 files changed, 1076 insertions, 0 deletions
diff --git a/trunk/src/emu/uigfx.c b/trunk/src/emu/uigfx.c
new file mode 100644
index 00000000000..baeaecdd814
--- /dev/null
+++ b/trunk/src/emu/uigfx.c
@@ -0,0 +1,1076 @@
+/*********************************************************************
+
+ uigfx.c
+
+ Internal graphics viewer.
+
+ Copyright Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+*********************************************************************/
+
+#include "emu.h"
+#include "ui.h"
+#include "uiinput.h"
+#include "render.h"
+#include "rendfont.h"
+#include "rendutil.h"
+#include "uigfx.h"
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _ui_gfx_state ui_gfx_state;
+struct _ui_gfx_state
+{
+ UINT8 mode; /* which mode are we in? */
+
+ /* intermediate bitmaps */
+ UINT8 bitmap_dirty; /* is the bitmap dirty? */
+ bitmap_t * bitmap; /* bitmap for drawing gfx and tilemaps */
+ render_texture *texture; /* texture for rendering the above bitmap */
+
+ /* palette-specific data */
+ struct
+ {
+ int which; /* which subset (palette or colortable)? */
+ int offset; /* current offset of top,left item */
+ int count; /* number of items per row */
+ } palette;
+
+ /* graphics-viewer-specific data */
+ struct
+ {
+ int set; /* which set is visible */
+ int offset[MAX_GFX_ELEMENTS]; /* current offset of top,left item */
+ int color[MAX_GFX_ELEMENTS]; /* current color selected */
+ int count[MAX_GFX_ELEMENTS]; /* number of items per row */
+ UINT8 rotate[MAX_GFX_ELEMENTS]; /* current rotation (orientation) value */
+ } gfxset;
+
+ /* tilemap-viewer-specific data */
+ struct
+ {
+ int which; /* which tilemap are we viewing? */
+ int xoffs; /* current X offset */
+ int yoffs; /* current Y offset */
+ int zoom; /* zoom factor */
+ UINT8 rotate; /* current rotation (orientation) value */
+ } tilemap;
+};
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+static ui_gfx_state ui_gfx;
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void ui_gfx_exit(running_machine &machine);
+
+/* palette handling */
+static void palette_handle_keys(running_machine &machine, ui_gfx_state *state);
+static void palette_handler(running_machine &machine, render_container *container, ui_gfx_state *state);
+
+/* graphics set handling */
+static void gfxset_handle_keys(running_machine &machine, ui_gfx_state *state, int xcells, int ycells);
+static void gfxset_draw_item(running_machine &machine, const gfx_element *gfx, int index, bitmap_t *bitmap, int dstx, int dsty, int color, int rotate);
+static void gfxset_update_bitmap(running_machine &machine, ui_gfx_state *state, int xcells, int ycells, gfx_element *gfx);
+static void gfxset_handler(running_machine &machine, render_container *container, ui_gfx_state *state);
+
+/* tilemap handling */
+static void tilemap_handle_keys(running_machine &machine, ui_gfx_state *state, int viswidth, int visheight);
+static void tilemap_update_bitmap(running_machine &machine, ui_gfx_state *state, int width, int height);
+static void tilemap_handler(running_machine &machine, render_container *container, ui_gfx_state *state);
+
+
+
+/***************************************************************************
+ CORE IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ ui_gfx_init - initialize the menu system
+-------------------------------------------------*/
+
+void ui_gfx_init(running_machine &machine)
+{
+ ui_gfx_state *state = &ui_gfx;
+ int gfx;
+
+ /* make sure we clean up after ourselves */
+ machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(ui_gfx_exit), &machine));
+
+ /* initialize our global state */
+ memset(state, 0, sizeof(*state));
+
+ /* set up the palette state */
+ state->palette.count = 16;
+
+ /* set up the graphics state */
+ for (gfx = 0; gfx < MAX_GFX_ELEMENTS; gfx++)
+ {
+ state->gfxset.rotate[gfx] = machine.system().flags & ORIENTATION_MASK;
+ state->gfxset.count[gfx] = 16;
+ }
+
+ /* set up the tilemap state */
+ state->tilemap.rotate = machine.system().flags & ORIENTATION_MASK;
+}
+
+
+/*-------------------------------------------------
+ ui_gfx_exit - clean up after ourselves
+-------------------------------------------------*/
+
+static void ui_gfx_exit(running_machine &machine)
+{
+ /* free the texture */
+ machine.render().texture_free(ui_gfx.texture);
+ ui_gfx.texture = NULL;
+
+ /* free the bitmap */
+ global_free(ui_gfx.bitmap);
+ ui_gfx.bitmap = NULL;
+}
+
+
+/*-------------------------------------------------
+ ui_gfx_ui_handler - primary UI handler
+-------------------------------------------------*/
+
+UINT32 ui_gfx_ui_handler(running_machine &machine, render_container *container, UINT32 uistate)
+{
+ ui_gfx_state *state = &ui_gfx;
+
+ /* if we have nothing, implicitly cancel */
+ if (machine.total_colors() == 0 && machine.colortable == NULL && machine.gfx[0] == NULL && tilemap_count(machine) == 0)
+ goto cancel;
+
+ /* if we're not paused, mark the bitmap dirty */
+ if (!machine.paused())
+ state->bitmap_dirty = TRUE;
+
+ /* switch off the state to display something */
+again:
+ switch (state->mode)
+ {
+ case 0:
+ /* if we have a palette, display it */
+ if (machine.total_colors() > 0)
+ {
+ palette_handler(machine, container, state);
+ break;
+ }
+
+ /* fall through...*/
+ state->mode++;
+
+ case 1:
+ /* if we have graphics sets, display them */
+ if (machine.gfx[0] != NULL)
+ {
+ gfxset_handler(machine, container, state);
+ break;
+ }
+
+ /* fall through...*/
+ state->mode++;
+
+ case 2:
+ /* if we have tilemaps, display them */
+ if (tilemap_count(machine) > 0)
+ {
+ tilemap_handler(machine, container, state);
+ break;
+ }
+
+ state->mode = 0;
+ goto again;
+ }
+
+ /* handle keys */
+ if (ui_input_pressed(machine, IPT_UI_SELECT))
+ {
+ state->mode = (state->mode + 1) % 3;
+ state->bitmap_dirty = TRUE;
+ }
+
+ if (ui_input_pressed(machine, IPT_UI_PAUSE))
+ {
+ if (machine.paused())
+ machine.resume();
+ else
+ machine.pause();
+ }
+
+ if (ui_input_pressed(machine, IPT_UI_CANCEL) || ui_input_pressed(machine, IPT_UI_SHOW_GFX))
+ goto cancel;
+
+ return uistate;
+
+cancel:
+ if (!uistate)
+ machine.resume();
+ state->bitmap_dirty = TRUE;
+ return UI_HANDLER_CANCEL;
+}
+
+
+
+/***************************************************************************
+ PALETTE VIEWER
+***************************************************************************/
+
+/*-------------------------------------------------
+ palette_handler - handler for the palette
+ viewer
+-------------------------------------------------*/
+
+static void palette_handler(running_machine &machine, render_container *container, ui_gfx_state *state)
+{
+ int total = state->palette.which ? colortable_palette_get_size(machine.colortable) : machine.total_colors();
+ const char *title = state->palette.which ? "COLORTABLE" : "PALETTE";
+ const rgb_t *raw_color = palette_entry_list_raw(machine.palette);
+ render_font *ui_font = ui_get_font(machine);
+ float cellwidth, cellheight;
+ float chwidth, chheight;
+ float titlewidth;
+ float x0, y0;
+ render_bounds cellboxbounds;
+ render_bounds boxbounds;
+ int x, y, skip;
+
+ /* add a half character padding for the box */
+ chheight = ui_get_line_height(machine);
+ chwidth = ui_font->char_width(chheight, machine.render().ui_aspect(), '0');
+ boxbounds.x0 = 0.0f + 0.5f * chwidth;
+ boxbounds.x1 = 1.0f - 0.5f * chwidth;
+ boxbounds.y0 = 0.0f + 0.5f * chheight;
+ boxbounds.y1 = 1.0f - 0.5f * chheight;
+
+ /* the character cell box bounds starts a half character in from the box */
+ cellboxbounds = boxbounds;
+ cellboxbounds.x0 += 0.5f * chwidth;
+ cellboxbounds.x1 -= 0.5f * chwidth;
+ cellboxbounds.y0 += 0.5f * chheight;
+ cellboxbounds.y1 -= 0.5f * chheight;
+
+ /* add space on the left for 5 characters of text, plus a half character of padding */
+ cellboxbounds.x0 += 5.5f * chwidth;
+
+ /* add space on the top for a title, a half line of padding, a header, and another half line */
+ cellboxbounds.y0 += 3.0f * chheight;
+
+ /* figure out the title and expand the outer box to fit */
+ titlewidth = ui_font->string_width(chheight, machine.render().ui_aspect(), title);
+ x0 = 0.0f;
+ if (boxbounds.x1 - boxbounds.x0 < titlewidth + chwidth)
+ x0 = boxbounds.x0 - (0.5f - 0.5f * (titlewidth + chwidth));
+
+ /* go ahead and draw the outer box now */
+ ui_draw_outlined_box(container, boxbounds.x0 - x0, boxbounds.y0, boxbounds.x1 + x0, boxbounds.y1, UI_GFXVIEWER_BG_COLOR);
+
+ /* draw the title */
+ x0 = 0.5f - 0.5f * titlewidth;
+ y0 = boxbounds.y0 + 0.5f * chheight;
+ for (x = 0; title[x] != 0; x++)
+ {
+ container->add_char(x0, y0, chheight, machine.render().ui_aspect(), ARGB_WHITE, *ui_font, title[x]);
+ x0 += ui_font->char_width(chheight, machine.render().ui_aspect(), title[x]);
+ }
+
+ /* compute the cell size */
+ cellwidth = (cellboxbounds.x1 - cellboxbounds.x0) / (float)state->palette.count;
+ cellheight = (cellboxbounds.y1 - cellboxbounds.y0) / (float)state->palette.count;
+
+ /* draw the top column headers */
+ skip = (int)(chwidth / cellwidth);
+ for (x = 0; x < state->palette.count; x += 1 + skip)
+ {
+ x0 = boxbounds.x0 + 6.0f * chwidth + (float)x * cellwidth;
+ y0 = boxbounds.y0 + 2.0f * chheight;
+ container->add_char(x0 + 0.5f * (cellwidth - chwidth), y0, chheight, machine.render().ui_aspect(), ARGB_WHITE, *ui_font, "0123456789ABCDEF"[x & 0xf]);
+
+ /* if we're skipping, draw a point between the character and the box to indicate which */
+ /* one it's referring to */
+ if (skip != 0)
+ container->add_point(x0 + 0.5f * cellwidth, 0.5f * (y0 + chheight + cellboxbounds.y0), UI_LINE_WIDTH, ARGB_WHITE, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+
+ /* draw the side column headers */
+ skip = (int)(chheight / cellheight);
+ for (y = 0; y < state->palette.count; y += 1 + skip)
+
+ /* only display if there is data to show */
+ if (state->palette.offset + y * state->palette.count < total)
+ {
+ char buffer[10];
+
+ /* if we're skipping, draw a point between the character and the box to indicate which */
+ /* one it's referring to */
+ x0 = boxbounds.x0 + 5.5f * chwidth;
+ y0 = boxbounds.y0 + 3.5f * chheight + (float)y * cellheight;
+ if (skip != 0)
+ container->add_point(0.5f * (x0 + cellboxbounds.x0), y0 + 0.5f * cellheight, UI_LINE_WIDTH, ARGB_WHITE, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ /* draw the row header */
+ sprintf(buffer, "%5X", state->palette.offset + y * state->palette.count);
+ for (x = 4; x >= 0; x--)
+ {
+ x0 -= ui_font->char_width(chheight, machine.render().ui_aspect(), buffer[x]);
+ container->add_char(x0, y0 + 0.5f * (cellheight - chheight), chheight, machine.render().ui_aspect(), ARGB_WHITE, *ui_font, buffer[x]);
+ }
+ }
+
+ /* now add the rectangles for the colors */
+ for (y = 0; y < state->palette.count; y++)
+ for (x = 0; x < state->palette.count; x++)
+ {
+ int index = state->palette.offset + y * state->palette.count + x;
+ if (index < total)
+ {
+ pen_t pen = state->palette.which ? colortable_palette_get_color(machine.colortable, index) : raw_color[index];
+ container->add_rect(cellboxbounds.x0 + x * cellwidth, cellboxbounds.y0 + y * cellheight,
+ cellboxbounds.x0 + (x + 1) * cellwidth, cellboxbounds.y0 + (y + 1) * cellheight,
+ 0xff000000 | pen, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+ }
+
+ /* handle keys */
+ palette_handle_keys(machine, state);
+}
+
+
+/*-------------------------------------------------
+ palette_handler - handle key inputs for the
+ palette viewer
+-------------------------------------------------*/
+
+static void palette_handle_keys(running_machine &machine, ui_gfx_state *state)
+{
+ int rowcount, screencount;
+ int total;
+
+ /* handle zoom (minus,plus) */
+ if (ui_input_pressed(machine, IPT_UI_ZOOM_OUT))
+ state->palette.count /= 2;
+ if (ui_input_pressed(machine, IPT_UI_ZOOM_IN))
+ state->palette.count *= 2;
+
+ /* clamp within range */
+ if (state->palette.count <= 4)
+ state->palette.count = 4;
+ if (state->palette.count > 64)
+ state->palette.count = 64;
+
+ /* handle colormap selection (open bracket,close bracket) */
+ if (ui_input_pressed(machine, IPT_UI_PREV_GROUP))
+ state->palette.which--;
+ if (ui_input_pressed(machine, IPT_UI_NEXT_GROUP))
+ state->palette.which++;
+
+ /* clamp within range */
+ if (state->palette.which < 0)
+ state->palette.which = 1;
+ if (state->palette.which > (int)(machine.colortable != NULL))
+ state->palette.which = (int)(machine.colortable != NULL);
+
+ /* cache some info in locals */
+ total = state->palette.which ? colortable_palette_get_size(machine.colortable) : machine.total_colors();
+
+ /* determine number of entries per row and total */
+ rowcount = state->palette.count;
+ screencount = rowcount * rowcount;
+
+ /* handle keyboard navigation */
+ if (ui_input_pressed_repeat(machine, IPT_UI_UP, 4))
+ state->palette.offset -= rowcount;
+ if (ui_input_pressed_repeat(machine, IPT_UI_DOWN, 4))
+ state->palette.offset += rowcount;
+ if (ui_input_pressed_repeat(machine, IPT_UI_PAGE_UP, 6))
+ state->palette.offset -= screencount;
+ if (ui_input_pressed_repeat(machine, IPT_UI_PAGE_DOWN, 6))
+ state->palette.offset += screencount;
+ if (ui_input_pressed_repeat(machine, IPT_UI_HOME, 4))
+ state->palette.offset = 0;
+ if (ui_input_pressed_repeat(machine, IPT_UI_END, 4))
+ state->palette.offset = total;
+
+ /* clamp within range */
+ if (state->palette.offset + screencount > ((total + rowcount - 1) / rowcount) * rowcount)
+ state->palette.offset = ((total + rowcount - 1) / rowcount) * rowcount - screencount;
+ if (state->palette.offset < 0)
+ state->palette.offset = 0;
+}
+
+
+
+/***************************************************************************
+ GRAPHICS VIEWER
+***************************************************************************/
+
+/*-------------------------------------------------
+ gfxset_handler - handler for the graphics
+ viewer
+-------------------------------------------------*/
+
+static void gfxset_handler(running_machine &machine, render_container *container, ui_gfx_state *state)
+{
+ render_font *ui_font = ui_get_font(machine);
+ int set = state->gfxset.set;
+ gfx_element *gfx = machine.gfx[set];
+ float fullwidth, fullheight;
+ float cellwidth, cellheight;
+ float chwidth, chheight;
+ float titlewidth;
+ //float cellaspect;
+ float x0, y0;
+ render_bounds cellboxbounds;
+ render_bounds boxbounds;
+ int cellboxwidth, cellboxheight;
+ int targwidth = machine.render().ui_target().width();
+ int targheight = machine.render().ui_target().height();
+ int cellxpix, cellypix;
+ int xcells, ycells;
+ int pixelscale = 0;
+ int x, y, skip;
+ char title[100];
+
+ /* add a half character padding for the box */
+ chheight = ui_get_line_height(machine);
+ chwidth = ui_font->char_width(chheight, machine.render().ui_aspect(), '0');
+ boxbounds.x0 = 0.0f + 0.5f * chwidth;
+ boxbounds.x1 = 1.0f - 0.5f * chwidth;
+ boxbounds.y0 = 0.0f + 0.5f * chheight;
+ boxbounds.y1 = 1.0f - 0.5f * chheight;
+
+ /* the character cell box bounds starts a half character in from the box */
+ cellboxbounds = boxbounds;
+ cellboxbounds.x0 += 0.5f * chwidth;
+ cellboxbounds.x1 -= 0.5f * chwidth;
+ cellboxbounds.y0 += 0.5f * chheight;
+ cellboxbounds.y1 -= 0.5f * chheight;
+
+ /* add space on the left for 5 characters of text, plus a half character of padding */
+ cellboxbounds.x0 += 5.5f * chwidth;
+
+ /* add space on the top for a title, a half line of padding, a header, and another half line */
+ cellboxbounds.y0 += 3.0f * chheight;
+
+ /* convert back to pixels */
+ cellboxwidth = (cellboxbounds.x1 - cellboxbounds.x0) * (float)targwidth;
+ cellboxheight = (cellboxbounds.y1 - cellboxbounds.y0) * (float)targheight;
+
+ /* compute the number of source pixels in a cell */
+ cellxpix = 1 + ((state->gfxset.rotate[state->gfxset.set] & ORIENTATION_SWAP_XY) ? gfx->height : gfx->width);
+ cellypix = 1 + ((state->gfxset.rotate[state->gfxset.set] & ORIENTATION_SWAP_XY) ? gfx->width : gfx->height);
+
+ /* compute the largest pixel scale factor that still fits */
+ xcells = state->gfxset.count[set];
+ while (xcells > 1)
+ {
+ pixelscale = (cellboxwidth / xcells) / cellxpix;
+ if (pixelscale != 0)
+ break;
+ xcells--;
+ }
+
+ /* worst case, we need a pixel scale of 1 */
+ pixelscale = MAX(1, pixelscale);
+
+ /* in the Y direction, we just display as many as we can */
+ ycells = cellboxheight / (pixelscale * cellypix);
+
+ /* now determine the actual cellbox size */
+ cellboxwidth = MIN(cellboxwidth, xcells * pixelscale * cellxpix);
+ cellboxheight = MIN(cellboxheight, ycells * pixelscale * cellypix);
+
+ /* compute the size of a single cell at this pixel scale factor, as well as the aspect ratio */
+ cellwidth = (cellboxwidth / (float)xcells) / (float)targwidth;
+ cellheight = (cellboxheight / (float)ycells) / (float)targheight;
+ //cellaspect = cellwidth / cellheight;
+
+ /* working from the new width/height, recompute the boxbounds */
+ fullwidth = (float)cellboxwidth / (float)targwidth + 6.5f * chwidth;
+ fullheight = (float)cellboxheight / (float)targheight + 4.0f * chheight;
+
+ /* recompute boxbounds from this */
+ boxbounds.x0 = (1.0f - fullwidth) * 0.5f;
+ boxbounds.x1 = boxbounds.x0 + fullwidth;
+ boxbounds.y0 = (1.0f - fullheight) * 0.5f;
+ boxbounds.y1 = boxbounds.y0 + fullheight;
+
+ /* figure out the title and expand the outer box to fit */
+ for (x = 0; x < MAX_GFX_ELEMENTS && machine.gfx[x] != NULL; x++) ;
+ sprintf(title, "GFX %d/%d %dx%d COLOR %X", state->gfxset.set, x - 1, gfx->width, gfx->height, state->gfxset.color[set]);
+ titlewidth = ui_font->string_width(chheight, machine.render().ui_aspect(), title);
+ x0 = 0.0f;
+ if (boxbounds.x1 - boxbounds.x0 < titlewidth + chwidth)
+ x0 = boxbounds.x0 - (0.5f - 0.5f * (titlewidth + chwidth));
+
+ /* go ahead and draw the outer box now */
+ ui_draw_outlined_box(container, boxbounds.x0 - x0, boxbounds.y0, boxbounds.x1 + x0, boxbounds.y1, UI_GFXVIEWER_BG_COLOR);
+
+ /* draw the title */
+ x0 = 0.5f - 0.5f * titlewidth;
+ y0 = boxbounds.y0 + 0.5f * chheight;
+ for (x = 0; title[x] != 0; x++)
+ {
+ container->add_char(x0, y0, chheight, machine.render().ui_aspect(), ARGB_WHITE, *ui_font, title[x]);
+ x0 += ui_font->char_width(chheight, machine.render().ui_aspect(), title[x]);
+ }
+
+ /* draw the top column headers */
+ skip = (int)(chwidth / cellwidth);
+ for (x = 0; x < xcells; x += 1 + skip)
+ {
+ x0 = boxbounds.x0 + 6.0f * chwidth + (float)x * cellwidth;
+ y0 = boxbounds.y0 + 2.0f * chheight;
+ container->add_char(x0 + 0.5f * (cellwidth - chwidth), y0, chheight, machine.render().ui_aspect(), ARGB_WHITE, *ui_font, "0123456789ABCDEF"[x & 0xf]);
+
+ /* if we're skipping, draw a point between the character and the box to indicate which */
+ /* one it's referring to */
+ if (skip != 0)
+ container->add_point(x0 + 0.5f * cellwidth, 0.5f * (y0 + chheight + boxbounds.y0 + 3.5f * chheight), UI_LINE_WIDTH, ARGB_WHITE, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+
+ /* draw the side column headers */
+ skip = (int)(chheight / cellheight);
+ for (y = 0; y < ycells; y += 1 + skip)
+
+ /* only display if there is data to show */
+ if (state->gfxset.offset[set] + y * xcells < gfx->total_elements)
+ {
+ char buffer[10];
+
+ /* if we're skipping, draw a point between the character and the box to indicate which */
+ /* one it's referring to */
+ x0 = boxbounds.x0 + 5.5f * chwidth;
+ y0 = boxbounds.y0 + 3.5f * chheight + (float)y * cellheight;
+ if (skip != 0)
+ container->add_point(0.5f * (x0 + boxbounds.x0 + 6.0f * chwidth), y0 + 0.5f * cellheight, UI_LINE_WIDTH, ARGB_WHITE, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ /* draw the row header */
+ sprintf(buffer, "%5X", state->gfxset.offset[set] + y * xcells);
+ for (x = 4; x >= 0; x--)
+ {
+ x0 -= ui_font->char_width(chheight, machine.render().ui_aspect(), buffer[x]);
+ container->add_char(x0, y0 + 0.5f * (cellheight - chheight), chheight, machine.render().ui_aspect(), ARGB_WHITE, *ui_font, buffer[x]);
+ }
+ }
+
+ /* update the bitmap */
+ gfxset_update_bitmap(machine, state, xcells, ycells, gfx);
+
+ /* add the final quad */
+ container->add_quad(boxbounds.x0 + 6.0f * chwidth, boxbounds.y0 + 3.5f * chheight,
+ boxbounds.x0 + 6.0f * chwidth + (float)cellboxwidth / (float)targwidth,
+ boxbounds.y0 + 3.5f * chheight + (float)cellboxheight / (float)targheight,
+ ARGB_WHITE, state->texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ /* handle keyboard navigation before drawing */
+ gfxset_handle_keys(machine, state, xcells, ycells);
+}
+
+
+/*-------------------------------------------------
+ gfxset_handle_keys - handle keys for the
+ graphics viewer
+-------------------------------------------------*/
+
+static void gfxset_handle_keys(running_machine &machine, ui_gfx_state *state, int xcells, int ycells)
+{
+ ui_gfx_state oldstate = *state;
+ gfx_element *gfx;
+ int temp, set;
+
+ /* handle gfxset selection (open bracket,close bracket) */
+ if (ui_input_pressed(machine, IPT_UI_PREV_GROUP))
+ {
+ for (temp = state->gfxset.set - 1; temp >= 0; temp--)
+ if (machine.gfx[temp] != NULL)
+ break;
+ if (temp >= 0)
+ state->gfxset.set = temp;
+ }
+ if (ui_input_pressed(machine, IPT_UI_NEXT_GROUP))
+ {
+ for (temp = state->gfxset.set + 1; temp < MAX_GFX_ELEMENTS; temp++)
+ if (machine.gfx[temp] != NULL)
+ break;
+ if (temp < MAX_GFX_ELEMENTS)
+ state->gfxset.set = temp;
+ }
+
+ /* cache some info in locals */
+ set = state->gfxset.set;
+ gfx = machine.gfx[set];
+
+ /* handle cells per line (minus,plus) */
+ if (ui_input_pressed(machine, IPT_UI_ZOOM_OUT))
+ state->gfxset.count[set] = xcells - 1;
+ if (ui_input_pressed(machine, IPT_UI_ZOOM_IN))
+ state->gfxset.count[set] = xcells + 1;
+
+ /* clamp within range */
+ if (state->gfxset.count[set] < 2)
+ state->gfxset.count[set] = 2;
+ if (state->gfxset.count[set] > 32)
+ state->gfxset.count[set] = 32;
+
+ /* handle rotation (R) */
+ if (ui_input_pressed(machine, IPT_UI_ROTATE))
+ state->gfxset.rotate[set] = orientation_add(ROT90, state->gfxset.rotate[set]);
+
+ /* handle navigation within the cells (up,down,pgup,pgdown) */
+ if (ui_input_pressed_repeat(machine, IPT_UI_UP, 4))
+ state->gfxset.offset[set] -= xcells;
+ if (ui_input_pressed_repeat(machine, IPT_UI_DOWN, 4))
+ state->gfxset.offset[set] += xcells;
+ if (ui_input_pressed_repeat(machine, IPT_UI_PAGE_UP, 6))
+ state->gfxset.offset[set] -= xcells * ycells;
+ if (ui_input_pressed_repeat(machine, IPT_UI_PAGE_DOWN, 6))
+ state->gfxset.offset[set] += xcells * ycells;
+ if (ui_input_pressed_repeat(machine, IPT_UI_HOME, 4))
+ state->gfxset.offset[set] = 0;
+ if (ui_input_pressed_repeat(machine, IPT_UI_END, 4))
+ state->gfxset.offset[set] = gfx->total_elements;
+
+ /* clamp within range */
+ if (state->gfxset.offset[set] + xcells * ycells > ((gfx->total_elements + xcells - 1) / xcells) * xcells)
+ state->gfxset.offset[set] = ((gfx->total_elements + xcells - 1) / xcells) * xcells - xcells * ycells;
+ if (state->gfxset.offset[set] < 0)
+ state->gfxset.offset[set] = 0;
+
+ /* handle color selection (left,right) */
+ if (ui_input_pressed_repeat(machine, IPT_UI_LEFT, 4))
+ state->gfxset.color[set] -= 1;
+ if (ui_input_pressed_repeat(machine, IPT_UI_RIGHT, 4))
+ state->gfxset.color[set] += 1;
+
+ /* clamp within range */
+ if (state->gfxset.color[set] >= (int)gfx->total_colors)
+ state->gfxset.color[set] = gfx->total_colors - 1;
+ if (state->gfxset.color[set] < 0)
+ state->gfxset.color[set] = 0;
+
+ /* if something changed, we need to force an update to the bitmap */
+ if (state->gfxset.set != oldstate.gfxset.set ||
+ state->gfxset.offset[set] != oldstate.gfxset.offset[set] ||
+ state->gfxset.rotate[set] != oldstate.gfxset.rotate[set] ||
+ state->gfxset.color[set] != oldstate.gfxset.color[set] ||
+ state->gfxset.count[set] != oldstate.gfxset.count[set])
+ {
+ state->bitmap_dirty = TRUE;
+ }
+}
+
+
+/*-------------------------------------------------
+ gfxset_update_bitmap - redraw the current
+ graphics view bitmap
+-------------------------------------------------*/
+
+static void gfxset_update_bitmap(running_machine &machine, ui_gfx_state *state, int xcells, int ycells, gfx_element *gfx)
+{
+ int set = state->gfxset.set;
+ int cellxpix, cellypix;
+ int x, y;
+
+ /* compute the number of source pixels in a cell */
+ cellxpix = 1 + ((state->gfxset.rotate[set] & ORIENTATION_SWAP_XY) ? gfx->height : gfx->width);
+ cellypix = 1 + ((state->gfxset.rotate[set] & ORIENTATION_SWAP_XY) ? gfx->width : gfx->height);
+
+ /* realloc the bitmap if it is too small */
+ if (state->bitmap == NULL || state->texture == NULL || state->bitmap->bpp != 32 || state->bitmap->width != cellxpix * xcells || state->bitmap->height != cellypix * ycells)
+ {
+ /* free the old stuff */
+ machine.render().texture_free(state->texture);
+ global_free(state->bitmap);
+
+ /* allocate new stuff */
+ state->bitmap = global_alloc(bitmap_t(cellxpix * xcells, cellypix * ycells, BITMAP_FORMAT_ARGB32));
+ state->texture = machine.render().texture_alloc();
+ state->texture->set_bitmap(state->bitmap, NULL, TEXFORMAT_ARGB32);
+
+ /* force a redraw */
+ state->bitmap_dirty = TRUE;
+ }
+
+ /* handle the redraw */
+ if (state->bitmap_dirty)
+ {
+ /* loop over rows */
+ for (y = 0; y < ycells; y++)
+ {
+ rectangle cellbounds;
+
+ /* make a rect that covers this row */
+ cellbounds.min_x = 0;
+ cellbounds.max_x = state->bitmap->width - 1;
+ cellbounds.min_y = y * cellypix;
+ cellbounds.max_y = (y + 1) * cellypix - 1;
+
+ /* only display if there is data to show */
+ if (state->gfxset.offset[set] + y * xcells < gfx->total_elements)
+ {
+ /* draw the individual cells */
+ for (x = 0; x < xcells; x++)
+ {
+ int index = state->gfxset.offset[set] + y * xcells + x;
+
+ /* update the bounds for this cell */
+ cellbounds.min_x = x * cellxpix;
+ cellbounds.max_x = (x + 1) * cellxpix - 1;
+
+ /* only render if there is data */
+ if (index < gfx->total_elements)
+ gfxset_draw_item(machine, gfx, index, state->bitmap, cellbounds.min_x, cellbounds.min_y, state->gfxset.color[set], state->gfxset.rotate[set]);
+
+ /* otherwise, fill with transparency */
+ else
+ bitmap_fill(state->bitmap, &cellbounds, 0);
+ }
+ }
+
+ /* otherwise, fill with transparency */
+ else
+ bitmap_fill(state->bitmap, &cellbounds, 0);
+ }
+
+ /* reset the texture to force an update */
+ state->texture->set_bitmap(state->bitmap, NULL, TEXFORMAT_ARGB32);
+ state->bitmap_dirty = FALSE;
+ }
+}
+
+
+/*-------------------------------------------------
+ gfxset_draw_item - draw a single item into
+ the view
+-------------------------------------------------*/
+
+static void gfxset_draw_item(running_machine &machine, const gfx_element *gfx, int index, bitmap_t *bitmap, int dstx, int dsty, int color, int rotate)
+{
+ static const pen_t default_palette[] =
+ {
+ MAKE_RGB(0,0,0), MAKE_RGB(0,0,255), MAKE_RGB(0,255,0), MAKE_RGB(0,255,255),
+ MAKE_RGB(255,0,0), MAKE_RGB(255,0,255), MAKE_RGB(255,255,0), MAKE_RGB(255,255,255)
+ };
+ int width = (rotate & ORIENTATION_SWAP_XY) ? gfx->height : gfx->width;
+ int height = (rotate & ORIENTATION_SWAP_XY) ? gfx->width : gfx->height;
+ const rgb_t *palette = (machine.total_colors() != 0) ? palette_entry_list_raw(machine.palette) : NULL;
+ UINT32 rowpixels = bitmap->rowpixels;
+ UINT32 palette_mask = ~0;
+ int x, y;
+
+ if (palette != NULL)
+ palette += gfx->color_base + color * gfx->color_granularity;
+ else
+ {
+ palette = default_palette;
+ palette_mask = 7;
+ }
+
+ /* loop over rows in the cell */
+ for (y = 0; y < height; y++)
+ {
+ UINT32 *dest = (UINT32 *)bitmap->base + (dsty + y) * rowpixels + dstx;
+ const UINT8 *src = gfx_element_get_data(gfx, index);
+
+ /* loop over columns in the cell */
+ for (x = 0; x < width; x++)
+ {
+ int effx = x, effy = y;
+ rgb_t pixel;
+ const UINT8 *s;
+
+ /* compute effective x,y values after rotation */
+ if (!(rotate & ORIENTATION_SWAP_XY))
+ {
+ if (rotate & ORIENTATION_FLIP_X)
+ effx = gfx->width - 1 - effx;
+ if (rotate & ORIENTATION_FLIP_Y)
+ effy = gfx->height - 1 - effy;
+ }
+ else
+ {
+ int temp;
+ if (rotate & ORIENTATION_FLIP_X)
+ effx = gfx->height - 1 - effx;
+ if (rotate & ORIENTATION_FLIP_Y)
+ effy = gfx->width - 1 - effy;
+ temp = effx; effx = effy; effy = temp;
+ }
+
+ /* get a pointer to the start of this source row */
+ s = src + effy * gfx->line_modulo;
+
+ /* extract the pixel */
+ if (gfx->flags & GFX_ELEMENT_PACKED)
+ pixel = (s[effx/2] >> ((effx & 1) * 4)) & 0xf;
+ else
+ pixel = s[effx];
+ *dest++ = 0xff000000 | palette[pixel & palette_mask];
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ TILEMAP VIEWER
+***************************************************************************/
+
+/*-------------------------------------------------
+ tilemap_handler - handler for the tilemap
+ viewer
+-------------------------------------------------*/
+
+static void tilemap_handler(running_machine &machine, render_container *container, ui_gfx_state *state)
+{
+ render_font *ui_font = ui_get_font(machine);
+ float chwidth, chheight;
+ render_bounds mapboxbounds;
+ render_bounds boxbounds;
+ int targwidth = machine.render().ui_target().width();
+ int targheight = machine.render().ui_target().height();
+ float titlewidth;
+ float x0, y0;
+ int mapboxwidth, mapboxheight;
+ int maxxscale, maxyscale;
+ UINT32 mapwidth, mapheight;
+ int x, pixelscale;
+ char title[100];
+
+ /* get the size of the tilemap itself */
+ tilemap_size_by_index(machine, state->tilemap.which, &mapwidth, &mapheight);
+ if (state->tilemap.rotate & ORIENTATION_SWAP_XY)
+ { UINT32 temp = mapwidth; mapwidth = mapheight; mapheight = temp; }
+
+ /* add a half character padding for the box */
+ chheight = ui_get_line_height(machine);
+ chwidth = ui_font->char_width(chheight, machine.render().ui_aspect(), '0');
+ boxbounds.x0 = 0.0f + 0.5f * chwidth;
+ boxbounds.x1 = 1.0f - 0.5f * chwidth;
+ boxbounds.y0 = 0.0f + 0.5f * chheight;
+ boxbounds.y1 = 1.0f - 0.5f * chheight;
+
+ /* the tilemap box bounds starts a half character in from the box */
+ mapboxbounds = boxbounds;
+ mapboxbounds.x0 += 0.5f * chwidth;
+ mapboxbounds.x1 -= 0.5f * chwidth;
+ mapboxbounds.y0 += 0.5f * chheight;
+ mapboxbounds.y1 -= 0.5f * chheight;
+
+ /* add space on the top for a title and a half line of padding */
+ mapboxbounds.y0 += 1.5f * chheight;
+
+ /* convert back to pixels */
+ mapboxwidth = (mapboxbounds.x1 - mapboxbounds.x0) * (float)targwidth;
+ mapboxheight = (mapboxbounds.y1 - mapboxbounds.y0) * (float)targheight;
+
+ /* determine the maximum integral scaling factor */
+ pixelscale = state->tilemap.zoom;
+ if (pixelscale == 0)
+ {
+ for (maxxscale = 1; mapwidth * (maxxscale + 1) < mapboxwidth; maxxscale++) ;
+ for (maxyscale = 1; mapheight * (maxyscale + 1) < mapboxheight; maxyscale++) ;
+ pixelscale = MIN(maxxscale, maxyscale);
+ }
+
+ /* recompute the final box size */
+ mapboxwidth = MIN(mapboxwidth, mapwidth * pixelscale);
+ mapboxheight = MIN(mapboxheight, mapheight * pixelscale);
+
+ /* recompute the bounds, centered within the existing bounds */
+ mapboxbounds.x0 += 0.5f * ((mapboxbounds.x1 - mapboxbounds.x0) - (float)mapboxwidth / (float)targwidth);
+ mapboxbounds.x1 = mapboxbounds.x0 + (float)mapboxwidth / (float)targwidth;
+ mapboxbounds.y0 += 0.5f * ((mapboxbounds.y1 - mapboxbounds.y0) - (float)mapboxheight / (float)targheight);
+ mapboxbounds.y1 = mapboxbounds.y0 + (float)mapboxheight / (float)targheight;
+
+ /* now recompute the outer box against this new info */
+ boxbounds.x0 = mapboxbounds.x0 - 0.5f * chwidth;
+ boxbounds.x1 = mapboxbounds.x1 + 0.5f * chwidth;
+ boxbounds.y0 = mapboxbounds.y0 - 2.0f * chheight;
+ boxbounds.y1 = mapboxbounds.y1 + 0.5f * chheight;
+
+ /* figure out the title and expand the outer box to fit */
+ sprintf(title, "TMAP %d/%d %dx%d OFFS %d,%d", state->tilemap.which, tilemap_count(machine) - 1, mapwidth, mapheight, state->tilemap.xoffs, state->tilemap.yoffs);
+ titlewidth = ui_font->string_width(chheight, machine.render().ui_aspect(), title);
+ if (boxbounds.x1 - boxbounds.x0 < titlewidth + chwidth)
+ {
+ boxbounds.x0 = 0.5f - 0.5f * (titlewidth + chwidth);
+ boxbounds.x1 = boxbounds.x0 + titlewidth + chwidth;
+ }
+
+ /* go ahead and draw the outer box now */
+ ui_draw_outlined_box(container, boxbounds.x0, boxbounds.y0, boxbounds.x1, boxbounds.y1, UI_GFXVIEWER_BG_COLOR);
+
+ /* draw the title */
+ x0 = 0.5f - 0.5f * titlewidth;
+ y0 = boxbounds.y0 + 0.5f * chheight;
+ for (x = 0; title[x] != 0; x++)
+ {
+ container->add_char(x0, y0, chheight, machine.render().ui_aspect(), ARGB_WHITE, *ui_font, title[x]);
+ x0 += ui_font->char_width(chheight, machine.render().ui_aspect(), title[x]);
+ }
+
+ /* update the bitmap */
+ tilemap_update_bitmap(machine, state, mapboxwidth / pixelscale, mapboxheight / pixelscale);
+
+ /* add the final quad */
+ container->add_quad(mapboxbounds.x0, mapboxbounds.y0,
+ mapboxbounds.x1, mapboxbounds.y1,
+ ARGB_WHITE, state->texture,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(state->tilemap.rotate));
+
+ /* handle keyboard input */
+ tilemap_handle_keys(machine, state, mapboxwidth, mapboxheight);
+}
+
+
+/*-------------------------------------------------
+ tilemap_handle_keys - handle keys for the
+ tilemap view
+-------------------------------------------------*/
+
+static void tilemap_handle_keys(running_machine &machine, ui_gfx_state *state, int viswidth, int visheight)
+{
+ ui_gfx_state oldstate = *state;
+ UINT32 mapwidth, mapheight;
+ int step;
+
+ /* handle tilemap selection (open bracket,close bracket) */
+ if (ui_input_pressed(machine, IPT_UI_PREV_GROUP))
+ state->tilemap.which--;
+ if (ui_input_pressed(machine, IPT_UI_NEXT_GROUP))
+ state->tilemap.which++;
+
+ /* clamp within range */
+ if (state->tilemap.which < 0)
+ state->tilemap.which = 0;
+ if (state->tilemap.which >= tilemap_count(machine))
+ state->tilemap.which = tilemap_count(machine) - 1;
+
+ /* cache some info in locals */
+ tilemap_size_by_index(machine, state->tilemap.which, &mapwidth, &mapheight);
+
+ /* handle zoom (minus,plus) */
+ if (ui_input_pressed(machine, IPT_UI_ZOOM_OUT))
+ state->tilemap.zoom--;
+ if (ui_input_pressed(machine, IPT_UI_ZOOM_IN))
+ state->tilemap.zoom++;
+
+ /* clamp within range */
+ if (state->tilemap.zoom < 0)
+ state->tilemap.zoom = 0;
+ if (state->tilemap.zoom > 8)
+ state->tilemap.zoom = 8;
+ if (state->tilemap.zoom != oldstate.tilemap.zoom)
+ {
+ if (state->tilemap.zoom != 0)
+ popmessage("Zoom = %d", state->tilemap.zoom);
+ else
+ popmessage("Zoom Auto");
+ }
+
+ /* handle rotation (R) */
+ if (ui_input_pressed(machine, IPT_UI_ROTATE))
+ state->tilemap.rotate = orientation_add(ROT90, state->tilemap.rotate);
+
+ /* handle navigation (up,down,left,right) */
+ step = 8;
+ if (machine.input().code_pressed(KEYCODE_LSHIFT)) step = 1;
+ if (machine.input().code_pressed(KEYCODE_LCONTROL)) step = 64;
+ if (ui_input_pressed_repeat(machine, IPT_UI_UP, 4))
+ state->tilemap.yoffs -= step;
+ if (ui_input_pressed_repeat(machine, IPT_UI_DOWN, 4))
+ state->tilemap.yoffs += step;
+ if (ui_input_pressed_repeat(machine, IPT_UI_LEFT, 6))
+ state->tilemap.xoffs -= step;
+ if (ui_input_pressed_repeat(machine, IPT_UI_RIGHT, 6))
+ state->tilemap.xoffs += step;
+
+ /* clamp within range */
+ while (state->tilemap.xoffs < 0)
+ state->tilemap.xoffs += mapwidth;
+ while (state->tilemap.xoffs >= mapwidth)
+ state->tilemap.xoffs -= mapwidth;
+ while (state->tilemap.yoffs < 0)
+ state->tilemap.yoffs += mapheight;
+ while (state->tilemap.yoffs >= mapheight)
+ state->tilemap.yoffs -= mapheight;
+
+ /* if something changed, we need to force an update to the bitmap */
+ if (state->tilemap.which != oldstate.tilemap.which ||
+ state->tilemap.xoffs != oldstate.tilemap.xoffs ||
+ state->tilemap.yoffs != oldstate.tilemap.yoffs ||
+ state->tilemap.rotate != oldstate.tilemap.rotate)
+ {
+ state->bitmap_dirty = TRUE;
+ }
+}
+
+
+/*-------------------------------------------------
+ tilemap_update_bitmap - update the bitmap
+ for the tilemap view
+-------------------------------------------------*/
+
+static void tilemap_update_bitmap(running_machine &machine, ui_gfx_state *state, int width, int height)
+{
+ bitmap_format screen_format = machine.primary_screen->format();
+ palette_t *palette = NULL;
+ int screen_texformat;
+
+ /* convert the screen format to a texture format */
+ switch (screen_format)
+ {
+ case BITMAP_FORMAT_INDEXED16: screen_texformat = TEXFORMAT_PALETTE16; palette = machine.palette; break;
+ case BITMAP_FORMAT_RGB15: screen_texformat = TEXFORMAT_RGB15; palette = NULL; break;
+ case BITMAP_FORMAT_RGB32: screen_texformat = TEXFORMAT_RGB32; palette = NULL; break;
+ default: fatalerror("Invalid bitmap format!"); break;
+ }
+
+ /* swap the coordinates back if they were talking about a rotated surface */
+ if (state->tilemap.rotate & ORIENTATION_SWAP_XY)
+ { UINT32 temp = width; width = height; height = temp; }
+
+ /* realloc the bitmap if it is too small */
+ if (state->bitmap == NULL || state->texture == NULL || state->bitmap->format != screen_format || state->bitmap->width != width || state->bitmap->height != height)
+ {
+ /* free the old stuff */
+ machine.render().texture_free(state->texture);
+ global_free(state->bitmap);
+
+ /* allocate new stuff */
+ state->bitmap = global_alloc(bitmap_t(width, height, screen_format));
+ state->texture = machine.render().texture_alloc();
+ state->texture->set_bitmap(state->bitmap, NULL, screen_texformat, palette);
+
+ /* force a redraw */
+ state->bitmap_dirty = TRUE;
+ }
+
+ /* handle the redraw */
+ if (state->bitmap_dirty)
+ {
+ tilemap_draw_by_index(machine, state->bitmap, state->tilemap.which, state->tilemap.xoffs, state->tilemap.yoffs);
+
+ /* reset the texture to force an update */
+ state->texture->set_bitmap(state->bitmap, NULL, screen_texformat, palette);
+ state->bitmap_dirty = FALSE;
+ }
+}