static void draw_scanline_normal(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid)
{
	const poly_extra_data *extra = (const poly_extra_data *)extradata;
	const cached_texture *texture = extra->texture;
	bitmap_t *destmap = (bitmap_t *)dest;
	UINT16 *p = BITMAP_ADDR16(destmap, scanline, 0);
	UINT32 *d = BITMAP_ADDR32(extra->zbuffer, scanline, 0);
	float ooz = extent->param[0].start;
	float uoz = extent->param[1].start;
	float voz = extent->param[2].start;
	float doozdx = extent->param[0].dpdx;
	float duozdx = extent->param[1].dpdx;
	float dvozdx = extent->param[2].dpdx;
	UINT32 polyi = extra->polygon_intensity;
	UINT32 umask = (((extra->texture_param & TRI_PARAM_TEXTURE_MIRROR_U) ? 64 : 32) << texture->width) - 1;
	UINT32 vmask = (((extra->texture_param & TRI_PARAM_TEXTURE_MIRROR_V) ? 64 : 32) << texture->height) - 1;
	UINT32 width = 6 + texture->width;
	int x;

	for (x = extent->startx; x < extent->stopx; x++)
	{
		UINT32 iz = ooz * 256.0f;
		if (iz > d[x])
		{
			float z = 1.0f / ooz;
			UINT32 u = uoz * z;
			UINT32 v = voz * z;
			UINT32 u1 = (u >> 8) & umask;
			UINT32 v1 = (v >> 8) & vmask;
			UINT32 u2 = (u1 + 1) & umask;
			UINT32 v2 = (v1 + 1) & vmask;
			UINT32 pix00 = texture->data[(v1 << width) + u1];
			UINT32 pix01 = texture->data[(v1 << width) + u2];
			UINT32 pix10 = texture->data[(v2 << width) + u1];
			UINT32 pix11 = texture->data[(v2 << width) + u2];
			UINT32 texel = rgba_bilinear_filter(pix00, pix01, pix10, pix11, u, v);
			UINT32 fr = ((texel & 0x00ff0000) * polyi) >> (8+9);
			UINT32 fg = ((texel & 0x0000ff00) * polyi) >> (8+6);
			UINT32 fb = ((texel & 0x000000ff) * polyi) >> (8+3);
			p[x] = (fr & 0x7c00) | (fg & 0x3e0) | (fb & 0x1f);
			d[x] = iz;
		}

		ooz += doozdx;
		uoz += duozdx;
		voz += dvozdx;
	}
}

static void draw_scanline_trans(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid)
{
	const poly_extra_data *extra = (const poly_extra_data *)extradata;
	const cached_texture *texture = extra->texture;
	bitmap_t *destmap = (bitmap_t *)dest;
	UINT16 *p = BITMAP_ADDR16(destmap, scanline, 0);
	UINT32 *d = BITMAP_ADDR32(extra->zbuffer, scanline, 0);
	float ooz = extent->param[0].start;
	float uoz = extent->param[1].start;
	float voz = extent->param[2].start;
	float doozdx = extent->param[0].dpdx;
	float duozdx = extent->param[1].dpdx;
	float dvozdx = extent->param[2].dpdx;
	UINT32 polyi = (extra->polygon_intensity * extra->polygon_transparency) >> 5;
	int desttrans = 32 - extra->polygon_transparency;
	UINT32 umask = (((extra->texture_param & TRI_PARAM_TEXTURE_MIRROR_U) ? 64 : 32) << texture->width) - 1;
	UINT32 vmask = (((extra->texture_param & TRI_PARAM_TEXTURE_MIRROR_V) ? 64 : 32) << texture->height) - 1;
	UINT32 width = 6 + texture->width;
	int x;

	for (x = extent->startx; x < extent->stopx; x++)
	{
		UINT32 iz = ooz * 256.0f;
		if (iz > d[x])
		{
			float z = 1.0f / ooz;
			UINT32 u = uoz * z;
			UINT32 v = voz * z;
			UINT32 u1 = (u >> 8) & umask;
			UINT32 v1 = (v >> 8) & vmask;
			UINT32 u2 = (u1 + 1) & umask;
			UINT32 v2 = (v1 + 1) & vmask;
			UINT32 pix00 = texture->data[(v1 << width) + u1];
			UINT32 pix01 = texture->data[(v1 << width) + u2];
			UINT32 pix10 = texture->data[(v2 << width) + u1];
			UINT32 pix11 = texture->data[(v2 << width) + u2];
			UINT32 texel = rgba_bilinear_filter(pix00, pix01, pix10, pix11, u, v);
			UINT32 fr = ((texel & 0x00ff0000) * polyi) >> (8+9);
			UINT32 fg = ((texel & 0x0000ff00) * polyi) >> (8+6);
			UINT32 fb = ((texel & 0x000000ff) * polyi) >> (8+3);
			UINT16 orig = p[x];
			fr += ((orig & 0x7c00) * desttrans) >> 5;
			fg += ((orig & 0x03e0) * desttrans) >> 5;
			fb += ((orig & 0x001f) * desttrans) >> 5;
			p[x] = (fr & 0x7c00) | (fg & 0x3e0) | (fb & 0x1f);
			d[x] = iz;
		}

		ooz += doozdx;
		uoz += duozdx;
		voz += dvozdx;
	}
}


