/*************************************************************************** tilemap.c Generic tilemap management system. Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. Visit http://mamedev.org for licensing and usage restrictions. ***************************************************************************/ #include "driver.h" #include "tilemap.h" #include "profiler.h" /*************************************************************************** CONSTANTS ***************************************************************************/ /* internal usage to mark tiles dirty */ #define TILE_FLAG_DIRTY 0xff /* invalid logical index */ #define INVALID_LOGICAL_INDEX ((tilemap_logical_index)~0) /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ /* logical index */ typedef UINT32 tilemap_logical_index; /* internal set of transparency states for rendering */ typedef enum { WHOLLY_TRANSPARENT, WHOLLY_OPAQUE, MASKED } trans_t; /* internal blitting callbacks */ typedef void (*blitmask_t)(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode); typedef void (*blitopaque_t)(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode); /* blitting parameters for rendering */ typedef struct _blit_parameters blit_parameters; struct _blit_parameters { mame_bitmap * bitmap; rectangle cliprect; blitmask_t draw_masked; blitopaque_t draw_opaque; UINT32 tilemap_priority_code; UINT8 mask; UINT8 value; }; /* core tilemap structure */ struct _tilemap { tilemap * next; /* pointer to next tilemap */ /* basic tilemap metrics */ tilemap_type type; /* type of tilemap */ UINT32 rows; /* number of tile rows */ UINT32 cols; /* number of tile columns */ UINT32 tilewidth; /* width of a single tile in pixels */ UINT32 tileheight; /* height of a single tile in pixels */ UINT32 width; /* width of the full tilemap in pixels */ UINT32 height; /* height of the full tilemap in pixels */ /* logical <-> memory mappings */ tilemap_mapper_callback mapper; /* callback to map a row/column to a memory index */ tilemap_logical_index * memory_to_logical; /* map from memory index to logical index */ tilemap_logical_index max_logical_index; /* maximum valid logical index */ tilemap_memory_index * logical_to_memory; /* map from logical index to memory index */ tilemap_memory_index max_memory_index; /* maximum valid memory index */ /* callback to interpret video RAM for the tilemap */ tile_get_info_callback tile_get_info; /* callback to get information about a tile */ tile_data tileinfo; /* structure to hold the data for a tile */ void * user_data; /* user data value passed to the callback */ /* global tilemap states */ UINT8 enable; /* true if we are enabled */ UINT8 attributes; /* global attributes (flipx/y) */ UINT8 all_tiles_dirty; /* true if all tiles are dirty */ UINT8 all_tiles_clean; /* true if all tiles are clean */ UINT32 palette_offset; /* palette offset */ /* scroll information */ UINT32 scrollrows; /* number of independently scrolled rows */ UINT32 scrollcols; /* number of independently scrolled colums */ INT32 * rowscroll; /* array of rowscroll values */ INT32 * colscroll; /* array of colscroll values */ INT32 dx; /* global horizontal scroll offset */ INT32 dx_flipped; /* global horizontal scroll offset when flipped */ INT32 dy; /* global vertical scroll offset */ INT32 dy_flipped; /* global vertical scroll offset when flipped */ /* pixel data */ mame_bitmap * pixmap; /* cached pixel data */ /* transparency mapping */ mame_bitmap * flagsmap; /* per-pixel flags */ UINT8 * tileflags; /* per-tile flags */ UINT8 * pen_to_flags; /* mapping of pens to flags */ UINT32 max_pen_to_flags; /* maximum index in each array */ }; /*************************************************************************** GLOBAL VARIABLES ***************************************************************************/ mame_bitmap * priority_bitmap; static tilemap * tilemap_list; static tilemap ** tilemap_tailptr; static int tilemap_instance; static UINT32 screen_width, screen_height; /*************************************************************************** FUNCTION PROTOTYPES ***************************************************************************/ /* system management helpers */ static void tilemap_exit(running_machine *machine); static void tilemap_postload(void *param); static void tilemap_dispose(tilemap *tmap); /* logical <-> memory index mapping */ static void mappings_create(tilemap *tmap); static void mappings_update(tilemap *tmap); /* tile rendering */ static void pixmap_update(tilemap *tmap, const rectangle *cliprect); static void tile_update(tilemap *tmap, tilemap_logical_index logindex, UINT32 cached_col, UINT32 cached_row); static UINT8 tile_draw(tilemap *tmap, const UINT8 *pendata, UINT32 x0, UINT32 y0, UINT32 palette_base, UINT8 category, UINT8 group, UINT8 flags); static UINT8 tile_draw_colortable(tilemap *tmap, const UINT8 *pendata, UINT32 x0, UINT32 y0, UINT32 palette_base, UINT8 category, UINT8 group, UINT8 flags); static UINT8 tile_draw_colortrans(tilemap *tmap, const UINT8 *pendata, UINT32 x0, UINT32 y0, UINT32 palette_base, UINT8 category, UINT8 group, UINT8 flags); static UINT8 tile_apply_bitmask(tilemap *tmap, const UINT8 *maskdata, UINT32 x0, UINT32 y0, UINT8 category, UINT8 flags); /* drawing helpers */ static void configure_blit_parameters(blit_parameters *blit, tilemap *tmap, mame_bitmap *dest, const rectangle *cliprect, UINT32 flags, UINT8 priority, UINT8 priority_mask); static void tilemap_draw_instance(tilemap *tmap, const blit_parameters *blit, int xpos, int ypos); static void tilemap_draw_roz_core(tilemap *tmap, const blit_parameters *blit, UINT32 startx, UINT32 starty, int incxx, int incxy, int incyx, int incyy, int wraparound); /* scanline rasterizers for drawing to the pixmap */ static void scanline_draw_opaque_null(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_masked_null(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_opaque_ind16(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_masked_ind16(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_opaque_rgb16(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_masked_rgb16(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_opaque_rgb16_alpha(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_masked_rgb16_alpha(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_opaque_rgb32(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_masked_rgb32(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_opaque_rgb32_alpha(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode); static void scanline_draw_masked_rgb32_alpha(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode); /*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ /*------------------------------------------------- effective_rowscroll - return the effective rowscroll value for a given index, taking into account tilemap flip states -------------------------------------------------*/ INLINE INT32 effective_rowscroll(tilemap *tmap, int index) { INT32 value; /* if we're flipping vertically, adjust the row number */ if (tmap->attributes & TILEMAP_FLIPY) index = tmap->scrollrows - 1 - index; /* adjust final result based on the horizontal flip and dx values */ if (!(tmap->attributes & TILEMAP_FLIPX)) value = tmap->dx - tmap->rowscroll[index]; else value = screen_width - tmap->width - (tmap->dx_flipped - tmap->rowscroll[index]); /* clamp to 0..width */ if (value < 0) value = tmap->width - (-value) % tmap->width; else value %= tmap->width; return value; } /*------------------------------------------------- effective_colscroll - return the effective colscroll value for a given index, taking into account tilemap flip states -------------------------------------------------*/ INLINE INT32 effective_colscroll(tilemap *tmap, int index) { INT32 value; /* if we're flipping horizontally, adjust the column number */ if (tmap->attributes & TILEMAP_FLIPX) index = tmap->scrollcols - 1 - index; /* adjust final result based on the vertical flip and dx values */ if (!(tmap->attributes & TILEMAP_FLIPY)) value = tmap->dy - tmap->colscroll[index]; else value = screen_height - tmap->height - (tmap->dy_flipped - tmap->colscroll[index]); /* clamp to 0..height */ if (value < 0) value = tmap->height - (-value) % tmap->height; else value %= tmap->height; return value; } /*------------------------------------------------- indexed_tilemap - return a tilemap by index -------------------------------------------------*/ INLINE tilemap *indexed_tilemap(int index) { tilemap *tmap; /* find by the tilemap index */ for (tmap = tilemap_list; tmap != NULL; tmap = tmap->next) if (index-- == 0) return tmap; return NULL; } /*************************************************************************** SYSTEM-WIDE MANAGEMENT ***************************************************************************/ /*------------------------------------------------- tilemap_init - initialize the tilemap system -------------------------------------------------*/ void tilemap_init(running_machine *machine) { screen_width = Machine->screen[0].width; screen_height = Machine->screen[0].height; tilemap_list = NULL; tilemap_tailptr = &tilemap_list; tilemap_instance = 0; priority_bitmap = auto_bitmap_alloc(screen_width, screen_height, BITMAP_FORMAT_INDEXED8); add_exit_callback(machine, tilemap_exit); } /*************************************************************************** TILEMAP CREATION AND CONFIGURATION ***************************************************************************/ /*------------------------------------------------- tilemap_create - allocate a new tilemap -------------------------------------------------*/ tilemap *tilemap_create(tile_get_info_callback tile_get_info, tilemap_mapper_callback mapper, tilemap_type type, int tilewidth, int tileheight, int cols, int rows) { tilemap *tmap; int group; /* allocate the tilemap itself */ tmap = malloc_or_die(sizeof(tilemap)); memset(tmap, 0, sizeof(tilemap)); /* fill in the basic metrics */ tmap->type = type; tmap->rows = rows; tmap->cols = cols; tmap->tilewidth = tilewidth; tmap->tileheight = tileheight; tmap->width = cols * tilewidth; tmap->height = rows * tileheight; /* set up the logical <-> memory mappings */ tmap->mapper = mapper; mappings_create(tmap); /* set up the tile map callbacks */ tmap->tile_get_info = tile_get_info; /* initialize global states */ tmap->enable = TRUE; tmap->all_tiles_dirty = TRUE; /* initialize scroll information */ tmap->scrollrows = 1; tmap->scrollcols = 1; tmap->rowscroll = malloc_or_die(tmap->height * sizeof(*tmap->rowscroll)); memset(tmap->rowscroll, 0, tmap->height * sizeof(*tmap->rowscroll)); tmap->colscroll = malloc_or_die(tmap->width * sizeof(*tmap->colscroll)); memset(tmap->colscroll, 0, tmap->width * sizeof(*tmap->colscroll)); /* allocate the pixel data cache */ tmap->pixmap = bitmap_alloc(tmap->width, tmap->height, BITMAP_FORMAT_INDEXED16); /* allocate transparency mapping data */ tmap->tileflags = malloc_or_die(tmap->max_logical_index); tmap->flagsmap = bitmap_alloc(tmap->width, tmap->height, BITMAP_FORMAT_INDEXED8); tmap->max_pen_to_flags = (tmap->type != TILEMAP_TYPE_COLORTABLE) ? 256 : Machine->drv->color_table_len; tmap->pen_to_flags = malloc_or_die(sizeof(tmap->pen_to_flags[0]) * tmap->max_pen_to_flags * TILEMAP_NUM_GROUPS); for (group = 0; group < TILEMAP_NUM_GROUPS; group++) tilemap_map_pens_to_layer(tmap, group, 0, 0, TILEMAP_PIXEL_LAYER0); /* add us to the end of the list of tilemaps */ *tilemap_tailptr = tmap; tilemap_tailptr = &tmap->next; /* save relevant state */ state_save_register_item("tilemap", tilemap_instance, tmap->enable); state_save_register_item("tilemap", tilemap_instance, tmap->attributes); state_save_register_item("tilemap", tilemap_instance, tmap->palette_offset); state_save_register_item("tilemap", tilemap_instance, tmap->scrollrows); state_save_register_item("tilemap", tilemap_instance, tmap->scrollcols); state_save_register_item_pointer("tilemap", tilemap_instance, tmap->rowscroll, rows * tileheight); state_save_register_item_pointer("tilemap", tilemap_instance, tmap->colscroll, cols * tilewidth); state_save_register_item("tilemap", tilemap_instance, tmap->dx); state_save_register_item("tilemap", tilemap_instance, tmap->dx_flipped); state_save_register_item("tilemap", tilemap_instance, tmap->dy); state_save_register_item("tilemap", tilemap_instance, tmap->dy_flipped); tilemap_instance++; /* reset everything after a load */ state_save_register_func_postload_ptr(tilemap_postload, tmap); return tmap; } /*------------------------------------------------- tilemap_set_user_data - specify a parameter to be passed into the tile_get_info callback -------------------------------------------------*/ void tilemap_set_user_data(tilemap *tmap, void *user_data) { tmap->user_data = user_data; } /*------------------------------------------------- tilemap_set_palette_offset - specify an offset to be added to each pixel before looking up the palette -------------------------------------------------*/ void tilemap_set_palette_offset(tilemap *tmap, UINT32 offset) { if (tmap->palette_offset != offset) { tmap->palette_offset = offset; tilemap_mark_all_tiles_dirty(tmap); } } /*------------------------------------------------- tilemap_set_enable - set an enable flag for the tilemap; if 0, requests to draw the tilemap are ignored -------------------------------------------------*/ void tilemap_set_enable(tilemap *tmap, int enable) { tmap->enable = (enable != 0); } /*------------------------------------------------- tilemap_set_flip - set a global flip for the tilemap; ALL_TILEMAPS can be passed here as well -------------------------------------------------*/ void tilemap_set_flip(tilemap *tmap, UINT32 attributes) { /* handle ALL_TILEMAPS */ if (tmap == ALL_TILEMAPS) { for (tmap = tilemap_list; tmap != NULL; tmap = tmap->next) tilemap_set_flip(tmap, attributes); } /* if we're changing things, force a refresh of the mappings and mark it all dirty */ else if (tmap->attributes != attributes) { tmap->attributes = attributes; mappings_update(tmap); } } /*************************************************************************** DIRTY TILE MARKING ***************************************************************************/ /*------------------------------------------------- tilemap_mark_tile_dirty - mark a single tile dirty based on its memory index -------------------------------------------------*/ void tilemap_mark_tile_dirty(tilemap *tmap, tilemap_memory_index memindex) { /* only mark if within range */ if (memindex < tmap->max_memory_index) { tilemap_logical_index logindex = tmap->memory_to_logical[memindex]; /* there may be no logical index for a given memory index */ if (logindex != INVALID_LOGICAL_INDEX) { tmap->tileflags[logindex] = TILE_FLAG_DIRTY; tmap->all_tiles_clean = FALSE; } } } /*------------------------------------------------- tilemap_mark_all_tiles_dirty - mark all the tiles in a tilemap dirty -------------------------------------------------*/ void tilemap_mark_all_tiles_dirty(tilemap *tmap) { /* handle ALL_TILEMAPS */ if (tmap == ALL_TILEMAPS) { for (tmap = tilemap_list; tmap != NULL; tmap = tmap->next) tilemap_mark_all_tiles_dirty(tmap); } /* mark all tiles dirty and clear the clean flag */ else { tmap->all_tiles_dirty = TRUE; tmap->all_tiles_clean = FALSE; } } /*************************************************************************** PEN-TO-LAYER MAPPING ***************************************************************************/ /*------------------------------------------------- tilemap_map_pens_to_layer - specify the mapping of one or more pens (where ( & mask) == pen) to a layer -------------------------------------------------*/ void tilemap_map_pens_to_layer(tilemap *tmap, int group, pen_t pen, pen_t mask, UINT8 layermask) { UINT8 *array = tmap->pen_to_flags + group * tmap->max_pen_to_flags; pen_t start, stop, cur; UINT8 changed = FALSE; assert(group < TILEMAP_NUM_GROUPS); assert((layermask & TILEMAP_PIXEL_CATEGORY_MASK) == 0); /* we start at the index where (pen & mask) == pen, and all other bits are 0 */ start = pen & mask; /* we stop at the index where (pen & mask) == pen, and all other bits are 1 */ stop = start | ~mask; /* clamp to the number of entries actually there */ stop = MIN(stop, tmap->max_pen_to_flags - 1); /* iterate and set */ for (cur = start; cur <= stop; cur++) if ((cur & mask) == pen && array[cur] != layermask) { changed = TRUE; array[cur] = layermask; } /* everything gets dirty if anything changed */ if (changed) tilemap_mark_all_tiles_dirty(tmap); } /*------------------------------------------------- tilemap_set_transparent_pen - set a single transparent pen into the tilemap, mapping all other pens to layer 0 -------------------------------------------------*/ void tilemap_set_transparent_pen(tilemap *tmap, pen_t pen) { /* reset the whole pen map to opaque */ tilemap_map_pens_to_layer(tmap, 0, 0, 0, TILEMAP_PIXEL_LAYER0); /* set the single pen to transparent */ tilemap_map_pen_to_layer(tmap, 0, pen, TILEMAP_PIXEL_TRANSPARENT); } /*------------------------------------------------- tilemap_set_transmask - set up the first 32 pens using a foreground mask (mapping to layer 0) and a background mask (mapping to layer 1) -------------------------------------------------*/ void tilemap_set_transmask(tilemap *tmap, int group, UINT32 fgmask, UINT32 bgmask) { pen_t pen; /* iterate over all 32 pens specified */ for (pen = 0; pen < 32; pen++) { UINT8 fgbits = ((fgmask >> pen) & 1) ? TILEMAP_PIXEL_TRANSPARENT : TILEMAP_PIXEL_LAYER0; UINT8 bgbits = ((bgmask >> pen) & 1) ? TILEMAP_PIXEL_TRANSPARENT : TILEMAP_PIXEL_LAYER1; tilemap_map_pen_to_layer(tmap, group, pen, fgbits | bgbits); } } /*************************************************************************** TILEMAP SCROLLING ***************************************************************************/ /*------------------------------------------------- tilemap_set_scroll_rows - specify the number of independently scrollable row units; each unit covers height/scroll_rows pixels -------------------------------------------------*/ void tilemap_set_scroll_rows(tilemap *tmap, UINT32 scroll_rows) { assert(scroll_rows <= tmap->height); tmap->scrollrows = scroll_rows; } /*------------------------------------------------- tilemap_set_scroll_cols - specify the number of independently scrollable column units; each unit covers width/scroll_cols pixels -------------------------------------------------*/ void tilemap_set_scroll_cols(tilemap *tmap, UINT32 scroll_cols) { assert(scroll_cols <= tmap->width); tmap->scrollcols = scroll_cols; } /*------------------------------------------------- tilemap_set_scrolldx - specify global horizontal scroll offset, for non-flipped and flipped cases -------------------------------------------------*/ void tilemap_set_scrolldx(tilemap *tmap, int dx, int dx_flipped) { tmap->dx = dx; tmap->dx_flipped = dx_flipped; } /*------------------------------------------------- tilemap_set_scrolldy - specify global vertical scroll offset, for non-flipped and flipped cases -------------------------------------------------*/ void tilemap_set_scrolldy(tilemap *tmap, int dy, int dy_flipped) { tmap->dy = dy; tmap->dy_flipped = dy_flipped; } /*------------------------------------------------- tilemap_get_scrolldx - return the global horizontal scroll offset, based on current flip state -------------------------------------------------*/ int tilemap_get_scrolldx(tilemap *tmap) { return (tmap->attributes & TILEMAP_FLIPX) ? tmap->dx_flipped : tmap->dx; } /*------------------------------------------------- tilemap_get_scrolldy - return the global vertical scroll offset, based on current flip state -------------------------------------------------*/ int tilemap_get_scrolldy(tilemap *tmap) { return (tmap->attributes & TILEMAP_FLIPY) ? tmap->dy_flipped : tmap->dy; } /*------------------------------------------------- tilemap_set_scrollx - specify the scroll value for a row unit -------------------------------------------------*/ void tilemap_set_scrollx(tilemap *tmap, int which, int value) { if (which < tmap->scrollrows) tmap->rowscroll[which] = value; } /*------------------------------------------------- tilemap_set_scrolly - specify the scroll value for a column unit -------------------------------------------------*/ void tilemap_set_scrolly(tilemap *tmap, int which, int value) { if (which < tmap->scrollcols) tmap->colscroll[which] = value; } /*************************************************************************** INTERNAL MAP ACCESS ***************************************************************************/ /*------------------------------------------------- tilemap_get_pixmap - return a pointer to the (updated) internal pixmap for a tilemap -------------------------------------------------*/ mame_bitmap *tilemap_get_pixmap(tilemap *tmap) { /* ensure all the tiles are up-to-date and then return the pixmap */ pixmap_update(tmap, NULL); return tmap->pixmap; } /*------------------------------------------------- tilemap_get_flagsmap - return a pointer to the (updated) internal flagsmap for a tilemap -------------------------------------------------*/ mame_bitmap *tilemap_get_flagsmap(tilemap *tmap) { /* ensure all the tiles are up-to-date and then return the pixmap */ pixmap_update(tmap, NULL); return tmap->flagsmap; } /*------------------------------------------------- tilemap_get_pixmap - return a pointer to the (updated) internal per-tile flags for a tilemap -------------------------------------------------*/ UINT8 *tilemap_get_tile_flags(tilemap *tmap) { /* ensure all the tiles are up-to-date and then return the pixmap */ pixmap_update(tmap, NULL); return tmap->tileflags; } /*************************************************************************** TILEMAP RENDERING ***************************************************************************/ /*------------------------------------------------- tilemap_draw_primask - draw a tilemap to the destination with clipping; pixels apply priority/priority_mask to the priority bitmap -------------------------------------------------*/ void tilemap_draw_primask(mame_bitmap *dest, const rectangle *cliprect, tilemap *tmap, UINT32 flags, UINT8 priority, UINT8 priority_mask) { rectangle original_cliprect; blit_parameters blit; int xpos, ypos; /* skip if disabled */ if (!tmap->enable) return; profiler_mark(PROFILER_TILEMAP_DRAW); /* configure the blit parameters based on the input parameters */ configure_blit_parameters(&blit, tmap, dest, cliprect, flags, priority, priority_mask); original_cliprect = blit.cliprect; /* if the whole map is dirty, mark it as such */ if (tmap->all_tiles_dirty) { memset(tmap->tileflags, TILE_FLAG_DIRTY, tmap->max_logical_index); tmap->all_tiles_dirty = FALSE; } /* XY scrolling playfield */ if (tmap->scrollrows == 1 && tmap->scrollcols == 1) { int scrollx = effective_rowscroll(tmap, 0); int scrolly = effective_colscroll(tmap, 0); /* iterate to handle wraparound */ for (ypos = scrolly - tmap->height; ypos <= blit.cliprect.max_y; ypos += tmap->height) for (xpos = scrollx - tmap->width; xpos <= blit.cliprect.max_x; xpos += tmap->width) tilemap_draw_instance(tmap, &blit, xpos, ypos); } /* scrolling rows + vertical scroll */ else if (tmap->scrollcols == 1) { const rectangle original_cliprect = blit.cliprect; int rowheight = tmap->height / tmap->scrollrows; int scrolly = effective_colscroll(tmap, 0); int currow, nextrow; /* iterate over rows in the tilemap */ for (currow = 0; currow < tmap->scrollrows; currow = nextrow) { int scrollx = effective_rowscroll(tmap, currow); /* scan forward until we find a non-matching row */ for (nextrow = currow + 1; nextrow < tmap->scrollrows; nextrow++) if (effective_rowscroll(tmap, nextrow) != scrollx) break; /* skip if disabled */ if (scrollx == TILE_LINE_DISABLED) continue; /* iterate over Y to handle wraparound */ for (ypos = scrolly - tmap->height; ypos <= original_cliprect.max_y; ypos += tmap->height) { /* update the cliprect just for this set of rows */ blit.cliprect.min_y = currow * rowheight + ypos; blit.cliprect.max_y = nextrow * rowheight - 1 + ypos; sect_rect(&blit.cliprect, &original_cliprect); /* iterate over X to handle wraparound */ for (xpos = scrollx - tmap->width; xpos <= original_cliprect.max_x; xpos += tmap->width) tilemap_draw_instance(tmap, &blit, xpos, ypos); } } } /* scrolling columns + horizontal scroll */ else if (tmap->scrollrows == 1) { const rectangle original_cliprect = blit.cliprect; int colwidth = tmap->width / tmap->scrollcols; int scrollx = effective_rowscroll(tmap, 0); int curcol, nextcol; /* iterate over columns in the tilemap */ for (curcol = 0; curcol < tmap->scrollcols; curcol = nextcol) { int scrolly = effective_colscroll(tmap, curcol); /* scan forward until we find a non-matching column */ for (nextcol = curcol + 1; nextcol < tmap->scrollcols; nextcol++) if (effective_colscroll(tmap, nextcol) != scrolly) break; /* skip if disabled */ if (scrolly == TILE_LINE_DISABLED) continue; /* iterate over X to handle wraparound */ for (xpos = scrollx - tmap->width; xpos <= original_cliprect.max_x; xpos += tmap->width) { /* update the cliprect just for this set of columns */ blit.cliprect.min_x = curcol * colwidth + xpos; blit.cliprect.max_x = nextcol * colwidth - 1 + xpos; sect_rect(&blit.cliprect, &original_cliprect); /* iterate over Y to handle wraparound */ for (ypos = scrolly - tmap->height; ypos <= original_cliprect.max_y; ypos += tmap->height) tilemap_draw_instance(tmap, &blit, xpos, ypos); } } } profiler_mark(PROFILER_END); } /*------------------------------------------------- tilemap_draw_primask - draw a tilemap to the destination with clipping and arbitrary rotate/zoom; pixels apply priority/ priority_mask to the priority bitmap -------------------------------------------------*/ void tilemap_draw_roz_primask(mame_bitmap *dest, const rectangle *cliprect, tilemap *tmap, UINT32 startx, UINT32 starty, int incxx, int incxy, int incyx, int incyy, int wraparound, UINT32 flags, UINT8 priority, UINT8 priority_mask) { blit_parameters blit; /* notes: - startx and starty MUST be UINT32 for calculations to work correctly - srcbitmap->width and height are assumed to be a power of 2 to speed up wraparound */ /* skip if disabled */ if (!tmap->enable) return; /* see if this is just a regular render and if so, do a regular render */ if (incxx == (1 << 16) && incxy == 0 && incyx == 0 && incyy == (1 << 16) && wraparound) { tilemap_set_scrollx(tmap, 0, startx >> 16); tilemap_set_scrolly(tmap, 0, starty >> 16); tilemap_draw(dest, cliprect, tmap, flags, priority); return; } profiler_mark(PROFILER_TILEMAP_DRAW_ROZ); /* configure the blit parameters */ configure_blit_parameters(&blit, tmap, dest, cliprect, flags, priority, priority_mask); /* get the full pixmap for the tilemap */ tilemap_get_pixmap(tmap); /* then do the roz copy */ tilemap_draw_roz_core(tmap, &blit, startx, starty, incxx, incxy, incyx, incyy, wraparound); profiler_mark(PROFILER_END); } /*************************************************************************** INDEXED TILEMAP HANDLING ***************************************************************************/ /*------------------------------------------------- tilemap_count - return the number of tilemaps -------------------------------------------------*/ int tilemap_count(void) { tilemap *tmap; int count = 0; /* find by the tilemap index */ for (tmap = tilemap_list; tmap != NULL; tmap = tmap->next) count++; return count; } /*------------------------------------------------- tilemap_size_by_index - return the size of an indexed tilemap -------------------------------------------------*/ void tilemap_size_by_index(int number, UINT32 *width, UINT32 *height) { tilemap *tmap = indexed_tilemap(number); *width = tmap->width; *height = tmap->height; } /*------------------------------------------------- tilemap_draw_by_index - render an indexed tilemap with fixed characteristics (no priority) -------------------------------------------------*/ void tilemap_draw_by_index(mame_bitmap *dest, int number, UINT32 scrollx, UINT32 scrolly) { tilemap *tmap = indexed_tilemap(number); blit_parameters blit; int xpos,ypos; /* set up for the blit, using hard-coded parameters (no priority, etc) */ configure_blit_parameters(&blit, tmap, dest, NULL, TILEMAP_DRAW_OPAQUE, 0, 0xff); /* compute the effective scroll positions */ scrollx = tmap->width - scrollx % tmap->width; scrolly = tmap->height - scrolly % tmap->height; /* if the whole map is dirty, mark it as such */ if (tmap->all_tiles_dirty) { memset(tmap->tileflags, TILE_FLAG_DIRTY, tmap->max_logical_index); tmap->all_tiles_dirty = FALSE; } /* iterate to handle wraparound */ for (ypos = scrolly - tmap->height; ypos <= blit.cliprect.max_y; ypos += tmap->height) for (xpos = scrollx - tmap->width; xpos <= blit.cliprect.max_x; xpos += tmap->width) tilemap_draw_instance(tmap, &blit, xpos, ypos); } /*************************************************************************** COMMON LOGICAL-TO-MEMORY MAPPERS ***************************************************************************/ /*------------------------------------------------- tilemap_scan_rows tilemap_scan_rows_flip_x tilemap_scan_rows_flip_y tilemap_scan_rows_flip_xy - scan in row-major order with optional flipping -------------------------------------------------*/ TILEMAP_MAPPER( tilemap_scan_rows ) { return row * num_cols + col; } TILEMAP_MAPPER( tilemap_scan_rows_flip_x ) { return row * num_cols + (num_cols - 1 - col); } TILEMAP_MAPPER( tilemap_scan_rows_flip_y ) { return (num_rows - 1 - row) * num_cols + col; } TILEMAP_MAPPER( tilemap_scan_rows_flip_xy ) { return (num_rows - 1 - row) * num_cols + (num_cols - 1 - col); } /*------------------------------------------------- tilemap_scan_cols tilemap_scan_cols_flip_x tilemap_scan_cols_flip_y tilemap_scan_cols_flip_xy - scan in column- major order with optional flipping -------------------------------------------------*/ TILEMAP_MAPPER( tilemap_scan_cols ) { return col * num_rows + row; } TILEMAP_MAPPER( tilemap_scan_cols_flip_x ) { return (num_cols - 1 - col) * num_rows + row; } TILEMAP_MAPPER( tilemap_scan_cols_flip_y ) { return col * num_rows + (num_rows - 1 - row); } TILEMAP_MAPPER( tilemap_scan_cols_flip_xy ) { return (num_cols - 1 - col) * num_rows + (num_rows - 1 - row); } /*************************************************************************** SYSTEM MANAGEMENT HELPERS ***************************************************************************/ /*------------------------------------------------- tilemap_exit - free memory allocated by the tilemap system -------------------------------------------------*/ static void tilemap_exit(running_machine *machine) { /* free all the tilemaps in the list */ while (tilemap_list != NULL) { tilemap *next = tilemap_list->next; tilemap_dispose(tilemap_list); tilemap_list = next; } tilemap_tailptr = NULL; } /*------------------------------------------------- tilemap_postload - after loading a save state invalidate everything -------------------------------------------------*/ static void tilemap_postload(void *param) { /* recompute the mappings for this tilemap */ tilemap *tmap = param; mappings_update(tmap); } /*------------------------------------------------- tilemap_dispose - dispose of a tilemap -------------------------------------------------*/ static void tilemap_dispose(tilemap *tmap) { tilemap **tmapptr; /* walk the list of tilemaps; when we find ourself, remove it */ for (tmapptr = &tilemap_list; *tmapptr != NULL; tmapptr = &(*tmapptr)->next) if (*tmapptr == tmap) { *tmapptr = tmap->next; break; } /* free allocated memory */ free(tmap->pen_to_flags); free(tmap->tileflags); bitmap_free(tmap->flagsmap); bitmap_free(tmap->pixmap); free(tmap->colscroll); free(tmap->rowscroll); free(tmap->logical_to_memory); free(tmap->memory_to_logical); free(tmap); } /*************************************************************************** LOGICAL <-> MEMORY INDEX MAPPING ***************************************************************************/ /*------------------------------------------------- mappings_create - allocate memory for the mapping tables and compute their extents -------------------------------------------------*/ static void mappings_create(tilemap *tmap) { UINT32 col, row; /* compute the maximum logical index */ tmap->max_logical_index = tmap->rows * tmap->cols; /* compute the maximum memory index */ tmap->max_memory_index = 0; for (row = 0; row < tmap->rows; row++) for (col = 0; col < tmap->cols; col++) { tilemap_memory_index memindex = (*tmap->mapper)(col, row, tmap->cols, tmap->rows); tmap->max_memory_index = MAX(tmap->max_memory_index, memindex); } tmap->max_memory_index++; /* allocate the necessary mappings */ tmap->memory_to_logical = malloc_or_die(tmap->max_memory_index * sizeof(*tmap->memory_to_logical)); tmap->logical_to_memory = malloc_or_die(tmap->max_logical_index * sizeof(*tmap->logical_to_memory)); /* update the mappings */ mappings_update(tmap); } /*------------------------------------------------- mappings_update - update the mappings after a major change (flip x/y changes) -------------------------------------------------*/ static void mappings_update(tilemap *tmap) { tilemap_logical_index logindex; tilemap_memory_index memindex; /* initialize all the mappings to invalid values */ for (memindex = 0; memindex < tmap->max_memory_index; memindex++) tmap->memory_to_logical[memindex] = -1; /* now iterate over all logical indexes and populate the memory index */ for (logindex = 0; logindex < tmap->max_logical_index; logindex++) { UINT32 logical_col = logindex % tmap->cols; UINT32 logical_row = logindex / tmap->cols; tilemap_memory_index memindex = (*tmap->mapper)(logical_col, logical_row, tmap->cols, tmap->rows); UINT32 flipped_logindex; /* apply tilemap flip to get the final location to store */ if (tmap->attributes & TILEMAP_FLIPX) logical_col = (tmap->cols - 1) - logical_col; if (tmap->attributes & TILEMAP_FLIPY) logical_row = (tmap->rows - 1) - logical_row; flipped_logindex = logical_row * tmap->cols + logical_col; /* fill in entries in both arrays */ tmap->memory_to_logical[memindex] = flipped_logindex; tmap->logical_to_memory[flipped_logindex] = memindex; } /* mark the whole tilemap dirty */ tilemap_mark_all_tiles_dirty(tmap); } /*************************************************************************** TILE RENDERING ***************************************************************************/ /*------------------------------------------------- pixmap_update - update the portion of the pixmap described by the cliprect -------------------------------------------------*/ static void pixmap_update(tilemap *tmap, const rectangle *cliprect) { int mincol, maxcol, minrow, maxrow; int row, col; /* if everything is clean, do nothing */ if (tmap->all_tiles_clean) return; profiler_mark(PROFILER_TILEMAP_DRAW); /* compute which columns and rows to update */ if (cliprect != NULL) { mincol = cliprect->min_x / tmap->tilewidth; maxcol = cliprect->max_x / tmap->tilewidth; minrow = cliprect->min_y / tmap->tileheight; maxrow = cliprect->max_y / tmap->tileheight; } else { mincol = minrow = 0; maxcol = tmap->cols - 1; maxrow = tmap->rows - 1; } /* if the whole map is dirty, mark it as such */ if (tmap->all_tiles_dirty) { memset(tmap->tileflags, TILE_FLAG_DIRTY, tmap->max_logical_index); tmap->all_tiles_dirty = FALSE; } /* iterate over rows */ for (row = minrow; row <= maxrow; row++) { tilemap_logical_index logindex = row * tmap->cols; /* iterate over colums */ for (col = mincol; col <= maxcol; col++) if (tmap->tileflags[logindex + col] == TILE_FLAG_DIRTY) tile_update(tmap, logindex + col, col, row); } /* mark it all clean */ if (mincol == 0 && minrow == 0 && maxcol == tmap->cols - 1 && maxcol == tmap->rows - 1) tmap->all_tiles_clean = TRUE; profiler_mark(PROFILER_END); } /*------------------------------------------------- tile_update - update a single dirty tile -------------------------------------------------*/ static void tile_update(tilemap *tmap, tilemap_logical_index logindex, UINT32 col, UINT32 row) { UINT32 x0 = tmap->tilewidth * col; UINT32 y0 = tmap->tileheight * row; tilemap_memory_index memindex; UINT32 flags; profiler_mark(PROFILER_TILEMAP_UPDATE); /* call the get info callback for the associated memory index */ memindex = tmap->logical_to_memory[logindex]; (*tmap->tile_get_info)(Machine, &tmap->tileinfo, memindex, tmap->user_data); /* apply the global tilemap flip to the returned flip flags */ flags = tmap->tileinfo.flags ^ (tmap->attributes & 0x03); /* draw the tile, using either direct or transparent */ if (Machine->game_colortable != NULL) { if (tmap->type != TILEMAP_TYPE_COLORTABLE) tmap->tileflags[logindex] = tile_draw_colortable(tmap, tmap->tileinfo.pen_data, x0, y0, tmap->tileinfo.palette_base, tmap->tileinfo.category, tmap->tileinfo.group, flags); else tmap->tileflags[logindex] = tile_draw_colortrans(tmap, tmap->tileinfo.pen_data, x0, y0, tmap->tileinfo.palette_base, tmap->tileinfo.category, tmap->tileinfo.group, flags); } else tmap->tileflags[logindex] = tile_draw(tmap, tmap->tileinfo.pen_data, x0, y0, tmap->tileinfo.palette_base, tmap->tileinfo.category, tmap->tileinfo.group, flags); /* if mask data is specified, apply it */ if ((flags & (TILE_FORCE_LAYER0 | TILE_FORCE_LAYER1 | TILE_FORCE_LAYER2)) == 0 && tmap->tileinfo.mask_data != NULL) tmap->tileflags[logindex] = tile_apply_bitmask(tmap, tmap->tileinfo.mask_data, x0, y0, tmap->tileinfo.category, flags); profiler_mark(PROFILER_END); } /*------------------------------------------------- tile_draw - draw a single tile to the tilemap's internal pixmap, using the pen as the pen_to_flags lookup value, and adding the palette_base -------------------------------------------------*/ static UINT8 tile_draw(tilemap *tmap, const UINT8 *pendata, UINT32 x0, UINT32 y0, UINT32 palette_base, UINT8 category, UINT8 group, UINT8 flags) { const UINT8 *penmap = tmap->pen_to_flags + group * tmap->max_pen_to_flags; mame_bitmap *flagsmap = tmap->flagsmap; mame_bitmap *pixmap = tmap->pixmap; int height = tmap->tileheight; int width = tmap->tilewidth; UINT8 andmask = ~0, ormask = 0; int dx0 = 1, dy0 = 1; int tx, ty; /* OR in the force layer flags */ category |= flags & (TILE_FORCE_LAYER0 | TILE_FORCE_LAYER1 | TILE_FORCE_LAYER2); /* if we're vertically flipped, point to the bottom row and work backwards */ if (flags & TILE_FLIPY) { y0 += height - 1; dy0 = -1; } /* if we're horizontally flipped, point to the rightmost column and work backwards */ if (flags & TILE_FLIPX) { x0 += width - 1; dx0 = -1; } /* in 4bpp mode, we draw in groups of 2 pixels, so halve the width now */ if (flags & TILE_4BPP) { assert(width % 2 == 0); width /= 2; } /* iterate over rows */ for (ty = 0; ty < height; ty++) { UINT16 *pixptr = BITMAP_ADDR16(pixmap, y0, x0); UINT8 *flagsptr = BITMAP_ADDR8(flagsmap, y0, x0); int xoffs = 0; /* pre-advance to the next row */ y0 += dy0; /* 8bpp data */ if (!(flags & TILE_4BPP)) { for (tx = 0; tx < width; tx++) { UINT8 pen = *pendata++; UINT8 map = penmap[pen]; pixptr[xoffs] = palette_base + pen; flagsptr[xoffs] = map | category; andmask &= map; ormask |= map; xoffs += dx0; } } /* 4bpp data */ else { for (tx = 0; tx < width; tx++) { UINT8 data = *pendata++; UINT8 pen, map; pen = data & 0x0f; map = penmap[pen]; pixptr[xoffs] = palette_base + pen; flagsptr[xoffs] = map | category; andmask &= map; ormask |= map; xoffs += dx0; pen = data >> 4; map = penmap[pen]; pixptr[xoffs] = palette_base + pen; flagsptr[xoffs] = map | category; andmask &= map; ormask |= map; xoffs += dx0; } } } return andmask ^ ormask; } /*------------------------------------------------- tile_draw_colortable - draw a single tile to the tilemap's internal pixmap, using the pen as the pen_to_flags lookup value, and using the machine's remapped_colortable -------------------------------------------------*/ static UINT8 tile_draw_colortable(tilemap *tmap, const UINT8 *pendata, UINT32 x0, UINT32 y0, UINT32 palette_base, UINT8 category, UINT8 group, UINT8 flags) { const UINT8 *penmap = tmap->pen_to_flags + group * tmap->max_pen_to_flags; const pen_t *palette_lookup = Machine->remapped_colortable + palette_base; mame_bitmap *flagsmap = tmap->flagsmap; mame_bitmap *pixmap = tmap->pixmap; int height = tmap->tileheight; int width = tmap->tilewidth; UINT8 andmask = ~0, ormask = 0; int dx0 = 1, dy0 = 1; int tx, ty; /* OR in the force layer flags */ category |= flags & (TILE_FORCE_LAYER0 | TILE_FORCE_LAYER1 | TILE_FORCE_LAYER2); /* if we're vertically flipped, point to the bottom row and work backwards */ if (flags & TILE_FLIPY) { y0 += height - 1; dy0 = -1; } /* if we're horizontally flipped, point to the rightmost column and work backwards */ if (flags & TILE_FLIPX) { x0 += width - 1; dx0 = -1; } /* in 4bpp mode, we draw in groups of 2 pixels, so halve the width now */ if (flags & TILE_4BPP) { assert(width % 2 == 0); width /= 2; } /* iterate over rows */ for (ty = 0; ty < height; ty++) { UINT16 *pixptr = BITMAP_ADDR16(pixmap, y0, x0); UINT8 *flagsptr = BITMAP_ADDR8(flagsmap, y0, x0); int xoffs = 0; /* pre-advance to the next row */ y0 += dy0; /* 8bpp data */ if (!(flags & TILE_4BPP)) { for (tx = 0; tx < width; tx++) { UINT8 pen = *pendata++; UINT8 map = penmap[pen]; pixptr[xoffs] = palette_lookup[pen]; flagsptr[xoffs] = map | category; andmask &= map; ormask |= map; xoffs += dx0; } } /* 4bpp data */ else { for (tx = 0; tx < width; tx++) { UINT8 data = *pendata++; pen_t pen; UINT8 map; pen = data & 0x0f; map = penmap[pen]; pixptr[xoffs] = palette_lookup[pen]; flagsptr[xoffs] = map | category; andmask &= map; ormask |= map; xoffs += dx0; pen = data >> 4; map = penmap[pen]; pixptr[xoffs] = palette_lookup[pen]; flagsptr[xoffs] = map | category; andmask &= map; ormask |= map; xoffs += dx0; } } } return andmask ^ ormask; } /*------------------------------------------------- tile_draw_colortrans - draw a single tile to the tilemap's internal pixmap, using the remapped pen as the pen_to_flags lookup value -------------------------------------------------*/ static UINT8 tile_draw_colortrans(tilemap *tmap, const UINT8 *pendata, UINT32 x0, UINT32 y0, UINT32 palette_base, UINT8 category, UINT8 group, UINT8 flags) { const UINT8 *penmap = tmap->pen_to_flags + group * tmap->max_pen_to_flags; const pen_t *palette_lookup = Machine->remapped_colortable + palette_base; mame_bitmap *flagsmap = tmap->flagsmap; mame_bitmap *pixmap = tmap->pixmap; int height = tmap->tileheight; int width = tmap->tilewidth; UINT8 andmask = ~0, ormask = 0; int dx0 = 1, dy0 = 1; int tx, ty; /* OR in the force layer flags */ category |= flags & (TILE_FORCE_LAYER0 | TILE_FORCE_LAYER1 | TILE_FORCE_LAYER2); /* if we're vertically flipped, point to the bottom row and work backwards */ if (flags & TILE_FLIPY) { y0 += height - 1; dy0 = -1; } /* if we're horizontally flipped, point to the rightmost column and work backwards */ if (flags & TILE_FLIPX) { x0 += width - 1; dx0 = -1; } /* in 4bpp mode, we draw in groups of 2 pixels, so halve the width now */ if (flags & TILE_4BPP) { assert(width % 2 == 0); width /= 2; } /* iterate over rows */ for (ty = 0; ty < height; ty++) { UINT16 *pixptr = BITMAP_ADDR16(pixmap, y0, x0); UINT8 *flagsptr = BITMAP_ADDR8(flagsmap, y0, x0); int xoffs = 0; /* pre-advance to the next row */ y0 += dy0; /* 8bpp data */ if (!(flags & TILE_4BPP)) { for (tx = 0; tx < width; tx++) { pen_t pen = palette_lookup[*pendata++]; UINT8 map = penmap[pen]; pixptr[xoffs] = pen; flagsptr[xoffs] = map | category; andmask &= map; ormask |= map; xoffs += dx0; } } /* 4bpp data */ else { for (tx = 0; tx < width; tx++) { UINT8 data = *pendata++; pen_t pen; UINT8 map; pen = palette_lookup[data & 0x0f]; map = penmap[pen]; pixptr[xoffs] = pen; flagsptr[xoffs] = map | category; andmask &= map; ormask |= map; xoffs += dx0; pen = palette_lookup[data >> 4]; map = penmap[pen]; pixptr[xoffs] = pen; flagsptr[xoffs] = map | category; andmask &= map; ormask |= map; xoffs += dx0; } } } return andmask ^ ormask; } /*------------------------------------------------- tile_apply_bitmask - apply a bitmask to an already-rendered tile by modifying the flagsmap appropriately -------------------------------------------------*/ static UINT8 tile_apply_bitmask(tilemap *tmap, const UINT8 *maskdata, UINT32 x0, UINT32 y0, UINT8 category, UINT8 flags) { mame_bitmap *flagsmap = tmap->flagsmap; int height = tmap->tileheight; int width = tmap->tilewidth; UINT8 andmask = ~0, ormask = 0; int dx0 = 1, dy0 = 1; int bitoffs = 0; int tx, ty; /* if we're vertically flipped, point to the bottom row and work backwards */ if (flags & TILE_FLIPY) { y0 += height - 1; dy0 = -1; } /* if we're horizontally flipped, point to the rightmost column and work backwards */ if (flags & TILE_FLIPX) { x0 += width - 1; dx0 = -1; } /* iterate over rows */ for (ty = 0; ty < height; ty++) { UINT8 *flagsptr = BITMAP_ADDR8(flagsmap, y0, x0); int xoffs = 0; /* pre-advance to the next row */ y0 += dy0; /* anywhere the bitmask is 0 should be transparent */ for (tx = 0; tx < width; tx++) { UINT8 map = flagsptr[xoffs]; if ((maskdata[bitoffs / 8] & (0x80 >> (bitoffs & 7))) == 0) map = flagsptr[xoffs] = TILEMAP_PIXEL_TRANSPARENT | category; andmask &= map; ormask |= map; xoffs += dx0; bitoffs++; } } return andmask ^ ormask; } /*************************************************************************** DRAWING HELPERS ***************************************************************************/ /*------------------------------------------------- configure_blit_parameters - fill in the standard blit parameters based on the input data; this code is shared by normal, roz, and indexed drawing code -------------------------------------------------*/ static void configure_blit_parameters(blit_parameters *blit, tilemap *tmap, mame_bitmap *dest, const rectangle *cliprect, UINT32 flags, UINT8 priority, UINT8 priority_mask) { /* start with nothing */ memset(blit, 0, sizeof(*blit)); /* set the target bitmap */ blit->bitmap = dest; /* if we have a cliprect, copy */ if (cliprect != NULL) blit->cliprect = *cliprect; /* otherwise, make one up */ else { blit->cliprect.min_x = blit->cliprect.min_y = 0; blit->cliprect.max_x = dest->width - 1; blit->cliprect.max_y = dest->height - 1; } /* set the priority code */ blit->tilemap_priority_code = priority | (priority_mask << 8) | (tmap->palette_offset << 16); /* if no destination, just render priority */ if (dest == NULL) { blit->draw_masked = scanline_draw_masked_null; blit->draw_opaque = scanline_draw_opaque_null; } /* otherwise get the appropriate callbacks for the format and flags */ else { switch (dest->format) { case BITMAP_FORMAT_RGB32: blit->draw_masked = (flags & TILEMAP_DRAW_ALPHA) ? scanline_draw_masked_rgb32_alpha : scanline_draw_masked_rgb32; blit->draw_opaque = (flags & TILEMAP_DRAW_ALPHA) ? scanline_draw_opaque_rgb32_alpha : scanline_draw_opaque_rgb32; break; case BITMAP_FORMAT_RGB15: blit->draw_masked = (flags & TILEMAP_DRAW_ALPHA) ? scanline_draw_masked_rgb16_alpha : scanline_draw_masked_rgb16; blit->draw_opaque = (flags & TILEMAP_DRAW_ALPHA) ? scanline_draw_opaque_rgb16_alpha : scanline_draw_opaque_rgb16; break; case BITMAP_FORMAT_INDEXED16: blit->draw_masked = scanline_draw_masked_ind16; blit->draw_opaque = scanline_draw_opaque_ind16; break; default: fatalerror("tilemap_draw_primask: Invalid bitmap format"); break; } } /* tile priority; unless otherwise specified, draw anything in layer 0 */ blit->mask = TILEMAP_PIXEL_CATEGORY_MASK; blit->value = flags & TILEMAP_PIXEL_CATEGORY_MASK; /* if no layers specified, draw layer 0 */ if ((flags & (TILEMAP_DRAW_LAYER0 | TILEMAP_DRAW_LAYER1 | TILEMAP_DRAW_LAYER2)) == 0) flags |= TILEMAP_DRAW_LAYER0; /* OR in the bits from the draw masks */ blit->mask |= flags & (TILEMAP_DRAW_LAYER0 | TILEMAP_DRAW_LAYER1 | TILEMAP_DRAW_LAYER2); blit->value |= flags & (TILEMAP_DRAW_LAYER0 | TILEMAP_DRAW_LAYER1 | TILEMAP_DRAW_LAYER2); /* for all-opaque rendering, don't check any of the layer bits */ if (flags & TILEMAP_DRAW_OPAQUE) { blit->mask &= ~(TILEMAP_PIXEL_LAYER0 | TILEMAP_PIXEL_LAYER1 | TILEMAP_PIXEL_LAYER2); blit->value &= ~(TILEMAP_PIXEL_LAYER0 | TILEMAP_PIXEL_LAYER1 | TILEMAP_PIXEL_LAYER2); } } /*------------------------------------------------- tilemap_draw_instance - draw a single instance of the tilemap to the internal pixmap at the given xpos,ypos -------------------------------------------------*/ static void tilemap_draw_instance(tilemap *tmap, const blit_parameters *blit, int xpos, int ypos) { mame_bitmap *dest = blit->bitmap; const UINT16 *source_baseaddr; const UINT8 *mask_baseaddr; void *dest_baseaddr = NULL; UINT8 *priority_baseaddr; int dest_line_pitch_bytes = 0; int dest_bytespp = 0; int mincol, maxcol; int x1, y1, x2, y2; int y, nexty; /* clip destination coordinates to the tilemap */ /* note that x2/y2 are exclusive, not inclusive */ x1 = MAX(xpos, blit->cliprect.min_x); x2 = MIN(xpos + (int)tmap->width, blit->cliprect.max_x + 1); y1 = MAX(ypos, blit->cliprect.min_y); y2 = MIN(ypos + (int)tmap->height, blit->cliprect.max_y + 1); /* if totally clipped, stop here */ if (x1 >= x2 || y1 >= y2) return; /* look up priority and destination base addresses for y1 */ priority_baseaddr = BITMAP_ADDR8(priority_bitmap, y1, xpos); if (dest != NULL) { dest_bytespp = dest->bpp / 8; dest_line_pitch_bytes = dest->rowpixels * dest_bytespp; dest_baseaddr = (UINT8 *)dest->base + (y1 * dest->rowpixels + xpos) * dest_bytespp; } /* convert screen coordinates to source tilemap coordinates */ x1 -= xpos; y1 -= ypos; x2 -= xpos; y2 -= ypos; /* get tilemap pixels */ source_baseaddr = BITMAP_ADDR16(tmap->pixmap, y1, 0); mask_baseaddr = BITMAP_ADDR8(tmap->flagsmap, y1, 0); /* get start/stop columns, rounding outward */ mincol = x1 / tmap->tilewidth; maxcol = (x2 + tmap->tilewidth - 1) / tmap->tilewidth; /* set up row counter */ y = y1; nexty = tmap->tileheight * (y1 / tmap->tileheight) + tmap->tileheight; nexty = MIN(nexty, y2); /* loop over tilemap rows */ for (;;) { int row = y / tmap->tileheight; trans_t prev_trans = WHOLLY_TRANSPARENT; trans_t cur_trans; int x_start = x1; int column; /* iterate across the applicable tilemap columns */ for (column = mincol; column <= maxcol; column++) { int x_end; /* we don't actually render the last column; it is always just used for flushing */ if (column == maxcol) cur_trans = WHOLLY_TRANSPARENT; /* for other columns we look up the transparency information */ else { tilemap_logical_index logindex = row * tmap->cols + column; /* if the current tile is dirty, fix it */ if (tmap->tileflags[logindex] == TILE_FLAG_DIRTY) tile_update(tmap, logindex, column, row); /* if the current summary data is non-zero, we must draw masked */ if ((tmap->tileflags[logindex] & blit->mask) != 0) cur_trans = MASKED; /* otherwise, our transparency state is constant across the tile; fetch it */ else cur_trans = ((mask_baseaddr[column * tmap->tilewidth] & blit->mask) == blit->value) ? WHOLLY_OPAQUE : WHOLLY_TRANSPARENT; } /* if the transparency state is the same as last time, don't render yet */ if (cur_trans == prev_trans) continue; /* compute the end of this run, in pixels */ x_end = column * tmap->tilewidth; x_end = MAX(x_end, x1); x_end = MIN(x_end, x2); /* if we're rendering something, compute the pointers */ if (prev_trans != WHOLLY_TRANSPARENT) { const UINT16 *source0 = source_baseaddr + x_start; void *dest0 = (UINT8 *)dest_baseaddr + x_start * dest_bytespp; UINT8 *pmap0 = priority_baseaddr + x_start; int cury; /* if we were opaque, use the opaque renderer */ if (prev_trans == WHOLLY_OPAQUE) { for (cury = y; cury < nexty; cury++) { (*blit->draw_opaque)(dest0, source0, x_end - x_start, pmap0, blit->tilemap_priority_code); dest0 = (UINT8 *)dest0 + dest_line_pitch_bytes; source0 += tmap->pixmap->rowpixels; pmap0 += priority_bitmap->rowpixels; } } /* otherwise use the masked renderer */ else { const UINT8 *mask0 = mask_baseaddr + x_start; for (cury = y; cury < nexty; cury++) { (*blit->draw_masked)(dest0, source0, mask0, blit->mask, blit->value, x_end - x_start, pmap0, blit->tilemap_priority_code); dest0 = (UINT8 *)dest0 + dest_line_pitch_bytes; source0 += tmap->pixmap->rowpixels; mask0 += tmap->flagsmap->rowpixels; pmap0 += priority_bitmap->rowpixels; } } } /* the new start is the end */ x_start = x_end; prev_trans = cur_trans; } /* if this was the last row, stop */ if (nexty == y2) break; /* advance to the next row on all our bitmaps */ priority_baseaddr += priority_bitmap->rowpixels * (nexty - y); source_baseaddr += tmap->pixmap->rowpixels * (nexty - y); mask_baseaddr += tmap->flagsmap->rowpixels * (nexty - y); dest_baseaddr = (UINT8 *)dest_baseaddr + dest_line_pitch_bytes * (nexty - y); /* increment the Y counter */ y = nexty; nexty += tmap->tileheight; nexty = MIN(nexty, y2); } } /*------------------------------------------------- tilemap_draw_roz_core - render the tilemap's pixmap to the destination with rotation and zoom -------------------------------------------------*/ #define ROZ_PLOT_PIXEL(INPUT_VAL) \ do { \ if (blit->draw_masked == scanline_draw_masked_ind16) \ *(UINT16 *)dest = (INPUT_VAL) + (priority >> 16); \ else if (blit->draw_masked == scanline_draw_masked_rgb32) \ *(UINT32 *)dest = clut[INPUT_VAL]; \ else if (blit->draw_masked == scanline_draw_masked_rgb16) \ *(UINT16 *)dest = clut[INPUT_VAL]; \ else if (blit->draw_masked == scanline_draw_masked_rgb32_alpha) \ *(UINT32 *)dest = alpha_blend32(*(UINT32 *)dest, clut[INPUT_VAL]); \ else if (blit->draw_masked == scanline_draw_masked_rgb16_alpha) \ *(UINT16 *)dest = alpha_blend16(*(UINT16 *)dest, clut[INPUT_VAL]); \ } while (0) static void tilemap_draw_roz_core(tilemap *tmap, const blit_parameters *blit, UINT32 startx, UINT32 starty, int incxx, int incxy, int incyx, int incyy, int wraparound) { const pen_t *clut = &Machine->remapped_colortable[blit->tilemap_priority_code >> 16]; mame_bitmap *destbitmap = blit->bitmap; mame_bitmap *srcbitmap = tmap->pixmap; mame_bitmap *flagsmap = tmap->flagsmap; const int xmask = srcbitmap->width-1; const int ymask = srcbitmap->height-1; const int widthshifted = srcbitmap->width << 16; const int heightshifted = srcbitmap->height << 16; UINT32 priority = blit->tilemap_priority_code; UINT8 mask = blit->mask; UINT8 value = blit->value; UINT32 cx; UINT32 cy; int x; int sx; int sy; int ex; int ey; void *dest; UINT8 *pri; const UINT16 *src; const UINT8 *maskptr; int destadvance = destbitmap->bpp / 8; /* pre-advance based on the cliprect */ startx += blit->cliprect.min_x * incxx + blit->cliprect.min_y * incyx; starty += blit->cliprect.min_x * incxy + blit->cliprect.min_y * incyy; /* extract start/end points */ sx = blit->cliprect.min_x; sy = blit->cliprect.min_y; ex = blit->cliprect.max_x; ey = blit->cliprect.max_y; /* optimized loop for the not rotated case */ if (incxy == 0 && incyx == 0 && !wraparound) { /* skip without drawing until we are within the bitmap */ while (startx >= widthshifted && sx <= ex) { startx += incxx; sx++; } /* early exit if we're done already */ if (sx > ex) return; /* loop over rows */ while (sy <= ey) { /* only draw if Y is within range */ if (starty < heightshifted) { /* initialize X counters */ x = sx; cx = startx; cy = starty >> 16; /* get source and priority pointers */ pri = BITMAP_ADDR8(priority_bitmap, sy, sx); src = BITMAP_ADDR16(srcbitmap, cy, 0); maskptr = BITMAP_ADDR8(flagsmap, cy, 0); dest = (UINT8 *)destbitmap->base + (destbitmap->rowpixels * sy + sx) * destadvance; /* loop over columns */ while (x <= ex && cx < widthshifted) { /* plot if we match the mask */ if ((maskptr[cx >> 16] & mask) == value) { ROZ_PLOT_PIXEL(src[cx >> 16]); *pri = (*pri & (priority >> 8)) | priority; } /* advance in X */ cx += incxx; x++; dest = (UINT8 *)dest + destadvance; pri++; } } /* advance in Y */ starty += incyy; sy++; } } /* wraparound case */ else if (wraparound) { /* loop over rows */ while (sy <= ey) { /* initialize X counters */ x = sx; cx = startx; cy = starty; /* get dest and priority pointers */ dest = (UINT8 *)destbitmap->base + (destbitmap->rowpixels * sy + sx) * destadvance; pri = BITMAP_ADDR8(priority_bitmap, sy, sx); /* loop over columns */ while (x <= ex) { /* plot if we match the mask */ if ((*BITMAP_ADDR8(flagsmap, (cy >> 16) & ymask, (cx >> 16) & xmask) & mask) == value) { ROZ_PLOT_PIXEL(*BITMAP_ADDR16(srcbitmap, (cy >> 16) & ymask, (cx >> 16) & xmask)); *pri = (*pri & (priority >> 8)) | priority; } /* advance in X */ cx += incxx; cy += incxy; x++; dest = (UINT8 *)dest + destadvance; pri++; } /* advance in Y */ startx += incyx; starty += incyy; sy++; } } /* non-wraparound case */ else { /* loop over rows */ while (sy <= ey) { /* initialize X counters */ x = sx; cx = startx; cy = starty; /* get dest and priority pointers */ dest = (UINT8 *)destbitmap->base + (destbitmap->rowpixels * sy + sx) * destadvance; pri = BITMAP_ADDR8(priority_bitmap, sy, sx); /* loop over columns */ while (x <= ex) { /* plot if we're within the bitmap and we match the mask */ if (cx < widthshifted && cy < heightshifted) if ((*BITMAP_ADDR8(flagsmap, cy >> 16, cx >> 16) & mask) == value) { ROZ_PLOT_PIXEL(*BITMAP_ADDR16(srcbitmap, cy >> 16, cx >> 16)); *pri = (*pri & (priority >> 8)) | priority; } /* advance in X */ cx += incxx; cy += incxy; x++; dest = (UINT8 *)dest + destadvance; pri++; } /* advance in Y */ startx += incyx; starty += incyy; sy++; } } } /*************************************************************************** SCANLINE RASTERIZERS ***************************************************************************/ /*------------------------------------------------- scanline_draw_opaque_null - draw to a NULL bitmap, setting priority only -------------------------------------------------*/ static void scanline_draw_opaque_null(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode) { int i; /* skip entirely if not changing priority */ if (pcode != 0xff00) { for (i = 0; i < count; i++) pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /*------------------------------------------------- scanline_draw_masked_null - draw to a NULL bitmap using a mask, setting priority only -------------------------------------------------*/ static void scanline_draw_masked_null(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode) { int i; /* skip entirely if not changing priority */ if (pcode != 0xff00) { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /*------------------------------------------------- scanline_draw_opaque_ind16 - draw to a 16bpp indexed bitmap -------------------------------------------------*/ static void scanline_draw_opaque_ind16(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode) { UINT16 *dest = _dest; int pal = pcode >> 16; int i; /* special case for no palette offset */ if (pal == 0) { memcpy(dest, source, count * 2); /* priority if necessary */ if (pcode != 0xff00) { for (i = 0; i < count; i++) pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* priority case */ else if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) { dest[i] = source[i] + pal; pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) dest[i] = source[i] + pal; } } /*------------------------------------------------- scanline_draw_masked_ind16 - draw to a 16bpp indexed bitmap using a mask -------------------------------------------------*/ static void scanline_draw_masked_ind16(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode) { UINT16 *dest = _dest; int pal = pcode >> 16; int i; /* priority case */ if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) { dest[i] = source[i] + pal; pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) dest[i] = source[i] + pal; } } /*------------------------------------------------- scanline_draw_opaque_rgb16 - draw to a 16bpp RGB bitmap -------------------------------------------------*/ static void scanline_draw_opaque_rgb16(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode) { const pen_t *clut = &Machine->remapped_colortable[pcode >> 16]; UINT16 *dest = _dest; int i; /* priority case */ if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) { dest[i] = clut[source[i]]; pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) dest[i] = clut[source[i]]; } } /*------------------------------------------------- scanline_draw_masked_rgb16 - draw to a 16bpp RGB bitmap using a mask -------------------------------------------------*/ static void scanline_draw_masked_rgb16(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode) { const pen_t *clut = &Machine->remapped_colortable[pcode >> 16]; UINT16 *dest = _dest; int i; /* priority case */ if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) { dest[i] = clut[source[i]]; pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) dest[i] = clut[source[i]]; } } /*------------------------------------------------- scanline_draw_opaque_rgb16_alpha - draw to a 16bpp RGB bitmap with alpha blending -------------------------------------------------*/ static void scanline_draw_opaque_rgb16_alpha(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode) { const pen_t *clut = &Machine->remapped_colortable[pcode >> 16]; UINT16 *dest = _dest; int i; /* priority case */ if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) { dest[i] = alpha_blend16(dest[i], clut[source[i]]); pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) dest[i] = alpha_blend16(dest[i], clut[source[i]]); } } /*------------------------------------------------- scanline_draw_masked_rgb16_alpha - draw to a 16bpp RGB bitmap using a mask and alpha blending -------------------------------------------------*/ static void scanline_draw_masked_rgb16_alpha(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode) { const pen_t *clut = &Machine->remapped_colortable[pcode >> 16]; UINT16 *dest = _dest; int i; /* priority case */ if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) { dest[i] = alpha_blend16(dest[i], clut[source[i]]); pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) dest[i] = alpha_blend16(dest[i], clut[source[i]]); } } /*------------------------------------------------- scanline_draw_opaque_rgb32 - draw to a 32bpp RGB bitmap -------------------------------------------------*/ static void scanline_draw_opaque_rgb32(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode) { const pen_t *clut = &Machine->remapped_colortable[pcode >> 16]; UINT32 *dest = _dest; int i; /* priority case */ if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) { dest[i] = clut[source[i]]; pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) dest[i] = clut[source[i]]; } } /*------------------------------------------------- scanline_draw_masked_rgb32 - draw to a 32bpp RGB bitmap using a mask -------------------------------------------------*/ static void scanline_draw_masked_rgb32(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode) { const pen_t *clut = &Machine->remapped_colortable[pcode >> 16]; UINT32 *dest = _dest; int i; /* priority case */ if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) { dest[i] = clut[source[i]]; pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) dest[i] = clut[source[i]]; } } /*------------------------------------------------- scanline_draw_opaque_rgb32_alpha - draw to a 32bpp RGB bitmap with alpha blending -------------------------------------------------*/ static void scanline_draw_opaque_rgb32_alpha(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode) { const pen_t *clut = &Machine->remapped_colortable[pcode >> 16]; UINT32 *dest = _dest; int i; /* priority case */ if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) { dest[i] = alpha_blend32(dest[i], clut[source[i]]); pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) dest[i] = alpha_blend32(dest[i], clut[source[i]]); } } /*------------------------------------------------- scanline_draw_masked_rgb32_alpha - draw to a 32bpp RGB bitmap using a mask and alpha blending -------------------------------------------------*/ static void scanline_draw_masked_rgb32_alpha(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode) { const pen_t *clut = &Machine->remapped_colortable[pcode >> 16]; UINT32 *dest = _dest; int i; /* priority case */ if ((pcode & 0xffff) != 0xff00) { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) { dest[i] = alpha_blend32(dest[i], clut[source[i]]); pri[i] = (pri[i] & (pcode >> 8)) | pcode; } } /* no priority case */ else { for (i = 0; i < count; i++) if ((maskptr[i] & mask) == value) dest[i] = alpha_blend32(dest[i], clut[source[i]]); } }