diff options
Diffstat (limited to 'src/frontend/mame/ui/viewgfx.cpp')
-rw-r--r-- | src/frontend/mame/ui/viewgfx.cpp | 2211 |
1 files changed, 1238 insertions, 973 deletions
diff --git a/src/frontend/mame/ui/viewgfx.cpp b/src/frontend/mame/ui/viewgfx.cpp index bc6dee9cd47..cbb104d5460 100644 --- a/src/frontend/mame/ui/viewgfx.cpp +++ b/src/frontend/mame/ui/viewgfx.cpp @@ -9,408 +9,1003 @@ *********************************************************************/ #include "emu.h" +#include "ui/viewgfx.h" + #include "emupal.h" -#include "ui/ui.h" -#include "uiinput.h" #include "render.h" #include "rendfont.h" #include "rendutil.h" +#include "screen.h" #include "tilemap.h" -#include "ui/viewgfx.h" +#include "uiinput.h" +#include "util/unicode.h" +#include "osdepend.h" -/*************************************************************************** - CONSTANTS -***************************************************************************/ +#include <cmath> +#include <vector> + + +namespace { -enum ui_gfx_modes +class gfx_viewer { - UI_GFX_PALETTE = 0, - UI_GFX_GFXSET, - UI_GFX_TILEMAP -}; +public: + gfx_viewer(running_machine &machine) : + m_machine(machine), + m_palette(machine), + m_gfxset(machine), + m_tilemap(machine) + { + } -const uint8_t MAX_GFX_DECODERS = 8; + // copy constructor needed to make std::any happy + gfx_viewer(gfx_viewer const &that) : + gfx_viewer(that.m_machine) + { + } + ~gfx_viewer() + { + if (m_texture) + m_machine.render().texture_free(m_texture); + } + uint32_t handle(mame_ui_manager &mui, render_container &container, bool uistate) + { + // implicitly cancel if there's nothing to display + if (!is_relevant()) + return cancel(uistate); -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ + // let the OSD do its thing + mui.machine().osd().check_osd_inputs(); -// information about a single gfx device -struct ui_gfx_info -{ - device_gfx_interface *interface; // pointer to device's gfx interface - uint8_t setcount; // how many gfx sets device has - uint8_t rotate[MAX_GFX_ELEMENTS]; // current rotation (orientation) value - uint8_t 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 - device_palette_interface *palette[MAX_GFX_ELEMENTS]; // associated palette (maybe multiple choice one day?) - int color_count[MAX_GFX_ELEMENTS]; // Range of color values -}; + // always mark the bitmap dirty if not paused + if (!m_machine.paused()) + m_bitmap_dirty = true; -struct ui_gfx_state -{ - bool started; // have we called ui_gfx_count_devices() yet? - uint8_t mode; // which mode are we in? + // handle pointer events to show hover info + ui_event event; + while (m_machine.ui_input().pop_event(&event)) + { + switch (event.event_type) + { + case ui_event::type::POINTER_UPDATE: + { + // ignore pointer input in windows other than the one that displays the UI + render_target &target(m_machine.render().ui_target()); + if (&target != event.target) + break; + + // don't change if the current pointer has buttons pressed and this one doesn't + if (event.pointer_id == m_current_pointer) + { + assert(m_pointer_type == event.pointer_type); + m_pointer_buttons = event.pointer_buttons; + m_pointer_inside = target.map_point_container( + event.pointer_x, + event.pointer_y, + container, + m_pointer_x, + m_pointer_y); + } + else if ((0 > m_current_pointer) || (!m_pointer_buttons && (!m_pointer_inside || event.pointer_buttons))) + { + float x, y; + bool const inside(target.map_point_container(event.pointer_x, event.pointer_y, container, x, y)); + if ((0 > m_current_pointer) || event.pointer_buttons || (!m_pointer_inside && inside)) + { + m_current_pointer = event.pointer_id; + m_pointer_type = event.pointer_type; + m_pointer_buttons = event.pointer_buttons; + m_pointer_x = x; + m_pointer_y = y; + m_pointer_inside = inside; + } + } + } + break; + + case ui_event::type::POINTER_LEAVE: + case ui_event::type::POINTER_ABORT: + { + // if this was our pointer, we've lost it + render_target &target(m_machine.render().ui_target()); + if ((&target == event.target) && (event.pointer_id == m_current_pointer)) + { + // keep the pointer position and type so we can show touch locations after release + m_current_pointer = -1; + m_pointer_buttons = 0U; + m_pointer_inside = target.map_point_container( + event.pointer_x, + event.pointer_y, + container, + m_pointer_x, + m_pointer_y); + } + } + break; + + // ignore anything that isn't pointer-related + default: + break; + } + } + + // always draw non-touch pointer + mame_ui_manager::display_pointer pointers[1]{ { m_machine.render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } }; + if (m_pointer_inside && (0 <= m_current_pointer) && (ui_event::pointer::TOUCH != m_pointer_type)) + mui.set_pointers(std::begin(pointers), std::end(pointers)); + else + mui.set_pointers(std::begin(pointers), std::begin(pointers)); + + // try to display the selected view + while (true) + { + switch (m_mode) + { + case view::PALETTE: + if (m_palette.interface()) + return handle_palette(mui, container, uistate); + m_mode = view::GFXSET; + break; + + case view::GFXSET: + if (m_gfxset.has_gfx()) + return handle_gfxset(mui, container, uistate); + m_mode = view::TILEMAP; + break; - // 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 + case view::TILEMAP: + if (m_machine.tilemap().count()) + return handle_tilemap(mui, container, uistate); + m_mode = view::PALETTE; + break; + } + } + } - // palette-specific data - struct +private: + enum class view { - device_palette_interface *interface; // pointer to current palette - int devcount; // how many palette devices exist - int devindex; // which palette device is visible - uint8_t which; // which subset (pens or indirect colors)? - uint8_t columns; // number of items per row - int offset; // current offset of top left item - } palette; - - // graphics-specific data - struct + PALETTE = 0, + GFXSET, + TILEMAP + }; + + class palette { - uint8_t devcount; // how many gfx devices exist - uint8_t devindex; // which device is visible - uint8_t set; // which set is visible - } gfxset; + public: + enum class subset + { + PENS, + INDIRECT + }; + + palette(running_machine &machine) : + m_count(palette_interface_enumerator(machine.root_device()).count()) + { + if (m_count) + set_device(machine); + } + + device_palette_interface *interface() const noexcept + { + return m_interface; + } + + bool indirect() const noexcept + { + return subset::INDIRECT == m_which; + } + + unsigned columns() const noexcept + { + return m_columns; + } + + unsigned index(unsigned x, unsigned y) const noexcept + { + return m_offset + (y * m_columns) + x; + } + + void handle_keys(running_machine &machine); + + private: + void set_device(running_machine &machine) + { + m_interface = palette_interface_enumerator(machine.root_device()).byindex(m_index); + } - // information about each gfx device - ui_gfx_info gfxdev[MAX_GFX_DECODERS]; + void next_group(running_machine &machine) noexcept + { + if ((subset::PENS == m_which) && m_interface->indirect_entries()) + { + m_which = subset::INDIRECT; + } + else if ((m_count - 1) > m_index) + { + ++m_index; + set_device(machine); + m_which = subset::PENS; + } + } - // tilemap-specific data - struct + void prev_group(running_machine &machine) noexcept + { + if (subset::INDIRECT == m_which) + { + m_which = subset::PENS; + } + else if (0 < m_index) + { + --m_index; + set_device(machine); + m_which = m_interface->indirect_entries() ? subset::INDIRECT : subset::PENS; + } + } + + device_palette_interface *m_interface = nullptr; + unsigned const m_count; + unsigned m_index = 0U; + subset m_which = subset::PENS; + unsigned m_columns = 16U; + int m_offset = 0; + }; + + class gfxset { - int which; // which tilemap are we viewing? - int xoffs; // current X offset - int yoffs; // current Y offset - int zoom; // zoom factor - uint8_t rotate; // current rotation (orientation) value - uint32_t flags; // render flags - } tilemap; -}; + public: + struct setinfo + { + void next_color() noexcept + { + if ((m_color_count - 1) > m_color) + ++m_color; + else + m_color = 0U; + } + void prev_color() noexcept + { + if (m_color) + --m_color; + else + m_color = m_color_count - 1; + } + device_palette_interface *m_palette = nullptr; + int m_offset = 0; + unsigned m_color = 0; + unsigned m_color_count = 0U; + uint8_t m_rotate = 0U; + uint8_t m_columns = 16U; + bool m_integer_scale = false; + }; -/*************************************************************************** - GLOBAL VARIABLES -***************************************************************************/ + class devinfo + { + public: + devinfo(device_gfx_interface &interface, device_palette_interface *first_palette, u8 rotate) : + m_interface(&interface), + m_setcount(0U) + { + for (gfx_element *gfx; (MAX_GFX_ELEMENTS > m_setcount) && ((gfx = interface.gfx(m_setcount)) != nullptr); ++m_setcount) + { + auto &set = m_sets[m_setcount]; + if (gfx->has_palette()) + { + set.m_palette = &gfx->palette(); + set.m_color_count = gfx->colors(); + } + else + { + set.m_palette = first_palette; + set.m_color_count = first_palette->entries() / gfx->granularity(); + if (!set.m_color_count) + set.m_color_count = 1U; + } + set.m_rotate = rotate; + } + } -static ui_gfx_state ui_gfx; + device_gfx_interface &interface() const noexcept + { + return *m_interface; + } + unsigned setcount() const noexcept + { + return m_setcount; + } + setinfo const &set(unsigned index) const noexcept + { + return m_sets[index]; + } -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ + setinfo &set(unsigned index) noexcept + { + return m_sets[index]; + } -static void ui_gfx_count_devices(running_machine &machine, ui_gfx_state &state); -static void ui_gfx_exit(running_machine &machine); + private: + device_gfx_interface *m_interface; + unsigned m_setcount; + setinfo m_sets[MAX_GFX_ELEMENTS]; + }; -// 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(mame_ui_manager &mui, render_container &container, ui_gfx_state &state); + gfxset(running_machine &machine) + { + // get useful defaults + uint8_t const rotate = machine.system().flags & machine_flags::MASK_ORIENTATION; + device_palette_interface *const first_palette = palette_interface_enumerator(machine.root_device()).first(); -// 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, device_palette_interface *dpalette); -static void gfxset_update_bitmap(running_machine &machine, ui_gfx_state &state, int xcells, int ycells, gfx_element &gfx); -static void gfxset_handler(mame_ui_manager &mui, render_container &container, ui_gfx_state &state); + // iterate over graphics decoders + for (device_gfx_interface &interface : gfx_interface_enumerator(machine.root_device())) + { + // if there are any exposed graphics sets, add the device + if (interface.gfx(0U)) + m_devices.emplace_back(interface, first_palette, rotate); + } + } -// 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(mame_ui_manager &mui, render_container &container, ui_gfx_state &state); + bool has_gfx() const noexcept + { + return !m_devices.empty(); + } + bool handle_keys(running_machine &machine, int xcells, int ycells); + std::vector<devinfo> m_devices; + unsigned m_device = 0U; + unsigned m_set = 0U; -/*************************************************************************** - CORE IMPLEMENTATION -***************************************************************************/ + private: + bool next_group() noexcept + { + if ((m_devices[m_device].setcount() - 1) > m_set) + { + ++m_set; + return true; + } + else if ((m_devices.size() - 1) > m_device) + { + ++m_device; + m_set = 0U; + return true; + } + else + { + return false; + } + } -//------------------------------------------------- -// ui_gfx_init - initialize the graphics viewer -//------------------------------------------------- + bool prev_group() noexcept + { + if (m_set) + { + --m_set; + return true; + } + else if (m_device) + { + --m_device; + m_set = m_devices[m_device].setcount() - 1; + return true; + } + else + { + return false; + } + } + }; -void ui_gfx_init(running_machine &machine) -{ - ui_gfx_state &state = ui_gfx; - uint8_t rotate = machine.system().flags & machine_flags::MASK_ORIENTATION; - - // make sure we clean up after ourselves - machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&ui_gfx_exit, &machine)); - - // initialize our global state - state.started = false; - state.mode = 0; - state.bitmap_dirty = false; - state.bitmap.reset(); - state.texture = nullptr; - state.gfxset.devcount = 0; - state.gfxset.devindex = 0; - state.gfxset.set = 0; - - // set up the palette state - state.palette.interface = nullptr; - state.palette.devcount = 0; - state.palette.devindex = 0; - state.palette.which = 0; - state.palette.columns = 16; - - // set up the graphics state - for (uint8_t i = 0; i < MAX_GFX_DECODERS; i++) + class tilemap { - state.gfxdev[i].interface = nullptr; - state.gfxdev[i].setcount = 0; - for (uint8_t j = 0; j < MAX_GFX_ELEMENTS; j++) + public: + tilemap(running_machine &machine) { - state.gfxdev[i].rotate[j] = rotate; - state.gfxdev[i].columns[j] = 16; - state.gfxdev[i].offset[j] = 0; - state.gfxdev[i].color[j] = 0; - state.gfxdev[i].palette[j] = nullptr; - state.gfxdev[i].color_count[j] = 0; + uint8_t const rotate = machine.system().flags & machine_flags::MASK_ORIENTATION; + m_info.resize(machine.tilemap().count()); + for (auto &info : m_info) + info.m_rotate = rotate; } - } - // set up the tilemap state - state.tilemap.which = 0; - state.tilemap.xoffs = 0; - state.tilemap.yoffs = 0; - state.tilemap.zoom = 0; - state.tilemap.rotate = rotate; - state.tilemap.flags = TILEMAP_DRAW_ALL_CATEGORIES; -} + unsigned index() const noexcept + { + return m_index; + } + float zoom_scale() const noexcept + { + auto const &info = m_info[m_index]; + return info.m_zoom_frac ? (1.0f / float(info.m_zoom)) : float(info.m_zoom); + } -//------------------------------------------------- -// ui_gfx_count_devices - count the palettes, -// gfx decoders and gfx sets in the machine -//------------------------------------------------- + bool auto_zoom() const noexcept + { + return m_info[m_index].m_auto_zoom; + } -static void ui_gfx_count_devices(running_machine &machine, ui_gfx_state &state) -{ - // count the palette devices - state.palette.devcount = palette_interface_iterator(machine.root_device()).count(); + uint8_t rotate() const noexcept + { + return m_info[m_index].m_rotate; + } + + uint32_t flags() const noexcept + { + return m_info[m_index].m_flags; + } - // set the pointer to the first palette - if (state.palette.devcount > 0) - palette_set_device(machine, state); + int xoffs() const noexcept + { + return m_info[m_index].m_xoffs; + } - // count the gfx devices - state.gfxset.devcount = 0; - for (device_gfx_interface &interface : gfx_interface_iterator(machine.root_device())) - { - // count the gfx sets in each device, skipping devices with none - uint8_t count = 0; - while (count < MAX_GFX_ELEMENTS && interface.gfx(count) != nullptr) - count++; + int yoffs() const noexcept + { + return m_info[m_index].m_yoffs; + } + + bool handle_keys(running_machine &machine, float pixelscale); + + private: + static constexpr int MAX_ZOOM_LEVEL = 8; // maximum tilemap zoom ratio screen:native + static constexpr int MIN_ZOOM_LEVEL = 8; // minimum tilemap zoom ratio native:screen - // count = index of first nullptr - if (count > 0) + struct info { - state.gfxdev[state.gfxset.devcount].interface = &interface; - state.gfxdev[state.gfxset.devcount].setcount = count; - for (uint8_t slot = 0; slot != count; slot++) { - auto gfx = interface.gfx(slot); - if (gfx->has_palette()) + bool zoom_in(float pixelscale) noexcept + { + if (m_auto_zoom) + { + // auto zoom never uses fractional factors + m_zoom = std::min<int>(std::lround(pixelscale) + 1, MAX_ZOOM_LEVEL); + m_zoom_frac = false; + m_auto_zoom = false; + return true; + } + else if (m_zoom_frac) + { + m_zoom--; + if (m_zoom == 1) + m_zoom_frac = false; // entering integer zoom range + return true; + } + else if (MAX_ZOOM_LEVEL > m_zoom) { - state.gfxdev[state.gfxset.devcount].palette[slot] = &gfx->palette(); - state.gfxdev[state.gfxset.devcount].color_count[slot] = gfx->colors(); + m_zoom++; // remaining in integer zoom range + return true; } else { - state.gfxdev[state.gfxset.devcount].palette[slot] = state.palette.interface; - state.gfxdev[state.gfxset.devcount].color_count[slot] = state.palette.interface->entries() / gfx->granularity(); - if (!state.gfxdev[state.gfxset.devcount].color_count[slot]) - state.gfxdev[state.gfxset.devcount].color_count[slot] = 1; + return false; } } - if (++state.gfxset.devcount == MAX_GFX_DECODERS) - break; + + bool zoom_out(float pixelscale) noexcept + { + if (m_auto_zoom) + { + // auto zoom never uses fractional factors + m_zoom = std::lround(pixelscale) - 1; + m_zoom_frac = !m_zoom; + if (m_zoom_frac) + m_zoom = 2; + m_auto_zoom = false; + return true; + } + else if (!m_zoom_frac) + { + if (m_zoom == 1) + { + m_zoom++; + m_zoom_frac = true; // entering fractional zoom range + } + else + { + m_zoom--; // remaining in integer zoom range + } + return true; + } + else if (MIN_ZOOM_LEVEL > m_zoom) + { + m_zoom++; // remaining in fractional zoom range + return true; + } + else + { + return false; + } + } + + bool next_category() noexcept + { + if (TILEMAP_DRAW_ALL_CATEGORIES == m_flags) + { + m_flags = 0U; + return true; + } + else if (TILEMAP_DRAW_CATEGORY_MASK > m_flags) + { + ++m_flags; + return true; + } + else + { + return false; + } + } + + bool prev_catagory() noexcept + { + if (!m_flags) + { + m_flags = TILEMAP_DRAW_ALL_CATEGORIES; + return true; + } + else if (TILEMAP_DRAW_ALL_CATEGORIES != m_flags) + { + --m_flags; + return true; + } + else + { + return false; + } + } + + int m_xoffs = 0; + int m_yoffs = 0; + unsigned m_zoom = 1U; + bool m_zoom_frac = false; + bool m_auto_zoom = true; + uint8_t m_rotate = 0U; + uint32_t m_flags = TILEMAP_DRAW_ALL_CATEGORIES; + }; + + static int scroll_step(running_machine &machine) + { + auto &input = machine.input(); + if (input.code_pressed(KEYCODE_LCONTROL) || input.code_pressed(KEYCODE_RCONTROL)) + return 64; + else if (input.code_pressed(KEYCODE_LSHIFT) || input.code_pressed(KEYCODE_RSHIFT)) + return 1; + else + return 8; } + + std::vector<info> m_info; + unsigned m_index = 0U; + }; + + bool is_relevant() const noexcept + { + return m_palette.interface() || m_gfxset.has_gfx() || m_machine.tilemap().count(); } - state.started = true; -} + uint32_t handle_general_keys(bool uistate) + { + auto &input = m_machine.ui_input(); + + // UI select cycles through views + if (input.pressed(IPT_UI_SELECT)) + { + m_mode = view((int(m_mode) + 1) % 3); + if (0 > m_current_pointer) + { + m_pointer_type = ui_event::pointer::UNKNOWN; + m_pointer_x = -1.0F; + m_pointer_x = -1.0F; + m_pointer_inside = false; + } + m_bitmap_dirty = true; + } + // pause does what you'd expect + if (input.pressed(IPT_UI_PAUSE)) + { + if (m_machine.paused()) + m_machine.resume(); + else + m_machine.pause(); + } -//------------------------------------------------- -// ui_gfx_exit - clean up after ourselves -//------------------------------------------------- + // cancel or graphics viewer dismisses the viewer + if (input.pressed(IPT_UI_BACK) || input.pressed(IPT_UI_SHOW_GFX)) + return cancel(uistate); -static void ui_gfx_exit(running_machine &machine) -{ - // free the texture - machine.render().texture_free(ui_gfx.texture); - ui_gfx.texture = nullptr; + return uistate; + } - // free the bitmap - ui_gfx.bitmap.reset(); -} + uint32_t cancel(bool uistate) + { + if (!uistate) + m_machine.resume(); + m_machine.ui_input().reset(); + m_current_pointer = -1; + m_pointer_type = ui_event::pointer::UNKNOWN; + m_pointer_buttons = 0U; + m_pointer_x = -1.0F; + m_pointer_y = -1.0F; + m_pointer_inside = false; + m_bitmap_dirty = true; + return mame_ui_manager::HANDLER_CANCEL; + } + uint32_t handle_palette(mame_ui_manager &mui, render_container &container, bool uistate); + uint32_t handle_gfxset(mame_ui_manager &mui, render_container &container, bool uistate); + uint32_t handle_tilemap(mame_ui_manager &mui, render_container &container, bool uistate); -//------------------------------------------------- -// 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 -//------------------------------------------------- + void update_gfxset_bitmap(int xcells, int ycells, gfx_element &gfx); + void update_tilemap_bitmap(int width, int height); -bool ui_gfx_is_relevant(running_machine &machine) -{ - ui_gfx_state &state = ui_gfx; + void gfxset_draw_item(gfx_element &gfx, int index, int dstx, int dsty, gfxset::setinfo const &info); - if (!state.started) - ui_gfx_count_devices(machine, state); + void draw_text(mame_ui_manager &mui, render_container &container, std::string_view str, float x, float y) + { + render_font *const font = mui.get_font(); + float const height = mui.get_line_height(); + float const aspect = m_machine.render().ui_aspect(&container); + rgb_t const color = mui.colors().text_color(); + + int n; + char32_t ch; + while ((n = uchar_from_utf8(&ch, str)) != 0) + { + if (0 > n) + ch = 0xfffd; + str.remove_prefix((0 > n) ? 1 : n); + container.add_char(x, y, height, aspect, color, *font, ch); + x += font->char_width(height, aspect, ch); + } + } - return state.palette.devcount > 0 - || state.gfxset.devcount > 0 - || machine.tilemap().count() > 0; -} + void resize_bitmap(int32_t width, int32_t height) + { + if (!m_bitmap.valid() || !m_texture || (m_bitmap.width() != width) || (m_bitmap.height() != height)) + { + // free the old stuff + if (m_texture) + m_machine.render().texture_free(m_texture); + // allocate new stuff + m_bitmap.resize(width, height); + m_texture = m_machine.render().texture_alloc(); + m_texture->set_bitmap(m_bitmap, m_bitmap.cliprect(), TEXFORMAT_ARGB32); -//------------------------------------------------- -// ui_gfx_ui_handler - primary UI handler -//------------------------------------------------- + // force a redraw + m_bitmap_dirty = true; + } + } -uint32_t ui_gfx_ui_handler(render_container &container, mame_ui_manager &mui, bool uistate) + bool map_mouse(render_container &container, render_bounds const &clip, float &x, float &y) const + { + if (((0 > m_current_pointer) && (m_pointer_type != ui_event::pointer::TOUCH)) || !m_pointer_inside) + return false; + + x = m_pointer_x; + y = m_pointer_y; + return clip.includes(x, y); + } + + running_machine &m_machine; + view m_mode = view::PALETTE; + + s32 m_current_pointer = -1; + ui_event::pointer m_pointer_type = ui_event::pointer::UNKNOWN; + u32 m_pointer_buttons = 0U; + float m_pointer_x = -1.0F; + float m_pointer_y = -1.0F; + bool m_pointer_inside = false; + + bitmap_rgb32 m_bitmap; + render_texture *m_texture = nullptr; + bool m_bitmap_dirty = false; + + palette m_palette; + gfxset m_gfxset; + tilemap m_tilemap; +}; + + +void gfx_viewer::palette::handle_keys(running_machine &machine) { - ui_gfx_state &state = ui_gfx; + auto &input = machine.ui_input(); - // if we have nothing, implicitly cancel - if (!ui_gfx_is_relevant(mui.machine())) - goto cancel; + // handle zoom (minus,plus) + if (input.pressed(IPT_UI_ZOOM_OUT)) + m_columns = std::min<unsigned>(m_columns * 2, 64); + if (input.pressed(IPT_UI_ZOOM_IN)) + m_columns = std::max<unsigned>(m_columns / 2, 4); + if (input.pressed(IPT_UI_ZOOM_DEFAULT)) + m_columns = 16; - // if we're not paused, mark the bitmap dirty - if (!mui.machine().paused()) - state.bitmap_dirty = true; + // handle colormap selection (open bracket,close bracket) + if (input.pressed(IPT_UI_PREV_GROUP)) + prev_group(machine); + if (input.pressed(IPT_UI_NEXT_GROUP)) + next_group(machine); - // 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(mui, container, state); - break; - } + // cache some info in locals + int const total = (subset::INDIRECT == m_which) ? m_interface->indirect_entries() : m_interface->entries(); - // fall through... - state.mode++; + // determine number of entries per row and total + int const rowcount = m_columns; + int const screencount = rowcount * rowcount; - case UI_GFX_GFXSET: - // if we have graphics sets, display them - if (state.gfxset.devcount > 0) - { - gfxset_handler(mui, container, state); - break; - } + // handle keyboard navigation + if (input.pressed_repeat(IPT_UI_UP, 4)) + m_offset -= rowcount; + if (input.pressed_repeat(IPT_UI_DOWN, 4)) + m_offset += rowcount; + if (input.pressed_repeat(IPT_UI_PAGE_UP, 6)) + m_offset -= screencount; + if (input.pressed_repeat(IPT_UI_PAGE_DOWN, 6)) + m_offset += screencount; + if (input.pressed_repeat(IPT_UI_HOME, 4)) + m_offset = 0; + if (input.pressed_repeat(IPT_UI_END, 4)) + m_offset = total; - // fall through... - state.mode++; + // clamp within range + if (m_offset + screencount > ((total + rowcount - 1) / rowcount) * rowcount) + m_offset = ((total + rowcount - 1) / rowcount) * rowcount - screencount; + if (m_offset < 0) + m_offset = 0; +} - case UI_GFX_TILEMAP: - // if we have tilemaps, display them - if (mui.machine().tilemap().count() > 0) - { - tilemap_handler(mui, container, state); - break; - } - state.mode = UI_GFX_PALETTE; - goto again; +bool gfx_viewer::gfxset::handle_keys(running_machine &machine, int xcells, int ycells) +{ + auto &input = machine.ui_input(); + bool const shift_pressed = machine.input().code_pressed(KEYCODE_LSHIFT) || machine.input().code_pressed(KEYCODE_RSHIFT); + bool result = false; + + // handle previous/next group + if (input.pressed(IPT_UI_PREV_GROUP) && prev_group()) + result = true; + if (input.pressed(IPT_UI_NEXT_GROUP) && next_group()) + result = true; + + auto &info = m_devices[m_device]; + auto &set = info.set(m_set); + auto &gfx = *info.interface().gfx(m_set); + + // handle cells per line (0/-/=) + if (input.pressed(IPT_UI_ZOOM_OUT) && (xcells < 128)) + { + set.m_columns = xcells + 1; + set.m_integer_scale = shift_pressed; + result = true; + } + if (input.pressed(IPT_UI_ZOOM_IN) && (xcells > 2)) + { + set.m_columns = xcells - 1; + set.m_integer_scale = shift_pressed; + result = true; + } + if (input.pressed(IPT_UI_ZOOM_DEFAULT) && ((xcells != 16) || (set.m_integer_scale != shift_pressed))) + { + set.m_columns = 16; + set.m_integer_scale = shift_pressed; + result = true; } - // handle keys - if (mui.machine().ui_input().pressed(IPT_UI_SELECT)) + // handle rotation (R) + if (input.pressed(IPT_UI_ROTATE)) { - state.mode = (state.mode + 1) % 3; - state.bitmap_dirty = true; + set.m_rotate = orientation_add(ROT90, set.m_rotate); + result = true; } - if (mui.machine().ui_input().pressed(IPT_UI_PAUSE)) + // handle navigation within the cells (up,down,pgup,pgdown) + if (input.pressed_repeat(IPT_UI_UP, 4)) { - if (mui.machine().paused()) - mui.machine().resume(); - else - mui.machine().pause(); + set.m_offset -= xcells; + result = true; + } + if (input.pressed_repeat(IPT_UI_DOWN, 4)) + { + set.m_offset += xcells; + result = true; + } + if (input.pressed_repeat(IPT_UI_PAGE_UP, 6)) + { + set.m_offset -= xcells * ycells; + result = true; + } + if (input.pressed_repeat(IPT_UI_PAGE_DOWN, 6)) + { + set.m_offset += xcells * ycells; + result = true; + } + if (input.pressed_repeat(IPT_UI_HOME, 4)) + { + set.m_offset = 0; + result = true; + } + if (input.pressed_repeat(IPT_UI_END, 4)) + { + set.m_offset = gfx.elements(); + result = true; } - if (mui.machine().ui_input().pressed(IPT_UI_CANCEL) || mui.machine().ui_input().pressed(IPT_UI_SHOW_GFX)) - goto cancel; + // clamp within range + if (set.m_offset + xcells * ycells > ((gfx.elements() + xcells - 1) / xcells) * xcells) + { + set.m_offset = ((gfx.elements() + xcells - 1) / xcells) * xcells - xcells * ycells; + result = true; + } + if (set.m_offset < 0) + { + set.m_offset = 0; + result = true; + } - return uistate; + // handle color selection (left,right) + if (input.pressed_repeat(IPT_UI_LEFT, 4)) + { + set.prev_color(); + result = true; + } + if (input.pressed_repeat(IPT_UI_RIGHT, 4)) + { + set.next_color(); + result = true; + } -cancel: - if (!uistate) - mui.machine().resume(); - state.bitmap_dirty = true; - return UI_HANDLER_CANCEL; + return result; } +bool gfx_viewer::tilemap::handle_keys(running_machine &machine, float pixelscale) +{ + auto &input = machine.ui_input(); + bool result = false; -/*************************************************************************** - PALETTE VIEWER -***************************************************************************/ + // handle tilemap selection (open bracket,close bracket) + if (input.pressed(IPT_UI_PREV_GROUP) && m_index > 0) + { + m_index--; + result = true; + } + if (input.pressed(IPT_UI_NEXT_GROUP) && ((m_info.size() - 1) > m_index)) + { + m_index++; + result = true; + } -//------------------------------------------------- -// palette_set_device - set the pointer to the -// current palette device -//------------------------------------------------- + auto &info = m_info[m_index]; -static void palette_set_device(running_machine &machine, ui_gfx_state &state) -{ - palette_interface_iterator pal_iter(machine.root_device()); - state.palette.interface = pal_iter.byindex(state.palette.devindex); -} + // handle zoom (minus,plus) + if (input.pressed(IPT_UI_ZOOM_OUT) && info.zoom_out(pixelscale)) + { + result = true; + machine.popmessage(info.m_zoom_frac ? _("gfxview", "Zoom = 1/%1$d") : _("gfxview", "Zoom = %1$d"), info.m_zoom); + } + if (input.pressed(IPT_UI_ZOOM_IN) && info.zoom_in(pixelscale)) + { + result = true; + machine.popmessage(info.m_zoom_frac ? _("gfxview", "Zoom = 1/%1$d") : _("gfxview", "Zoom = %1$d"), info.m_zoom); + } + if (input.pressed(IPT_UI_ZOOM_DEFAULT) && !info.m_auto_zoom) + { + info.m_auto_zoom = true; + machine.popmessage(_("gfxview", "Expand to fit")); + } + // handle rotation (R) + if (input.pressed(IPT_UI_ROTATE)) + { + info.m_rotate = orientation_add(ROT90, info.m_rotate); + result = true; + } -//------------------------------------------------- -// palette_handler - handler for the palette -// viewer -//------------------------------------------------- + // return to (0,0) (HOME) + if (input.pressed(IPT_UI_HOME)) + { + info.m_xoffs = 0; + info.m_yoffs = 0; + result = true; + } + + // handle flags (category) + if (input.pressed(IPT_UI_PAGE_UP) && info.prev_catagory()) + { + result = true; + if (TILEMAP_DRAW_ALL_CATEGORIES == info.m_flags) + machine.popmessage(_("gfxview", "All categories")); + else + machine.popmessage(_("gfxview", "Category %1$d"), info.m_flags); + } + if (input.pressed(IPT_UI_PAGE_DOWN) && info.next_category()) + { + result = true; + machine.popmessage(_("gfxview", "Category %1$d"), info.m_flags); + } -static void palette_handler(mame_ui_manager &mui, render_container &container, ui_gfx_state &state) + // handle navigation (up,down,left,right), taking orientation into account + int const step = scroll_step(machine); // this may be applied more than once if multiple directions are pressed + if (input.pressed_repeat(IPT_UI_UP, 4)) + { + if (info.m_rotate & ORIENTATION_SWAP_XY) + info.m_xoffs -= (info.m_rotate & ORIENTATION_FLIP_Y) ? -step : step; + else + info.m_yoffs -= (info.m_rotate & ORIENTATION_FLIP_Y) ? -step : step; + result = true; + } + if (input.pressed_repeat(IPT_UI_DOWN, 4)) + { + if (info.m_rotate & ORIENTATION_SWAP_XY) + info.m_xoffs += (info.m_rotate & ORIENTATION_FLIP_Y) ? -step : step; + else + info.m_yoffs += (info.m_rotate & ORIENTATION_FLIP_Y) ? -step : step; + result = true; + } + if (input.pressed_repeat(IPT_UI_LEFT, 6)) + { + if (info.m_rotate & ORIENTATION_SWAP_XY) + info.m_yoffs -= (info.m_rotate & ORIENTATION_FLIP_X) ? -step : step; + else + info.m_xoffs -= (info.m_rotate & ORIENTATION_FLIP_X) ? -step : step; + result = true; + } + if (input.pressed_repeat(IPT_UI_RIGHT, 6)) + { + if (info.m_rotate & ORIENTATION_SWAP_XY) + info.m_yoffs += (info.m_rotate & ORIENTATION_FLIP_X) ? -step : step; + else + info.m_xoffs += (info.m_rotate & ORIENTATION_FLIP_X) ? -step : step; + result = true; + } + + // cache some info in locals + tilemap_t *const tilemap = machine.tilemap().find(m_index); + uint32_t const mapwidth = tilemap->width(); + uint32_t const mapheight = tilemap->height(); + + // clamp within range + while (info.m_xoffs < 0) + info.m_xoffs += mapwidth; + while (info.m_xoffs >= mapwidth) + info.m_xoffs -= mapwidth; + while (info.m_yoffs < 0) + info.m_yoffs += mapheight; + while (info.m_yoffs >= mapheight) + info.m_yoffs -= mapheight; + + return result; +} + + +uint32_t gfx_viewer::handle_palette(mame_ui_manager &mui, render_container &container, bool uistate) { - device_palette_interface *palette = state.palette.interface; - palette_device *paldev = dynamic_cast<palette_device *>(&palette->device()); + device_palette_interface &palette = *m_palette.interface(); + palette_device *const paldev = dynamic_cast<palette_device *>(&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 = mui.get_font(); - float titlewidth; - float x0, y0; - render_bounds cellboxbounds; - render_bounds boxbounds; - int skip; + bool const indirect = m_palette.indirect(); + unsigned const total = indirect ? palette.indirect_entries() : palette.entries(); + rgb_t const *const raw_color = palette.palette()->entry_list_raw(); // add a half character padding for the box - const float aspect = mui.machine().render().ui_aspect(&container); - const float chheight = mui.get_line_height(); - const float chwidth = ui_font->char_width(chheight, 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; + render_font *const ui_font = mui.get_font(); + float const aspect = m_machine.render().ui_aspect(&container); + float const chheight = mui.get_line_height(); + float const chwidth = ui_font->char_width(chheight, aspect, '0'); + render_bounds const boxbounds{ + 0.0f + (0.5f * chwidth), + 0.0f + (0.5f * chheight), + 1.0f - (0.5f * chwidth), + 1.0f - (0.5f * chheight) }; // the character cell box bounds starts a half character in from the box - cellboxbounds = boxbounds; + render_bounds cellboxbounds = boxbounds; cellboxbounds.x0 += 0.5f * chwidth; - cellboxbounds.x1 -= 0.5f * chwidth; cellboxbounds.y0 += 0.5f * chheight; + cellboxbounds.x1 -= 0.5f * chwidth; cellboxbounds.y1 -= 0.5f * chheight; // add space on the left for 5 characters of text, plus a half character of padding @@ -420,41 +1015,57 @@ static void palette_handler(mame_ui_manager &mui, render_container &container, u cellboxbounds.y0 += 3.0f * chheight; // compute the cell size - const float cellwidth = (cellboxbounds.x1 - cellboxbounds.x0) / float(state.palette.columns); - const float cellheight = (cellboxbounds.y1 - cellboxbounds.y0) / float(state.palette.columns); + float const cellwidth = (cellboxbounds.x1 - cellboxbounds.x0) / float(m_palette.columns()); + float const cellheight = (cellboxbounds.y1 - cellboxbounds.y0) / float(m_palette.columns()); // figure out the title std::ostringstream title_buf; - util::stream_format(title_buf, "'%s'", palette->device().tag()); - if (palette->indirect_entries() > 0) - title_buf << (state.palette.which ? _(" COLORS") : _(" PENS")); + util::stream_format(title_buf, + !palette.indirect_entries() ? _("gfxview", "[root%1$s]") : indirect ? _("gfxview", "[root%1$s] colors") : _("gfxview", "[root%1$s] pens"), + palette.device().tag()); // if the mouse pointer is over one of our cells, add some info about the corresponding palette entry - int32_t mouse_target_x, mouse_target_y; float mouse_x, mouse_y; - bool mouse_button; - render_target *const mouse_target = mui.machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button); - if (mouse_target != nullptr && mouse_target->map_point_container(mouse_target_x, mouse_target_y, container, mouse_x, mouse_y) - && cellboxbounds.x0 <= mouse_x && cellboxbounds.x1 > mouse_x - && cellboxbounds.y0 <= mouse_y && cellboxbounds.y1 > mouse_y) + if (map_mouse(container, cellboxbounds, mouse_x, mouse_y)) { - int index = state.palette.offset + int((mouse_x - cellboxbounds.x0) / cellwidth) + int((mouse_y - cellboxbounds.y0) / cellheight) * state.palette.columns; + int const index = m_palette.index(int((mouse_x - cellboxbounds.x0) / cellwidth), int((mouse_y - cellboxbounds.y0) / cellheight)); if (index < total) { - util::stream_format(title_buf, " #%X", index); - if (palette->indirect_entries() > 0 && !state.palette.which) - util::stream_format(title_buf, " => %X", palette->pen_indirect(index)); - else if (paldev != nullptr && paldev->basemem().base() != nullptr) - util::stream_format(title_buf, " = %X", paldev->read_entry(index)); - - rgb_t col = state.palette.which ? palette->indirect_color(index) : raw_color[index]; - util::stream_format(title_buf, " (A:%X R:%X G:%X B:%X)", col.a(), col.r(), col.g(), col.b()); + rgb_t const col = indirect ? palette.indirect_color(index) : raw_color[index]; + if (palette.indirect_entries() && indirect) + { + util::stream_format(title_buf, + _("gfxview", u8" #%1$X \u2192 %2$X (A:%3$02X R:%4$02X G:%5$02X B:%6$02X)"), + index, palette.pen_indirect(index), + col.a(), col.r(), col.g(), col.b()); + } + else if (paldev && paldev->basemem().base()) + { + util::stream_format(title_buf, + _("gfxview", " #%1$X = %2$X (A:%3$02X R:%4$02X G:%5$02X B:%6$02X)"), + index, paldev->read_entry(index), + col.a(), col.r(), col.g(), col.b()); + } + else + { + util::stream_format(title_buf, + _("gfxview", " #%1$X (A:%2$02X R:%3$02X G:%4$02X B:%5$02X)"), + index, + col.a(), col.r(), col.g(), col.b()); + } + + // keep touch pointer displayed after release so they know what it's pointing at + mame_ui_manager::display_pointer pointers[1]{ { m_machine.render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } }; + if (ui_event::pointer::TOUCH == m_pointer_type) + mui.set_pointers(std::begin(pointers), std::end(pointers)); } } + float x0, y0; + // expand the outer box to fit the title - const std::string title = title_buf.str(); - titlewidth = ui_font->string_width(chheight, aspect, title.c_str()); + std::string const title = std::move(title_buf).str(); + float const titlewidth = ui_font->string_width(chheight, aspect, title); x0 = 0.0f; if (boxbounds.x1 - boxbounds.x0 < titlewidth + chwidth) x0 = boxbounds.x0 - (0.5f - 0.5f * (titlewidth + chwidth)); @@ -463,193 +1074,97 @@ static void palette_handler(mame_ui_manager &mui, render_container &container, u mui.draw_outlined_box(container, boxbounds.x0 - x0, boxbounds.y0, boxbounds.x1 + x0, boxbounds.y1, mui.colors().gfxviewer_bg_color()); // draw the title - x0 = 0.5f - 0.5f * titlewidth; - y0 = boxbounds.y0 + 0.5f * chheight; - for (auto ch : title) - { - container.add_char(x0, y0, chheight, aspect, rgb_t::white(), *ui_font, ch); - x0 += ui_font->char_width(chheight, aspect, ch); - } + draw_text(mui, container, title, 0.5f - 0.5f * titlewidth, boxbounds.y0 + 0.5f * chheight); // draw the top column headers - skip = int(chwidth / cellwidth); - for (int x = 0; x < state.palette.columns; x += 1 + skip) + int const rowskip = int(chwidth / cellwidth); + for (int x = 0; x < m_palette.columns(); x += 1 + rowskip) { - x0 = boxbounds.x0 + 6.0f * chwidth + (float)x * cellwidth; + 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, aspect, rgb_t::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) + // if we're skipping, draw a point between the character and the box to indicate which one it's referring to + if (rowskip) container.add_point(x0 + 0.5f * cellwidth, 0.5f * (y0 + chheight + cellboxbounds.y0), UI_LINE_WIDTH, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); } // draw the side column headers - skip = int(chheight / cellheight); - for (int y = 0; y < state.palette.columns; y += 1 + skip) - + int const colskip = int(chheight / cellheight); + for (int y = 0; y < m_palette.columns(); y += 1 + colskip) + { // only display if there is data to show - if (state.palette.offset + y * state.palette.columns < total) + unsigned const index = m_palette.index(0, y); + if (index < 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) + y0 = boxbounds.y0 + 3.5f * chheight + float(y) * cellheight; + if (colskip != 0) container.add_point(0.5f * (x0 + cellboxbounds.x0), y0 + 0.5f * cellheight, UI_LINE_WIDTH, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); // draw the row header - sprintf(buffer, "%5X", state.palette.offset + y * state.palette.columns); + auto buffer = util::string_format("%5X", index); for (int x = 4; x >= 0; x--) { x0 -= ui_font->char_width(chheight, aspect, buffer[x]); container.add_char(x0, y0 + 0.5f * (cellheight - chheight), chheight, aspect, rgb_t::white(), *ui_font, buffer[x]); } } + } // now add the rectangles for the colors - for (int y = 0; y < state.palette.columns; y++) - for (int x = 0; x < state.palette.columns; x++) + for (int y = 0; y < m_palette.columns(); y++) + { + for (int x = 0; x < m_palette.columns(); x++) { - int index = state.palette.offset + y * state.palette.columns + x; + int const index = m_palette.index(x, y); if (index < total) { - const 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)); + pen_t const pen = indirect ? 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(mui.machine(), state); -} - - -//------------------------------------------------- -// palette_handle_keys - handle key inputs for -// the palette viewer -//------------------------------------------------- - -static void palette_handle_keys(running_machine &machine, ui_gfx_state &state) -{ - device_palette_interface *palette = state.palette.interface; - int rowcount, screencount; - int total; - - // handle zoom (minus,plus) - if (machine.ui_input().pressed(IPT_UI_ZOOM_OUT)) - state.palette.columns /= 2; - if (machine.ui_input().pressed(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 (machine.ui_input().pressed(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.interface; - state.palette.which = (palette->indirect_entries() > 0); - } - } - if (machine.ui_input().pressed(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.interface; - 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 (machine.ui_input().pressed_repeat(IPT_UI_UP, 4)) - state.palette.offset -= rowcount; - if (machine.ui_input().pressed_repeat(IPT_UI_DOWN, 4)) - state.palette.offset += rowcount; - if (machine.ui_input().pressed_repeat(IPT_UI_PAGE_UP, 6)) - state.palette.offset -= screencount; - if (machine.ui_input().pressed_repeat(IPT_UI_PAGE_DOWN, 6)) - state.palette.offset += screencount; - if (machine.ui_input().pressed_repeat(IPT_UI_HOME, 4)) - state.palette.offset = 0; - if (machine.ui_input().pressed_repeat(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; + // handle keys + m_palette.handle_keys(m_machine); + return handle_general_keys(uistate); } - -/*************************************************************************** - GRAPHICS VIEWER -***************************************************************************/ - -//------------------------------------------------- -// gfxset_handler - handler for the graphics -// viewer -//------------------------------------------------- - -static void gfxset_handler(mame_ui_manager &mui, render_container &container, ui_gfx_state &state) +uint32_t gfx_viewer::handle_gfxset(mame_ui_manager &mui, render_container &container, bool uistate) { - render_font *ui_font = mui.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 x0, y0; - render_bounds cellboxbounds; - render_bounds boxbounds; - int cellboxwidth, cellboxheight; - int targwidth = mui.machine().render().ui_target().width(); - int targheight = mui.machine().render().ui_target().height(); - int cellxpix, cellypix; - int xcells; - int pixelscale = 0; - int skip; + // get graphics info + auto &info = m_gfxset.m_devices[m_gfxset.m_device]; + auto &set = info.set(m_gfxset.m_set); + device_gfx_interface &interface = info.interface(); + gfx_element &gfx = *interface.gfx(m_gfxset.m_set); + + // get some UI metrics + render_font *const ui_font = mui.get_font(); + int const targwidth = m_machine.render().ui_target().width(); + int const targheight = m_machine.render().ui_target().height(); + float const aspect = m_machine.render().ui_aspect(&container); + float const chheight = mui.get_line_height(); + float const chwidth = ui_font->char_width(chheight, aspect, '0'); // add a half character padding for the box - const float aspect = mui.machine().render().ui_aspect(&container); - const float chheight = mui.get_line_height(); - const float chwidth = ui_font->char_width(chheight, 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; + render_bounds boxbounds{ + 0.0f + (0.5f * chwidth), + 0.0f + (0.5f * chheight), + 1.0f - (0.5f * chwidth), + 1.0f - (0.5f * chheight) }; // the character cell box bounds starts a half character in from the box - cellboxbounds = boxbounds; + render_bounds cellboxbounds = boxbounds; cellboxbounds.x0 += 0.5f * chwidth; - cellboxbounds.x1 -= 0.5f * chwidth; cellboxbounds.y0 += 0.5f * chheight; + cellboxbounds.x1 -= 0.5f * chwidth; cellboxbounds.y1 -= 0.5f * chheight; // add space on the left for 5 characters of text, plus a half character of padding @@ -659,93 +1174,110 @@ static void gfxset_handler(mame_ui_manager &mui, render_container &container, ui cellboxbounds.y0 += 3.0f * chheight; // convert back to pixels - cellboxwidth = (cellboxbounds.x1 - cellboxbounds.x0) * float(targwidth); - cellboxheight = (cellboxbounds.y1 - cellboxbounds.y0) * float(targheight); + float cellboxwidth = cellboxbounds.width() * float(targwidth); + float cellboxheight = cellboxbounds.height() * 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()); + int const cellxpix = 1 + ((set.m_rotate & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width()); + int const cellypix = 1 + ((set.m_rotate & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height()); // compute the largest pixel scale factor that still fits - xcells = info.columns[set]; + int xcells = set.m_columns; + float pixelscale = 0.0f; while (xcells > 1) { - pixelscale = (cellboxwidth / xcells) / cellxpix; - if (pixelscale != 0) + pixelscale = cellboxwidth / (xcells * cellxpix); + if (set.m_integer_scale) + pixelscale = std::floor(pixelscale); + if (0.25f <= pixelscale) break; xcells--; } - info.columns[set] = xcells; - - // worst case, we need a pixel scale of 1 - pixelscale = std::max(1, pixelscale); + if (0.0f == pixelscale) + pixelscale = cellboxwidth / (xcells * cellxpix); // in the Y direction, we just display as many as we can - const int ycells = cellboxheight / (pixelscale * cellypix); + int ycells = int(cellboxheight / (pixelscale * cellypix)); + if (!ycells) + { + ycells = 1; + pixelscale = cellboxheight / cellypix; + xcells = int(cellboxwidth / (pixelscale * cellxpix)); + } // now determine the actual cellbox size + set.m_columns = xcells; cellboxwidth = std::min(cellboxwidth, xcells * pixelscale * cellxpix); cellboxheight = std::min(cellboxheight, ycells * pixelscale * cellypix); - // compute the size of a single cell at this pixel scale factor, as well as the aspect ratio - const float cellwidth = (cellboxwidth / float(xcells)) / float(targwidth); - const float cellheight = (cellboxheight / float(ycells)) / float(targheight); - //cellaspect = cellwidth / cellheight; + // compute the size of a single cell at this pixel scale factor + float const cellwidth = (cellboxwidth / xcells) / targwidth; + float const cellheight = (cellboxheight / ycells) / targheight; // working from the new width/height, recompute the boxbounds - const float fullwidth = float(cellboxwidth) / float(targwidth) + 6.5f * chwidth; - const float fullheight = float(cellboxheight) / float(targheight) + 4.0f * chheight; + float const fullwidth = cellboxwidth / targwidth + 6.5f * chwidth; + float const fullheight = cellboxheight / 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.x1 = boxbounds.x0 + fullwidth; boxbounds.y1 = boxbounds.y0 + fullheight; // recompute cellboxbounds cellboxbounds.x0 = boxbounds.x0 + 6.0f * chwidth; - cellboxbounds.x1 = cellboxbounds.x0 + float(cellboxwidth) / float(targwidth); cellboxbounds.y0 = boxbounds.y0 + 3.5f * chheight; - cellboxbounds.y1 = cellboxbounds.y0 + float(cellboxheight) / float(targheight); + cellboxbounds.x1 = cellboxbounds.x0 + cellboxwidth / float(targwidth); + cellboxbounds.y1 = cellboxbounds.y0 + cellboxheight / float(targheight); // figure out the title std::ostringstream title_buf; - util::stream_format(title_buf, "'%s' %d/%d", interface.device().tag(), set, info.setcount - 1); + util::stream_format(title_buf, + _("gfxview", "[root%1$s] %2$d/%3$d"), + interface.device().tag(), + m_gfxset.m_set, info.setcount() - 1); // if the mouse pointer is over a pixel in a tile, add some info about the tile and pixel bool found_pixel = false; - int32_t mouse_target_x, mouse_target_y; float mouse_x, mouse_y; - bool mouse_button; - render_target *const mouse_target = mui.machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button); - if (mouse_target != nullptr && mouse_target->map_point_container(mouse_target_x, mouse_target_y, container, mouse_x, mouse_y) - && cellboxbounds.x0 <= mouse_x && cellboxbounds.x1 > mouse_x - && cellboxbounds.y0 <= mouse_y && cellboxbounds.y1 > mouse_y) + if (map_mouse(container, cellboxbounds, mouse_x, mouse_y)) { - int code = info.offset[set] + int((mouse_x - cellboxbounds.x0) / cellwidth) + int((mouse_y - cellboxbounds.y0) / cellheight) * xcells; + int const code = set.m_offset + int((mouse_x - cellboxbounds.x0) / cellwidth) + int((mouse_y - cellboxbounds.y0) / cellheight) * xcells; int xpixel = int((mouse_x - cellboxbounds.x0) / (cellwidth / cellxpix)) % cellxpix; int ypixel = int((mouse_y - cellboxbounds.y0) / (cellheight / cellypix)) % cellypix; - if (code < gfx.elements() && xpixel < (cellxpix - 1) && ypixel < (cellypix - 1)) + if ((code < gfx.elements()) && (xpixel < (cellxpix - 1)) && (ypixel < (cellypix - 1))) { found_pixel = true; - if (info.rotate[set] & ORIENTATION_FLIP_X) + if (set.m_rotate & ORIENTATION_FLIP_X) xpixel = (cellxpix - 2) - xpixel; - if (info.rotate[set] & ORIENTATION_FLIP_Y) + if (set.m_rotate & ORIENTATION_FLIP_Y) ypixel = (cellypix - 2) - ypixel; - if (info.rotate[set] & ORIENTATION_SWAP_XY) + if (set.m_rotate & ORIENTATION_SWAP_XY) std::swap(xpixel, ypixel); - uint8_t pixdata = gfx.get_data(code)[xpixel + ypixel * gfx.rowbytes()]; - util::stream_format(title_buf, " #%X:%X @ %d,%d = %X", - code, info.color[set], xpixel, ypixel, - gfx.colorbase() + info.color[set] * gfx.granularity() + pixdata); + uint8_t const pixdata = gfx.get_data(code)[xpixel + ypixel * gfx.rowbytes()]; + util::stream_format(title_buf, + _("gfxview", " #%1$X:%2$X (%3$d %4$d) = %5$X"), + code, set.m_color, + xpixel, ypixel, + gfx.colorbase() + (set.m_color * gfx.granularity()) + pixdata); + + // keep touch pointer displayed after release so they know what it's pointing at + mame_ui_manager::display_pointer pointers[1]{ { m_machine.render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } }; + if (ui_event::pointer::TOUCH == m_pointer_type) + mui.set_pointers(std::begin(pointers), std::end(pointers)); } } if (!found_pixel) - util::stream_format(title_buf, " %dx%d COLOR %X/%X", gfx.width(), gfx.height(), info.color[set], info.color_count[set]); + util::stream_format(title_buf, + _("gfxview", u8" %1$d\u00d7%2$d color %3$X/%4$X"), + gfx.width(), gfx.height(), + set.m_color, set.m_color_count); + + float x0, y0; // expand the outer box to fit the title - const std::string title = title_buf.str(); - const float titlewidth = ui_font->string_width(chheight, aspect, title.c_str()); + std::string const title = std::move(title_buf).str(); + float const titlewidth = ui_font->string_width(chheight, aspect, title); x0 = 0.0f; if (boxbounds.x1 - boxbounds.x0 < titlewidth + chwidth) x0 = boxbounds.x0 - (0.5f - 0.5f * (titlewidth + chwidth)); @@ -754,326 +1286,85 @@ static void gfxset_handler(mame_ui_manager &mui, render_container &container, ui mui.draw_outlined_box(container, boxbounds.x0 - x0, boxbounds.y0, boxbounds.x1 + x0, boxbounds.y1, mui.colors().gfxviewer_bg_color()); // draw the title - x0 = 0.5f - 0.5f * titlewidth; - y0 = boxbounds.y0 + 0.5f * chheight; - for (auto ch : title) - { - container.add_char(x0, y0, chheight, aspect, rgb_t::white(), *ui_font, ch); - x0 += ui_font->char_width(chheight, aspect, ch); - } + draw_text(mui, container, title, 0.5f - 0.5f * titlewidth, boxbounds.y0 + 0.5f * chheight); // draw the top column headers - skip = int(chwidth / cellwidth); - for (int x = 0; x < xcells; x += 1 + skip) + int const colskip = int(chwidth / cellwidth); + for (int x = 0; x < xcells; x += 1 + colskip) { - x0 = boxbounds.x0 + 6.0f * chwidth + (float)x * cellwidth; + 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, aspect, rgb_t::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) + // if we're skipping, draw a point between the character and the box to indicate which one it's referring to + if (colskip) container.add_point(x0 + 0.5f * cellwidth, 0.5f * (y0 + chheight + boxbounds.y0 + 3.5f * chheight), UI_LINE_WIDTH, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); } // draw the side column headers - skip = int(chheight / cellheight); - for (int y = 0; y < ycells; y += 1 + skip) - + int const rowskip = int(chheight / cellheight); + for (int y = 0; y < ycells; y += 1 + rowskip) + { // only display if there is data to show - if (info.offset[set] + y * xcells < gfx.elements()) + if (set.m_offset + (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 + // 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) + y0 = boxbounds.y0 + 3.5f * chheight + float(y) * cellheight; + if (rowskip) container.add_point(0.5f * (x0 + boxbounds.x0 + 6.0f * chwidth), y0 + 0.5f * cellheight, UI_LINE_WIDTH, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); // draw the row header - sprintf(buffer, "%5X", info.offset[set] + y * xcells); + auto buffer = util::string_format("%5X", set.m_offset + (y * xcells)); for (int x = 4; x >= 0; x--) { x0 -= ui_font->char_width(chheight, aspect, buffer[x]); container.add_char(x0, y0 + 0.5f * (cellheight - chheight), chheight, aspect, rgb_t::white(), *ui_font, buffer[x]); } } + } // update the bitmap - gfxset_update_bitmap(mui.machine(), state, xcells, ycells, gfx); + update_gfxset_bitmap(xcells, ycells, gfx); // add the final quad - container.add_quad(cellboxbounds.x0, cellboxbounds.y0, cellboxbounds.x1, cellboxbounds.y1, - rgb_t::white(), state.texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container.add_quad( + cellboxbounds.x0, cellboxbounds.y0, cellboxbounds.x1, cellboxbounds.y1, + rgb_t::white(), m_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); // handle keyboard navigation before drawing - gfxset_handle_keys(mui.machine(), state, xcells, ycells); + if (m_gfxset.handle_keys(m_machine, xcells, ycells)) + m_bitmap_dirty = true; + return handle_general_keys(uistate); } -//------------------------------------------------- -// 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) +uint32_t gfx_viewer::handle_tilemap(mame_ui_manager &mui, render_container &container, bool uistate) { - // handle gfxset selection (open bracket,close bracket) - if (machine.ui_input().pressed(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 (machine.ui_input().pressed(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 (machine.ui_input().pressed(IPT_UI_ZOOM_OUT)) - { info.columns[set] = xcells - 1; state.bitmap_dirty = true; } - - if (machine.ui_input().pressed(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 (machine.ui_input().pressed(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 (machine.ui_input().pressed_repeat(IPT_UI_UP, 4)) - { info.offset[set] -= xcells; state.bitmap_dirty = true; } - if (machine.ui_input().pressed_repeat(IPT_UI_DOWN, 4)) - { info.offset[set] += xcells; state.bitmap_dirty = true; } - if (machine.ui_input().pressed_repeat(IPT_UI_PAGE_UP, 6)) - { info.offset[set] -= xcells * ycells; state.bitmap_dirty = true; } - if (machine.ui_input().pressed_repeat(IPT_UI_PAGE_DOWN, 6)) - { info.offset[set] += xcells * ycells; state.bitmap_dirty = true; } - if (machine.ui_input().pressed_repeat(IPT_UI_HOME, 4)) - { info.offset[set] = 0; state.bitmap_dirty = true; } - if (machine.ui_input().pressed_repeat(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 (machine.ui_input().pressed_repeat(IPT_UI_LEFT, 4)) - { info.color[set] -= 1; state.bitmap_dirty = true; } - if (machine.ui_input().pressed_repeat(IPT_UI_RIGHT, 4)) - { info.color[set] += 1; state.bitmap_dirty = true; } - - // clamp within range - if (info.color[set] >= info.color_count[set]) - { info.color[set] = info.color_count[set] - 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.valid() || state.texture == nullptr || state.bitmap.width() != cellxpix * xcells || state.bitmap.height() != cellypix * ycells) - { - // free the old stuff - machine.render().texture_free(state.texture); - state.bitmap.reset(); - - // allocate new stuff - state.bitmap.allocate(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], info.palette[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, device_palette_interface *dpalette) -{ - int width = (rotate & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width(); - int height = (rotate & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height(); - rgb_t const *const palette = dpalette->palette()->entry_list_raw() + gfx.colorbase() + color * gfx.granularity(); - - // loop over rows in the cell - for (int y = 0; y < height; y++) - { - uint32_t *dest = &bitmap.pix(dsty + y, dstx); - const uint8_t *src = gfx.get_data(index); - - // loop over columns in the cell - for (int x = 0; x < width; x++) - { - int effx = x, effy = y; - const uint8_t *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 - { - if (rotate & ORIENTATION_FLIP_X) - effx = gfx.height() - 1 - effx; - if (rotate & ORIENTATION_FLIP_Y) - effy = gfx.width() - 1 - effy; - std::swap(effx, effy); - } - - // 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(mame_ui_manager &mui, render_container &container, ui_gfx_state &state) -{ - render_font *ui_font = mui.get_font(); - render_bounds mapboxbounds; - render_bounds boxbounds; - const int targwidth = mui.machine().render().ui_target().width(); - const int targheight = mui.machine().render().ui_target().height(); - float titlewidth; - float x0, y0; - int mapboxwidth, mapboxheight; + // get some UI metrics + render_font *const ui_font = mui.get_font(); + int const targwidth = m_machine.render().ui_target().width(); + int const targheight = m_machine.render().ui_target().height(); + float const aspect = m_machine.render().ui_aspect(&container); + float const chheight = mui.get_line_height(); + float const chwidth = ui_font->char_width(chheight, aspect, '0'); // get the size of the tilemap itself - tilemap_t *tilemap = mui.machine().tilemap().find(state.tilemap.which); - uint32_t mapwidth = tilemap->width(); - uint32_t mapheight = tilemap->height(); - if (state.tilemap.rotate & ORIENTATION_SWAP_XY) + tilemap_t &tilemap = *m_machine.tilemap().find(m_tilemap.index()); + uint32_t mapwidth = tilemap.width(); + uint32_t mapheight = tilemap.height(); + if (m_tilemap.rotate() & ORIENTATION_SWAP_XY) std::swap(mapwidth, mapheight); // add a half character padding for the box - const float aspect = mui.machine().render().ui_aspect(&container); - const float chheight = mui.get_line_height(); - const float chwidth = ui_font->char_width(chheight, 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; + render_bounds boxbounds{ + 0.0f + (0.5f * chwidth), + 0.0f + (0.5f * chheight), + 1.0f - (0.5f * chwidth), + 1.0f - (0.5f * chheight) }; // the tilemap box bounds starts a half character in from the box - mapboxbounds = boxbounds; + render_bounds mapboxbounds = boxbounds; mapboxbounds.x0 += 0.5f * chwidth; mapboxbounds.x1 -= 0.5f * chwidth; mapboxbounds.y0 += 0.5f * chheight; @@ -1083,28 +1374,30 @@ static void tilemap_handler(mame_ui_manager &mui, render_container &container, u mapboxbounds.y0 += 1.5f * chheight; // convert back to pixels - mapboxwidth = (mapboxbounds.x1 - mapboxbounds.x0) * (float)targwidth; - mapboxheight = (mapboxbounds.y1 - mapboxbounds.y0) * (float)targheight; + int mapboxwidth = mapboxbounds.width() * float(targwidth); + int mapboxheight = mapboxbounds.height() * float(targheight); - // determine the maximum integral scaling factor - int pixelscale = state.tilemap.zoom; - if (pixelscale == 0) + float pixelscale; + if (m_tilemap.auto_zoom()) { - int maxxscale, maxyscale; - for (maxxscale = 1; mapwidth * (maxxscale + 1) < mapboxwidth; maxxscale++) { } - for (maxyscale = 1; mapheight * (maxyscale + 1) < mapboxheight; maxyscale++) { } - pixelscale = std::min(maxxscale, maxyscale); + // determine the maximum integral scaling factor + pixelscale = std::min(std::floor(mapboxwidth / mapwidth), std::floor(mapboxheight / mapheight)); + pixelscale = std::max(pixelscale, 1.0f); + } + else + { + pixelscale = m_tilemap.zoom_scale(); } // recompute the final box size - mapboxwidth = std::min(mapboxwidth, int(mapwidth * pixelscale)); - mapboxheight = std::min(mapboxheight, int(mapheight * pixelscale)); + mapboxwidth = std::min<int>(mapboxwidth, std::lround(mapwidth * pixelscale)); + mapboxheight = std::min<int>(mapboxheight, std::lround(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; + mapboxbounds.x0 += 0.5f * ((mapboxbounds.x1 - mapboxbounds.x0) - float(mapboxwidth) / targwidth); + mapboxbounds.x1 = mapboxbounds.x0 + float(mapboxwidth) / targwidth; + mapboxbounds.y0 += 0.5f * ((mapboxbounds.y1 - mapboxbounds.y0) - float(mapboxheight) / targheight); + mapboxbounds.y1 = mapboxbounds.y0 + float(mapboxheight) / targheight; // now recompute the outer box against this new info boxbounds.x0 = mapboxbounds.x0 - 0.5f * chwidth; @@ -1114,43 +1407,49 @@ static void tilemap_handler(mame_ui_manager &mui, render_container &container, u // figure out the title std::ostringstream title_buf; - util::stream_format(title_buf, "TILEMAP %d/%d", state.tilemap.which + 1, mui.machine().tilemap().count()); + util::stream_format(title_buf, + (m_tilemap.flags() != TILEMAP_DRAW_ALL_CATEGORIES) ? _("gfxview", "Tilemap %1$d/%2$d category %3$u") : _("gfxview", "Tilemap %1$d/%2$d "), + m_tilemap.index() + 1, m_machine.tilemap().count(), + m_tilemap.flags()); // if the mouse pointer is over a tile, add some info about its coordinates and color - int32_t mouse_target_x, mouse_target_y; float mouse_x, mouse_y; - bool mouse_button; - render_target *const mouse_target = mui.machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button); - if (mouse_target != nullptr && mouse_target->map_point_container(mouse_target_x, mouse_target_y, container, mouse_x, mouse_y) - && mapboxbounds.x0 <= mouse_x && mapboxbounds.x1 > mouse_x - && mapboxbounds.y0 <= mouse_y && mapboxbounds.y1 > mouse_y) + if (map_mouse(container, mapboxbounds, mouse_x, mouse_y)) { int xpixel = (mouse_x - mapboxbounds.x0) * targwidth; int ypixel = (mouse_y - mapboxbounds.y0) * targheight; - if (state.tilemap.rotate & ORIENTATION_FLIP_X) + if (m_tilemap.rotate() & ORIENTATION_FLIP_X) xpixel = (mapboxwidth - 1) - xpixel; - if (state.tilemap.rotate & ORIENTATION_FLIP_Y) + if (m_tilemap.rotate() & ORIENTATION_FLIP_Y) ypixel = (mapboxheight - 1) - ypixel; - if (state.tilemap.rotate & ORIENTATION_SWAP_XY) + if (m_tilemap.rotate() & ORIENTATION_SWAP_XY) std::swap(xpixel, ypixel); - uint32_t col = ((xpixel / pixelscale + state.tilemap.xoffs) / tilemap->tilewidth()) % tilemap->cols(); - uint32_t row = ((ypixel / pixelscale + state.tilemap.yoffs) / tilemap->tileheight()) % tilemap->rows(); + uint32_t const col = ((std::lround(xpixel / pixelscale) + m_tilemap.xoffs()) / tilemap.tilewidth()) % tilemap.cols(); + uint32_t const row = ((std::lround(ypixel / pixelscale) + m_tilemap.yoffs()) / tilemap.tileheight()) % tilemap.rows(); uint8_t gfxnum; uint32_t code, color; - tilemap->get_info_debug(col, row, gfxnum, code, color); - util::stream_format(title_buf, " @ %d,%d = GFX%d #%X:%X", - col * tilemap->tilewidth(), row * tilemap->tileheight(), - int(gfxnum), code, color); + tilemap.get_info_debug(col, row, gfxnum, code, color); + util::stream_format(title_buf, + _("gfxview", " (%1$u %2$u) = GFX%3$u #%4$X:%5$X"), + col * tilemap.tilewidth(), row * tilemap.tileheight(), + gfxnum, code, color); + + // keep touch pointer displayed after release so they know what it's pointing at + mame_ui_manager::display_pointer pointers[1]{ { m_machine.render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } }; + if (ui_event::pointer::TOUCH == m_pointer_type) + mui.set_pointers(std::begin(pointers), std::end(pointers)); } else - util::stream_format(title_buf, " %dx%d OFFS %d,%d", tilemap->width(), tilemap->height(), state.tilemap.xoffs, state.tilemap.yoffs); - - if (state.tilemap.flags != TILEMAP_DRAW_ALL_CATEGORIES) - util::stream_format(title_buf, " CAT %d", state.tilemap.flags); + { + util::stream_format(title_buf, + _("gfxview", u8" %1$d\u00d7%2$d origin (%3$d %4$d)"), + tilemap.width(), tilemap.height(), + m_tilemap.xoffs(), m_tilemap.yoffs()); + } // expand the outer box to fit the title - const std::string title = title_buf.str(); - titlewidth = ui_font->string_width(chheight, aspect, title.c_str()); + std::string const title = std::move(title_buf).str(); + float const titlewidth = ui_font->string_width(chheight, aspect, title); if (boxbounds.x1 - boxbounds.x0 < titlewidth + chwidth) { boxbounds.x0 = 0.5f - 0.5f * (titlewidth + chwidth); @@ -1161,192 +1460,158 @@ static void tilemap_handler(mame_ui_manager &mui, render_container &container, u mui.draw_outlined_box(container, boxbounds.x0, boxbounds.y0, boxbounds.x1, boxbounds.y1, mui.colors().gfxviewer_bg_color()); // draw the title - x0 = 0.5f - 0.5f * titlewidth; - y0 = boxbounds.y0 + 0.5f * chheight; - for (auto ch : title) - { - container.add_char(x0, y0, chheight, aspect, rgb_t::white(), *ui_font, ch); - x0 += ui_font->char_width(chheight, aspect, ch); - } + draw_text(mui, container, title, 0.5f - 0.5f * titlewidth, boxbounds.y0 + 0.5f * chheight); // update the bitmap - tilemap_update_bitmap(mui.machine(), state, mapboxwidth / pixelscale, mapboxheight / pixelscale); + update_tilemap_bitmap(std::lround(mapboxwidth / pixelscale), std::lround(mapboxheight / pixelscale)); // add the final quad - container.add_quad(mapboxbounds.x0, mapboxbounds.y0, - mapboxbounds.x1, mapboxbounds.y1, - rgb_t::white(), state.texture, - PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(state.tilemap.rotate)); + container.add_quad( + mapboxbounds.x0, mapboxbounds.y0, + mapboxbounds.x1, mapboxbounds.y1, + rgb_t::white(), m_texture, + PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(m_tilemap.rotate())); // handle keyboard input - tilemap_handle_keys(mui.machine(), state, mapboxwidth, mapboxheight); + if (m_tilemap.handle_keys(m_machine, pixelscale)) + m_bitmap_dirty = true; + return handle_general_keys(uistate); } -//------------------------------------------------- -// 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) +void gfx_viewer::update_gfxset_bitmap(int xcells, int ycells, gfx_element &gfx) { - // handle tilemap selection (open bracket,close bracket) - if (machine.ui_input().pressed(IPT_UI_PREV_GROUP) && state.tilemap.which > 0) - { state.tilemap.which--; state.bitmap_dirty = true; } - if (machine.ui_input().pressed(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); - uint32_t mapwidth = tilemap->width(); - uint32_t mapheight = tilemap->height(); + auto const &info = m_gfxset.m_devices[m_gfxset.m_device]; + auto const &set = info.set(m_gfxset.m_set); - // handle zoom (minus,plus) - if (machine.ui_input().pressed(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 (machine.ui_input().pressed(IPT_UI_ZOOM_IN) && state.tilemap.zoom < 8) - { - state.tilemap.zoom++; - state.bitmap_dirty = true; - machine.popmessage("Zoom = %d", state.tilemap.zoom); - } + // compute the number of source pixels in a cell + int const cellxpix = 1 + ((set.m_rotate & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width()); + int const cellypix = 1 + ((set.m_rotate & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height()); - // handle rotation (R) - if (machine.ui_input().pressed(IPT_UI_ROTATE)) - { - state.tilemap.rotate = orientation_add(ROT90, state.tilemap.rotate); - state.bitmap_dirty = true; - } + // reallocate the bitmap if it is too small + resize_bitmap(cellxpix * xcells, cellypix * ycells); - // return to (0,0) (HOME) - if (machine.ui_input().pressed(IPT_UI_HOME)) + // handle the redraw + if (m_bitmap_dirty) { - state.tilemap.xoffs = 0; - state.tilemap.yoffs = 0; - state.bitmap_dirty = true; - } + // pre-fill with transparency + m_bitmap.fill(0); - // handle flags (category) - if (machine.ui_input().pressed(IPT_UI_PAGE_UP) && state.tilemap.flags != TILEMAP_DRAW_ALL_CATEGORIES) - { - if (state.tilemap.flags > 0) - { - state.tilemap.flags--; - machine.popmessage("Category = %d", state.tilemap.flags); - } - else + // loop over rows + for (int y = 0, index = set.m_offset; y < ycells; y++) { - state.tilemap.flags = TILEMAP_DRAW_ALL_CATEGORIES; - machine.popmessage("Category All"); + // make a rectangle that covers this row + rectangle cellbounds(0, m_bitmap.width() - 1, y * cellypix, (y + 1) * cellypix - 1); + + // only display if there is data to show + if (index < gfx.elements()) + { + // draw the individual cells + for (int x = 0; x < xcells; x++, index++) + { + // update the bounds for this cell + cellbounds.min_x = x * cellxpix; + cellbounds.max_x = (x + 1) * cellxpix - 1; + + if (index < gfx.elements()) // only render if there is data + gfxset_draw_item(gfx, index, cellbounds.min_x, cellbounds.min_y, set); + else // otherwise, fill with transparency + m_bitmap.fill(0, cellbounds); + } + } } - state.bitmap_dirty = true; - } - if (machine.ui_input().pressed(IPT_UI_PAGE_DOWN) && (state.tilemap.flags < TILEMAP_DRAW_CATEGORY_MASK || (state.tilemap.flags == TILEMAP_DRAW_ALL_CATEGORIES))) - { - if (state.tilemap.flags == TILEMAP_DRAW_ALL_CATEGORIES) - state.tilemap.flags = 0; - else - state.tilemap.flags++; - state.bitmap_dirty = true; - machine.popmessage("Category = %d", state.tilemap.flags); - } - // handle navigation (up,down,left,right), taking orientation into account - int step = 8; // this may be applied more than once if multiple directions are pressed - if (machine.input().code_pressed(KEYCODE_LSHIFT)) step = 1; - if (machine.input().code_pressed(KEYCODE_LCONTROL)) step = 64; - if (machine.ui_input().pressed_repeat(IPT_UI_UP, 4)) - { - if (state.tilemap.rotate & ORIENTATION_SWAP_XY) - state.tilemap.xoffs -= (state.tilemap.rotate & ORIENTATION_FLIP_Y) ? -step : step; - else - state.tilemap.yoffs -= (state.tilemap.rotate & ORIENTATION_FLIP_Y) ? -step : step; - state.bitmap_dirty = true; - } - if (machine.ui_input().pressed_repeat(IPT_UI_DOWN, 4)) - { - if (state.tilemap.rotate & ORIENTATION_SWAP_XY) - state.tilemap.xoffs += (state.tilemap.rotate & ORIENTATION_FLIP_Y) ? -step : step; - else - state.tilemap.yoffs += (state.tilemap.rotate & ORIENTATION_FLIP_Y) ? -step : step; - state.bitmap_dirty = true; - } - if (machine.ui_input().pressed_repeat(IPT_UI_LEFT, 6)) - { - if (state.tilemap.rotate & ORIENTATION_SWAP_XY) - state.tilemap.yoffs -= (state.tilemap.rotate & ORIENTATION_FLIP_X) ? -step : step; - else - state.tilemap.xoffs -= (state.tilemap.rotate & ORIENTATION_FLIP_X) ? -step : step; - state.bitmap_dirty = true; - } - if (machine.ui_input().pressed_repeat(IPT_UI_RIGHT, 6)) - { - if (state.tilemap.rotate & ORIENTATION_SWAP_XY) - state.tilemap.yoffs += (state.tilemap.rotate & ORIENTATION_FLIP_X) ? -step : step; - else - state.tilemap.xoffs += (state.tilemap.rotate & ORIENTATION_FLIP_X) ? -step : step; - state.bitmap_dirty = true; + // reset the texture to force an update + m_texture->set_bitmap(m_bitmap, m_bitmap.cliprect(), TEXFORMAT_ARGB32); + m_bitmap_dirty = false; } - - // 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) +void gfx_viewer::update_tilemap_bitmap(int width, int height) { // swap the coordinates back if they were talking about a rotated surface - if (state.tilemap.rotate & ORIENTATION_SWAP_XY) + if (m_tilemap.rotate() & ORIENTATION_SWAP_XY) std::swap(width, height); - // realloc the bitmap if it is too small - if (!state.bitmap.valid() || state.texture == nullptr || state.bitmap.width() != width || state.bitmap.height() != height) - { - // free the old stuff - machine.render().texture_free(state.texture); - state.bitmap.reset(); + // reallocate the bitmap if it is too small + resize_bitmap(width, height); - // allocate new stuff - state.bitmap.allocate(width, height); - state.texture = machine.render().texture_alloc(); - state.texture->set_bitmap(state.bitmap, state.bitmap.cliprect(), TEXFORMAT_RGB32); + // handle the redraw + if (m_bitmap_dirty) + { + m_bitmap.fill(0); + tilemap_t &tilemap = *m_machine.tilemap().find(m_tilemap.index()); + screen_device *const first_screen = screen_device_enumerator(m_machine.root_device()).first(); + if (first_screen) + tilemap.draw_debug(*first_screen, m_bitmap, m_tilemap.xoffs(), m_tilemap.yoffs(), m_tilemap.flags()); - // force a redraw - state.bitmap_dirty = true; + // reset the texture to force an update + m_texture->set_bitmap(m_bitmap, m_bitmap.cliprect(), TEXFORMAT_RGB32); + m_bitmap_dirty = false; } +} - // handle the redraw - if (state.bitmap_dirty) + +void gfx_viewer::gfxset_draw_item(gfx_element &gfx, int index, int dstx, int dsty, gfxset::setinfo const &info) +{ + int const width = (info.m_rotate & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width(); + int const height = (info.m_rotate & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height(); + rgb_t const *const palette = info.m_palette->palette()->entry_list_raw() + gfx.colorbase() + info.m_color * gfx.granularity(); + uint8_t const *const src = gfx.get_data(index); + + // loop over rows in the cell + for (int y = 0; y < height; y++) { - state.bitmap.fill(0); - tilemap_t *tilemap = machine.tilemap().find(state.tilemap.which); - screen_device *first_screen = screen_device_iterator(machine.root_device()).first(); - if (first_screen) + uint32_t *dest = &m_bitmap.pix(dsty + y, dstx); + + // loop over columns in the cell + for (int x = 0; x < width; x++) { - tilemap->draw_debug(*first_screen, state.bitmap, state.tilemap.xoffs, state.tilemap.yoffs, state.tilemap.flags); - } + // compute effective x,y values after rotation + int effx = x, effy = y; + if (!(info.m_rotate & ORIENTATION_SWAP_XY)) + { + if (info.m_rotate & ORIENTATION_FLIP_X) + effx = gfx.width() - 1 - effx; + if (info.m_rotate & ORIENTATION_FLIP_Y) + effy = gfx.height() - 1 - effy; + } + else + { + if (info.m_rotate & ORIENTATION_FLIP_X) + effx = gfx.height() - 1 - effx; + if (info.m_rotate & ORIENTATION_FLIP_Y) + effy = gfx.width() - 1 - effy; + std::swap(effx, effy); + } - // reset the texture to force an update - state.texture->set_bitmap(state.bitmap, state.bitmap.cliprect(), TEXFORMAT_RGB32); - state.bitmap_dirty = false; + // get a pointer to the start of this source row + uint8_t const *const s = src + (effy * gfx.rowbytes()); + + // extract the pixel + *dest++ = 0xff000000 | palette[s[effx]]; + } } } + +} // anonymous namespace + + + +/*************************************************************************** + MAIN ENTRY POINT +***************************************************************************/ + +//------------------------------------------------- +// ui_gfx_ui_handler - primary UI handler +// +// NOTE: this must not be called before machine +// initialization is complete, as some drivers +// create or modify gfx sets in VIDEO_START +//------------------------------------------------- + +uint32_t ui_gfx_ui_handler(render_container &container, mame_ui_manager &mui, bool uistate) +{ + return mui.get_session_data<gfx_viewer, gfx_viewer>(mui.machine()).handle(mui, container, uistate); +} |