News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

GDI+: bmp/gif/png/jpg to bmp/gif/png/jpg

Started by Brett Kuntz, July 09, 2006, 06:02:15 PM

Previous topic - Next topic

Brett Kuntz

Hello, I took some time to tinker with the GDI+ API, the result is very simple code for opening one image file type and saving it to another. It doesn't support encoding settings (so it defaults to Full) yet, but if I can figure the rest out I'll edit them in.

Header stuff

    GdiplusStartup_proto typedef proto :dword, :dword, :dword
    GdiplusStartup_proc typedef ptr GdiplusStartup_proto
    GdiplusShutdown_proto typedef proto :dword
    GdiplusShutdown_proc typedef ptr GdiplusShutdown_proto
    GdipLoadImageFromFile_proto typedef proto :dword, :dword
    GdipLoadImageFromFile_proc typedef ptr GdipLoadImageFromFile_proto
    GdipSaveImageToFile_proto typedef proto :dword, :dword, :dword, :dword
    GdipSaveImageToFile_proc typedef ptr GdipSaveImageToFile_proto
    GdipGetImageEncodersSize_proto typedef proto :dword, :dword
    GdipGetImageEncodersSize_proc typedef ptr GdipGetImageEncodersSize_proto
    GdipGetImageEncoders_proto typedef proto :dword, :dword, :dword
    GdipGetImageEncoders_proc typedef ptr GdipGetImageEncoders_proto
    GdipDisposeImage_proto typedef proto :dword
    GdipDisposeImage_proc typedef ptr GdipDisposeImage_proto

    initjpg proto
    bmptojpg proto
    uninitjpg proto

    GdiplusStartupInput struct
        GdiplusVersion dd ?
        DebugEventCallback dd ?
        SuppressBackgroundThread dd ?
        SuppressExternalCodecs dd ?
    GdiplusStartupInput ends

    GdiplusStartupOutput struct
        NotificationHook dd ?
        NotificationUnhook dd ?
    GdiplusStartupOutput ends

    ImageCodecInfo struct
        Clsid dd ?, ?, ?, ?
        FormatID dd ?, ?, ?, ?
        CodecName dd ?
        DllName dd ?
        FormatDescription dd ?
        FilenameExtension dd ?
        MimeType dd ?
        Flags dd ?
        Version dd ?
        SigCount dd ?
        SigSize dd ?
        SigPattern dd ?
        SigMask dd ?
    ImageCodecInfo ends

.data?
    gditoken dd ?
    GdiplusStartup GdiplusStartup_proc ?
    GdiplusShutdown GdiplusShutdown_proc ?
    GdipLoadImageFromFile GdipLoadImageFromFile_proc ?
    GdipSaveImageToFile GdipSaveImageToFile_proc ?
    GdipGetImageEncodersSize GdipGetImageEncodersSize_proc ?
    GdipGetImageEncoders GdipGetImageEncoders_proc ?
    GdipDisposeImage GdipDisposeImage_proc ?
.code
    gdiplus_dll db "gdiplus.dll", 0
    bmpopen dw 's', 'i', 'g', '.', 'b', 'm', 'p', 0 ; sig.bmp, change to suit your needs. Must be unicode.
    jpgsave dw 's', 'i', 'g', '.', 'j', 'p', 'g', 0 ; sig.jpg
    mime_type dw 'i', 'm', 'a', 'g', 'e', '/', 'j', 'p', 'e', 'g', 0 ; Image type for saving. Use ie image/bmp, image/gif, image/jpg, image/png. Must be unicode.
    LGdiplusStartup db "GdiplusStartup", 0
    LGdiplusShutdown db "GdiplusShutdown", 0
    LGdipLoadImageFromFile db "GdipLoadImageFromFile", 0
    LGdipSaveImageToFile db "GdipSaveImageToFile", 0
    LGdipGetImageEncodersSize db "GdipGetImageEncodersSize", 0
    LGdipGetImageEncoders db "GdipGetImageEncoders", 0
    LGdipDisposeImage db "GdipDisposeImage", 0


Initialize - Do not call in DllMain if you are putting this into a DLL. Instead export a function that will call this and get your program to first call that.

