News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

GDI+ demo

Started by Vortex, April 10, 2020, 05:19:27 AM

Previous topic - Next topic

Vortex

Attached is a GDI+ demo displaying a .PNG image from file

hutch--

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.

jj2007

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

Vortex

Hi Hutch,

Let me check my archive. I will see what I can do.

Vortex

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.

Vortex

Here is the bitmap to png converter.

Vortex

New version of BMPtoPNG.zip attached above. Removed an unnecessary VirtualFree statement.

hutch--

Hi Erol,

Works fine, all I have to do now is comprehend how it works.  :tongue:

Vortex

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

Siekmanski

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}}>
Creative coders use backward thinking techniques as a strategy.

hutch--

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.

Vortex

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.

Siekmanski

#12
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
Creative coders use backward thinking techniques as a strategy.

Vortex

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+

Siekmanski

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:
Creative coders use backward thinking techniques as a strategy.