BITMAP3B.ASM - WIN32NASM bitmap fader

From: Al (opcodeac_at_hotmail.com)
Date: 12/15/03


Date: Mon, 15 Dec 2003 21:03:20 GMT


This piece of code was extracted from a derelect win32 screensaver
project of mine that I worked on in early 2000. This code is designed to
fade the image in and out in a nonstop loop. Be advised though that the
speed of this is highly dependent on the speed of the machine and the
sort of image you use. Some images go faster than others.

As far as specifying the image, it's hardcoded in the asm. There's a
variable (BitmapPath) in the data section that holds the filename. Enjoy
ppl.


EXTERN GetMessageA
IMPORT GetMessageA user32.dll

EXTERN DispatchMessageA
IMPORT DispatchMessageA user32.dll

EXTERN TranslateMessage
IMPORT TranslateMessage user32.dll

EXTERN SendMessageA
IMPORT SendMessageA user32.dll

EXTERN PostQuitMessage
IMPORT PostQuitMessage user32.dll

EXTERN CreateWindowExA
IMPORT CreateWindowExA user32.dll

EXTERN ShowWindow
IMPORT ShowWindow user32.dll

EXTERN UpdateWindow
IMPORT UpdateWindow user32.dll

EXTERN ExitProcess
IMPORT ExitProcess kernel32.dll

EXTERN RegisterClassA
IMPORT RegisterClassA user32.dll

EXTERN DefWindowProcA
IMPORT DefWindowProcA user32.dll

EXTERN LoadIconA
IMPORT LoadIconA user32.dll

EXTERN LoadCursorA
IMPORT LoadCursorA user32.dll

EXTERN MessageBoxA
IMPORT MessageBoxA user32.dll

EXTERN GetModuleHandleA
IMPORT GetModuleHandleA kernel32.dll

EXTERN DestroyWindow
IMPORT DestroyWindow user32.dll

EXTERN BeginPaint
IMPORT BeginPaint user32.dll

EXTERN TextOutA
IMPORT TextOutA gdi32.dll

EXTERN EndPaint
IMPORT EndPaint user32.dll

EXTERN LoadImageA
IMPORT LoadImageA user32.dll

EXTERN GetDC
IMPORT GetDC user32.dll

EXTERN CreateCompatibleDC
IMPORT CreateCompatibleDC gdi32.dll

EXTERN SelectObject
IMPORT SelectObject gdi32.dll

EXTERN BitBlt
IMPORT BitBlt gdi32.dll

EXTERN StretchBlt
IMPORT StretchBlt gdi32.dll

EXTERN InvalidateRect
IMPORT InvalidateRect user32.dll

EXTERN DeleteObject
IMPORT DeleteObject gdi32.dll

EXTERN DeleteDC
IMPORT DeleteDC gdi32.dll

EXTERN ReleaseDC
IMPORT ReleaseDC user32.dll

EXTERN CreateFileA
IMPORT CreateFileA kernel32.dll

EXTERN GetFileSize
IMPORT GetFileSize kernel32.dll

EXTERN ReadFile
IMPORT ReadFile kernel32.dll

EXTERN StretchDIBits
IMPORT StretchDIBits gdi32.dll

EXTERN GlobalAlloc
IMPORT GlobalAlloc kernel32.dll

EXTERN GlobalLock
IMPORT GlobalLock kernel32.dll

EXTERN GlobalUnlock
IMPORT GlobalUnlock kernel32.dll

EXTERN GlobalFree
IMPORT GlobalFree kernel32.dll

EXTERN CloseHandle
IMPORT CloseHandle kernel32.dll

EXTERN CreateDIBitmap
IMPORT CreateDIBitmap gdi32.dll

EXTERN CreateDIBSection
IMPORT CreateDIBSection gdi32.dll

EXTERN GetBitmapDimensionEx
IMPORT GetBitmapDimensionEx gdi32.dll

EXTERN SetFilePointer
IMPORT SetFilePointer kernel32.dll

EXTERN SetTimer
IMPORT SetTimer user32.dll

EXTERN BeginPaint
IMPORT BeginPaint user32.dll

EXTERN EndPaint
IMPORT EndPaint user32.dll

[BITS 32]
SECTION CODE USE32 CLASS=CODE
..start:

