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
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
Wow thanks a lot, I'll take a look at it now.
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?
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
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...)
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?
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.
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.
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
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?
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]
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.
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
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?
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.
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
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