flat assembler
Message board for the users of flat assembler.

Index > Windows > [Example] Sprite with GDI

Author
Thread Post new topic Reply to topic
Krecik



Joined: 19 Jul 2012
Posts: 13
Krecik
I'm going to write a simple open-source 2D game in FASM. I know that it is a long way for the beginner but I would like to seek your opinion.

Here is my algorithm for copying a image to buffer:
Code:
macro Sprite Image*, DestX*, DestY*, SrcWidth*, SrcHeight*
{
local .height, .width, .transparent
 xor ecx, ecx
 xor edx, edx
 .height:
 xor eax, eax ;X
 .width:
 cmp byte [Image+eax*4+3+ecx], 0 ;If byte is transparent
 jnz .transparent ;Then go to .transparent
 mov ebx, [Image+eax*4+ecx]
 mov [Buffer+DestX*4+DestY*Width*4+eax*4+edx], ebx
 .transparent:
 inc eax
 cmp eax, SrcWidth
 jb .width
 add ecx,SrcWidth*4
 add edx,Width*4
 cmp ecx,SrcWidth*SrcHeight*4
 jb .height
 }    

How to use:
• First we copy as much as we want images to buffer
Code:
Sprite  MyImage1, 400, 200, 150, 100
Sprite  MyImage2, 50, 10, 40, 200
...    

• Than we call framebuffer.
Code:
invoke  SetDIBitsToDevice,[hDC],0,0,Width,Height,0,0,0,Height,Buffer,bmi,0    

Just simple.

I attach example with source that display "monster" image.

What is the image.bin file in the attachment? It is just image converted specially for my example. Every pixel consist of 4 bytes (the first three are RGB and 4th is transparent setting). My code works only with this images format.

What do you think about it?

#Edit: I attach fixed files.
#Edit 2: I changed a few things. Look here


Description:
Download
Filename: GDI Sprite.zip
Filesize: 6.99 KB
Downloaded: 84 Time(s)



Last edited by Krecik on 31 Jul 2012, 20:39; edited 3 times in total
Post 22 Jul 2012, 09:45
View user's profile Send private message Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3500
Location: Bulgaria
JohnFound
You need to rework your example. It is inexcusable to have 1.8MB .exe file because of data block full of $FF
Post 22 Jul 2012, 09:55
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Krecik



Joined: 19 Jul 2012
Posts: 13
Krecik
JohnFound:
Right. I try create an array of bits by using VirtualAlloc but I must do something wrong because it crashes.

Look here, please:
Code:
; Settings
  Width = 800   
  Height = 600   
  R = 255   
  G = 255  
  B = 255  
; Code   

format PE GUI 4.0   
entry start   

include 'win32w.inc'   

section '.text' code readable executable   

  start:   

        invoke  GetModuleHandle,0   
        mov     [wc.hInstance],eax   
        invoke  LoadIcon,0,IDI_APPLICATION   
        mov     [wc.hIcon],eax   
        invoke  LoadCursor,0,IDC_ARROW   
        mov     [wc.hCursor],eax   
        invoke  RegisterClass,wc   
        test    eax,eax   
        jz      error   

        invoke  CreateWindowEx,0,_class,_title,WS_VISIBLE+WS_DLGFRAME+WS_SYSMENU,0,0,800,600,NULL,NULL,[wc.hInstance],NULL 
        mov     [hwnd],eax   
        invoke GetDC,[hwnd]   
        mov [hDC],eax   
        mov [bmi.biSize],sizeof.BITMAPINFOHEADER   
        mov [bmi.biWidth],Width   
        mov [bmi.biHeight],-Height 
        mov [bmi.biPlanes],1  
        mov [bmi.biBitCount],32 
        mov [bmi.biCompression],BI_RGB  