;-----------------------------------------------------------------------------------
; |
; The window creation routines |
; |
; This code will generate a window that will be used for all graphics |
; operations. |
; |
; Program flow: |
; 1. Get the current module's handle |
; 2. Load system default icon for usage in this program |
; 3. Load system default cursor for usage in this program |
; 4. Register my window class |
; 5. Get module handle once again |
; 6. Generate the desired window, using information I pushed onto |
; da stack and data I placed within the WNDCLASS structure. |
; 7. Make the window show up |
; 8. Update the window (UpdateWindow API call will send a WM_PAINT |
; message to this program's window procedure |
; 9. Get da DC (Device Context) for this window |
; 10. Create a timer to send WM_TIMER message to our window procedure |
; 11. Send WM_CREATE message to window |
; |
; Registers/usage: |
; EAX = Holds return values from each API call. Also used for |
; loading with data which needs to be pushed X times. |
; Saves bytes |
; EBX = Zeroed. PUSHed instead of an immediate 0 to save bytes |
; |
; |
; |
;-----------------------------------------------------------------------------------

        xor ebx, ebx ; Make EBX register 0
                                             ; Save bytes on PUSHes
        push LPCTSTR ebx ; Pass NULL value
        call [GetModuleHandleA] ; Get da module handle
        mov [WCS+WNDCLASS.hInstance], eax ; Place returned module handle
                                             ; into hInstance member of
                                             ; WNDCLASS structure

        push LPCTSTR IDI_APPLICATION ; Request default icon
        push HINSTANCE ebx ; Pass NULL value
        call [LoadIconA] ; Get da icon handle
        mov [WCS+WNDCLASS.hIcon], eax ; Place returned icon handle
                                             ; into hIcon member of WNDCLASS
                                             ; structure

        push LPCTSTR IDC_ARROW ; Request default cursor
        push HINSTANCE ebx ; Pass NULL value
        call [LoadCursorA] ; Get da cursor handle
        mov [WCS+WNDCLASS.hCursor], eax ; Place returned cursor handle
                                             ; into hCursor member of WNDCLASS
                                             ; structure

        push DWORD WCS ; Pass pointer to WCS instance
                                             ; of WNDCLASS structure
        call [RegisterClassA] ; Register da class!

        push LPCTSTR ebx ; Pass a NULL value
        call [GetModuleHandleA] ; Yep, we're gettin da module
                                             ; handle

        push LPVOID ebx ; Pass NULL since we don't use
                                             ; the CREATESTRUCT or an MDI
        push HINSTANCE eax ; Pass the module handle
        push HMENU ebx ; Pass NULL since we have no menu
        push HWND ebx ; This window is no child window,
                                             ; so NULL is passed
        mov eax, CW_USEDEFAULT ; Place CW_USEDEFAULT into EAX
        push INTEGER eax ; Window height will be default
        push INTEGER eax ; Window width will be default
        push INTEGER eax ; Vertical position is default
        push INTEGER eax ; Horizontal position is default
        push DWORD WS_OVERLAPPEDWINDOW ; Combines WS_EX_CLIENTEDGE and
                                             ; WS_EX_WINDOWEDGE styles
        push LPCTSTR WindowName ; Caption of window
        push LPCTSTR WindowClass ; Registered window class
        push DWORD ebx ; No extended window style
        call [CreateWindowExA] ; Generate the darn window!
        mov [WindowHwnd], eax ; Save returned window value in
                                             ; WindowHwnd variable

        push INTEGER SW_SHOWNORMAL ; Specify that window will
                                             ; appear at normal size
        push HWND eax ; Pass window handle
        call [ShowWindow] ; Make da window appear!

        push HWND [WindowHwnd] ; Handle of window
        call [UpdateWindow] ; Send WM_PAINT message to da
                                             ; window procedure

        push HWND [WindowHwnd] ; Handle of window
        call [GetDC] ; Get da Device Context
        mov [MyDC], eax ; Save it!

        push DWORD NULL ; No timer procedure
        push UINT 1 ; 1 millisecond
        push UINT NULL ; Let system decide timer
                                             ; identifier
        push HWND [WindowHwnd] ; Da window handle!
        call [SetTimer] ; Make the timer!

        push LPARAM NULL ; No low word parameter
        push WPARAM NULL ; No high word parameter
        push UINT WM_CREATE ; WM_CREATE message
        push UINT [WindowHwnd] ; Window handle!
        call [SendMessageA] ; Do it!

;---------------------------------------------------------------------------
; |
; Message fetch and dispatch loop |
; |
; This loop is the heart of all activity in this program. It basically |
; fetches messages and dispatches the message to the window procedure. |
; |
; Program flow: |
; 1. Get da message |
; 2. Dispatch it |
; 3. Repeat da loop |
; |
; Registers/usage: |
; EAX = Holds return values for each API call. Also used to pass |
; NULL X times. |
; |
;----------------------------------------------------------------------------

Message_Loop:
        mov LPMSG ebx, Msg ; Place pointer to Msg instance for MSG struct
                                ; into EBX
        xor eax, eax ; Zero EAX
        push UINT eax ; No message filtering
        push UINT eax ; No message filtering
        push HWND eax ; Get messages for any window belonging to
                                ; this program
        push LPMSG ebx ; Pass EBX
        call [GetMessageA] ; Get da message!
        test eax, eax ; Did we get WM_QUIT?
        je Quit ; If so, then go to my program termination
                                ; routine

        push LPMSG ebx ; Pass that instance
        call [DispatchMessageA] ; Dispatch da message!
        jmp Message_Loop ; Keep the loop goin!

Quit:
        push UINT NULL ; Return a NULL exit code
        call [ExitProcess] ; Kill da program!

WindowProc:
%define hwnd ebp+8
%define uMsg ebp+12
%define wParam ebp+16
%define lParam ebp+20

        enter 0, 0 ; Allocate da stack frame for my program. This
                                ; line o code simply reserves enough space for
                                ; the message info on the stack

        cmp UINT [uMsg], WM_PAINT ; Did we get WM_PAINT?
            je NEAR PaintBitmap ; paint da bitmap
                                         ; Hopefully the rest is rather
                                         ; straightforward :)
        cmp UINT [uMsg], WM_CREATE
            je NEAR LoadShowBitmap
        cmp UINT [uMsg], WM_TIMER
            je NEAR FadeBitmap
        cmp UINT [uMsg], WM_DESTROY
            je NEAR PostQMSG
        
        push LPARAM [lParam] ; Pass low word paramter
        push WPARAM [wParam] ; Pass high word
        push UINT [uMsg] ; Pass window message
        push HWND [hwnd] ; Pass da window handle
        call [DefWindowProcA] ; Let default window procedure handle
                                         ; messages we'd rather ignore :)
        jmp unhandled ; Go to our routine that serves as
                                         ; an end point to all the others
