Hi Everyone,

The following code creates a region for a window based on a
bitmap's mask. I'm currently using CALL to execute this proc...

Can some one tell me why I can't use:

ecx in place of "begin",
ebx in place of dwCurrentLine, and
edx in place of dwCurrentPixel ??????

If I do, I get a lot of garbage in the registers....




CreateWinRgnFromBitmap proc hWin:HWND, hMaskBitmap:HBITMAP

LOCAL WindowRgn :HRGN
LOCAL hTempRgn :HRGN
LOCAL sInfo :BITMAP
LOCAL mdc :HDC
LOCAL hOldBmp :HBITMAP

LOCAL dwCurrentPixel :DWORD
LOCAL dwCurrentLine :DWORD
LOCAL begin :DWORD



push 0
push 0
push 0
push 0
call CreateRectRgn
mov WindowRgn, eax


invoke GetObject, hMaskBitmap, sizeof BITMAP, ADDR sInfo


push 0
call CreateCompatibleDC
mov mdc, eax


push hMaskBitmap
push mdc
call SelectObject
mov hOldBmp, eax


mov dwCurrentLine, 0
.WHILE 1

mov eax, dwCurrentLine
.BREAK .IF (eax == sInfo.bmHeight)

inc dwCurrentLine
mov begin, 0
mov dwCurrentPixel, 0

.WHILE 1

mov eax, dwCurrentPixel
.BREAK .IF (eax == sInfo.bmWidth)

inc dwCurrentPixel
invoke GetPixel, mdc, dwCurrentPixel, dwCurrentLine

.IF eax == 0
.IF begin == 0
push dwCurrentPixel
pop begin
.ENDIF
.ELSEIF begin > 0
mov eax, dwCurrentLine
inc eax

push eax
push dwCurrentPixel
push dwCurrentLine
push begin
call CreateRectRgn
mov hTempRgn, eax

push RGN_OR
push WindowRgn
push hTempRgn
push WindowRgn
call CombineRgn

push hTempRgn
call DeleteObject

mov begin, 0
.ENDIF

.ENDW

.ENDW


invoke SetWindowRgn, hWin,WindowRgn,1

push hOldBmp
push mdc
call SelectObject
push hTempRgn
call DeleteObject
push mdc
call DeleteDC

mov eax, WindowRgn
ret
CreateWinRgnFromBitmap endp
Posted on 2001-09-12 09:49:43 by Xtreme
It is because of register preservation in function calls.

After a function call, the values of eax, ecx and edx can be different to their value before the function was called.
They may be the same, but this is purely coincidence, and a further revision of the function will not guarantee that the same registers will hold the same predictable values.

I'm not entirely sure what ebx, esi, and edi are used for within the operating system, and whether or not certain API calls rely on the data contained within them, but at the end of any given function call they must be the same as when they were called.

If you want to use the reserved registers, there is the USES directive that will push & pop them automatically where appropriate (ie the ret command will be expanded to include the poping of these variables off the stack).

It is not a good idea to rely on the value contained in any register (other than eax!) after a function call, unless you wrote the function specifically (even then if you "improve" the function it may screw up your code)!

Mirno
Posted on 2001-09-12 10:39:50 by Mirno
Thanks Mirno,

What method should I use to optimize this code then? Should
I focus on what API calls are used? ....what about the WHILE
macro, is it slower than LOOPxxx?

I want to be able to use this code "on-the-fly" as I need to
change a window's region in an animation about once every
0.1 to 0.5 seconds.

Heres a cleaned-up version (using invoke):



CreateWinRgnFromBitmap proc hWin:HWND, hMaskBitmap:HBITMAP

LOCAL WindowRgn :HRGN
LOCAL hTempRgn :HRGN
LOCAL sInfo :BITMAP
LOCAL mdc :HDC
LOCAL hOldBmp :HBITMAP
LOCAL dwCurrentPixel :DWORD
LOCAL dwCurrentLine :DWORD
LOCAL begin :DWORD


invoke CreateRectRgn,0,0,0,0
mov WindowRgn, eax

invoke GetObject, hMaskBitmap, sizeof BITMAP, ADDR sInfo

invoke CreateCompatibleDC,0
mov mdc, eax

invoke SelectObject,mdc,hMaskBitmap
mov hOldBmp, eax

mov dwCurrentLine, 0
.WHILE 1

mov eax, dwCurrentLine
.BREAK .IF eax == sInfo.bmHeight

inc dwCurrentLine
mov begin, 0
mov dwCurrentPixel, 0

.WHILE 1

mov eax, dwCurrentPixel
.BREAK .IF eax == sInfo.bmWidth

inc dwCurrentPixel
invoke GetPixel, mdc, dwCurrentPixel, dwCurrentLine

.IF eax == 0

.IF begin == 0
push dwCurrentPixel
pop begin
.ENDIF

.ELSEIF begin > 0

mov eax, dwCurrentLine
inc eax
invoke CreateRectRgn,begin,dwCurrentLine,dwCurrentPixel,eax
mov hTempRgn, eax
invoke CombineRgn,WindowRgn,hTempRgn,WindowRgn,RGN_OR
invoke DeleteObject, hTempRgn
mov begin, 0

.ENDIF

.ENDW

.ENDW

invoke SetWindowRgn, hWin,WindowRgn,1
invoke SelectObject, mdc, hOldBmp
invoke DeleteObject, hTempRgn
invoke DeleteDC, mdc

mov eax, WindowRgn
ret
CreateWinRgnFromBitmap endp
Posted on 2001-09-12 13:10:51 by Xtreme
Your main problem is the API. API calls are going to drown your code, so optimisations will show little benefit.
If you can remove any function calls that will help most. Also check your API manual, see if there are alternatives, and test with those instead (some APIs are better suited to certain situations).

Probably the easiest way to get a bit more speed out of your code will be to write the whole branching system yourself. MASM uses macros to build up its loops, so each one is self contained. When you end your ifs, you jump to the end of them, then the end of the while performs a second jump, this is an unnecessary cost.
Don't use loop, its an old instruction and not optimised in the silicon for most modern processors. Stick to cmp & jmp combinations.

The final thing you can do is to use the return value of previous function calls in subsequent ones:


;for example replace
invoke CreateRectRgn,begin,dwCurrentLine,dwCurrentPixel,eax
mov hTempRgn, eax
invoke CombineRgn,WindowRgn,hTempRgn,WindowRgn,RGN_OR

;with:
invoke CreateRectRgn,begin,dwCurrentLine,dwCurrentPixel,eax
mov hTempRgn, eax ;Still save it for later!
invoke CombineRgn,WindowRgn,eax,WindowRgn,RGN_OR


Pushing memory can take longer on a PPro / PII / PIII, and WILL take longer on the old Pentium architecture (plus it cannot pair).

As I said at the start though, this is all really more of an exercise in code optimisation, unless you are only just short of the desired performance!
When optimising code, it is better to pick your battles carefully. You can waste hours squeezing cycles out of every function and algorithm, but in the end it doesn't really matter if your main cost are external functions beyond your control....

Mirno
Posted on 2001-09-13 04:10:41 by Mirno