mame/src/emu/drawgfxm.h
AJR 5fba45b0bc v9938, v9958: Convert to use RGB32 bitmaps
- Palette has been retained mostly for the sake of the palette viewer, and now reflects the actual programmed values, rather than being a fixed RRRGGGBBB encoding plus a hacky mess for the V9958's YJK colors.
- V9938-on-V9938 transparent overlay is fixed for meritm.cpp (was broken a few releases ago).
2017-11-05 21:55:34 -05:00

1276 lines
79 KiB
C

// license:BSD-3-Clause
// copyright-holders:Nicola Salmoria, Aaron Giles
/*********************************************************************
drawgfxm.h
Macros implementing drawgfx core operations. Drivers can use
these if they need custom behavior not provided by the existing
drawgfx functions.
**********************************************************************
How to use these macros:
There are two sets of macros. The PIXEL_OP* macros are simple
per-pixel operations, designed to take a SOURCE pixel and
copy it to the DEST, perhaps updating the PRIORITY pixel as
well. On their own, they are not particularly useful.
The second set of macros represents the core gfx/bitmap walking
and rendering code. These macros generally take the target pixel
type (u8, u16, u32), one of the PIXEL_OP* macros,
and a priority bitmap pixel type (u8, u16, u32, or the
special type NO_PRIORITY).
Although the code may look inefficient at first, the compiler is
able to easily optimize out unused cases due to the way the
macros are written, leaving behind just the cases we are
interested in.
The general approach for using these macros is:
my_drawing_function(params)
{
// ensure that all the required parameters for the mega
// macro are defined (if they are not needed, just declare
// the variables and set them equal to a known constant
// value to help the compiler)
// set up any additional variables needed by the PIXEL_OP*
// macro you want to use (each macro has its own
// requirements)
MEGA_MACRO(BITMAP_TYPE, PIXEL_OP, PRIORITY_TYPE);
}
*********************************************************************/
#pragma once
#ifndef __DRAWGFXM_H__
#define __DRAWGFXM_H__
/* special priority type meaning "none" */
struct NO_PRIORITY { char dummy[3]; };
extern bitmap_ind8 drawgfx_dummy_priority_bitmap;
#define DECLARE_NO_PRIORITY bitmap_t &priority = drawgfx_dummy_priority_bitmap;
/* macros for using the optional priority */
#define PRIORITY_VALID(x) (sizeof(x) != sizeof(NO_PRIORITY))
#define PRIORITY_ADDR(p,t,y,x) (PRIORITY_VALID(t) ? (&(p).pixt<t>(y, x)) : nullptr)
#define PRIORITY_ADVANCE(t,p,i) do { if (PRIORITY_VALID(t)) (p) += (i); } while (0)
/***************************************************************************
PIXEL OPERATIONS
***************************************************************************/
/*-------------------------------------------------
PIXEL_OP_COPY_OPAQUE - render all pixels
regardless of pen, copying directly
-------------------------------------------------*/
#define PIXEL_OP_COPY_OPAQUE(DEST, PRIORITY, SOURCE) \
do \
{ \
(DEST) = SOURCE; \
} \
while (0)
/*-------------------------------------------------
PIXEL_OP_COPY_TRANSPEN - render all pixels
except those matching 'transpen', copying
directly
-------------------------------------------------*/
#define PIXEL_OP_COPY_TRANSPEN(DEST, PRIORITY, SOURCE) \
do \
{ \
u32 srcdata = (SOURCE); \
if (srcdata != trans_pen) \
(DEST) = SOURCE; \
} \
while (0)
/*-------------------------------------------------
PIXEL_OP_COPY_TRANSALPHA - render all pixels
except those with an alpha of zero, copying
directly
-------------------------------------------------*/
#define PIXEL_OP_COPY_TRANSALPHA(DEST, PRIORITY, SOURCE) \
do \
{ \
u32 srcdata = (SOURCE); \
if ((srcdata & 0xff000000) != 0) \
(DEST) = SOURCE; \
} \
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, PRIORITY, 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)
/*-------------------------------------------------
PIXEL_OP_REBASE_OPAQUE - render all pixels
regardless of pen, adding 'color' to the
pen value
-------------------------------------------------*/
#define PIXEL_OP_REBASE_OPAQUE(DEST, PRIORITY, 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, PRIORITY, 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, PRIORITY, 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, PRIORITY, 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, PRIORITY, 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, PRIORITY, 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, PRIORITY, 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, PRIORITY, 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
***************************************************************************/
/*
Assumed input parameters or local variables:
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
u32 color - index of the color 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 (even if PRIORITY_TYPE is NO_PRIORITY, at least needs a dummy)
*/
#define DRAWGFX_CORE(PIXEL_TYPE, PIXEL_OP, PRIORITY_TYPE) \
do { \
g_profiler.start(PROFILER_DRAWGFX); \
do { \
const u8 *srcdata; \
s32 destendx, destendy; \
s32 srcx, srcy; \
s32 curx, cury; \
s32 dy; \
\
assert(dest.valid()); \
assert(!PRIORITY_VALID(PRIORITY_TYPE) || 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 */ \
destendx = destx + width() - 1; \
if (destx > cliprect.max_x || destendx < cliprect.min_x) \
break; \
\
/* apply left clip */ \
srcx = 0; \
if (destx < cliprect.min_x) \
{ \
srcx = cliprect.min_x - destx; \
destx = cliprect.min_x; \
} \
\
/* apply right clip */ \
if (destendx > cliprect.max_x) \
destendx = cliprect.max_x; \
\
/* compute final pixel in Y and exit if we are entirely clipped */ \
destendy = desty + height() - 1; \
if (desty > cliprect.max_y || destendy < cliprect.min_y) \
break; \
\
/* apply top clip */ \
srcy = 0; \
if (desty < cliprect.min_y) \
{ \
srcy = cliprect.min_y - desty; \
desty = cliprect.min_y; \
} \
\
/* apply bottom clip */ \
if (destendy > cliprect.max_y) \
destendy = cliprect.max_y; \
\
/* apply X flipping */ \
if (flipx) \
srcx = width() - 1 - srcx; \
\
/* apply Y flipping */ \
dy = rowbytes(); \
if (flipy) \
{ \
srcy = height() - 1 - srcy; \
dy = -dy; \
} \
\
/* fetch the source data */ \
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 (cury = desty; cury <= destendy; cury++) \
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, cury, destx); \
PIXEL_TYPE *destptr = &dest.pixt<PIXEL_TYPE>(cury, destx); \
const u8 *srcptr = srcdata; \
srcdata += dy; \
\
/* iterate over unrolled blocks of 4 */ \
for (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; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
for (curx = 0; curx < leftovers; curx++) \
{ \
PIXEL_OP(destptr[0], priptr[0], srcptr[0]); \
srcptr++; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} \
\
/* flipped 8bpp case */ \
else \
{ \
/* iterate over pixels in Y */ \
for (cury = desty; cury <= destendy; cury++) \
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, cury, destx); \
PIXEL_TYPE *destptr = &dest.pixt<PIXEL_TYPE>(cury, destx); \
const u8 *srcptr = srcdata; \
srcdata += dy; \
\
/* iterate over unrolled blocks of 4 */ \
for (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; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
for (curx = 0; curx < leftovers; curx++) \
{ \
PIXEL_OP(destptr[0], priptr[0], srcptr[0]); \
srcptr--; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} \
} while (0); \
g_profiler.stop(); \
} while (0)
/***************************************************************************
BASIC DRAWGFXZOOM CORE
***************************************************************************/
/*
Assumed input parameters or local variables:
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
u32 color - index of the color 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 (even if PRIORITY_TYPE is NO_PRIORITY, at least needs a dummy)
*/
#define DRAWGFXZOOM_CORE(PIXEL_TYPE, PIXEL_OP, PRIORITY_TYPE) \
do { \
g_profiler.start(PROFILER_DRAWGFX); \
do { \
const u8 *srcdata; \
u32 dstwidth, dstheight; \
s32 destendx, destendy; \
s32 srcx, srcy; \
s32 curx, cury; \
s32 dx, dy; \
\
assert(dest.valid()); \
assert(!PRIORITY_VALID(PRIORITY_TYPE) || priority.valid()); \
assert(dest.cliprect().contains(cliprect)); \
\
/* ignore empty/invalid cliprects */ \
if (cliprect.empty()) \
break; \
\
/* compute scaled size */ \
dstwidth = (scalex * width() + 0x8000) >> 16; \
dstheight = (scaley * height() + 0x8000) >> 16; \
if (dstwidth < 1 || dstheight < 1) \
break; \
\
/* compute 16.16 source steps in dx and dy */ \
dx = (width() << 16) / dstwidth; \
dy = (height() << 16) / dstheight; \
\
/* compute final pixel in X and exit if we are entirely clipped */ \
destendx = destx + dstwidth - 1; \
if (destx > cliprect.max_x || destendx < cliprect.min_x) \
break; \
\
/* apply left clip */ \
srcx = 0; \
if (destx < cliprect.min_x) \
{ \
srcx = (cliprect.min_x - destx) * dx; \
destx = cliprect.min_x; \
} \
\
/* apply right clip */ \
if (destendx > cliprect.max_x) \
destendx = cliprect.max_x; \
\
/* compute final pixel in Y and exit if we are entirely clipped */ \
destendy = desty + dstheight - 1; \
if (desty > cliprect.max_y || destendy < cliprect.min_y) \
{ \
g_profiler.stop(); \
return; \
} \
\
/* apply top clip */ \
srcy = 0; \
if (desty < cliprect.min_y) \
{ \
srcy = (cliprect.min_y - desty) * dy; \
desty = cliprect.min_y; \
} \
\
/* apply bottom clip */ \
if (destendy > cliprect.max_y) \
destendy = cliprect.max_y; \
\
/* 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 */ \
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 (cury = desty; cury <= destendy; cury++) \
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, cury, destx); \
PIXEL_TYPE *destptr = &dest.pixt<PIXEL_TYPE>(cury, destx); \
const u8 *srcptr = srcdata + (srcy >> 16) * rowbytes(); \
s32 cursrcx = srcx; \
srcy += dy; \
\
/* iterate over unrolled blocks of 4 */ \
for (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; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
for (curx = 0; curx < leftovers; curx++) \
{ \
PIXEL_OP(destptr[0], priptr[0], srcptr[cursrcx >> 16]); \
cursrcx += dx; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} while (0); \
g_profiler.stop(); \
} while (0)
/***************************************************************************
BASIC COPYBITMAP CORE
***************************************************************************/
/*
Assumed input parameters or local variables:
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 (even if PRIORITY_TYPE is NO_PRIORITY, at least needs a dummy)
*/
#define COPYBITMAP_CORE(PIXEL_TYPE, PIXEL_OP, PRIORITY_TYPE) \
do { \
g_profiler.start(PROFILER_COPYBITMAP); \
do { \
const PIXEL_TYPE *srcdata; \
u32 numblocks, leftovers; \
s32 destendx, destendy; \
s32 srcx, srcy; \
s32 curx, cury; \
s32 dx, dy; \
\
assert(dest.valid()); \
assert(src.valid()); \
assert(!PRIORITY_VALID(PRIORITY_TYPE) || 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 */ \
dx = 1; \
dy = src.rowpixels(); \
\
/* compute final pixel in X and exit if we are entirely clipped */ \
destendx = destx + src.width() - 1; \
if (destx > cliprect.max_x || destendx < cliprect.min_x) \
break; \
\
/* apply left clip */ \
srcx = 0; \
if (destx < cliprect.min_x) \
{ \
srcx = cliprect.min_x - destx; \
destx = cliprect.min_x; \
} \
\
/* apply right clip */ \
if (destendx > cliprect.max_x) \
destendx = cliprect.max_x; \
\
/* compute final pixel in Y and exit if we are entirely clipped */ \
destendy = desty + src.height() - 1; \
if (desty > cliprect.max_y || destendy < cliprect.min_y) \
break; \
\
/* apply top clip */ \
srcy = 0; \
if (desty < cliprect.min_y) \
{ \
srcy = cliprect.min_y - desty; \
desty = cliprect.min_y; \
} \
\
/* apply bottom clip */ \
if (destendy > cliprect.max_y) \
destendy = cliprect.max_y; \
\
/* 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 */ \
numblocks = (destendx + 1 - destx) / 4; \
leftovers = (destendx + 1 - destx) - 4 * numblocks; \
\
/* compute the address of the first source pixel of the first row */ \
srcdata = &src.pixt<PIXEL_TYPE>(srcy, srcx); \
\
/* non-flipped case */ \
if (!flipx) \
{ \
/* iterate over pixels in Y */ \
for (cury = desty; cury <= destendy; cury++) \
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, cury, destx); \
PIXEL_TYPE *destptr = &dest.pixt<PIXEL_TYPE>(cury, destx); \
const PIXEL_TYPE *srcptr = srcdata; \
srcdata += dy; \
\
/* iterate over unrolled blocks of 4 */ \
for (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; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
for (curx = 0; curx < leftovers; curx++) \
{ \
PIXEL_OP(destptr[0], priptr[0], srcptr[0]); \
srcptr++; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} \
\
/* flipped case */ \
else \
{ \
/* iterate over pixels in Y */ \
for (cury = desty; cury <= destendy; cury++) \
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, cury, destx); \
PIXEL_TYPE *destptr = &dest.pixt<PIXEL_TYPE>(cury, destx); \
const PIXEL_TYPE *srcptr = srcdata; \
srcdata += dy; \
\
/* iterate over unrolled blocks of 4 */ \
for (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; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
for (curx = 0; curx < leftovers; curx++) \
{ \
PIXEL_OP(destptr[0], priptr[0], srcptr[0]); \
srcptr--; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} \
} while (0); \
g_profiler.stop(); \
} while (0)
/***************************************************************************
BASIC COPYROZBITMAP CORE
***************************************************************************/
/*
Assumed input parameters or local variables:
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 (even if PRIORITY_TYPE is NO_PRIORITY, at least needs a dummy)
*/
#define COPYROZBITMAP_CORE(PIXEL_TYPE, PIXEL_OP, PRIORITY_TYPE) \
do { \
u32 srcfixwidth, srcfixheight; \
u32 numblocks, leftovers; \
s32 curx, cury; \
\
g_profiler.start(PROFILER_COPYBITMAP); \
\
assert(dest.valid()); \
assert(dest.valid()); \
assert(!PRIORITY_VALID(PRIORITY_TYPE) || 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()) \
break; \
\
/* compute fixed-point 16.16 size of the source bitmap */ \
srcfixwidth = src.width() << 16; \
srcfixheight = src.height() << 16; \
\
/* advance the starting coordinates to the top-left of the cliprect */ \
startx += cliprect.min_x * incxx + cliprect.min_y * incyx; \
starty += cliprect.min_x * incxy + cliprect.min_y * incyy; \
\
/* compute how many blocks of 4 pixels we have */ \
numblocks = cliprect.width() / 4; \
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 (cury = cliprect.min_y; cury <= cliprect.max_y; cury++) \
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, cury, cliprect.min_x); \
PIXEL_TYPE *destptr = &dest.pixt<PIXEL_TYPE>(cury, cliprect.min_x); \
const PIXEL_TYPE *srcptr; \
s32 srcx = startx; \
s32 srcy = starty; \
\
starty += incyy; \
\
/* check srcy for the whole row at once */ \
if ((u32)srcy < srcfixheight) \
{ \
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16); \
\
/* iterate over unrolled blocks of 4 */ \
for (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; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
for (curx = 0; curx < leftovers; curx++) \
{ \
if (u32(srcx) < srcfixwidth) \
PIXEL_OP(destptr[0], priptr[0], srcptr[srcx >> 16]); \
srcx += incxx; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} \
} \
\
/* 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 (cury = cliprect.min_y; cury <= cliprect.max_y; cury++) \
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, cury, cliprect.min_x); \
PIXEL_TYPE *destptr = &dest.pixt<PIXEL_TYPE>(cury, cliprect.min_x); \
const PIXEL_TYPE *srcptr = &src.pixt<PIXEL_TYPE>(starty >> 16); \
s32 srcx = startx; \
\
starty = (starty + incyy) & srcfixheight; \
\
/* iterate over unrolled blocks of 4 */ \
for (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; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
for (curx = 0; curx < leftovers; curx++) \
{ \
PIXEL_OP(destptr[0], priptr[0], srcptr[srcx >> 16]); \
srcx = (srcx + incxx) & srcfixwidth; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} \
} \
\
/* full rotation case */ \
else \
{ \
/* full rotation, non-wraparound case */ \
if (!wraparound) \
{ \
/* iterate over pixels in Y */ \
for (cury = cliprect.min_y; cury <= cliprect.max_y; cury++) \
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, cury, cliprect.min_x); \
PIXEL_TYPE *destptr = &dest.pixt<PIXEL_TYPE>(cury, cliprect.min_x); \
const PIXEL_TYPE *srcptr; \
s32 srcx = startx; \
s32 srcy = starty; \
\
startx += incyx; \
starty += incyy; \
\
/* iterate over unrolled blocks of 4 */ \
for (curx = 0; curx < numblocks; curx++) \
{ \
if (u32(srcx) < srcfixwidth && (u32)srcy < srcfixheight) \
{ \
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[0], priptr[0], srcptr[0]); \
} \
srcx += incxx; \
srcy += incxy; \
\
if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) \
{ \
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[1], priptr[1], srcptr[0]); \
} \
srcx += incxx; \
srcy += incxy; \
\
if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) \
{ \
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[2], priptr[2], srcptr[0]); \
} \
srcx += incxx; \
srcy += incxy; \
\
if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) \
{ \
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[3], priptr[3], srcptr[0]); \
} \
srcx += incxx; \
srcy += incxy; \
\
destptr += 4; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
for (curx = 0; curx < leftovers; curx++) \
{ \
if (u32(srcx) < srcfixwidth && u32(srcy) < srcfixheight) \
{ \
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[0], priptr[0], srcptr[0]); \
} \
srcx += incxx; \
srcy += incxy; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} \
\
/* 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 (cury = cliprect.min_y; cury <= cliprect.max_y; cury++) \
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, cury, cliprect.min_x); \
PIXEL_TYPE *destptr = &dest.pixt<PIXEL_TYPE>(cury, cliprect.min_x); \
const PIXEL_TYPE *srcptr; \
s32 srcx = startx; \
s32 srcy = starty; \
\
startx = (startx + incyx) & srcfixwidth; \
starty = (starty + incyy) & srcfixheight; \
\
/* iterate over unrolled blocks of 4 */ \
for (curx = 0; curx < numblocks; curx++) \
{ \
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[0], priptr[0], srcptr[0]); \
srcx = (srcx + incxx) & srcfixwidth; \
srcy = (srcy + incxy) & srcfixheight; \
\
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[1], priptr[1], srcptr[0]); \
srcx = (srcx + incxx) & srcfixwidth; \
srcy = (srcy + incxy) & srcfixheight; \
\
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[2], priptr[2], srcptr[0]); \
srcx = (srcx + incxx) & srcfixwidth; \
srcy = (srcy + incxy) & srcfixheight; \
\
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[3], priptr[3], srcptr[0]); \
srcx = (srcx + incxx) & srcfixwidth; \
srcy = (srcy + incxy) & srcfixheight; \
\
destptr += 4; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
for (curx = 0; curx < leftovers; curx++) \
{ \
srcptr = &src.pixt<PIXEL_TYPE>(srcy >> 16, srcx >> 16); \
PIXEL_OP(destptr[0], priptr[0], srcptr[0]); \
srcx = (srcx + incxx) & srcfixwidth; \
srcy = (srcy + incxy) & srcfixheight; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} \
} \
g_profiler.stop(); \
} while (0)
/***************************************************************************
BASIC DRAWSCANLINE CORE
***************************************************************************/
/*
Assumed input parameters or local variables:
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 (even if PRIORITY_TYPE is NO_PRIORITY, at least needs a dummy)
*/
#define DRAWSCANLINE_CORE(PIXEL_TYPE, PIXEL_OP, PRIORITY_TYPE) \
do { \
assert(bitmap.valid()); \
assert(destx >= 0); \
assert(destx + length <= bitmap.width()); \
assert(desty >= 0); \
assert(desty < bitmap.height()); \
assert(srcptr != nullptr); \
assert(!PRIORITY_VALID(PRIORITY_TYPE) || priority.valid()); \
\
{ \
PRIORITY_TYPE *priptr = PRIORITY_ADDR(priority, PRIORITY_TYPE, desty, destx); \
PIXEL_TYPE *destptr = &bitmap.pixt<PIXEL_TYPE>(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; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 4); \
} \
\
/* iterate over leftover pixels */ \
while (length-- > 0) \
{ \
PIXEL_OP(destptr[0], priptr[0], srcptr[0]); \
srcptr++; \
destptr++; \
PRIORITY_ADVANCE(PRIORITY_TYPE, priptr, 1); \
} \
} \
} while (0)
/***************************************************************************
BASIC EXTRACTSCANLINE CORE
***************************************************************************/
/*
Assumed input parameters:
bitmap_t &bitmap - the bitmap to extract from
s32 srcx - the X coordinate to begin extraction
s32 srcy - the Y coordinate to begin extraction
s32 length - the total number of pixels to extract
UINTx *destptr - pointer to memory to receive the extracted pixels
*/
#define EXTRACTSCANLINE_CORE(PIXEL_TYPE) \
do { \
assert(bitmap.valid()); \
assert(srcx >= 0); \
assert(srcx + length <= bitmap.width()); \
assert(srcy >= 0); \
assert(srcy < bitmap.height()); \
assert(destptr != nullptr); \
\
{ \
const PIXEL_TYPE *srcptr = &bitmap.pixt<PIXEL_TYPE>(srcy, srcx); \
\
/* iterate over unrolled blocks of 4 */ \
while (length >= 4) \
{ \
destptr[0] = srcptr[0]; \
destptr[1] = srcptr[1]; \
destptr[2] = srcptr[2]; \
destptr[3] = srcptr[3]; \
length -= 4; \
srcptr += 4; \
destptr += 4; \
} \
\
/* iterate over leftover pixels */ \
while (length > 0) \
{ \
destptr[0] = srcptr[0]; \
length--; \
srcptr++; \
destptr++; \
} \
} \
} while (0)
#endif /* __DRAWGFXM_H__ */