initjpg proc

    local gdiplus:dword
    local gdisi:GdiplusStartupInput
    local gdiso:GdiplusStartupOutput

    invoke LoadLibrary, addr gdiplus_dll
    mov gdiplus, eax
    invoke GetProcAddress, gdiplus, addr LGdiplusStartup
    mov GdiplusStartup, eax
    invoke GetProcAddress, gdiplus, addr LGdiplusShutdown
    mov GdiplusShutdown, eax
    invoke GetProcAddress, gdiplus, addr LGdipLoadImageFromFile
    mov GdipLoadImageFromFile, eax
    invoke GetProcAddress, gdiplus, addr LGdipSaveImageToFile
    mov GdipSaveImageToFile, eax
    invoke GetProcAddress, gdiplus, addr LGdipGetImageEncodersSize
    mov GdipGetImageEncodersSize, eax
    invoke GetProcAddress, gdiplus, addr LGdipGetImageEncoders
    mov GdipGetImageEncoders, eax
    invoke GetProcAddress, gdiplus, addr LGdipDisposeImage
    mov GdipDisposeImage, eax
    mov gdisi.GdiplusVersion, 1
    and gdisi.DebugEventCallback, 0
    and gdisi.SuppressBackgroundThread, 0
    and gdisi.SuppressExternalCodecs, 0
    invoke GdiplusStartup, addr gditoken, addr gdisi, addr gdiso
    ret

initjpg endp


A sample procedure I made, you can change it around to suit your needs.

bmptojpg proc

    local jpgimage:dword
    local encnum:dword
    local encsize:dword
    local encinfo:dword

    push ebx

    ; Load the image from file
    invoke GdipLoadImageFromFile, addr bmpopen, addr jpgimage

    ; Get the availble encode settings
    invoke GdipGetImageEncodersSize, addr encnum, addr encsize ; num = encoders availble on the computer, size = total amount of memory needed to store them
    invoke VirtualAlloc, 0, encsize, MEM_COMMIT, PAGE_READWRITE
    mov encinfo, eax
    invoke GdipGetImageEncoders, encnum, encsize, encinfo ; Fills the memory block with all the encoding info

    ; Loop through all the encoders untill we find the one we want (in this case image/jpeg)
    mov ebx, encinfo
@@: mov eax, [ebx.ImageCodecInfo.MimeType]
    add ebx, sizeof ImageCodecInfo
    invoke lstrcmpW, eax, addr mime_type
    test eax, eax
    jz @F
    dec encnum
    jnz @B
@@: sub ebx, sizeof ImageCodecInfo

    ; Save the image to a new file, ebx is a pointer to the array storing the CLSID for image/jpeg
    invoke GdipSaveImageToFile, jpgimage, addr jpgsave, ebx, 0

    ; Clean up
    invoke VirtualFree, encinfo, 0, MEM_RELEASE
    invoke GdipDisposeImage, jpgimage

    pop ebx
    ret

bmptojpg endp


Uninitialize - Do not call in DllMain if you are putting this into a DLL. Instead export a function that will call this and get your program to first call that.

uninitjpg proc

    invoke GdiplusShutdown, gditoken
    ret

uninitjpg endp

jj2007

Yes I know this post is over a year old... but kunt0r never got a reply.
Question to the more experienced colleagues: GDI_plus.dll is installed with the OS since XP; and apparently conversion is not too difficult. Are there good arguments not to use GDI+ ?

Tedd

It's a little more work (than a few simple functions) to use directly from asm, but no other good reason, really :wink
No snowflake in an avalanche feels responsible.

Vortex

As an alternative, masm32.lib provides image displaying functions

jj2007

Quote from: Vortex on November 11, 2007, 10:04:26 PM
As an alternative, masm32.lib provides image displaying functions
Actually, I have a DIB to be saved to disk as png - haven't seen any solution in masm32.lib. Currently I save it to disk as bmp, then execute bmp2png.exe (http://cetus.sakura.ne.jp/softlab/b2p-home/), which works fine but is somewhat slow.

jj2007

Quote from: Tedd on November 11, 2007, 09:13:29 PM
It's a little more work (than a few simple functions) to use directly from asm, but no other good reason, really :wink

kunt0r has done all the work already. Attached a version for bmp to png, assuming there are 15 bitmaps in the pic/ folder. It is blazingly fast, but the compression is much worse than with bmp2png...

