News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Beginners Graphic Questions

Started by Jimg, December 01, 2008, 08:10:19 PM

Previous topic - Next topic

Jimg

I've been programming for a long time, but I've mostly avoided graphics like the plague.  For some reason I have the itch to do some in my old age.

I've attached a quicky program for testing that just BitBlt's some card images that I extracted from cards.dll, to a window.

I have two questions to help me get started here. 

1.  Without going crazy with directx/opengl/etc, whats the method to write code to drag and drop the bitmaps I've drawn on the screen?  I'm thinking the ImageList api functions, but being a beginner, I thought I'd ask first before wasting a lot of time going in the wrong direction.  I'd like to be able to drag all 4 images as a group with the underlying images/background appearing in the gap left as needed.

2.  What is the method to make the three dots in each corner transparent?  As you can see in the example, they are just white for now and wipe out the background.   Is this again something that would be done with ImageList or is there some more efficient method?

I realize it would probably take a book on the subject to cover all aspects of this stuff, but I'd appreciate a little direction at this stage.

[attachment deleted by admin]

jj2007

Quote from: Jimg on December 01, 2008, 08:10:19 PM

1.  Without going crazy with directx/opengl/etc, whats the method to write code to drag and drop the bitmaps I've drawn on the screen?

- Check for a mouse down event
- if it's inside your bitmap,
- wait for a mouse move
- restore the background, either by painting it or by blitting a copy of the background
- blit your bitmaps to the new position

Quote
2.  What is the method to make the three dots in each corner transparent?  As you can see in the example, they are just white for now and wipe out the background.   Is this again something that would be done with ImageList or is there some more efficient method?

You have 1-bit bitmaps. Your code cannot know whether the white pixel is inside or outside. There are two ways to deal with it:
1. Go for more colours, and make the edges identical to your background colour
2. Check the roundrect clipping functions; i.e. create a clipping rounded rectangle for the cards, and apply it to the position where you want to blit it:
invoke CreateRoundRectRgn, ...
invoke SelectClipRgn, ...

Enough for some sleepless nights?
:thumbu

Mark Jones

"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

MichaelW

Jim,

I don't know much about this either, but I think the MaskBlt function was intended for this purpose. Using a mask I created with Paint, this code appears to work correctly, although it seemed to me that the background raster operation code should have been SRCPAINT, and the ROP encoding is more complex than the simple constants I expected.

include \masm32\include\masm32rt.inc

inv EQU invoke

DlgProc PROTO hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD

.list

.data?
hWin  dd ?
buff  db 100 dup (?)
ps  PAINTSTRUCT <>
hdc HDC ?
mDC HDC ?

hBmp1   dd ?
hBmp2   dd ?
hBmp3   dd ?
hBmp4   dd ?
;;;
hBmpMask  dd ?
;;;
btmap   BITMAP <>
strtx   dd ?
strty   dd ?
.data
file1 db 'Bitmap_1.bmp',0
file2 db 'Bitmap_2.bmp',0
file3 db 'Bitmap_3.bmp',0
file4 db 'Bitmap_4.bmp',0
;;;
fileMask db 'mask.bmp',0
;;;
.code
Program:
    inv InitCommonControls
    inv GetModuleHandle,0
    inv DialogBoxParam,eax,101,0,DlgProc, 0
    inv ExitProcess, eax

Loadbmp proc indx,filename
    inv LoadImage,0,filename,IMAGE_BITMAP,0,0,LR_LOADFROMFILE
    mov ecx,indx
    mov hBmp1[ecx*4-4],eax
ret
Loadbmp endp
Showbmp proc indx
    mov eax,indx
    inv SelectObject,mDC,hBmp1[eax*4-4]
    ;inv BitBlt,hdc,strtx,strty,btmap.bmWidth, btmap.bmHeight ,mDC,0,0,SRCCOPY
;=============================================================================
;#define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))

    inv MaskBlt,hdc,strtx,strty,btmap.bmWidth, btmap.bmHeight ,mDC,0,0,
                hBmpMask,0,0,((MERGEPAINT SHL 8) AND 0ff000000h) OR SRCCOPY
