The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: Brett Kuntz on June 25, 2005, 11:08:39 AM

Title: Saving a Memory DC to a bitmap file
Post by: Brett Kuntz on June 25, 2005, 11:08:39 AM
Hello. I have code that I want to load up a JPG, draw some simple text to it, and then save it as a bitmap (or even better, as a JPG). I have the following code so far, completely at a loss of how to save the 'memdc' info/bitmap into a file. I want this all to be done invisible to the user, so there will be no drawing to the screen at any point.


    local hjpg:dword
    local hdc:dword
    local memdc:dword

    invoke BitmapFromFile, addr jpgcachename
    mov hjpg, eax
    invoke CreateDC, addr devicename, 0, 0, 0
    mov hdc, eax
    invoke CreateCompatibleDC, hdc
    mov memdc, eax
    invoke SelectObject, memdc, hjpg
    invoke TextOut, memdc, 10, 10, addr ostatus, sizeof ostatus
Title: Re: Saving a Memory DC to a bitmap file
Post by: donkey on June 26, 2005, 01:11:19 AM
I have a routine to save a DIB32 to a file in my graphics library, You require only a bitmap handle and a file handle, it does the rest. You will ofcourse have to draw to the DC as a DIB bitmap but you should be doing that anyway as it is much faster and allows you to directly draw to the bits instead of using the GDI. If necessary, there is a routine to convert a normal compatible bitmap (v3) to a DIB. I also have many other routines you can freely use, no license, no fees don't even have to say that I wrote them. There are a few in there that are from other authors so you should mention them if you use them. The library works with both MASM and GoAsm (though it is written in GoAsm) and is available from my website...

http://www.quickersoft.com/donkey/libs.htm
Title: Re: Saving a Memory DC to a bitmap file
Post by: Brett Kuntz on June 26, 2005, 01:18:48 AM
Wow thanks a lot, I'll take a look at it now.
Title: Re: Saving a Memory DC to a bitmap file
Post by: Brett Kuntz on June 26, 2005, 05:08:45 AM
I'm not to sure what I must do then. LoadBitmapFile returns an handle to a bitmap. How do I get that into a DIB DC, draw text to it, then save?
Title: Re: Saving a Memory DC to a bitmap file
Post by: donkey on June 26, 2005, 05:38:02 AM
You would generally load the bitmap, select it into a DC (memory or actual) then draw to it, select it out of the DC then save the bitmap. Because there are some functions when examining a bitmap that require that it is not currently selected into a DC, you must select it out before saving it. The following is example code for you...

#Define COLOR_3DFACE 15
#Define DT_CENTER 1h
#Define DT_VCENTER 4h
#Define DT_SINGLELINE 20h
#Define NULL 0
#Define GENERIC_WRITE 40000000h
#Define CREATE_ALWAYS 2

RECT STRUCT
  left    dd
  top     dd
  right   dd
  bottom  dd
ENDS

GRAPHLIB = "C:\RadASM\GoAsm\Projects\Graphics\Graphics.lib"
CreateIndependantBitmap = GRAPHLIB:CreateIndependantBitmap
SaveDIB32 = GRAPHLIB:SaveDIB32

DATA SECTION
hBmp DD ?
hFile DD ?
rect RECT <>

CODE SECTION

Start:
invoke GetDC,NULL
mov ebx,eax
invoke CreateIndependantBitmap, ebx,200,200
mov [hBmp], eax
invoke CreateCompatibleDC, ebx
push eax
invoke ReleaseDC, NULL, ebx
pop ebx

invoke SelectObject, ebx, [hBmp]
mov [hBmp], eax

mov D[rect.left], 0
mov D[rect.top], 0
mov D[rect.right], 199
mov D[rect.bottom], 199

invoke FillRect, ebx, offset rect, COLOR_3DFACE+1

invoke DrawTextA, ebx, "Hello", -1, offset rect,DT_CENTER + DT_SINGLELINE + DT_VCENTER

invoke SelectObject, ebx, [hBmp]
mov [hBmp], eax

invoke DeleteDC, ebx

invoke CreateFileA, "C:\Test.bmp",GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,NULL,NULL
mov [hFile], eax

invoke SaveDIB32, [hBmp], [hFile]

invoke CloseHandle, [hFile]

invoke DeleteObject, [hBmp]

