summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2022-08-23 05:01:26 +1000
committer Vas Crabb <vas@vastheman.com>2022-08-23 05:01:26 +1000
commit53d32b27d14738140e1b3488c86819385c3c8f71 (patch)
tree8cef00323db75b1619a37e97e048cee2febce09b /src/frontend
parent7bd9db575b27db74e8bee8bd3ed4517b0e21b414 (diff)
Lua scripting enhancements:
Added minimal support for manipulating bitmaps and drawing them in render containers. Y'CbCr 4:2:2, RGB and ARGB are supported. Argument order doesn't always match the underlying classes to make the Lua interface more consistent with render bounds and render containers. Added bindings for device_palette_interface. Fixed some errors in the documentation as well as documenting new functionality.
Diffstat (limited to 'src/frontend')
-rw-r--r--src/frontend/mame/luaengine.cpp53
-rw-r--r--src/frontend/mame/luaengine_render.cpp320
2 files changed, 363 insertions, 10 deletions
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp
index 43449a62184..1d0bfca5e76 100644
--- a/src/frontend/mame/luaengine.cpp
+++ b/src/frontend/mame/luaengine.cpp
@@ -720,6 +720,9 @@ void lua_engine::initialize()
emu["device_enumerator"] = sol::overload(
[] (device_t &dev) { return devenum<device_enumerator>(dev); },
[] (device_t &dev, int maxdepth) { return devenum<device_enumerator>(dev, maxdepth); });
+ emu["palette_enumerator"] = sol::overload(
+ [] (device_t &dev) { return devenum<palette_interface_enumerator>(dev); },
+ [] (device_t &dev, int maxdepth) { return devenum<palette_interface_enumerator>(dev, maxdepth); });
emu["screen_enumerator"] = sol::overload(
[] (device_t &dev) { return devenum<screen_device_enumerator>(dev); },
[] (device_t &dev, int maxdepth) { return devenum<screen_device_enumerator>(dev, maxdepth); });
@@ -1271,6 +1274,7 @@ void lua_engine::initialize()
machine_type["exit_pending"] = sol::property(&running_machine::exit_pending);
machine_type["hard_reset_pending"] = sol::property(&running_machine::hard_reset_pending);
machine_type["devices"] = sol::property([] (running_machine &m) { return devenum<device_enumerator>(m.root_device()); });
+ machine_type["palettes"] = sol::property([] (running_machine &m) { return devenum<palette_interface_enumerator>(m.root_device()); });
machine_type["screens"] = sol::property([] (running_machine &m) { return devenum<screen_device_enumerator>(m.root_device()); });
machine_type["cassettes"] = sol::property([] (running_machine &m) { return devenum<cassette_device_enumerator>(m.root_device()); });
machine_type["images"] = sol::property([] (running_machine &m) { return devenum<image_interface_enumerator>(m.root_device()); });
@@ -1432,6 +1436,54 @@ void lua_engine::initialize()
});
+ auto dipalette_type = sol().registry().new_usertype<device_palette_interface>("dipalette", sol::no_constructor);
+ dipalette_type.set_function("pen", &device_palette_interface::pen);
+ dipalette_type.set_function(
+ "pen_color",
+ [] (device_palette_interface const &pal, pen_t pen)
+ {
+ return uint32_t(pal.pen_color(pen));
+ });
+ dipalette_type.set_function("pen_contrast", &device_palette_interface::pen_contrast);
+ dipalette_type.set_function("pen_indirect", &device_palette_interface::pen_indirect);
+ dipalette_type.set_function(
+ "indirect_color",
+ [] (device_palette_interface const &pal, int index)
+ {
+ return uint32_t(pal.indirect_color(index));
+ });
+ dipalette_type["set_pen_color"] = sol::overload(
+ [] (device_palette_interface &pal, pen_t pen, uint32_t color)
+ {
+ pal.set_pen_color(pen, rgb_t(color));
+ },
+ static_cast<void (device_palette_interface::*)(pen_t, uint8_t, uint8_t, uint8_t)>(&device_palette_interface::set_pen_color));
+ dipalette_type.set_function("set_pen_red_level", &device_palette_interface::set_pen_red_level);
+ dipalette_type.set_function("set_pen_green_level", &device_palette_interface::set_pen_green_level);
+ dipalette_type.set_function("set_pen_blue_level", &device_palette_interface::set_pen_blue_level);
+ dipalette_type.set_function("set_pen_contrast", &device_palette_interface::set_pen_contrast);
+ dipalette_type.set_function("set_pen_indirect", &device_palette_interface::set_pen_indirect);
+ dipalette_type["set_indirect_color"] = sol::overload(
+ [] (device_palette_interface &pal, int index, uint32_t color)
+ {
+ pal.set_indirect_color(index, rgb_t(color));
+ },
+ [] (device_palette_interface &pal, int index, uint8_t r, uint8_t g, uint8_t b)
+ {
+ pal.set_indirect_color(index, rgb_t(r, g, b));
+ });
+ dipalette_type.set_function("set_shadow_factor", &device_palette_interface::set_shadow_factor);
+ dipalette_type.set_function("set_highlight_factor", &device_palette_interface::set_highlight_factor);
+ dipalette_type.set_function("set_shadow_mode", &device_palette_interface::set_shadow_mode);
+ dipalette_type["entries"] = sol::property(&device_palette_interface::entries);
+ dipalette_type["indirect_entries"] = sol::property(&device_palette_interface::indirect_entries);
+ dipalette_type["black_pen"] = sol::property(&device_palette_interface::black_pen);
+ dipalette_type["white_pen"] = sol::property(&device_palette_interface::white_pen);
+ dipalette_type["shadows_enabled"] = sol::property(&device_palette_interface::shadows_enabled);
+ dipalette_type["highlights_enabled"] = sol::property(&device_palette_interface::hilights_enabled);
+ dipalette_type["device"] = sol::property(static_cast<device_t & (device_palette_interface::*)()>(&device_palette_interface::device));
+
+
auto screen_dev_type = sol().registry().new_usertype<screen_device>(
"screen_dev",
sol::no_constructor,
@@ -1589,6 +1641,7 @@ void lua_engine::initialize()
screen_dev_type["frame_period"] = sol::property([] (screen_device &sdev) { return sdev.frame_period().as_double(); });
screen_dev_type["frame_number"] = &screen_device::frame_number;
screen_dev_type["container"] = sol::property(&screen_device::container);
+ screen_dev_type["palette"] = sol::property([] (screen_device const &sdev) { return sdev.has_palette() ? &sdev.palette() : nullptr; });
auto cass_type = sol().registry().new_usertype<cassette_image_device>(
diff --git a/src/frontend/mame/luaengine_render.cpp b/src/frontend/mame/luaengine_render.cpp
index 893412a4b30..ed56d7470b2 100644
--- a/src/frontend/mame/luaengine_render.cpp
+++ b/src/frontend/mame/luaengine_render.cpp
@@ -17,11 +17,181 @@
#include "render.h"
#include "rendlay.h"
+#include <atomic>
#include <iterator>
namespace {
+template <typename T>
+class bitmap_helper : public T
+{
+public:
+ using ptr = std::shared_ptr<bitmap_helper>;
+
+ bitmap_helper(bitmap_helper const &) = delete;
+ bitmap_helper(bitmap_helper &&) = delete;
+ bitmap_helper &operator=(bitmap_helper const &) = delete;
+ bitmap_helper &operator=(bitmap_helper &&) = delete;
+
+ bitmap_helper(sol::this_state s, int width, int height, int xslop, int yslop)
+ : T(width, height, xslop, yslop)
+ , lock_count(0)
+ , storage()
+ {
+ if ((0 < width) && (0 < height) && !this->valid())
+ luaL_error(s, "Error allocating bitmap storage");
+ }
+
+ bitmap_helper(ptr const &source, rectangle const &subrect)
+ : T(*source, subrect)
+ , lock_count(0)
+ , storage(source->storage ? source->storage : source)
+ {
+ ++storage->lock_count;
+ }
+
+ ~bitmap_helper()
+ {
+ assert(!lock_count);
+ release_storage();
+ }
+
+ bool locked() const
+ {
+ return bool(lock_count);
+ }
+
+ void reset(sol::this_state s)
+ {
+ if (lock_count)
+ luaL_error(s, "Cannot reset bitmap while in use");
+ T::reset();
+ release_storage();
+ }
+
+ void allocate(sol::this_state s, int width, int height, int xslop, int yslop)
+ {
+ if (lock_count)
+ luaL_error(s, "Cannot reallocate bitmap while in use");
+ T::allocate(width, height, xslop, yslop);
+ release_storage();
+ if ((0 < width) && (0 < height) && !this->valid())
+ luaL_error(s, "Error allocating bitmap storage");
+ }
+
+ void resize(sol::this_state s, int width, int height, int xslop, int yslop)
+ {
+ if (lock_count)
+ luaL_error(s, "Cannot resize bitmap while in use");
+ T::resize(width, height, xslop, yslop);
+ release_storage();
+ if ((0 < width) && (0 < height) && !this->valid())
+ luaL_error(s, "Error allocating bitmap storage");
+ }
+
+ void wrap(sol::this_state s, ptr const &source, rectangle const &subrect)
+ {
+ if (source.get() == this)
+ luaL_error(s, "Bitmap cannot wrap itself");
+ if (lock_count)
+ luaL_error(s, "Cannot free bitmap storage while in use");
+ if (!source->cliprect().contains(subrect))
+ luaL_error(s, "Bounds exceed source clipping rectangle");
+ T::wrap(*source, subrect);
+ release_storage();
+ storage = source->storage ? source->storage : source;
+ ++storage->lock_count;
+ }
+
+ std::atomic<unsigned> lock_count;
+
+private:
+ void release_storage()
+ {
+ if (storage)
+ {
+ assert(storage->lock_count);
+ --storage->lock_count;
+ storage.reset();
+ }
+ }
+
+ ptr storage;
+};
+
+
+class render_texture_helper
+{
+public:
+ render_texture_helper(render_texture_helper const &) = delete;
+
+ render_texture_helper(render_texture_helper &&that)
+ : texture(that.texture)
+ , bitmap(that.bitmap)
+ , manager(that.manager)
+ , lock_count(that.lock_count)
+ {
+ that.texture = nullptr;
+ that.bitmap.reset();
+ }
+
+ template <typename T>
+ render_texture_helper(sol::this_state s, render_manager &m, std::shared_ptr<T> const &b, texture_format f)
+ : texture(nullptr)
+ , bitmap(b)
+ , manager(m)
+ , lock_count(b->lock_count)
+ {
+ if (bitmap)
+ {
+ texture = manager.texture_alloc();
+ if (texture)
+ {
+ ++lock_count;
+ texture->set_bitmap(*bitmap, bitmap->cliprect(), f);
+ }
+ else
+ {
+ luaL_error(s, "Error allocating texture");
+ }
+ }
+ }
+
+ ~render_texture_helper()
+ {
+ free();
+ }
+
+ bool valid() const
+ {
+ return texture && bitmap;
+ }
+
+ void free()
+ {
+ if (texture)
+ {
+ manager.texture_free(texture);
+ texture = nullptr;
+ }
+ if (bitmap)
+ {
+ assert(lock_count);
+ --lock_count;
+ bitmap.reset();
+ }
+ }
+
+ render_texture *texture;
+ std::shared_ptr<bitmap_t> bitmap;
+
+private:
+ render_manager &manager;
+ std::atomic<unsigned> &lock_count;
+};
+
+
struct layout_file_views
{
layout_file_views(layout_file &f) : file(f) { }
@@ -54,6 +224,84 @@ struct render_target_view_names
int count;
};
+
+template <typename T>
+auto make_bitmap_specific_type(sol::table registry, char const *name)
+{
+ auto result = registry.new_usertype<T>(
+ name,
+ sol::no_constructor,
+ sol::base_classes, sol::bases<bitmap_t>());
+ result.set_function("pix", [] (T &bitmap, int32_t x, int32_t y) { return bitmap.pix(y, x); });
+ result["fill"] = sol::overload(
+ static_cast<void (T::*)(typename T::pixel_t)>(&T::fill),
+ [] (T &bitmap, typename T::pixel_t color, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ bitmap.fill(color, rectangle(minx, maxx, miny, maxy));
+ });
+ result.set_function(
+ "plot",
+ [] (T &bitmap, int32_t x, int32_t y, typename T::pixel_t color)
+ {
+ if (bitmap.cliprect().contains(x, y))
+ bitmap.pix(y, x) = color;
+ });
+ result.set_function("plot_box", &T::plot_box);
+ result["bpp"] = sol::property(&T::bpp);
+ return result;
+}
+
+
+template <typename T, typename B>
+auto make_bitmap_type(sol::table &registry, char const *name)
+{
+ auto result = registry.new_usertype<T>(
+ name,
+ sol::call_constructor, sol::factories(
+ [] (sol::this_state s)
+ {
+ return std::make_shared<T>(s, 0, 0, 0, 0);
+ },
+ [] (sol::this_state s, int width, int height)
+ {
+ return std::make_shared<T>(s, width, height, 0, 0);
+ },
+ [] (sol::this_state s, int width, int height, int xslop, int yslop)
+ {
+ return std::make_shared<T>(s, width, height, xslop, yslop);
+ },
+ [] (typename T::ptr const &source)
+ {
+ return std::make_shared<T>(source, source->cliprect());
+ },
+ [] (sol::this_state s, typename T::ptr const &source, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ rectangle const subrect(minx, maxx, miny, maxy);
+ if (!source->cliprect().contains(subrect))
+ luaL_error(s, "Bounds exceed source clipping rectangle");
+ return std::make_shared<T>(source, subrect);
+ }),
+ sol::base_classes, sol::bases<B, bitmap_t>());
+ result.set_function("reset", &T::reset);
+ result["allocate"] = sol::overload(
+ &T::allocate,
+ [] (T &bitmap, sol::this_state s, int width, int height) { bitmap.allocate(s, width, height, 0, 0); });
+ result["resize"] = sol::overload(
+ &T::resize,
+ [] (T &bitmap, sol::this_state s, int width, int height) { bitmap.resize(s, width, height, 0, 0); });
+ result["wrap"] = sol::overload(
+ [] (T &bitmap, sol::this_state s, typename T::ptr const &source)
+ {
+ bitmap.wrap(s, source, source->cliprect());
+ },
+ [] (T &bitmap, sol::this_state s, typename T::ptr const &source, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ bitmap.wrap(s, source, rectangle(minx, maxx, miny, maxy));
+ });
+ result["locked"] = sol::property(&T::locked);
+ return result;
+}
+
} // anonymous namespace
@@ -226,9 +474,9 @@ void lua_engine::initialize_render(sol::table &emu)
sol::call_constructor, sol::initializers(
[] (render_bounds &b) { new (&b) render_bounds{ 0.0F, 0.0F, 1.0F, 1.0F }; },
[] (render_bounds &b, float x0, float y0, float x1, float y1) { new (&b) render_bounds{ x0, y0, x1, y1 }; }));
- bounds_type["includes"] = &render_bounds::includes;
- bounds_type["set_xy"] = &render_bounds::set_xy;
- bounds_type["set_wh"] = &render_bounds::set_wh;
+ bounds_type.set_function("includes", &render_bounds::includes);
+ bounds_type.set_function("set_xy", &render_bounds::set_xy);
+ bounds_type.set_function("set_wh", &render_bounds::set_wh);
bounds_type["x0"] = &render_bounds::x0;
bounds_type["y0"] = &render_bounds::y0;
bounds_type["x1"] = &render_bounds::x1;
@@ -243,13 +491,43 @@ void lua_engine::initialize_render(sol::table &emu)
sol::call_constructor, sol::initializers(
[] (render_color &c) { new (&c) render_color{ 1.0F, 1.0F, 1.0F, 1.0F }; },
[] (render_color &c, float a, float r, float g, float b) { new (&c) render_color{ a, r, g, b }; }));
- color_type["set"] = &render_color::set;
+ color_type.set_function("set", &render_color::set);
color_type["a"] = &render_color::a;
color_type["r"] = &render_color::r;
color_type["g"] = &render_color::g;
color_type["b"] = &render_color::b;
+ auto bitmap_type = sol().registry().new_usertype<bitmap_t>("bitmap", sol::no_constructor);
+ bitmap_type["fill"] = sol::overload(
+ static_cast<void (bitmap_t::*)(uint64_t)>(&bitmap_t::fill),
+ [] (bitmap_t &bitmap, uint64_t color, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ bitmap.fill(color, rectangle(minx, maxx, miny, maxy));
+ });
+ bitmap_type.set_function("plot_box", &bitmap_t::plot_box);
+ bitmap_type["width"] = sol::property(&bitmap_t::width);
+ bitmap_type["height"] = sol::property(&bitmap_t::height);
+ bitmap_type["rowpixels"] = sol::property(&bitmap_t::rowpixels);
+ bitmap_type["rowbytes"] = sol::property(&bitmap_t::rowbytes);
+ bitmap_type["bpp"] = sol::property(&bitmap_t::bpp);
+ bitmap_type["valid"] = sol::property(&bitmap_t::valid);
+
+ make_bitmap_specific_type<bitmap8_t>(sol().registry(), "bitmap8");
+ make_bitmap_specific_type<bitmap16_t>(sol().registry(), "bitmap16");
+ make_bitmap_specific_type<bitmap32_t>(sol().registry(), "bitmap32");
+ make_bitmap_specific_type<bitmap64_t>(sol().registry(), "bitmap64");
+
+ make_bitmap_type<bitmap_helper<bitmap_yuy16>, bitmap16_t>(emu, "bitmap_yuy16");
+ make_bitmap_type<bitmap_helper<bitmap_rgb32>, bitmap32_t>(emu, "bitmap_rgb32");
+ make_bitmap_type<bitmap_helper<bitmap_argb32>, bitmap32_t>(emu, "bitmap_argb32");
+
+
+ auto render_texture_type = emu.new_usertype<render_texture_helper>("render_texture", sol::no_constructor);
+ render_texture_type.set_function("free", &render_texture_helper::free);
+ render_texture_type["valid"] = sol::property(&render_texture_helper::valid);
+
+
auto layout_view_type = sol().registry().new_usertype<layout_view>("layout_view", sol::no_constructor);
layout_view_type["has_screen"] = &layout_view::has_screen;
layout_view_type["set_prepare_items_callback"] =
@@ -392,7 +670,8 @@ void lua_engine::initialize_render(sol::table &emu)
auto render_container_type = sol().registry().new_usertype<render_container>("render_container", sol::no_constructor);
- render_container_type["draw_box"] =
+ render_container_type.set_function(
+ "draw_box",
[] (render_container &ctnr, float x1, float y1, float x2, float y2, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
x1 = std::clamp(x1, 0.0f, 1.0f);
@@ -405,8 +684,9 @@ void lua_engine::initialize_render(sol::table &emu)
if (!bgcolor)
bgcolor = ui.colors().background_color();
ui.draw_outlined_box(ctnr, x1, y1, x2, y2, *fgcolor, *bgcolor);
- };
- render_container_type["draw_line"] =
+ });
+ render_container_type.set_function(
+ "draw_line",
[] (render_container &ctnr, float x1, float y1, float x2, float y2, std::optional<uint32_t> color)
{
x1 = std::clamp(x1, 0.0f, 1.0f);
@@ -416,8 +696,15 @@ void lua_engine::initialize_render(sol::table &emu)
if (!color)
color = mame_machine_manager::instance()->ui().colors().text_color();
ctnr.add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(*color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- };
- render_container_type["draw_text"] =
+ });
+ render_container_type.set_function(
+ "draw_quad",
+ [] (render_container &cntr, render_texture_helper const &tex, float x1, float y1, float x2, float y2, std::optional<uint32_t> color)
+ {
+ cntr.add_quad(x1, y1, x2, y2, color ? *color : uint32_t(0xffffffff), tex.texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ });
+ render_container_type.set_function(
+ "draw_text",
[] (render_container &ctnr, sol::this_state s, sol::object xobj, float y, char const *msg, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
auto justify = ui::text_layout::text_justify::LEFT;
@@ -453,7 +740,7 @@ void lua_engine::initialize_render(sol::table &emu)
x, y, (1.0f - x),
justify, ui::text_layout::word_wrapping::WORD,
mame_ui_manager::OPAQUE_, *fgcolor, *bgcolor);
- };
+ });
render_container_type["user_settings"] = sol::property(&render_container::get_user_settings, &render_container::set_user_settings);
render_container_type["orientation"] = sol::property(
&render_container::orientation,
@@ -510,6 +797,19 @@ void lua_engine::initialize_render(sol::table &emu)
auto render_type = sol().registry().new_usertype<render_manager>("render", sol::no_constructor);
+ render_type["texture_alloc"] = sol::overload(
+ [] (render_manager &manager, sol::this_state s, bitmap_helper<bitmap_yuy16>::ptr const &bitmap)
+ {
+ return render_texture_helper(s, manager, bitmap, TEXFORMAT_YUY16);
+ },
+ [] (render_manager &manager, sol::this_state s, bitmap_helper<bitmap_rgb32>::ptr const &bitmap)
+ {
+ return render_texture_helper(s, manager, bitmap, TEXFORMAT_RGB32);
+ },
+ [] (render_manager &manager, sol::this_state s, bitmap_helper<bitmap_argb32>::ptr const &bitmap)
+ {
+ return render_texture_helper(s, manager, bitmap, TEXFORMAT_ARGB32);
+ });
render_type["max_update_rate"] = sol::property(&render_manager::max_update_rate);
render_type["ui_target"] = sol::property(&render_manager::ui_target);
render_type["ui_container"] = sol::property(&render_manager::ui_container);