static void draw_scanline_alpha(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid)
{
	const poly_extra_data *extra = (const poly_extra_data *)extradata;
	const cached_texture *texture = extra->texture;
	bitmap_t *destmap = (bitmap_t *)dest;
	UINT16 *p = BITMAP_ADDR16(destmap, scanline, 0);
	UINT32 *d = BITMAP_ADDR32(extra->zbuffer, scanline, 0);
	float ooz = extent->param[0].start;
	float uoz = extent->param[1].start;
	float voz = extent->param[2].start;
	float doozdx = extent->param[0].dpdx;
	float duozdx = extent->param[1].dpdx;
	float dvozdx = extent->param[2].dpdx;
	UINT32 polyi = (extra->polygon_intensity * extra->polygon_transparency) >> 5;
	int desttrans = 32 - extra->polygon_transparency;
	UINT32 umask = (((extra->texture_param & TRI_PARAM_TEXTURE_MIRROR_U) ? 64 : 32) << texture->width) - 1;
	UINT32 vmask = (((extra->texture_param & TRI_PARAM_TEXTURE_MIRROR_V) ? 64 : 32) << texture->height) - 1;
	UINT32 width = 6 + texture->width;
	int x;

	for (x = extent->startx; x < extent->stopx; x++)
	{
		UINT32 iz = ooz * 256.0f;
		if (iz > d[x])
		{
			float z = 1.0f / ooz;
			UINT32 u = uoz * z;
			UINT32 v = voz * z;
			UINT32 u1 = (u >> 8) & umask;
			UINT32 v1 = (v >> 8) & vmask;
			UINT32 u2 = (u1 + 1) & umask;
			UINT32 v2 = (v1 + 1) & vmask;
			UINT32 pix00 = texture->data[(v1 << width) + u1];
			UINT32 pix01 = texture->data[(v1 << width) + u2];
			UINT32 pix10 = texture->data[(v2 << width) + u1];
			UINT32 pix11 = texture->data[(v2 << width) + u2];
			UINT32 texel = rgba_bilinear_filter(pix00, pix01, pix10, pix11, u, v);
			UINT32 fa = texel >> 24;
			if (fa != 0)
			{
				UINT32 combined = ((fa + 1) * polyi) >> 8;
				UINT32 fr = ((texel & 0x00ff0000) * combined) >> (8+9);
				UINT32 fg = ((texel & 0x0000ff00) * combined) >> (8+6);
				UINT32 fb = ((texel & 0x000000ff) * combined) >> (8+3);
				UINT16 orig = p[x];
				combined = ((255 - fa) * desttrans) >> 5;
				fr += ((orig & 0x7c00) * combined) >> 8;
				fg += ((orig & 0x03e0) * combined) >> 8;
				fb += ((orig & 0x001f) * combined) >> 8;
				p[x] = (fr & 0x7c00) | (fg & 0x3e0) | (fb & 0x1f);
				d[x] = iz;
			}
		}

		ooz += doozdx;
		uoz += duozdx;
		voz += dvozdx;
	}
}


