// Copyright (C) 2002-2012 Nikolaus Gebhardt / Thomas Alten // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #pragma once #include "SoftwareDriver2_helper.h" namespace irr { //! f18 - fixpoint 14.18 limit to 16k Textures #define CBLIT_USE_FIXPOINT18 #if defined(CBLIT_USE_FIXPOINT18) typedef int f18; #define f18_one 262144 #define f18_zero 0 #define f32_to_f18(x) ((f18)floorf(((x) * 262144.f) + 0.f)) #define f32_to_f32(x) (x) #define f18_floor(x) ((x) >> 18) #define f18_round(x) ((x + 131.072) >> 18) #else typedef float f18; #define f18_one 1.f #define f18_zero_dot_five 0.5f #define f18_zero 0.f #define f32_to_f18(x) (x) #define f32_to_f32(x) (x) #define f18_floor(x) ((int)(x)) #define f18_round(x) ((int)(x + 0.5f)) #endif struct SBlitJob { AbsRectangle Dest; AbsRectangle Source; u32 argb; const void *src; void *dst; u32 width; // draw size u32 height; u32 srcPixelMul; // pixel byte size u32 dstPixelMul; int srcPitch; // scanline byte size. allow negative for mirror u32 dstPitch; bool stretch; f32 x_stretch; f32 y_stretch; }; // Bitfields Cohen Sutherland enum eClipCode { CLIPCODE_EMPTY = 0, CLIPCODE_BOTTOM = 1, CLIPCODE_TOP = 2, CLIPCODE_LEFT = 4, CLIPCODE_RIGHT = 8 }; inline u32 GetClipCode(const AbsRectangle &r, const core::position2d &p) { u32 code = CLIPCODE_EMPTY; if (p.X < r.x0) code = CLIPCODE_LEFT; else if (p.X > r.x1) code = CLIPCODE_RIGHT; if (p.Y < r.y0) code |= CLIPCODE_TOP; else if (p.Y > r.y1) code |= CLIPCODE_BOTTOM; return code; } /* */ inline void GetClip(AbsRectangle &clipping, video::IImage *t) { clipping.x0 = 0; clipping.y0 = 0; clipping.x1 = t->getDimension().Width - 1; clipping.y1 = t->getDimension().Height - 1; } /* return alpha in [0;256] Granularity from 32-Bit ARGB add highbit alpha ( alpha > 127 ? + 1 ) */ static inline u32 extractAlpha(const u32 c) { return (c >> 24) + (c >> 31); } /* return alpha in [0;255] Granularity and 32-Bit ARGB add highbit alpha ( alpha > 127 ? + 1 ) */ static inline u32 packAlpha(const u32 c) { return (c > 127 ? c - 1 : c) << 24; } /*! Scale Color by (1/value) value 0 - 256 ( alpha ) */ inline u32 PixelLerp32(const u32 source, const u32 value) { u32 srcRB = source & 0x00FF00FF; u32 srcXG = (source & 0xFF00FF00) >> 8; srcRB *= value; srcXG *= value; srcRB >>= 8; // srcXG >>= 8; srcXG &= 0xFF00FF00; srcRB &= 0x00FF00FF; return srcRB | srcXG; } /*! */ static void executeBlit_TextureCopy_x_to_x(const SBlitJob *job) { if (job->stretch) { const f18 wscale = f32_to_f18(job->x_stretch); const f18 hscale = f32_to_f18(job->y_stretch); f18 src_y = f18_zero; if (job->srcPixelMul == 4) { const u32 *src = (u32 *)(job->src); u32 *dst = (u32 *)(job->dst); for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) { src = (u32 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y)); f18 src_x = f18_zero; for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) { dst[dx] = src[f18_floor(src_x)]; } dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } else if (job->srcPixelMul == 2) { const u16 *src = (u16 *)(job->src); u16 *dst = (u16 *)(job->dst); for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) { src = (u16 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y)); f18 src_x = f18_zero; for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) { dst[dx] = src[f18_floor(src_x)]; } dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } } else { const size_t widthPitch = job->width * job->dstPixelMul; const void *src = (void *)job->src; void *dst = (void *)job->dst; for (u32 dy = 0; dy < job->height; ++dy) { memcpy(dst, src, widthPitch); src = (void *)((u8 *)(src) + job->srcPitch); dst = (void *)((u8 *)(dst) + job->dstPitch); } } } /*! */ static void executeBlit_TextureCopy_32_to_16(const SBlitJob *job) { const u32 w = job->width; const u32 h = job->height; const u32 *src = static_cast(job->src); u16 *dst = static_cast(job->dst); if (job->stretch) { const float wscale = job->x_stretch; const float hscale = job->y_stretch; for (u32 dy = 0; dy < h; ++dy) { const u32 src_y = (u32)(dy * hscale); src = (u32 *)((u8 *)(job->src) + job->srcPitch * src_y); for (u32 dx = 0; dx < w; ++dx) { const u32 src_x = (u32)(dx * wscale); // 16 bit Blitter depends on pre-multiplied color const u32 s = PixelLerp32(src[src_x] | 0xFF000000, extractAlpha(src[src_x])); dst[dx] = video::A8R8G8B8toA1R5G5B5(s); } dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } else { for (u32 dy = 0; dy != h; ++dy) { for (u32 dx = 0; dx != w; ++dx) { // 16 bit Blitter depends on pre-multiplied color const u32 s = PixelLerp32(src[dx] | 0xFF000000, extractAlpha(src[dx])); dst[dx] = video::A8R8G8B8toA1R5G5B5(s); } src = (u32 *)((u8 *)(src) + job->srcPitch); dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } } /*! */ static void executeBlit_TextureCopy_24_to_16(const SBlitJob *job) { const u32 w = job->width; const u32 h = job->height; const u8 *src = static_cast(job->src); u16 *dst = static_cast(job->dst); if (job->stretch) { const float wscale = job->x_stretch * 3.f; const float hscale = job->y_stretch; for (u32 dy = 0; dy < h; ++dy) { const u32 src_y = (u32)(dy * hscale); src = (u8 *)(job->src) + job->srcPitch * src_y; for (u32 dx = 0; dx < w; ++dx) { const u8 *src_x = src + (u32)(dx * wscale); dst[dx] = video::RGBA16(src_x[0], src_x[1], src_x[2]); } dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } else { for (u32 dy = 0; dy != h; ++dy) { const u8 *s = src; for (u32 dx = 0; dx != w; ++dx) { dst[dx] = video::RGBA16(s[0], s[1], s[2]); s += 3; } src = src + job->srcPitch; dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } } /*! */ static void executeBlit_TextureCopy_16_to_32(const SBlitJob *job) { const u32 w = job->width; const u32 h = job->height; const u16 *src = static_cast(job->src); u32 *dst = static_cast(job->dst); if (job->stretch) { const float wscale = job->x_stretch; const float hscale = job->y_stretch; for (u32 dy = 0; dy < h; ++dy) { const u32 src_y = (u32)(dy * hscale); src = (u16 *)((u8 *)(job->src) + job->srcPitch * src_y); for (u32 dx = 0; dx < w; ++dx) { const u32 src_x = (u32)(dx * wscale); dst[dx] = video::A1R5G5B5toA8R8G8B8(src[src_x]); } dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } else { for (u32 dy = 0; dy != h; ++dy) { for (u32 dx = 0; dx != w; ++dx) { dst[dx] = video::A1R5G5B5toA8R8G8B8(src[dx]); } src = (u16 *)((u8 *)(src) + job->srcPitch); dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } } static void executeBlit_TextureCopy_16_to_24(const SBlitJob *job) { const u32 w = job->width; const u32 h = job->height; const u16 *src = static_cast(job->src); u8 *dst = static_cast(job->dst); if (job->stretch) { const float wscale = job->x_stretch; const float hscale = job->y_stretch; for (u32 dy = 0; dy < h; ++dy) { const u32 src_y = (u32)(dy * hscale); src = (u16 *)((u8 *)(job->src) + job->srcPitch * src_y); for (u32 dx = 0; dx < w; ++dx) { const u32 src_x = (u32)(dx * wscale); u32 color = video::A1R5G5B5toA8R8G8B8(src[src_x]); u8 *writeTo = &dst[dx * 3]; *writeTo++ = (color >> 16) & 0xFF; *writeTo++ = (color >> 8) & 0xFF; *writeTo++ = color & 0xFF; } dst += job->dstPitch; } } else { for (u32 dy = 0; dy != h; ++dy) { for (u32 dx = 0; dx != w; ++dx) { u32 color = video::A1R5G5B5toA8R8G8B8(src[dx]); u8 *writeTo = &dst[dx * 3]; *writeTo++ = (color >> 16) & 0xFF; *writeTo++ = (color >> 8) & 0xFF; *writeTo++ = color & 0xFF; } src = (u16 *)((u8 *)(src) + job->srcPitch); dst += job->dstPitch; } } } /*! */ static void executeBlit_TextureCopy_24_to_32(const SBlitJob *job) { const u32 w = job->width; const u32 h = job->height; const u8 *src = static_cast(job->src); u32 *dst = static_cast(job->dst); if (job->stretch) { const float wscale = job->x_stretch * 3.f; const float hscale = job->y_stretch; for (u32 dy = 0; dy < h; ++dy) { const u32 src_y = (u32)(dy * hscale); src = (const u8 *)job->src + (job->srcPitch * src_y); for (u32 dx = 0; dx < w; ++dx) { const u8 *s = src + (u32)(dx * wscale); dst[dx] = 0xFF000000 | s[0] << 16 | s[1] << 8 | s[2]; } dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } else { for (u32 dy = 0; dy < job->height; ++dy) { const u8 *s = src; for (u32 dx = 0; dx < job->width; ++dx) { dst[dx] = 0xFF000000 | s[0] << 16 | s[1] << 8 | s[2]; s += 3; } src = src + job->srcPitch; dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } } static void executeBlit_TextureCopy_32_to_24(const SBlitJob *job) { const u32 w = job->width; const u32 h = job->height; const u32 *src = static_cast(job->src); u8 *dst = static_cast(job->dst); if (job->stretch) { const float wscale = job->x_stretch; const float hscale = job->y_stretch; for (u32 dy = 0; dy < h; ++dy) { const u32 src_y = (u32)(dy * hscale); src = (u32 *)((u8 *)(job->src) + job->srcPitch * src_y); for (u32 dx = 0; dx < w; ++dx) { const u32 src_x = src[(u32)(dx * wscale)]; u8 *writeTo = &dst[dx * 3]; *writeTo++ = (src_x >> 16) & 0xFF; *writeTo++ = (src_x >> 8) & 0xFF; *writeTo++ = src_x & 0xFF; } dst += job->dstPitch; } } else { for (u32 dy = 0; dy != h; ++dy) { for (u32 dx = 0; dx != w; ++dx) { u8 *writeTo = &dst[dx * 3]; *writeTo++ = (src[dx] >> 16) & 0xFF; *writeTo++ = (src[dx] >> 8) & 0xFF; *writeTo++ = src[dx] & 0xFF; } src = (u32 *)((u8 *)(src) + job->srcPitch); dst += job->dstPitch; } } } /*! */ static void executeBlit_TextureBlend_16_to_16(const SBlitJob *job) { const f18 wscale = f32_to_f18(job->x_stretch); const f18 hscale = f32_to_f18(job->y_stretch); f18 src_y = f18_zero; u16 *dst = (u16 *)job->dst; for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) { const u16 *src = (u16 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y)); f18 src_x = f18_zero; for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) { dst[dx] = PixelBlend16(dst[dx], src[f18_floor(src_x)]); } dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } /*! */ static void executeBlit_TextureBlend_32_to_32(const SBlitJob *job) { const f18 wscale = f32_to_f18(job->x_stretch); const f18 hscale = f32_to_f18(job->y_stretch); f18 src_y = f18_zero; u32 *dst = (u32 *)job->dst; for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) { const u32 *src = (u32 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y)); f18 src_x = f18_zero; for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) { dst[dx] = PixelBlend32(dst[dx], src[f18_floor(src_x)]); } dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } /*! */ static void executeBlit_TextureBlendColor_16_to_16(const SBlitJob *job) { const u16 blend = video::A8R8G8B8toA1R5G5B5(job->argb); const f18 wscale = f32_to_f18(job->x_stretch); const f18 hscale = f32_to_f18(job->y_stretch); f18 src_y = f18_zero; u16 *dst = (u16 *)job->dst; for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) { const u16 *src = (u16 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y)); f18 src_x = f18_zero; for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) { u16 c0 = src[f18_floor(src_x)]; if (0 == (c0 & 0x8000)) continue; dst[dx] = PixelMul16_2(c0, blend); } dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } /*! */ static void executeBlit_TextureBlendColor_32_to_32(const SBlitJob *job) { const f18 wscale = f32_to_f18(job->x_stretch); const f18 hscale = f32_to_f18(job->y_stretch); u32 *dst = (u32 *)job->dst; f18 src_y = f18_zero; for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) { const u32 *src = (u32 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y)); f18 src_x = f18_zero; for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) { dst[dx] = PixelBlend32(dst[dx], PixelMul32_2(src[f18_floor(src_x)], job->argb)); } dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } /*! */ static void executeBlit_Color_16_to_16(const SBlitJob *job) { const u16 c = video::A8R8G8B8toA1R5G5B5(job->argb); u16 *dst = (u16 *)job->dst; for (u32 dy = 0; dy < job->height; ++dy) { memset16(dst, c, job->srcPitch); dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } /*! */ static void executeBlit_Color_32_to_32(const SBlitJob *job) { u32 *dst = (u32 *)job->dst; for (u32 dy = 0; dy < job->height; ++dy) { memset32(dst, job->argb, job->srcPitch); dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } /*! */ static void executeBlit_ColorAlpha_16_to_16(const SBlitJob *job) { u16 *dst = (u16 *)job->dst; const u16 alpha = extractAlpha(job->argb) >> 3; if (0 == alpha) return; const u32 src = video::A8R8G8B8toA1R5G5B5(job->argb); for (u32 dy = 0; dy != job->height; ++dy) { for (u32 dx = 0; dx != job->width; ++dx) { dst[dx] = PixelBlend16(dst[dx], src, alpha); } dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } /*! */ static void executeBlit_ColorAlpha_32_to_32(const SBlitJob *job) { const u32 alpha = extractAlpha(job->argb); if (0 == alpha) return; u32 *dst = (u32 *)job->dst; for (u32 dy = 0; dy < job->height; ++dy) { for (u32 dx = 0; dx < job->width; ++dx) { dst[dx] = PixelBlend32(dst[dx], job->argb, alpha); } dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } /*! Pixel => color = sourceAlpha > 0 ? source, else dest alpha = max(destAlpha, sourceAlpha) */ inline u16 PixelCombine16(const u16 c2, const u16 c1) { if (video::getAlpha(c1) > 0) return c1; else return c2; } /*! Combine alpha channels (increases alpha / reduces transparency) */ static void executeBlit_TextureCombineColor_16_to_16(const SBlitJob *job) { const u32 w = job->width * 2; const u32 h = job->height * 2; u16 *src = (u16 *)job->src; u16 *dst = (u16 *)job->dst; const u16 jobColor = video::A8R8G8B8toA1R5G5B5(job->argb); /* Stretch not supported. */ for (u32 dy = 0; dy != h; dy++) { for (u32 dx = 0; dx != w; dx++) { const u16 src_x = src[dx]; const u16 dst_x = dst[dx]; dst[dx] = PixelCombine16(dst_x, PixelMul16_2(src_x, jobColor)); } src = (u16 *)((u8 *)(src) + job->srcPitch); dst = (u16 *)((u8 *)(dst) + job->dstPitch); } } /*! Combine alpha channels (increases alpha / reduces transparency) */ static void executeBlit_TextureCombineColor_16_to_24(const SBlitJob *job) { const u32 w = job->width; const u32 h = job->height; const u16 *src = static_cast(job->src); u8 *dst = static_cast(job->dst); const u16 jobColor = video::A8R8G8B8toA1R5G5B5(job->argb); if (job->stretch) { const float wscale = job->x_stretch; const float hscale = job->y_stretch; for (u32 dy = 0; dy < h; ++dy) { const u32 src_y = (u32)(dy * hscale); src = (u16 *)((u8 *)(job->src) + job->srcPitch * src_y); for (u32 dx = 0; dx < w; ++dx) { const u32 src_x = (u32)(dx * wscale); u32 color = PixelMul16_2(video::A1R5G5B5toA8R8G8B8(src[src_x]), jobColor); u8 *writeTo = &dst[dx * 3]; if (video::getAlpha(src[src_x]) > 0) { // only overlay if source has visible alpha (alpha == 1) *writeTo++ = (color >> 16) & 0xFF; *writeTo++ = (color >> 8) & 0xFF; *writeTo++ = color & 0xFF; } } dst += job->dstPitch; } } else { for (u32 dy = 0; dy != h; ++dy) { for (u32 dx = 0; dx != w; ++dx) { u32 color = PixelMul16_2(video::A1R5G5B5toA8R8G8B8(src[dx]), jobColor); u8 *writeTo = &dst[dx * 3]; if (video::getAlpha(src[dx]) > 0) { // only overlay if source has visible alpha (alpha == 1) *writeTo++ = (color >> 16) & 0xFF; *writeTo++ = (color >> 8) & 0xFF; *writeTo++ = color & 0xFF; } } src = (u16 *)((u8 *)(src) + job->srcPitch); dst += job->dstPitch; } } } /*! Pixel => color = dest * ( 1 - SourceAlpha ) + source * SourceAlpha, alpha = destAlpha * ( 1 - SourceAlpha ) + sourceAlpha where "1" means "full scale" (255) */ inline u32 PixelCombine32(const u32 c2, const u32 c1) { // alpha test u32 alpha = c1 & 0xFF000000; if (0 == alpha) return c2; if (0xFF000000 == alpha) { return c1; } alpha >>= 24; // add highbit alpha, if ( alpha > 127 ) alpha += 1; // stretches [0;255] to [0;256] to avoid division by 255. use division 256 == shr 8 alpha += (alpha >> 7); u32 srcRB = c1 & 0x00FF00FF; u32 srcXG = c1 & 0x0000FF00; u32 dstRB = c2 & 0x00FF00FF; u32 dstXG = c2 & 0x0000FF00; u32 rb = srcRB - dstRB; u32 xg = srcXG - dstXG; rb *= alpha; xg *= alpha; rb >>= 8; xg >>= 8; rb += dstRB; xg += dstXG; rb &= 0x00FF00FF; xg &= 0x0000FF00; u32 sa = c1 >> 24; u32 da = c2 >> 24; u32 blendAlpha_fix8 = (sa * 256 + da * (256 - alpha)) >> 8; return blendAlpha_fix8 << 24 | rb | xg; } /*! Combine alpha channels (increases alpha / reduces transparency) Destination alpha is treated as full 255 */ static void executeBlit_TextureCombineColor_32_to_24(const SBlitJob *job) { const u32 w = job->width; const u32 h = job->height; const u32 *src = static_cast(job->src); u8 *dst = static_cast(job->dst); if (job->stretch) { const float wscale = job->x_stretch; const float hscale = job->y_stretch; for (u32 dy = 0; dy < h; ++dy) { const u32 src_y = (u32)(dy * hscale); src = (u32 *)((u8 *)(job->src) + job->srcPitch * src_y); for (u32 dx = 0; dx < w; ++dx) { const u32 src_x = src[(u32)(dx * wscale)]; u8 *writeTo = &dst[dx * 3]; const u32 dst_x = 0xFF000000 | writeTo[0] << 16 | writeTo[1] << 8 | writeTo[2]; const u32 combo = PixelCombine32(dst_x, PixelMul32_2(src_x, job->argb)); *writeTo++ = (combo >> 16) & 0xFF; *writeTo++ = (combo >> 8) & 0xFF; *writeTo++ = combo & 0xFF; } dst += job->dstPitch; } } else { for (u32 dy = 0; dy != h; ++dy) { for (u32 dx = 0; dx != w; ++dx) { u8 *writeTo = &dst[dx * 3]; const u32 dst_x = 0xFF000000 | writeTo[0] << 16 | writeTo[1] << 8 | writeTo[2]; const u32 combo = PixelCombine32(dst_x, PixelMul32_2(src[dx], job->argb)); *writeTo++ = (combo >> 16) & 0xFF; *writeTo++ = (combo >> 8) & 0xFF; *writeTo++ = combo & 0xFF; } src = (u32 *)((u8 *)(src) + job->srcPitch); dst += job->dstPitch; } } } /*! Combine alpha channels (increases alpha / reduces transparency) */ static void executeBlit_TextureCombineColor_32_to_32(const SBlitJob *job) { u32 *src = (u32 *)job->src; u32 *dst = (u32 *)job->dst; for (u32 dy = 0; dy != job->height; ++dy) { for (u32 dx = 0; dx != job->width; ++dx) { dst[dx] = PixelCombine32(dst[dx], PixelMul32_2(src[dx], job->argb)); } src = (u32 *)((u8 *)(src) + job->srcPitch); dst = (u32 *)((u8 *)(dst) + job->dstPitch); } } // Blitter Operation enum eBlitter { BLITTER_INVALID = 0, BLITTER_COLOR, BLITTER_COLOR_ALPHA, BLITTER_TEXTURE, BLITTER_TEXTURE_ALPHA_BLEND, BLITTER_TEXTURE_ALPHA_COLOR_BLEND, BLITTER_TEXTURE_COMBINE_ALPHA, }; typedef void (*tExecuteBlit)(const SBlitJob *job); /*! */ struct blitterTable { eBlitter operation; s32 destFormat; s32 sourceFormat; tExecuteBlit func; }; static const blitterTable blitTable[] = { {BLITTER_TEXTURE, -2, -2, executeBlit_TextureCopy_x_to_x}, {BLITTER_TEXTURE, video::ECF_A1R5G5B5, video::ECF_A8R8G8B8, executeBlit_TextureCopy_32_to_16}, {BLITTER_TEXTURE, video::ECF_A1R5G5B5, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_16}, {BLITTER_TEXTURE, video::ECF_A8R8G8B8, video::ECF_A1R5G5B5, executeBlit_TextureCopy_16_to_32}, {BLITTER_TEXTURE, video::ECF_A8R8G8B8, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_32}, {BLITTER_TEXTURE, video::ECF_R8G8B8, video::ECF_A1R5G5B5, executeBlit_TextureCopy_16_to_24}, {BLITTER_TEXTURE, video::ECF_R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureCopy_32_to_24}, {BLITTER_TEXTURE_ALPHA_BLEND, video::ECF_A1R5G5B5, video::ECF_A1R5G5B5, executeBlit_TextureBlend_16_to_16}, {BLITTER_TEXTURE_ALPHA_BLEND, video::ECF_A8R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureBlend_32_to_32}, {BLITTER_TEXTURE_ALPHA_COLOR_BLEND, video::ECF_A1R5G5B5, video::ECF_A1R5G5B5, executeBlit_TextureBlendColor_16_to_16}, {BLITTER_TEXTURE_ALPHA_COLOR_BLEND, video::ECF_A8R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureBlendColor_32_to_32}, {BLITTER_COLOR, video::ECF_A1R5G5B5, -1, executeBlit_Color_16_to_16}, {BLITTER_COLOR, video::ECF_A8R8G8B8, -1, executeBlit_Color_32_to_32}, {BLITTER_COLOR_ALPHA, video::ECF_A1R5G5B5, -1, executeBlit_ColorAlpha_16_to_16}, {BLITTER_COLOR_ALPHA, video::ECF_A8R8G8B8, -1, executeBlit_ColorAlpha_32_to_32}, {BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A8R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureCombineColor_32_to_32}, {BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A8R8G8B8, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_32}, {BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureCombineColor_32_to_24}, {BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_R8G8B8, video::ECF_R8G8B8, executeBlit_TextureCopy_x_to_x}, {BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A1R5G5B5, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_16}, {BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A1R5G5B5, video::ECF_A1R5G5B5, executeBlit_TextureCombineColor_16_to_16}, {BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A1R5G5B5, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_16}, {BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_R8G8B8, video::ECF_A1R5G5B5, executeBlit_TextureCombineColor_16_to_24}, {BLITTER_INVALID, -1, -1, 0}, }; static inline tExecuteBlit getBlitter2(eBlitter operation, const video::IImage *dest, const video::IImage *source) { video::ECOLOR_FORMAT sourceFormat = (video::ECOLOR_FORMAT)(source ? source->getColorFormat() : -1); video::ECOLOR_FORMAT destFormat = (video::ECOLOR_FORMAT)(dest ? dest->getColorFormat() : -1); const blitterTable *b = blitTable; while (b->operation != BLITTER_INVALID) { if (b->operation == operation) { if ((b->destFormat == -1 || b->destFormat == destFormat) && (b->sourceFormat == -1 || b->sourceFormat == sourceFormat)) return b->func; else if (b->destFormat == -2 && (sourceFormat == destFormat)) return b->func; } b += 1; } return 0; } // bounce clipping to texture inline void setClip(AbsRectangle &out, const core::rect *clip, const video::IImage *tex, s32 passnative, const core::dimension2d *tex_org) { if (0 == tex) { if (clip && passnative) { out.x0 = clip->UpperLeftCorner.X; out.x1 = clip->LowerRightCorner.X; out.y0 = clip->UpperLeftCorner.Y; out.y1 = clip->LowerRightCorner.Y; } else { out.x0 = 0; out.x1 = 0; out.y0 = 0; out.y1 = 0; } return; } const s32 w = tex->getDimension().Width; const s32 h = tex->getDimension().Height; // driver could have changed texture size. if (clip && tex_org && ((u32)w != tex_org->Width || (u32)h != tex_org->Height)) { out.x0 = core::s32_clamp((clip->UpperLeftCorner.X * w) / tex_org->Width, 0, w - 1); out.x1 = core::s32_clamp((clip->LowerRightCorner.X * w) / tex_org->Width, out.x0, w); out.y0 = core::s32_clamp((clip->UpperLeftCorner.Y * h) / tex_org->Height, 0, h - 1); out.y1 = core::s32_clamp((clip->LowerRightCorner.Y * h) / tex_org->Height, out.y0, h); } else if (clip) { // y-1 to prevent starting on illegal memory (not ideal!). out.x0 = core::s32_clamp(clip->UpperLeftCorner.X, 0, w - 1); out.x1 = core::s32_clamp(clip->LowerRightCorner.X, passnative ? 0 : out.x0, w); out.y0 = core::s32_clamp(clip->UpperLeftCorner.Y, 0, h - 1); out.y1 = core::s32_clamp(clip->LowerRightCorner.Y, passnative ? 0 : out.y0, h); } else { out.x0 = 0; out.y0 = 0; out.x1 = w; out.y1 = h; } } /*! a generic 2D Blitter */ static s32 Blit(eBlitter operation, video::IImage *dest, const core::rect *destClipping, const core::position2d *destPos, video::IImage *const source, const core::rect *sourceClipping, u32 argb) { tExecuteBlit blitter = getBlitter2(operation, dest, source); if (0 == blitter) { return 0; } // Clipping AbsRectangle sourceClip; AbsRectangle destClip; AbsRectangle v; SBlitJob job; setClip(sourceClip, sourceClipping, source, 1, 0); setClip(destClip, destClipping, dest, 0, 0); v.x0 = destPos ? destPos->X : 0; v.y0 = destPos ? destPos->Y : 0; v.x1 = v.x0 + (sourceClip.x1 - sourceClip.x0); v.y1 = v.y0 + (sourceClip.y1 - sourceClip.y0); if (!intersect(job.Dest, destClip, v)) return 0; job.width = job.Dest.x1 - job.Dest.x0; job.height = job.Dest.y1 - job.Dest.y0; job.Source.x0 = sourceClip.x0 + (job.Dest.x0 - v.x0); job.Source.x1 = job.Source.x0 + job.width; job.Source.y0 = sourceClip.y0 + (job.Dest.y0 - v.y0); job.Source.y1 = job.Source.y0 + job.height; job.argb = argb; job.stretch = false; job.x_stretch = 1.f; job.y_stretch = 1.f; if (source) { job.srcPitch = source->getPitch(); job.srcPixelMul = source->getBytesPerPixel(); job.src = (void *)((u8 *)source->getData() + (job.Source.y0 * job.srcPitch) + (job.Source.x0 * job.srcPixelMul)); } else { // use srcPitch for color operation on dest job.srcPitch = job.width * dest->getBytesPerPixel(); } job.dstPitch = dest->getPitch(); job.dstPixelMul = dest->getBytesPerPixel(); job.dst = (void *)((u8 *)dest->getData() + (job.Dest.y0 * job.dstPitch) + (job.Dest.x0 * job.dstPixelMul)); blitter(&job); return 1; } }