invoke ExitProcess, 0
Title: Re: Saving a Memory DC to a bitmap file
Post by: donkey on June 26, 2005, 05:52:26 AM
Note that a DC is not specific to either compatible or independant bitmaps, the bitmap will automatically take on the characteristics of the DC it is selected into and revert to it's original format when selected out. Your only requirement with my library is that you use 32 bit DIB sections (bitmaps), the DC can be any valid type of device context (ie screen, printer etc...)
Title: Re: Saving a Memory DC to a bitmap file
Post by: Brett Kuntz on June 26, 2005, 10:39:50 AM
Hello, I tried to following code just to see if it'd load up the jpg and save it as a bmp right away, it does not work:


    local hbmp:dword
    local hfile:dword

    invoke BitmapFromFile, addr jpgcachename
    invoke ConvertToDIB32, eax
    mov hbmp, eax
    invoke CreateFile, addr filename, FILE_ALL_ACCESS, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
    mov hfile, eax
    invoke SaveDIB32, hbmp, hfile
    invoke CloseHandle, hfile
    ret


It creates a 56 byte file that doesn't draw properly. However paint and explorer report the bmp is 500x100, which is the dimensions of the jpg. Any idea?
Title: Re: Saving a Memory DC to a bitmap file
Post by: donkey on June 26, 2005, 01:57:49 PM
I will have to brush off my copy of MASM and try it out, the code I posted tested perfectly using GoAsm. I assume that BitmapFromFile is Ernie's imagelib routine, I wrote my own but will use the MASM32 version and try it out.
Title: Re: Saving a Memory DC to a bitmap file
Post by: donkey on June 26, 2005, 02:15:56 PM
can't seem to get the MASM32 library to work on my system anymore, probably have to reinstall it or something, I haven't used MASM for quite a long time and rarely used the library even then. But I tried this...

.386
.model flat, stdcall  ;32 bit memory model
option casemap :none  ;case sensitive

; ##########################################
; PROTO's for Graphics.lib

ConvertToDIB32 PROTO :DWORD
SaveDIB32 PROTO :DWORD,:DWORD

includelib C:\RadASM\GoAsm\Projects\Graphics\Graphics.lib

include windows.inc
include kernel32.inc
include user32.inc
include shell32.inc
include gdi32.inc
include MASM32.INC

includelib kernel32.lib
includelib user32.lib
includelib shell32.lib
includelib gdi32.lib
includelib masm32.lib

.DATA
hInst DD ?
hBmp DD ?
hFile DD ?
BmpFile DB "Paullette.bmp",0
OutFile DB "TestMASM.bmp",0

.CODE
Start:
invoke GetModuleHandle, 0
mov [hInst], eax

invoke LoadImage, [hInst], offset BmpFile,IMAGE_BITMAP,0 , 0, LR_LOADFROMFILE
mov [hBmp],eax
invoke ConvertToDIB32, [hBmp]
mov [hBmp],eax

invoke CreateFile,offset OutFile,GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,NULL,NULL
mov [hFile], eax

invoke SaveDIB32, [hBmp], [hFile]

invoke CloseHandle, [hFile]
invoke DeleteObject, [hBmp]
xor eax,eax
ret

end Start


And it worked perfectly as expected using LoadImage, so I would suspect that there might be a problem with the BitmapFromFile function. I would check to make sure that the stream for IPicture actually contains data, this would give you the effect of showing a size but no image in the final file.
Title: Re: Saving a Memory DC to a bitmap file
Post by: donkey on June 26, 2005, 03:31:13 PM
Arrgh, forgot that you need to actually use import libraries for everything, even functions in other libraries (not a problem in GoAsm). So I added Ole32 to the proggy and got BitmapFromFile to work. It seems to work perfectly as long as you use a fully qualified path, otherwise I get the same problem you had. The returned bitmap handle was 0 (which you should have been checking for) if a relative or incomplete path was used. Anyway, this works perfectly...

.386
.model flat, stdcall  ;32 bit memory model
option casemap :none  ;case sensitive

; ##########################################
; PROTO's for Graphics.lib

ConvertToDIB32 PROTO :DWORD
SaveDIB32 PROTO :DWORD,:DWORD

includelib C:\RadASM\GoAsm\Projects\Graphics\Graphics.lib

