Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Image conversions and capturing with GDI+


  • Please log in to reply
72 replies to this topic
PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
In an article, Goyyah explained how to do BMP to JPEG Conversion with a third party DLL (dilib.dll).
I immediately thought: "Why use a third party DLL that has to be installed, when Windows now provide such facility with GDI+?".
GDI+ (gdiplus.dll) is provided by default in the latest versions of Windows, and can be installed freely on older versions.

So I started to look at the GDI+ API.
Ow! It wasn't so simple at all!
First problem: GDI+ is (AFAIK) really a bunch of pure C functions, and Microsoft created a C++ wrapper around them, to offer an object-oriented interface. It is nice if you use C++, some .NET language or other OO languages, but it is unusable in AutoHotkey, of course.
MS provides a "flat API", ie. access to the pure C functions, but under-document them to discourage its use...
Fortunately, Visual Basic (and PowerBASIC) users has the same problem, solved it, and provided some samples of flat API use.
Here comes the second problem: this interface might be flexible, but it is awful to use!
You have to convert, for some obscure reason, good Mime types like image/jpeg, to arcane GUIDs like {1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}, it abuses of pointers to pointers, opaque structures, and so on.
I had hard time converting the examples and even harder making them work!

Well, finally, I successed (otherwise I would be in the Ask for Help section...).
So, here is a simple script, converting a file of whatever read format supported by GDI+ (BMP, JPeg, Gif, PNG, Tiff, WMF, EMF, icon) to whatever write format it supports (BMP, JPG, GIF, PNG, TIF).
It uses the GDIplusWrapper.ahk file (wrapper around some GDI+ functions) which depends on the DllCallStruct.ahk library, which itself depends on BinaryEncodingDecoding.ahk...
[EDIT] Note: holomind already used this library to take screenshots. Great! For compactness, he gathered all necessary functions in one file.
I summarize here the functions to copy from DllCallStruct (BinaryEncodingDecoding is actually not used, here only because of generic inclusion of files in generic library files): GetUnicodeString, GetAnsiStringFromUnicodePointer (all strings in GDI+ are encoded in Unicode) and GetInteger/SetInteger. Just copy these functions in your copy of GDIplusWrapper and you have a nice, compact library. :-)

The test code was short, I used to just give it here, but it gets longer, so just download it:
TestGDIplus.ahk

I don't plan to extend much this library right now, but I am open to suggestions. I think it opens a whole world of image creation/alteration...

[EDIT 4] 1.04.000 -- 2006/10/02 (PL) -- Added GDIplus_FreeImage.
[EDIT 3] 1.03.000 -- 2006/09/15 (PL) -- Added GDIplus_CaptureScreenRectangle.
[EDIT 2] 1.02.000 -- 2006/08/22 (PL) -- Added GDIplus_LoadBitmapFromClipboard.
[EDIT 1] 1.01.000 -- 2006/08/21 (PL) -- Some fixes, support of more parameters.
The interface for parameters is changed.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

robiandi
  • Members
  • 49 posts
  • Last active: Jul 27 2008 05:04 PM
  • Joined: 08 Aug 2006
I didn't found https://ahknet.autoh...plusWrapper.ahk

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

I didn't found https://ahknet.autoh...plusWrapper.ahk


Dear PhiLho, :D

Are you not in town? The link is still not working! ...
Eagerly waiting.
Regards, Posted Image

@Titan: If you're reading this .., Please let us know if there is a typo in the URL.
kWo4Lk1.png

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
Oops, I just forgot one thing... to upload the file!
Sorry for the inconvenience, and tell me what you think of my little library.
Thanks you for bringing this up, I should have tested my links.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Posted ImagePosted ImagePosted Image

I tried it in Windows 2000 SP4. The script hinted me to put GDIPlus.dll in the Path.
Since I have WIN XP installed, I borrowed a copy of GDIPlus.dll and dropped it into the Scripts folder...

I tested with a BMP snapshot of my desktop 1024x768 file size: 2305 KB.
Oh my! Terrific speed ... I did not expect it to be fast! :shock:

I have to spend time with your code to understand the internals! How does this work fast?
Thanks for putting such a great effort .. and for sharing the wonderful code with us.

I don't plan to extend much this library right now, but I am open to suggestions. I think it opens a whole world of image creation/alteration...


