diff options
Diffstat (limited to 'src/emu/drawgfxt.ipp')
-rw-r--r-- | src/emu/drawgfxt.ipp | 1911 |
1 files changed, 1911 insertions, 0 deletions
diff --git a/src/emu/drawgfxt.ipp b/src/emu/drawgfxt.ipp new file mode 100644 index 00000000000..eb4910ebf78 --- /dev/null +++ b/src/emu/drawgfxt.ipp @@ -0,0 +1,1911 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles +/********************************************************************* + + drawgfxt.ipp + + Template function implementing drawgfx core operations. Drivers + can use these if they need custom behavior not provided by the + existing drawgfx functions. + +*********************************************************************/ + +#pragma once + +#ifndef MAME_EMU_DRAWGFXT_IPP +#define MAME_EMU_DRAWGFXT_IPP + + +/*************************************************************************** + PIXEL OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + PIXEL_OP_COPY_OPAQUE - render all pixels + regardless of pen, copying directly +-------------------------------------------------*/ + +#define PIXEL_OP_COPY_OPAQUE(DEST, SOURCE) \ +do \ +{ \ + (DEST) = SOURCE; \ +} \ +while (0) +#define PIXEL_OP_COPY_OPAQUE_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = SOURCE; \ + (PRIORITY) = 31; \ +} \ +while (0) +#define PIXEL_OP_COPY_OPAQUE_PRIMASK(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + (DEST) = SOURCE; \ + (PRIORITY) = ((PRIORITY) & pmask) | pcode; \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_COPY_TRANSPEN - render all pixels + except those matching 'transpen', copying + directly +-------------------------------------------------*/ + +#define PIXEL_OP_COPY_TRANSPEN(DEST, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (srcdata != trans_pen) \ + (DEST) = SOURCE; \ +} \ +while (0) +#define PIXEL_OP_COPY_TRANSPEN_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (srcdata != trans_pen) \ + { \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = SOURCE; \ + (PRIORITY) = 31; \ + } \ +} \ +while (0) +#define PIXEL_OP_COPY_TRANSPEN_PRIMASK(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (srcdata != trans_pen) \ + { \ + (DEST) = SOURCE; \ + (PRIORITY) = ((PRIORITY) & pmask) | pcode; \ + } \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_COPY_TRANSALPHA - render all pixels + except those with an alpha of zero, copying + directly +-------------------------------------------------*/ + +#define PIXEL_OP_COPY_TRANSALPHA(DEST, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if ((srcdata & 0xff000000) != 0) \ + (DEST) = SOURCE; \ +} \ +while (0) +#define PIXEL_OP_COPY_TRANSALPHA_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if ((srcdata & 0xff000000) != 0) \ + { \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = SOURCE; \ + (PRIORITY) = 31; \ + } \ +} \ +while (0) +#define PIXEL_OP_COPY_TRANSALPHA_PRIMASK(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if ((srcdata & 0xff000000) != 0) \ + { \ + (DEST) = SOURCE; \ + (PRIORITY) = ((PRIORITY) & pmask) | pcode; \ + } \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_REMAP_OPAQUE - render all pixels + regardless of pen, mapping the pen via the + 'paldata' array +-------------------------------------------------*/ + +#define PIXEL_OP_REMAP_OPAQUE(DEST, SOURCE) \ +do \ +{ \ + (DEST) = paldata[SOURCE]; \ +} \ +while (0) +#define PIXEL_OP_REMAP_OPAQUE_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = paldata[SOURCE]; \ + (PRIORITY) = 31; \ +} \ +while (0) +#define PIXEL_OP_REMAP_OPAQUE_PRIMASK(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + (DEST) = paldata[SOURCE]; \ + (PRIORITY) = ((PRIORITY) & pmask) | pcode; \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_REBASE_OPAQUE - render all pixels + regardless of pen, adding 'color' to the + pen value +-------------------------------------------------*/ + +#define PIXEL_OP_REBASE_OPAQUE(DEST, SOURCE) \ +do \ +{ \ + (DEST) = color + (SOURCE); \ +} \ +while (0) +#define PIXEL_OP_REBASE_OPAQUE_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = color + (SOURCE); \ + (PRIORITY) = 31; \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_REMAP_TRANSPEN - render all pixels + except those matching 'trans_pen', mapping the + pen via the 'paldata' array +-------------------------------------------------*/ + +#define PIXEL_OP_REMAP_TRANSPEN(DEST, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (srcdata != trans_pen) \ + (DEST) = paldata[srcdata]; \ +} \ +while (0) +#define PIXEL_OP_REMAP_TRANSPEN_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (srcdata != trans_pen) \ + { \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = paldata[srcdata]; \ + (PRIORITY) = 31; \ + } \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_REBASE_TRANSPEN - render all pixels + except those matching 'transpen', adding + 'color' to the pen value +-------------------------------------------------*/ + +#define PIXEL_OP_REBASE_TRANSPEN(DEST, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (srcdata != trans_pen) \ + (DEST) = color + srcdata; \ +} \ +while (0) +#define PIXEL_OP_REBASE_TRANSPEN_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (srcdata != trans_pen) \ + { \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = color + srcdata; \ + (PRIORITY) = 31; \ + } \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_REMAP_TRANSMASK - render all pixels + except those matching 'trans_mask', mapping the + pen via the 'paldata' array +-------------------------------------------------*/ + +#define PIXEL_OP_REMAP_TRANSMASK(DEST, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (((trans_mask >> srcdata) & 1) == 0) \ + (DEST) = paldata[srcdata]; \ +} \ +while (0) +#define PIXEL_OP_REMAP_TRANSMASK_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (((trans_mask >> srcdata) & 1) == 0) \ + { \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = paldata[srcdata]; \ + (PRIORITY) = 31; \ + } \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_REBASE_TRANSMASK - render all pixels + except those matching 'trans_mask', adding + 'color' to the pen value +-------------------------------------------------*/ + +#define PIXEL_OP_REBASE_TRANSMASK(DEST, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (((trans_mask >> srcdata) & 1) == 0) \ + (DEST) = color + srcdata; \ +} \ +while (0) +#define PIXEL_OP_REBASE_TRANSMASK_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (((trans_mask >> srcdata) & 1) == 0) \ + { \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = color + srcdata; \ + (PRIORITY) = 31; \ + } \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_REBASE_TRANSTABLE - look up each pen in + 'pentable'; if the entry is DRAWMODE_NONE, + don't draw it; if the entry is DRAWMODE_SOURCE, + add 'color' to the pen value; if the entry is + DRAWMODE_SHADOW, generate a shadow of the + destination pixel using 'shadowtable' + + PIXEL_OP_REMAP_TRANSTABLE - look up each pen in + 'pentable'; if the entry is DRAWMODE_NONE, + don't draw it; if the entry is DRAWMODE_SOURCE, + look up the pen via the 'paldata' array; if the + entry is DRAWMODE_SHADOW, generate a shadow of + the destination pixel using 'shadowtable' +-------------------------------------------------*/ + +#define PIXEL_OP_REBASE_TRANSTABLE16(DEST, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + u32 entry = pentable[srcdata]; \ + if (entry != DRAWMODE_NONE) \ + { \ + if (entry == DRAWMODE_SOURCE) \ + (DEST) = color + srcdata; \ + else \ + (DEST) = shadowtable[DEST]; \ + } \ +} \ +while (0) +#define PIXEL_OP_REMAP_TRANSTABLE32(DEST, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + u32 entry = pentable[srcdata]; \ + if (entry != DRAWMODE_NONE) \ + { \ + if (entry == DRAWMODE_SOURCE) \ + (DEST) = paldata[srcdata]; \ + else \ + (DEST) = shadowtable[rgb_t(DEST).as_rgb15()]; \ + } \ +} \ +while (0) +#define PIXEL_OP_REBASE_TRANSTABLE16_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + u32 entry = pentable[srcdata]; \ + if (entry != DRAWMODE_NONE) \ + { \ + u8 pridata = (PRIORITY); \ + if (entry == DRAWMODE_SOURCE) \ + { \ + if (((1 << (pridata & 0x1f)) & pmask) == 0) \ + (DEST) = color + srcdata; \ + (PRIORITY) = 31; \ + } \ + else if ((pridata & 0x80) == 0 && ((1 << (pridata & 0x1f)) & pmask) == 0) \ + { \ + (DEST) = shadowtable[DEST]; \ + (PRIORITY) = pridata | 0x80; \ + } \ + } \ +} \ +while (0) +#define PIXEL_OP_REMAP_TRANSTABLE32_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + u32 entry = pentable[srcdata]; \ + if (entry != DRAWMODE_NONE) \ + { \ + u8 pridata = (PRIORITY); \ + if (entry == DRAWMODE_SOURCE) \ + { \ + if (((1 << (pridata & 0x1f)) & pmask) == 0) \ + (DEST) = paldata[srcdata]; \ + (PRIORITY) = 31; \ + } \ + else if ((pridata & 0x80) == 0 && ((1 << (pridata & 0x1f)) & pmask) == 0) \ + { \ + (DEST) = shadowtable[rgb_t(DEST).as_rgb15()]; \ + (PRIORITY) = pridata | 0x80; \ + } \ + } \ +} \ +while (0) + +/*------------------------------------------------- + PIXEL_OP_REMAP_TRANSPEN_ALPHA - render all + pixels except those matching 'transpen', + mapping the pen to via the 'paldata' array; + the resulting color is RGB alpha blended + against the destination using 'alpha' +-------------------------------------------------*/ + +#define PIXEL_OP_REMAP_TRANSPEN_ALPHA32(DEST, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (srcdata != trans_pen) \ + (DEST) = alpha_blend_r32((DEST), paldata[srcdata], alpha_val); \ +} \ +while (0) +#define PIXEL_OP_REMAP_TRANSPEN_ALPHA32_PRIORITY(DEST, PRIORITY, SOURCE) \ +do \ +{ \ + u32 srcdata = (SOURCE); \ + if (srcdata != trans_pen) \ + { \ + if (((1 << ((PRIORITY) & 0x1f)) & pmask) == 0) \ + (DEST) = alpha_blend_r32((DEST), paldata[srcdata], alpha_val); \ + (PRIORITY) = 31; \ + } \ +} \ +while (0) + + +/*************************************************************************** + BASIC DRAWGFX CORE +***************************************************************************/ + +/* + Input parameters: + + bitmap_t &dest - the bitmap to render to + const rectangle &cliprect - a clipping rectangle (assumed to be clipped to the size of 'dest') + gfx_element *gfx - pointer to the gfx_element to render + u32 code - index of the entry within gfx_element + int flipx - non-zero means render right-to-left instead of left-to-right + int flipy - non-zero means render bottom-to-top instead of top-to-bottom + s32 destx - the top-left X coordinate to render to + s32 desty - the top-left Y coordinate to render to + bitmap_t &priority - the priority bitmap (if and only if priority is to be applied) +*/ + + +template <typename BitmapType, typename FunctionClass> +void gfx_element::drawgfx_core(BitmapType &dest, const rectangle &cliprect, u32 code, int flipx, int flipy, s32 destx, s32 desty, FunctionClass pixel_op) +{ + g_profiler.start(PROFILER_DRAWGFX); + do { + assert(dest.valid()); + assert(dest.cliprect().contains(cliprect)); + assert(code < elements()); + + // ignore empty/invalid cliprects + if (cliprect.empty()) + break; + + // compute final pixel in X and exit if we are entirely clipped + s32 destendx = destx + width() - 1; + if (destx > cliprect.right() || destendx < cliprect.left()) + break; + + // apply left clip + s32 srcx = 0; + if (destx < cliprect.left()) + { + srcx = cliprect.left() - destx; + destx = cliprect.left(); + } + + // apply right clip + if (destendx > cliprect.right()) + destendx = cliprect.right(); + + // compute final pixel in Y and exit if we are entirely clipped + s32 destendy = desty + height() - 1; + if (desty > cliprect.bottom() || destendy < cliprect.top()) + break; + + // apply top clip + s32 srcy = 0; + if (desty < cliprect.top()) + { + srcy = cliprect.top() - desty; + desty = cliprect.top(); + } + + // apply bottom clip + if (destendy > cliprect.bottom()) + destendy = cliprect.bottom(); + + // apply X flipping + if (flipx) + srcx = width() - 1 - srcx; + + // apply Y flipping + s32 dy = rowbytes(); + if (flipy) + { + srcy = height() - 1 - srcy; + dy = -dy; + } + + // fetch the source data + const u8 *srcdata = get_data(code); + + // compute how many blocks of 4 pixels we have + u32 numblocks = (destendx + 1 - destx) / 4; + u32 leftovers = (destendx + 1 - destx) - 4 * numblocks; + + // adjust srcdata to point to the first source pixel of the row + srcdata += srcy * rowbytes() + srcx; + + // non-flipped 8bpp case + if (!flipx) + { + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *destptr = &dest.pix(cury, destx); + const u8 *srcptr = srcdata; + srcdata += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], srcptr[0]); + pixel_op(destptr[1], srcptr[1]); + pixel_op(destptr[2], srcptr[2]); + pixel_op(destptr[3], srcptr[3]); + + srcptr += 4; + destptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], srcptr[0]); + srcptr++; + destptr++; + } + } + } + + // flipped 8bpp case + else + { + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *destptr = &dest.pix(cury, destx); + const u8 *srcptr = srcdata; + srcdata += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], srcptr[ 0]); + pixel_op(destptr[1], srcptr[-1]); + pixel_op(destptr[2], srcptr[-2]); + pixel_op(destptr[3], srcptr[-3]); + + srcptr -= 4; + destptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], srcptr[0]); + srcptr--; + destptr++; + } + } + } + } while (0); + g_profiler.stop(); +} + + +template <typename BitmapType, typename PriorityType, typename FunctionClass> +void gfx_element::drawgfx_core(BitmapType &dest, const rectangle &cliprect, u32 code, int flipx, int flipy, s32 destx, s32 desty, PriorityType &priority, FunctionClass pixel_op) +{ + g_profiler.start(PROFILER_DRAWGFX); + do { + assert(dest.valid()); + assert(priority.valid()); + assert(dest.cliprect().contains(cliprect)); + assert(code < elements()); + + // ignore empty/invalid cliprects + if (cliprect.empty()) + break; + + // compute final pixel in X and exit if we are entirely clipped + s32 destendx = destx + width() - 1; + if (destx > cliprect.right() || destendx < cliprect.left()) + break; + + // apply left clip + s32 srcx = 0; + if (destx < cliprect.left()) + { + srcx = cliprect.left() - destx; + destx = cliprect.left(); + } + + // apply right clip + if (destendx > cliprect.right()) + destendx = cliprect.right(); + + // compute final pixel in Y and exit if we are entirely clipped + s32 destendy = desty + height() - 1; + if (desty > cliprect.bottom() || destendy < cliprect.top()) + break; + + // apply top clip + s32 srcy = 0; + if (desty < cliprect.top()) + { + srcy = cliprect.top() - desty; + desty = cliprect.top(); + } + + // apply bottom clip + if (destendy > cliprect.bottom()) + destendy = cliprect.bottom(); + + // apply X flipping + if (flipx) + srcx = width() - 1 - srcx; + + // apply Y flipping + s32 dy = rowbytes(); + if (flipy) + { + srcy = height() - 1 - srcy; + dy = -dy; + } + + // fetch the source data + const u8 *srcdata = get_data(code); + + // compute how many blocks of 4 pixels we have + u32 numblocks = (destendx + 1 - destx) / 4; + u32 leftovers = (destendx + 1 - destx) - 4 * numblocks; + + // adjust srcdata to point to the first source pixel of the row + srcdata += srcy * rowbytes() + srcx; + + // non-flipped 8bpp case + if (!flipx) + { + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *priptr = &priority.pix(cury, destx); + auto *destptr = &dest.pix(cury, destx); + const u8 *srcptr = srcdata; + srcdata += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[0]); + pixel_op(destptr[1], priptr[1], srcptr[1]); + pixel_op(destptr[2], priptr[2], srcptr[2]); + pixel_op(destptr[3], priptr[3], srcptr[3]); + + srcptr += 4; + destptr += 4; + priptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[0]); + srcptr++; + destptr++; + priptr++; + } + } + } + + // flipped 8bpp case + else + { + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *priptr = &priority.pix(cury, destx); + auto *destptr = &dest.pix(cury, destx); + const u8 *srcptr = srcdata; + srcdata += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[ 0]); + pixel_op(destptr[1], priptr[1], srcptr[-1]); + pixel_op(destptr[2], priptr[2], srcptr[-2]); + pixel_op(destptr[3], priptr[3], srcptr[-3]); + + srcptr -= 4; + destptr += 4; + priptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[0]); + srcptr--; + destptr++; + priptr++; + } + } + } + } while (0); + g_profiler.stop(); +} + + + +/*************************************************************************** + BASIC DRAWGFXZOOM CORE +***************************************************************************/ + +/* + Input parameters: + + bitmap_t &dest - the bitmap to render to + const rectangle &cliprect - a clipping rectangle (assumed to be clipped to the size of 'dest') + gfx_element *gfx - pointer to the gfx_element to render + u32 code - index of the entry within gfx_element + int flipx - non-zero means render right-to-left instead of left-to-right + int flipy - non-zero means render bottom-to-top instead of top-to-bottom + s32 destx - the top-left X coordinate to render to + s32 desty - the top-left Y coordinate to render to + u32 scalex - the 16.16 scale factor in the X dimension + u32 scaley - the 16.16 scale factor in the Y dimension + bitmap_t &priority - the priority bitmap (if and only if priority is to be applied) +*/ + + +template <typename BitmapType, typename FunctionClass> +void gfx_element::drawgfxzoom_core(BitmapType &dest, const rectangle &cliprect, u32 code, int flipx, int flipy, s32 destx, s32 desty, u32 scalex, u32 scaley, FunctionClass pixel_op) +{ + g_profiler.start(PROFILER_DRAWGFX); + do { + assert(dest.valid()); + assert(dest.cliprect().contains(cliprect)); + + // ignore empty/invalid cliprects + if (cliprect.empty()) + break; + + // compute scaled size + u32 dstwidth = (scalex * width() + 0x8000) >> 16; + u32 dstheight = (scaley * height() + 0x8000) >> 16; + if (dstwidth < 1 || dstheight < 1) + break; + + // compute 16.16 source steps in dx and dy + s32 dx = (width() << 16) / dstwidth; + s32 dy = (height() << 16) / dstheight; + + // compute final pixel in X and exit if we are entirely clipped + s32 destendx = destx + dstwidth - 1; + if (destx > cliprect.right() || destendx < cliprect.left()) + break; + + // apply left clip + s32 srcx = 0; + if (destx < cliprect.left()) + { + srcx = (cliprect.left() - destx) * dx; + destx = cliprect.left(); + } + + // apply right clip + if (destendx > cliprect.right()) + destendx = cliprect.right(); + + // compute final pixel in Y and exit if we are entirely clipped + s32 destendy = desty + dstheight - 1; + if (desty > cliprect.bottom() || destendy < cliprect.top()) + { + g_profiler.stop(); + return; + } + + // apply top clip + s32 srcy = 0; + if (desty < cliprect.top()) + { + srcy = (cliprect.top() - desty) * dy; + desty = cliprect.top(); + } + + // apply bottom clip + if (destendy > cliprect.bottom()) + destendy = cliprect.bottom(); + + // apply X flipping + if (flipx) + { + srcx = (dstwidth - 1) * dx - srcx; + dx = -dx; + } + + // apply Y flipping + if (flipy) + { + srcy = (dstheight - 1) * dy - srcy; + dy = -dy; + } + + // fetch the source data + const u8 *srcdata = get_data(code); + + // compute how many blocks of 4 pixels we have + u32 numblocks = (destendx + 1 - destx) / 4; + u32 leftovers = (destendx + 1 - destx) - 4 * numblocks; + + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *destptr = &dest.pix(cury, destx); + const u8 *srcptr = srcdata + (srcy >> 16) * rowbytes(); + s32 cursrcx = srcx; + srcy += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], srcptr[cursrcx >> 16]); + cursrcx += dx; + pixel_op(destptr[1], srcptr[cursrcx >> 16]); + cursrcx += dx; + pixel_op(destptr[2], srcptr[cursrcx >> 16]); + cursrcx += dx; + pixel_op(destptr[3], srcptr[cursrcx >> 16]); + cursrcx += dx; + + destptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], srcptr[cursrcx >> 16]); + cursrcx += dx; + destptr++; + } + } + } while (0); + g_profiler.stop(); +} + + +template <typename BitmapType, typename PriorityType, typename FunctionClass> +void gfx_element::drawgfxzoom_core(BitmapType &dest, const rectangle &cliprect, u32 code, int flipx, int flipy, s32 destx, s32 desty, u32 scalex, u32 scaley, PriorityType &priority, FunctionClass pixel_op) +{ + g_profiler.start(PROFILER_DRAWGFX); + do { + assert(dest.valid()); + assert(priority.valid()); + assert(dest.cliprect().contains(cliprect)); + + // ignore empty/invalid cliprects + if (cliprect.empty()) + break; + + // compute scaled size + u32 dstwidth = (scalex * width() + 0x8000) >> 16; + u32 dstheight = (scaley * height() + 0x8000) >> 16; + if (dstwidth < 1 || dstheight < 1) + break; + + // compute 16.16 source steps in dx and dy + s32 dx = (width() << 16) / dstwidth; + s32 dy = (height() << 16) / dstheight; + + // compute final pixel in X and exit if we are entirely clipped + s32 destendx = destx + dstwidth - 1; + if (destx > cliprect.right() || destendx < cliprect.left()) + break; + + // apply left clip + s32 srcx = 0; + if (destx < cliprect.left()) + { + srcx = (cliprect.left() - destx) * dx; + destx = cliprect.left(); + } + + // apply right clip + if (destendx > cliprect.right()) + destendx = cliprect.right(); + + // compute final pixel in Y and exit if we are entirely clipped + s32 destendy = desty + dstheight - 1; + if (desty > cliprect.bottom() || destendy < cliprect.top()) + { + g_profiler.stop(); + return; + } + + // apply top clip + s32 srcy = 0; + if (desty < cliprect.top()) + { + srcy = (cliprect.top() - desty) * dy; + desty = cliprect.top(); + } + + // apply bottom clip + if (destendy > cliprect.bottom()) + destendy = cliprect.bottom(); + + // apply X flipping + if (flipx) + { + srcx = (dstwidth - 1) * dx - srcx; + dx = -dx; + } + + // apply Y flipping + if (flipy) + { + srcy = (dstheight - 1) * dy - srcy; + dy = -dy; + } + + // fetch the source data + const u8 *srcdata = get_data(code); + + // compute how many blocks of 4 pixels we have + u32 numblocks = (destendx + 1 - destx) / 4; + u32 leftovers = (destendx + 1 - destx) - 4 * numblocks; + + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *priptr = &priority.pix(cury, destx); + auto *destptr = &dest.pix(cury, destx); + const u8 *srcptr = srcdata + (srcy >> 16) * rowbytes(); + s32 cursrcx = srcx; + srcy += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[cursrcx >> 16]); + cursrcx += dx; + pixel_op(destptr[1], priptr[1], srcptr[cursrcx >> 16]); + cursrcx += dx; + pixel_op(destptr[2], priptr[2], srcptr[cursrcx >> 16]); + cursrcx += dx; + pixel_op(destptr[3], priptr[3], srcptr[cursrcx >> 16]); + cursrcx += dx; + + destptr += 4; + priptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[cursrcx >> 16]); + cursrcx += dx; + destptr++; + priptr++; + } + } + } while (0); + g_profiler.stop(); +} + + + +/*************************************************************************** + BASIC COPYBITMAP CORE +***************************************************************************/ + +/* + Input parameters: + + bitmap_t &dest - the bitmap to copy to + bitmap_t &src - the bitmap to copy from (must be same bpp as dest) + const rectangle &cliprect - a clipping rectangle (assumed to be clipped to the size of 'dest') + int flipx - non-zero means render right-to-left instead of left-to-right + int flipy - non-zero means render bottom-to-top instead of top-to-bottom + s32 destx - the top-left X coordinate to copy to + s32 desty - the top-left Y coordinate to copy to + bitmap_t &priority - the priority bitmap (if and only if priority is to be applied) +*/ + +template <typename BitmapType, typename FunctionClass> +void copybitmap_core(BitmapType &dest, const BitmapType &src, int flipx, int flipy, s32 destx, s32 desty, const rectangle &cliprect, FunctionClass pixel_op) +{ + g_profiler.start(PROFILER_COPYBITMAP); + do { + assert(dest.valid()); + assert(src.valid()); + assert(dest.cliprect().contains(cliprect)); + + // ignore empty/invalid cliprects + if (cliprect.empty()) + break; + + // standard setup; dx counts bytes in X, dy counts pixels in Y + s32 dx = 1; + s32 dy = src.rowpixels(); + + // compute final pixel in X and exit if we are entirely clipped + s32 destendx = destx + src.width() - 1; + if (destx > cliprect.right() || destendx < cliprect.left()) + break; + + // apply left clip + s32 srcx = 0; + if (destx < cliprect.left()) + { + srcx = cliprect.left() - destx; + destx = cliprect.left(); + } + + // apply right clip + if (destendx > cliprect.right()) + destendx = cliprect.right(); + + // compute final pixel in Y and exit if we are entirely clipped + s32 destendy = desty + src.height() - 1; + if (desty > cliprect.bottom() || destendy < cliprect.top()) + break; + + // apply top clip + s32 srcy = 0; + if (desty < cliprect.top()) + { + srcy = cliprect.top() - desty; + desty = cliprect.top(); + } + + // apply bottom clip + if (destendy > cliprect.bottom()) + destendy = cliprect.bottom(); + + // apply X flipping + if (flipx) + { + srcx = src.width() - 1 - srcx; + dx = -dx; + } + + // apply Y flipping + if (flipy) + { + srcy = src.height() - 1 - srcy; + dy = -dy; + } + + // compute how many blocks of 4 pixels we have + u32 numblocks = (destendx + 1 - destx) / 4; + u32 leftovers = (destendx + 1 - destx) - 4 * numblocks; + + // compute the address of the first source pixel of the first row + const auto *srcdata = &src.pix(srcy, srcx); + + // non-flipped case + if (!flipx) + { + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *destptr = &dest.pix(cury, destx); + const auto *srcptr = srcdata; + srcdata += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], srcptr[0]); + pixel_op(destptr[1], srcptr[1]); + pixel_op(destptr[2], srcptr[2]); + pixel_op(destptr[3], srcptr[3]); + + srcptr += 4; + destptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], srcptr[0]); + srcptr++; + destptr++; + } + } + } + + // flipped case + else + { + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *destptr = &dest.pix(cury, destx); + const auto *srcptr = srcdata; + srcdata += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], srcptr[ 0]); + pixel_op(destptr[1], srcptr[-1]); + pixel_op(destptr[2], srcptr[-2]); + pixel_op(destptr[3], srcptr[-3]); + + srcptr -= 4; + destptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], srcptr[0]); + srcptr--; + destptr++; + } + } + } + } while (0); + g_profiler.stop(); +} + + +template <typename BitmapType, typename PriorityType, typename FunctionClass> +void copybitmap_core(BitmapType &dest, const BitmapType &src, int flipx, int flipy, s32 destx, s32 desty, const rectangle &cliprect, PriorityType &priority, FunctionClass pixel_op) +{ + g_profiler.start(PROFILER_COPYBITMAP); + do { + assert(dest.valid()); + assert(src.valid()); + assert(priority.valid()); + assert(dest.cliprect().contains(cliprect)); + + // ignore empty/invalid cliprects + if (cliprect.empty()) + break; + + // standard setup; dx counts bytes in X, dy counts pixels in Y + s32 dx = 1; + s32 dy = src.rowpixels(); + + // compute final pixel in X and exit if we are entirely clipped + s32 destendx = destx + src.width() - 1; + if (destx > cliprect.right() || destendx < cliprect.left()) + break; + + // apply left clip + s32 srcx = 0; + if (destx < cliprect.left()) + { + srcx = cliprect.left() - destx; + destx = cliprect.left(); + } + + // apply right clip + if (destendx > cliprect.right()) + destendx = cliprect.right(); + + // compute final pixel in Y and exit if we are entirely clipped + s32 destendy = desty + src.height() - 1; + if (desty > cliprect.bottom() || destendy < cliprect.top()) + break; + + // apply top clip + s32 srcy = 0; + if (desty < cliprect.top()) + { + srcy = cliprect.top() - desty; + desty = cliprect.top(); + } + + // apply bottom clip + if (destendy > cliprect.bottom()) + destendy = cliprect.bottom(); + + // apply X flipping + if (flipx) + { + srcx = src.width() - 1 - srcx; + dx = -dx; + } + + // apply Y flipping + if (flipy) + { + srcy = src.height() - 1 - srcy; + dy = -dy; + } + + // compute how many blocks of 4 pixels we have + u32 numblocks = (destendx + 1 - destx) / 4; + u32 leftovers = (destendx + 1 - destx) - 4 * numblocks; + + // compute the address of the first source pixel of the first row + const auto *srcdata = &src.pix(srcy, srcx); + + // non-flipped case + if (!flipx) + { + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *priptr = &priority.pix(cury, destx); + auto *destptr = &dest.pix(cury, destx); + const auto *srcptr = srcdata; + srcdata += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[0]); + pixel_op(destptr[1], priptr[1], srcptr[1]); + pixel_op(destptr[2], priptr[2], srcptr[2]); + pixel_op(destptr[3], priptr[3], srcptr[3]); + + srcptr += 4; + destptr += 4; + priptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[0]); + srcptr++; + destptr++; + priptr++; + } + } + } + + // flipped case + else + { + // iterate over pixels in Y + for (s32 cury = desty; cury <= destendy; cury++) + { + auto *priptr = &priority.pix(cury, destx); + auto *destptr = &dest.pix(cury, destx); + const auto *srcptr = srcdata; + srcdata += dy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[ 0]); + pixel_op(destptr[1], priptr[1], srcptr[-1]); + pixel_op(destptr[2], priptr[2], srcptr[-2]); + pixel_op(destptr[3], priptr[3], srcptr[-3]); + + srcptr -= 4; + destptr += 4; + priptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[0]); + srcptr--; + destptr++; + priptr++; + } + } + } + } while (0); + g_profiler.stop(); +} + + + +/*************************************************************************** + BASIC COPYROZBITMAP CORE +***************************************************************************/ + +/* + Input parameters: + + bitmap_t &dest - the bitmap to copy to + bitmap_t &src - the bitmap to copy from (must be same bpp as dest) + const rectangle &cliprect - a clipping rectangle (assumed to be clipped to the size of 'dest') + s32 destx - the 16.16 source X position at destination pixel (0,0) + s32 desty - the 16.16 source Y position at destination pixel (0,0) + s32 incxx - the 16.16 amount to increment in source X for each destination X pixel + s32 incyx - the 16.16 amount to increment in source Y for each destination X pixel + s32 incxy - the 16.16 amount to increment in source X for each destination Y pixel + s32 incyy - the 16.16 amount to increment in source Y for each destination Y pixel + int wraparound - non-zero means wrap when hitting the edges of the source + bitmap_t &priority - the priority bitmap (if and only if priority is to be applied) +*/ + +template <typename BitmapType, typename FunctionClass> +void copyrozbitmap_core(BitmapType &dest, const rectangle &cliprect, const BitmapType &src, s32 startx, s32 starty, s32 incxx, s32 incxy, s32 incyx, s32 incyy, int wraparound, FunctionClass pixel_op) +{ + g_profiler.start(PROFILER_COPYBITMAP); + + assert(dest.valid()); + assert(dest.valid()); + assert(dest.cliprect().contains(cliprect)); + assert(!wraparound || (src.width() & (src.width() - 1)) == 0); + assert(!wraparound || (src.height() & (src.height() - 1)) == 0); + + // ignore empty/invalid cliprects + if (cliprect.empty()) + { + g_profiler.stop(); + return; + } + + // compute fixed-point 16.16 size of the source bitmap + u32 srcfixwidth = src.width() << 16; + u32 srcfixheight = src.height() << 16; + + // advance the starting coordinates to the top-left of the cliprect + startx += cliprect.left() * incxx + cliprect.top() * incyx; + starty += cliprect.left() * incxy + cliprect.top() * incyy; + + // compute how many blocks of 4 pixels we have + u32 numblocks = cliprect.width() / 4; + u32 leftovers = cliprect.width() - 4 * numblocks; + + // if incxy and incyx are 0, then we aren't rotating, just zooming + if (incxy == 0 && incyx == 0) + { + // zoom-only, non-wraparound case + if (!wraparound) + { + // iterate over pixels in Y + for (s32 cury = cliprect.top(); cury <= cliprect.bottom(); cury++) + { + auto *destptr = &dest.pix(cury, cliprect.left()); + s32 srcx = startx; + s32 srcy = starty; + + starty += incyy; + + // check srcy for the whole row at once + if (u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16); + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[0], srcptr[srcx >> 16]); + srcx += incxx; + + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[1], srcptr[srcx >> 16]); + srcx += incxx; + + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[2], srcptr[srcx >> 16]); + srcx += incxx; + + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[3], srcptr[srcx >> 16]); + srcx += incxx; + + destptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[0], srcptr[srcx >> 16]); + srcx += incxx; + destptr++; + } + } + } + } + + // zoom-only, wraparound case + else + { + // convert srcfixwidth/height into a mask and apply + srcfixwidth--; + srcfixheight--; + startx &= srcfixwidth; + starty &= srcfixheight; + + // iterate over pixels in Y + for (s32 cury = cliprect.top(); cury <= cliprect.bottom(); cury++) + { + auto *destptr = &dest.pix(cury, cliprect.left()); + const auto *srcptr = &src.pix(starty >> 16); + s32 srcx = startx; + + starty = (starty + incyy) & srcfixheight; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + + pixel_op(destptr[1], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + + pixel_op(destptr[2], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + + pixel_op(destptr[3], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + + destptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + destptr++; + } + } + } + } + + // full rotation case + else + { + // full rotation, non-wraparound case + if (!wraparound) + { + // iterate over pixels in Y + for (s32 cury = cliprect.top(); cury <= cliprect.bottom(); cury++) + { + auto *destptr = &dest.pix(cury, cliprect.left()); + s32 srcx = startx; + s32 srcy = starty; + + startx += incyx; + starty += incyy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[0], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[1], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[2], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[3], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + + destptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[0], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + destptr++; + } + } + } + + // zoom-only, wraparound case + else + { + // convert srcfixwidth/height into a mask and apply + srcfixwidth--; + srcfixheight--; + startx &= srcfixwidth; + starty &= srcfixheight; + + // iterate over pixels in Y + for (s32 cury = cliprect.top(); cury <= cliprect.bottom(); cury++) + { + auto *destptr = &dest.pix(cury, cliprect.left()); + s32 srcx = startx; + s32 srcy = starty; + + startx = (startx + incyx) & srcfixwidth; + starty = (starty + incyy) & srcfixheight; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[0], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + + srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[1], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + + srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[2], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + + srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[3], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + + destptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[0], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + destptr++; + } + } + } + } + g_profiler.stop(); +} + + +template <typename BitmapType, typename PriorityType, typename FunctionClass> +void copyrozbitmap_core(BitmapType &dest, const rectangle &cliprect, const BitmapType &src, s32 startx, s32 starty, s32 incxx, s32 incxy, s32 incyx, s32 incyy, int wraparound, PriorityType &priority, FunctionClass pixel_op) +{ + g_profiler.start(PROFILER_COPYBITMAP); + + assert(dest.valid()); + assert(dest.valid()); + assert(priority.valid()); + assert(dest.cliprect().contains(cliprect)); + assert(!wraparound || (src.width() & (src.width() - 1)) == 0); + assert(!wraparound || (src.height() & (src.height() - 1)) == 0); + + // ignore empty/invalid cliprects + if (cliprect.empty()) + { + g_profiler.stop(); + return; + } + + // compute fixed-point 16.16 size of the source bitmap + u32 srcfixwidth = src.width() << 16; + u32 srcfixheight = src.height() << 16; + + // advance the starting coordinates to the top-left of the cliprect + startx += cliprect.left() * incxx + cliprect.top() * incyx; + starty += cliprect.left() * incxy + cliprect.top() * incyy; + + // compute how many blocks of 4 pixels we have + u32 numblocks = cliprect.width() / 4; + u32 leftovers = cliprect.width() - 4 * numblocks; + + // if incxy and incyx are 0, then we aren't rotating, just zooming + if (incxy == 0 && incyx == 0) + { + // zoom-only, non-wraparound case + if (!wraparound) + { + // iterate over pixels in Y + for (s32 cury = cliprect.top(); cury <= cliprect.bottom(); cury++) + { + auto *priptr = &priority.pix(cury, cliprect.left()); + auto *destptr = &dest.pix(cury, cliprect.left()); + s32 srcx = startx; + s32 srcy = starty; + + starty += incyy; + + // check srcy for the whole row at once + if (u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16); + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[0], priptr[0], srcptr[srcx >> 16]); + srcx += incxx; + + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[1], priptr[1], srcptr[srcx >> 16]); + srcx += incxx; + + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[2], priptr[2], srcptr[srcx >> 16]); + srcx += incxx; + + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[3], priptr[3], srcptr[srcx >> 16]); + srcx += incxx; + + destptr += 4; + priptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + if (u32(srcx) < srcfixwidth) + pixel_op(destptr[0], priptr[0], srcptr[srcx >> 16]); + srcx += incxx; + destptr++; + priptr++; + } + } + } + } + + // zoom-only, wraparound case + else + { + // convert srcfixwidth/height into a mask and apply + srcfixwidth--; + srcfixheight--; + startx &= srcfixwidth; + starty &= srcfixheight; + + // iterate over pixels in Y + for (s32 cury = cliprect.top(); cury <= cliprect.bottom(); cury++) + { + auto *priptr = &priority.pix(cury, cliprect.left()); + auto *destptr = &dest.pix(cury, cliprect.left()); + const auto *srcptr = &src.pix(starty >> 16); + s32 srcx = startx; + + starty = (starty + incyy) & srcfixheight; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + + pixel_op(destptr[1], priptr[1], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + + pixel_op(destptr[2], priptr[2], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + + pixel_op(destptr[3], priptr[3], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + + destptr += 4; + priptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + pixel_op(destptr[0], priptr[0], srcptr[srcx >> 16]); + srcx = (srcx + incxx) & srcfixwidth; + destptr++; + priptr++; + } + } + } + } + + // full rotation case + else + { + // full rotation, non-wraparound case + if (!wraparound) + { + // iterate over pixels in Y + for (s32 cury = cliprect.top(); cury <= cliprect.bottom(); cury++) + { + auto *priptr = &priority.pix(cury, cliprect.left()); + auto *destptr = &dest.pix(cury, cliprect.left()); + s32 srcx = startx; + s32 srcy = starty; + + startx += incyx; + starty += incyy; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[0], priptr[0], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[1], priptr[1], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[2], priptr[2], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[3], priptr[3], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + + destptr += 4; + priptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[0], priptr[0], srcptr[0]); + } + srcx += incxx; + srcy += incxy; + destptr++; + priptr++; + } + } + } + + // zoom-only, wraparound case + else + { + // convert srcfixwidth/height into a mask and apply + srcfixwidth--; + srcfixheight--; + startx &= srcfixwidth; + starty &= srcfixheight; + + // iterate over pixels in Y + for (s32 cury = cliprect.top(); cury <= cliprect.bottom(); cury++) + { + auto *priptr = &priority.pix(cury, cliprect.left()); + auto *destptr = &dest.pix(cury, cliprect.left()); + s32 srcx = startx; + s32 srcy = starty; + + startx = (startx + incyx) & srcfixwidth; + starty = (starty + incyy) & srcfixheight; + + // iterate over unrolled blocks of 4 + for (s32 curx = 0; curx < numblocks; curx++) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[0], priptr[0], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + + srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[1], priptr[1], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + + srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[2], priptr[2], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + + srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[3], priptr[3], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + + destptr += 4; + priptr += 4; + } + + // iterate over leftover pixels + for (s32 curx = 0; curx < leftovers; curx++) + { + const auto *srcptr = &src.pix(srcy >> 16, srcx >> 16); + pixel_op(destptr[0], priptr[0], srcptr[0]); + srcx = (srcx + incxx) & srcfixwidth; + srcy = (srcy + incxy) & srcfixheight; + destptr++; + priptr++; + } + } + } + } + g_profiler.stop(); +} + + + +/*************************************************************************** + BASIC DRAWSCANLINE CORE +***************************************************************************/ + +/* + Input parameters: + + bitmap_t &bitmap - the bitmap to copy to + s32 destx - the X coordinate to copy to + s32 desty - the Y coordinate to copy to + s32 length - the total number of pixels to copy + const UINTx *srcptr - pointer to memory containing the source pixels + bitmap_t &priority - the priority bitmap (if and only if priority is to be applied) +*/ + +template <typename BitmapType, typename SourceType, typename FunctionClass> +void drawscanline_core(BitmapType &bitmap, s32 destx, s32 desty, s32 length, const SourceType *srcptr, FunctionClass pixel_op) +{ + assert(bitmap.valid()); + assert(destx >= 0); + assert(destx + length <= bitmap.width()); + assert(desty >= 0); + assert(desty < bitmap.height()); + assert(srcptr != nullptr); + + auto *destptr = &bitmap.pix(desty, destx); + + // iterate over unrolled blocks of 4 + while (length >= 4) + { + pixel_op(destptr[0], srcptr[0]); + pixel_op(destptr[1], srcptr[1]); + pixel_op(destptr[2], srcptr[2]); + pixel_op(destptr[3], srcptr[3]); + + length -= 4; + srcptr += 4; + destptr += 4; + } + + // iterate over leftover pixels + while (length-- > 0) + { + pixel_op(destptr[0], srcptr[0]); + srcptr++; + destptr++; + } +} + + +template <typename BitmapType, typename SourceType, typename PriorityType, typename FunctionClass> +void drawscanline_core(BitmapType &bitmap, s32 destx, s32 desty, s32 length, const SourceType *srcptr, PriorityType &priority, FunctionClass pixel_op) +{ + assert(bitmap.valid()); + assert(destx >= 0); + assert(destx + length <= bitmap.width()); + assert(desty >= 0); + assert(desty < bitmap.height()); + assert(srcptr != nullptr); + assert(priority.valid()); + + auto *priptr = &priority.pix(desty, destx); + auto *destptr = &bitmap.pix(desty, destx); + + // iterate over unrolled blocks of 4 + while (length >= 4) + { + pixel_op(destptr[0], priptr[0], srcptr[0]); + pixel_op(destptr[1], priptr[1], srcptr[1]); + pixel_op(destptr[2], priptr[2], srcptr[2]); + pixel_op(destptr[3], priptr[3], srcptr[3]); + + length -= 4; + srcptr += 4; + destptr += 4; + priptr += 4; + + // iterate over leftover pixels + while (length-- > 0) + { + pixel_op(destptr[0], priptr[0], srcptr[0]); + srcptr++; + destptr++; + priptr++; + } + } +} + + +#endif // MAME_EMU_DRAWGFXT_IPP |