1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
|
// license:BSD-3-Clause
// copyright-holders:Aaron Giles, Vas Crabb
/***************************************************************************
rendlay.h
Core rendering layout parser and manager.
***************************************************************************/
#ifndef MAME_EMU_RENDLAY_H
#define MAME_EMU_RENDLAY_H
#pragma once
#include "rendertypes.h"
#include "screen.h"
#include <array>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <string_view>
#include <tuple>
#include <unordered_map>
#include <vector>
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
namespace emu::render::detail {
struct bounds_step
{
render_bounds get() const { return bounds; }
int state;
render_bounds bounds;
render_bounds delta;
};
using bounds_vector = std::vector<bounds_step>;
struct color_step
{
render_color get() const { return color; }
int state;
render_color color;
render_color delta;
};
using color_vector = std::vector<color_step>;
class layout_environment;
class view_environment;
} // namespace emu::render::detail
/// \brief A description of a piece of visible artwork
///
/// Most view items (except for those referencing screens) have exactly
/// one layout_element which describes the contents of the item.
/// Elements are separate from items because they can be re-used
/// multiple times within a layout. Even though an element can contain
/// a number of components, they are drawn as a single textured quad.
class layout_element
{
public:
using environment = emu::render::detail::layout_environment;
// construction/destruction
layout_element(environment &env, util::xml::data_node const &elemnode);
virtual ~layout_element();
// getters
running_machine &machine() const { return m_machine; }
int default_state() const { return m_defstate; }
render_texture *state_texture(int state);
// operations
void preload();
private:
/// \brief A drawing component within a layout element
///
/// Each #layout_element contains one or more components. Each
/// component can describe either an image or a rectangle/disk
/// primitive. A component can also have a state mask and value
/// for controlling visibility. If the state of the item
/// instantiating the element matches the component's state value
/// for the bits that are set in the mask, the component is visible.
class component
{
public:
typedef std::unique_ptr<component> ptr;
// construction/destruction
component(environment &env, util::xml::data_node const &compnode);
virtual ~component() = default;
// setup
void normalize_bounds(float xoffs, float yoffs, float xscale, float yscale);
// getters
int statemask() const { return m_statemask; }
int stateval() const { return m_stateval; }
std::pair<int, bool> statewrap() const;
render_bounds overall_bounds() const;
render_bounds bounds(int state) const;
render_color color(int state) const;
// operations
virtual void preload(running_machine &machine);
virtual void draw(running_machine &machine, bitmap_argb32 &dest, int state);
protected:
// helpers
virtual int maxstate() const;
virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state);
// drawing helpers
static void draw_text(render_font &font, bitmap_argb32 &dest, const rectangle &bounds, std::string_view str, int align, const render_color &color);
static void draw_segment_horizontal_caps(bitmap_argb32 &dest, int minx, int maxx, int midy, int width, int caps, rgb_t color);
static void draw_segment_horizontal(bitmap_argb32 &dest, int minx, int maxx, int midy, int width, rgb_t color);
static void draw_segment_vertical_caps(bitmap_argb32 &dest, int miny, int maxy, int midx, int width, int caps, rgb_t color);
static void draw_segment_vertical(bitmap_argb32 &dest, int miny, int maxy, int midx, int width, rgb_t color);
static void draw_segment_diagonal_1(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color);
static void draw_segment_diagonal_2(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color);
static void draw_segment_decimal(bitmap_argb32 &dest, int midx, int midy, int width, rgb_t color);
static void draw_segment_comma(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color);
static void apply_skew(bitmap_argb32 &dest, int skewwidth);
private:
using bounds_vector = emu::render::detail::bounds_vector;
using color_vector = emu::render::detail::color_vector;
// internal state
int const m_statemask; // bits of state used to control visibility
int const m_stateval; // masked state value to make component visible
bounds_vector m_bounds; // bounds of the element
color_vector m_color; // color of the element
};
// component implementations
class image_component;
class rect_component;
class disk_component;
class text_component;
class led7seg_component;
class led14seg_component;
class led16seg_component;
class led14segsc_component;
class led16segsc_component;
class simplecounter_component;
class reel_component;
// a texture encapsulates a texture for a given element in a given state
class texture
{
public:
texture();
texture(texture const &that) = delete;
texture(texture &&that);
~texture();
texture &operator=(texture const &that) = delete;
texture &operator=(texture &&that);
layout_element * m_element; // pointer back to the element
render_texture * m_texture; // texture for this state
int m_state; // associated state number
};
typedef component::ptr (*make_component_func)(environment &env, util::xml::data_node const &compnode);
typedef std::map<std::string, make_component_func> make_component_map;
// internal helpers
static void element_scale(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param);
template <typename T> static component::ptr make_component(environment &env, util::xml::data_node const &compnode);
static make_component_map const s_make_component; // maps component XML names to creator functions
// internal state
running_machine & m_machine; // reference to the owning machine
std::vector<component::ptr> m_complist; // list of components
int const m_defstate; // default state of this element
int m_statemask; // mask to apply to state values
bool m_foldhigh; // whether we need to fold state values above the mask range
std::vector<texture> m_elemtex; // array of element textures used for managing the scaled bitmaps
};
/// \brief A reusable group of items
///
/// Views expand/flatten groups into their component elements applying
/// an optional coordinate transform. This is useful for duplicating
/// the same sublayout in multiple views, or grouping related items to
/// simplify overall view arrangement. Groups only exist while parsing
/// a layout file - no information about element grouping is preserved
/// after the views have been built.
class layout_group
{
public:
using environment = emu::render::detail::layout_environment;
using group_map = std::unordered_map<std::string, layout_group>;
using transform = std::array<std::array<float, 3>, 3>;
layout_group(util::xml::data_node const &groupnode);
~layout_group();
util::xml::data_node const &get_groupnode() const { return m_groupnode; }
transform make_transform(int orientation, render_bounds const &dest) const;
transform make_transform(int orientation, transform const &trans) const;
transform make_transform(int orientation, render_bounds const &dest, transform const &trans) const;
void set_bounds_unresolved();
void resolve_bounds(environment &env, group_map &groupmap);
private:
void resolve_bounds(environment &env, group_map &groupmap, std::vector<layout_group const *> &seen);
void resolve_bounds(
environment &env,
util::xml::data_node const &parentnode,
group_map &groupmap,
std::vector<layout_group const *> &seen,
bool &empty,
bool vistoggle,
bool repeat,
bool init);
util::xml::data_node const & m_groupnode;
render_bounds m_bounds;
bool m_bounds_resolved;
};
/// \brief A single item in a view
///
/// Each view has a list of item structures describing the visual
/// elements to draw, where they are located, additional blending modes,
/// and bindings for inputs and outputs.
class layout_view_item
{
friend class layout_view;
public:
using view_environment = emu::render::detail::view_environment;
using element_map = std::unordered_map<std::string, layout_element>;
using state_delegate = delegate<int ()>;
using bounds_delegate = delegate<render_bounds ()>;
using color_delegate = delegate<render_color ()>;
using scroll_size_delegate = delegate<float ()>;
using scroll_pos_delegate = delegate<float ()>;
// construction/destruction
layout_view_item(
view_environment &env,
util::xml::data_node const &itemnode,
element_map &elemmap,
int orientation,
layout_group::transform const &trans,
render_color const &color);
~layout_view_item();
// getters
std::string const &id() const { return m_id; }
layout_element *element() const { return m_element; }
screen_device *screen() const { return m_screen; }
bool bounds_animated() const { return m_bounds.size() > 1U; }
bool color_animated() const { return m_color.size() > 1U; }
render_bounds bounds() const { return m_get_bounds(); }
render_color color() const { return m_get_color(); }
bool scroll_wrap_x() const { return m_scrollwrapx; }
bool scroll_wrap_y() const { return m_scrollwrapy; }
float scroll_size_x() const { return m_get_scroll_size_x(); }
float scroll_size_y() const { return m_get_scroll_size_y(); }
float scroll_pos_x() const { return m_get_scroll_pos_x(); }
float scroll_pos_y() const { return m_get_scroll_pos_y(); }
int blend_mode() const { return m_blend_mode; }
u32 visibility_mask() const { return m_visibility_mask; }
int orientation() const { return m_orientation; }
render_container *screen_container() const { return m_screen ? &m_screen->container() : nullptr; }
// interactivity
bool has_input() const { return bool(m_input_port); }
std::pair<ioport_port *, ioport_value> input_tag_and_mask() const { return std::make_pair(m_input_port, m_input_mask); }
bool clickthrough() const { return m_clickthrough; }
// fetch state based on configured source
int element_state() const { return m_get_elem_state(); }
int animation_state() const { return m_get_anim_state(); }
// set state
void set_state(int state) { m_elem_state = state; }
void set_scroll_size_x(float size) { m_scrollsizex = std::clamp(size, 0.01f, 1.0f); }
void set_scroll_size_y(float size) { m_scrollsizey = std::clamp(size, 0.01f, 1.0f); }
void set_scroll_pos_x(float pos) { m_scrollposx = pos; }
void set_scroll_pos_y(float pos) { m_scrollposy = pos; }
// set handlers
void set_element_state_callback(state_delegate &&handler);
void set_animation_state_callback(state_delegate &&handler);
void set_bounds_callback(bounds_delegate &&handler);
void set_color_callback(color_delegate &&handler);
void set_scroll_size_x_callback(scroll_size_delegate &&handler);
void set_scroll_size_y_callback(scroll_size_delegate &&handler);
void set_scroll_pos_x_callback(scroll_pos_delegate &&handler);
void set_scroll_pos_y_callback(scroll_pos_delegate &&handler);
// resolve tags, if any
void resolve_tags();
private:
using bounds_vector = emu::render::detail::bounds_vector;
using color_vector = emu::render::detail::color_vector;
state_delegate default_get_elem_state();
state_delegate default_get_anim_state();
bounds_delegate default_get_bounds();
color_delegate default_get_color();
scroll_size_delegate default_get_scroll_size_x();
scroll_size_delegate default_get_scroll_size_y();
scroll_pos_delegate default_get_scroll_pos_x();
scroll_pos_delegate default_get_scroll_pos_y();
int get_state() const;
int get_output() const;
int get_input_raw() const;
int get_input_field_cached() const;
int get_input_field_conditional() const;
int get_anim_output() const;
int get_anim_input() const;
float get_scrollsizex() const;
float get_scrollsizey() const;
float get_scrollposx() const;
float get_scrollposy() const;
template <bool Wrap> float get_scrollx_output() const;
template <bool Wrap> float get_scrolly_output() const;
template <bool Wrap> float get_scrollx_input() const;
template <bool Wrap> float get_scrolly_input() const;
render_bounds get_interpolated_bounds() const;
render_color get_interpolated_color() const;
static layout_element *find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap);
static bounds_vector make_bounds(view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans);
static color_vector make_color(view_environment &env, util::xml::data_node const &itemnode, render_color const &mult);
// internal state
layout_element *const m_element; // pointer to the associated element (non-screens only)
state_delegate m_get_elem_state; // resolved element state function
state_delegate m_get_anim_state; // resolved animation state function
bounds_delegate m_get_bounds; // resolved bounds function
color_delegate m_get_color; // resolved color function
scroll_size_delegate m_get_scroll_size_x; // resolved horizontal scroll window size function
scroll_size_delegate m_get_scroll_size_y; // resolved vertical scroll window size function
scroll_pos_delegate m_get_scroll_pos_x; // resolved horizontal scroll position function
scroll_pos_delegate m_get_scroll_pos_y; // resolved vertical scroll position function
output_finder<> m_output; // associated output
output_finder<> m_animoutput; // associated output for animation if different
output_finder<> m_scrollxoutput; // associated output for horizontal scroll position
output_finder<> m_scrollyoutput; // associated output for vertical scroll position
ioport_port * m_animinput_port; // input port used for animation
ioport_port * m_scrollxinput_port; // input port used for horizontal scrolling
ioport_port * m_scrollyinput_port; // input port used for vertical scrolling
bool const m_scrollwrapx; // whether horizontal scrolling works like a loop
bool const m_scrollwrapy; // whether vertical scrolling works like a loop
int m_elem_state; // element state used in absence of bindings
float m_scrollsizex; // horizontal scroll window size used in absence of bindings
float m_scrollsizey; // vertical scroll window size used in absence of bindings
float m_scrollposx; // horizontal scroll position used in absence of bindings
float m_scrollposy; // vertical scroll position used in absence of bindings
ioport_value const m_animmask; // mask for animation state
ioport_value const m_scrollxmask; // mask for horizontal scroll position
ioport_value const m_scrollymask; // mask for vertical scroll position
ioport_value const m_scrollxmin; // minimum value for horizontal scroll position
ioport_value const m_scrollymin; // minimum value for vertical scroll position
ioport_value const m_scrollxmax; // maximum value for horizontal scroll position
ioport_value const m_scrollymax; // maximum value for vertical scroll position
u8 const m_animshift; // shift for animation state
u8 const m_scrollxshift; // shift for horizontal scroll position
u8 const m_scrollyshift; // shift for vertical scroll position
ioport_port * m_input_port; // input port of this item
ioport_field const * m_input_field; // input port field of this item
ioport_value const m_input_mask; // input mask of this item
u8 const m_input_shift; // input mask rightshift for raw (trailing 0s)
bool m_clickthrough; // should click pass through to lower elements
screen_device * m_screen; // pointer to screen
int const m_orientation; // orientation of this item
bounds_vector m_bounds; // bounds of the item
color_vector const m_color; // color of the item
int m_blend_mode; // blending mode to use when drawing
u32 m_visibility_mask; // combined mask of parent visibility groups
// cold items
std::string const m_id; // optional unique item identifier
std::string const m_input_tag; // input tag of this item
std::string const m_animinput_tag; // tag of input port for animation state
std::string const m_scrollxinput_tag; // tag of input port for horizontal scroll position
std::string const m_scrollyinput_tag; // tag of input port for vertical scroll position
bounds_vector const m_rawbounds; // raw (original) bounds of the item
bool const m_have_output; // whether we actually have an output
bool const m_input_raw; // get raw data from input port
bool const m_have_animoutput; // whether we actually have an output for animation
bool const m_have_scrollxoutput; // whether we actually have an output for horizontal scroll
bool const m_have_scrollyoutput; // whether we actually have an output for vertical scroll
bool const m_has_clickthrough; // whether clickthrough was explicitly configured
};
/// \brief A single view within a #layout_file
///
/// The view is described using arbitrary coordinates that are scaled to
/// fit within the render target. Pixels within a view are assumed to
/// be square.
class layout_view
{
public:
using layout_environment = emu::render::detail::layout_environment;
using view_environment = emu::render::detail::view_environment;
using element_map = layout_view_item::element_map;
using group_map = std::unordered_map<std::string, layout_group>;
using screen_ref_vector = std::vector<std::reference_wrapper<screen_device const>>;
using prepare_items_delegate = delegate<void ()>;
using preload_delegate = delegate<void ()>;
using recomputed_delegate = delegate<void ()>;
using item = layout_view_item;
using item_list = std::list<item>;
using item_ref_vector = std::vector<std::reference_wrapper<item> >;
/// \brief A subset of items in a view that can be hidden or shown
///
/// Visibility toggles allow the user to show or hide selected parts
/// of a view.
class visibility_toggle
{
public:
// construction/destruction/assignment
visibility_toggle(std::string &&name, u32 mask);
visibility_toggle(visibility_toggle const &) = default;
visibility_toggle(visibility_toggle &&) = default;
visibility_toggle &operator=(visibility_toggle const &) = default;
visibility_toggle &operator=(visibility_toggle &&) = default;
// getters
std::string const &name() const { return m_name; }
u32 mask() const { return m_mask; }
private:
std::string m_name; // display name for the toggle
u32 m_mask; // toggle combination to show
};
using visibility_toggle_vector = std::vector<visibility_toggle>;
/// \brief An edge of an item in a view
class edge
{
public:
// construction/destruction
constexpr edge(unsigned index, float position, bool trailing)
: m_index(index)
, m_position(position)
, m_trailing(trailing)
{
}
// getters
constexpr unsigned index() const { return m_index; }
constexpr float position() const { return m_position; }
constexpr bool trailing() const { return m_trailing; }
// comparison
constexpr bool operator<(edge const &that) const
{
return std::make_tuple(m_position, m_trailing, m_index) < std::make_tuple(that.m_position, that.m_trailing, that.m_index);
}
private:
unsigned m_index; // index of item in some collection
float m_position; // position of edge on given axis
bool m_trailing; // false for edge at lower position on axis
};
using edge_vector = std::vector<edge>;
// construction/destruction
layout_view(
layout_environment &env,
util::xml::data_node const &viewnode,
element_map &elemmap,
group_map &groupmap);
~layout_view();
// getters
item *get_item(std::string const &id);
item_list &items() { return m_items; }
bool has_screen(screen_device const &screen) const;
const std::string &name() const { return m_name; }
const std::string &unqualified_name() const { return m_unqualified_name; }
size_t visible_screen_count() const { return m_screens.size(); }
float effective_aspect() const { return m_effaspect; }
const render_bounds &bounds() const { return m_bounds; }
bool has_visible_screen(screen_device const &screen) const;
const item_ref_vector &visible_items() const { return m_visible_items; }
const item_ref_vector &visible_screen_items() const { return m_screen_items; }
const item_ref_vector &interactive_items() const { return m_interactive_items; }
const edge_vector &interactive_edges_x() const { return m_interactive_edges_x; }
const edge_vector &interactive_edges_y() const { return m_interactive_edges_y; }
const screen_ref_vector &visible_screens() const { return m_screens; }
const visibility_toggle_vector &visibility_toggles() const { return m_vistoggles; }
u32 default_visibility_mask() const { return m_defvismask; }
bool has_art() const { return m_has_art; }
// set handlers
void set_prepare_items_callback(prepare_items_delegate &&handler);
void set_preload_callback(preload_delegate &&handler);
void set_recomputed_callback(recomputed_delegate &&handler);
// operations
void prepare_items() { if (!m_prepare_items.isnull()) m_prepare_items(); }
void recompute(u32 visibility_mask, bool zoom_to_screens);
void preload();
// resolve tags, if any
void resolve_tags();
private:
struct layer_lists;
using item_id_map = std::unordered_map<
std::reference_wrapper<std::string const>,
item &,
std::hash<std::string>,
std::equal_to<std::string> >;
// add items, recursing for groups
void add_items(
layer_lists &layers,
view_environment &env,
util::xml::data_node const &parentnode,
element_map &elemmap,
group_map &groupmap,
int orientation,
layout_group::transform const &trans,
render_color const &color,
bool root,
bool repeat,
bool init);
static std::string make_name(layout_environment &env, util::xml::data_node const &viewnode);
// internal state
float m_effaspect; // X/Y of the layout in current configuration
render_bounds m_bounds; // computed bounds of the view in current configuration
item_list m_items; // list of layout items
item_ref_vector m_visible_items; // all visible items
item_ref_vector m_screen_items; // visible items that represent screens to draw
item_ref_vector m_interactive_items;// visible items that can accept pointer input
edge_vector m_interactive_edges_x;
edge_vector m_interactive_edges_y;
screen_ref_vector m_screens; // list screens visible in current configuration
// handlers
prepare_items_delegate m_prepare_items; // prepare items for adding to render container
preload_delegate m_preload; // additional actions when visible items change
recomputed_delegate m_recomputed; // additional actions on resizing/visibility change
// cold items
std::string m_name; // display name for the view
std::string m_unqualified_name; // the name exactly as specified in the layout file
item_id_map m_items_by_id; // items with non-empty ID indexed by ID
visibility_toggle_vector m_vistoggles; // collections of items that can be shown/hidden
render_bounds m_expbounds; // explicit bounds of the view
u32 m_defvismask; // default visibility mask
bool m_has_art; // true if the layout contains non-screen elements
};
/// \brief Layout description file
///
/// Comprises a list of elements and a list of views. The elements are
/// reusable items that the views reference.
class layout_file
{
public:
using element_map = std::unordered_map<std::string, layout_element>;
using group_map = std::unordered_map<std::string, layout_group>;
using view_list = std::list<layout_view>;
using resolve_tags_delegate = delegate<void ()>;
// construction/destruction
layout_file(device_t &device, util::xml::data_node const &rootnode, char const *searchpath, char const *dirname);
~layout_file();
// getters
device_t &device() const { return m_device; }
element_map const &elements() const { return m_elemmap; }
view_list &views() { return m_viewlist; }
view_list const &views() const { return m_viewlist; }
// resolve tags, if any
void resolve_tags();
// set handlers
void set_resolve_tags_callback(resolve_tags_delegate &&handler);
private:
using environment = emu::render::detail::layout_environment;
// add elements and parameters
void add_elements(
environment &env,
util::xml::data_node const &parentnode,
group_map &groupmap,
bool repeat,
bool init);
// internal state
device_t & m_device; // device that caused file to be loaded
element_map m_elemmap; // list of shared layout elements
view_list m_viewlist; // list of views
resolve_tags_delegate m_resolve_tags; // additional actions after resolving tags
};
#endif // MAME_EMU_RENDLAY_H
|