[VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Sometimes you need a better SavePicture() function. Not a lot better, just one that can save in some compressed format instead of just BMP format. Like JPEG usually, or PNG. Well this one does that, and throws in GIF as well though as usual (being based on GDI+) those tend to come out dithered and sort of crap in general.
What we have here is a simple preclared-instance Class with one method: SavePicture().
You give it a StdPiture, a file name (yes, it can save using Unicode paths), which format you want, and for JPEG you can add a "quality" in percent. It saves to disk, not to Byte arrays.
Nothing here people haven't seen before. This is just a "stripped to essentials" rendition of the well worn theme.
It only requires a version of Windows with IE 5 or later. It uses GDI+ but most systems with IE 5 or later cover that as well. In any case it should work on nearly anything you run anymore.
There are no 3rd party DLLs required, and not even any typelibs. Just add PicSave.cls to your Projects.
The attachment contains a simple demo. Its bulk is all source image data.
The StdPicture you pass to it must have a bitmap handle. In practical terms this means you may have to pass it the persistant-image property (.Image) if you have drawn your picture onto a Form, PictureBox, etc. and there is no provision for dealing with metafile vector images.
Notes:
New attachment incorporating feedback from discussion below to address issues encountered when GDI v. 1.1 is in play, running on 64-bit Windows, etc.
Also note that this makes no effort to handle transparency or alpha-channel translucency for GIF or PNG output. It saves simple "whole bitmap" images. If you load a picture with transparency into a StdPicture and save it back using this class the transparency is lost.
Last edited by dilettante; Nov 1st, 2015 at 05:09 AM.
Reason: replaced attachment
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
In response to a PM:
No Ted, it is not using a WebBrowser control. I mentioned IE 5 because it makes a call to the Shell Lightweight Utility API and it requires Shell32.dll 5.0 or later, which in olden times was updated as part of IE.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Originally Posted by LaVolpe
Couple things. Constructive criticism follows. You know I have respect for you, so don't take this in a negative way...
I am far less concerned about reputation than about not misleading others with flawed code samples, so criticism is valuable.
Originally Posted by LaVolpe
1. Crashes when passing this line while picture contains GIF: PicSave.SavePicture Me.Picture "test.gif", fmtGIF
I can't reproduce the problem. Can I assume "Me" is a Form? How was the GIF loaded into it?
Originally Posted by LaVolpe
2. GIF likely not to save with transparency if it has it, i.e., from a design-time loaded GIF. Same applies to PNG if trying to save GIF frame to PNG.
I agree, but that wasn't the purpose here at all. Perhaps I should have stated that transparency is not addressed. I had things like webcam snapshots in mind when I cobbled this together.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
I can't reproduce the problem. Can I assume "Me" is a Form? How was the GIF loaded into it?
Well, seems doesn't really matter whether Me.Picture has a GIF or not, crash seems to occur. From catching it before/as it occurs, appears to be related to the SHCreateStreamOnFile call. If it doesn't crash after that call, then GdipSaveImageToStream always fails with a zero-byte gif file. Tested on Vista with all latest MS patches applied.
Insomnia is just a byproduct of, "It can't be done"
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Hmmm...
I tried this on Vista SP2, fully patched. Works fine, but 32-bit. Tried it on XP SP3, as fully patched as that can be anymore. Works fine, but 32-bit.
Tried it on a 64-bit Windows Home Server 2011 box and it failed but created an empty GIF file. There seems to be some weird file-creation delay on 64-bit.
No, stuffing in a DoEvents didn't help. Weird, weird, weird. I need to dig further because my entire point was to eliminate the need for any IStream typelib. Perhaps this just won't be practical.
The error was a system error 2: ERROR_FILE_NOT_FOUND
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Something is fishy here because I see people using SHCreateStreamOnFile all over the place for all sorts of things.
I'm beginning to wonder if this isn't a GDI+ v. 1.1 issue. Were you testing on Vista with a manifest selecting the v. 1.1 assembly? i don't have 64-bit Vista or 32-bit Win7 handy to test against.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Ok. I dug up a 32-bit Win7 VM and sure enough it fails there the same way. I'm suspecting GDI+ v. 1.1 myself, but that doesn't really help much so I need to create a memory stream and write that to disk myself I suppose.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Sorry didn't get back to you sooner. Yes v1.1 via manifested IDE. Seems you've identified a bug
You can still avoid the typelib by simply using GDIpSaveImageToFile
After some trial & error, I got past the crashes this way.
I changed all references in the class from "Object" to "IUknown". Don't know if that was absolutely necessary but it didn't immediately correct the problem, but it did get past the crashes that occurred on or immediately after calls to SHCreateStreamOnFile. Now the problem was the error related to GdipSaveImageToStream. GDI+ reporting invalid parameter.
So I changed your GdipSaveImageToStream API declaration to pass the final parameter ByRef as Any. Then changed the call in your class to the following. Crashes & errors not occurring any longer.
HRESULT = GdipSaveImageToStream(gdipBitmap, Stream, EncoderGUID, ByVal 0&)
Insomnia is just a byproduct of, "It can't be done"
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Interesting.
Ok, I changed the three As Object to As IUnknown though I'd assumed it shouldn't matter. I'm beginning to think that only matters under WOW64 but I'll live with it without further testing because the change costs nothing. Worth being aware of though so I'll make a note to myself for future reference... and now I wonder how much code I have with that potential time-bomb in it. *sigh*
I decided to avoid the need to play flippity-flop having two separate calls to GdipSaveImageToStream.
Instead I declared the last argument ByVal pEncoderParams As Long and have a pParams As Long that I either leave = 0 or assign VarPtr(Params) value to before the single call.
Originally Posted by The trick
I think need use GdipGetImageEncodersSize+GdipGetImageEncoders (ie GetEncoderClsid) for obtaining the Class Identifier for an Encoder.
I'm not sure we need to do this here because I'm only making use of a subset of the encoders that both versions of GDI+ always come with. I suppose you could do that to retrieve all of the ImageCodecInfos and then build a mapping from a format-choice enum's values to those CLSIDs by parsing the MIME types. I'm not sure what value that has though.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
You always say something like "It's crime to use Windows XP because ...(list of security issues)".
And every now and then you say "This code also works with Windows 9x".
Maybe it would be time to make a clearer alignment.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Indeed, Windows XP is dead and no longer gets security updates and thus is dangerous for general use.
That doesn't mean that an old versions of Windows might not be useful now and then for special cases, and sometimes you may not have a choice. Windows 95 through Windows XP are still often used as embedded system OSs. The vendor doesn't support installing another OS, or the systems are isolated from the Internet, the SOC board used has tiny RAM, etc.
That is entirely different from a desktop or laptop being used as a general-purpose PC connected to the Internet for web surfing though.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
I just came across this post this evening and downloaded your module. It works fantastic! So, thank you very much!!
I'm using VB 6 Pro under Windows 7 Home Premium 64 bit and having no troubles at all saving PNG images from my PictureBox controls. However, i did have one little issue that was very easy to overcome. The output file is 4 pixels smaller than the image control, both horizontally and vertically. So, all i did was add 4 pixels to height and width to get the image output size i want.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
I'm not sure about those 4 pixels. Is there a High DPI scenario in play that is throwing calculations off a bit? Or are you trying to save the borders of the Image control?
Here's a newer version with loading and saving, just the .CLS file.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Originally Posted by dilettante
I'm not sure about those 4 pixels. Is there a High DPI scenario in play that is throwing calculations off a bit? Or are you trying to save the borders of the Image control?
Here's a newer version with loading and saving, just the .CLS file.
I did figure it out. Total goof on my part. I was using .height & .width instead of .scaleheight & .scalewidth.
Thanks for the new version! Will try it out. Being able to load too will be a big help.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
@dilletante: JFYI, possible to skip the GdipImageRotateFlip call if using negative height in BITMAPINFO.bmiHeader.biHeight = -.bmHeight
Not tested extensively besides saving to a PNG but it seems you are not dealing with vbPicTypeIcon's or any other non-picture type so probably is good enough.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
I have created my own picture types, using a longint array, and all my own drawing routines that allow ultra-hires 16384x9216 in some projects. The routines allow anti-aliased drawing, transparency, and a host of plotting permutations like lighten, darken, invert, etc. All the goodies an artist needs that aren't included in VB. However, i then copy the image buffer over to a standard VB picture box for saving, so your save to PNG routine works perfectly. Thanks again!
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Originally Posted by wqweto
@dilletante: JFYI, possible to skip the GdipImageRotateFlip call if using negative height in BITMAPINFO.bmiHeader.biHeight = -.bmHeight
Excellent point. Seems so obvious now.
Originally Posted by wqweto
Not tested extensively besides saving to a PNG but it seems you are not dealing with vbPicTypeIcon's or any other non-picture type so probably is good enough.
That's true. I should probably be more explicit that only bitmap type StdPicture objects are supported and not icon, metafile, etc. as well as test for other types and throw an exception for that.
[edit] Thanks for help Dilletante, the project is now updated. It's a VB5 release but your code seem working properly. (note: the res file isn't update. a link to this page is enclosed in the binary zipped file)
If something's wrong, let me know.
Last edited by XavSnap; Oct 3rd, 2019 at 04:55 PM.
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
This code works very well. But can it be enhanced to convert to SVG files?
There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!
Richard P. Feynman
Re: [VB6] PicSave - Simple SavePicture as GIF, PNG, JPEG
Originally Posted by dilettante
I'm not sure about those 4 pixels. Is there a High DPI scenario in play that is throwing calculations off a bit? Or are you trying to save the borders of the Image control?
Here's a newer version with loading and saving, just the .CLS file.
Thank you so much Friend !
It solved my very important problem to save Base64 QR Code to a BMP file.
Thank you so much !!