Fast pasting of an image to another image in DBP with Alpha and full clipping...
Attached is a test program and the dll.
The final working version of the paste function used (optimised with assembler):
extern int GetMemOffset(int format);
// This section checks x1,y1,w,h is within dw,dh and sets the flag and vars to fit
struct _RECT_CHECK {
int x, y, w, h; // source rectangle
int dw, dh; // destination rectangle dimension
int xoff, yoff; // offset into source image
};
static _RECT_CHECK rc;
static bool rect_check()
{
// check if rect is completely outside dest rect
if ( rc.x + rc.w < 0 ) return false;
if ( rc.y + rc.h < 0 ) return false;
if ( rc.x > rc.dw ) return false;
if ( rc.y > rc.dh ) return false;
// reset offset into source image
rc.xoff = rc.yoff = 0;
// fix the source x axis
if ( rc.x < 0 )
{
rc.w += rc.x;
rc.xoff = -rc.x;
rc.x = 0;
}
if ( rc.x + rc.w >= rc.dw )
{
rc.w -= ( rc.x + rc.w ) - rc.dw;
}
// fix the source y axis
if ( rc.y < 0 )
{
rc.h += rc.y;
rc.yoff = -rc.y;
rc.y = 0;
}
if ( rc.y + rc.h >= rc.dh )
{
rc.h -= ( rc.y + rc.h ) - rc.dh;
}
// coords fixed, now return return
return true;
}
EXPORTC void ImagePasteToImageAlpha_mod(int Src, int Dst, int DstX, int DstY)
{
LPDIRECT3DSURFACE9 SrcSurface = 0;
D3DLOCKED_RECT SrcRect;
D3DSURFACE_DESC SrcDesc;
LPDIRECT3DSURFACE9 DstSurface = 0;
D3DLOCKED_RECT DstRect;
D3DSURFACE_DESC DstDesc;
unsigned char *SrcAddr, *DstAddr;
if ( 0 == DBPro::ImageExist(Src) ) return; // if either image doesn't exist
if ( 0 == DBPro::ImageExist(Dst) ) return;
DBPro::GetImageTexturePtr(Src)->GetSurfaceLevel(0, &SrcSurface); // DX surface stuff
DBPro::GetImageTexturePtr(Dst)->GetSurfaceLevel(0, &DstSurface);
// set the clipping check structure
rc.w = DBPro::ImageWidth(Src);
rc.h = DBPro::ImageHeight(Src);
rc.dw = DBPro::ImageWidth(Dst);
rc.dh = DBPro::ImageHeight(Dst);
rc.x = DstX;
rc.y = DstY;
if ( !rect_check() ) return; // exit if out of range of dest
//int SrcXOffset = 0, SrcYOffset = 0; // needed to clip source image
DstSurface->GetDesc(&DstDesc);
DstSurface->LockRect(&DstRect, 0, 0);
DstAddr = (unsigned char*)DstRect.pBits;
int DstOffset = GetMemOffset(DstDesc.Format);
SrcSurface->GetDesc(&SrcDesc);
SrcSurface->LockRect(&SrcRect, 0, 0);
SrcAddr = (unsigned char*)SrcRect.pBits;
int SrcOffset = GetMemOffset(SrcDesc.Format);
int SrcPitch = SrcRect.Pitch;
int DstPitch = DstRect.Pitch;
unsigned char *SrcPtr, *DstPtr;
int y;
unsigned int src;
unsigned int a; // using x86 register
unsigned int rb;
unsigned int g; // using x86 register
unsigned int bg;
unsigned int nega;
for ( y = 0; y < rc.h; y++ )
{
SrcPtr = ( y + rc.yoff ) * SrcPitch + ( rc.xoff * SrcOffset) + SrcAddr;
DstPtr = ( y + rc.y ) * DstPitch + ( rc.x * DstOffset ) + DstAddr;
int x;
#ifndef WL_OPTIMISE_ASM
for ( x = 0; x < rc.w; x++ )
{
src = *(DWORD*)SrcPtr;
a = src >> 24;
if ( a ) // if alpha is 0 then don't do anything
{
bg = *(DWORD*)DstPtr;
rb = (((src & 0x00ff00ff) * a) + ((bg & 0x00ff00ff) * (0xff - a))) & 0xff00ff00;
g = (((src & 0x0000ff00) * a) + ((bg & 0x0000ff00) * (0xff - a))) & 0x00ff0000;
*(DWORD*)DstPtr = (src & 0xff000000) | ((rb | g) >> 8);
}
DstPtr += DstOffset;
SrcPtr += SrcOffset;
}
#else
int SrcW = rc.w;
__asm {
mov eax, [SrcW] // setup main x loop
mov [x], eax
loopx:
mov ebx, [SrcPtr] // start pixel on line
mov eax, [ebx] // store pixel in Src
mov [src], eax
add ebx, 4 // next src pixel
mov [SrcPtr], ebx
shr eax, 24 // get alpha from pixel
je donothing // is alpha 0
mov edx, eax
// edx = ALPHA / ebx = bg / nega = (0xff - a)
// using registers for optimisation
mov eax, 0xff
sub eax, edx
mov [nega], eax
mov ecx, [DstPtr] // get background pixel bg
mov ebx, [ecx]
//mov [bg], ebx
mov eax, [src] // process rb
and eax, 0x00ff00ff
mul edx
mov ecx, eax
mov eax, ebx
and eax, 0x00ff00ff
mul [nega]
add eax, ecx
and eax, 0xff00ff00
mov [rb], eax
mov eax, [src] // process g
and eax, 0x0000ff00
mul edx
mov ecx, eax
mov eax, ebx
and eax, 0x0000ff00
mul [nega]
add eax, ecx
and eax, 0x00ff0000
or eax, [rb] // put rb and g together
shr eax, 8
mov ecx, [src]
and ecx, 0xff000000
or eax, ecx
mov ecx, [DstPtr] // store new pixel
mov [ecx], eax
donothing:
mov eax, 4 // next pixel on dest line
add [DstPtr], eax
mov eax, 1
sub [x], eax
jne loopx
}
#endif
}
DstSurface->UnlockRect();
DstSurface->Release();
DstSurface = 0;
SrcSurface->UnlockRect();
SrcSurface->Release();
SrcSurface = 0;
return;
}
I don't have Image Kit by Sven B, but if anyone could maybe compare that would be great. With the quick method I have used I've not needed any MMX/SSE instructions. The assembler optimisation took some time because as usual I had plenty of typos to fix...
Oh yeah, the DBP code to test this alpha pasting of images to other images:
set bitmap format 21 : set display mode 640, 480, 32
autocam off : sync on : sync rate 60
` create a blank image for our main texture
wl make blank image 1, 256, 256
` create a texture with an alpha cross in it larger than the main texture image
wl make blank image 2, 1024, 1024
for p = 0 to 1023 step 64
wl image fill rect 2, p, 0, 7, 1022, 0x40fff7ff
wl image fill rect 2, 0, p, 1022, 7, 0x40ff7fff
wl image fill rect 2, p, 0, 1, 1022, 0x7f000000
wl image fill rect 2, 0, p, 1022, 1, 0x7f000000
next
` create the 3d cube
make object cube 1, 10
` position the camera
position camera 0, 0, -14
point camera 0, 0, 0
` cubes rotation angle
cube_ang# = 0.0
` vars for the texture rotation
text_rot# = 0.0
` run the main loop
do
` clear the main texture
wl image cls 1, 0x00007f00
` paste the image to the texture (4 copies so it's tiled)
posx# = 256.0 * cos( text_rot# ) - 256.0
posy# = 256.0 * sin( text_rot# ) - 256.0
wl image to image alpha 2, 1, posx#, posy#
` reverse paste the image
wl image to image alpha 2, 1, posy#, posx#
texture object 1, 1
rotate object 1, cube_ang#, cube_ang#, 0
` sort the rotation and the angles out now
cube_ang# = wrapvalue( cube_ang# + 0.5 )
text_rot# = wrapvalue( text_rot# + 0.9 )
` and update
sync
loop
Mental arithmetic? Me? (That's for computers) I can't subtract a fart from a plate of beans!
Warning! May contain Nuts!