[attachment deleted by admin]

zooba

The compression is probably settable and defaults to none/minimal. There is a C++ example of setting JPEG compression here. I don't have the time myself to convert/test it but I assume it will be similar for PNG.

Cheers,

Zooba :U

jj2007

Quote from: zooba on November 12, 2007, 12:28:57 PM
The compression is probably settable
Njet. I searched a lot, and came across a certain Peter Ritchie ("MSDN Forums Moderator, Microsoft MVP") who confessed in MSDN .net forums, after a rather useless and misleading discussion of "losslessness", that better compression is not foreseen :tdown
"there are no methods of improving the existing compression of PNG" (http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=490790&SiteID=1)
See here for a full list of options:
http://msdn2.microsoft.com/en-us/library/system.drawing.imaging.encodervalue.aspx
Anyway, I will implement the fast gdiplus routine in the dashboard; for testing websites, the additional size is no problem, and for the final release I can use pngcrush.

Vortex

Hi jj2007,

You are right, masm32.lib has no support for png. Here is an interesting project : PNGlib - a free PNG decoder library (beta)

http://madwizard.org/view.php?page=downloads

jj2007

#9
Quote from: Vortex on November 12, 2007, 06:57:03 PM
Hi jj2007,

You are right, masm32.lib has no support for png. Here is an interesting project : PNGlib - a free PNG decoder library (beta)

http://madwizard.org/view.php?page=downloads

Hi Vortex,
I know PNGlib but wanted a solution that is already installed on the user's pc.
Attached a very simple implementation based on kunt0r's code:
Bmp2Png.asm produces Bmp2Png.dll with only two exports:
invoke Bmp2Png, addr BmpFilePath ; should be self-explanatory
invoke FreeGdiPlus
The first export checks whether the init has been done.
Other files are for testing the dll.
Output file has the same name and path as input but with png extension.

[attachment deleted by admin]

Grincheux

For loading image I use the GFL SDK given with XNView.
I made an interface on it at http://www.stosd.com/realise.html andmade a small pgm with it http://i-view.idcat39.com/download.html
It works on png, bmp, gif, tif, tga, psd...
:U
Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Vortex


jj2007

I slightly rewrote my dll, adding some checks in case somebody wants to free GdiPLus even if it had never been used. The package contains:
MakeBmp2Png.BAT
Bmp2Png.def
Bmp2Png.asm ; source of the dll
Img2Png.asm ; source of the little proggie that loads & uses the dll
The latter accepts one command line parameter. If you drag any image over Img2Png.exe, it will be converted to any.png
By the way, I struggled a bit with getting the first argument of the command line, e.g. "my_image.jpg". From what I saw, Windows (XP in my case) puts char 34 to the left and right of the first arg, i.e. the name of the executable, even if there is no need for it (full path DOS 8.3 compliant); when there is no first argument, the last byte before the terminating NULL is char 32 aka space. MSDN is silent about this behaviour. Anyway, my parser seems to work with all kind of arguments.

[attachment deleted by admin]

jj2007

Quote from: jj2007 on November 26, 2007, 08:45:36 AM
Windows (XP in my case) puts char 34 to the left and right of the first arg, i.e. the name of the executable, even if there is no need for it (full path DOS 8.3 compliant)
I checked that again:
- started from Explorer (or 2xplorer), the first command line arg always has 34 to left and right; the second arg has them only if in need, i.e. if there are spaces present.

D:\masm32\test me.bmp dragged over D:\masm32\img2png.exe yields
"D:\masm32\img2png.exe" "D:\masm32\test me.bmp" (with a space in test me)

D:\masm32\test_me.bmp dragged over D:\masm32\img2png.exe yields
"D:\masm32\img2png.exe" D:\masm32\test_me.bmp (with an understroke in test_me)

- from a batch file, the command lines appear exactly as entered (and if there are spaces in the exe path, it will not work without 34's):

D:\masm32\Img2Png.exe
Img2Png.exe
D:\masm32\Img2Png.exe D:\wwn\GFW\asm_jj\test me.bmp (this one works, too)
D:\masm32\Img2Png.exe "D:\wwn\GFW\asm_jj\test me.bmp"

They all work with the img2png.asm parser.

Brett Kuntz