// 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 #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" // SVG screen layouts #include "svg.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 int scrnum = 0; for (const screen_device &device : screen_device_iterator(machine.root_device())) { // 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); } // keep count scrnum++; } // 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, nullptr); static char buffer[1000]; // if nothing, just return the default if (str == nullptr) return defvalue; // if no tildes, don't worry if (strchr(str, '~') == nullptr) 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, nullptr); int value; unsigned int uvalue; if (string == nullptr) 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, nullptr); float value; if (string == nullptr || 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 == nullptr) { bounds.x0 = bounds.y0 = 0.0f; bounds.x1 = bounds.y1 = 1.0f; return; } // parse out the data if (xml_get_attribute(boundsnode, "left") != nullptr) { // 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") != nullptr) { // 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 == nullptr) { 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 == nullptr) { 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(nullptr), m_machine(machine), m_defstate(0), m_maxstate(0) { // extract the name const char *name = xml_get_attribute_string_with_subst(machine, elemnode, "name", nullptr); if (name == nullptr) 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 != nullptr; 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.empty()) { // 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) { 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 == nullptr) { 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) 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(nullptr), m_texture(nullptr), m_state(0) { } //------------------------------------------------- // ~texture - destructor //------------------------------------------------- layout_element::texture::~texture() { if (m_element != nullptr) 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(nullptr), m_type(CTYPE_INVALID), m_state(0) { for (auto & elem : m_hasalpha) elem = 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 != nullptr) 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(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 nullptr if we're coming from internal layout, and our string assignment doesn't like that if (dirname != nullptr) m_dirname = dirname; for (int i=0;i(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 const char *origs = m_string.c_str(); const char *ends = origs + strlen(origs); const char *s = origs; unicode_char schar; // loop over characters while (*s != 0) { int scharcount = uchar_from_utf8(&schar, s, ends - s); if (scharcount == -1) break; // get the font bitmap rectangle chbounds; font->get_scaled_bitmap_and_bounds(tempbitmap, bounds.height(), aspect, schar, 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, schar); s += scharcount; } // 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 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()); const char *origs = m_stopnames[fruit].c_str(); const char *ends = origs + strlen(origs); const char *s = origs; unicode_char schar; // loop over characters while (*s != 0) { int scharcount = uchar_from_utf8(&schar, s, ends - s); if (scharcount == -1) break; // get the font bitmap rectangle chbounds; font->get_scaled_bitmap_and_bounds(tempbitmap, ourheight/num_shown, aspect, schar, 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, schar); s += scharcount; } } } 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 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()); const char *origs =m_stopnames[fruit].c_str(); const char *ends = origs + strlen(origs); const char *s = origs; unicode_char schar; // loop over characters while (*s != 0) { int scharcount = uchar_from_utf8(&schar, s, ends - s); if (scharcount == -1) break; // get the font bitmap rectangle chbounds; font->get_scaled_bitmap_and_bounds(tempbitmap, dest.height(), aspect, schar, 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, schar); s += scharcount; } } } currx += ourwidth/num_shown; } // free the temporary bitmap and font machine.render().font_free(font); } //------------------------------------------------- // load_bitmap - load a PNG/JPG file with artwork for // a component //------------------------------------------------- void layout_element::component::load_bitmap() { // load the basic bitmap assert(m_file[0] != nullptr); 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); // PNG failed, let's try JPG if (!m_bitmap[0].valid()) render_load_jpeg(m_bitmap[0], *m_file[0], m_dirname.c_str(), m_imagefile[0].c_str()); // 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 != nullptr); /*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 &elemlist) : m_next(nullptr), 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 != nullptr) 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 != nullptr; 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 != nullptr; 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 != nullptr; 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 != nullptr; 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 != nullptr; 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 != nullptr; 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() { } //------------------------------------------------- // items - return the appropriate list //------------------------------------------------- const simple_list &layout_view::items(item_layer layer) const { static simple_list s_null_list; switch (layer) { case ITEM_LAYER_BACKDROP: return m_backdrop_list; case ITEM_LAYER_SCREEN: return m_screen_list; case ITEM_LAYER_OVERLAY: return m_overlay_list; case ITEM_LAYER_BEZEL: return m_bezel_list; case ITEM_LAYER_CPANEL: return m_cpanel_list; case ITEM_LAYER_MARQUEE: return m_marquee_list; default: return s_null_list; } } //------------------------------------------------- // 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 : items(layer)) { // 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 != nullptr) { 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 : items(layer)) { 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 : items(layer)) { curitem.resolve_tags(); } } } //************************************************************************** // LAYOUT VIEW ITEM //************************************************************************** //------------------------------------------------- // item - constructor //------------------------------------------------- layout_view::item::item(running_machine &machine, xml_data_node &itemnode, simple_list &elemlist) : m_next(nullptr), m_element(nullptr), m_input_port(nullptr), m_input_mask(0), m_screen(nullptr), 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", nullptr); if (name != nullptr) { // search the list of elements for a match for (layout_element &elem : elemlist) if (strcmp(name, elem.name()) == 0) { m_element = &elem; break; } // error if not found if (m_element == nullptr) 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) m_screen = screen_device_iterator(machine.root_device()).byindex(index); m_input_mask = xml_get_attribute_int_with_subst(machine, itemnode, "inputmask", 0); if (m_output_name[0] != 0 && m_element != nullptr) machine.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 == nullptr) throw emu_fatalerror("Layout references invalid screen index %d", index); } else { if (m_element == nullptr) 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 != nullptr) ? &m_screen->container() : nullptr; } //------------------------------------------------- // state - fetch state based on configured source //------------------------------------------------- int layout_view::item::state() const { int state = 0; assert(m_element != nullptr); // if configured to an output, fetch the output value if (m_output_name[0] != 0) state = m_element->machine().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 != nullptr) { ioport_field *field = m_input_port->field(m_input_mask); if (field != nullptr) 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(nullptr) { // find the layout node xml_data_node *mamelayoutnode = xml_get_sibling(rootnode.child, "mamelayout"); if (mamelayoutnode == nullptr) 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 != nullptr; 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 != nullptr; 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() { }