Attached is a GDI+ demo displaying a .PNG image from file
Erol,
Have you done any code that will write a bitmap back to disk as a PNG or JPG ? The only good work I have seen in this area is by Jose Roca in PowerBASIC but I have not had enough life and time to decypher it from PowerBASIC to either 32 bit or 64 bit MASM.
If you just need a job to be done, here is the bare minimum: loads any image file (bmp, jpg, ico, ...) from the commandline and saves it as png.
If you want to use it in a batch file, just rename the Just_a_test.png and continue with the next image.
include \masm32\MasmBasic\Res\MbGui.asm
Event Paint
GuiImageCallback SaveMyImage ; user-defined callback function
GuiImage CL$() ; take image from commandline
EndOfEvents
SaveMyImage:
SaveImageToFile "Just_a_test.png"
invoke SendMessage, hGui, WM_CLOSE, 0, 0 ; say bye bye
ret
GuiEnd
Hi Hutch,
Let me check my archive. I will see what I can do.
Hi Hutch,
I found two Masm examples, one converting a bitmap to jpg and the other one converting a png to bitmap. Later, I will try to code the bitmap to png version.
Here is the bitmap to png converter.
New version of BMPtoPNG.zip attached above. Removed an unnecessary VirtualFree statement.
Hi Erol,
Works fine, all I have to do now is comprehend how it works. :tongue:
Hi Hutch,
Here is my attempt to explain the whole image conversion process. José Roca's website has a lot of useful material. Here is the GDI+ Flat API reference :
http://www.jose.it-berater.org/gdiplus/iframe/index.htm
Attached is another BMP to PNG converter using the API GdipCreateBitmapFromFile :
QuoteDescription
Creates a Bitmap object based on an image file.
http://www.jose.it-berater.org/gdiplus/reference/flatapi/bitmap/gdipcreatebitmapfromfile.htm
This new version eliminates the need of an extra function to read a bitmap file from disk. That was my ReadFileToMem function.
The GDI+ workflow is mainly based on five steps :
i) Initialisation of GDI+
mov esi,OFFSET StartupInfo
mov GdiplusStartupInput.GdiplusVersion[esi],1
; must be always seto to 1
invoke GdiplusStartup,ADDR token,esi,0
ii) The GDI+ plus API requires UNICODE names to handle files. GdipCreateBitmapFromFile reads easily a file from disk :
invoke MultiByteToWideChar,CP_ACP,0,\
ADDR PngFile,-1,ADDR UnicodePngFile,BUFFER_SIZE
invoke MultiByteToWideChar,CP_ACP,0,\
ADDR fname,-1,ADDR UnicodeBmpFile,BUFFER_SIZE
invoke GdipCreateBitmapFromFile,ADDR UnicodeBmpFile,ADDR BmpImageObj
iii) The crucial part is to get the Class Identifier for a specific encoder. Our aim is to convert a bitmap to png so the mime type will be :
ImgType db 'image/png',0
I removed all the stuff related to image compression, we don't need of this at the moment to create a .PNG image.
Regarding the function GetEncoderClsid , we obtain the list of the encoders with the APIs GdipGetImageEncodersSize and GdipGetImageEncoders. A loop scanning the list of the encoders determines the CLSID\ Class Identifier matching the mime type of the PNG format.
iv) Once we retrieve the CLSID of the desired format ( PNG ) , we can save the original bitmap as PNG :
invoke GdipSaveImageToFile,BmpImageObj,ADDR UnicodePngFile,eax,0
eax holds the CLSID value :
.
.
lea eax,[edi+_ImageCodecInfo.Clsid]
ret
GetEncoderClsid ENDP
iv) The rest is the traditional cleanup releasing all the resources :
invoke VirtualFree,pImageCodecInfo,0,MEM_RELEASE
invoke GdipDisposeImage,BmpImageObj ; release the image
invoke GdiplusShutdown,token ; shutdown the GDI+ system
You could skip the GetEncoderClsid routine and use the CLSID directly.
As you can see the 4th byte is the image type.
This makes it possible to use only 16 bytes of data memory for all image types.
Just fill the 4th byte with 0,1,2,5,6 or 7 ( no need to use the "mime type" string )
.data
CLSID_ImageType db 055h,07Ch,0F4h
ImageType db 6 ; PNG
dd 01A0411D3h,09A730000h,0F81EF32Eh
; encoders
CLSID_BMP TEXTEQU <{0557CF400h,01A04h,011D3h,{09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh}}>
CLSID_JPG TEXTEQU <{0557CF401h,01A04h,011D3h,{09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh}}>
CLSID_GIF TEXTEQU <{0557CF402h,01A04h,011D3h,{09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh}}>
CLSID_TIF TEXTEQU <{0557CF405h,01A04h,011D3h,{09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh}}>
CLSID_PNG TEXTEQU <{0557CF406h,01A04h,011D3h,{09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh}}>
CLSID_ICO TEXTEQU <{0557CF407h,01A04h,011D3h,{09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh}}>
Erol,
I already have reliable code for either loading a disk file or a memory image from an RC/RES file of JPG PNG and a few others working reliable but I have not had enough time to try and get the other way, from Bitmap image back to a disk file of JPG PNG or some of the others. I think your first pair of example address what I need to be able to do but I am not really familiar with the techniques to use the long string references yet. I am just washing my brains out of custom buttons which were something like picking fly specks out of pepper so I am a bit slow doing something original at the moment.
I will certainly try and digest the first examples and this latest work you have done.
Hi Siekmanski,
Thanks, I tested the GUID you posted and it works. Examining my previous example, Ollydbg displayed the same value matching the mime type PNG :
.data
CLSID_PNG GUID <0557CF406h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>>
invoke GdipSaveImageToFile,BmpImageObj,ADDR UnicodePngFile,ADDR CLSID_PNG,0
Is it safe to assume that those GUID values will remain constant across different future releases of Windows? The practice of long years was to retrieve the GUIDs with is a simple loop.
Hi Hutch,
No worries, it will be OK. Please let me know if there is anything I can do for you.
Hi Vortex,
I have used it from XP to win10 and it still works.
This is the method I use in my code. ( ! We need to read and write a DWORD to write the 4th BYTE in the GUID. ) <-- it worked, but it was a wrong assumption!
I forgot the first DWORD was endian based so, we can write the ImageType byte directly at position 0.
EDIT: Below the adjusted code. ( much simpler as it already was) :tongue:
.const
Image_BMP equ 0
Image_JPG equ 1
Image_GIF equ 2
Image_TIF equ 5
Image_PNG equ 6
Image_ICO equ 7
.data
CLSID_ImageType GUID <0557CF400h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>>
.code
mov eax,offset CLSID_ImageType
mov byte ptr [eax],Image_PNG
invoke GdipSaveImageToFile,pImage,offset FilenameW,eax,NULL
Hi Siekmanski,
Thanks for the information.
QuoteWe need to read and write a DWORD to write the 4th BYTE in the GUID.
Yes, I see that all the CLSIDs are enumerated from 0 to 7 to include all the image formats supported by GDI+
Yeah, Windows APIs are often built with helper functions that make the code bloat unnecessarily.
I have the ( compulsive ) habit when I understand how it works, to break it down and minimize the code. :biggrin:
EDIT: I made a stupid mistake about the DWORD read and write, corrected the code in the previous post. :undecided:
OK, I have a question, I have an example that loads a BMP image and displays it on the client area which gives me a bitmap handle.
If I try to use the bitmap handle with,
invoke GdipSaveImageToFile,bitmap,L(filename),rax,NULL
It will not start. If I put 0 as the first arg that app will at least run but the function does not work.
I don't know what is expected with the first argument, the reference material I have says a pointer to the image. What would you normally point at it ?
Hutch,
the handle must be Gdi+, not Gdi:
GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap)
Creates a Bitmap::Bitmap object based on a handle to a Windows Graphics Device Interface (GDI) bitmap and a handle to a GDI palette.
https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-bitmap-flat
Thanks, that worked correctly. I now have a function that will write an empty 0 length file to disk.
It vurx, it vurx !!!! Like Frankenstein :tongue:
Powered by Vortex, Siekmanski and JJ2007. :biggrin:
I have done 3 different types, JPG, PNG and BMP. All seem to work OK.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
.data
CLSID_ImageType1 GUID <0557CF401h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>>
pJPG@@@@@@ dq CLSID_ImageType1
.code
; ------------------------------------------
SaveAsJPG proc bmHandle:QWORD,filename:QWORD
LOCAL hGdip :QWORD
invoke GdipCreateBitmapFromHBITMAP,bmHandle,0,ptr$(hGdip)
invoke GdipSaveImageToFile,hGdip,L(filename),pJPG@@@@@@,NULL
invoke GdipDisposeImage,hGdip
ret
SaveAsJPG endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
That's great Hutch :thumbsup:
The BMP to PNG convertion example is translated to Poasm.