static void draw_scanline_alpha_test(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid)
{
	const poly_extra_data *extra = (const poly_extra_data *)extradata;
	const cached_texture *texture = extra->texture;
	bitmap_t *destmap = (bitmap_t *)dest;
	UINT16 *p = BITMAP_ADDR16(destmap, scanline, 0);
	UINT32 *d = BITMAP_ADDR32(extra->zbuffer, scanline, 0);
	float ooz = extent->param[0].start;
	float uoz = extent->param[1].start;
	float voz = extent->param[2].start;
	float doozdx = extent->param[0].dpdx;
	float duozdx = extent->param[1].dpdx;
	float dvozdx = extent->param[2].dpdx;
	UINT32 polyi = (extra->polygon_intensity * extra->polygon_transparency) >> 5;
	int desttrans = 32 - extra->polygon_transparency;
	UINT32 umask = (((extra->texture_param & TRI_PARAM_TEXTURE_MIRROR_U) ? 64 : 32) << texture->width) - 1;
	UINT32 vmask = (((extra->texture_param & TRI_PARAM_TEXTURE_MIRROR_V) ? 64 : 32) << texture->height) - 1;
	UINT32 width = 6 + texture->width;
	int x;

	for (x = extent->startx; x < extent->stopx; x++)
	{
		UINT32 iz = ooz * 256.0f;
		if (iz > d[x])
		{
			float z = 1.0f / ooz;
			UINT32 u = uoz * z;
			UINT32 v = voz * z;
			UINT32 u1 = (u >> 8) & umask;
			UINT32 v1 = (v >> 8) & vmask;
			UINT32 u2 = (u1 + 1) & umask;
			UINT32 v2 = (v1 + 1) & vmask;
			UINT32 pix00 = texture->data[(v1 << width) + u1];
			UINT32 pix01 = texture->data[(v1 << width) + u2];
			UINT32 pix10 = texture->data[(v2 << width) + u1];
			UINT32 pix11 = texture->data[(v2 << width) + u2];
			UINT32 texel = rgba_bilinear_filter(pix00, pix01, pix10, pix11, u, v);
			UINT32 fa = texel >> 24;
			if (fa >= 0xf8)
			{
				UINT32 combined = ((fa + 1) * polyi) >> 8;
				UINT32 fr = ((texel & 0x00ff0000) * combined) >> (8+9);
				UINT32 fg = ((texel & 0x0000ff00) * combined) >> (8+6);
				UINT32 fb = ((texel & 0x000000ff) * combined) >> (8+3);
				UINT16 orig = p[x];
				combined = ((255 - fa) * desttrans) >> 8;
				fr += ((orig & 0x7c00) * combined) >> 5;
				fg += ((orig & 0x03e0) * combined) >> 5;
				fb += ((orig & 0x001f) * combined) >> 5;
				p[x] = (fr & 0x7c00) | (fg & 0x3e0) | (fb & 0x1f);
				d[x] = iz;
			}
		}

		ooz += doozdx;
		uoz += duozdx;
		voz += dvozdx;
	}
}

static void draw_scanline_color(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid)
{
	const poly_extra_data *extra = (const poly_extra_data *)extradata;
	bitmap_t *destmap = (bitmap_t *)dest;
	UINT16 *p = BITMAP_ADDR16(destmap, scanline, 0);
	UINT32 *d = BITMAP_ADDR32(extra->zbuffer, scanline, 0);
	float ooz = extent->param[0].start;
	float doozdx = extent->param[0].dpdx;
	int fr = extra->color & 0x7c00;
	int fg = extra->color & 0x03e0;
	int fb = extra->color & 0x001f;
	int x;

	// apply intensity
	fr = (fr * extra->polygon_intensity) >> 8;
	fg = (fg * extra->polygon_intensity) >> 8;
	fb = (fb * extra->polygon_intensity) >> 8;

	/* simple case: no transluceny */
	if (extra->polygon_transparency >= 32)
	{
		UINT32 color = (fr & 0x7c00) | (fg & 0x03e0) | (fb & 0x1f);
		for (x = extent->startx; x < extent->stopx; x++)
		{
			UINT32 iz = ooz * 256.0f;
			if (iz > d[x])
			{
				p[x] = color;
				d[x] = iz;
			}
			ooz += doozdx;
		}
	}

	/* translucency */
	else
	{
		int polytrans = extra->polygon_transparency;

		fr = (fr * polytrans) >> 5;
		fg = (fg * polytrans) >> 5;
		fb = (fb * polytrans) >> 5;
		polytrans = 32 - polytrans;

		for (x = extent->startx; x < extent->stopx; x++)
		{
			UINT32 iz = ooz * 256.0f;
			if (iz > d[x])
			{
				UINT16 orig = p[x];
				int r = fr + (((orig & 0x7c00) * polytrans) >> 5);
				int g = fg + (((orig & 0x03e0) * polytrans) >> 5);
				int b = fb + (((orig & 0x001f) * polytrans) >> 5);

				p[x] = (r & 0x7c00) | (g & 0x3e0) | (b & 0x1f);
				d[x] = iz;
			}
			ooz += doozdx;
		}
	}
}