macro Sprite Image*, DestX*, DestY*, SrcWidth*, SrcHeight*
{
local .height, .width, .transparent
 xor ecx, ecx
 xor edx, edx
 .height:
 xor eax, eax ;X
 .width:
 cmp byte [Image+eax*4+3+ecx], 0 ;If byte is transparent
 jnz .transparent ;Then go to .transparent
 mov ebx, [Image+eax*4+ecx]
 mov [Buffer+DestX*4+DestY*Width*4+eax*4+edx], ebx
 .transparent:
 inc eax
 cmp eax, SrcWidth
 jb .width
 add ecx,SrcWidth*4
 add edx,Width*4
 cmp ecx,SrcWidth*SrcHeight*4
 jb .height
 }
  msg_loop:
        invoke VirtualAlloc,[Buffer],Width*Height*4,MEM_COMMIT,PAGE_READWRITE
        mov [Buffer], eax
        Sprite  MyMonster, 200, 100, 64, 64
        Sprite  MyMonster, 50, 500, 64, 64
        Sprite  MyMonster, 500, 400, 64, 64
        invoke  SetDIBitsToDevice,[hDC],0,0,Width,Height,0,0,0,Height,Buffer,bmi,0
        invoke  GetMessage,msg,NULL,0,0  
        cmp     eax,1   
        jb      end_loop   
        jne     msg_loop   
        invoke  TranslateMessage,msg   
        invoke  DispatchMessage,msg  
        jmp     msg_loop   

  error:   
        invoke  MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK   

  end_loop:   
        invoke  ExitProcess,[msg.wParam]

proc WindowProc uses ebx esi edi, hwnd,wmsg,wparam,lparam   
        cmp     [wmsg],WM_DESTROY   
        je      .wmdestroy  
  .defwndproc:  
        invoke  DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]   
        jmp     .finish   
  .wmdestroy:   
        invoke  PostQuitMessage,0   
        xor     eax,eax  
  .finish:  
        ret   
endp

section '.data' data readable writeable   

  _class TCHAR 'FASMWIN32',0   
  _title TCHAR 'Sprite Test',0
  _error TCHAR 'Startup failed.',0   

  wc WNDCLASS 0,WindowProc,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,_class   

  msg MSG   
  hDC dd 0   
  hwnd dd 0   
  bmi BITMAPINFOHEADER   
  MyMonster: file 'image.bin'
  Buffer  dd 0

section '.idata' import data readable writeable   

  library kernel32,'KERNEL32.DLL',\   
          gdi32, 'GDI32.DLL',\   
          user32,'USER32.DLL'   

  include 'api\kernel32.inc'   
  include 'api\gdi32.inc'   
  include 'api\user32.inc'     
Post 22 Jul 2012, 12:49
View user's profile Send private message Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3500
Location: Bulgaria
JohnFound
You can use static arrays. Only place it at the last place and define it as "?":
Code:
section '.data' data readable writeable

  _class TCHAR 'FASMWIN32',0
  _title TCHAR 'Sprite Test',0
  _error TCHAR 'Startup failed.',0

  wc WNDCLASS 0,WindowProc,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,_class

  MyMonster: file 'image.bin'
  msg MSG
  hDC dd ?
  hwnd dd ?
  Buffer  rd Width*Height
  bmi BITMAPINFOHEADER
    


And then if you really need white background, put at the beginning:
Code:
        mov     edi, Buffer
        mov     ecx, Width*Height
        mov     eax, $ffffff
        rep stosd    
Post 22 Jul 2012, 13:31
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Picnic



Joined: 05 May 2007
Posts: 1288
Location: behind the arc
Picnic
Hi,

VirtualAlloc returns a pointer to the address of the allocated memory. You place that pointer in [Buffer]

So pass that pointer to function with brackets.
Code:
 
invoke  SetDIBitsToDevice,[hDC],0,0,Width,Height,0,0,0,Height,[Buffer],bmi,0 
    


A small fix here
Code:
;mov [Buffer+DestX*4+DestY*Width*4+eax*4+edx], ebx   
lea edi, [DestX*4+DestY*Width*4+eax*4+edx]
add edi, [Buffer]
mov [edi], ebx  
    


p.s. I don't know what SetDIBitsToDevice does, but it seems to work now... Smile
Post 22 Jul 2012, 14:41
View user's profile Send private message Reply with quote
Krecik



Joined: 19 Jul 2012
Posts: 13
Krecik
Thank you both.
I attach new files. Now EXE has 18,5 KB. I hope this size is enough small.