PaintBitmap:

      push BOOL FALSE ; Don't erase window client area
      push DWORD Rct ; Update area that contains picture
      push HWND [hwnd] ; Pass da handle
      call [InvalidateRect] ; Mark the area for repainting

      mov ebx, [hwnd]
      push DWORD PaintSt ; Pass instance to PAINTSTRUCT
      push DWORD ebx ; pass da handle
      call [BeginPaint] ; Update area set by previous API call

      push DWORD SRCCOPY ; Copy source to destination as is
      xor eax, eax ; The byte saving technique!
      push INTEGER 384 ; 384 pixels vertical source
      push INTEGER 384 ; 384 pixels horizontal source
      push INTEGER eax ; 0 horizontal axis source
      push INTEGER eax ; 0 vertical axis source
      push HDC [mDC] ; Source device context containing pic
      push INTEGER 384 ; 384 pixels vertical destination
      push INTEGER 384 ; 384 pixels horizontal destination
      push INTEGER eax ; 0 horizontal axis destination
      push INTEGER eax ; 0 vertical axis destination
      push HDC [MyDC] ; The window client area
      call [StretchBlt] ; Blit the pic!

      push DWORD PaintSt ; PAINTSTRUCT
      push DWORD ebx ; Window handle
      call [EndPaint] ; End the paint operation

      jmp unhandled ; Go to the end point