What would I ask? or What should I request?
One thing is missing in this forum for a long time...
Saving Clipboard Bitmap data to a file..
Maybe, this is too much to ask .. but it will definitely supplement.

Please consider ..

Regards, :)
kWo4Lk1.png

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005

I have to spend time with your code to understand the internals! How does this work fast?

Easy: I let Windows (ie. the GDI+ DLL) to all the hard work! So it is as fast as a C program can be...

What would I ask? or What should I request?

Perhaps something like this Unicode Logo Generator... Yes, that's the kind of things that you can do with GDI+! And what I won't do, at least not soon...

One thing is missing in this forum for a long time...
Saving Clipboard Bitmap data to a file..

Excellent idea, I will consider it.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

robiandi
  • Members
  • 49 posts
  • Last active: Jul 27 2008 05:04 PM
  • Joined: 08 Aug 2006
It works fine !!!

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
Great work. I went through a similar challenge when adding the GDIplus support to AutoHotkey (because the functions have to be called in a way similar to DllCall due to the absence of GDIplus on operating systems older than XP).

By the way, I think someone above mentioned something about needing the DLL on XP. However, any system running XP or later shouldn't need a GDIplus DLL because the system already has one.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

I think someone above mentioned something about needing the DLL on XP. However, any system running XP or later shouldn't need a GDIplus DLL because the system already has one.


I was that someone above!

I tested in Window 2000 - SP4 & so had to borrow GDIplus.dll from Windows XP (which is also installed in my computer.)

I mentioned it to alert Windows 2000 users.

Regards, :)


kWo4Lk1.png

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005

I think someone above mentioned something about needing the DLL on XP.

You read too fast ;-) It was on Win2k.
I rewrite part of the code to add flexibility to define the encoding parameters. But it is a nightmare, because MS doesn't clearly define what parameters each encoder accepts (it just shows how to query these parameters from the encoders, and give some examples).
It can also be used to get some stats on images, like dimensions or content of EXIF fields, but it doesn't seems straightforward either.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

It can also be used to get some stats on images, like dimensions or content of EXIF fields, but it doesn't seems straightforward either.


:shock: EXIF! Oooh! Sounds pretty interesting .. Regards, :)
kWo4Lk1.png

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
OK, it was quite easy to add support to images on clipboard. File is updated, I give a small example.

Perhaps I should simplify a bit the interface. The GDIplus_GetEncoderCLSID function could be avoided by just providing the Mime type of the format to the functions. They would have to call internally the function to get the encoder CLSID each time, but I suppose it wouldn't be noticeable on a modern computer.
Likewise, calling GDIplus_InitEncoderParameters isn't very nice, but currently needed to allocate the right size of memory.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

support to images on clipboard. File is updated, I give a small example.


Thanks for including support for Clipboard image data.

I downloaded the three .ahk's afresh as well as the example code.
Then took a snapshot of Destktop ( {PrintScreen} ) and tried the example - but I get an error :(

Posted Image
However, I am able to paste the image in any Image editor!

Please look into this.

Regards, :)

Edit: The first example also does not work for me. I get the following error:
Error in GdipCreateBitmapFromFile (2: InvalidParameter) 0
kWo4Lk1.png

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005

I downloaded the three .ahk's afresh as well as the example code.
Then took a snapshot of Destktop ( {PrintScreen} ) and tried the example - but I get an error :(

For some mysterious reason, the parameter to the GetClipboardData call went back to 8 (CF_DIB) instead of 2 (CF_BITMAP).
Well, I corrected this, it should work now, thanks for reporting.

The first example also does not work for me. I get the following error:
Error in GdipCreateBitmapFromFile (2: InvalidParameter) 0

Well, it still works for me...
You should try and change the tiffColorDepth value (if the problem comes from here, I haven't added an error locator... but I had similar problems here), maybe it depends on the image you chose to test.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Dear PhiLho, :)

Thanks for the fast response.
Things are perfect now. :D

However, Jpeg Image Quality is not satisfactory!

jpegQuality = 3


What will be the range for above variable?
I tried 10 .. I think the compression is heavy with too much of artifacts. :roll:

What am I missing?

Regards, :)

Edit: Oops sorry! I found that the range is 1-100
I suggest this: It will be better you change the value to 80 in you example.
kWo4Lk1.png