JohnFound:
Can you explain me how your code works?
Code:
        mov     edi, Buffer 
        mov     ecx, Width*Height 
        mov     eax, $ffffff 
        rep stosd    



Btw what do you think about my idea "sprite with GDI"?
Post 22 Jul 2012, 15:42
View user's profile Send private message Reply with quote
bzdashek



Joined: 15 Feb 2012
Posts: 147
Location: Tolstokvashino, Russia
bzdashek
Good job, Krecik
Post 23 Jul 2012, 05:47
View user's profile Send private message Reply with quote
r22



Joined: 27 Dec 2004
Posts: 805
r22
@Krecik
Code:
        mov     edi, Buffer ;; address
        mov     ecx, Width*Height ;; size
        mov     eax, $ffffff ;; value
        rep stosd ;; repeat store dword
    

It's equivalent to the "memset" api.
REP is an opcode prefix that will repeat the opcode after it, ECX (value of count register) number of times (in this case until ECX = 0).
STOSD will store the dword value of EAX at the address of EDI it will then increment or decrement EDI by 4 bytes (depending on the DF-directional flag).

You can lookup up these instructions in the manual.
Post 23 Jul 2012, 11:59
View user's profile Send private message AIM Address Yahoo Messenger Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1409
Location: Toronto, Canada
AsmGuru62
@Krecik:
You call VirtualAlloc in your main message loop and never call VirtualFree.
Is that right? So, how long the game plays?
Post 23 Jul 2012, 14:41
View user's profile Send private message Send e-mail Reply with quote
Enko



Joined: 03 Apr 2007
Posts: 678
Location: Mar del Plata
Enko
Quote:

Btw what do you think about my idea "sprite with GDI"?

I did once a simple sprite 2d engine with gdi.

The problem is alpha and transparent sprites.

its not a bad idea implement some linked list for the sprites.

An example:
Sprite.Move
;moves function to move the sprite,
Code:
mySprite SPRITE 
mov [mySprite.Move], mySpriteProc

proc mySpriteProc
ret
endp
    

Sprite.doCollison
The proc you call if the sprite collied with another sprite.

Sprite.Draw
;same thing


Engine.AddSprite
Code:
Engine.AddSprite, mySprite
    

Engine.Move
Code:
;pseudocode
for i = 0, i<= Engine.SpriteCount
{
   Engine.Sprite[i].Move();
}
;you call al the Move procs of the sprites added to the engine
    

Engine.Draw
;same thing.

Engine.RemoveSprite

Engine.TestCollision
Code:
;test the collition bettween all the sprites of the engine
somthing like
for i=0, i<=Engine.Sprite.Count 
   for j=i, j<=Engine.SpriteCount
      if  Collision(Engine.Sprite[i], Engine.Sprite[j])
                Engine.Sprite[i].doCollision()
                Engine.Sprite[j].doCollision()
    
Post 23 Jul 2012, 15:12
View user's profile Send private message Reply with quote
Krecik



Joined: 19 Jul 2012
Posts: 13
Krecik
r22:
Thank you for explain. Now I understand it.

AsmGuru62:
I think you are right but I don't use Virtal~ functions yet.

Enko:
You can use my transparent method in your engine.
Post 24 Jul 2012, 13:49
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1409
Location: Toronto, Canada
AsmGuru62
I am thinking of writing a small demo with sprites.
But I do not want to draw them (sprites) -- where I can download some ready images?
Post 24 Jul 2012, 14:01
View user's profile Send private message Send e-mail Reply with quote
Enko



Joined: 03 Apr 2007
Posts: 678
Location: Mar del Plata
Enko
Post 24 Jul 2012, 14:37
View user's profile Send private message Reply with quote
Krecik



Joined: 19 Jul 2012
Posts: 13
Krecik
I recently worked on the my project and I added few changes.

Here is a new algorithm for copying sprites to buffer:
Code:
fSprite:
    imul ecx, [ScreenWidth]
    add ecx, ebx
    imul ecx, 4
    mov ebx, [ScreenWidth]
    imul ebx, 4
    mov esi, [eax]
    imul esi, [eax+4]
    xor edx, edx
    xor edi, edi
    .height:
    .width:
    cmp byte [eax+8+edx*4+3], 0
    jnz .transparent
    mov ebp, [eax+8+edx*4]
    mov [Buffer+edi*4+ecx], ebp
  .transparent:
    inc edx
    inc edi
    cmp edi, [eax]
    jb .width
    xor edi, edi
    add ecx, ebx
    cmp edx, esi
    jb .height
    ret
    