include windows.inc
include kernel32.inc
include user32.inc
include shell32.inc
include gdi32.inc
include oleaut32.inc
include ole32.inc
include MASM32.INC

includelib kernel32.lib
includelib user32.lib
includelib shell32.lib
includelib gdi32.lib
includelib oleaut32.lib
includelib ole32.lib
includelib masm32.lib

.DATA
hInst DD ?
hBmp DD ?
hFile DD ?
JpgFile DB "C:\RadASM\Masm\Projects\TestBitmap\Paullette.jpg",0
OutFile DB "TestMASM.bmp",0

.CODE
Start:
invoke CoInitialize, 0

invoke BitmapFromFile, offset JpgFile
test eax,eax
jz ErrorBmp

invoke ConvertToDIB32, eax
mov [hBmp],eax

invoke CreateFile,offset OutFile,GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,NULL,NULL
mov [hFile], eax

invoke SaveDIB32, [hBmp], [hFile]

invoke CloseHandle, [hFile]
invoke DeleteObject, [hBmp]

invoke CoUninitialize
invoke ExitProcess, 0

ErrorBmp:
invoke CoUninitialize
invoke ExitProcess, -1

end Start
Title: Re: Saving a Memory DC to a bitmap file
Post by: Brett Kuntz on June 27, 2005, 01:34:28 AM
Hello, it seems it still does not work. My debugger tells me your ConvertToDIB32 doesn't actually do anything. It puts the number 20 into eax, checks if eax is equal to 20, then just returns. It doesn't actually do anything else other then this it seems. The following code of course does not work, I also tried calling CoInit but that function fails, so I removed it.


    local hbmp:dword
    local hfile:dword

    invoke BitmapFromFile, addr jpgcachename
    invoke ConvertToDIB32, eax
    mov hbmp, eax
    invoke CreateFile, addr filen, FILE_ALL_ACCESS, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
    mov hfile, eax
    invoke SaveDIB32, hbmp, hfile
    invoke CloseHandle, hfile
    ret


BitmapFromFile returns a valid handle to my jpg image, I know this because I can blit it to the screen and see it. Next up, your function executes the following:

PUSH EBP
MOV EBP,ESP
SUB ESP,58
PUSH EBP
ADD DWORD PTR SS:[ESP],-28
PUSH 18
PUSH DWORD PTR SS:[EBP+8]
CALL <JMP.&gdi32.GetObjectA>  ; Always returns the number 18h
MOV EAX,DWORD PTR SS:[EBP-16]  ; Always the number 20h
CMP EAX,20
JNZ SHORT gstats.00404026
MOV EAX,DWORD PTR SS:[EBP+8]
MOV ESP,EBP
POP EBP
RETN 4

Any ideas?

Edit-

I forgot I could check your source code. So the problem seems; either the JPG isn't 32 bits, or BitmapFromFile doesn't load it into a 32 bit thingy. Is there a solution to this problem?
Title: Re: Saving a Memory DC to a bitmap file
Post by: donkey on June 27, 2005, 01:49:02 AM
Well, the jump short jumps past a return that exits if it is not passed a valid bitmap handle. The source for the library is available with the download, and the disassebled code you are showing is definitely not my ConvertToDIB32 function. It comes preassembled in the lib so MASM has nothing to do but insert the code at a page boundary and fix up the addresses. The following is my MASM test code using the MASM32 library for BitmapFromFile and my lib routines to convert and save it.

When I assemble using MASM I get the following, it is static in that it should not change as it is precompiled in a lib...

00402000   55               PUSH EBP
00402001   89E5             MOV EBP,ESP
00402003   81EC 94000000    SUB ESP,94
00402009   8D7D AC          LEA EDI,DWORD PTR SS:[EBP-54]
0040200C   31C0             XOR EAX,EAX
0040200E   B9 54000000      MOV ECX,54
00402013   F3:AA            REP STOS BYTE PTR ES:[EDI]
00402015   55               PUSH EBP
00402016   830424 AC        ADD DWORD PTR SS:[ESP],-54
0040201A   6A 54            PUSH 54
0040201C   FF75 08          PUSH DWORD PTR SS:[EBP+8]
0040201F   E8 84F0FFFF      CALL <JMP.&gdi32.GetObjectA>
00402024   837D B0 00       CMP DWORD PTR SS:[EBP-50],0
00402028   0F84 A6010000    JE TestBitm.004021D4
0040202E   837D B4 00       CMP DWORD PTR SS:[EBP-4C],0
00402032   0F84 9C010000    JE TestBitm.004021D4
00402038   C785 78FFFFFF 28>MOV DWORD PTR SS:[EBP-88],28

