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.
This commit is contained in:
Vas Crabb 2022-08-23 05:01:26 +10:00
parent 7bd9db575b
commit 53d32b27d1
4 changed files with 798 additions and 45 deletions

View File

@ -305,6 +305,9 @@ machine.hard_reset_pending (read-only)
machine.devices (read-only)
A :ref:`device enumerator <luareference-dev-enum>` that yields all
:ref:`devices <luareference-dev-device>` in the emulated system.
machine.palettes (read-only)
A :ref:`device enumerator <luareference-dev-enum>` that yields all
:ref:`palette devices <luareference-dev-dipalette>` in the emulated system.
machine.screens (read-only)
A :ref:`device enumerator <luareference-dev-enum>` that yields all
:ref:`screen devices <luareference-dev-screen>` in the emulated system.
@ -725,6 +728,9 @@ Instantiation
manager.machine.devices
Returns a device enumerator that will iterate over
:ref:`devices <luareference-dev-device>` in the system.
manager.machine.palettes
Returns a device enumerator that will iterate over
:ref:`palette devices <luareference-dev-dipalette>` in the system.
manager.machine.screens
Returns a device enumerator that will iterate over
:ref:`screen devices <luareference-dev-screen>` in the system.
@ -744,6 +750,13 @@ emu.device_enumerator(device, [depth])
provided, it must be an integer specifying the maximum number of levels to
iterate below the specified device (i.e. 1 will limit iteration to the
device and its immediate children).
emu.palette_enumerator(device, [depth])
Returns a device enumerator that will iterate over
:ref:`palette devices <luareference-dev-dipalette>` in the sub-tree starting
at the specified device. The specified device will be included if it is a
palette device. If the depth is provided, it must be an integer specifying
the maximum number of levels to iterate below the specified device (i.e. 1
will limit iteration to the device and its immediate children).
emu.screen_enumerator(device, [depth])
Returns a device enumerator that will iterate over
:ref:`screen devices <luareference-dev-screen>` in the sub-tree starting at
@ -849,6 +862,90 @@ device.spaces[] (read-only)
interface. Note that the names are specific to the device type and have no
special significance.
.. _luareference-dev-dipalette:
Palette device
~~~~~~~~~~~~~~
Wraps MAMEs ``device_palette_interface`` class, which represents a device that
translates pen values to colours.
Colours are in alpha/red/green/blue (ARGB) format. Channel values are in the
range 0 (transparent or off) to 255 (opaque or full intensity), inclusive.
Colour channel values are not pre-multiplied by the alpha value. Channel values
are packed into the bytes of 32-bit unsigned integers, in the order alpha, red,
green, blue from most-significant to least-significant byte.
Instantiation
^^^^^^^^^^^^^
manager.machine.palettes[tag]
Gets a palette device by tag relative to the root machine device, or ``nil``
if no such device exists or it is not a palette device.
Methods
^^^^^^^
palette:pen(index)
Gets the remapped pen number for the specified palette index.
palette:pen_color(pen)
Gets the colour for the specified pen number.
palette:pen_contrast(pen)
Gets the contrast value for the specified pen number. The contrast is a
floating-point number.
palette:pen_indirect(index)
Gets the indirect pen index for the specified palette index.
palette:indirect_color(index)
Gets the indirect pen colour for the specified palette index.
palette:set_pen_color(pen, color)
Sets the colour for the specified pen number. The colour may be specified
as a single packed 32-bit value; or as individual red, green and blue
channel values, in that order.
palette:set_pen_red_level(pen, level)
Sets the red channel value of the colour for the specified pen number.
Other channel values are not affected.
palette:set_pen_green_level(pen, level)
Sets the green channel value of the colour for the specified pen number.
Other channel values are not affected.
palette:set_pen_blue_level(pen, level)
Sets the blue channel value of the colour for the specified pen number.
Other channel values are not affected.
palette:set_pen_contrast(pen, factor)
Sets the contrast value for the specified pen number. The value must be a
floating-point number.
palette:set_pen_indirect(pen, index)
Sets the indirect pen index for the specified pen number.
palette:set_indirect_color(index, color)
Sets the indirect pen colour for the specified palette index. The colour
may be specified as a single packed 32-bit value; or as individual red,
green and blue channel values, in that order.
palette:set_shadow_factor(factor)
Sets the contrast value for the current shadow group. The value must be a
floating-point number.
palette:set_highlight_factor(factor)
Sets the contrast value for the current highlight group. The value must be
a floating-point number.
palette:set_shadow_mode(mode)
Sets the shadow mode. The value is the index of the desired shadow table.
Properties
^^^^^^^^^^
palette.entries (read-only)
The number of colour entries in the palette.
palette.indirect_entries (read-only)
The number of indirect pen entries in the palette.
palette.black_pen (read-only)
The index of the fixed black pen entry.
palette.white_pen (read-only)
The index of the fixed white pen.
palette.shadows_enabled (read-only)
A Boolean indicating whether shadow colours are enabled.
palette.highlights_enabled (read-only)
A Boolean indicating whether highlight colours are enabled.
palette.device (read-only)
The underlying :ref:`device <luareference-dev-device>`.
.. _luareference-dev-screen:
Screen device
@ -926,8 +1023,8 @@ screen:draw_box(left, top, right, bottom, [line], [fill])
most-significant to least-significant byte. If the line colour is not
provided, the UI text colour is used; if the fill colour is not provided,
the UI background colour is used.
screen:draw_line(x1, y1, x2, y2, [color])
Draws a line from (x1, y1) to (x2, y2).
screen:draw_line(x0, y0, x1, y1, [color])
Draws a line from (x0, y0) to (x1, y1).
Coordinates are floating-point numbers in units of emulated screen pixels,
with the origin at (0, 0). Note that emulated screen pixels often arent
@ -1016,6 +1113,10 @@ screen.frame_number (read-only)
screen.container (read-only)
The :ref:`render container <luareference-render-container>` used to draw the
screen.
screen.palette (read-only)
The :ref:`palette device <luareference-dev-dipalette>` used to translate
pixel values to colours, or ``nil`` if the screen uses a direct colour pixel
format.
.. _luareference-dev-cass:
@ -2596,6 +2697,276 @@ color.b (read/write)
Blue channel value, in the range of zero (0, off) to one (1, full
intensity).
.. _luareference-render-bitmap:
Bitmap
~~~~~~
Wraps implementation of MAMEs ``bitmap_t`` and ``bitmap_specific`` classes,
which represent two-dimensional bitmaps stored in row-major order. Pixel
coordinates are zero-based, increasing to the right and down. Several pixel
formats are supported.
Instantiation
^^^^^^^^^^^^^
emu.bitmap_yuy16([width, height], [xslop], yslop])
Creates a Y'CbCr format bitmap with 4:2:2 chroma subsampling (horizontal
pairs of pixels have individual luma values but share chroma values). Each
pixel is a 16-bit integer value. The most significant byte of the pixel
value is the unsigned 8-bit Y' (luma) component of the pixel colour. For
each horizontal pair of pixels, the least significant byte of the first
pixel (even zero-based X coordinate) value is the signed 8-bit Cb value for
the pair of pixels, and the least significant byte of the second pixel (odd
zero-based X coordinate) value is the signed 8-bit Cr value for the pair of
pixels.
If no width and height are specified, they are assumed to be zero. If the
width is specified, the height must also be specified. The X and Y slop
values set the amount of extra storage in pixels to reserve at the
left/right of each row and top/bottom of each column, respectively. If an X
slop value is specified, a Y slop value must be specified as well. If no X
and Y slop values are specified, they are assumed to be zero (the storage
will be sized to fit the bitmap content). If the width and/or height is
less than or equal to zero, no storage will be allocated, irrespective of
the X and Y slop values, and the width and height of the bitmap will both be
set to zero.
The initial clipping rectangle is set to the entirety of the bitmap.
emu.bitmap_rgb32([width, height], [xslop, yslop])
Creates an RGB format bitmap with no alpha (transparency) channel. Each
pixel is represented by a 32-bit integer value. The most significant byte
of the pixel value is ignored. The remaining three bytes, from most
significant to least significant, are the unsigned 8-bit unsigned red, green
and blue channel values (larger values correspond to higher intensities).
If no width and height are specified, they are assumed to be zero. If the
width is specified, the height must also be specified. The X and Y slop
values set the amount of extra storage in pixels to reserve at the
left/right of each row and top/bottom of each column, respectively. If an X
slop value is specified, a Y slop value must be specified as well. If no X
and Y slop values are specified, they are assumed to be zero (the storage
will be sized to fit the bitmap content). If the width and/or height is
less than or equal to zero, no storage will be allocated, irrespective of
the X and Y slop values, and the width and height of the bitmap will both be
set to zero.
The initial clipping rectangle is set to the entirety of the bitmap.
emu.bitmap_argb32([width, height], [xslop, yslop])
Creates an ARGB format bitmap. Each pixel is represented by a 32-bit
integer value. The most significant byte of the pixel is the 8-bit unsigned
alpha (transparency) channel value (smaller values are more transparent).
The remaining three bytes, from most significant to least significant, are
the unsigned 8-bit unsigned red, green and blue channel values (larger
values correspond to higher intensities). Colour channel values are not
pre-multiplied by the alpha channel value.
If no width and height are specified, they are assumed to be zero. If the
width is specified, the height must also be specified. The X and Y slop
values set the amount of extra storage in pixels to reserve at the
left/right of each row and top/bottom of each column, respectively. If an X
slop value is specified, a Y slop value must be specified as well. If no X
and Y slop values are specified, they are assumed to be zero (the storage
will be sized to fit the bitmap content). If the width and/or height is
less than or equal to zero, no storage will be allocated, irrespective of
the X and Y slop values, and the width and height of the bitmap will both be
set to zero.
The initial clipping rectangle is set to the entirety of the bitmap.
emu.bitmap_yuy16(source, [x0, y0, x1, y1])
Creates a Y'CbCr format bitmap with 4:2:2 chroma subsampling representing a
view of a portion of an existing bitmap. The initial clipping rectangle is
set to the bounds of the view. The source bitmap will be locked, preventing
resizing and reallocation.
If no coordinates are specified, the new bitmap will represent a view of the
source bitmaps current clipping rectangle. If coordinates are specified,
the new bitmap will represent a view of the rectangle with top left corner
at (x0, y0) and bottom right corner at (x1, y1) in the source bitmap.
Coordinates are in units of pixels. The bottom right coordinates are
inclusive.
The source bitmap must be owned by the Lua script and must use the Y'CbCr
format. Raises an error if coordinates are specified representing a
rectangle not fully contained within the source bitmaps clipping rectangle.
emu.bitmap_rgb32(source, [x0, y0, x1, y1])
Creates an RGB format bitmap with 4:2:2 chroma subsampling representing a
view of a portion of an existing bitmap. The initial clipping rectangle is
set to the bounds of the view. The source bitmap will be locked, preventing
resizing and reallocation.
If no coordinates are specified, the new bitmap will represent a view of the
source bitmaps current clipping rectangle. If coordinates are specified,
the new bitmap will represent a view of the rectangle with top left corner
at (x0, y0) and bottom right corner at (x1, y1) in the source bitmap.
Coordinates are in units of pixels. The bottom right coordinates are
inclusive.
The source bitmap must be owned by the Lua script and must use the RGB
format. Raises an error if coordinates are specified representing a
rectangle not fully contained within the source bitmaps clipping rectangle.
emu.bitmap_argb32(source, [x0, y0, x1, y1])
Creates an ARGB format bitmap with 4:2:2 chroma subsampling representing a
view of a portion of an existing bitmap. The initial clipping rectangle is
set to the bounds of the view. The source bitmap will be locked, preventing
resizing and reallocation.
If no coordinates are specified, the new bitmap will represent a view of the
source bitmaps current clipping rectangle. If coordinates are specified,
the new bitmap will represent a view of the rectangle with top left corner
at (x0, y0) and bottom right corner at (x1, y1) in the source bitmap.
Coordinates are in units of pixels. The bottom right coordinates are
inclusive.
The source bitmap must be owned by the Lua script and must use the ARGB
format. Raises an error if coordinates are specified representing a
rectangle not fully contained within the source bitmaps clipping rectangle.
Methods
^^^^^^^
bitmap:reset()
Sets the width and height to zero, and frees the pixel storage if the bitmap
owns its own storage, or releases the source bitmap if the it represents a
view of another bitmap.
The bitmap must be owned by the Lua script. Raises an error if the bitmaps
storage is referenced by another bitmap or a :ref:`texture
<luareference-render-texture>`.
bitmap:allocate(width, height, [xslop, yslop])
Reallocates storage for the bitmap, sets its width and height, and sets the
clipping rectangle to the entirety of the bitmap. If the bitmap already
owns allocated storage, it will always be freed and reallocated; if the
bitmap represents a view of another bitmap, the source bitmap will be
released. The storage will be filled with pixel value zero.
The X and Y slop values set the amount of extra storage in pixels to reserve
at the left/right of each row and top/bottom of each column, respectively.
If an X slop value is specified, a Y slop value must be specified as well.
If no X and Y slop values are specified, they are assumed to be zero (the
storage will be sized to fit the bitmap content). If the width and/or
height is less than or equal to zero, no storage will be allocated,
irrespective of the X and Y slop values, and the width and height of the
bitmap will both be set to zero.
The bitmap must be owned by the Lua script. Raises an error if the bitmaps
storage is referenced by another bitmap or a :ref:`texture
<luareference-render-texture>`.
bitmap:resize(width, height, [xslop, yslop])
Changes the width and height, and sets the clipping rectangle to the
entirety of the bitmap.
The X and Y slop values set the amount of extra storage in pixels to reserve
at the left/right of each row and top/bottom of each column, respectively.
If an X slop value is specified, a Y slop value must be specified as well.
If no X and Y slop values are specified, they are assumed to be zero (rows
will be stored contiguously, and the top row will be placed at the beginning
of the bitmaps storage).
If the bitmap already owns allocated storage and it is large enough for the
updated size, it will be used without being freed; if it is too small for
the updated size, it will always be freed and reallocated. If the bitmap
represents a view of another bitmap, the source bitmap will be released. If
storage is allocated, it will be filled with pixel value zero (if existing
storage is used, its contents will not be changed).
Raises an error if the bitmaps storage is referenced by another bitmap or a
:ref:`texture <luareference-render-texture>`.
bitmap:wrap(source, [x0, y0, x1, y1])
Makes the bitmap represent a view of a portion of another bitmap and sets
the clipping rectangle to the bounds of the view.
If no coordinates are specified, the target bitmap will represent a view of
the source bitmaps current clipping rectangle. If coordinates are
specified, the target bitmap will represent a view of the rectangle with top
left corner at (x0, y0) and bottom right corner at (x1, y1) in the source
bitmap. Coordinates are in units of pixels. The bottom right coordinates
are inclusive.
The source bitmap will be locked, preventing resizing and reallocation. If
the target bitmap owns allocated storage, it will be freed; if it represents
a view of another bitmap, the current source bitmap will be released.
The source and target bitmaps must both be owned by the Lua script and must
use the same pixel format. Raises an error if coordinates are specified
representing a rectangle not fully contained within the source bitmaps
clipping rectangle; if the bitmaps storage is referenced by another bitmap
or a :ref:`texture <luareference-render-texture>`; or if the source and
target are the same bitmap.
bitmap:pix(x, y)
Returns the colour value of the pixel at the specified location.
Coordinates are zero-based in units of pixels.
bitmap:fill(color, [x0, y0, x1, y1])
Fills a portion of the bitmap with the specified colour value. If
coordinates are not specified, the clipping rectangle is filled; if
coordinates are specified, the intersection of the clipping rectangle and
the rectangle with top left corner at (x0, y0) and bottom right corner at
(x1, y1) is filled. Coordinates are in units of pixels. The bottom right
coordinates are inclusive.
bitmap:plot(x, y, color)
Sets the colour value of the pixel at the specified location if it is within
the clipping rectangle. Coordinates are zero-based in units of pixels.
bitmap:plot_box(x, y, width, height, color)
Fills the intersection of the clipping rectangle and the rectangle with top
left (x, y) and the specified height and width with the specified colour
value. Coordinates and dimensions are in units of pixels.
Properties
^^^^^^^^^^
bitmap.width (read-only)
Width of the bitmap in pixels.
bitmap.height (read-only)
Height of the bitmap in pixels.
bitmap.rowpixels (read-only)
Row stride of the bitmaps storage in pixels. That is, the difference in
pixel offsets of the pixels at the same horizontal location in consecutive
rows. May be greater than the width.
bitmap.rowbytes (read-only)
Row stride of the bitmaps storage in bytes. That is, the difference in
byte addresses of the pixels at the same horizontal location in consecutive
rows.
bitmap.bpp (read-only)
Size of the type used to represent pixels in the bitmap in bits (may be
larger than the number of significant bits).
bitmap.valid (read-only)
A Boolean indicating whether the bitmap has storage available (may be false
for empty bitmaps).
bitmap.locked (read-only)
A Boolean indicating whether the bitmaps storage is referenced by another
bitmap or a :ref:`texture <luareference-render-texture>`.
.. _luareference-render-texture:
Render texture
~~~~~~~~~~~~~~
Wraps MAMEs ``render_texture`` class, representing a texture that cam be drawn
in a :ref:`render container <luareference-render-container>`. Render textures
must be freed before the emulation session ends.
Instantiation
^^^^^^^^^^^^^
manager.machine.render:texture_alloc(bitmap)
Creates a render texture based on a :ref:`bitmap
<luareference-render-bitmap>`. The bitmap must be owned by the Lua script,
and must use the Y'CbCr, RGB or ARGB format. The bitmaps storage will be
locked, preventing resizing and reallocation.
Methods
^^^^^^^
texture:free()
Frees the texture. The storage of the underlying bitmap will be released.
Properties
^^^^^^^^^^
texture.valid (read-only)
A Boolean indicating whether the texture is valid (false if the texture has
been freed).
.. _luareference-render-manager:
Render manager
@ -2610,6 +2981,16 @@ Instantiation
manager.machine.render
Gets the global render manager instance for the emulation session.
Methods
^^^^^^^
render:texture_alloc(bitmap)
Creates a :ref:`render texture <luareference-render-texture>` based on a
:ref:`bitmap <luareference-render-bitmap>`. The bitmap must be owned by the
Lua script, and must use the Y'CbCr, RGB or ARGB format. The bitmaps
storage will be locked, preventing resizing and reallocation. Render
textures must be freed before the emulation session ends.
Properties
^^^^^^^^^^
@ -2718,7 +3099,7 @@ container:draw_box(left, top, right, bottom, [line], [fill])
Coordinates are floating-point numbers in the range of 0 (zero) to 1 (one),
with (0, 0) at the top left and (1, 1) at the bottom right of the window or
screen that showss the user interface. Note that the aspect ratio is
the screen that shows the user interface. Note that the aspect ratio is
usually not square. Coordinates are limited to the window or screen area.
The fill and line colours are in alpha/red/green/blue (ARGB) format.
@ -2729,21 +3110,14 @@ container:draw_box(left, top, right, bottom, [line], [fill])
most-significant to least-significant byte. If the line colour is not
provided, the UI text colour is used; if the fill colour is not provided,
the UI background colour is used.
container:draw_line(x1, y1, x2, y2, [color])
Draws a line from (x1, y1) to (x2, y2).
container:draw_line(x0, y0, x1, y1, [color])
Draws a line from (x0, y0) to (x1, y1).
Coordinates are floating-point numbers in the range of 0 (zero) to 1 (one),
with (0, 0) at the top left and (1, 1) at the bottom right of the window or
screen that showss the user interface. Note that the aspect ratio is
the screen that shows the user interface. Note that the aspect ratio is
usually not square. Coordinates are limited to the window or screen area.
Coordinates are floating-point numbers in units of screen pixels, with the
origin at (0, 0). Note that screen pixels often arent square. The
coordinate system is rotated if the screen is rotated, which is usually the
case for vertical-format screens. Before rotation, the origin is at the top
left, and coordinates increase to the right and downwards. Coordinates are
limited to the screen area.
The line colour is in alpha/red/green/blue (ARGB) format. Channel values
are in the range 0 (transparent or off) to 255 (opaque or full intensity),
inclusive. Colour channel values are not pre-multiplied by the alpha value.
@ -2751,6 +3125,24 @@ container:draw_line(x1, y1, x2, y2, [color])
integer, in the order alpha, red, green, blue from most-significant to
least-significant byte. If the line colour is not provided, the UI text
colour is used.
container:draw_quad(texture, x0, y0, x1, y1, [color])
Draws a textured rectangle with top left corner at (x0, y0) and bottom right
corner at (x1, y1). If a colour is specified, the ARGB channel values of
the textures pixels are multiplied by the corresponding values of the
specified colour.
Coordinates are floating-point numbers in the range of 0 (zero) to 1 (one),
with (0, 0) at the top left and (1, 1) at the bottom right of the window or
the screen that shows the user interface. Note that the aspect ratio is
usually not square. If the rectangle extends beyond the containers bounds,
it will be cropped.
The colour is in alpha/red/green/blue (ARGB) format. Channel values are in
the range 0 (transparent or off) to 255 (opaque or full intensity),
inclusive. Colour channel values are not pre-multiplied by the alpha value.
The channel values must be packed into the bytes of a 32-bit unsigned
integer, in the order alpha, red, green, blue from most-significant to
least-significant byte.
container:draw_text(x|justify, y, text, [foreground], [background])
Draws text at the specified position. If the screen is rotated the text
will be rotated.
@ -2765,7 +3157,7 @@ container:draw_text(x|justify, y, text, [foreground], [background])
Coordinates are floating-point numbers in the range of 0 (zero) to 1 (one),
with (0, 0) at the top left and (1, 1) at the bottom right of the window or
screen that showss the user interface. Note that the aspect ratio is
the screen that shows the user interface. Note that the aspect ratio is
usually not square. Coordinates are limited to the window or screen area.
The foreground and background colours are in alpha/red/green/blue (ARGB)