Now it is a normal function which you can call using macro Sprite.
Code:
macro Sprite Image*, DestX*, DestY*
{
  mov ecx, DestY
  mov ebx, DestX
  lea eax, [Image]
  call fSprite
}        

Example parameters for this macro:
Code:
Sprite Image123, 300, 50    


If you take look for a image.bin you can see that it has been changed too. Now the first two bytes are width and height of the image. The rest of my file type is the same.

I added also a speed test. It shows how many sprites are copying to buffer per second.

Just look for a new attachment!

My computer:
Quote:
Windows XP
AMD Athlon(tm) 64 X2 Dual
Core Processor 4800+
2.50 GHz

Displays 60,000 - 70, 000 spr/sec.

I have a question: how can I simply switch between 32-bit and 64-bit OS without code editing (include win32ax/64ax doesn't work) O maybe is some method to do program working on both?
Post 31 Jul 2012, 20:36
View user's profile Send private message Reply with quote
Madis731



Joined: 25 Sep 2003
Posts: 2141
Location: Estonia
Madis731
You can make an assembly time variable that you change and make the necessary edits in your code, but there is no such thing as a universal code. There must be 2 paths in your code: one for 32 and the other for 64 bits. Btw, does anyone know the statistics about 64-bit OS-s being used around the world?

You can change
imul ecx, 4
imul ebx, 4
to
shl ecx,2
shl ecx,2
to make the setup a bit faster for smaller sprites.

You could also get rid of the transparency check by reading both SRC and DST pixels and then conditionally (CMOVxx) overwriting the DST.
Post 01 Aug 2012, 12:12
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1409
Location: Toronto, Canada
AsmGuru62
I think PE formats also different between 32-bit and 64-bit.
So, to run it natively -- it needs to have been compiled into two PE files.
Post 01 Aug 2012, 13:26
View user's profile Send private message Send e-mail Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17271
Location: In your JS exploiting you and your system
revolution
The main difference with 64-bit is the addressing, so in most circumstances it isn't actually necessary to write 64-bit code unless you need to address large (> ~2GB-3GB) portions of memory. In many cases using the 32-bit binary is perfectly usable.

Of course you can't use the 64-bit native integer arithmetic unless you use 64-bit code but often this is not a deal-breaker as many compute intensive applications use the FPU/SSE units.
Post 01 Aug 2012, 14:01
View user's profile Send private message Visit poster's website Reply with quote
Xorpd!



Joined: 21 Dec 2006
Posts: 161
Xorpd!
GoAsm has some tools to enable 32/64 bit code on the source code level to some extent. I have never used them, though. You can write a 32/64 bit binary but I think you would also need to write 32-bit and 64-bit stub programs that load the 32/64 bit binary into memory and jump into it because I don't think Windows lets you specify such an executable or dll. Normally the 32/64 bit code would start with a test for bitness then jump to the correct code. Code that just contained an instruction stream interpretable as something useful in both 32-bit and 64-bit modes would be extremely beautiful but also challenging to compose.
Post 01 Aug 2012, 17:42
View user's profile Send private message Visit poster's website Reply with quote
Madis731



Joined: 25 Sep 2003
Posts: 2141
Location: Estonia
Madis731
To an assembly programmer 64-bit is highly valuable because of the 8 additional GPRs and SSE registers.

Where I'm getting at is the world might be switching over to 64-bit completely just because people are starting to have 4GB+ RAM on board and they need to address 2GB of video memory and what-not.

For example if we take this 32-bit fSprite call then you can see that we're running out of registers. Rewriting this function to 64-bit can achieve better performance and less LOC.

Btw, I love this idea, but I agree that its nearly impossible:
Xorpd! wrote:
Code that just contained an instruction stream interpretable as something useful in both 32-bit and 64-bit modes would be extremely beautiful but also challenging to compose.
Post 02 Aug 2012, 06:44
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar.

Powered by rwasa.