comment |
BOOL MaskBlt(
  HDC hdcDest,     // handle to destination DC
  int nXDest,      // x-coord of destination upper-left corner
  int nYDest,      // y-coord of destination upper-left corner
  int nWidth,      // width of source and destination
  int nHeight,     // height of source and destination
  HDC hdcSrc,      // handle to source DC
  int nXSrc,       // x-coord of upper-left corner of source
  int nYSrc,       // y-coord of upper-left corner of source
  HBITMAP hbmMask, // handle to monochrome bit mask
  int xMask,       // horizontal offset into mask bitmap
  int yMask,       // vertical offset into mask bitmap
  DWORD dwRop      // raster operation code
);

BOOL BitBlt(
  HDC hdcDest, // handle to destination DC
  int nXDest,  // x-coord of destination upper-left corner
  int nYDest,  // y-coord of destination upper-left corner
  int nWidth,  // width of destination rectangle
  int nHeight, // height of destination rectangle
  HDC hdcSrc,  // handle to source DC
  int nXSrc,   // x-coordinate of source upper-left corner
  int nYSrc,   // y-coordinate of source upper-left corner
  DWORD dwRop  // raster operation code
);
|
;=============================================================================
    mov eax,btmap.bmWidth
    ;add strtx,eax
    add strtx,20
    add strty,20
ret
Showbmp endp   
DlgProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
    .if uMsg == WM_COMMAND
        ;.if    wParam == buttonid
        ;.endif
    .elseif uMsg== WM_INITDIALOG
        mov eax,hWnd    ; save handle for everyone
        mov hWin,eax
        inv Loadbmp,1,addr file1
        inv Loadbmp,2,addr file2
        inv Loadbmp,3,addr file3
        inv Loadbmp,4,addr file4
        ;;;
        inv Loadbmp,5,addr fileMask
        ;;;

    .elseif uMsg==WM_PAINT
        inv BeginPaint,hWnd,ADDR ps
        mov hdc,eax
        inv CreateCompatibleDC,eax
        mov mDC,eax

        mov strtx,0
        mov strty,0
        inv GetObject,hBmp1,sizeof btmap,addr btmap
       
        inv Showbmp,1
        inv Showbmp,2
        inv Showbmp,3
        inv Showbmp,4

        inv DeleteDC,mDC
        inv EndPaint,hWnd,ADDR ps
    .elseif uMsg == WM_CLOSE
        inv DeleteObject,hBmp1
        inv DeleteObject,hBmp2
        inv DeleteObject,hBmp3
        inv DeleteObject,hBmp4
        inv EndDialog,hWin,0
    .endif

    xor eax,eax
    ret
DlgProc endp

end Program



[attachment deleted by admin]
eschew obfuscation

Jimg

Thanks guys, that should keep me busy for awhile :U

jj-

  So for 1, I need to
    a. make a copy bitmap of everything except the images being moved
    b. bitblit the copy
    c. bitblit all the images the user is dragging
    d. repeat b and c on every mousemove event

  Thanks, that would be my first inclination, but I wanted to be
    sure there wasn't some easy api I should be using instead.

Mark-
   
   Thanks for the link.  Ultrano's library looks interesting, probably overkill
      for what I'm doing and I want to learn how to do it myself also.
      I've stashed it away for later.

Michael-

   Thanks, that worked well.  My first inclination would have been to blit the
    first two rows, the main block, and the bottom two rows.  Five bitblits
    would probably have been much slower.  Now for some testing :bg

   

jj2007

Quote from: Jimg on December 02, 2008, 03:49:52 PM
jj-

  So for 1, I need to
    a. make a copy bitmap of everything except the images being moved
    b. bitblit the copy
    c. bitblit all the images the user is dragging
    d. repeat b and c on every mousemove event


More precisely, you can draw your background only once, and then save, to a temporary bitmap, only the area you will overwrite. That is faster, and since you can use the same coordinates twice, it's also not very complicated.
Re the corners, clipping does the job, but MichaelW's mask might be equally elegant. The solution with the background colour is not feasible if you draw over different backgrounds, e.g. over another game card.

Jimg

#6
I've been working on this a bit.  I have a question about what's faster.  I'm not looking for the absolute fastest, I just hate looking stupid when someone else looks at my code :wink

Using BitBlt, I've found that it is slightly faster to select the bitmaps into a compatible memory dc, and then BitBlt from this dc as needed (test2, 7200 ticks), rather than doing a SelectObject into a dc of a bitmap each time (test1, 7500 ticks).

In my time tests, I've found that it is much faster to BitBlit from a memory dc into the dc of my dialog (test3, 2100 ticks), than it is to BitBlit from one memory dc to another.  Why is that??