View File

@ -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>(

View File

@ -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);

View File

@ -36,7 +36,7 @@ inline int32_t bitmap_t::compute_rowpixels(int width, int xslop)
inline void bitmap_t::compute_base(int xslop, int yslop)
{
m_base = m_alloc.get() + (m_rowpixels * yslop + xslop) * (m_bpp / 8);
m_base = &m_alloc[(m_rowpixels * yslop + xslop) * (m_bpp / 8)];
}
@ -234,24 +234,28 @@ void bitmap_t::allocate(int width, int height, int xslop, int yslop)
reset();
// handle empty requests cleanly
if (width <= 0 || height <= 0)
return;
if ((0 < width) && (0 < height))
{
// allocate memory for the bitmap itself
int32_t const new_rowpixels = compute_rowpixels(width, xslop);
uint32_t const new_allocbytes = new_rowpixels * (height + 2 * yslop) * m_bpp / 8;
m_alloc.reset(new (std::nothrow) uint8_t[new_allocbytes]);
if (m_alloc)
{
// initialize fields
m_allocbytes = new_allocbytes;
m_rowpixels = new_rowpixels;
m_width = width;
m_height = height;
m_cliprect.set(0, width - 1, 0, height - 1);
// initialize fields
m_rowpixels = compute_rowpixels(width, xslop);
m_width = width;
m_height = height;
m_cliprect.set(0, width - 1, 0, height - 1);
// clear to 0 by default
memset(m_alloc.get(), 0, m_allocbytes);
// allocate memory for the bitmap itself
m_allocbytes = m_rowpixels * (m_height + 2 * yslop) * m_bpp / 8;
m_alloc.reset(new uint8_t[m_allocbytes]);
// clear to 0 by default
memset(m_alloc.get(), 0, m_allocbytes);
// compute the base
compute_base(xslop, yslop);
// compute the base
compute_base(xslop, yslop);
}
}
}
/**
@ -274,12 +278,12 @@ void bitmap_t::resize(int width, int height, int xslop, int yslop)
assert(m_bpp == 8 || m_bpp == 16 || m_bpp == 32 || m_bpp == 64);
// handle empty requests cleanly
if (width <= 0 || height <= 0)
if ((width <= 0) || (height <= 0))
width = height = 0;
// determine how much memory we need for the new bitmap
int new_rowpixels = compute_rowpixels(width, xslop);
uint32_t new_allocbytes = new_rowpixels * (height + 2 * yslop) * m_bpp / 8;
int32_t const new_rowpixels = compute_rowpixels(width, xslop);
uint32_t const new_allocbytes = new_rowpixels * (height + 2 * yslop) * m_bpp / 8;
if (new_allocbytes > m_allocbytes)
{
@ -290,7 +294,6 @@ void bitmap_t::resize(int width, int height, int xslop, int yslop)
}
else
{
// otherwise, reconfigure
m_rowpixels = new_rowpixels;
m_width = width;
@ -318,6 +321,7 @@ void bitmap_t::reset()
m_base = nullptr;
// reset all fields
m_allocbytes = 0;
m_rowpixels = 0;
m_width = 0;
m_height = 0;
@ -339,6 +343,9 @@ void bitmap_t::reset()
void bitmap_t::wrap(void *base, int width, int height, int rowpixels)
{
assert(base || (!width && !height));
assert(!m_alloc || (&m_alloc[0] > base) || (&m_alloc[m_allocbytes] <= base));
// delete any existing stuff
reset();
@ -364,6 +371,7 @@ void bitmap_t::wrap(void *base, int width, int height, int rowpixels)
void bitmap_t::wrap(bitmap_t &source, const rectangle &subrect)
{
assert(&source != this);
assert(m_format == source.m_format);
assert(m_bpp == source.m_bpp);
assert(source.cliprect().contains(subrect));