This came up while converting my tic tac toe game to NOT use bitmap image resources *.bmp.
I now use gdi32 drawing functions to draw lines and flood fill the interior with a solid color. In others, I use FillRect to draw elements of the game board.
Still another draws an ellipse.
My question is, can I only do this once on program startup, and save a bitmap of each of them, to simply use BitBlt to draw them onto the DC later rather than using the drawing functions each time that WM_PAINT is called? It draws too slowly that way...
I know it is possible, but I am at a loss on how to do so properly.
I've got some ideas, but not sure if they are sound ideas. I don't need code, just point me in the right direction. :smiley:
Well, maybe a little code would help to demonstrate the process. I have been known to screw things like this up. And badly.
Quote from: zedd151 on March 03, 2025, 04:51:09 PMMy question is, can I only do this once on program startup, and save a bitmap of each of them, to simply use BitBlt to draw them onto the DC later rather than using the drawing functions each time that WM_PAINT is called?
Yes. The simplest approach is to use the technique that's also used by many tools to save the contents of the desktop to a .BMP file: create a memory context and copy the screen to this context. In your case you have to copy the client area instead of the screen, of course. A little obstacle may arize if you have to deal with palettes:
invoke GetSystemMetrics,SM_CXSCREEN
mov dwWidth,eax
invoke GetSystemMetrics,SM_CYSCREEN
mov dwHeight,eax
invoke GetDC,0
mov hDCSrc,eax
invoke CreateCompatibleDC,eax ;create a memory context
mov hDCMemory,eax
invoke CreateCompatibleBitmap,hDCSrc,dwWidth,dwHeight ; create the bitmap for the memory context
mov hBitmap,eax
invoke SelectObject,hDCMemory,eax
mov hOldBitmap,eax
invoke GetDeviceCaps,hDCSrc,RASTERCAPS;Raster
and eax,RC_PALETTE
mov HasPaletteScrn,eax ;Palette
invoke GetDeviceCaps,hDCSrc,SIZEPALETTE ;Size of
mov PaletteSizeScrn,eax
.if HasPaletteScrn && (PaletteSizeScrn == 256)
mov LogPal.palVersion , 300h
mov LogPal.palNumEntries , 256
invoke GetSystemPaletteEntries,hDCSrc,0,256,addr LogPal.palPalEntry
invoke CreatePalette,addr LogPal
mov hPal,eax
invoke SelectPalette,hDCMemory,hPal,0
mov hPalPrev,eax
invoke RealizePalette,hDCMemory
.endif
invoke BitBlt,hDCMemory,0,0,dwWidth,dwHeight,hDCSrc,0,0,SRCCOPY
invoke SelectObject,hDCMemory,hOldBitmap
mov hBitmap,eax
.if HasPaletteScrn && (PaletteSizeScrn == 256)
invoke SelectPalette,hDCMemory,hPalPrev,0
; mov hPal,eax
.endif
invoke ReleaseDC,0,hDCSrc
Quote from: _japheth on March 03, 2025, 10:45:53 PMYes. The simplest approach is to use the technique that's also used by many tools to save the contents of the desktop to a .BMP file
Well crap. I actually have code to capture the desktop. :greensml: I had not thought about that.
Thanks. :smiley:
Some of the things in that code, were some of the steps I would have *tried* already. It confirms that my thought processes were not too far off. :smiley:
I don't think I will need device caps or pallet (my iPads auto correct keeps correcting there), or changing bit depth, since I won't be saving it to file.
I am going to play around with this later today. :azn: and post the results of my experimentation, if all goes well.
Nothing ventured, nothing gained. :cool:
Create our "O" piece in WM_CREATE...
.if uMsg == WM_CREATE
invoke DrawShapeO, hWin ;; O piece DC handle and its bmp handle are global in scope, to delete upon program close.
... By calling this procedure:
I use global variables, and will need those to delete the O_DC and O_bmp in WM_CLOSE
DrawShapeO proc hWin:dword
local hDC:dword, x:dword, y:dword
local hPen:dword, hBrush:dword, hBrushOld:dword, hPenOld:dword
.data
O_DC dd 0 ; DC for "O" piece
O_bmp dd 0 ; bmp for "O" piece
O_bmpold dd 0
.code
invoke GetDC, hWin ; get DC for the client area of the main window. :P
mov hDC, eax
invoke CreateCompatibleDC, hDC
mov O_DC, eax
invoke CreateCompatibleBitmap, hDC, 140, 140
mov O_bmp, eax
invoke SelectObject, O_DC, O_bmp
invoke CreateSolidBrush, bcolor ;; get background colored brush
mov hBrush, eax
invoke SelectObject, O_DC, hBrush
mov hBrushOld, eax
invoke ExtFloodFill, O_DC, 2, 2, 0, FLOODFILLSURFACE ; flood fill
invoke CreatePen, PS_SOLID, 21, blue
mov hPen, eax
invoke SelectObject, O_DC, hPen
add x, 20
mov eax, x
add eax, 98
add y, 20
mov ecx, y
add ecx, 98
invoke Ellipse, O_DC, x, y, eax, ecx
invoke SelectObject, O_DC, hBrushOld
invoke DeleteObject, hBrush
invoke SelectObject, O_DC, hPenOld
invoke DeleteObject, hPen
invoke ReleaseDC, hWin, hDC
invoke SelectObject, O_DC, O_bmp
mov O_bmpold, eax
ret
DrawShapeO endp
Now we can use O_DC to draw "O" to memory DC, prior to memory DC getting BitBlt'ed to the window DC
excerpt:
;; drawpiece "O" in these positions
invoke BitBlt, mDC, 14, 14, 154, 154, O_DC, 0, 0, SRCCOPY
invoke BitBlt, mDC, 174, 14, 154, 154, O_DC, 0, 0, SRCCOPY
invoke BitBlt, mDC, 14, 174, 154, 154, O_DC, 0, 0, SRCCOPY
invoke BitBlt, mDC, 174, 174, 154, 154, O_DC, 0, 0, SRCCOPY
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; WM_PAINT ending code
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; blit memory DC to window DC
invoke BitBlt, hDC, 0, 0, rct.right, rct.bottom, mDC, 0, 0, SRCCOPY
;; WM_PAINT cleanup
invoke SelectObject, mDC, hBmpOld
invoke DeleteObject, hBmp
invoke DeleteDC, mDC
invoke EndPaint, hWin, addr ps
mov eax, 0
ret
Lastly we delete the DC and bmp associated with the "O" piece in WM_CLOSE:
.elseif uMsg == WM_CLOSE
invoke SelectObject, O_DC, O_bmpold
invoke DeleteObject, O_bmp
invoke DeleteDC, O_DC
Works fine! Yes the colors chosen are an ugly combination. Only used for this demonstration.
(https://i.postimg.cc/6QhGjG4G/gdi32.png)
Expert opinions welcome. Meaning those with good gdi32 experience. Does this code look okay?
Now if I am 100% correct, I will no longer need to call InvalidateRect in WM_SIZE anymore... to repair drawing issues upon resizing the widow, that were caused by FloodFill/ExtFloodFill. I will test that theory later. :azn:
Later: I tested it, and so far, so good. :thumbsup: it proved true, at least with only 4 drawing events...
Thanks _japheth for your assist and moral support, although I had the right idea already. I just didn't know it so didn't try it, I was afraid to royally screw things up as usual. :biggrin:
I am now converting tic tac toe, using the above methods. So far it is going well. No redrawing issues, and no gdi leakage, and no need for call to InvalidateRect in WM_SIZE. :azn: I think I'm onto something.
I have completed reworking tic-tac-toe to use the methods described above to create reusable memory bitmaps rather than resource bitmaps, or doing multiple drawing functions directly in WM_PAINT.
The memory bitmap creation code is called from WM_CREATE. And the memory bitmaps only get BitBlt'd in WM_PAINT, just as if actual resource bitmaps were used. Perfect!
No more InvalidateRect in WM_SIZE. :biggrin:
Attachment removed,
Latest version is posted in the Game Development board..
Here's something that may or may not be useful to you.
If I read you correctly, you want to include bitmap images in your program without having to put them in a resource file.
Here's my method for doing this:
1. I create a bitmap in memory by defining it with DB statements. (How you create the data is another question; let's assume that you have the bits at hand that define the image you want to create.)
2. I use this magical li'l routine to "get a handle" to this bitmap, one that I can use with any GDI function that takes a bitmap handle (like, say, BitBlt()):
;====================================
; GetBMPhandle (bmpPtr, hWin)
;
; "Opens" BMP "file" from memory.
;
; Returns handle to bitmap.
;
; Hat tip to "fearless" of the MASM32 forum for
; vastly simplifying this process!
;====================================
GetBMPhandle PROC bmpPtr:DWORD, hWin:HWND
LOCAL hDC:HDC
INVOKE GetDC, hWin
MOV hDC, EAX
MOV EDX, bmpPtr
LEA ECX, [EDX + SIZEOF BITMAPFILEHEADER] ;Point to BITMAPINFOHEADER header.
MOV EAX, [EDX].BITMAPFILEHEADER.bfOffBits ;Offset of bitmap bits.
ADD EDX, EAX ;Point to bitmap bits.
INVOKE CreateDIBitmap, hDC, ECX, CBM_INIT, EDX, ECX, DIB_RGB_COLORS
PUSH EAX
INVOKE DeleteDC, hDC
POP EAX
RET ;Return w/handle.
GetBMPhandle ENDP
It's neat and a treat, and makes your source code self-contained without the need for an external .rc file.
Regarding how you create the in-memory bitmap, it's actually easy peasy.
I use Hutch's bin2db utility to create the DB statements from a bitmap (.BMP) file, like so:
; E:\Programming stuff\Assembly language\Windows\Bitmaps\RedX (12x12x16).bmp is 214 bytes long
db 66,77,214,0,0,0,0,0,0,0,118,0,0,0,40,0
db 0,0,12,0,0,0,12,0,0,0,1,0,4,0,0,0
db 0,0,96,0,0,0,19,11,0,0,19,11,0,0,16,0
db 0,0,16,0,0,0,0,0,0,0,0,0,128,0,0,128
db 0,0,0,128,128,0,128,0,0,0,128,0,128,0,128,128
db 0,0,192,192,192,0,128,128,128,0,0,0,255,0,143,143
db 247,0,0,255,255,0,255,0,0,0,255,0,255,0,255,255
db 0,0,255,255,255,0,153,175,255,255,250,153,114,0,153,154
db 255,255,169,153,101,0,249,153,175,250,153,159,99,0,255,153
db 154,169,153,255,32,0,255,249,153,153,159,255,88,0,255,255
db 153,153,255,255,51,0,255,250,153,153,175,255,110,0,255,169
db 153,153,154,255,100,0,250,153,159,249,153,175,0,0,169,153
db 255,255,153,154,0,0,153,159,255,255,249,153,180,236,153,255
db 255,255,255,153,255,69
This includes the bitmap header and the actual bitmap bits; the code I posted above knows how to find the bitmap bits in there.
Thanks for your example. Maybe for another time though, it does sound kind of nifty.
But here, I went another route. Creating the memory bitmap in code, using Rects, Lines, an Ellipse, etc. and plenty of use of FloodFill or ExtFloodFill. Not very hard to do, btw. Just open a real .bmp image in mspaint of what you want to use, get all the needed data points then figure out which gdi drawing function would be most suitable for it. Its a little code heavy, but was fun to do. :smiley:
Maybe I can try your method on Connect 4... which I will get started on now that tictactoe is just about done.
Well, that's a good strategy too; create the thing once in memory, then use it multiple times.
Your method seems it would be better for multiple colors. I think I used a total of six colors, but maximum 3 colors for each bitmap.
Fully Commented (* note 4) example of the methods that I ultimately used in ttt.
Function and variable names (used here) are for demonstration purposes. You may of course choose other names. :biggrin:
Example Code:
Custom1 proto :dword, :dword
.data?
hDC_Custom1 dd ? ;; global variable for hDC_Custom1
hBmp_Custom1 dd ? ;; global variable for hBmp_Custom1
hBmpOld_Custom1 dd ? ;; global variable for hBmpOld_Custom1
.code
Custom1 proc hWin:dword, bclr:dword
local hDC:dword, hPen:dword, hBrush:dword, hBrushOld:dword, hPenOld:dword
invoke GetDC, hWin ;; get client area DC of main window
mov hDC, eax ;; save as local variable
invoke CreateCompatibleDC, hDC
mov hDC_Custom1, eax ;; save as global variable - important * note2
invoke CreateCompatibleBitmap, hDC, 140, 140
mov hBmp_Custom1, eax ;; save as global variable - important * note2
invoke SelectObject, hDC_Custom1, hBmp_Custom1
mov hBmpOld_Custom1, eax ;; save as global variable - important * note2
;; any brushes, pens or other gdi objects used after this point, should be kept local, and
;; deleted here inside the function after finished using them
;; this piece of code fills the 'bitmap' with the background color 'bclr'.
;; important if your custom drawn bitmap has 'transparent' elements to it, and may be omitted otherwise.
invoke CreateSolidBrush, bclr ;; create brush in background color 'bclr'
mov hBrush, eax ;; store in hBrush
invoke GetPixel, hDC_Custom1, 1, 1 ;; get the current color
invoke SelectObject, hDC_Custom1, hBrush ;; select hBrush
mov hBrushOld, eax
invoke ExtFloodFill, hDC_Custom1, 1, 1, eax, FLOODFILLSURFACE ;; flood fill everything that is current color
invoke SelectObject, hDC_Custom1, hBrushOld ;; select hBrushOld
invoke DeleteObject, hBrush ;; delete hBrush
;; ######################################################################
;;
;; this area is where you will draw your lines, rectangles, ellipses, etc.
;; after using brushes, pens, etc., you must delete them when no longer needed.
;;
;; ######################################################################
;; do NOT delete hDC_Custom1, hBmp_Custom1, or hBmpOld_Custom1 Here!!!
;; delete them in WM_CLOSE... that is the reason to keep those variables global in scope.
invoke ReleaseDC, hWin, hDC ;; release main window DC
ret
Custom1 endp
note1 Now you can call this function from WM_CREATE to create your custom drawn bitmap
note2 You can now use the handle, hDC_Custom1 to BitBlt your custom drawn bitmap into the DC in WM_PAINT...
... the DC used will depend on whether double buffering is used or not.
note3 You must delete hDC_Custom1, hBmp_Custom1 in WM_CLOSE
typical usage:
;; .elseif uMsg == WM_CREATE
;; invoke Custom1, hWin, BackColor ; BackColor is whatever background color you want to use.
;; .elseif uMsg == WM_CLOSE
;; invoke SelectObject, hDC_Custom1, hBmpOld_Custom1 ;; select old bmp
;; invoke DeleteObject, hBmp_Custom1 ;; delete bmp
;; invoke DeleteDC, hDC_Custom1 ;; delete DC
That is it, in a nutshell.
It strikes me that since I have here the function name appended to its accociated handle names, I could probably make a qeplugin for inserting the template code into an asm source using a dialog box for choosing the desired name. :biggrin: More on that, in a new topic near you soon.
note 4: :tongue:
Hello,
You don't need embedded data in the source code. Hutch's File Data Assembler ( fda ) does the the job, it converts any file to a linkable MS COFF object module :
\masm32\fda.exe
QuoteFile Data Assembler 2.0 Copyright (c) The MASM32 Project 1998-2005
PARAMETERS
1 source file
2 target object file
3 data label name
4 data alignment [optional]
NOTES
Source file MUST be an existing file
Target file MUST have an "obj" extension
The optional data aligment MUST be specified in powers of 2
Alignment range is 1 to 8192 bytes, default is 4 bytes.
Long names are supported but names with spaces MUST be
enclosed in quotations.
OUTPUT
fda.exe produces two (2) files,
a. An object module file as named in the target.
b. An INCLUDE file of the same name as the target
which contains an EXTERNDEF for the data label
name and an equate that contains the bytecount
for the data in the object module.
EXAMPLE
fda "My Source File.Ext" target.obj MyFile 16
Here is a quick sample. The traditional method to introduce binary resource data to the source code :
.data
DlgboxRsrc db 1,0,255,255,0,0,0,0,0,0,0,0,64,0,207,16
db 0,0,6,0,6,0,147,0,136,0,0,0,0,0,71,0
db 114,0,97,0,112,0,104,0,0,0,8,0,0,0,0,0
db 77,0,83,0,32,0,83,0,65,0,78,0,83,0,32,0
db 83,0,69,0,82,0,73,0,70,0,0,0
xor ecx,ecx
invoke DialogBoxIndirectParam,hInstance,\
ADDR DlgboxRsrc,ecx,ADDR DlgProc,ecx
The binary resource data can be extracted from a compiled resource script ( .res file ) with a tool like Resource Hacker.
Dlgbox.res -> Resource Hacker -> Dlgbox.bin
Using Hutch's fda tool :
\masm32\fda Dlgbox.bin Dlgbox.obj DlgboxRsrc
\masm32\bin\ml /c /coff Lines.asm
\masm32\bin\polink /SUBSYSTEM:WINDOWS Lines.obj Dlgbox.obj
The source code is now simplified :
EXTERN DlgboxRsrc:BYTE
.
.
.code
.
.
xor ecx,ecx
invoke DialogBoxIndirectParam,hInstance,\
ADDR DlgboxRsrc,ecx,ADDR DlgProc,ecx
Quote from: zedd151 on March 05, 2025, 04:03:59 AM invoke GetWindowDC, hWin ;; get main window DC
Are you sure that
GetWindowDC is the one to use? That gets the DC for the entire window, including non-client areas. According to Microsoft
QuoteGetWindowDC is intended for special painting effects within a window's nonclient area. Painting in nonclient areas of any window is not recommended.
It is just used sinsi, to create the compatible memory DC and bitmap, not doing any drawing to it.
Isn't that the same way one would do it in WM_PAINT? Create the compatible DC from the DC returned by BeginPaint? Or is there a difference that I do not yet understand. :toothy:
Nevermind. I had it backwards, what I was thinking... it should be GetDC (https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) Thanks for catching that, sinsi :thumbsup:
Looks like I have a few minor adjustments to make. :tongue: :joking:
I have modified all posts and attachments using GetWindowDC, replacing it with GetDC.
Quote from: Vortex on March 05, 2025, 05:26:16 AMHello,
You don't need embedded data in the source code. Hutch's File Data Assembler ( fda ) does the the job, it converts any file to a linkable MS COFF object module :
\masm32\fda.exe
Well, kinda 6 of one, half a dozen of the other.
My technique (embedding the bitmap data in the program source) works well for small-ish bitmaps. (You could handle larger ones by putting the embedded data into an include file instead of right in the source code.)
Creating the data as an object file might work better for larger bitmaps.
What I like about my method is that it keeps the file count down; I prefer not to have a zillion .obj files to have to link to create the executable. (And of course forget to include them when I zip the files up to post here.)