Is there another faster way to copy these images from one dc to another?

I've attached my messy graphics time test program for anyone that wants.  Tests 1-3 are what I'm looking at.

Here's my code:

Setup:
hPen      DWORD ?
hBrush    DWORD ?
lb        LOGBRUSH <?>
hctrl dd ?
hdc dd ?

mbmp dd ?
sbmp dd ?
tbmp dd ?

mdc HDC ?   ; memory dc of hidden buffer
sdc HDC ?   ; scratch dc for card images
tdc HDC ?   ; scratch dc for single card image
hBmpMask dd ?
hBmp1   dd 4 dup (?)    ; room for 4 card images at present
btmap   BITMAP <>
CWidth  equ btmap.bmWidth
CHeight equ btmap.bmHeight
crect RECT <>
mwidth equ crect.right   ;total width in pixels
mheight equ crect.bottom ;total height in pixels
hbkcolor dd ?
bkcolor dd ?
strtx   dd ?
strty   dd ?
.data
fileMask db 'mask.bmp',0
file1 db 'Bitmap1.bmp',0
file2 db 'Bitmap2.bmp',0
file3 db 'Bitmap3.bmp',0
file4 db 'Bitmap4.bmp',0


Tests:
Test1:
    Desc 'SelectObject/BitBlt to hidden buffer from bmp'

    inv LoadImage,0,addr file2,IMAGE_BITMAP,0,0,LR_LOADFROMFILE
    mov hBmp1,eax
    inv GetObject,hBmp1,sizeof btmap,addr btmap ; get card info

    inv GetClientRect,hWin,addr crect    ; get the size needed for the bitmap
    inv GetDC,hWin  ; get dc's
    mov hdc,eax
    inv CreateCompatibleDC,hdc      ; create dc for hidden buffer
    mov mdc,eax
    inv CreateCompatibleDC,hdc      ; create dc for single card image
    mov tdc,eax

    inv CreateCompatibleBitmap,hdc, mwidth, mheight ; make a hidden buffer of the appropriate size
    mov mbmp,eax
    inv SelectObject, mdc, mbmp
    inv DeleteObject,eax        ; delete old bitmap
    inv CreateCompatibleBitmap,tdc,btmap.bmWidth,btmap.bmHeight ; make one of the appropriate size for card images
    mov tbmp,eax
       
    mov strtx,10
    mov strty,20

    BeginCounter
        inv SelectObject,tdc,hBmp1  ; get a card
        inv BitBlt,mdc,strtx,strty,btmap.bmWidth,btmap.bmHeight,tdc,0,0,SRCCOPY
    EndCounter

    inv BitBlt,hdc,strtx,strty,btmap.bmWidth,btmap.bmHeight,mdc,0,0,SRCCOPY
    inv ReleaseDC,hWin,hdc
    inv DeleteObject,mbmp
    inv DeleteDC,mdc
    inv DeleteDC,tdc
    inv DeleteObject,hBmp1
    ;Showit x
    ret

Test2:
    Desc 'BitBlt to hidden buffer from compatible buffer containing images'

    inv LoadImage,0,addr file1,IMAGE_BITMAP,0,0,LR_LOADFROMFILE
    mov hBmp1,eax
    inv GetObject,hBmp1,sizeof btmap,addr btmap ; get card info

    inv GetClientRect,hWin,addr crect    ; get the size needed for the bitmap
    inv GetDC,hWin  ; get dc's
    mov hdc,eax
    inv CreateCompatibleDC,hdc      ; create dc for hidden buffer
    mov mdc,eax
    inv CreateCompatibleDC,hdc      ; create dc for scratch card images
    mov sdc,eax
    inv CreateCompatibleDC,hdc      ; create dc for single card image
    mov tdc,eax

    inv CreateCompatibleBitmap,hdc, mwidth, mheight ; make a hidden buffer of the appropriate size
    mov mbmp,eax
    inv SelectObject, mdc, mbmp
    inv DeleteObject,eax        ; delete old bitmap
    inv CreateCompatibleBitmap,tdc,btmap.bmWidth,btmap.bmHeight ; make one of the appropriate size for card images
    mov tbmp,eax
   
    mov eax,btmap.bmWidth
    shl eax,6   ; enough for 64 cards
    inv CreateCompatibleBitmap,sdc,eax,btmap.bmHeight ; make one of the appropriate size for card images
    mov sbmp,eax
    inv SelectObject, sdc, sbmp
    inv DeleteObject,eax        ; delete old bitmap

    ; copy 4 card images into the same dc to avoide SelectObject,tdc,hBmp1
    push esi
    xor esi,esi
