diff options
Diffstat (limited to 'src/emu/ui/viewgfx.cpp')
-rw-r--r-- | src/emu/ui/viewgfx.cpp | 1173 |
1 files changed, 1173 insertions, 0 deletions
diff --git a/src/emu/ui/viewgfx.cpp b/src/emu/ui/viewgfx.cpp new file mode 100644 index 00000000000..434cc9255f3 --- /dev/null +++ b/src/emu/ui/viewgfx.cpp @@ -0,0 +1,1173 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/********************************************************************* + + ui/viewgfx.c + + Internal graphics viewer. + +*********************************************************************/ + +#include "emu.h" +#include "ui/ui.h" +#include "ui/menu.h" +#include "uiinput.h" +#include "render.h" +#include "rendfont.h" +#include "rendutil.h" +#include "ui/viewgfx.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +enum ui_gfx_modes +{ + UI_GFX_PALETTE = 0, + UI_GFX_GFXSET, + UI_GFX_TILEMAP +}; + +const int MAX_GFX_DECODERS = 8; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +// information about a single gfx device +struct ui_gfx_info +{ + device_gfx_interface *interface; // pointer to device's gfx interface + UINT8 setcount; // how many gfx sets device has + UINT8 rotate[MAX_GFX_ELEMENTS]; // current rotation (orientation) value + UINT8 columns[MAX_GFX_ELEMENTS]; // number of items per row + int offset[MAX_GFX_ELEMENTS]; // current offset of top,left item + int color[MAX_GFX_ELEMENTS]; // current color selected +}; + +struct ui_gfx_state +{ + bool started; // have we called ui_gfx_count_devices() yet? + UINT8 mode; // which mode are we in? + + // intermediate bitmaps + bool bitmap_dirty; // is the bitmap dirty? + bitmap_rgb32 * bitmap; // bitmap for drawing gfx and tilemaps + render_texture *texture; // texture for rendering the above bitmap + + // palette-specific data + struct + { + palette_device *device; // pointer to current device + int devcount; // how many palette devices exist + int devindex; // which palette device is visible + UINT8 which; // which subset (pens or indirect colors)? + UINT8 columns; // number of items per row + int offset; // current offset of top left item + } palette; + + // graphics-specific data + struct + { + UINT8 devcount; // how many gfx devices exist + UINT8 devindex; // which device is visible + UINT8 set; // which set is visible + } gfxset; + + // information about each gfx device + ui_gfx_info gfxdev[MAX_GFX_DECODERS]; + + // tilemap-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_count_devices(running_machine &machine, ui_gfx_state &state); +static void ui_gfx_exit(running_machine &machine); + +// palette handling +static void palette_set_device(running_machine &machine, ui_gfx_state &state); +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, gfx_element &gfx, int index, bitmap_rgb32 &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 graphics viewer +//------------------------------------------------- + +void ui_gfx_init(running_machine &machine) +{ + ui_gfx_state *state = &ui_gfx; + int rotate = machine.system().flags & ORIENTATION_MASK; + + // 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.columns = 16; + + // set up the graphics state + for (int i = 0; i < MAX_GFX_DECODERS; i++) + for (int j = 0; j < MAX_GFX_ELEMENTS; j++) + { + state->gfxdev[i].rotate[j] = rotate; + state->gfxdev[i].columns[j] = 16; + } + + // set up the tilemap state + state->tilemap.rotate = rotate; +} + + +//------------------------------------------------- +// ui_gfx_count_devices - count the palettes, +// gfx decoders and gfx sets in the machine +//------------------------------------------------- + +static void ui_gfx_count_devices(running_machine &machine, ui_gfx_state &state) +{ + palette_device_iterator pal_iter(machine.root_device()); + gfx_interface_iterator gfx_iter(machine.root_device()); + + // count the palette devices + state.palette.devcount = pal_iter.count(); + + // set the pointer to the first palette + if (state.palette.devcount > 0) + palette_set_device(machine, state); + + // count the gfx devices + state.gfxset.devcount = 0; + int tempcount = gfx_iter.count(); + + // count the gfx sets in each device, skipping devices with none + if (tempcount > 0) + { + device_gfx_interface *interface; + int i, count; + + for (i = 0, interface = gfx_iter.first(); + i < tempcount && state.gfxset.devcount < MAX_GFX_DECODERS; + i++, interface = gfx_iter.next()) + { + for (count = 0; count < MAX_GFX_ELEMENTS && interface->gfx(count) != NULL; count++); + + // count = index of first NULL + if (count > 0) + { + state.gfxdev[state.gfxset.devcount].interface = interface; + state.gfxdev[state.gfxset.devcount].setcount = count; + state.gfxset.devcount++; + } + } + } + + state.started = true; +} + + +//------------------------------------------------- +// 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_is_relevant - returns 'true' if the +// internal graphics viewer has relevance +// +// NOTE: this must not be called before machine +// initialization is complete, as some drivers +// create or modify gfx sets in VIDEO_START +//------------------------------------------------- + +bool ui_gfx_is_relevant(running_machine &machine) +{ + ui_gfx_state &state = ui_gfx; + + if (!state.started) + ui_gfx_count_devices(machine, state); + + return state.palette.devcount > 0 + || state.gfxset.devcount > 0 + || machine.tilemap().count() > 0; +} + + +//------------------------------------------------- +// 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 (!ui_gfx_is_relevant(machine)) + 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 UI_GFX_PALETTE: + // if we have a palette, display it + if (state.palette.devcount > 0) + { + palette_handler(machine, container, state); + break; + } + + // fall through... + state.mode++; + + case UI_GFX_GFXSET: + // if we have graphics sets, display them + if (state.gfxset.devcount > 0) + { + gfxset_handler(machine, container, state); + break; + } + + // fall through... + state.mode++; + + case UI_GFX_TILEMAP: + // if we have tilemaps, display them + if (machine.tilemap().count() > 0) + { + tilemap_handler(machine, container, state); + break; + } + + state.mode = UI_GFX_PALETTE; + 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_set_device - set the pointer to the +// current palette device +//------------------------------------------------- + +static void palette_set_device(running_machine &machine, ui_gfx_state &state) +{ + palette_device_iterator pal_iter(machine.root_device()); + state.palette.device = pal_iter.byindex(state.palette.devindex); +} + + +//------------------------------------------------- +// palette_handler - handler for the palette +// viewer +//------------------------------------------------- + +static void palette_handler(running_machine &machine, render_container *container, ui_gfx_state &state) +{ + palette_device *palette = state.palette.device; + + int total = state.palette.which ? palette->indirect_entries() : palette->entries(); + const rgb_t *raw_color = palette->palette()->entry_list_raw(); + render_font *ui_font = machine.ui().get_font(); + float cellwidth, cellheight; + float chwidth, chheight; + float titlewidth; + float x0, y0; + render_bounds cellboxbounds; + render_bounds boxbounds; + int x, y, skip; + char title[100]; + + // add a half character padding for the box + chheight = machine.ui().get_line_height(); + 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 + const char *suffix = palette->indirect_entries() == 0 ? "" : state.palette.which ? " COLORS" : " PENS"; + sprintf(title, "'%s'%s", palette->tag(), suffix); + 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 + machine.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.columns; + cellheight = (cellboxbounds.y1 - cellboxbounds.y0) / (float)state.palette.columns; + + // draw the top column headers + skip = (int)(chwidth / cellwidth); + for (x = 0; x < state.palette.columns; 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.columns; y += 1 + skip) + + // only display if there is data to show + if (state.palette.offset + y * state.palette.columns < 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.columns); + 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.columns; y++) + for (x = 0; x < state.palette.columns; x++) + { + int index = state.palette.offset + y * state.palette.columns + x; + if (index < total) + { + pen_t pen = state.palette.which ? palette->indirect_color(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_handle_keys - handle key inputs for +// the palette viewer +//------------------------------------------------- + +static void palette_handle_keys(running_machine &machine, ui_gfx_state &state) +{ + palette_device *palette = state.palette.device; + int rowcount, screencount; + int total; + + // handle zoom (minus,plus) + if (ui_input_pressed(machine, IPT_UI_ZOOM_OUT)) + state.palette.columns /= 2; + if (ui_input_pressed(machine, IPT_UI_ZOOM_IN)) + state.palette.columns *= 2; + + // clamp within range + if (state.palette.columns <= 4) + state.palette.columns = 4; + if (state.palette.columns > 64) + state.palette.columns = 64; + + // handle colormap selection (open bracket,close bracket) + if (ui_input_pressed(machine, IPT_UI_PREV_GROUP)) + { + if (state.palette.which) + state.palette.which = 0; + else if (state.palette.devindex > 0) + { + state.palette.devindex--; + palette_set_device(machine, state); + palette = state.palette.device; + state.palette.which = (palette->indirect_entries() > 0); + } + } + if (ui_input_pressed(machine, IPT_UI_NEXT_GROUP)) + { + if (!state.palette.which && palette->indirect_entries() > 0) + state.palette.which = 1; + else if (state.palette.devindex < state.palette.devcount - 1) + { + state.palette.devindex++; + palette_set_device(machine, state); + palette = state.palette.device; + state.palette.which = 0; + } + } + + // cache some info in locals + total = state.palette.which ? palette->indirect_entries() : palette->entries(); + + // determine number of entries per row and total + rowcount = state.palette.columns; + 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 = machine.ui().get_font(); + int dev = state.gfxset.devindex; + int set = state.gfxset.set; + ui_gfx_info &info = state.gfxdev[dev]; + device_gfx_interface &interface = *info.interface; + gfx_element &gfx = *interface.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 = machine.ui().get_line_height(); + 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 + ((info.rotate[set] & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width()); + cellypix = 1 + ((info.rotate[set] & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height()); + + // compute the largest pixel scale factor that still fits + xcells = info.columns[set]; + while (xcells > 1) + { + pixelscale = (cellboxwidth / xcells) / cellxpix; + if (pixelscale != 0) + break; + xcells--; + } + info.columns[set] = 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 + sprintf(title, "'%s' %d/%d %dx%d COLOR %X", + interface.device().tag(), + set, info.setcount - 1, + gfx.width(), gfx.height(), + info.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 + machine.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 (info.offset[set] + y * xcells < gfx.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", info.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) +{ + // handle gfxset selection (open bracket,close bracket) + if (ui_input_pressed(machine, IPT_UI_PREV_GROUP)) + { + if (state.gfxset.set > 0) + state.gfxset.set--; + else if (state.gfxset.devindex > 0) + { + state.gfxset.devindex--; + state.gfxset.set = state.gfxdev[state.gfxset.devindex].setcount - 1; + } + state.bitmap_dirty = true; + } + if (ui_input_pressed(machine, IPT_UI_NEXT_GROUP)) + { + if (state.gfxset.set < state.gfxdev[state.gfxset.devindex].setcount - 1) + state.gfxset.set++; + else if (state.gfxset.devindex < state.gfxset.devcount - 1) + { + state.gfxset.devindex++; + state.gfxset.set = 0; + } + state.bitmap_dirty = true; + } + + // cache some info in locals + int dev = state.gfxset.devindex; + int set = state.gfxset.set; + ui_gfx_info &info = state.gfxdev[dev]; + gfx_element &gfx = *info.interface->gfx(set); + + // handle cells per line (minus,plus) + if (ui_input_pressed(machine, IPT_UI_ZOOM_OUT)) + { info.columns[set] = xcells - 1; state.bitmap_dirty = true; } + + if (ui_input_pressed(machine, IPT_UI_ZOOM_IN)) + { info.columns[set] = xcells + 1; state.bitmap_dirty = true; } + + // clamp within range + if (info.columns[set] < 2) + { info.columns[set] = 2; state.bitmap_dirty = true; } + if (info.columns[set] > 128) + { info.columns[set] = 128; state.bitmap_dirty = true; } + + // handle rotation (R) + if (ui_input_pressed(machine, IPT_UI_ROTATE)) + { + info.rotate[set] = orientation_add(ROT90, info.rotate[set]); + state.bitmap_dirty = true; + } + + // handle navigation within the cells (up,down,pgup,pgdown) + if (ui_input_pressed_repeat(machine, IPT_UI_UP, 4)) + { info.offset[set] -= xcells; state.bitmap_dirty = true; } + if (ui_input_pressed_repeat(machine, IPT_UI_DOWN, 4)) + { info.offset[set] += xcells; state.bitmap_dirty = true; } + if (ui_input_pressed_repeat(machine, IPT_UI_PAGE_UP, 6)) + { info.offset[set] -= xcells * ycells; state.bitmap_dirty = true; } + if (ui_input_pressed_repeat(machine, IPT_UI_PAGE_DOWN, 6)) + { info.offset[set] += xcells * ycells; state.bitmap_dirty = true; } + if (ui_input_pressed_repeat(machine, IPT_UI_HOME, 4)) + { info.offset[set] = 0; state.bitmap_dirty = true; } + if (ui_input_pressed_repeat(machine, IPT_UI_END, 4)) + { info.offset[set] = gfx.elements(); state.bitmap_dirty = true; } + + // clamp within range + if (info.offset[set] + xcells * ycells > ((gfx.elements() + xcells - 1) / xcells) * xcells) + { + info.offset[set] = ((gfx.elements() + xcells - 1) / xcells) * xcells - xcells * ycells; + state.bitmap_dirty = true; + } + if (info.offset[set] < 0) + { info.offset[set] = 0; state.bitmap_dirty = true; } + + // handle color selection (left,right) + if (ui_input_pressed_repeat(machine, IPT_UI_LEFT, 4)) + { info.color[set] -= 1; state.bitmap_dirty = true; } + if (ui_input_pressed_repeat(machine, IPT_UI_RIGHT, 4)) + { info.color[set] += 1; state.bitmap_dirty = true; } + + // clamp within range + if (info.color[set] >= (int)gfx.colors()) + { info.color[set] = gfx.colors() - 1; state.bitmap_dirty = true; } + if (info.color[set] < 0) + { info.color[set] = 0; 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 dev = state.gfxset.devindex; + int set = state.gfxset.set; + ui_gfx_info &info = state.gfxdev[dev]; + int cellxpix, cellypix; + int x, y; + + // compute the number of source pixels in a cell + cellxpix = 1 + ((info.rotate[set] & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width()); + cellypix = 1 + ((info.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_rgb32(cellxpix * xcells, cellypix * ycells)); + state.texture = machine.render().texture_alloc(); + state.texture->set_bitmap(*state.bitmap, state.bitmap->cliprect(), 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.set(0, state.bitmap->width() - 1, y * cellypix, (y + 1) * cellypix - 1); + + // only display if there is data to show + if (info.offset[set] + y * xcells < gfx.elements()) + { + // draw the individual cells + for (x = 0; x < xcells; x++) + { + int index = info.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.elements()) + gfxset_draw_item(machine, gfx, index, *state.bitmap, cellbounds.min_x, cellbounds.min_y, info.color[set], info.rotate[set]); + + // otherwise, fill with transparency + else + state.bitmap->fill(0, cellbounds); + } + } + + // otherwise, fill with transparency + else + state.bitmap->fill(0, cellbounds); + } + + // reset the texture to force an update + state.texture->set_bitmap(*state.bitmap, state.bitmap->cliprect(), TEXFORMAT_ARGB32); + state.bitmap_dirty = false; + } +} + + +//------------------------------------------------- +// gfxset_draw_item - draw a single item into +// the view +//------------------------------------------------- + +static void gfxset_draw_item(running_machine &machine, gfx_element &gfx, int index, bitmap_rgb32 &bitmap, int dstx, int dsty, int color, int rotate) +{ + int width = (rotate & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width(); + int height = (rotate & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height(); + const rgb_t *palette = gfx.palette()->palette()->entry_list_raw() + gfx.colorbase() + color * gfx.granularity(); + int x, y; + + // loop over rows in the cell + for (y = 0; y < height; y++) + { + UINT32 *dest = &bitmap.pix32(dsty + y, dstx); + const UINT8 *src = gfx.get_data(index); + + // loop over columns in the cell + for (x = 0; x < width; x++) + { + int effx = x, effy = y; + 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.rowbytes(); + + // extract the pixel + *dest++ = 0xff000000 | palette[s[effx]]; + } + } +} + + + +/*************************************************************************** + 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 = machine.ui().get_font(); + 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_t *tilemap = machine.tilemap().find(state.tilemap.which); + mapwidth = tilemap->width(); + mapheight = tilemap->height(); + if (state.tilemap.rotate & ORIENTATION_SWAP_XY) + { UINT32 temp = mapwidth; mapwidth = mapheight; mapheight = temp; } + + // add a half character padding for the box + chheight = machine.ui().get_line_height(); + 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, "TILEMAP %d/%d %dx%d OFFS %d,%d", state.tilemap.which, machine.tilemap().count() - 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 + machine.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) +{ + UINT32 mapwidth, mapheight; + int step; + + // handle tilemap selection (open bracket,close bracket) + if (ui_input_pressed(machine, IPT_UI_PREV_GROUP) && state.tilemap.which > 0) + { state.tilemap.which--; state.bitmap_dirty = true; } + if (ui_input_pressed(machine, IPT_UI_NEXT_GROUP) && state.tilemap.which < machine.tilemap().count() - 1) + { state.tilemap.which++; state.bitmap_dirty = true; } + + // cache some info in locals + tilemap_t *tilemap = machine.tilemap().find(state.tilemap.which); + mapwidth = tilemap->width(); + mapheight = tilemap->height(); + + // handle zoom (minus,plus) + if (ui_input_pressed(machine, IPT_UI_ZOOM_OUT) && state.tilemap.zoom > 0) + { + state.tilemap.zoom--; + state.bitmap_dirty = true; + if (state.tilemap.zoom != 0) + machine.popmessage("Zoom = %d", state.tilemap.zoom); + else + machine.popmessage("Zoom Auto"); + } + if (ui_input_pressed(machine, IPT_UI_ZOOM_IN) && state.tilemap.zoom < 8) + { + state.tilemap.zoom++; + state.bitmap_dirty = true; + machine.popmessage("Zoom = %d", state.tilemap.zoom); + } + + // handle rotation (R) + if (ui_input_pressed(machine, IPT_UI_ROTATE)) + { + state.tilemap.rotate = orientation_add(ROT90, state.tilemap.rotate); + state.bitmap_dirty = true; + } + + // 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; state.bitmap_dirty = true; } + if (ui_input_pressed_repeat(machine, IPT_UI_DOWN, 4)) + { state.tilemap.yoffs += step; state.bitmap_dirty = true; } + if (ui_input_pressed_repeat(machine, IPT_UI_LEFT, 6)) + { state.tilemap.xoffs -= step; state.bitmap_dirty = true; } + if (ui_input_pressed_repeat(machine, IPT_UI_RIGHT, 6)) + { state.tilemap.xoffs += step; state.bitmap_dirty = true; } + + // 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; +} + + +//------------------------------------------------- +// 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) +{ + // 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->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_rgb32(width, height)); + state.texture = machine.render().texture_alloc(); + state.texture->set_bitmap(*state.bitmap, state.bitmap->cliprect(), TEXFORMAT_RGB32); + + // force a redraw + state.bitmap_dirty = true; + } + + // handle the redraw + if (state.bitmap_dirty) + { + tilemap_t *tilemap = machine.tilemap().find(state.tilemap.which); + tilemap->draw_debug(*machine.first_screen(), *state.bitmap, state.tilemap.xoffs, state.tilemap.yoffs); + + // reset the texture to force an update + state.texture->set_bitmap(*state.bitmap, state.bitmap->cliprect(), TEXFORMAT_RGB32); + state.bitmap_dirty = false; + } +} |