mirror of
https://github.com/holub/mame
synced 2025-04-18 22:49:58 +03:00
2492 lines
78 KiB
C++
2492 lines
78 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Aaron Giles
|
|
/***************************************************************************
|
|
|
|
rendlay.c
|
|
|
|
Core rendering layout parser and manager.
|
|
|
|
****************************************************************************
|
|
|
|
Overview of objects:
|
|
|
|
layout_file -- A layout_file comprises a list of elements and a
|
|
list of views. The elements are reusable items that the views
|
|
reference.
|
|
|
|
layout_view -- A layout_view describes 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.
|
|
|
|
view_item -- Each view has four lists of view_items, one for each
|
|
"layer." Each view item is specified using floating point
|
|
coordinates in arbitrary units, and is assumed to have square
|
|
pixels. Each view item can control its orientation independently.
|
|
Each item can also have an optional name, and can be set at
|
|
runtime into different "states", which control how the embedded
|
|
elements are displayed.
|
|
|
|
layout_element -- A layout_element is a description of a piece of
|
|
visible artwork. Most view_items (except for those in the screen
|
|
layer) 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 treated
|
|
as if they were a single bitmap.
|
|
|
|
element_component -- Each layout_element contains one or more
|
|
components. Each component can describe either an image or
|
|
a rectangle/disk primitive. Each component also has a "state"
|
|
associated with it, which controls whether or not the component
|
|
is visible (if the owning item has the same state, it is
|
|
visible).
|
|
|
|
***************************************************************************/
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "emu.h"
|
|
#include "emuopts.h"
|
|
#include "render.h"
|
|
#include "rendfont.h"
|
|
#include "rendlay.h"
|
|
#include "rendutil.h"
|
|
#include "xmlfile.h"
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
STANDARD LAYOUTS
|
|
***************************************************************************/
|
|
|
|
// screenless layouts
|
|
#include "noscreens.lh"
|
|
|
|
// single screen layouts
|
|
#include "horizont.lh"
|
|
#include "vertical.lh"
|
|
|
|
// dual screen layouts
|
|
#include "dualhsxs.lh"
|
|
#include "dualhovu.lh"
|
|
#include "dualhuov.lh"
|
|
|
|
// triple screen layouts
|
|
#include "triphsxs.lh"
|
|
|
|
// quad screen layouts
|
|
#include "quadhsxs.lh"
|
|
|
|
// LCD screen layouts
|
|
#include "lcd.lh"
|
|
#include "lcd_rot.lh"
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// CONSTANTS
|
|
//**************************************************************************
|
|
|
|
const int LAYOUT_VERSION = 2;
|
|
|
|
enum
|
|
{
|
|
LINE_CAP_NONE = 0,
|
|
LINE_CAP_START = 1,
|
|
LINE_CAP_END = 2
|
|
};
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// GLOBAL VARIABLES
|
|
//**************************************************************************
|
|
|
|
render_screen_list render_target::s_empty_screen_list;
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// INLINE HELPERS
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// gcd - compute the greatest common divisor (GCD)
|
|
// of two integers using the Euclidean algorithm
|
|
//-------------------------------------------------
|
|
|
|
inline int gcd(int a, int b)
|
|
{
|
|
while (b != 0)
|
|
{
|
|
int t = b;
|
|
b = a % b;
|
|
a = t;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// reduce_fraction - reduce a fraction by
|
|
// dividing out common factors
|
|
//-------------------------------------------------
|
|
|
|
inline void reduce_fraction(int &num, int &den)
|
|
{
|
|
// search the greatest common divisor
|
|
int div = gcd(num, den);
|
|
|
|
// reduce the fraction if a common divisor has been found
|
|
if (div > 1)
|
|
{
|
|
num /= div;
|
|
den /= div;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// SHARED PARSING HELPERS
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// get_variable_value - compute the value of
|
|
// a variable in an XML attribute
|
|
//-------------------------------------------------
|
|
|
|
static int get_variable_value(running_machine &machine, const char *string, char **outputptr)
|
|
{
|
|
char temp[100];
|
|
|
|
// screen 0 parameters
|
|
screen_device_iterator iter(machine.root_device());
|
|
int scrnum = 0;
|
|
for (const screen_device *device = iter.first(); device != NULL; device = iter.next(), scrnum++)
|
|
{
|
|
// native X aspect factor
|
|
sprintf(temp, "~scr%dnativexaspect~", scrnum);
|
|
if (!strncmp(string, temp, strlen(temp)))
|
|
{
|
|
int num = device->visible_area().width();
|
|
int den = device->visible_area().height();
|
|
reduce_fraction(num, den);
|
|
*outputptr += sprintf(*outputptr, "%d", num);
|
|
return strlen(temp);
|
|
}
|
|
|
|
// native Y aspect factor
|
|
sprintf(temp, "~scr%dnativeyaspect~", scrnum);
|
|
if (!strncmp(string, temp, strlen(temp)))
|
|
{
|
|
int num = device->visible_area().width();
|
|
int den = device->visible_area().height();
|
|
reduce_fraction(num, den);
|
|
*outputptr += sprintf(*outputptr, "%d", den);
|
|
return strlen(temp);
|
|
}
|
|
|
|
// native width
|
|
sprintf(temp, "~scr%dwidth~", scrnum);
|
|
if (!strncmp(string, temp, strlen(temp)))
|
|
{
|
|
*outputptr += sprintf(*outputptr, "%d", device->visible_area().width());
|
|
return strlen(temp);
|
|
}
|
|
|
|
// native height
|
|
sprintf(temp, "~scr%dheight~", scrnum);
|
|
if (!strncmp(string, temp, strlen(temp)))
|
|
{
|
|
*outputptr += sprintf(*outputptr, "%d", device->visible_area().height());
|
|
return strlen(temp);
|
|
}
|
|
}
|
|
|
|
// default: copy the first character and continue
|
|
**outputptr = *string;
|
|
*outputptr += 1;
|
|
return 1;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// xml_get_attribute_string_with_subst - analog
|
|
// to xml_get_attribute_string but with variable
|
|
// substitution
|
|
//-------------------------------------------------
|
|
|
|
static const char *xml_get_attribute_string_with_subst(running_machine &machine, xml_data_node &node, const char *attribute, const char *defvalue)
|
|
{
|
|
const char *str = xml_get_attribute_string(&node, attribute, NULL);
|
|
static char buffer[1000];
|
|
|
|
// if nothing, just return the default
|
|
if (str == NULL)
|
|
return defvalue;
|
|
|
|
// if no tildes, don't worry
|
|
if (strchr(str, '~') == NULL)
|
|
return str;
|
|
|
|
// make a copy of the string, doing substitutions along the way
|
|
const char *s;
|
|
char *d;
|
|
for (s = str, d = buffer; *s != 0; )
|
|
{
|
|
// if not a variable, just copy
|
|
if (*s != '~')
|
|
*d++ = *s++;
|
|
|
|
// extract the variable
|
|
else
|
|
s += get_variable_value(machine, s, &d);
|
|
}
|
|
*d = 0;
|
|
return buffer;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// xml_get_attribute_int_with_subst - analog
|
|
// to xml_get_attribute_int but with variable
|
|
// substitution
|
|
//-------------------------------------------------
|
|
|
|
static int xml_get_attribute_int_with_subst(running_machine &machine, xml_data_node &node, const char *attribute, int defvalue)
|
|
{
|
|
const char *string = xml_get_attribute_string_with_subst(machine, node, attribute, NULL);
|
|
int value;
|
|
unsigned int uvalue;
|
|
|
|
if (string == NULL)
|
|
return defvalue;
|
|
if (string[0] == '$')
|
|
return (sscanf(&string[1], "%X", &uvalue) == 1) ? uvalue : defvalue;
|
|
if (string[0] == '0' && string[1] == 'x')
|
|
return (sscanf(&string[2], "%X", &uvalue) == 1) ? uvalue : defvalue;
|
|
if (string[0] == '#')
|
|
return (sscanf(&string[1], "%d", &value) == 1) ? value : defvalue;
|
|
return (sscanf(&string[0], "%d", &value) == 1) ? value : defvalue;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// xml_get_attribute_float_with_subst - analog
|
|
// to xml_get_attribute_float but with variable
|
|
// substitution
|
|
//-------------------------------------------------
|
|
|
|
static float xml_get_attribute_float_with_subst(running_machine &machine, xml_data_node &node, const char *attribute, float defvalue)
|
|
{
|
|
const char *string = xml_get_attribute_string_with_subst(machine, node, attribute, NULL);
|
|
float value;
|
|
|
|
if (string == NULL || sscanf(string, "%f", &value) != 1)
|
|
return defvalue;
|
|
return value;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_bounds - parse a bounds XML node
|
|
//-------------------------------------------------
|
|
|
|
void parse_bounds(running_machine &machine, xml_data_node *boundsnode, render_bounds &bounds)
|
|
{
|
|
// skip if nothing
|
|
if (boundsnode == NULL)
|
|
{
|
|
bounds.x0 = bounds.y0 = 0.0f;
|
|
bounds.x1 = bounds.y1 = 1.0f;
|
|
return;
|
|
}
|
|
|
|
// parse out the data
|
|
if (xml_get_attribute(boundsnode, "left") != NULL)
|
|
{
|
|
// left/right/top/bottom format
|
|
bounds.x0 = xml_get_attribute_float_with_subst(machine, *boundsnode, "left", 0.0f);
|
|
bounds.x1 = xml_get_attribute_float_with_subst(machine, *boundsnode, "right", 1.0f);
|
|
bounds.y0 = xml_get_attribute_float_with_subst(machine, *boundsnode, "top", 0.0f);
|
|
bounds.y1 = xml_get_attribute_float_with_subst(machine, *boundsnode, "bottom", 1.0f);
|
|
}
|
|
else if (xml_get_attribute(boundsnode, "x") != NULL)
|
|
{
|
|
// x/y/width/height format
|
|
bounds.x0 = xml_get_attribute_float_with_subst(machine, *boundsnode, "x", 0.0f);
|
|
bounds.x1 = bounds.x0 + xml_get_attribute_float_with_subst(machine, *boundsnode, "width", 1.0f);
|
|
bounds.y0 = xml_get_attribute_float_with_subst(machine, *boundsnode, "y", 0.0f);
|
|
bounds.y1 = bounds.y0 + xml_get_attribute_float_with_subst(machine, *boundsnode, "height", 1.0f);
|
|
}
|
|
else
|
|
throw emu_fatalerror("Illegal bounds value in XML");
|
|
|
|
// check for errors
|
|
if (bounds.x0 > bounds.x1 || bounds.y0 > bounds.y1)
|
|
throw emu_fatalerror("Illegal bounds value in XML: (%f-%f)-(%f-%f)",
|
|
(double) bounds.x0, (double) bounds.x1, (double) bounds.y0, (double) bounds.y1);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_color - parse a color XML node
|
|
//-------------------------------------------------
|
|
|
|
void parse_color(running_machine &machine, xml_data_node *colornode, render_color &color)
|
|
{
|
|
// skip if nothing
|
|
if (colornode == NULL)
|
|
{
|
|
color.r = color.g = color.b = color.a = 1.0f;
|
|
return;
|
|
}
|
|
|
|
// parse out the data
|
|
color.r = xml_get_attribute_float_with_subst(machine, *colornode, "red", 1.0);
|
|
color.g = xml_get_attribute_float_with_subst(machine, *colornode, "green", 1.0);
|
|
color.b = xml_get_attribute_float_with_subst(machine, *colornode, "blue", 1.0);
|
|
color.a = xml_get_attribute_float_with_subst(machine, *colornode, "alpha", 1.0);
|
|
|
|
// check for errors
|
|
if (color.r < 0.0f || color.r > 1.0f || color.g < 0.0f || color.g > 1.0f ||
|
|
color.b < 0.0f || color.b > 1.0f || color.a < 0.0f || color.a > 1.0f)
|
|
throw emu_fatalerror("Illegal ARGB color value in XML: %f,%f,%f,%f",
|
|
(double) color.r, (double) color.g, (double) color.b, (double) color.a);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_orientation - parse an orientation XML
|
|
// node
|
|
//-------------------------------------------------
|
|
|
|
static void parse_orientation(running_machine &machine, xml_data_node *orientnode, int &orientation)
|
|
{
|
|
// skip if nothing
|
|
if (orientnode == NULL)
|
|
{
|
|
orientation = ROT0;
|
|
return;
|
|
}
|
|
|
|
// parse out the data
|
|
int rotate = xml_get_attribute_int_with_subst(machine, *orientnode, "rotate", 0);
|
|
switch (rotate)
|
|
{
|
|
case 0: orientation = ROT0; break;
|
|
case 90: orientation = ROT90; break;
|
|
case 180: orientation = ROT180; break;
|
|
case 270: orientation = ROT270; break;
|
|
default: throw emu_fatalerror("Invalid rotation in XML orientation node: %d", rotate);
|
|
}
|
|
if (strcmp("yes", xml_get_attribute_string_with_subst(machine, *orientnode, "swapxy", "no")) == 0)
|
|
orientation ^= ORIENTATION_SWAP_XY;
|
|
if (strcmp("yes", xml_get_attribute_string_with_subst(machine, *orientnode, "flipx", "no")) == 0)
|
|
orientation ^= ORIENTATION_FLIP_X;
|
|
if (strcmp("yes", xml_get_attribute_string_with_subst(machine, *orientnode, "flipy", "no")) == 0)
|
|
orientation ^= ORIENTATION_FLIP_Y;
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// LAYOUT ELEMENT
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// layout_element - constructor
|
|
//-------------------------------------------------
|
|
|
|
layout_element::layout_element(running_machine &machine, xml_data_node &elemnode, const char *dirname)
|
|
: m_next(NULL),
|
|
m_machine(machine),
|
|
m_defstate(0),
|
|
m_maxstate(0)
|
|
{
|
|
// extract the name
|
|
const char *name = xml_get_attribute_string_with_subst(machine, elemnode, "name", NULL);
|
|
if (name == NULL)
|
|
throw emu_fatalerror("All layout elements must have a name!\n");
|
|
m_name = name;
|
|
|
|
// get the default state
|
|
m_defstate = xml_get_attribute_int_with_subst(machine, elemnode, "defstate", -1);
|
|
|
|
// parse components in order
|
|
bool first = true;
|
|
render_bounds bounds = { 0 };
|
|
for (xml_data_node *compnode = elemnode.child; compnode != NULL; compnode = compnode->next)
|
|
{
|
|
// allocate a new component
|
|
component &newcomp = m_complist.append(*global_alloc(component(machine, *compnode, dirname)));
|
|
|
|
// accumulate bounds
|
|
if (first)
|
|
bounds = newcomp.m_bounds;
|
|
else
|
|
union_render_bounds(&bounds, &newcomp.m_bounds);
|
|
first = false;
|
|
|
|
// determine the maximum state
|
|
if (newcomp.m_state > m_maxstate)
|
|
m_maxstate = newcomp.m_state;
|
|
if (newcomp.m_type == component::CTYPE_LED7SEG || newcomp.m_type == component::CTYPE_LED8SEG_GTS1)
|
|
m_maxstate = 255;
|
|
if (newcomp.m_type == component::CTYPE_LED14SEG)
|
|
m_maxstate = 16383;
|
|
if (newcomp.m_type == component::CTYPE_LED14SEGSC || newcomp.m_type == component::CTYPE_LED16SEG)
|
|
m_maxstate = 65535;
|
|
if (newcomp.m_type == component::CTYPE_LED16SEGSC)
|
|
m_maxstate = 262143;
|
|
if (newcomp.m_type == component::CTYPE_DOTMATRIX)
|
|
m_maxstate = 255;
|
|
if (newcomp.m_type == component::CTYPE_DOTMATRIX5DOT)
|
|
m_maxstate = 31;
|
|
if (newcomp.m_type == component::CTYPE_DOTMATRIXDOT)
|
|
m_maxstate = 1;
|
|
if (newcomp.m_type == component::CTYPE_SIMPLECOUNTER)
|
|
m_maxstate = xml_get_attribute_int_with_subst(machine, *compnode, "maxstate", 999);
|
|
if (newcomp.m_type == component::CTYPE_REEL)
|
|
m_maxstate = 65536;
|
|
}
|
|
|
|
if (m_complist.first() != NULL)
|
|
{
|
|
// determine the scale/offset for normalization
|
|
float xoffs = bounds.x0;
|
|
float yoffs = bounds.y0;
|
|
float xscale = 1.0f / (bounds.x1 - bounds.x0);
|
|
float yscale = 1.0f / (bounds.y1 - bounds.y0);
|
|
|
|
// normalize all the component bounds
|
|
for (component *curcomp = m_complist.first(); curcomp != NULL; curcomp = curcomp->next())
|
|
{
|
|
curcomp->m_bounds.x0 = (curcomp->m_bounds.x0 - xoffs) * xscale;
|
|
curcomp->m_bounds.x1 = (curcomp->m_bounds.x1 - xoffs) * xscale;
|
|
curcomp->m_bounds.y0 = (curcomp->m_bounds.y0 - yoffs) * yscale;
|
|
curcomp->m_bounds.y1 = (curcomp->m_bounds.y1 - yoffs) * yscale;
|
|
}
|
|
}
|
|
|
|
// allocate an array of element textures for the states
|
|
m_elemtex.resize(m_maxstate + 1);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~layout_element - destructor
|
|
//-------------------------------------------------
|
|
|
|
layout_element::~layout_element()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// state_texture - return a pointer to a
|
|
// render_texture for the given state, allocating
|
|
// one if needed
|
|
//-------------------------------------------------
|
|
|
|
render_texture *layout_element::state_texture(int state)
|
|
{
|
|
assert(state <= m_maxstate);
|
|
if (m_elemtex[state].m_texture == NULL)
|
|
{
|
|
m_elemtex[state].m_element = this;
|
|
m_elemtex[state].m_state = state;
|
|
m_elemtex[state].m_texture = machine().render().texture_alloc(element_scale, &m_elemtex[state]);
|
|
}
|
|
return m_elemtex[state].m_texture;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// element_scale - scale an element by rendering
|
|
// all the components at the appropriate
|
|
// resolution
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::element_scale(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param)
|
|
{
|
|
texture *elemtex = (texture *)param;
|
|
|
|
// iterate over components that are part of the current state
|
|
for (component *curcomp = elemtex->m_element->m_complist.first(); curcomp != NULL; curcomp = curcomp->next())
|
|
if (curcomp->m_state == -1 || curcomp->m_state == elemtex->m_state)
|
|
{
|
|
// get the local scaled bounds
|
|
rectangle bounds;
|
|
bounds.min_x = render_round_nearest(curcomp->bounds().x0 * dest.width());
|
|
bounds.min_y = render_round_nearest(curcomp->bounds().y0 * dest.height());
|
|
bounds.max_x = render_round_nearest(curcomp->bounds().x1 * dest.width());
|
|
bounds.max_y = render_round_nearest(curcomp->bounds().y1 * dest.height());
|
|
bounds &= dest.cliprect();
|
|
|
|
// based on the component type, add to the texture
|
|
curcomp->draw(elemtex->m_element->machine(), dest, bounds, elemtex->m_state);
|
|
}
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
// LAYOUT ELEMENT TEXTURE
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// texture - constructor
|
|
//-------------------------------------------------
|
|
|
|
layout_element::texture::texture()
|
|
: m_element(NULL),
|
|
m_texture(NULL),
|
|
m_state(0)
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~texture - destructor
|
|
//-------------------------------------------------
|
|
|
|
layout_element::texture::~texture()
|
|
{
|
|
if (m_element != NULL)
|
|
m_element->machine().render().texture_free(m_texture);
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// LAYOUT ELEMENT COMPONENT
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// component - constructor
|
|
//-------------------------------------------------
|
|
|
|
layout_element::component::component(running_machine &machine, xml_data_node &compnode, const char *dirname)
|
|
: m_next(NULL),
|
|
m_type(CTYPE_INVALID),
|
|
m_state(0)
|
|
{
|
|
for (int i=0;i<MAX_BITMAPS;i++)
|
|
m_hasalpha[i] = false;
|
|
|
|
// fetch common data
|
|
m_state = xml_get_attribute_int_with_subst(machine, compnode, "state", -1);
|
|
parse_bounds(machine, xml_get_sibling(compnode.child, "bounds"), m_bounds);
|
|
parse_color(machine, xml_get_sibling(compnode.child, "color"), m_color);
|
|
|
|
// image nodes
|
|
if (strcmp(compnode.name, "image") == 0)
|
|
{
|
|
m_type = CTYPE_IMAGE;
|
|
if (dirname != NULL)
|
|
m_dirname = dirname;
|
|
m_imagefile[0] = xml_get_attribute_string_with_subst(machine, compnode, "file", "");
|
|
m_alphafile[0] = xml_get_attribute_string_with_subst(machine, compnode, "alphafile", "");
|
|
m_file[0] = std::make_unique<emu_file>(machine.options().art_path(), OPEN_FLAG_READ);
|
|
}
|
|
|
|
// text nodes
|
|
else if (strcmp(compnode.name, "text") == 0)
|
|
{
|
|
m_type = CTYPE_TEXT;
|
|
m_string = xml_get_attribute_string_with_subst(machine, compnode, "string", "");
|
|
m_textalign = xml_get_attribute_int_with_subst(machine, compnode, "align", 0);
|
|
}
|
|
|
|
// dotmatrix nodes
|
|
else if (strcmp(compnode.name, "dotmatrix") == 0)
|
|
{
|
|
m_type = CTYPE_DOTMATRIX;
|
|
}
|
|
else if (strcmp(compnode.name, "dotmatrix5dot") == 0)
|
|
{
|
|
m_type = CTYPE_DOTMATRIX5DOT;
|
|
}
|
|
else if (strcmp(compnode.name, "dotmatrixdot") == 0)
|
|
{
|
|
m_type = CTYPE_DOTMATRIXDOT;
|
|
}
|
|
|
|
// simplecounter nodes
|
|
else if (strcmp(compnode.name, "simplecounter") == 0)
|
|
{
|
|
m_type = CTYPE_SIMPLECOUNTER;
|
|
m_digits = xml_get_attribute_int_with_subst(machine, compnode, "digits", 2);
|
|
m_textalign = xml_get_attribute_int_with_subst(machine, compnode, "align", 0);
|
|
}
|
|
|
|
// fruit machine reels
|
|
else if (strcmp(compnode.name, "reel") == 0)
|
|
{
|
|
m_type = CTYPE_REEL;
|
|
|
|
std::string symbollist = xml_get_attribute_string_with_subst(machine, compnode, "symbollist", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15");
|
|
|
|
// split out position names from string and figure out our number of symbols
|
|
int location;
|
|
m_numstops = 0;
|
|
location=symbollist.find(",");
|
|
while (location!=-1)
|
|
{
|
|
m_stopnames[m_numstops] = symbollist;
|
|
m_stopnames[m_numstops] = m_stopnames[m_numstops].substr(0, location);
|
|
symbollist = symbollist.substr(location+1, symbollist.length()-(location-1));
|
|
m_numstops++;
|
|
location=symbollist.find(",");
|
|
}
|
|
m_stopnames[m_numstops++] = symbollist;
|
|
|
|
// careful, dirname is NULL if we're coming from internal layout, and our string assignment doesn't like that
|
|
if (dirname != NULL)
|
|
m_dirname = dirname;
|
|
|
|
for (int i=0;i<m_numstops;i++)
|
|
{
|
|
location=m_stopnames[i].find(":");
|
|
if (location!=-1)
|
|
{
|
|
m_imagefile[i] = m_stopnames[i];
|
|
m_stopnames[i] = m_stopnames[i].substr(0, location);
|
|
m_imagefile[i] = m_imagefile[i].substr(location+1, m_imagefile[i].length()-(location-1));
|
|
|
|
//m_alphafile[i] =
|
|
m_file[i] = std::make_unique<emu_file>(machine.options().art_path(), OPEN_FLAG_READ);
|
|
}
|
|
else
|
|
{
|
|
//m_imagefile[i] = 0;
|
|
//m_alphafile[i] = 0;
|
|
m_file[i].reset();
|
|
}
|
|
}
|
|
|
|
m_stateoffset = xml_get_attribute_int_with_subst(machine, compnode, "stateoffset", 0);
|
|
m_numsymbolsvisible = xml_get_attribute_int_with_subst(machine, compnode, "numsymbolsvisible", 3);
|
|
m_reelreversed = xml_get_attribute_int_with_subst(machine, compnode, "reelreversed", 0);
|
|
m_beltreel = xml_get_attribute_int_with_subst(machine, compnode, "beltreel", 0);
|
|
|
|
}
|
|
|
|
// led7seg nodes
|
|
else if (strcmp(compnode.name, "led7seg") == 0)
|
|
m_type = CTYPE_LED7SEG;
|
|
|
|
// led8seg_gts1 nodes
|
|
else if (strcmp(compnode.name, "led8seg_gts1") == 0)
|
|
m_type = CTYPE_LED8SEG_GTS1;
|
|
|
|
// led14seg nodes
|
|
else if (strcmp(compnode.name, "led14seg") == 0)
|
|
m_type = CTYPE_LED14SEG;
|
|
|
|
// led14segsc nodes
|
|
else if (strcmp(compnode.name, "led14segsc") == 0)
|
|
m_type = CTYPE_LED14SEGSC;
|
|
|
|
// led16seg nodes
|
|
else if (strcmp(compnode.name, "led16seg") == 0)
|
|
m_type = CTYPE_LED16SEG;
|
|
|
|
// led16segsc nodes
|
|
else if (strcmp(compnode.name, "led16segsc") == 0)
|
|
m_type = CTYPE_LED16SEGSC;
|
|
|
|
// rect nodes
|
|
else if (strcmp(compnode.name, "rect") == 0)
|
|
m_type = CTYPE_RECT;
|
|
|
|
// disk nodes
|
|
else if (strcmp(compnode.name, "disk") == 0)
|
|
m_type = CTYPE_DISK;
|
|
|
|
// error otherwise
|
|
else
|
|
throw emu_fatalerror("Unknown element component: %s", compnode.name);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~component - destructor
|
|
//-------------------------------------------------
|
|
|
|
layout_element::component::~component()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw - draw a component
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state)
|
|
{
|
|
switch (m_type)
|
|
{
|
|
case CTYPE_IMAGE:
|
|
if (!m_bitmap[0].valid())
|
|
load_bitmap();
|
|
{
|
|
bitmap_argb32 destsub(dest, bounds);
|
|
render_resample_argb_bitmap_hq(destsub, m_bitmap[0], m_color);
|
|
}
|
|
break;
|
|
|
|
case CTYPE_RECT:
|
|
draw_rect(dest, bounds);
|
|
break;
|
|
|
|
case CTYPE_DISK:
|
|
draw_disk(dest, bounds);
|
|
break;
|
|
|
|
case CTYPE_TEXT:
|
|
draw_text(machine, dest, bounds);
|
|
break;
|
|
|
|
case CTYPE_LED7SEG:
|
|
draw_led7seg(dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_LED8SEG_GTS1:
|
|
draw_led8seg_gts1(dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_LED14SEG:
|
|
draw_led14seg(dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_LED16SEG:
|
|
draw_led16seg(dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_LED14SEGSC:
|
|
draw_led14segsc(dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_LED16SEGSC:
|
|
draw_led16segsc(dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_DOTMATRIX:
|
|
draw_dotmatrix(8, dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_DOTMATRIX5DOT:
|
|
draw_dotmatrix(5, dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_DOTMATRIXDOT:
|
|
draw_dotmatrix(1, dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_SIMPLECOUNTER:
|
|
draw_simplecounter(machine, dest, bounds, state);
|
|
break;
|
|
|
|
case CTYPE_REEL:
|
|
draw_reel(machine, dest, bounds, state);
|
|
break;
|
|
|
|
default:
|
|
throw emu_fatalerror("Unknown component type requested draw()");
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_rect - draw a rectangle in the specified
|
|
// color
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_rect(bitmap_argb32 &dest, const rectangle &bounds)
|
|
{
|
|
// compute premultiplied colors
|
|
UINT32 r = m_color.r * m_color.a * 255.0f;
|
|
UINT32 g = m_color.g * m_color.a * 255.0f;
|
|
UINT32 b = m_color.b * m_color.a * 255.0f;
|
|
UINT32 inva = (1.0f - m_color.a) * 255.0f;
|
|
|
|
// iterate over X and Y
|
|
for (UINT32 y = bounds.min_y; y <= bounds.max_y; y++)
|
|
{
|
|
for (UINT32 x = bounds.min_x; x <= bounds.max_x; x++)
|
|
{
|
|
UINT32 finalr = r;
|
|
UINT32 finalg = g;
|
|
UINT32 finalb = b;
|
|
|
|
// if we're translucent, add in the destination pixel contribution
|
|
if (inva > 0)
|
|
{
|
|
rgb_t dpix = dest.pix32(y, x);
|
|
finalr += (dpix.r() * inva) >> 8;
|
|
finalg += (dpix.g() * inva) >> 8;
|
|
finalb += (dpix.b() * inva) >> 8;
|
|
}
|
|
|
|
// store the target pixel, dividing the RGBA values by the overall scale factor
|
|
dest.pix32(y, x) = rgb_t(finalr, finalg, finalb);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_disk - draw an ellipse in the specified
|
|
// color
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_disk(bitmap_argb32 &dest, const rectangle &bounds)
|
|
{
|
|
// compute premultiplied colors
|
|
UINT32 r = m_color.r * m_color.a * 255.0f;
|
|
UINT32 g = m_color.g * m_color.a * 255.0f;
|
|
UINT32 b = m_color.b * m_color.a * 255.0f;
|
|
UINT32 inva = (1.0f - m_color.a) * 255.0f;
|
|
|
|
// find the center
|
|
float xcenter = float(bounds.xcenter());
|
|
float ycenter = float(bounds.ycenter());
|
|
float xradius = float(bounds.width()) * 0.5f;
|
|
float yradius = float(bounds.height()) * 0.5f;
|
|
float ooyradius2 = 1.0f / (yradius * yradius);
|
|
|
|
// iterate over y
|
|
for (UINT32 y = bounds.min_y; y <= bounds.max_y; y++)
|
|
{
|
|
float ycoord = ycenter - ((float)y + 0.5f);
|
|
float xval = xradius * sqrtf(1.0f - (ycoord * ycoord) * ooyradius2);
|
|
|
|
// compute left/right coordinates
|
|
INT32 left = (INT32)(xcenter - xval + 0.5f);
|
|
INT32 right = (INT32)(xcenter + xval + 0.5f);
|
|
|
|
// draw this scanline
|
|
for (UINT32 x = left; x < right; x++)
|
|
{
|
|
UINT32 finalr = r;
|
|
UINT32 finalg = g;
|
|
UINT32 finalb = b;
|
|
|
|
// if we're translucent, add in the destination pixel contribution
|
|
if (inva > 0)
|
|
{
|
|
rgb_t dpix = dest.pix32(y, x);
|
|
finalr += (dpix.r() * inva) >> 8;
|
|
finalg += (dpix.g() * inva) >> 8;
|
|
finalb += (dpix.b() * inva) >> 8;
|
|
}
|
|
|
|
// store the target pixel, dividing the RGBA values by the overall scale factor
|
|
dest.pix32(y, x) = rgb_t(finalr, finalg, finalb);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_text - draw text in the specified color
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_text(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds)
|
|
{
|
|
// compute premultiplied colors
|
|
UINT32 r = m_color.r * 255.0f;
|
|
UINT32 g = m_color.g * 255.0f;
|
|
UINT32 b = m_color.b * 255.0f;
|
|
UINT32 a = m_color.a * 255.0f;
|
|
|
|
// get the width of the string
|
|
render_font *font = machine.render().font_alloc("default");
|
|
float aspect = 1.0f;
|
|
INT32 width;
|
|
|
|
|
|
while (1)
|
|
{
|
|
width = font->string_width(bounds.height(), aspect, m_string.c_str());
|
|
if (width < bounds.width())
|
|
break;
|
|
aspect *= 0.9f;
|
|
}
|
|
|
|
|
|
// get alignment
|
|
INT32 curx;
|
|
switch (m_textalign)
|
|
{
|
|
// left
|
|
case 1:
|
|
curx = bounds.min_x;
|
|
break;
|
|
|
|
// right
|
|
case 2:
|
|
curx = bounds.max_x - width;
|
|
break;
|
|
|
|
// default to center
|
|
default:
|
|
curx = bounds.min_x + (bounds.width() - width) / 2;
|
|
break;
|
|
}
|
|
|
|
// allocate a temporary bitmap
|
|
bitmap_argb32 tempbitmap(dest.width(), dest.height());
|
|
|
|
// loop over characters
|
|
for (const char *s = m_string.c_str(); *s != 0; s++)
|
|
{
|
|
// get the font bitmap
|
|
rectangle chbounds;
|
|
font->get_scaled_bitmap_and_bounds(tempbitmap, bounds.height(), aspect, *s, chbounds);
|
|
|
|
// copy the data into the target
|
|
for (int y = 0; y < chbounds.height(); y++)
|
|
{
|
|
int effy = bounds.min_y + y;
|
|
if (effy >= bounds.min_y && effy <= bounds.max_y)
|
|
{
|
|
UINT32 *src = &tempbitmap.pix32(y);
|
|
UINT32 *d = &dest.pix32(effy);
|
|
for (int x = 0; x < chbounds.width(); x++)
|
|
{
|
|
int effx = curx + x + chbounds.min_x;
|
|
if (effx >= bounds.min_x && effx <= bounds.max_x)
|
|
{
|
|
UINT32 spix = rgb_t(src[x]).a();
|
|
if (spix != 0)
|
|
{
|
|
rgb_t dpix = d[effx];
|
|
UINT32 ta = (a * (spix + 1)) >> 8;
|
|
UINT32 tr = (r * ta + dpix.r() * (0x100 - ta)) >> 8;
|
|
UINT32 tg = (g * ta + dpix.g() * (0x100 - ta)) >> 8;
|
|
UINT32 tb = (b * ta + dpix.b() * (0x100 - ta)) >> 8;
|
|
d[effx] = rgb_t(tr, tg, tb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// advance in the X direction
|
|
curx += font->char_width(bounds.height(), aspect, *s);
|
|
}
|
|
|
|
// free the temporary bitmap and font
|
|
machine.render().font_free(font);
|
|
}
|
|
|
|
void layout_element::component::draw_simplecounter(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state)
|
|
{
|
|
char temp[256];
|
|
sprintf(temp, "%0*d", m_digits, state);
|
|
m_string = std::string(temp);
|
|
draw_text(machine, dest, bounds);
|
|
}
|
|
|
|
/* state is a normalized value between 0 and 65536 so that we don't need to worry about how many motor steps here or in the .lay, only the number of symbols */
|
|
void layout_element::component::draw_reel(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state)
|
|
{
|
|
if (m_beltreel)
|
|
{
|
|
draw_beltreel(machine,dest,bounds,state);
|
|
}
|
|
else
|
|
{
|
|
const int max_state_used = 0x10000;
|
|
|
|
// shift the reels a bit based on this param, allows fine tuning
|
|
int use_state = (state + m_stateoffset) % max_state_used;
|
|
|
|
// compute premultiplied colors
|
|
UINT32 r = m_color.r * 255.0f;
|
|
UINT32 g = m_color.g * 255.0f;
|
|
UINT32 b = m_color.b * 255.0f;
|
|
UINT32 a = m_color.a * 255.0f;
|
|
|
|
// get the width of the string
|
|
render_font *font = machine.render().font_alloc("default");
|
|
float aspect = 1.0f;
|
|
INT32 width;
|
|
|
|
|
|
int curry = 0;
|
|
int num_shown = m_numsymbolsvisible;
|
|
|
|
int ourheight = bounds.height();
|
|
|
|
for (int fruit = 0;fruit<m_numstops;fruit++)
|
|
{
|
|
int basey;
|
|
|
|
if (m_reelreversed==1)
|
|
{
|
|
basey = bounds.min_y + ((use_state)*(ourheight/num_shown)/(max_state_used/m_numstops)) + curry;
|
|
}
|
|
else
|
|
{
|
|
basey = bounds.min_y - ((use_state)*(ourheight/num_shown)/(max_state_used/m_numstops)) + curry;
|
|
}
|
|
|
|
// wrap around...
|
|
if (basey < bounds.min_y)
|
|
basey += ((max_state_used)*(ourheight/num_shown)/(max_state_used/m_numstops));
|
|
if (basey > bounds.max_y)
|
|
basey -= ((max_state_used)*(ourheight/num_shown)/(max_state_used/m_numstops));
|
|
|
|
int endpos = basey+ourheight/num_shown;
|
|
|
|
// only render the symbol / text if it's atually in view because the code is SLOW
|
|
if ((endpos >= bounds.min_y) && (basey <= bounds.max_y))
|
|
{
|
|
while (1)
|
|
{
|
|
width = font->string_width(ourheight / num_shown, aspect, m_stopnames[fruit].c_str());
|
|
if (width < bounds.width())
|
|
break;
|
|
aspect *= 0.9f;
|
|
}
|
|
|
|
INT32 curx;
|
|
curx = bounds.min_x + (bounds.width() - width) / 2;
|
|
|
|
if (m_file[fruit])
|
|
if (!m_bitmap[fruit].valid())
|
|
load_reel_bitmap(fruit);
|
|
|
|
if (m_file[fruit]) // render gfx
|
|
{
|
|
bitmap_argb32 tempbitmap2(dest.width(), ourheight/num_shown);
|
|
|
|
if (m_bitmap[fruit].valid())
|
|
{
|
|
render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], m_color);
|
|
|
|
for (int y = 0; y < ourheight/num_shown; y++)
|
|
{
|
|
int effy = basey + y;
|
|
|
|
if (effy >= bounds.min_y && effy <= bounds.max_y)
|
|
{
|
|
UINT32 *src = &tempbitmap2.pix32(y);
|
|
UINT32 *d = &dest.pix32(effy);
|
|
for (int x = 0; x < dest.width(); x++)
|
|
{
|
|
int effx = x;
|
|
if (effx >= bounds.min_x && effx <= bounds.max_x)
|
|
{
|
|
UINT32 spix = rgb_t(src[x]).a();
|
|
if (spix != 0)
|
|
{
|
|
d[effx] = src[x];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
else // render text (fallback)
|
|
{
|
|
// allocate a temporary bitmap
|
|
bitmap_argb32 tempbitmap(dest.width(), dest.height());
|
|
|
|
// loop over characters
|
|
for (const char *s = m_stopnames[fruit].c_str(); *s != 0; s++)
|
|
{
|
|
// get the font bitmap
|
|
rectangle chbounds;
|
|
font->get_scaled_bitmap_and_bounds(tempbitmap, ourheight/num_shown, aspect, *s, chbounds);
|
|
|
|
// copy the data into the target
|
|
for (int y = 0; y < chbounds.height(); y++)
|
|
{
|
|
int effy = basey + y;
|
|
|
|
if (effy >= bounds.min_y && effy <= bounds.max_y)
|
|
{
|
|
UINT32 *src = &tempbitmap.pix32(y);
|
|
UINT32 *d = &dest.pix32(effy);
|
|
for (int x = 0; x < chbounds.width(); x++)
|
|
{
|
|
int effx = curx + x + chbounds.min_x;
|
|
if (effx >= bounds.min_x && effx <= bounds.max_x)
|
|
{
|
|
UINT32 spix = rgb_t(src[x]).a();
|
|
if (spix != 0)
|
|
{
|
|
rgb_t dpix = d[effx];
|
|
UINT32 ta = (a * (spix + 1)) >> 8;
|
|
UINT32 tr = (r * ta + dpix.r() * (0x100 - ta)) >> 8;
|
|
UINT32 tg = (g * ta + dpix.g() * (0x100 - ta)) >> 8;
|
|
UINT32 tb = (b * ta + dpix.b() * (0x100 - ta)) >> 8;
|
|
d[effx] = rgb_t(tr, tg, tb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// advance in the X direction
|
|
curx += font->char_width(ourheight/num_shown, aspect, *s);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
curry += ourheight/num_shown;
|
|
}
|
|
// free the temporary bitmap and font
|
|
machine.render().font_free(font);
|
|
}
|
|
}
|
|
|
|
|
|
void layout_element::component::draw_beltreel(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state)
|
|
{
|
|
const int max_state_used = 0x10000;
|
|
|
|
// shift the reels a bit based on this param, allows fine tuning
|
|
int use_state = (state + m_stateoffset) % max_state_used;
|
|
|
|
// compute premultiplied colors
|
|
UINT32 r = m_color.r * 255.0f;
|
|
UINT32 g = m_color.g * 255.0f;
|
|
UINT32 b = m_color.b * 255.0f;
|
|
UINT32 a = m_color.a * 255.0f;
|
|
|
|
// get the width of the string
|
|
render_font *font = machine.render().font_alloc("default");
|
|
float aspect = 1.0f;
|
|
INT32 width;
|
|
int currx = 0;
|
|
int num_shown = m_numsymbolsvisible;
|
|
|
|
int ourwidth = bounds.width();
|
|
|
|
for (int fruit = 0;fruit<m_numstops;fruit++)
|
|
{
|
|
int basex;
|
|
if (m_reelreversed==1)
|
|
{
|
|
basex = bounds.min_x + ((use_state)*(ourwidth/num_shown)/(max_state_used/m_numstops)) + currx;
|
|
}
|
|
else
|
|
{
|
|
basex = bounds.min_x - ((use_state)*(ourwidth/num_shown)/(max_state_used/m_numstops)) + currx;
|
|
}
|
|
|
|
// wrap around...
|
|
if (basex < bounds.min_x)
|
|
basex += ((max_state_used)*(ourwidth/num_shown)/(max_state_used/m_numstops));
|
|
if (basex > bounds.max_x)
|
|
basex -= ((max_state_used)*(ourwidth/num_shown)/(max_state_used/m_numstops));
|
|
|
|
int endpos = basex+(ourwidth/num_shown);
|
|
|
|
// only render the symbol / text if it's atually in view because the code is SLOW
|
|
if ((endpos >= bounds.min_x) && (basex <= bounds.max_x))
|
|
{
|
|
while (1)
|
|
{
|
|
width = font->string_width(dest.height(), aspect, m_stopnames[fruit].c_str());
|
|
if (width < bounds.width())
|
|
break;
|
|
aspect *= 0.9f;
|
|
}
|
|
|
|
INT32 curx;
|
|
curx = bounds.min_x;
|
|
|
|
if (m_file[fruit])
|
|
if (!m_bitmap[fruit].valid())
|
|
load_reel_bitmap(fruit);
|
|
|
|
if (m_file[fruit]) // render gfx
|
|
{
|
|
bitmap_argb32 tempbitmap2(ourwidth/num_shown, dest.height());
|
|
|
|
if (m_bitmap[fruit].valid())
|
|
{
|
|
render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], m_color);
|
|
|
|
for (int y = 0; y < dest.height(); y++)
|
|
{
|
|
int effy = y;
|
|
|
|
if (effy >= bounds.min_y && effy <= bounds.max_y)
|
|
{
|
|
UINT32 *src = &tempbitmap2.pix32(y);
|
|
UINT32 *d = &dest.pix32(effy);
|
|
for (int x = 0; x < ourwidth/num_shown; x++)
|
|
{
|
|
int effx = basex + x;
|
|
if (effx >= bounds.min_x && effx <= bounds.max_x)
|
|
{
|
|
UINT32 spix = rgb_t(src[x]).a();
|
|
if (spix != 0)
|
|
{
|
|
d[effx] = src[x];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
else // render text (fallback)
|
|
{
|
|
// allocate a temporary bitmap
|
|
bitmap_argb32 tempbitmap(dest.width(), dest.height());
|
|
|
|
// loop over characters
|
|
for (const char *s = m_stopnames[fruit].c_str(); *s != 0; s++)
|
|
{
|
|
// get the font bitmap
|
|
rectangle chbounds;
|
|
font->get_scaled_bitmap_and_bounds(tempbitmap, dest.height(), aspect, *s, chbounds);
|
|
|
|
// copy the data into the target
|
|
for (int y = 0; y < chbounds.height(); y++)
|
|
{
|
|
int effy = y;
|
|
|
|
if (effy >= bounds.min_y && effy <= bounds.max_y)
|
|
{
|
|
UINT32 *src = &tempbitmap.pix32(y);
|
|
UINT32 *d = &dest.pix32(effy);
|
|
for (int x = 0; x < chbounds.width(); x++)
|
|
{
|
|
int effx = basex + curx + x;
|
|
if (effx >= bounds.min_x && effx <= bounds.max_x)
|
|
{
|
|
UINT32 spix = rgb_t(src[x]).a();
|
|
if (spix != 0)
|
|
{
|
|
rgb_t dpix = d[effx];
|
|
UINT32 ta = (a * (spix + 1)) >> 8;
|
|
UINT32 tr = (r * ta + dpix.r() * (0x100 - ta)) >> 8;
|
|
UINT32 tg = (g * ta + dpix.g() * (0x100 - ta)) >> 8;
|
|
UINT32 tb = (b * ta + dpix.b() * (0x100 - ta)) >> 8;
|
|
d[effx] = rgb_t(tr, tg, tb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// advance in the X direction
|
|
curx += font->char_width(dest.height(), aspect, *s);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
currx += ourwidth/num_shown;
|
|
}
|
|
|
|
// free the temporary bitmap and font
|
|
machine.render().font_free(font);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// load_bitmap - load a PNG file with artwork for
|
|
// a component
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::load_bitmap()
|
|
{
|
|
// load the basic bitmap
|
|
assert(m_file[0] != NULL);
|
|
m_hasalpha[0] = render_load_png(m_bitmap[0], *m_file[0], m_dirname.c_str(), m_imagefile[0].c_str());
|
|
|
|
// load the alpha bitmap if specified
|
|
if (m_bitmap[0].valid() && !m_alphafile[0].empty())
|
|
render_load_png(m_bitmap[0], *m_file[0], m_dirname.c_str(), m_alphafile[0].c_str(), true);
|
|
|
|
// if we can't load the bitmap, allocate a dummy one and report an error
|
|
if (!m_bitmap[0].valid())
|
|
{
|
|
// draw some stripes in the bitmap
|
|
m_bitmap[0].allocate(100, 100);
|
|
m_bitmap[0].fill(0);
|
|
for (int step = 0; step < 100; step += 25)
|
|
for (int line = 0; line < 100; line++)
|
|
m_bitmap[0].pix32((step + line) % 100, line % 100) = rgb_t(0xff,0xff,0xff,0xff);
|
|
|
|
// log an error
|
|
if (m_alphafile[0].empty())
|
|
osd_printf_warning("Unable to load component bitmap '%s'\n", m_imagefile[0].c_str());
|
|
else
|
|
osd_printf_warning("Unable to load component bitmap '%s'/'%s'\n", m_imagefile[0].c_str(), m_alphafile[0].c_str());
|
|
}
|
|
}
|
|
|
|
|
|
void layout_element::component::load_reel_bitmap(int number)
|
|
{
|
|
// load the basic bitmap
|
|
assert(m_file != NULL);
|
|
/*m_hasalpha[number] = */ render_load_png(m_bitmap[number], *m_file[number], m_dirname.c_str(), m_imagefile[number].c_str());
|
|
|
|
// load the alpha bitmap if specified
|
|
//if (m_bitmap[number].valid() && m_alphafile[number])
|
|
// render_load_png(m_bitmap[number], *m_file[number], m_dirname, m_alphafile[number], true);
|
|
|
|
// if we can't load the bitmap just use text rendering
|
|
if (!m_bitmap[number].valid())
|
|
{
|
|
// fallback to text rendering
|
|
m_file[number].reset();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_led7seg - draw a 7-segment LCD
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_led7seg(bitmap_argb32 &dest, const rectangle &bounds, int pattern)
|
|
{
|
|
const rgb_t onpen = rgb_t(0xff,0xff,0xff,0xff);
|
|
const rgb_t offpen = rgb_t(0xff,0x20,0x20,0x20);
|
|
|
|
// sizes for computation
|
|
int bmwidth = 250;
|
|
int bmheight = 400;
|
|
int segwidth = 40;
|
|
int skewwidth = 40;
|
|
|
|
// allocate a temporary bitmap for drawing
|
|
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight);
|
|
tempbitmap.fill(rgb_t(0xff,0x00,0x00,0x00));
|
|
|
|
// top bar
|
|
draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2, segwidth, (pattern & (1 << 0)) ? onpen : offpen);
|
|
|
|
// top-right bar
|
|
draw_segment_vertical(tempbitmap, 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, segwidth, (pattern & (1 << 1)) ? onpen : offpen);
|
|
|
|
// bottom-right bar
|
|
draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, segwidth, (pattern & (1 << 2)) ? onpen : offpen);
|
|
|
|
// bottom bar
|
|
draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2, segwidth, (pattern & (1 << 3)) ? onpen : offpen);
|
|
|
|
// bottom-left bar
|
|
draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, segwidth, (pattern & (1 << 4)) ? onpen : offpen);
|
|
|
|
// top-left bar
|
|
draw_segment_vertical(tempbitmap, 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, segwidth, (pattern & (1 << 5)) ? onpen : offpen);
|
|
|
|
// middle bar
|
|
draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight/2, segwidth, (pattern & (1 << 6)) ? onpen : offpen);
|
|
|
|
// apply skew
|
|
apply_skew(tempbitmap, 40);
|
|
|
|
// decimal point
|
|
draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, (pattern & (1 << 7)) ? onpen : offpen);
|
|
|
|
// resample to the target size
|
|
render_resample_argb_bitmap_hq(dest, tempbitmap, m_color);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------
|
|
// draw_led8seg_gts1 - draw a 8-segment fluorescent (Gottlieb System 1)
|
|
//-----------------------------------------------------------------
|
|
|
|
void layout_element::component::draw_led8seg_gts1(bitmap_argb32 &dest, const rectangle &bounds, int pattern)
|
|
{
|
|
const rgb_t onpen = rgb_t(0xff,0xff,0xff,0xff);
|
|
const rgb_t offpen = rgb_t(0xff,0x20,0x20,0x20);
|
|
const rgb_t backpen = rgb_t(0xff,0x00,0x00,0x00);
|
|
|
|
// sizes for computation
|
|
int bmwidth = 250;
|
|
int bmheight = 400;
|
|
int segwidth = 40;
|
|
int skewwidth = 40;
|
|
|
|
// allocate a temporary bitmap for drawing
|
|
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight);
|
|
tempbitmap.fill(backpen);
|
|
|
|
// top bar
|
|
draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2, segwidth, (pattern & (1 << 0)) ? onpen : offpen);
|
|
|
|
// top-right bar
|
|
draw_segment_vertical(tempbitmap, 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, segwidth, (pattern & (1 << 1)) ? onpen : offpen);
|
|
|
|
// bottom-right bar
|
|
draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, segwidth, (pattern & (1 << 2)) ? onpen : offpen);
|
|
|
|
// bottom bar
|
|
draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2, segwidth, (pattern & (1 << 3)) ? onpen : offpen);
|
|
|
|
// bottom-left bar
|
|
draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, segwidth, (pattern & (1 << 4)) ? onpen : offpen);
|
|
|
|
// top-left bar
|
|
draw_segment_vertical(tempbitmap, 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, segwidth, (pattern & (1 << 5)) ? onpen : offpen);
|
|
|
|
// horizontal bars
|
|
draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, 2*bmwidth/3 - 2*segwidth/3, bmheight/2, segwidth, (pattern & (1 << 6)) ? onpen : offpen);
|
|
draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3 + bmwidth/2, bmwidth - 2*segwidth/3, bmheight/2, segwidth, (pattern & (1 << 6)) ? onpen : offpen);
|
|
|
|
// vertical bars
|
|
draw_segment_vertical(tempbitmap, 0 + segwidth/3 - 8, bmheight/2 - segwidth/3 + 2, 2*bmwidth/3 - segwidth/2 - 4, segwidth + 8, backpen);
|
|
draw_segment_vertical(tempbitmap, 0 + segwidth/3, bmheight/2 - segwidth/3, 2*bmwidth/3 - segwidth/2 - 4, segwidth, (pattern & (1 << 7)) ? onpen : offpen);
|
|
|
|
draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3 - 2, bmheight - segwidth/3 + 8, 2*bmwidth/3 - segwidth/2 - 4, segwidth + 8, backpen);
|
|
draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - segwidth/3, 2*bmwidth/3 - segwidth/2 - 4, segwidth, (pattern & (1 << 7)) ? onpen : offpen);
|
|
|
|
// apply skew
|
|
apply_skew(tempbitmap, 40);
|
|
|
|
// resample to the target size
|
|
render_resample_argb_bitmap_hq(dest, tempbitmap, m_color);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_led14seg - draw a 14-segment LCD
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_led14seg(bitmap_argb32 &dest, const rectangle &bounds, int pattern)
|
|
{
|
|
const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
const rgb_t offpen = rgb_t(0xff, 0x20, 0x20, 0x20);
|
|
|
|
// sizes for computation
|
|
int bmwidth = 250;
|
|
int bmheight = 400;
|
|
int segwidth = 40;
|
|
int skewwidth = 40;
|
|
|
|
// allocate a temporary bitmap for drawing
|
|
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight);
|
|
tempbitmap.fill(rgb_t(0xff, 0x00, 0x00, 0x00));
|
|
|
|
// top bar
|
|
draw_segment_horizontal(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 0)) ? onpen : offpen);
|
|
|
|
// right-top bar
|
|
draw_segment_vertical(tempbitmap,
|
|
0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
|
|
segwidth, (pattern & (1 << 1)) ? onpen : offpen);
|
|
|
|
// right-bottom bar
|
|
draw_segment_vertical(tempbitmap,
|
|
bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
|
|
segwidth, (pattern & (1 << 2)) ? onpen : offpen);
|
|
|
|
// bottom bar
|
|
draw_segment_horizontal(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
|
|
segwidth, (pattern & (1 << 3)) ? onpen : offpen);
|
|
|
|
// left-bottom bar
|
|
draw_segment_vertical(tempbitmap,
|
|
bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 4)) ? onpen : offpen);
|
|
|
|
// left-top bar
|
|
draw_segment_vertical(tempbitmap,
|
|
0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 5)) ? onpen : offpen);
|
|
|
|
// horizontal-middle-left bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
|
|
segwidth, LINE_CAP_START, (pattern & (1 << 6)) ? onpen : offpen);
|
|
|
|
// horizontal-middle-right bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
|
|
segwidth, LINE_CAP_END, (pattern & (1 << 7)) ? onpen : offpen);
|
|
|
|
// vertical-middle-top bar
|
|
draw_segment_vertical_caps(tempbitmap,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
|
|
segwidth, LINE_CAP_NONE, (pattern & (1 << 8)) ? onpen : offpen);
|
|
|
|
// vertical-middle-bottom bar
|
|
draw_segment_vertical_caps(tempbitmap,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
|
|
segwidth, LINE_CAP_NONE, (pattern & (1 << 9)) ? onpen : offpen);
|
|
|
|
// diagonal-left-bottom bar
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
|
|
segwidth, (pattern & (1 << 10)) ? onpen : offpen);
|
|
|
|
// diagonal-left-top bar
|
|
draw_segment_diagonal_2(tempbitmap,
|
|
0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
|
|
segwidth, (pattern & (1 << 11)) ? onpen : offpen);
|
|
|
|
// diagonal-right-top bar
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
|
|
segwidth, (pattern & (1 << 12)) ? onpen : offpen);
|
|
|
|
// diagonal-right-bottom bar
|
|
draw_segment_diagonal_2(tempbitmap,
|
|
bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
|
|
segwidth, (pattern & (1 << 13)) ? onpen : offpen);
|
|
|
|
// apply skew
|
|
apply_skew(tempbitmap, 40);
|
|
|
|
// resample to the target size
|
|
render_resample_argb_bitmap_hq(dest, tempbitmap, m_color);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_led14segsc - draw a 14-segment LCD with
|
|
// semicolon (2 extra segments)
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_led14segsc(bitmap_argb32 &dest, const rectangle &bounds, int pattern)
|
|
{
|
|
const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
const rgb_t offpen = rgb_t(0xff, 0x20, 0x20, 0x20);
|
|
|
|
// sizes for computation
|
|
int bmwidth = 250;
|
|
int bmheight = 400;
|
|
int segwidth = 40;
|
|
int skewwidth = 40;
|
|
|
|
// allocate a temporary bitmap for drawing, adding some extra space for the tail
|
|
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight + segwidth);
|
|
tempbitmap.fill(rgb_t(0xff, 0x00, 0x00, 0x00));
|
|
|
|
// top bar
|
|
draw_segment_horizontal(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 0)) ? onpen : offpen);
|
|
|
|
// right-top bar
|
|
draw_segment_vertical(tempbitmap,
|
|
0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
|
|
segwidth, (pattern & (1 << 1)) ? onpen : offpen);
|
|
|
|
// right-bottom bar
|
|
draw_segment_vertical(tempbitmap,
|
|
bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
|
|
segwidth, (pattern & (1 << 2)) ? onpen : offpen);
|
|
|
|
// bottom bar
|
|
draw_segment_horizontal(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
|
|
segwidth, (pattern & (1 << 3)) ? onpen : offpen);
|
|
|
|
// left-bottom bar
|
|
draw_segment_vertical(tempbitmap,
|
|
bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 4)) ? onpen : offpen);
|
|
|
|
// left-top bar
|
|
draw_segment_vertical(tempbitmap,
|
|
0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 5)) ? onpen : offpen);
|
|
|
|
// horizontal-middle-left bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
|
|
segwidth, LINE_CAP_START, (pattern & (1 << 6)) ? onpen : offpen);
|
|
|
|
// horizontal-middle-right bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
|
|
segwidth, LINE_CAP_END, (pattern & (1 << 7)) ? onpen : offpen);
|
|
|
|
// vertical-middle-top bar
|
|
draw_segment_vertical_caps(tempbitmap,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
|
|
segwidth, LINE_CAP_NONE, (pattern & (1 << 8)) ? onpen : offpen);
|
|
|
|
// vertical-middle-bottom bar
|
|
draw_segment_vertical_caps(tempbitmap,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
|
|
segwidth, LINE_CAP_NONE, (pattern & (1 << 9)) ? onpen : offpen);
|
|
|
|
// diagonal-left-bottom bar
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
|
|
segwidth, (pattern & (1 << 10)) ? onpen : offpen);
|
|
|
|
// diagonal-left-top bar
|
|
draw_segment_diagonal_2(tempbitmap,
|
|
0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
|
|
segwidth, (pattern & (1 << 11)) ? onpen : offpen);
|
|
|
|
// diagonal-right-top bar
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
|
|
segwidth, (pattern & (1 << 12)) ? onpen : offpen);
|
|
|
|
// diagonal-right-bottom bar
|
|
draw_segment_diagonal_2(tempbitmap,
|
|
bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
|
|
segwidth, (pattern & (1 << 13)) ? onpen : offpen);
|
|
|
|
// apply skew
|
|
apply_skew(tempbitmap, 40);
|
|
|
|
// comma tail
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
bmwidth - (segwidth/2), bmwidth + segwidth,
|
|
bmheight - (segwidth), bmheight + segwidth*1.5,
|
|
segwidth/2, (pattern & (1 << 15)) ? onpen : offpen);
|
|
|
|
// decimal point
|
|
draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, (pattern & (1 << 14)) ? onpen : offpen);
|
|
|
|
// resample to the target size
|
|
render_resample_argb_bitmap_hq(dest, tempbitmap, m_color);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_led16seg - draw a 16-segment LCD
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_led16seg(bitmap_argb32 &dest, const rectangle &bounds, int pattern)
|
|
{
|
|
const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
const rgb_t offpen = rgb_t(0xff, 0x20, 0x20, 0x20);
|
|
|
|
// sizes for computation
|
|
int bmwidth = 250;
|
|
int bmheight = 400;
|
|
int segwidth = 40;
|
|
int skewwidth = 40;
|
|
|
|
// allocate a temporary bitmap for drawing
|
|
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight);
|
|
tempbitmap.fill(rgb_t(0xff, 0x00, 0x00, 0x00));
|
|
|
|
// top-left bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth/2 - segwidth/10, 0 + segwidth/2,
|
|
segwidth, LINE_CAP_START, (pattern & (1 << 0)) ? onpen : offpen);
|
|
|
|
// top-right bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, 0 + segwidth/2,
|
|
segwidth, LINE_CAP_END, (pattern & (1 << 1)) ? onpen : offpen);
|
|
|
|
// right-top bar
|
|
draw_segment_vertical(tempbitmap,
|
|
0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
|
|
segwidth, (pattern & (1 << 2)) ? onpen : offpen);
|
|
|
|
// right-bottom bar
|
|
draw_segment_vertical(tempbitmap,
|
|
bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
|
|
segwidth, (pattern & (1 << 3)) ? onpen : offpen);
|
|
|
|
// bottom-right bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
|
|
segwidth, LINE_CAP_END, (pattern & (1 << 4)) ? onpen : offpen);
|
|
|
|
// bottom-left bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight - segwidth/2,
|
|
segwidth, LINE_CAP_START, (pattern & (1 << 5)) ? onpen : offpen);
|
|
|
|
// left-bottom bar
|
|
draw_segment_vertical(tempbitmap,
|
|
bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 6)) ? onpen : offpen);
|
|
|
|
// left-top bar
|
|
draw_segment_vertical(tempbitmap,
|
|
0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 7)) ? onpen : offpen);
|
|
|
|
// horizontal-middle-left bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
|
|
segwidth, LINE_CAP_START, (pattern & (1 << 8)) ? onpen : offpen);
|
|
|
|
// horizontal-middle-right bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
|
|
segwidth, LINE_CAP_END, (pattern & (1 << 9)) ? onpen : offpen);
|
|
|
|
// vertical-middle-top bar
|
|
draw_segment_vertical_caps(tempbitmap,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
|
|
segwidth, LINE_CAP_NONE, (pattern & (1 << 10)) ? onpen : offpen);
|
|
|
|
// vertical-middle-bottom bar
|
|
draw_segment_vertical_caps(tempbitmap,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
|
|
segwidth, LINE_CAP_NONE, (pattern & (1 << 11)) ? onpen : offpen);
|
|
|
|
// diagonal-left-bottom bar
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
|
|
segwidth, (pattern & (1 << 12)) ? onpen : offpen);
|
|
|
|
// diagonal-left-top bar
|
|
draw_segment_diagonal_2(tempbitmap,
|
|
0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
|
|
segwidth, (pattern & (1 << 13)) ? onpen : offpen);
|
|
|
|
// diagonal-right-top bar
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
|
|
segwidth, (pattern & (1 << 14)) ? onpen : offpen);
|
|
|
|
// diagonal-right-bottom bar
|
|
draw_segment_diagonal_2(tempbitmap,
|
|
bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
|
|
segwidth, (pattern & (1 << 15)) ? onpen : offpen);
|
|
|
|
// apply skew
|
|
apply_skew(tempbitmap, 40);
|
|
|
|
// resample to the target size
|
|
render_resample_argb_bitmap_hq(dest, tempbitmap, m_color);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_led16segsc - draw a 16-segment LCD with
|
|
// semicolon (2 extra segments)
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_led16segsc(bitmap_argb32 &dest, const rectangle &bounds, int pattern)
|
|
{
|
|
const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
const rgb_t offpen = rgb_t(0xff, 0x20, 0x20, 0x20);
|
|
|
|
// sizes for computation
|
|
int bmwidth = 250;
|
|
int bmheight = 400;
|
|
int segwidth = 40;
|
|
int skewwidth = 40;
|
|
|
|
// allocate a temporary bitmap for drawing
|
|
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight + segwidth);
|
|
tempbitmap.fill(rgb_t(0xff, 0x00, 0x00, 0x00));
|
|
|
|
// top-left bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth/2 - segwidth/10, 0 + segwidth/2,
|
|
segwidth, LINE_CAP_START, (pattern & (1 << 0)) ? onpen : offpen);
|
|
|
|
// top-right bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, 0 + segwidth/2,
|
|
segwidth, LINE_CAP_END, (pattern & (1 << 1)) ? onpen : offpen);
|
|
|
|
// right-top bar
|
|
draw_segment_vertical(tempbitmap,
|
|
0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
|
|
segwidth, (pattern & (1 << 2)) ? onpen : offpen);
|
|
|
|
// right-bottom bar
|
|
draw_segment_vertical(tempbitmap,
|
|
bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
|
|
segwidth, (pattern & (1 << 3)) ? onpen : offpen);
|
|
|
|
// bottom-right bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
|
|
segwidth, LINE_CAP_END, (pattern & (1 << 4)) ? onpen : offpen);
|
|
|
|
// bottom-left bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight - segwidth/2,
|
|
segwidth, LINE_CAP_START, (pattern & (1 << 5)) ? onpen : offpen);
|
|
|
|
// left-bottom bar
|
|
draw_segment_vertical(tempbitmap,
|
|
bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 6)) ? onpen : offpen);
|
|
|
|
// left-top bar
|
|
draw_segment_vertical(tempbitmap,
|
|
0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
|
|
segwidth, (pattern & (1 << 7)) ? onpen : offpen);
|
|
|
|
// horizontal-middle-left bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
|
|
segwidth, LINE_CAP_START, (pattern & (1 << 8)) ? onpen : offpen);
|
|
|
|
// horizontal-middle-right bar
|
|
draw_segment_horizontal_caps(tempbitmap,
|
|
0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
|
|
segwidth, LINE_CAP_END, (pattern & (1 << 9)) ? onpen : offpen);
|
|
|
|
// vertical-middle-top bar
|
|
draw_segment_vertical_caps(tempbitmap,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
|
|
segwidth, LINE_CAP_NONE, (pattern & (1 << 10)) ? onpen : offpen);
|
|
|
|
// vertical-middle-bottom bar
|
|
draw_segment_vertical_caps(tempbitmap,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
|
|
segwidth, LINE_CAP_NONE, (pattern & (1 << 11)) ? onpen : offpen);
|
|
|
|
// diagonal-left-bottom bar
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
|
|
segwidth, (pattern & (1 << 12)) ? onpen : offpen);
|
|
|
|
// diagonal-left-top bar
|
|
draw_segment_diagonal_2(tempbitmap,
|
|
0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
|
|
segwidth, (pattern & (1 << 13)) ? onpen : offpen);
|
|
|
|
// diagonal-right-top bar
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
|
|
0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
|
|
segwidth, (pattern & (1 << 14)) ? onpen : offpen);
|
|
|
|
// diagonal-right-bottom bar
|
|
draw_segment_diagonal_2(tempbitmap,
|
|
bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
|
|
bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
|
|
segwidth, (pattern & (1 << 15)) ? onpen : offpen);
|
|
|
|
// comma tail
|
|
draw_segment_diagonal_1(tempbitmap,
|
|
bmwidth - (segwidth/2), bmwidth + segwidth,
|
|
bmheight - (segwidth), bmheight + segwidth*1.5,
|
|
segwidth/2, (pattern & (1 << 17)) ? onpen : offpen);
|
|
|
|
// decimal point (draw last for priority)
|
|
draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, (pattern & (1 << 16)) ? onpen : offpen);
|
|
|
|
// apply skew
|
|
apply_skew(tempbitmap, 40);
|
|
|
|
// resample to the target size
|
|
render_resample_argb_bitmap_hq(dest, tempbitmap, m_color);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_dotmatrix - draw a row of dots for a
|
|
// dotmatrix
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_dotmatrix(int dots, bitmap_argb32 &dest, const rectangle &bounds, int pattern)
|
|
{
|
|
const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
const rgb_t offpen = rgb_t(0xff, 0x20, 0x20, 0x20);
|
|
|
|
// sizes for computation
|
|
int bmheight = 300;
|
|
int dotwidth = 250;
|
|
|
|
// allocate a temporary bitmap for drawing
|
|
bitmap_argb32 tempbitmap(dotwidth*dots, bmheight);
|
|
tempbitmap.fill(rgb_t(0xff, 0x00, 0x00, 0x00));
|
|
|
|
for (int i = 0; i < dots; i++)
|
|
draw_segment_decimal(tempbitmap, ((dotwidth/2 )+ (i * dotwidth)), bmheight/2, dotwidth, (pattern & (1 << i))?onpen:offpen);
|
|
|
|
// resample to the target size
|
|
render_resample_argb_bitmap_hq(dest, tempbitmap, m_color);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_segment_horizontal_caps - draw a
|
|
// horizontal LED segment with definable end
|
|
// and start points
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_segment_horizontal_caps(bitmap_argb32 &dest, int minx, int maxx, int midy, int width, int caps, rgb_t color)
|
|
{
|
|
// loop over the width of the segment
|
|
for (int y = 0; y < width / 2; y++)
|
|
{
|
|
UINT32 *d0 = &dest.pix32(midy - y);
|
|
UINT32 *d1 = &dest.pix32(midy + y);
|
|
int ty = (y < width / 8) ? width / 8 : y;
|
|
|
|
// loop over the length of the segment
|
|
for (int x = minx + ((caps & LINE_CAP_START) ? ty : 0); x < maxx - ((caps & LINE_CAP_END) ? ty : 0); x++)
|
|
d0[x] = d1[x] = color;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_segment_horizontal - draw a horizontal
|
|
// LED segment
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_segment_horizontal(bitmap_argb32 &dest, int minx, int maxx, int midy, int width, rgb_t color)
|
|
{
|
|
draw_segment_horizontal_caps(dest, minx, maxx, midy, width, LINE_CAP_START | LINE_CAP_END, color);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_segment_vertical_caps - draw a
|
|
// vertical LED segment with definable end
|
|
// and start points
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_segment_vertical_caps(bitmap_argb32 &dest, int miny, int maxy, int midx, int width, int caps, rgb_t color)
|
|
{
|
|
// loop over the width of the segment
|
|
for (int x = 0; x < width / 2; x++)
|
|
{
|
|
UINT32 *d0 = &dest.pix32(0, midx - x);
|
|
UINT32 *d1 = &dest.pix32(0, midx + x);
|
|
int tx = (x < width / 8) ? width / 8 : x;
|
|
|
|
// loop over the length of the segment
|
|
for (int y = miny + ((caps & LINE_CAP_START) ? tx : 0); y < maxy - ((caps & LINE_CAP_END) ? tx : 0); y++)
|
|
d0[y * dest.rowpixels()] = d1[y * dest.rowpixels()] = color;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_segment_vertical - draw a vertical
|
|
// LED segment
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_segment_vertical(bitmap_argb32 &dest, int miny, int maxy, int midx, int width, rgb_t color)
|
|
{
|
|
draw_segment_vertical_caps(dest, miny, maxy, midx, width, LINE_CAP_START | LINE_CAP_END, color);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_segment_diagonal_1 - draw a diagonal
|
|
// LED segment that looks like a backslash
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_segment_diagonal_1(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color)
|
|
{
|
|
// compute parameters
|
|
width *= 1.5;
|
|
float ratio = (maxy - miny - width) / (float)(maxx - minx);
|
|
|
|
// draw line
|
|
for (int x = minx; x < maxx; x++)
|
|
if (x >= 0 && x < dest.width())
|
|
{
|
|
UINT32 *d = &dest.pix32(0, x);
|
|
int step = (x - minx) * ratio;
|
|
|
|
for (int y = maxy - width - step; y < maxy - step; y++)
|
|
if (y >= 0 && y < dest.height())
|
|
d[y * dest.rowpixels()] = color;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_segment_diagonal_2 - draw a diagonal
|
|
// LED segment that looks like a forward slash
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_segment_diagonal_2(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color)
|
|
{
|
|
// compute parameters
|
|
width *= 1.5;
|
|
float ratio = (maxy - miny - width) / (float)(maxx - minx);
|
|
|
|
// draw line
|
|
for (int x = minx; x < maxx; x++)
|
|
if (x >= 0 && x < dest.width())
|
|
{
|
|
UINT32 *d = &dest.pix32(0, x);
|
|
int step = (x - minx) * ratio;
|
|
|
|
for (int y = miny + step; y < miny + step + width; y++)
|
|
if (y >= 0 && y < dest.height())
|
|
d[y * dest.rowpixels()] = color;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_segment_decimal - draw a decimal point
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_segment_decimal(bitmap_argb32 &dest, int midx, int midy, int width, rgb_t color)
|
|
{
|
|
// compute parameters
|
|
width /= 2;
|
|
float ooradius2 = 1.0f / (float)(width * width);
|
|
|
|
// iterate over y
|
|
for (UINT32 y = 0; y <= width; y++)
|
|
{
|
|
UINT32 *d0 = &dest.pix32(midy - y);
|
|
UINT32 *d1 = &dest.pix32(midy + y);
|
|
float xval = width * sqrt(1.0f - (float)(y * y) * ooradius2);
|
|
INT32 left, right;
|
|
|
|
// compute left/right coordinates
|
|
left = midx - (INT32)(xval + 0.5f);
|
|
right = midx + (INT32)(xval + 0.5f);
|
|
|
|
// draw this scanline
|
|
for (UINT32 x = left; x < right; x++)
|
|
d0[x] = d1[x] = color;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_segment_comma - draw a comma tail
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::draw_segment_comma(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color)
|
|
{
|
|
// compute parameters
|
|
width *= 1.5;
|
|
float ratio = (maxy - miny - width) / (float)(maxx - minx);
|
|
|
|
// draw line
|
|
for (int x = minx; x < maxx; x++)
|
|
{
|
|
UINT32 *d = &dest.pix32(0, x);
|
|
int step = (x - minx) * ratio;
|
|
|
|
for (int y = maxy; y < maxy - width - step; y--)
|
|
d[y * dest.rowpixels()] = color;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// apply_skew - apply skew to a bitmap
|
|
//-------------------------------------------------
|
|
|
|
void layout_element::component::apply_skew(bitmap_argb32 &dest, int skewwidth)
|
|
{
|
|
for (int y = 0; y < dest.height(); y++)
|
|
{
|
|
UINT32 *destrow = &dest.pix32(y);
|
|
int offs = skewwidth * (dest.height() - y) / dest.height();
|
|
for (int x = dest.width() - skewwidth - 1; x >= 0; x--)
|
|
destrow[x + offs] = destrow[x];
|
|
for (int x = 0; x < offs; x++)
|
|
destrow[x] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// LAYOUT VIEW
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// layout_view - constructor
|
|
//-------------------------------------------------
|
|
|
|
layout_view::layout_view(running_machine &machine, xml_data_node &viewnode, simple_list<layout_element> &elemlist)
|
|
: m_next(NULL),
|
|
m_aspect(1.0f),
|
|
m_scraspect(1.0f)
|
|
{
|
|
// allocate a copy of the name
|
|
m_name = xml_get_attribute_string_with_subst(machine, viewnode, "name", "");
|
|
|
|
// if we have a bounds item, load it
|
|
xml_data_node *boundsnode = xml_get_sibling(viewnode.child, "bounds");
|
|
m_expbounds.x0 = m_expbounds.y0 = m_expbounds.x1 = m_expbounds.y1 = 0;
|
|
if (boundsnode != NULL)
|
|
parse_bounds(machine, xml_get_sibling(boundsnode, "bounds"), m_expbounds);
|
|
|
|
// load backdrop items
|
|
for (xml_data_node *itemnode = xml_get_sibling(viewnode.child, "backdrop"); itemnode != NULL; itemnode = xml_get_sibling(itemnode->next, "backdrop"))
|
|
m_backdrop_list.append(*global_alloc(item(machine, *itemnode, elemlist)));
|
|
|
|
// load screen items
|
|
for (xml_data_node *itemnode = xml_get_sibling(viewnode.child, "screen"); itemnode != NULL; itemnode = xml_get_sibling(itemnode->next, "screen"))
|
|
m_screen_list.append(*global_alloc(item(machine, *itemnode, elemlist)));
|
|
|
|
// load overlay items
|
|
for (xml_data_node *itemnode = xml_get_sibling(viewnode.child, "overlay"); itemnode != NULL; itemnode = xml_get_sibling(itemnode->next, "overlay"))
|
|
m_overlay_list.append(*global_alloc(item(machine, *itemnode, elemlist)));
|
|
|
|
// load bezel items
|
|
for (xml_data_node *itemnode = xml_get_sibling(viewnode.child, "bezel"); itemnode != NULL; itemnode = xml_get_sibling(itemnode->next, "bezel"))
|
|
m_bezel_list.append(*global_alloc(item(machine, *itemnode, elemlist)));
|
|
|
|
// load cpanel items
|
|
for (xml_data_node *itemnode = xml_get_sibling(viewnode.child, "cpanel"); itemnode != NULL; itemnode = xml_get_sibling(itemnode->next, "cpanel"))
|
|
m_cpanel_list.append(*global_alloc(item(machine, *itemnode, elemlist)));
|
|
|
|
// load marquee items
|
|
for (xml_data_node *itemnode = xml_get_sibling(viewnode.child, "marquee"); itemnode != NULL; itemnode = xml_get_sibling(itemnode->next, "marquee"))
|
|
m_marquee_list.append(*global_alloc(item(machine, *itemnode, elemlist)));
|
|
|
|
// recompute the data for the view based on a default layer config
|
|
recompute(render_layer_config());
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// layout_view - destructor
|
|
//-------------------------------------------------
|
|
|
|
layout_view::~layout_view()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// first_item - return the first item in the
|
|
// appropriate list
|
|
//-------------------------------------------------
|
|
|
|
layout_view::item *layout_view::first_item(item_layer layer) const
|
|
{
|
|
switch (layer)
|
|
{
|
|
case ITEM_LAYER_BACKDROP: return m_backdrop_list.first();
|
|
case ITEM_LAYER_SCREEN: return m_screen_list.first();
|
|
case ITEM_LAYER_OVERLAY: return m_overlay_list.first();
|
|
case ITEM_LAYER_BEZEL: return m_bezel_list.first();
|
|
case ITEM_LAYER_CPANEL: return m_cpanel_list.first();
|
|
case ITEM_LAYER_MARQUEE: return m_marquee_list.first();
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// recompute - recompute the bounds and aspect
|
|
// ratio of a view and all of its contained items
|
|
//-------------------------------------------------
|
|
|
|
void layout_view::recompute(render_layer_config layerconfig)
|
|
{
|
|
// reset the bounds
|
|
m_bounds.x0 = m_bounds.y0 = m_bounds.x1 = m_bounds.y1 = 0.0f;
|
|
m_scrbounds.x0 = m_scrbounds.y0 = m_scrbounds.x1 = m_scrbounds.y1 = 0.0f;
|
|
m_screens.reset();
|
|
|
|
// loop over all layers
|
|
bool first = true;
|
|
bool scrfirst = true;
|
|
for (item_layer layer = ITEM_LAYER_FIRST; layer < ITEM_LAYER_MAX; ++layer)
|
|
{
|
|
// determine if this layer should be visible
|
|
switch (layer)
|
|
{
|
|
case ITEM_LAYER_BACKDROP: m_layenabled[layer] = layerconfig.backdrops_enabled(); break;
|
|
case ITEM_LAYER_OVERLAY: m_layenabled[layer] = layerconfig.overlays_enabled(); break;
|
|
case ITEM_LAYER_BEZEL: m_layenabled[layer] = layerconfig.bezels_enabled(); break;
|
|
case ITEM_LAYER_CPANEL: m_layenabled[layer] = layerconfig.cpanels_enabled(); break;
|
|
case ITEM_LAYER_MARQUEE: m_layenabled[layer] = layerconfig.marquees_enabled(); break;
|
|
default: m_layenabled[layer] = true; break;
|
|
}
|
|
|
|
// only do it if requested
|
|
if (m_layenabled[layer])
|
|
for (item *curitem = first_item(layer); curitem != NULL; curitem = curitem->next())
|
|
{
|
|
// accumulate bounds
|
|
if (first)
|
|
m_bounds = curitem->m_rawbounds;
|
|
else
|
|
union_render_bounds(&m_bounds, &curitem->m_rawbounds);
|
|
first = false;
|
|
|
|
// accumulate screen bounds
|
|
if (curitem->m_screen != NULL)
|
|
{
|
|
if (scrfirst)
|
|
m_scrbounds = curitem->m_rawbounds;
|
|
else
|
|
union_render_bounds(&m_scrbounds, &curitem->m_rawbounds);
|
|
scrfirst = false;
|
|
|
|
// accumulate the screens in use while we're scanning
|
|
m_screens.add(*curitem->m_screen);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we have an explicit bounds, override it
|
|
if (m_expbounds.x1 > m_expbounds.x0)
|
|
m_bounds = m_expbounds;
|
|
|
|
// if we're handling things normally, the target bounds are (0,0)-(1,1)
|
|
render_bounds target_bounds;
|
|
if (!layerconfig.zoom_to_screen() || m_screens.count() == 0)
|
|
{
|
|
// compute the aspect ratio of the view
|
|
m_aspect = (m_bounds.x1 - m_bounds.x0) / (m_bounds.y1 - m_bounds.y0);
|
|
|
|
target_bounds.x0 = target_bounds.y0 = 0.0f;
|
|
target_bounds.x1 = target_bounds.y1 = 1.0f;
|
|
}
|
|
|
|
// if we're cropping, we want the screen area to fill (0,0)-(1,1)
|
|
else
|
|
{
|
|
// compute the aspect ratio of the screen
|
|
m_scraspect = (m_scrbounds.x1 - m_scrbounds.x0) / (m_scrbounds.y1 - m_scrbounds.y0);
|
|
|
|
float targwidth = (m_bounds.x1 - m_bounds.x0) / (m_scrbounds.x1 - m_scrbounds.x0);
|
|
float targheight = (m_bounds.y1 - m_bounds.y0) / (m_scrbounds.y1 - m_scrbounds.y0);
|
|
target_bounds.x0 = (m_bounds.x0 - m_scrbounds.x0) / (m_bounds.x1 - m_bounds.x0) * targwidth;
|
|
target_bounds.y0 = (m_bounds.y0 - m_scrbounds.y0) / (m_bounds.y1 - m_bounds.y0) * targheight;
|
|
target_bounds.x1 = target_bounds.x0 + targwidth;
|
|
target_bounds.y1 = target_bounds.y0 + targheight;
|
|
}
|
|
|
|
// determine the scale/offset for normalization
|
|
float xoffs = m_bounds.x0;
|
|
float yoffs = m_bounds.y0;
|
|
float xscale = (target_bounds.x1 - target_bounds.x0) / (m_bounds.x1 - m_bounds.x0);
|
|
float yscale = (target_bounds.y1 - target_bounds.y0) / (m_bounds.y1 - m_bounds.y0);
|
|
|
|
// normalize all the item bounds
|
|
for (item_layer layer = ITEM_LAYER_FIRST; layer < ITEM_LAYER_MAX; ++layer)
|
|
for (item *curitem = first_item(layer); curitem != NULL; curitem = curitem->next())
|
|
{
|
|
curitem->m_bounds.x0 = target_bounds.x0 + (curitem->m_rawbounds.x0 - xoffs) * xscale;
|
|
curitem->m_bounds.x1 = target_bounds.x0 + (curitem->m_rawbounds.x1 - xoffs) * xscale;
|
|
curitem->m_bounds.y0 = target_bounds.y0 + (curitem->m_rawbounds.y0 - yoffs) * yscale;
|
|
curitem->m_bounds.y1 = target_bounds.y0 + (curitem->m_rawbounds.y1 - yoffs) * yscale;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------
|
|
// resolve_tags - resolve tags
|
|
//-----------------------------
|
|
|
|
void layout_view::resolve_tags()
|
|
{
|
|
for (item_layer layer = ITEM_LAYER_FIRST; layer < ITEM_LAYER_MAX; ++layer)
|
|
{
|
|
for (item *curitem = first_item(layer); curitem != NULL; curitem = curitem->next())
|
|
{
|
|
curitem->resolve_tags();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// LAYOUT VIEW ITEM
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// item - constructor
|
|
//-------------------------------------------------
|
|
|
|
layout_view::item::item(running_machine &machine, xml_data_node &itemnode, simple_list<layout_element> &elemlist)
|
|
: m_next(NULL),
|
|
m_element(NULL),
|
|
m_input_port(NULL),
|
|
m_input_mask(0),
|
|
m_screen(NULL),
|
|
m_orientation(ROT0)
|
|
{
|
|
// allocate a copy of the output name
|
|
m_output_name = xml_get_attribute_string_with_subst(machine, itemnode, "name", "");
|
|
|
|
// allocate a copy of the input tag
|
|
m_input_tag = xml_get_attribute_string_with_subst(machine, itemnode, "inputtag", "");
|
|
|
|
// find the associated element
|
|
const char *name = xml_get_attribute_string_with_subst(machine, itemnode, "element", NULL);
|
|
if (name != NULL)
|
|
{
|
|
// search the list of elements for a match
|
|
for (m_element = elemlist.first(); m_element != NULL; m_element = m_element->next())
|
|
if (strcmp(name, m_element->name()) == 0)
|
|
break;
|
|
|
|
// error if not found
|
|
if (m_element == NULL)
|
|
throw emu_fatalerror("Unable to find layout element %s", name);
|
|
}
|
|
|
|
// fetch common data
|
|
int index = xml_get_attribute_int_with_subst(machine, itemnode, "index", -1);
|
|
if (index != -1)
|
|
{
|
|
screen_device_iterator iter(machine.root_device());
|
|
m_screen = iter.byindex(index);
|
|
}
|
|
m_input_mask = xml_get_attribute_int_with_subst(machine, itemnode, "inputmask", 0);
|
|
if (m_output_name[0] != 0 && m_element != NULL)
|
|
output_set_value(m_output_name.c_str(), m_element->default_state());
|
|
parse_bounds(machine, xml_get_sibling(itemnode.child, "bounds"), m_rawbounds);
|
|
parse_color(machine, xml_get_sibling(itemnode.child, "color"), m_color);
|
|
parse_orientation(machine, xml_get_sibling(itemnode.child, "orientation"), m_orientation);
|
|
|
|
// sanity checks
|
|
if (strcmp(itemnode.name, "screen") == 0)
|
|
{
|
|
if (m_screen == NULL)
|
|
throw emu_fatalerror("Layout references invalid screen index %d", index);
|
|
}
|
|
else
|
|
{
|
|
if (m_element == NULL)
|
|
throw emu_fatalerror("Layout item of type %s require an element tag", itemnode.name);
|
|
}
|
|
|
|
if (has_input())
|
|
{
|
|
m_input_port = m_element->machine().root_device().ioport(m_input_tag.c_str());
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// item - destructor
|
|
//-------------------------------------------------
|
|
|
|
layout_view::item::~item()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// screen_container - retrieve screen container
|
|
//-------------------------------------------------
|
|
|
|
|
|
render_container *layout_view::item::screen_container(running_machine &machine) const
|
|
{
|
|
return (m_screen != NULL) ? &m_screen->container() : NULL;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// state - fetch state based on configured source
|
|
//-------------------------------------------------
|
|
|
|
int layout_view::item::state() const
|
|
{
|
|
int state = 0;
|
|
|
|
assert(m_element != NULL);
|
|
|
|
// if configured to an output, fetch the output value
|
|
if (m_output_name[0] != 0)
|
|
state = output_get_value(m_output_name.c_str());
|
|
|
|
// if configured to an input, fetch the input value
|
|
else if (m_input_tag[0] != 0)
|
|
{
|
|
if (m_input_port != NULL)
|
|
{
|
|
ioport_field *field = m_input_port->field(m_input_mask);
|
|
if (field != NULL)
|
|
state = ((m_input_port->read() ^ field->defvalue()) & m_input_mask) ? 1 : 0;
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
|
|
|
|
//---------------------------------------------
|
|
// resolve_tags - resolve tags, if any are set
|
|
//---------------------------------------------
|
|
|
|
|
|
void layout_view::item::resolve_tags()
|
|
{
|
|
if (has_input())
|
|
{
|
|
m_input_port = m_element->machine().root_device().ioport(m_input_tag.c_str());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// LAYOUT FILE
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// layout_file - constructor
|
|
//-------------------------------------------------
|
|
|
|
layout_file::layout_file(running_machine &machine, xml_data_node &rootnode, const char *dirname)
|
|
: m_next(NULL)
|
|
{
|
|
// find the layout node
|
|
xml_data_node *mamelayoutnode = xml_get_sibling(rootnode.child, "mamelayout");
|
|
if (mamelayoutnode == NULL)
|
|
throw emu_fatalerror("Invalid XML file: missing mamelayout node");
|
|
|
|
// validate the config data version
|
|
int version = xml_get_attribute_int(mamelayoutnode, "version", 0);
|
|
if (version != LAYOUT_VERSION)
|
|
throw emu_fatalerror("Invalid XML file: unsupported version");
|
|
|
|
// parse all the elements
|
|
for (xml_data_node *elemnode = xml_get_sibling(mamelayoutnode->child, "element"); elemnode != NULL; elemnode = xml_get_sibling(elemnode->next, "element"))
|
|
m_elemlist.append(*global_alloc(layout_element(machine, *elemnode, dirname)));
|
|
|
|
// parse all the views
|
|
for (xml_data_node *viewnode = xml_get_sibling(mamelayoutnode->child, "view"); viewnode != NULL; viewnode = xml_get_sibling(viewnode->next, "view"))
|
|
m_viewlist.append(*global_alloc(layout_view(machine, *viewnode, m_elemlist)));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~layout_file - destructor
|
|
//-------------------------------------------------
|
|
|
|
layout_file::~layout_file()
|
|
{
|
|
}
|