;       (already loaded above to get size)
;    inv LoadImage,0,addr file1,IMAGE_BITMAP,0,0,LR_LOADFROMFILE
;    mov hBmp1,eax
;    inv GetObject,hBmp1,sizeof btmap,addr btmap ; get card info
    inv SelectObject,tdc,hBmp1  ; get a card
    inv DeleteObject,eax        ; delete old bitmap
    inv BitBlt,sdc,esi,0,btmap.bmWidth,btmap.bmHeight,tdc,0,0,SRCCOPY
    inv DeleteObject,hBmp1

    inv LoadImage,0,addr file2,IMAGE_BITMAP,0,0,LR_LOADFROMFILE
    mov hBmp1,eax
    inv SelectObject,tdc,hBmp1  ; get a card
    inv DeleteObject,eax        ; delete old bitmap
    add esi,btmap.bmWidth       ; move over one bitmap width
    inv BitBlt,sdc,esi,0,btmap.bmWidth,btmap.bmHeight,tdc,0,0,SRCCOPY
    inv DeleteObject,hBmp1
   
    inv LoadImage,0,addr file3,IMAGE_BITMAP,0,0,LR_LOADFROMFILE
    mov hBmp1,eax
    inv SelectObject,tdc,hBmp1  ; get a card
    inv DeleteObject,eax        ; delete old bitmap
    add esi,btmap.bmWidth
    inv BitBlt,sdc,esi,0,btmap.bmWidth,btmap.bmHeight,tdc,0,0,SRCCOPY
    inv DeleteObject,hBmp1
           
    inv LoadImage,0,addr file4,IMAGE_BITMAP,0,0,LR_LOADFROMFILE
    mov hBmp1,eax
    inv SelectObject,tdc,hBmp1  ; get a card
    inv DeleteObject,eax        ; delete old bitmap
    add esi,btmap.bmWidth
    inv BitBlt,sdc,esi,0,btmap.bmWidth,btmap.bmHeight,tdc,0,0,SRCCOPY
    inv DeleteObject,hBmp1
   
    pop esi
       
    mov strtx,10
    mov strty,20
   
    BeginCounter
        xor eax,eax         ; choose 4th card for test
        add eax,btmap.bmWidth
        add eax,btmap.bmWidth
        add eax,btmap.bmWidth
        inv BitBlt,mdc,strtx,strty,btmap.bmWidth,btmap.bmHeight,sdc,eax,0,SRCCOPY
    EndCounter

    inv BitBlt,hdc,strtx,strty,btmap.bmWidth,btmap.bmHeight,mdc,0,0,SRCCOPY
    inv ReleaseDC,hWin,hdc
    inv DeleteObject,mbmp
    inv DeleteObject,sbmp
    inv DeleteDC,mdc
    inv DeleteDC,sdc
    inv DeleteDC,tdc
    inv DeleteObject,hBmp1
    ;Showit x
    ret

Test3:
    Desc 'BitBlt to to dialog from hidden buffer'

    inv LoadImage,0,addr file3,IMAGE_BITMAP,0,0,LR_LOADFROMFILE
    mov hBmp1,eax

    inv GetObject,hBmp1,sizeof btmap,addr btmap ; get card info

    inv GetClientRect,hWin,addr crect    ; get the size needed for the bitmap
    inv GetDC,hWin  ; get dc's
    mov hdc,eax
    inv CreateCompatibleDC,hdc      ; create dc for hidden buffer
    mov mdc,eax
    inv CreateCompatibleDC,hdc      ; create dc for scratch card images
    mov sdc,eax

    inv CreateCompatibleBitmap,hdc, mwidth, mheight ; make a hidden buffer of the appropriate size
    mov mbmp,eax
    inv SelectObject, mdc, mbmp
    inv DeleteObject,eax        ; delete old bitmap
    inv CreateCompatibleBitmap,sdc,btmap.bmWidth,btmap.bmHeight ; make one of the appropriate size for card images
    mov sbmp,eax
    inv SelectObject, sdc, sbmp
    inv DeleteObject,eax        ; delete old bitmap
    inv SelectObject,sdc,hBmp1
    inv BitBlt,mdc,strtx,strty,btmap.bmWidth,btmap.bmHeight,sdc,0,0,SRCCOPY

    mov strtx,10
    mov strty,20

    BeginCounter
        inv BitBlt,hdc,strtx,strty,btmap.bmWidth,btmap.bmHeight,mdc,0,0,SRCCOPY
    EndCounter

    inv ReleaseDC,hWin,hdc
    inv DeleteObject,mbmp
    inv DeleteObject,sbmp
    inv DeleteDC,mdc
    inv DeleteDC,sdc
    inv DeleteObject,hBmp1
    ;Showit x
    ret


