summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/rendlay.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/rendlay.c')
-rw-r--r--src/emu/rendlay.c2126
1 files changed, 2126 insertions, 0 deletions
diff --git a/src/emu/rendlay.c b/src/emu/rendlay.c
new file mode 100644
index 00000000000..95f92443ede
--- /dev/null
+++ b/src/emu/rendlay.c
@@ -0,0 +1,2126 @@
+/***************************************************************************
+
+ rendlay.c
+
+ Core rendering layout parser and manager.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+****************************************************************************
+
+ Notes:
+
+ Unlike the old system, the artwork is not rotated with the game
+ orientation. This is to support odd configurations like two
+ monitors in different orientations. You can specify an orientation
+ for a backdrop/screen/overlay/bezel element, but it only applies
+ to the artwork itself, and does not affect coordinates in any way.
+
+
+ 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 "render.h"
+#include "rendfont.h"
+#include "rendlay.h"
+#include "rendutil.h"
+#include "output.h"
+#include "xmlfile.h"
+#include "png.h"
+
+
+
+/***************************************************************************
+ STANDARD LAYOUTS
+***************************************************************************/
+
+/* 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"
+
+/* generic color overlay layouts */
+#include "ho20ffff.lh"
+#include "ho2eff2e.lh"
+#include "ho4f893d.lh"
+#include "ho88ffff.lh"
+#include "hoa0a0ff.lh"
+#include "hoffe457.lh"
+#include "voffff20.lh"
+#include "hoffff20.lh"
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define LAYOUT_VERSION 2
+
+#define LINE_CAP_NONE 0
+#define LINE_CAP_START 1
+#define LINE_CAP_END 2
+
+enum
+{
+ COMPONENT_TYPE_IMAGE = 0,
+ COMPONENT_TYPE_RECT,
+ COMPONENT_TYPE_DISK,
+ COMPONENT_TYPE_TEXT,
+ COMPONENT_TYPE_LED7SEG,
+ COMPONENT_TYPE_LED14SEG,
+ COMPONENT_TYPE_LED16SEG,
+ COMPONENT_TYPE_LED14SEGSC,
+ COMPONENT_TYPE_LED16SEGSC,
+ COMPONENT_TYPE_MAX
+};
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* an element_component represents an image, rectangle, or disk in an element */
+struct _element_component
+{
+ element_component * next; /* link to next component */
+ int type; /* type of component */
+ int state; /* state where this component is visible (-1 means all states) */
+ render_bounds bounds; /* bounds of the element */
+ render_color color; /* color of the element */
+ const char * string; /* string for text components */
+ mame_bitmap * bitmap; /* source bitmap for images */
+ const char * dirname; /* directory name of image file (for lazy loading) */
+ const char * imagefile; /* name of the image file (for lazy loading) */
+ const char * alphafile; /* name of the alpha file (for lazy loading) */
+ int hasalpha; /* is there any alpha component present? */
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* layout elements */
+static void layout_element_scale(mame_bitmap *dest, const mame_bitmap *source, const rectangle *sbounds, void *param);
+static void layout_element_draw_rect(mame_bitmap *dest, const rectangle *bounds, const render_color *color);
+static void layout_element_draw_disk(mame_bitmap *dest, const rectangle *bounds, const render_color *color);
+static void layout_element_draw_text(mame_bitmap *dest, const rectangle *bounds, const render_color *color, const char *string);
+static void layout_element_draw_led7seg(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int state);
+static void layout_element_draw_led14seg(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int state);
+static void layout_element_draw_led16seg(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int state);
+static void layout_element_draw_led14segsc(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int state);
+static void layout_element_draw_led16segsc(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int state);
+
+/* layout file parsing */
+static layout_element *load_layout_element(xml_data_node *elemnode, const char *dirname);
+static element_component *load_element_component(xml_data_node *compnode, const char *dirname);
+static layout_view *load_layout_view(xml_data_node *viewnode, layout_element *elemlist);
+static view_item *load_view_item(xml_data_node *itemnode, layout_element *elemlist);
+static mame_bitmap *load_component_bitmap(const char *dirname, const char *file, const char *alphafile, int *hasalpha);
+static int load_bounds(xml_data_node *boundsnode, render_bounds *bounds);
+static int load_color(xml_data_node *colornode, render_color *color);
+static int load_orientation(xml_data_node *orientnode, int *orientation);
+static void layout_view_free(layout_view *view);
+static void layout_element_free(layout_element *element);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ 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)
+{
+ int div;
+
+ /* search the greatest common divisor */
+ div = gcd(*num, *den);
+
+ /* reduce the fraction if a common divisor has been found */
+ if (div > 1)
+ {
+ *num /= div;
+ *den /= div;
+ }
+}
+
+
+/*-------------------------------------------------
+ copy_string - make a copy of a string
+-------------------------------------------------*/
+
+INLINE const char *copy_string(const char *string)
+{
+ char *newstring = malloc_or_die(strlen(string) + 1);
+ strcpy(newstring, string);
+ return newstring;
+}
+
+
+/*-------------------------------------------------
+ view_has_art - true if a render_view contains
+ any non-screen elements
+-------------------------------------------------*/
+
+INLINE int view_has_art(layout_view *view)
+{
+ return (view->itemlist[ITEM_LAYER_BACKDROP] != 0 ||
+ view->itemlist[ITEM_LAYER_OVERLAY] != 0 ||
+ view->itemlist[ITEM_LAYER_BEZEL] != 0);
+}
+
+
+
+/***************************************************************************
+ LAYOUT VIEWS
+***************************************************************************/
+
+/*-------------------------------------------------
+ layout_view_recompute - recompute the bounds
+ and aspect ratio of a view and all of its
+ contained items
+-------------------------------------------------*/
+
+void layout_view_recompute(layout_view *view, int layerconfig)
+{
+ render_bounds target_bounds;
+ float xscale, yscale;
+ float xoffs, yoffs;
+ int scrfirst = TRUE;
+ int first = TRUE;
+ int layer;
+
+ /* reset the bounds */
+ view->bounds.x0 = view->bounds.y0 = view->bounds.x1 = view->bounds.y1 = 0.0f;
+ view->scrbounds.x0 = view->scrbounds.y0 = view->scrbounds.x1 = view->scrbounds.y1 = 0.0f;
+ view->screens = 0;
+
+ /* loop over all layers */
+ for (layer = 0; layer < ITEM_LAYER_MAX; layer++)
+ {
+ static const int layer_mask[ITEM_LAYER_MAX] = { LAYER_CONFIG_ENABLE_BACKDROP, 0, LAYER_CONFIG_ENABLE_OVERLAY, LAYER_CONFIG_ENABLE_BEZEL };
+
+ /* determine if this layer should be visible */
+ view->layenabled[layer] = (layer_mask[layer] == 0 || (layerconfig & layer_mask[layer]));
+
+ /* only do it if requested */
+ if (view->layenabled[layer])
+ {
+ view_item *item;
+
+ for (item = view->itemlist[layer]; item != NULL; item = item->next)
+ {
+ /* accumulate bounds */
+ if (first)
+ view->bounds = item->rawbounds;
+ else
+ union_render_bounds(&view->bounds, &item->rawbounds);
+ first = FALSE;
+
+ /* accumulate screen bounds */
+ if (item->element == NULL)
+ {
+ if (scrfirst)
+ view->scrbounds = item->rawbounds;
+ else
+ union_render_bounds(&view->scrbounds, &item->rawbounds);
+ scrfirst = FALSE;
+ }
+ }
+ }
+ }
+
+ /* if we have an explicit bounds, override it */
+ if (view->expbounds.x1 > view->expbounds.x0)
+ view->bounds = view->expbounds;
+
+ /* compute the aspect ratio of the view */
+ view->aspect = (view->bounds.x1 - view->bounds.x0) / (view->bounds.y1 - view->bounds.y0);
+ view->scraspect = (view->scrbounds.x1 - view->scrbounds.x0) / (view->scrbounds.y1 - view->scrbounds.y0);
+
+ /* if we're handling things normally, the target bounds are (0,0)-(1,1) */
+ if (!(layerconfig & LAYER_CONFIG_ZOOM_TO_SCREEN))
+ {
+ 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
+ {
+ float targwidth = (view->bounds.x1 - view->bounds.x0) / (view->scrbounds.x1 - view->scrbounds.x0);
+ float targheight = (view->bounds.y1 - view->bounds.y0) / (view->scrbounds.y1 - view->scrbounds.y0);
+ target_bounds.x0 = (view->bounds.x0 - view->scrbounds.x0) / (view->bounds.x1 - view->bounds.x0) * targwidth;
+ target_bounds.y0 = (view->bounds.y0 - view->scrbounds.y0) / (view->bounds.y1 - view->bounds.y0) * targheight;
+ target_bounds.x1 = target_bounds.x0 + targwidth;
+ target_bounds.y1 = target_bounds.y0 + targheight;
+ }
+
+ /* determine the scale/offset for normalization */
+ xoffs = view->bounds.x0;
+ yoffs = view->bounds.y0;
+ xscale = (target_bounds.x1 - target_bounds.x0) / (view->bounds.x1 - view->bounds.x0);
+ yscale = (target_bounds.y1 - target_bounds.y0) / (view->bounds.y1 - view->bounds.y0);
+
+ /* normalize all the item bounds */
+ for (layer = 0; layer < ITEM_LAYER_MAX; layer++)
+ {
+ view_item *item;
+
+ /* adjust the bounds for each item */
+ for (item = view->itemlist[layer]; item; item = item->next)
+ {
+ item->bounds.x0 = target_bounds.x0 + (item->rawbounds.x0 - xoffs) * xscale;
+ item->bounds.x1 = target_bounds.x0 + (item->rawbounds.x1 - xoffs) * xscale;
+ item->bounds.y0 = target_bounds.y0 + (item->rawbounds.y0 - yoffs) * yscale;
+ item->bounds.y1 = target_bounds.y0 + (item->rawbounds.y1 - yoffs) * yscale;
+
+ /* accumulate the screens in use while we're scanning */
+ if (item->element == NULL)
+ view->screens |= 1 << item->index;
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ LAYOUT ELEMENTS
+***************************************************************************/
+
+/*-------------------------------------------------
+ layout_element_scale - scale an element by
+ rendering all the components at the
+ appropriate resolution
+-------------------------------------------------*/
+
+static void layout_element_scale(mame_bitmap *dest, const mame_bitmap *source, const rectangle *sbounds, void *param)
+{
+ element_texture *elemtex = param;
+ element_component *component;
+
+ /* iterate over components that are part of the current state */
+ for (component = elemtex->element->complist; component != NULL; component = component->next)
+ if (component->state == -1 || component->state == elemtex->state)
+ {
+ rectangle bounds;
+
+ /* get the local scaled bounds */
+ bounds.min_x = render_round_nearest(component->bounds.x0 * dest->width);
+ bounds.min_y = render_round_nearest(component->bounds.y0 * dest->height);
+ bounds.max_x = render_round_nearest(component->bounds.x1 * dest->width);
+ bounds.max_y = render_round_nearest(component->bounds.y1 * dest->height);
+
+ /* based on the component type, add to the texture */
+ switch (component->type)
+ {
+ case COMPONENT_TYPE_IMAGE:
+ if (component->bitmap == NULL)
+ component->bitmap = load_component_bitmap(component->dirname, component->imagefile, component->alphafile, &component->hasalpha);
+ render_resample_argb_bitmap_hq(
+ BITMAP_ADDR32(dest, bounds.min_y, bounds.min_x),
+ dest->rowpixels,
+ bounds.max_x - bounds.min_x,
+ bounds.max_y - bounds.min_y,
+ component->bitmap, NULL, &component->color);
+ break;
+
+ case COMPONENT_TYPE_RECT:
+ layout_element_draw_rect(dest, &bounds, &component->color);
+ break;
+
+ case COMPONENT_TYPE_DISK:
+ layout_element_draw_disk(dest, &bounds, &component->color);
+ break;
+
+ case COMPONENT_TYPE_TEXT:
+ layout_element_draw_text(dest, &bounds, &component->color, component->string);
+ break;
+
+ case COMPONENT_TYPE_LED7SEG:
+ layout_element_draw_led7seg(dest, &bounds, &component->color, elemtex->state);
+ break;
+
+ case COMPONENT_TYPE_LED14SEG:
+ layout_element_draw_led14seg(dest, &bounds, &component->color, elemtex->state);
+ break;
+
+ case COMPONENT_TYPE_LED16SEG:
+ layout_element_draw_led16seg(dest, &bounds, &component->color, elemtex->state);
+ break;
+
+ case COMPONENT_TYPE_LED14SEGSC:
+ layout_element_draw_led14segsc(dest, &bounds, &component->color, elemtex->state);
+ break;
+
+ case COMPONENT_TYPE_LED16SEGSC:
+ layout_element_draw_led16segsc(dest, &bounds, &component->color, elemtex->state);
+ break;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ layout_element_draw_rect - draw a rectangle
+ in the specified color
+-------------------------------------------------*/
+
+static void layout_element_draw_rect(mame_bitmap *dest, const rectangle *bounds, const render_color *color)
+{
+ UINT32 r, g, b, inva;
+ UINT32 x, y;
+
+ /* compute premultiplied colors */
+ r = color->r * color->a * 255.0;
+ g = color->g * color->a * 255.0;
+ b = color->b * color->a * 255.0;
+ inva = (1.0f - color->a) * 255.0;
+
+ /* iterate over X and Y */
+ for (y = bounds->min_y; y < bounds->max_y; y++)
+ for (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)
+ {
+ UINT32 dpix = *BITMAP_ADDR32(dest, y, x);
+ finalr += (RGB_RED(dpix) * inva) >> 8;
+ finalg += (RGB_GREEN(dpix) * inva) >> 8;
+ finalb += (RGB_BLUE(dpix) * inva) >> 8;
+ }
+
+ /* store the target pixel, dividing the RGBA values by the overall scale factor */
+ *BITMAP_ADDR32(dest, y, x) = MAKE_ARGB(0xff, finalr, finalg, finalb);
+ }
+}
+
+
+/*-------------------------------------------------
+ layout_element_draw_disk - draw an ellipse
+ in the specified color
+-------------------------------------------------*/
+
+static void layout_element_draw_disk(mame_bitmap *dest, const rectangle *bounds, const render_color *color)
+{
+ float xcenter, ycenter;
+ float xradius, yradius, ooyradius2;
+ UINT32 r, g, b, inva;
+ UINT32 x, y;
+
+ /* compute premultiplied colors */
+ r = color->r * color->a * 255.0;
+ g = color->g * color->a * 255.0;
+ b = color->b * color->a * 255.0;
+ inva = (1.0f - color->a) * 255.0;
+
+ /* find the center */
+ xcenter = (float)(bounds->min_x + bounds->max_x) * 0.5f;
+ ycenter = (float)(bounds->min_y + bounds->max_y) * 0.5f;
+ xradius = (float)(bounds->max_x - bounds->min_x) * 0.5f;
+ yradius = (float)(bounds->max_y - bounds->min_y) * 0.5f;
+ ooyradius2 = 1.0f / (yradius * yradius);
+
+ /* iterate over y */
+ for (y = bounds->min_y; y < bounds->max_y; y++)
+ {
+ float ycoord = ycenter - ((float)y + 0.5f);
+ float xval = xradius * sqrt(1.0f - (ycoord * ycoord) * ooyradius2);
+ INT32 left, right;
+
+ /* compute left/right coordinates */
+ left = (INT32)(xcenter - xval + 0.5f);
+ right = (INT32)(xcenter + xval + 0.5f);
+
+ /* draw this scanline */
+ for (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)
+ {
+ UINT32 dpix = *BITMAP_ADDR32(dest, y, x);
+ finalr += (RGB_RED(dpix) * inva) >> 8;
+ finalg += (RGB_GREEN(dpix) * inva) >> 8;
+ finalb += (RGB_BLUE(dpix) * inva) >> 8;
+ }
+
+ /* store the target pixel, dividing the RGBA values by the overall scale factor */
+ *BITMAP_ADDR32(dest, y, x) = MAKE_ARGB(0xff, finalr, finalg, finalb);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ layout_element_draw_text - draw text in the
+ specified color
+-------------------------------------------------*/
+
+static void layout_element_draw_text(mame_bitmap *dest, const rectangle *bounds, const render_color *color, const char *string)
+{
+ render_font *font = render_font_alloc(NULL);
+ mame_bitmap *tempbitmap;
+ UINT32 r, g, b, a;
+ float aspect = 1.0f;
+ INT32 curx, width;
+ const char *s;
+
+ /* compute premultiplied colors */
+ r = color->r * 255.0;
+ g = color->g * 255.0;
+ b = color->b * 255.0;
+ a = color->a * 255.0;
+
+ /* get the width of the string */
+ while (1)
+ {
+ width = render_font_get_string_width(font, bounds->max_y - bounds->min_y, aspect, string);
+ if (width < bounds->max_x - bounds->min_x)
+ break;
+ aspect *= 0.9f;
+ }
+ curx = bounds->min_x + (bounds->max_x - bounds->min_x - width) / 2;
+
+ /* allocate a temporary bitmap */
+ tempbitmap = bitmap_alloc(dest->width, dest->height, BITMAP_FORMAT_ARGB32);
+
+ /* loop over characters */
+ for (s = string; *s != 0; s++)
+ {
+ rectangle chbounds;
+ int x, y;
+
+ /* get the font bitmap */
+ render_font_get_scaled_bitmap_and_bounds(font, tempbitmap, bounds->max_y - bounds->min_y, aspect, *s, &chbounds);
+
+ /* copy the data into the target */
+ for (y = 0; y < chbounds.max_y - chbounds.min_y; y++)
+ {
+ int effy = bounds->min_y + y;
+ if (effy >= bounds->min_y && effy <= bounds->max_y)
+ {
+ UINT32 *s = BITMAP_ADDR32(tempbitmap, y, 0);
+ UINT32 *d = BITMAP_ADDR32(dest, effy, 0);
+ for (x = 0; x < chbounds.max_x - chbounds.min_x; x++)
+ {
+ int effx = curx + x + chbounds.min_x;
+ if (effx >= bounds->min_x && effx <= bounds->max_x)
+ {
+ UINT32 spix = RGB_ALPHA(s[x]);
+ if (spix != 0)
+ {
+ UINT32 dpix = d[effx];
+ UINT32 ta, tr, tg, tb;
+
+ ta = (a * (spix + 1)) >> 8;
+ tr = (r * ta + RGB_RED(dpix) * (0x100 - ta)) >> 8;
+ tg = (g * ta + RGB_GREEN(dpix) * (0x100 - ta)) >> 8;
+ tb = (b * ta + RGB_BLUE(dpix) * (0x100 - ta)) >> 8;
+ d[effx] = MAKE_ARGB(0xff, tr, tg, tb);
+ }
+ }
+ }
+ }
+ }
+
+ /* advance in the X direction */
+ curx += render_font_get_char_width(font, bounds->max_y - bounds->min_y, aspect, *s);
+ }
+
+ /* free the temporary bitmap and font */
+ bitmap_free(tempbitmap);
+ render_font_free(font);
+}
+
+
+/*-------------------------------------------------
+ draw_segment_horizontal_caps - draw a
+ horizontal LED segment with definable end
+ and start points
+-------------------------------------------------*/
+
+static void draw_segment_horizontal_caps(mame_bitmap *dest, int minx, int maxx, int midy, int width, int caps, rgb_t color)
+{
+ int x, y;
+
+ /* loop over the width of the segment */
+ for (y = 0; y < width / 2; y++)
+ {
+ UINT32 *d0 = BITMAP_ADDR32(dest, midy - y, 0);
+ UINT32 *d1 = BITMAP_ADDR32(dest, midy + y, 0);
+ int ty = (y < width / 8) ? width / 8 : y;
+
+ /* loop over the length of the segment */
+ for (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
+-------------------------------------------------*/
+
+static void draw_segment_horizontal(mame_bitmap *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
+-------------------------------------------------*/
+
+static void draw_segment_vertical_caps(mame_bitmap *dest, int miny, int maxy, int midx, int width, int caps, rgb_t color)
+{
+ int x, y;
+
+ /* loop over the width of the segment */
+ for (x = 0; x < width / 2; x++)
+ {
+ UINT32 *d0 = BITMAP_ADDR32(dest, 0, midx - x);
+ UINT32 *d1 = BITMAP_ADDR32(dest, 0, midx + x);
+ int tx = (x < width / 8) ? width / 8 : x;
+
+ /* loop over the length of the segment */
+ for (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
+-------------------------------------------------*/
+
+static void draw_segment_vertical(mame_bitmap *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 this: /
+-------------------------------------------------*/
+
+static void draw_segment_diagonal_1(mame_bitmap *dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color)
+{
+ int x, y;
+ float ratio;
+
+ /* compute parameters */
+ width *= 1.5;
+ ratio = (maxy - miny - width) / (float)(maxx - minx);
+
+ /* draw line */
+ for (x = minx; x < maxx; x++)
+ if (x >= 0 && x < dest->width)
+ {
+ UINT32 *d = BITMAP_ADDR32(dest, 0, x);
+ int step = (x - minx) * ratio;
+
+ for (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 this: \
+-------------------------------------------------*/
+
+static void draw_segment_diagonal_2(mame_bitmap *dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color)
+{
+ int x, y;
+ float ratio;
+
+ /* compute parameters */
+ width *= 1.5;
+ ratio = (maxy - miny - width) / (float)(maxx - minx);
+
+ /* draw line */
+ for (x = minx; x < maxx; x++)
+ if (x >= 0 && x < dest->width)
+ {
+ UINT32 *d = BITMAP_ADDR32(dest, 0, x);
+ int step = (x - minx) * ratio;
+
+ for (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
+-------------------------------------------------*/
+
+static void draw_segment_decimal(mame_bitmap *dest, int midx, int midy, int width, rgb_t color)
+{
+ float ooradius2;
+ UINT32 x, y;
+
+ /* compute parameters */
+ width /= 2;
+ ooradius2 = 1.0f / (float)(width * width);
+
+ /* iterate over y */
+ for (y = 0; y <= width; y++)
+ {
+ UINT32 *d0 = BITMAP_ADDR32(dest, midy - y, 0);
+ UINT32 *d1 = BITMAP_ADDR32(dest, midy + y, 0);
+ 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 (x = left; x < right; x++)
+ d0[x] = d1[x] = color;
+ }
+}
+
+/*-------------------------------------------------
+ draw_segment_comma - draw a comma tail
+-------------------------------------------------*/
+#if 0
+static void draw_segment_comma(mame_bitmap *dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color)
+{
+ int x, y;
+ float ratio;
+
+ /* compute parameters */
+ width *= 1.5;
+ ratio = (maxy - miny - width) / (float)(maxx - minx);
+
+ /* draw line */
+ for (x = minx; x < maxx; x++)
+ {
+ UINT32 *d = BITMAP_ADDR32(dest, 0, x);
+ int step = (x - minx) * ratio;
+
+ for (y = maxy; y < maxy - width - step; y--)
+ {
+ d[y * dest->rowpixels] = color;
+ }
+ }
+}
+#endif
+
+
+/*-------------------------------------------------
+ apply_skew - apply skew to a mame_bitmap
+-------------------------------------------------*/
+
+static void apply_skew(mame_bitmap *dest, int skewwidth)
+{
+ int x, y;
+
+ for (y = 0; y < dest->height; y++)
+ {
+ UINT32 *destrow = BITMAP_ADDR32(dest, y, 0);
+ int offs = skewwidth * (dest->height - y) / dest->height;
+ for (x = dest->width - skewwidth - 1; x >= 0; x--)
+ destrow[x + offs] = destrow[x];
+ for (x = 0; x < offs; x++)
+ destrow[x] = 0;
+ }
+}
+
+
+/*-------------------------------------------------
+ layout_element_draw_led7seg - draw a
+ 7-segment LCD
+-------------------------------------------------*/
+
+static void layout_element_draw_led7seg(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int pattern)
+{
+ const rgb_t onpen = MAKE_ARGB(0xff,0xff,0xff,0xff);
+ const rgb_t offpen = MAKE_ARGB(0xff,0x20,0x20,0x20);
+ int bmwidth, bmheight, segwidth, skewwidth;
+ mame_bitmap *tempbitmap;
+
+ /* sizes for computation */
+ bmwidth = 250;
+ bmheight = 400;
+ segwidth = 40;
+ skewwidth = 40;
+
+ /* allocate a temporary bitmap for drawing */
+ tempbitmap = bitmap_alloc(bmwidth + skewwidth, bmheight, BITMAP_FORMAT_ARGB32);
+ fillbitmap(tempbitmap, MAKE_ARGB(0xff,0x00,0x00,0x00), NULL);
+
+ /* 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->base, dest->rowpixels, dest->width, dest->height, tempbitmap, NULL, color);
+
+ bitmap_free(tempbitmap);
+}
+
+
+/*-------------------------------------------------
+ layout_element_draw_led14seg - draw a
+ 14-segment LCD
+-------------------------------------------------*/
+
+static void layout_element_draw_led14seg(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int pattern)
+{
+ const rgb_t onpen = MAKE_ARGB(0xff, 0xff, 0xff, 0xff);
+ const rgb_t offpen = MAKE_ARGB(0xff, 0x20, 0x20, 0x20);
+ int bmwidth, bmheight, segwidth, skewwidth;
+ mame_bitmap *tempbitmap;
+
+ /* sizes for computation */
+ bmwidth = 250;
+ bmheight = 400;
+ segwidth = 40;
+ skewwidth = 40;
+
+ /* allocate a temporary bitmap for drawing */
+ tempbitmap = bitmap_alloc(bmwidth + skewwidth, bmheight, BITMAP_FORMAT_ARGB32);
+ fillbitmap(tempbitmap, MAKE_ARGB(0xff, 0x00, 0x00, 0x00), NULL);
+
+ /* 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->base, dest->rowpixels, dest->width, dest->height, tempbitmap, NULL, color);
+
+ bitmap_free(tempbitmap);
+}
+
+
+/*-------------------------------------------------
+ layout_element_draw_led14segsc - draw a
+ 14-segment LCD with semicolon (2 extra segments)
+-------------------------------------------------*/
+
+static void layout_element_draw_led14segsc(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int pattern)
+{
+ const rgb_t onpen = MAKE_ARGB(0xff, 0xff, 0xff, 0xff);
+ const rgb_t offpen = MAKE_ARGB(0xff, 0x20, 0x20, 0x20);
+ int bmwidth, bmheight, segwidth, skewwidth;
+ mame_bitmap *tempbitmap;
+
+ /* sizes for computation */
+ bmwidth = 250;
+ bmheight = 400;
+ segwidth = 40;
+ skewwidth = 40;
+
+ /* allocate a temporary bitmap for drawing, adding some extra space for the tail */
+ tempbitmap = bitmap_alloc(bmwidth + skewwidth, bmheight + segwidth, BITMAP_FORMAT_ARGB32);
+ fillbitmap(tempbitmap, MAKE_ARGB(0xff, 0x00, 0x00, 0x00), NULL);
+
+ /* 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);
+
+ /* decimal point */
+ draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, (pattern & (1 << 14)) ? onpen : offpen);
+
+ /* 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);
+
+ /* resample to the target size */
+ render_resample_argb_bitmap_hq(dest->base, dest->rowpixels, dest->width, dest->height, tempbitmap, NULL, color);
+
+ bitmap_free(tempbitmap);
+}
+
+
+/*-------------------------------------------------
+ layout_element_draw_led16seg - draw a
+ 16-segment LCD
+-------------------------------------------------*/
+
+static void layout_element_draw_led16seg(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int pattern)
+{
+ const rgb_t onpen = MAKE_ARGB(0xff, 0xff, 0xff, 0xff);
+ const rgb_t offpen = MAKE_ARGB(0xff, 0x20, 0x20, 0x20);
+ int bmwidth, bmheight, segwidth, skewwidth;
+ mame_bitmap *tempbitmap;
+
+ /* sizes for computation */
+ bmwidth = 250;
+ bmheight = 400;
+ segwidth = 40;
+ skewwidth = 40;
+
+ /* allocate a temporary bitmap for drawing */
+ tempbitmap = bitmap_alloc(bmwidth + skewwidth, bmheight, BITMAP_FORMAT_ARGB32);
+ fillbitmap(tempbitmap, MAKE_ARGB(0xff, 0x00, 0x00, 0x00), NULL);
+
+ /* 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->base, dest->rowpixels, dest->width, dest->height, tempbitmap, NULL, color);
+
+ bitmap_free(tempbitmap);
+}
+
+
+/*-------------------------------------------------
+ layout_element_draw_led16segsc - draw a
+ 16-segment LCD with semicolon (2 extra segments)
+-------------------------------------------------*/
+
+static void layout_element_draw_led16segsc(mame_bitmap *dest, const rectangle *bounds, const render_color *color, int pattern)
+{
+ const rgb_t onpen = MAKE_ARGB(0xff, 0xff, 0xff, 0xff);
+ const rgb_t offpen = MAKE_ARGB(0xff, 0x20, 0x20, 0x20);
+ int bmwidth, bmheight, segwidth, skewwidth;
+ mame_bitmap *tempbitmap;
+
+ /* sizes for computation */
+ bmwidth = 250;
+ bmheight = 400;
+ segwidth = 40;
+ skewwidth = 40;
+
+ /* allocate a temporary bitmap for drawing */
+ tempbitmap = bitmap_alloc(bmwidth + skewwidth, bmheight + segwidth, BITMAP_FORMAT_ARGB32);
+ fillbitmap(tempbitmap, MAKE_ARGB(0xff, 0x00, 0x00, 0x00), NULL);
+
+ /* 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);
+
+ /* decimal point */
+ draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, (pattern & (1 << 16)) ? 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);
+
+ /* apply skew */
+ apply_skew(tempbitmap, 40);
+
+ /* resample to the target size */
+ render_resample_argb_bitmap_hq(dest->base, dest->rowpixels, dest->width, dest->height, tempbitmap, NULL, color);
+
+ bitmap_free(tempbitmap);
+}
+
+
+
+/***************************************************************************
+ LAYOUT FILE PARSING
+***************************************************************************/
+
+/*-------------------------------------------------
+ get_variable_value - compute the value of
+ a variable in an XML attribute
+-------------------------------------------------*/
+
+static int get_variable_value(const char *string, char **outputptr)
+{
+ int num, den, scrnum;
+ char temp[100];
+
+ /* screen 0 parameters */
+ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++)
+ {
+ /* native X aspect factor */
+ sprintf(temp, "~scr%dnativexaspect~", scrnum);
+ if (!strncmp(string, temp, strlen(temp)))
+ {
+ num = Machine->drv->screen[scrnum].defstate.visarea.max_x + 1 - Machine->drv->screen[scrnum].defstate.visarea.min_x;
+ den = Machine->drv->screen[scrnum].defstate.visarea.max_y + 1 - Machine->drv->screen[scrnum].defstate.visarea.min_y;
+ 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)))
+ {
+ num = Machine->drv->screen[scrnum].defstate.visarea.max_x + 1 - Machine->drv->screen[scrnum].defstate.visarea.min_x;
+ den = Machine->drv->screen[scrnum].defstate.visarea.max_y + 1 - Machine->drv->screen[scrnum].defstate.visarea.min_y;
+ 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", Machine->drv->screen[scrnum].defstate.visarea.max_x + 1 - Machine->drv->screen[0].defstate.visarea.min_x);
+ return strlen(temp);
+ }
+
+ /* native height */
+ sprintf(temp, "~scr%dheight~", scrnum);
+ if (!strncmp(string, temp, strlen(temp)))
+ {
+ *outputptr += sprintf(*outputptr, "%d", Machine->drv->screen[scrnum].defstate.visarea.max_y + 1 - Machine->drv->screen[0].defstate.visarea.min_y);
+ 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(xml_data_node *node, const char *attribute, const char *defvalue)
+{
+ const char *str = xml_get_attribute_string(node, attribute, NULL);
+ static char buffer[1000];
+ const char *s;
+ char *d;
+
+ /* 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 */
+ 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(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(xml_data_node *node, const char *attribute, int defvalue)
+{
+ const char *string = xml_get_attribute_string_with_subst(node, attribute, NULL);
+ int value;
+
+ if (!string || sscanf(string, "%d", &value) != 1)
+ return defvalue;
+ return value;
+}
+
+
+/*-------------------------------------------------
+ xml_get_attribute_float_with_subst - analog
+ to xml_get_attribute_float but with variable
+ substitution
+-------------------------------------------------*/
+
+static float xml_get_attribute_float_with_subst(xml_data_node *node, const char *attribute, float defvalue)
+{
+ const char *string = xml_get_attribute_string_with_subst(node, attribute, NULL);
+ float value;
+
+ if (!string || sscanf(string, "%f", &value) != 1)
+ return defvalue;
+ return value;
+}
+
+
+/*-------------------------------------------------
+ layout_file_load - parse a layout XML file
+ into a layout_file
+-------------------------------------------------*/
+
+layout_file *layout_file_load(const char *dirname, const char *filename)
+{
+ xml_data_node *rootnode, *mamelayoutnode, *elemnode, *viewnode;
+ layout_element **elemnext;
+ layout_view **viewnext;
+ layout_file *file;
+ int version;
+
+ /* if the first character of the "file" is an open brace, assume it is an XML string */
+ if (filename[0] == '<')
+ rootnode = xml_string_read(filename, NULL);
+
+ /* otherwise, assume it is a file */
+ else
+ {
+ file_error filerr;
+ mame_file *layoutfile;
+ astring *fname;
+
+ fname = astring_assemble_2(astring_alloc(), filename, ".lay");
+ if (dirname != NULL)
+ {
+ astring_insc(fname, 0, PATH_SEPARATOR);
+ astring_insc(fname, 0, dirname);
+ }
+ filerr = mame_fopen(SEARCHPATH_ARTWORK, astring_c(fname), OPEN_FLAG_READ, &layoutfile);
+ astring_free(fname);
+
+ if (filerr != FILERR_NONE)
+ return NULL;
+ rootnode = xml_file_read(mame_core_file(layoutfile), NULL);
+ mame_fclose(layoutfile);
+ }
+
+ /* if unable to parse the file, just bail */
+ if (rootnode == NULL)
+ return NULL;
+
+ /* allocate the layout group object first */
+ file = malloc_or_die(sizeof(*file));
+ memset(file, 0, sizeof(*file));
+
+ /* find the layout node */
+ mamelayoutnode = xml_get_sibling(rootnode->child, "mamelayout");
+ if (mamelayoutnode == NULL)
+ fatalerror("Invalid XML file: missing mamelayout node");
+
+ /* validate the config data version */
+ version = xml_get_attribute_int(mamelayoutnode, "version", 0);
+ if (version != LAYOUT_VERSION)
+ fatalerror("Invalid XML file: unsupported version");
+
+ /* parse all the elements */
+ file->elemlist = NULL;
+ elemnext = &file->elemlist;
+ for (elemnode = xml_get_sibling(mamelayoutnode->child, "element"); elemnode; elemnode = xml_get_sibling(elemnode->next, "element"))
+ {
+ layout_element *element = load_layout_element(elemnode, dirname);
+ if (element == NULL)
+ goto error;
+
+ /* add to the end of the list */
+ *elemnext = element;
+ elemnext = &element->next;
+ }
+
+ /* parse all the views */
+ file->viewlist = NULL;
+ viewnext = &file->viewlist;
+ for (viewnode = xml_get_sibling(mamelayoutnode->child, "view"); viewnode; viewnode = xml_get_sibling(viewnode->next, "view"))
+ {
+ layout_view *view = load_layout_view(viewnode, file->elemlist);
+ if (view == NULL)
+ goto error;
+
+ /* add to the end of the list */
+ *viewnext = view;
+ viewnext = &view->next;
+ }
+ xml_file_free(rootnode);
+ return file;
+
+error:
+ layout_file_free(file);
+ xml_file_free(rootnode);
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ load_layout_element - parse an element XML
+ node from the layout file
+-------------------------------------------------*/
+
+static layout_element *load_layout_element(xml_data_node *elemnode, const char *dirname)
+{
+ render_bounds bounds = { 0 };
+ element_component **nextcomp;
+ element_component *component;
+ xml_data_node *compnode;
+ layout_element *element;
+ float xscale, yscale;
+ float xoffs, yoffs;
+ const char *name;
+ int state;
+ int first;
+
+ /* allocate a new element */
+ element = malloc_or_die(sizeof(*element));
+ memset(element, 0, sizeof(*element));
+
+ /* extract the name */
+ name = xml_get_attribute_string_with_subst(elemnode, "name", NULL);
+ if (name == NULL)
+ {
+ logerror("All layout elements must have a name!\n");
+ goto error;
+ }
+ element->name = copy_string(name);
+ element->defstate = xml_get_attribute_int_with_subst(elemnode, "defstate", -1);
+
+ /* parse components in order */
+ first = TRUE;
+ nextcomp = &element->complist;
+ for (compnode = elemnode->child; compnode; compnode = compnode->next)
+ {
+ /* allocate a new component */
+ element_component *component = load_element_component(compnode, dirname);
+ if (component == NULL)
+ goto error;
+
+ /* link it into the list */
+ *nextcomp = component;
+ nextcomp = &component->next;
+
+ /* accumulate bounds */
+ if (first)
+ bounds = component->bounds;
+ else
+ union_render_bounds(&bounds, &component->bounds);
+ first = FALSE;
+
+ /* determine the maximum state */
+ if (component->state > element->maxstate)
+ element->maxstate = component->state;
+ if (component->type == COMPONENT_TYPE_LED7SEG)
+ element->maxstate = 255;
+ if (component->type == COMPONENT_TYPE_LED14SEG)
+ element->maxstate = 16383;
+ if (component->type == COMPONENT_TYPE_LED14SEGSC || component->type == COMPONENT_TYPE_LED16SEG)
+ element->maxstate = 65535;
+ if (component->type == COMPONENT_TYPE_LED16SEGSC)
+ element->maxstate = 262143;
+ }
+
+ /* determine the scale/offset for normalization */
+ xoffs = bounds.x0;
+ yoffs = bounds.y0;
+ xscale = 1.0f / (bounds.x1 - bounds.x0);
+ yscale = 1.0f / (bounds.y1 - bounds.y0);
+
+ /* normalize all the component bounds */
+ for (component = element->complist; component != NULL; component = component->next)
+ {
+ component->bounds.x0 = (component->bounds.x0 - xoffs) * xscale;
+ component->bounds.x1 = (component->bounds.x1 - xoffs) * xscale;
+ component->bounds.y0 = (component->bounds.y0 - yoffs) * yscale;
+ component->bounds.y1 = (component->bounds.y1 - yoffs) * yscale;
+ }
+
+ /* allocate an array of element textures for the states */
+ element->elemtex = malloc_or_die((element->maxstate + 1) * sizeof(element->elemtex[0]));
+ for (state = 0; state <= element->maxstate; state++)
+ {
+ element->elemtex[state].element = element;
+ element->elemtex[state].state = state;
+ element->elemtex[state].texture = render_texture_alloc(layout_element_scale, &element->elemtex[state]);
+ }
+
+ return element;
+
+error:
+ layout_element_free(element);
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ load_element_component - parse a component
+ XML node (image/rect/disk)
+-------------------------------------------------*/
+
+static element_component *load_element_component(xml_data_node *compnode, const char *dirname)
+{
+ element_component *component;
+
+ /* allocate memory for the component */
+ component = malloc_or_die(sizeof(*component));
+ memset(component, 0, sizeof(*component));
+
+ /* fetch common data */
+ component->state = xml_get_attribute_int_with_subst(compnode, "state", -1);
+ if (load_bounds(xml_get_sibling(compnode->child, "bounds"), &component->bounds))
+ goto error;
+ if (load_color(xml_get_sibling(compnode->child, "color"), &component->color))
+ goto error;
+
+ /* image nodes */
+ if (strcmp(compnode->name, "image") == 0)
+ {
+ const char *file = xml_get_attribute_string_with_subst(compnode, "file", NULL);
+ const char *afile = xml_get_attribute_string_with_subst(compnode, "alphafile", NULL);
+
+ /* load and allocate the bitmap */
+ component->type = COMPONENT_TYPE_IMAGE;
+ component->dirname = (dirname == NULL) ? NULL : copy_string(dirname);
+ component->imagefile = (file == NULL) ? NULL : copy_string(file);
+ component->alphafile = (afile == NULL) ? NULL : copy_string(afile);
+ }
+
+ /* text nodes */
+ else if (strcmp(compnode->name, "text") == 0)
+ {
+ const char *text = xml_get_attribute_string_with_subst(compnode, "string", "");
+ char *string;
+
+ /* allocate a copy of the string */
+ component->type = COMPONENT_TYPE_TEXT;
+ string = malloc_or_die(strlen(text) + 1);
+ strcpy(string, text);
+ component->string = string;
+ }
+
+ /* led7seg nodes */
+ else if (strcmp(compnode->name, "led7seg") == 0)
+ component->type = COMPONENT_TYPE_LED7SEG;
+
+ /* led14seg nodes */
+ else if (strcmp(compnode->name, "led14seg") == 0)
+ component->type = COMPONENT_TYPE_LED14SEG;
+
+ /* led14segsc nodes */
+ else if (strcmp(compnode->name, "led14segsc") == 0)
+ component->type = COMPONENT_TYPE_LED14SEGSC;
+
+ /* led16seg nodes */
+ else if (strcmp(compnode->name, "led16seg") == 0)
+ component->type = COMPONENT_TYPE_LED16SEG;
+
+ /* led16segsc nodes */
+ else if (strcmp(compnode->name, "led16segsc") == 0)
+ component->type = COMPONENT_TYPE_LED16SEGSC;
+
+ /* rect nodes */
+ else if (strcmp(compnode->name, "rect") == 0)
+ component->type = COMPONENT_TYPE_RECT;
+
+ /* disk nodes */
+ else if (strcmp(compnode->name, "disk") == 0)
+ component->type = COMPONENT_TYPE_DISK;
+
+ /* error otherwise */
+ else
+ fatalerror("Unknown element component: %s", compnode->name);
+
+ return component;
+
+error:
+ free(component);
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ load_layout_view - parse a view XML node
+-------------------------------------------------*/
+
+static layout_view *load_layout_view(xml_data_node *viewnode, layout_element *elemlist)
+{
+ xml_data_node *boundsnode;
+ view_item **itemnext;
+ layout_view *view;
+ int layer;
+
+ /* first allocate memory */
+ view = malloc_or_die(sizeof(*view));
+ memset(view, 0, sizeof(*view));
+
+ /* allocate a copy of the name */
+ view->name = copy_string(xml_get_attribute_string_with_subst(viewnode, "name", ""));
+
+ /* if we have a bounds item, load it */
+ boundsnode = xml_get_sibling(viewnode->child, "bounds");
+ if (boundsnode != NULL && load_bounds(xml_get_sibling(boundsnode, "bounds"), &view->expbounds))
+ goto error;
+
+ /* loop over all the layer types we support */
+ for (layer = 0; layer < ITEM_LAYER_MAX; layer++)
+ {
+ static const char *layer_node_name[ITEM_LAYER_MAX] = { "backdrop", "screen", "overlay", "bezel" };
+ xml_data_node *itemnode;
+
+ /* initialize the list */
+ view->itemlist[layer] = NULL;
+ itemnext = &view->itemlist[layer];
+
+ /* parse all of the elements of that type */
+ for (itemnode = xml_get_sibling(viewnode->child, layer_node_name[layer]); itemnode; itemnode = xml_get_sibling(itemnode->next, layer_node_name[layer]))
+ {
+ view_item *item = load_view_item(itemnode, elemlist);
+ if (!item)
+ goto error;
+
+ /* add to the end of the list */
+ *itemnext = item;
+ itemnext = &item->next;
+ }
+ }
+
+ /* recompute the data for the view */
+ layout_view_recompute(view, ~0);
+ return view;
+
+error:
+ layout_view_free(view);
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ load_view_item - parse an item XML node
+-------------------------------------------------*/
+
+static view_item *load_view_item(xml_data_node *itemnode, layout_element *elemlist)
+{
+ view_item *item;
+ const char *name;
+
+ /* allocate a new item */
+ item = malloc_or_die(sizeof(*item));
+ memset(item, 0, sizeof(*item));
+
+ /* allocate a copy of the name */
+ item->name = copy_string(xml_get_attribute_string_with_subst(itemnode, "name", ""));
+
+ /* find the associated element */
+ name = xml_get_attribute_string_with_subst(itemnode, "element", NULL);
+ if (name != NULL)
+ {
+ layout_element *element;
+
+ /* search the list of elements for a match */
+ for (element = elemlist; element; element = element->next)
+ if (strcmp(name, element->name) == 0)
+ break;
+
+ /* error if not found */
+ if (element == NULL)
+ fatalerror("Unable to find layout element %s", name);
+ item->element = element;
+ }
+
+ /* fetch common data */
+ item->index = xml_get_attribute_int_with_subst(itemnode, "index", -1);
+ if (item->name[0] != 0 && item->element != 0)
+ output_set_value(item->name, item->element->defstate);
+ if (load_bounds(xml_get_sibling(itemnode->child, "bounds"), &item->rawbounds))
+ goto error;
+ if (load_color(xml_get_sibling(itemnode->child, "color"), &item->color))
+ goto error;
+ if (load_orientation(xml_get_sibling(itemnode->child, "orientation"), &item->orientation))
+ goto error;
+
+ /* sanity checks */
+ if (strcmp(itemnode->name, "screen") == 0)
+ {
+ if (item->index >= MAX_SCREENS)
+ fatalerror("Layout references invalid screen index %d", item->index);
+ }
+ else
+ {
+ if (item->element == NULL)
+ fatalerror("Layout item of type %s require an element tag", itemnode->name);
+ }
+
+ return item;
+
+error:
+ if (item->name != NULL)
+ free((void *)item->name);
+ free(item);
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ load_component_bitmap - load a PNG file
+ with artwork for a component
+-------------------------------------------------*/
+
+static mame_bitmap *load_component_bitmap(const char *dirname, const char *file, const char *alphafile, int *hasalpha)
+{
+ mame_bitmap *bitmap;
+
+ /* load the basic bitmap */
+ bitmap = render_load_png(dirname, file, NULL, hasalpha);
+ if (bitmap != NULL && alphafile != NULL)
+
+ /* load the alpha bitmap if specified */
+ if (render_load_png(dirname, alphafile, bitmap, hasalpha) == NULL)
+ {
+ bitmap_free(bitmap);
+ bitmap = NULL;
+ }
+
+ /* if we can't load the bitmap, allocate a dummy one and report an error */
+ if (bitmap == NULL)
+ {
+ int step, line;
+
+ /* draw some stripes in the bitmap */
+ bitmap = bitmap_alloc(100, 100, BITMAP_FORMAT_ARGB32);
+ fillbitmap(bitmap, 0, NULL);
+ for (step = 0; step < 100; step += 25)
+ for (line = 0; line < 100; line++)
+ *BITMAP_ADDR32(bitmap, (step + line) % 100, line % 100) = MAKE_ARGB(0xff,0xff,0xff,0xff);
+
+ /* log an error */
+ if (alphafile == NULL)
+ logerror("Unable to load component bitmap '%s'", file);
+ else
+ logerror("Unable to load component bitmap '%s'/'%s'", file, alphafile);
+ }
+
+ return bitmap;
+}
+
+
+/*-------------------------------------------------
+ load_bounds - parse a bounds XML node
+-------------------------------------------------*/
+
+static int load_bounds(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 0;
+ }
+
+ /* parse out the data */
+ if (xml_get_attribute(boundsnode, "left") != NULL)
+ {
+ /* left/right/top/bottom format */
+ bounds->x0 = xml_get_attribute_float_with_subst(boundsnode, "left", 0.0);
+ bounds->x1 = xml_get_attribute_float_with_subst(boundsnode, "right", 1.0);
+ bounds->y0 = xml_get_attribute_float_with_subst(boundsnode, "top", 0.0);
+ bounds->y1 = xml_get_attribute_float_with_subst(boundsnode, "bottom", 1.0);
+ }
+ else if (xml_get_attribute(boundsnode, "x") != NULL)
+ {
+ /* x/y/width/height format */
+ bounds->x0 = xml_get_attribute_float_with_subst(boundsnode, "x", 0.0);
+ bounds->x1 = bounds->x0 + xml_get_attribute_float_with_subst(boundsnode, "width", 1.0);
+ bounds->y0 = xml_get_attribute_float_with_subst(boundsnode, "y", 0.0);
+ bounds->y1 = bounds->y0 + xml_get_attribute_float_with_subst(boundsnode, "height", 1.0);
+ }
+ else
+ {
+ fatalerror("Illegal bounds value in XML");
+ return 1;
+ }
+
+ /* check for errors */
+ if (bounds->x0 > bounds->x1 || bounds->y0 > bounds->y1)
+ {
+ fatalerror("Illegal bounds value in XML: (%f-%f)-(%f-%f)", bounds->x0, bounds->x1, bounds->y0, bounds->y1);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ load_color - parse a color XML node
+-------------------------------------------------*/
+
+static int load_color(xml_data_node *colornode, render_color *color)
+{
+ /* skip if nothing */
+ if (colornode == NULL)
+ {
+ color->r = color->g = color->b = color->a = 1.0f;
+ return 0;
+ }
+
+ /* parse out the data */
+ color->r = xml_get_attribute_float_with_subst(colornode, "red", 1.0);
+ color->g = xml_get_attribute_float_with_subst(colornode, "green", 1.0);
+ color->b = xml_get_attribute_float_with_subst(colornode, "blue", 1.0);
+ color->a = xml_get_attribute_float_with_subst(colornode, "alpha", 1.0);
+
+ /* check for errors */
+ if (color->r < 0.0 || color->r > 1.0 || color->g < 0.0 || color->g > 1.0 ||
+ color->b < 0.0 || color->b > 1.0 || color->a < 0.0 || color->a > 1.0)
+ {
+ fatalerror("Illegal ARGB color value in XML: %f,%f,%f,%f", color->r, color->g, color->b, color->a);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ load_orientation - parse an orientation XML
+ node
+-------------------------------------------------*/
+
+static int load_orientation(xml_data_node *orientnode, int *orientation)
+{
+ int rotate;
+
+ /* skip if nothing */
+ if (orientnode == NULL)
+ {
+ *orientation = ROT0;
+ return 0;
+ }
+
+ /* parse out the data */
+ rotate = xml_get_attribute_int_with_subst(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:
+ fatalerror("Invalid rotation in XML orientation node: %d", rotate);
+ return 1;
+ }
+ if (strcmp("yes", xml_get_attribute_string_with_subst(orientnode, "swapxy", "no")) == 0)
+ *orientation ^= ORIENTATION_SWAP_XY;
+ if (strcmp("yes", xml_get_attribute_string_with_subst(orientnode, "flipx", "no")) == 0)
+ *orientation ^= ORIENTATION_FLIP_X;
+ if (strcmp("yes", xml_get_attribute_string_with_subst(orientnode, "flipy", "no")) == 0)
+ *orientation ^= ORIENTATION_FLIP_Y;
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ layout_file_free - free memory for a
+ layout_file and all of its subelements
+-------------------------------------------------*/
+
+void layout_file_free(layout_file *file)
+{
+ /* free each element in the list */
+ while (file->elemlist != NULL)
+ {
+ layout_element *temp = file->elemlist;
+ file->elemlist = temp->next;
+ layout_element_free(temp);
+ }
+
+ /* free each layout */
+ while (file->viewlist != NULL)
+ {
+ layout_view *temp = file->viewlist;
+ file->viewlist = temp->next;
+ layout_view_free(temp);
+ }
+
+ /* free the file itself */
+ free(file);
+}
+
+
+/*-------------------------------------------------
+ layout_view_free - free memory for a
+ layout_view and all of its subelements
+-------------------------------------------------*/
+
+static void layout_view_free(layout_view *view)
+{
+ int layer;
+
+ /* for each layer, free each item in that layer */
+ for (layer = 0; layer < ITEM_LAYER_MAX; layer++)
+ while (view->itemlist[layer] != NULL)
+ {
+ view_item *temp = view->itemlist[layer];
+ view->itemlist[layer] = temp->next;
+ if (temp->name != NULL)
+ free((void *)temp->name);
+ free(temp);
+ }
+
+ /* free the view itself */
+ if (view->name != NULL)
+ free((void *)view->name);
+ free(view);
+}
+
+
+/*-------------------------------------------------
+ layout_element_free - free memory for a
+ layout_element and its components
+-------------------------------------------------*/
+
+static void layout_element_free(layout_element *element)
+{
+ /* free all allocated components */
+ while (element->complist != NULL)
+ {
+ element_component *temp = element->complist;
+ element->complist = temp->next;
+ if (temp->string != NULL)
+ free((void *)temp->string);
+ if (temp->dirname != NULL)
+ free((void *)temp->dirname);
+ if (temp->imagefile != NULL)
+ free((void *)temp->imagefile);
+ if (temp->alphafile != NULL)
+ free((void *)temp->alphafile);
+ if (temp->bitmap != NULL)
+ bitmap_free(temp->bitmap);
+ free(temp);
+ }
+
+ /* free all textures */
+ if (element->elemtex != NULL)
+ {
+ int state;
+
+ /* loop over all states and free their textures */
+ for (state = 0; state <= element->maxstate; state++)
+ if (element->elemtex[state].texture != NULL)
+ render_texture_free(element->elemtex[state].texture);
+
+ free(element->elemtex);
+ }
+
+ /* free the element itself */
+ if (element->name != NULL)
+ free((void *)element->name);
+ free(element);
+}