FadeBitmap:
        IsFadeInDone:
              cmp DWORD [FFlag], 1 ; Has the image already faded out?
              jne InitFadeRoutine ; If not, go to initialization code
                                         ; for fade in

        InitFadeInRoutine:
                        mov eax, [MemPointer2] ; Get pointer to allocated
                                               ; memory holding original
                                               ; image into EAX
                        mov eax, [eax+000ah] ; Retrieve offset to bitmap
                                               ; data from entry in bitmap
                                               ; header
                        add eax, [MemPointer2] ; Add this offset to the
                                               ; memory pointer
                        mov [BitmapData2], eax ; Place pointer to bitmap data
                                               ; in memory into BitmapData2
                                               ; variable
                        mov edi, eax ; EDI will now contain the
                                               ; bitmap data pointer
                        mov edx, [BitmapData] ; Get pointer to bitmap data
                                               ; in first allocated chunk of
                                               ; memory holding the fading
                                               ; image
                        mov ecx, [BitmapSize] ; Get the total number of
                                               ; bitmap data bytes into ECX

                        push ecx ; Save registers onto stack :)
                        push edx
                        push edi

        DoFadeInLoop:
                    mov eax, DWORD [edi] ; Grab pixel in second chunk
                                               ; pointed to by EDI
                    mov ebx, DWORD [edx] ; Grab pixel in first chunk
                                               ; pointed to by EDX
                    cmp bl, al ; Is the pixel in both images
                                               ; the same value?
                    je SkipInFade ; If so, skip to next pixel
                    inc ebx ; Increment pixel value
                    mov [edx], ebx ; Overwrite current pixel value
                                               ; in first chunk
        SkipInFade:
                    test ecx, ecx ; Have all the pixels been
                                               ; incremented?
                    je CheckIfInAllDone ; If so, check to make sure
                                               ; the pic has been fully
                                               ; restored
                    dec ecx ; Another byte to go!
                    inc edx ; Point to next pixel in fading img
                    inc edi ; Point to next pixel in original img
                    jmp DoFadeInLoop ; Repeat the loop

        CheckIfInAllDone:
                    pop edi ; Restore da registers
                    pop edx
                    pop ecx
                DoCheckInLoop:
                    mov eax, DWORD [edi] ; Load EAX with pixel pointed to in original img
                    mov ebx, DWORD [edx] ; Load EBX with pixel pointed to in fading img
                    cmp bl, al ; Are both pixels the same?
                    jne DeleteHandles ; Display the newly faded image
                    inc edx ; Point to next byte in fading image
                    inc edi ; Point to next byte in original image
                    test ecx, ecx ; Has all the bytes been checked?
                    je CheckInDone ; Yep, signify that the fade in has complete
                    dec ecx ; Another byte to go!
                    jmp DoCheckInLoop ; Check next byte :)

                         CheckInDone:
                                xor ebx, ebx ; Obvious
                                mov [FFlag], ebx ; Make FFlag variable contain 0
                                jmp unhandled ; End the routine!

        InitFadeRoutine:
                mov ebx, [FileSize]
                mov ecx, [MemPointer]
                mov ecx, [ecx+000ah]
                sub ebx, ecx
                xchg ebx, ecx
                mov [BitmapSize], ecx
                mov edx, [BitmapData]
                push ecx
                push edx

        DoFadeLoop:
                 FadeDDSubloopInit:
                    mov ebx, DWORD [edx]
                    cmp bl, 0
                    je SkipFade
                    dec ebx
                    mov [edx], ebx
        SkipFade:
                    test ecx, ecx
                    je CheckIfAllDone
                    dec ecx
                    inc edx
                    jmp DoFadeLoop

        CheckIfAllDone:
                    pop edx
                    pop ecx
                DoCheckLoop:
                    mov ebx, DWORD [edx]
                    cmp bl, 0
                    jne DeleteHandles
                    inc edx
                    test ecx, ecx
                    je CheckDone
                    dec ecx
                    jmp DoCheckLoop

                         CheckDone:
                                xor ebx, ebx
                                inc ebx
                                mov [FFlag], ebx
                                jmp unhandled

DeleteHandles:

      push HDC [mDC]
      call [DeleteDC]

      push HDC [BmpHandle]
      call [DeleteObject]

      jmp DispBitmap

PostQMSG:

      push HDC [mDC]
      call [DeleteDC]

      push HDC [BmpHandle]
      call [DeleteObject]

      push DWORD [MemObject]
      call [GlobalUnlock]

      push DWORD [MemObject]
      call [GlobalFree]

      push DWORD [FileHwnd]
      call [CloseHandle]

      push INTEGER NULL
      call [PostQuitMessage]

break:
        xor eax, eax

unhandled:
        leave
        ret