EDIT:  I removed the old attachement, new program below.

jj2007


japheth

Quote from: Jimg on December 17, 2008, 06:39:32 PM
In my time tests, I've found that it is much faster to BitBlit from a memory dc into the dc of my dialog (test3, 2100 ticks), than it is to BitBlit from one memory dc to another.  Why is that??

My guess is that the copy to video memory is done via PCI Busmaster DMA, that is, the cpu is not involved in the copy process.


Mark Jones

Attached is my timing results. :U

[attachment deleted by admin]
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

OceanJeff32

#10
It's been a while since I've posted...

I must recommend a few things. Search fireworks mmx on this forum, for my first recommended program to look at.

This program uses a bitmap that is written to in memory, and then blotted on the screen every frame.

It also uses MMX technology features to manipulate the effects, pretty neat.  Not sure if it works on VISTA.

I am currently working with DARK GDK, which is free from thegamecreators.com, and using inline assembly to do some physics.

Also, the Visual C++ Express 2008 edition is free with microsoft and is designed to work with Dark GDK.

MASM32 is awesome, but I haven't been working with it in a while, you should be hearing more from me soon...??

I found the windows programming model to be crazy, bitmaps, etc.  Direct X was interesting.  And old VGA assembly programming was complex, but I loved it...Michael Abrash has a lot of free stuff online too.

later all,

jeff
:dazzled: :eek :lol


Any good programmer knows, every large and/or small job, is equally large, to the programmer!

Jimg

Hi Jeff, nice to see you back, and thanks for the tips.

I found one of my problems with test2.  Where I did
inv CreateCompatibleBitmap,sdc,eax,btmap.bmHeight
I should have made one compatible with hdc rather than sdc, even though they should be the same.  It cut the runtime from 7200 to 1900.  I'm not sure why and I'm still testing different things so I'll have more specs later.

jj2007

Quote from: Mark Jones on December 18, 2008, 04:59:44 PM
Attached is my timing results. :U

Attached mine (Core 2 Celeron M, Win XP SP3) against yours. There are some remarkable differences, see e.g. Test 29.



[attachment deleted by admin]

Jimg

#13
Here's something weird....

I accidentally found out that if I get about 10000 compatible dc's without deleting them, my other code runs much, much faster.

To see what I mean, I modified Test2 by inserting this setup code-

    inv GetDC,hWin
    mov hdc,eax
    pusha
    mov ebx,1000
    .repeat
        inv CreateCompatibleDC,hdc
        ;inv DeleteDC,eax
        dec ebx
    .until Zero?
    popa
    inv ReleaseDC,hWin,hdc


Notice I only did a thousand here.

If you run test2 ten times, about the tenth or eleventh time, the code for the first four tests start running incredibly faster-

First Run:
        Clocks   Description
Test 1  7514     SelectObject/BitBlt to hidden buffer from bmp
Test 2  1991     BitBlt to hidden buffer from compatible buffer containing images
Test 3  1996     BitBlt to to memory dc from hidden buffer
Test 11 2086     BitBlt to to dialog from hidden buffer

After running test2 10 times:
        Clocks   Description
Test 1  134      SelectObject/BitBlt to hidden buffer from bmp
Test 2  56       BitBlt to hidden buffer from compatible buffer containing images
Test 3  55       BitBlt to to memory dc from hidden buffer
Test 11 514      BitBlt to to dialog from hidden buffer

How's that for cutting down the cycle count?

Seems to use up about 4 Megs of memory, but for that kind of performance increase, it may well be worth it in some circumstances.

If you run the test app, it now outputs a file called Result.txt so you don't have to take screen shots. 

EDIT:---  File removed with flawed tests :(  .  New version in later post.

Jimg

Ah, I now notice, after creating all these dc's, nothing works anymore.  It's faster because it's crashed the system and isn't doing anything anymore.  Well, it was interesting, anyway.