[attachment deleted by admin]
Title: Re: Saving a Memory DC to a bitmap file
Post by: Brett Kuntz on June 27, 2005, 01:55:30 AM
Hello, I got it to work. The following file in your library "ConvertToDIB32.asm" has a bug:


invoke GetObjectA,[hBitmap],SIZEOF BITMAP,offset dibs
mov eax,[dibs.bmBitsPixel]
cmp eax,32
jne >
    mov eax,[hBitmap]
    ret
:


Needs to be changed to:


invoke GetObjectA,[hBitmap],SIZEOF BITMAP,offset dibs
mov eax,[dibs.bmBitsPixel]
cmp eax,32
je >
    mov eax,[hBitmap]
    ret
:


So that the proc continues if the dibs.bmBitsPixel equals 32, and fails if it does not. With this change my code works great. Your old code, if the ConvertToDIB32 proc was passed a valid 32-bit handle, it would fail. If it was passed a valid 8/16/24-bit handle I'm assuming it would succeed.
Title: Re: Saving a Memory DC to a bitmap file
Post by: donkey on June 27, 2005, 02:02:36 AM
Not exactly sure what you are looking at there, this is my ConvertToDIB32 function. The proc exits and returns the handle if the DIB is already 32 bits, no need to blt it if it's already at the right color depth, only if it is not equal to 32 bits do you need to change it.

ConvertToDIB32 FRAME hBitmap
LOCAL bmp :DIBSECTION
LOCAL hDC :D
LOCAL hDIB :D
LOCAL bmi :BITMAPINFO
LOCAL ppvBits :D
LOCAL memdc1 :D
LOCAL memdc2 :D

lea edi,bmp
xor eax,eax
mov ecx,SIZEOF DIBSECTION
rep stosb

invoke GetObjectA,[hBitmap],SIZEOF DIBSECTION,offset bmp
cmp D[bmp.dsBm.bmWidth],0
je >>E2

cmp D[bmp.dsBm.bmHeight],0
je >>E2

mov D[bmi.bmiHeader.biSize],SIZEOF BITMAPINFOHEADER
mov eax,[bmp.dsBm.bmWidth]
mov D[bmi.bmiHeader.biWidth],eax
mov eax,[bmp.dsBm.bmHeight]
mov D[bmi.bmiHeader.biHeight],eax
mov W[bmi.bmiHeader.biPlanes],1
mov W[bmi.bmiHeader.biBitCount],32
mov D[bmi.bmiHeader.biCompression],BI_RGB
mov D[bmi.bmiHeader.biXPelsPerMeter],0
mov D[bmi.bmiHeader.biYPelsPerMeter],0
mov D[bmi.bmiHeader.biClrUsed],0
mov D[bmi.bmiHeader.biClrImportant],0

invoke GetDC,NULL
mov [hDC],eax

; Create a DIB section with the right attributes
invoke CreateDIBSection,[hDC],offset bmi,DIB_RGB_COLORS,offset ppvBits,0,0
mov [hDIB],eax
or eax,eax
jz >>E1
cmp D[ppvBits],0
je >>E1

; check if the original bitmap is a DIB Section
cmp D[bmp.dsBmih.biBitCount],0
jne >

; The Bitmap is a compatible bitmap so get the bits
invoke GetDIBits,[hDC],[hBitmap],0,[bmp.dsBm.bmHeight],[ppvBits],offset bmi,DIB_RGB_COLORS
invoke ReleaseDC,NULL,[hDC]
invoke DeleteObject,[hBitmap]
; 2 bool GDI operations should be flushed to preserve GDI memory on 9x systems
invoke GdiFlush
mov eax,[hDIB]
RET

:
; The Bitmap is a DIB section so BLT it to the new one
cmp D[bmp.dsBmih.biBitCount],32
jne >
mov eax,[hBitmap]
ret
:

invoke CreateCompatibleDC,[hDC]
mov [memdc1],eax
invoke CreateCompatibleDC,[hDC]
mov [memdc2],eax

invoke ReleaseDC,NULL,[hDC]

invoke SelectObject,[memdc1],[hBitmap]
mov [hBitmap],eax
invoke SelectObject,[memdc2],[hDIB]
mov [hDIB],eax
; BitBlt is faster in most cases than a DWORD copy because of HW accel.
invoke BitBlt,[memdc2],0,0,[bmp.dsBm.bmWidth],[bmp.dsBm.bmHeight],[memdc1],0,0,SRCCOPY

invoke SelectObject,[memdc1],[hBitmap]
invoke DeleteObject,eax
invoke SelectObject,[memdc2],[hDIB]
mov [hDIB],eax
invoke DeleteDC,[memdc1]
invoke DeleteDC,[memdc2]
; 2 bool GDI operations should be flushed to preserve GDI memory on 9x systems
invoke GdiFlush
mov eax,[hDIB]

RET

; If an error occurs clean up and return -1, original is preserved
E1:
invoke ReleaseDC,NULL,[hDC]
E2:
xor eax,eax
dec eax
RET
ENDF
Title: Re: Saving a Memory DC to a bitmap file
Post by: Brett Kuntz on June 27, 2005, 02:30:39 AM
That's strange, the Graphics.zip I downloaded last night is completely different then the one availble now. The one now has the code you pasted, the old one has the code I pasted. Did you upload a new version of the lib today?
Title: Re: Saving a Memory DC to a bitmap file
Post by: donkey on June 27, 2005, 02:40:08 AM
Actually, I uploaded a new version today but for something unrelated. Perhaps I had fixed a bug and not uploaded the code at some point. The code today was a small mod to add the IID for IPicture directly to the lib instead of requiring it to be added via the ComDef.inc file. There was a problem importing IPicture using MASM that was not present in GoAsm. It is also possible that the last time I updated the website I uploaded the wrong file. I do remember changing the conversion function sometime last February.

I am sorry about that, I am usually pretty good at updates and such but obviously I fell short here.
Title: Re: Saving a Memory DC to a bitmap file
Post by: Brett Kuntz on June 27, 2005, 02:45:39 AM
Ah ok, well the full/new library looks great. Works like a charm now, just have to figure out how to do my TextOut's and I should be set. Alright, got everything to work now, this was my final code:


    local hbmp:dword
    local hfile:dword
    local hdc:dword

    invoke BitmapFromFile, addr jpgcachename
    mov hbmp, eax
    invoke CreateCompatibleDC, 0
    mov hdc, eax
    invoke SelectObject, hdc, hbmp
    mov hbmp, eax
    invoke TextOut, hdc, 0, 0, addr testtext, sizeof testtext
    invoke SelectObject, hdc, hbmp
    mov hbmp, eax
    invoke DeleteDC, hdc
    invoke ConvertToDIB32, hbmp
    mov hbmp, eax
    invoke CreateFile, addr filen, FILE_ALL_ACCESS, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
    mov hfile, eax
    invoke SaveDIB32, hbmp, hfile
    invoke CloseHandle, hfile
    ret
Title: Re: Saving a Memory DC to a bitmap file
Post by: Faiseur on June 28, 2005, 09:39:39 PM
I discovered the graphic library of Donkey and I play with :-) Thanks.  By testing the code of kuntor I made a small function which can be called to draw text in a Bitmap.

   Paramters:
   hBitmap = Handle to the bitmap (compatible bitmap or 32bit DIB section)
   Text = text to draw
   Color = text color (background is transparent)
   hFont = font (0 = default)
   px = position x
   py = position y

   Original bitmap handle is modified



DIBText PROC uses ebx,hBitmap,Text,Color,hFont,px,py:DWORD

  mov ebx, FUNC (CreateCompatibleDC, 0)
  mov hBitmap, FUNC(SelectObject,ebx, hBitmap)
  invoke  SetBkMode,ebx,TRANSPARENT
  invoke  SelectObject,ebx,hFont
  invoke  SetTextColor,ebx,Color
  invoke TextOut,ebx, px, py,Text,len(Text)
  mov hBitmap, FUNC(SelectObject,ebx, hBitmap)
  invoke DeleteDC,ebx
  ret
DIBText endp