DispBitmap:
; mov eax, [MemPointer]
; mov eax, [eax+000ah]
; mov ebx, eax

      mov eax, [MemPointer]
      mov eax, [eax+000ah]
      add eax, [MemPointer]
      mov [BitmapData], eax
      mov eax, [MemPointer]

      mov ebx, [eax+0012h]
      mov [Rct+RECT.right], ebx
      mov ebx, [eax+0016h]
      mov [Rct+RECT.bottom], ebx
      xor ebx, ebx

      push UINT DIB_RGB_COLORS
      lea eax, [eax+000eh]
      push DWORD eax
      push DWORD [BitmapData]
      push DWORD CBM_INIT
      push DWORD eax
      push HDC [MyDC]
      call [CreateDIBitmap]
      mov [BmpHandle], eax

      push HDC [MyDC]
      call [CreateCompatibleDC]
      mov [mDC], eax

      push DWORD [BmpHandle]
      push DWORD eax
      call [SelectObject]

      mov ecx, [MemPointer]
      mov edx, [ecx+0016h]
      mov esi, [ecx+0012h]
      push DWORD SRCCOPY
      xor eax, eax
      push INTEGER edx
      push INTEGER esi
      push INTEGER eax
      push INTEGER eax
      push HDC [mDC]
      push INTEGER edx
      push INTEGER esi
      push INTEGER eax
      push INTEGER eax
      push HDC [MyDC]
      call [StretchBlt]

      jmp break

LoadShowBitmap:
      xor ebx, ebx
      push DWORD ebx
      push DWORD FILE_ATTRIBUTE_NORMAL
      push DWORD OPEN_EXISTING
      push DWORD ebx
      push DWORD FILE_SHARE_READ
      push DWORD GENERIC_READ
      push DWORD BitmapPath
      call [CreateFileA]
      mov [FileHwnd], eax
     
      push DWORD ebx
      push DWORD [FileHwnd]
      call [GetFileSize]
      mov [FileSize], eax

      add eax, 4
      push DWORD eax
      push UINT GMEM_MOVEABLE | GMEM_ZEROINIT
      call [GlobalAlloc]
      mov [MemObject], eax

      push DWORD eax
      call [GlobalLock]
      mov [MemPointer], eax

      push DWORD ebx
      push LPDWORD NumBytesRead
      push DWORD [FileSize]
      push DWORD [MemPointer]
      push HANDLE [FileHwnd]
      call [ReadFile]

MakeSecondCopyInMem:
      mov eax, [FileSize]

      add eax, 4
      push DWORD eax
      push UINT GMEM_MOVEABLE | GMEM_ZEROINIT
      call [GlobalAlloc]
      mov [MemObject2], eax

      push DWORD eax
      call [GlobalLock]
      mov [MemPointer2], eax

      push DWORD FILE_BEGIN
      push DWORD NULL
      push DWORD NULL
      push HWND [FileHwnd]
      call [SetFilePointer]

      push DWORD ebx
      push LPDWORD NumBytesRead
      push DWORD [FileSize]
      push DWORD [MemPointer2]
      push HANDLE [FileHwnd]
      call [ReadFile]

      push DWORD [FileHwnd]
      call [CloseHandle]

      jmp DispBitmap

SECTION DATA USE32 CLASS=DATA
BitmapData resd 1
BitmapData2 resd 1
BitmapSize resd 1

WCS:
ISTRUC WNDCLASS
at WNDCLASS.style, DD CS_HREDRAW | CS_VREDRAW
at WNDCLASS.lpfnWndProc, DD WindowProc
at WNDCLASS.cbClsExtra, DD 0
at WNDCLASS.cbWndExtra, DD 0
at WNDCLASS.hInstance, DD 0
at WNDCLASS.hIcon, DD 0
at WNDCLASS.hCursor, DD 0
at WNDCLASS.hbrBackground, DD COLOR_WINDOW + 1
at WNDCLASS.lpszMenuName, DD 0
at WNDCLASS.lpszClassName, DD WindowClass
IEND

Msg:
ISTRUC MSG
IEND

FileHwnd resd 1
FileSize resd 1
MemObject resd 1
MemPointer resd 1
MemObject2 resd 1
MemPointer2 resd 1

NumBytesRead resd 1

WindowClass db "MyClass", 0
WindowName db "Al's Window", 0
WindowHwnd resd 1
MyDC resd 1
mDC resd 1
BmpHandle resd 1
BitmapPath db "red.bmp", 0
FFlag dd 0

PaintSt:
ISTRUC PAINTSTRUCT
IEND

Rct:
ISTRUC RECT
at RECT.left, DD NULL
at RECT.top, DD NULL
at RECT.right, DD NULL
at RECT.bottom, DD NULL
IEND