Yes, you can use GDI+ with VB6 (and maybe previous versions with a little editing) along with Win98 or better! Fixed GdiplusShutdown declaration [8/3]. Added ImageLockMode and ImageCodecFlags enums [1/22]. Dana Seaman contributed the translation of the huge EmfPlusRecordType enum and I fixed a frame delay bug in Play GIF [12/4]. Dana created an even better function for getting all GUID constants [12/19]. The goal was to simply port the C++ APIs to VB6, and as I never personally found any comprehensive examples or declarations, although there was one newsgroup post mentioned to me that clarified one issue holding me back (See "HOWTO: call
GDIPLUS directly from VB6" in comp.lang.basic.visual.misc), I decided to port nearly 30 SDK samples and declared basically every single API I could find and the required enums and constants. There is so much, I hope I didn't miss anything else... Estimated API conversion accuracy: 99% Correct. If you don't have WinXP, you may need the GDI+ Redistributable RTM which is available here: http://www.microsoft.com/downloads/release.asp?releaseid=32738 . . . You should also read the beginning of the GDIPlusAPI module, as it has some useful information. If you find anything wrong, complain away, but show the fix when possible! I don't like when people have a problem and then simply say "I fixed it", as others may have a similar problem and not know how to fix it. I encourage everyone to edit the sample, post better samples, and make some classes to simplify the GDI+ usage even more! I'm not great at writing boring or even exciting introductions, so I'll try my best to explain some core ideas to you, and the code is also heavily commented, though not "well" commented. A GDI+ object class, for the purposes of VB, is simply a pointer (aka Long). The pointer, once retrieved, is simply passed ByVal to the API functions. All you really have to worry about is remembering to delete the object that the pointer represents when you are done. You could also think of the pointer as a handle, if it helps you understand better. Some API functions also want arrays of types for a parameter (you'll usually see a count parameter associated with that function). Another important fact to remember is that ALL strings that GDI+ returns or uses MUST BE UNICODE! In this code I opted to use StrConv to achieve Unicode strings, and the APIs are accordingly declared As String, but you can alter the APIs to use the StrPtr result if you like by selecting the API function declarations and Replace " As String" with " As Long". I opted not to do that as I feared I might forget what type of variable is really required, although doing so improves performance. Also, if you see "argb" floating around, it stands for Alpha-blending, Red, Green, and Blue - it is the color "encoding" scheme used by GDI+. I hope this will quickly cover some of the finer points about GDI+, but I can't rival the explainations of the GDI+ documentation: http://msdn.microsoft.com/library/en-us/gdicpp/gdi+/gdi+.asp?frame=true . . . Notes about the example code: Look at the mnuRedraw_Click menu event to see a list of available samples, and uncomment them to view the demo. I did this to encourage you to view the source. For the thumbnail sample, you'll have to insert the filename of a huge file you'd like to view the thumbnail for. There is also a very basic "freehand" curve drawing demo under the file menu [9/4]. Added a rough on demo how to play an animated GIF [9/7]. Added a SaveGrayscale demo with code from Dana Seaman [11/26]. Added a wrapper demo from Dana [11/30]. Added a DrawTextBackColor demo [1/18]. Added a LockBits demo [1/22]. Added two different BMPtoGIF demos [2/6].
Note: Due to the size or complexity of this submission, the author has submitted it as a .zip file to shorten your download time. Afterdownloading it, you will need a program like Winzip to decompress it.Virus note:All files are scanned once-a-day by Planet Source Code for viruses, but new viruses come
out every day, so no prevention program can catch 100% of them. For your own safety, please:
Re-scan downloaded files using your personal virus checker before using it.
NEVER, EVER run compiled files (.exe's, .ocx's, .dll's etc.)--only run source code.
Scan the source code with Minnow's Project Scanner
If you don't have a virus scanner, you can get one at many places on the net
Terms of Agreement:
By using this code, you agree to the following terms...
You may use
this code in your own programs (and may compile it into a program and distribute it in compiled format for languages that allow it) freely and with no charge.
You MAY NOT redistribute this code (for example to a web site) without written permission from the original author. Failure to do so is a violation of copyright laws.
You may link to this code from another website, but ONLY if it is not wrapped in a frame.
You will abide by any additional copyright restrictions which the author may have placed in the code or code's description.
I noticed in another recent post, which looked very good, someone asked if GDI+ could rotate images. Alas, I didn't show another useful feature, so I just added a DrawRotate function to the example. If you are wondering if GDI+ can do something, just ask! I'm sure it can! The real question is how! (If this comment was disrespectful, please report it.)
I came across this code looking for something about .Net but the codes looks real nice to me. In .Net it's possible to rotate using : Dim m as Image = new Bitmap( (If this comment was disrespectful, please report it.)
Yeah, the .Net classes are VERY nice, just like the C++ classes, but Microsoft doesn't have (to my knowledge) any way for VB6 to access GDI+, thus, the programmer must either make their own typelib like Vlad said, or declare the APIs for VB, and I, not knowing how to make a typelib and disliking the idea of requiring an extra dependency (VB programs have enough of those), I opted to convert the APIs...by hand. (I don't think I'll be doing that again anytime soon!) (If this comment was disrespectful, please report it.)
Add to Declarations: Dim GPS As GpStatus. In Sub DrawSolidShape after graphics is initialized add GPS = GdipSetSmoothingMode(graphics, SmoothingModeAntiAlias). Voila you now have antialiased objects. (If this comment was disrespectful, please report it.)
Wow! Lot of work went into this. Excellent. I must admit I didn't realise that you could use GDI+ in Win 98 either. Extra bonus. 98 and Vb6. 10/5 *****++ (If this comment was disrespectful, please report it.)
I wondered what the capabilities of the RotateFlip function that Yves used was, so I looked and the function to use is GdipImageRotateFlip, but you can only rotate 90, 180, or 270 degrees, but there are many X,Y flip configurations to choose from. I encourage you to play with it, and all the functions! (If this comment was disrespectful, please report it.)
ok, as a first step this is great... but VB coders are lazy (me included) and having only api declarations is not useful, it would be great if you could make a class to implement most of the functions with ease. (If this comment was disrespectful, please report it.)
I added 4 more demos, 2 are basically the same; just text and graphic clipping, DrawBezier draws a bezier line and a DrawHitTest demo, which demonstrates Hit Testing. If I may reply to the above comment, I don't understand how the API declarations are great and yet not useful. Anyway, there are three reasons why I shant make classes: 1) No time anymore for such an endeavor, but I strongly encourage others with the time to do so and post away! There should be more than one GDI+/GDIPlus hit in the VB area! 2) I don't think that VB classes would be as nice as the C++/.NET classes due to VB6's inability to cope with function overloading and class constructors with initialization values. I will admit Get/Set APIs would do well to be in a class so explicit object deletion would no longer be an issue. 3) I dispise VB classes and avoid them like the plague when possible. Also, with my limited experience with them, they make VB programs require a full install since the classes are ActiveX. (If this comment was disrespectful, please report it.)
.... (continued) ... Gonchuki has, however, raised some valid issues, such as this is only a first step to using GDI+ with VB6 (maybe even previous versions), and some improvements are just yearning to be made. I encourage everyone to take this to the next level!
(If this comment was disrespectful, please report it.)
Small question. I have this in the startup of my main form gpinput.GdiplusVersion = 1 If GdiplusStartup(token, gpinput) <> Ok Then MsgBox "Ouch!" Unload Me End If
Now the thing is i dloaded the gdiplus.dll from microsoft and when I run the program thru the File/Open Project of VB6 I get the message gdiplus.dll not found. And yet if I load the program by clicking on its vbp icon and starting VB6 that way, the error doesnt occur.
Should that .gdiplusversion=1 be any different? I dont understand the error and would appreciate your input.
Again, excellent code. :) Brommers (If this comment was disrespectful, please report it.)
No, the gdiplus version value should be one, that is correct - you'd get the "Ouch" message box if GDI+ failed to load, by since you get the "DLL not found" message, VB can't find the DLL in the usual places. Make sure that the DLL is in one of these places: EXE folder, Windows\System folder, or the Windows folder. Is that what the problem was? (If this comment was disrespectful, please report it.)
Hmm, wait, I think I better understand the problem now - I'll bet that the DLL is in the EXE folder, right? The problem is all VB's fault! I found out the hard way, just like you have, that when you open VB and do a File/Open for a project, VB's application directory is set to the folder VB is installed inside. This is where files opened like this: "data.txt" would be saved/loaded. If you open the project by clicking the file icon, either in Explorer or Start Menu -> Documents, the application directory will be set to the folder the project is located in, and all with work as expected. My solution was to never use File/Open again, but maybe there is a better way...? (If this comment was disrespectful, please report it.)
Hehe...your timing was impeccable...I had the dll IN the EXE folder as you said, and after moving it to windows/system all works fine for that.
Guess what...another question though. I've resized an image using the drawscaled example, but how on earth do I save that resized image to file? the gdipdrawrectrect just seems to 'draw' the image rather than change the image itself, so saving the img just saves the original again. I tried using the gdipcreatebitmapfromgraphics and although it does save an image in the resized dimensions, all i get is a black box at the right size lol. No picture.
Thanks for your reply, and hope you can help with this one :) Brommers
(If this comment was disrespectful, please report it.)
Excellent Question! I never saw any mention of actually saving altered images, but I took a long look at the APIs, and found an answer! Therefore, I added a new demo to the ZIP File source: SaveScaling. Hopefully this is exactly what you are looking for, and is a good guide for extra image alteration and saving features (IE: I did not know you could create a graphics object from an Image! Just think of the possibilities...) As a side-note, gdipcreatebitmapfromgraphics seems to only create a blank bitmap which has the same palette, resolution, etc. as the graphics object passed to it. The key to getting the draw function to draw on that bitmap is to create a graphics object for the new bitmap, like it is in the example. I like tough questions - keep them coming people! (If this comment was disrespectful, please report it.)
You absolute star you!!!!!!! lol, seriously thankyou ever so much avery. And if I have any more hard questions I'll be sure to drop you a note lol Brommers (If this comment was disrespectful, please report it.)
I added another sample which uses one of the IStream functions. These functions allow you to save the image to memory, as if it were on disk. This could be very useful for some internet applications. You can also load images from a stream with GDI+ in a similar manner if you desire. To run the sample, you'll need a typelib (I mentioned the one I used for testing in the source) with IStorage and IStream interfaces or use the OLE APIs and rewrite the sample and GDI+ declarations...either way you'll have some uncommenting to do. Also, the IStream declarations were wrong when I first posted them - all stream objects, except for the one in GdipCreateStreamOnFile, should be passed ByVal or you will get a GPF. I made the new sample in part to see if they were correct, and now I think they are correct... (If this comment was disrespectful, please report it.)
First of all, THANK YOU! This is great. I downloaded and tried the code last weekend but ran into a problem that maybe you could help me with. I modified the commenting under the menu to produce a .png file, and it worked great the first time. Tried it again and got an error message. Figured this must be because I forgot to delete the first output file, so deleted it and ran again but got the same error message (I think it was 2, invalid parameter or something like that). Down-loaded the clean code again, but continued to get the same error message. To make a long story short, it appeared that something in my system got tromped. So before I try it again, I was wondering whether you have any ideas what might be happening here so I can avoid it next time. Thanks very much. (If this comment was disrespectful, please report it.)
Ah yes, if you get the status code 2 error, then one of two things has happened: 1) the bitmap file I used in in the demo is deleted or missing or 2) you opened VB and selected File->Open or selected the project from the recent project list within VB. This goes back to the issue of VB6 searching for relative files, such as "foo.txt", inside the folder that VB is installed inside. You can open the VB project from Start Menu->Documents, or open windows explorer and double-click the project file (my usual method), or if that is too much hassle, which it can be sometimes, change the path of the images to an absolute path such as "C:\Dev\GrapeBunch.bmp" or even better: App.path & IIf(Right$(App.path, 1) = "\", "", "\") & "GrapeBunch.bmp",
Er, I suppose I should add that getting the status code of two (2) does not always mean that the bitmap is missing or you opened the project in VB via File->Open so VB is searching in the wrong places, but in this case, I'll bet it is the case. (If this comment was disrespectful, please report it.)
I had never heard of that problem before, but yes, I tried what you said, and it's definitely associated with starting VB and then opening the project from either the Existing or Recent projects tabs. When I go directly to the folder where the project is stored and double-click the .vbp file, it works fine every time. Thanks again! (If this comment was disrespectful, please report it.)
I am getting a crash in VB when I make the function call 'GdipGetAllPropertyItems'. MSDN says that I need to allocate a buffer large enough to receive the array. My question is, how do I use malloc in VB to allocate memory to the allItems parameter? Is there something I can use in the GDIPlusAPI module? (If this comment was disrespectful, please report it.)
Ah, another good question! Since there is no way to do what the C++ example does in VB that I know of (ie: allocate a memory block and treat the resulting memory block as an array), there are at least two workarounds I know of: create two separate variables; one is a byte array and the other is a dynamic array of the structure we need. After retrieving the data to the byte array I use CopyMemory to copy the data to the dynamic structure array and that seems to work. The other option is that you could eliminate the byte array by ensuring that the structure array was large enough to hold all the data. This would also reduce a few lines of code In the BMPtoJPEG_Params example, I used a variation of the byte array method since the functions has different requirements; the Get__coderClsid functions are a better example of the byte array method I mentioned ...but alas! (Continued below)... (If this comment was disrespectful, please report it.)
...(Continued)... When you try using the byte array method with the current API, you will get a ByRef error. Why? Because when I translated the APIs, I declared the API in question to expect a PropertyItem structure pointer not a byte array, and VB can't handle that, so the parameter would need to be changed to 'As Any' instead of 'As PropertyItem'. I decided to use an over-dimmed structure that will be big enough to hold the structure array and all the extra data so I won't have to change the API declaration and also to demonstrate the method in action. (Come to think of it, it might be better than the byte array approach...) I just added an example for you entitled GetAllProperties, which should outline the process I have just described and hopefully give you good insight and a starting point. I say do whatever you feel seems right and understandable and if there is yet another better way someone knows about, please, do tell! As always, ask away! (If this comment was disrespectful, please report it.)
The GetAllProperties example was very helpful. Thanks for taking the time to write it!
I have a follow-up question on the 'PropertyItem' type within the module. If the type of my property was 'PropertyTagTypeASCII', how would I extract the string if the 'value' attribute is always declared as a long? (If this comment was disrespectful, please report it.)
Hmmm... I can't seem to figure out how the 'value' attribute (type long) within the 'PropertyItem' type stores its data. If the property is of type 'PropertyTagTypeASCII', then I can use the PtrToStrW function. But, what if the property type is 'PropertyTagTypeRational' (two longs) or 'PropertyTagTypeShort'?
I am guessing that the 'value' attribute is just a pointer which needs to be dereferenced. After which, the contents of the memory in that location are copied into a variable of the type required (just like the PtrToStrW function). Am I right here? (If this comment was disrespectful, please report it.)
Yep, you are correct. From what I can tell, the value is always pointer - an array pointer according to the SDK. The pointer will usually point to the data that is within the allocated buffer, I believe, which would explain why the totalBufferSize does not equal the structure size times the number of entries. You'd probably have to use CopyMemory to place the data in another usable variable using the 'length' member to see how many bytes to copy, just like you mentioned.
Here is a link to a page in the MSDN GDI+ docs that lists the property types along with what and how data is stored in the 'value' member, in case you hadn't seen it already: http://msdn.microsoft.com/library/en-us/gdicpp/GDI+/GDI+Reference/Constants/ImagePropert yTagTypeConstants.asp?frame=true
Hope this helps, and, as always, feel free to ask questions! (If this comment was disrespectful, please report it.)
I have been told by Microsoft and have seen in the documentation that we shouldn't directly call the GDI+ "flat" API's, but instead always use the C++ wrapper classes, or use .NET. Why is this, and what's your assessment of the "dangers" of calling the API's directly? I'm guessing it has something to do with changes that may occur in future versions of gdiplus.dll, but I really don't know. (If this comment was disrespectful, please report it.)
Well, I never read or heard that one before (I'd like to know where that is though; post a link or something and I'll read up on it), but if that is true, knowing Microsoft it probably is, the only reasons I can think of are that the GDI+ API names could change (there are a few APIs that have numbers in the function name, but I aliased them in the VB declarations). Another is that the C++ and .NET classes completely wrap the GDI+ API, so there is no reason to call the API directly. If you look at the GDI+ C++ classes, you can see for yourself that the classes are just thin wrappers to the GDI+ API, and the .NET classes seem to wrap even more of the GDI+ functionality. The potential of passing an invalid parameter to the APIs, causing a GPF or maybe worse is another reason, but this problem is true for any API usage. (Continued below)... (If this comment was disrespectful, please report it.)
...(Continued).. There is also no GDI+ API documentation per-se; it is all mainly based on GDI+ class usage, so you'd have to either look at the C++ classes to see how to call the API (which is what I did), or figure it out based on parameter names. I think this was done because the classes call the Delete/Dispose functions automatically, otherwise you must call them explicitly, which is very easy to forget, and they wrap the API so well. All in all, I personally would say it is as dangerous as calling any other windows API. I also wouldn't worry too much about function names breaking since Microsoft seems to love providing backward compatiblity. If this still doesn't seem settling, I suppose you could do what Microsoft would ultimately like: upgrade to .Net, but this wasn't one of my options. I suspect this is why there is no Microsoft-provided way for VB6 to access GDI+. (If this comment was disrespectful, please report it.)
The most "official" reference to "don't use GDI+ base API's directly" that I've seen is at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdicpp/GDI+/GDI+Referen ce/Constants/ImagePropertyTagTypeConstants.asp - this section is notably missing from the Oct 2001 copy of MSDN, as far as I can tell. I haven't checked the later versions.
In addition, at http://msdn.microsoft.com/newsgroups/, and then Graphics and Multimedia -> GDI -> Win32.Programmer.GDI I found several responses to user questions by the MS Tech Support personnel that basically say GDI+ is "difficult to access" from [pre-.NET] Visual Studio (didn't say impossible, though) Must admit that I just looked and couldn't find those specific posts - but I didn't look a real long time looking.
When I asked an MS tech support person who was helping me with something else about this, he said the official position was they don't support it and the official recommendation was not to use it. (If this comment was disrespectful, please report it.)
Ah, yes, I see a post in that newsgroup from a Microsoft employee (thread entitled: "GDI+ with Borland C++ Builder") in which he confirms that the only "supported" methods are via .Net's System.Drawing and the C++ classes so you are right. However, there are many, many things that Microsoft does not "support" that people routinely do or use on a day-to-day basis, and Microsoft even provides free downloads that are "unsupported" but are extremely useful (IE: TweakUI). I don't even really know what "unsupported" means exactly, but I suspect it means that if something goes wrong, Microsoft won't help you and you are on your own... Somewhere, I also saw mention of the gdiplus.lib containing some sort of extra functionality critical to using GDI+, but all these VB examples seem to run fine without it, hmm. If I was a conspiracy theorist... Oh well, maybe they are just avoiding potential lawsuits, or something like that.
Thanks for the links! As usual, if anyone has questions, ask! (If this comment was disrespectful, please report it.)
That's the same error I encountered earlier (8-17-02 post). I don't know if this will fix your problem, but in my case the problem was solved by following Avery's suggestion: I put App.Path & "\" before both the input and output file names, and then also inserted a unique string based on date and time before the output file extension (.jpg or whatever). (If this comment was disrespectful, please report it.)
Hmm, I read your problem and saw another C++ sample that does kind of what you are wanting, so I ported it and thus there is yet another sample, entitled "ChangeTitleProperty". It basically uses the sample JPEG from BMPtoJPEG and changes the title or makes one if there wasn't one. The only odd thing I encountered is that the Image Title must be an ASNI string!? I find this is a bit odd since just about everything else is Unicode. Anyway, I added a PtrToStrA function and made a "hack" which turns a string into a byte array, which is an ANSI string. I never got any errors using StrPtr though (just truncated titles). I'd have to see your full code to determine what is wrong, but maybe Doug's suggestion and this sample will help? If not, let us know! Some additional guesses: Is img a valid pointer? Was an encoder actually found(Check the function result)? As always, if you have problems, I'll try to solve them! (If this comment was disrespectful, please report it.)
It is a link to a page in the Online MSDN which lists and documents each GDI+ "Flat" API, in C++ of course. Regardless, it should also be a useful reference as it shows the APIs and the associated C++ class methods. (If this comment was disrespectful, please report it.)
There's a wealth of information on this board. Thanks a lot for the 'ChangeTitleProperty' example!
It turned out that while trying to save to a new image, I had an invalid image pointer, since in this case, I never thought to obtain the pointer to my original image.
This leads me to another question. Whenever I try over-writing the same file which I loaded using GdipSaveImageToFile, I get a 'Win32Error' status. Am I doing something wrong or is this designed behaviour? (If this comment was disrespectful, please report it.)
First off, it seems I had somehow managed to not declare one very important API: GdipCloneImage() I hope you all can forgive me! I have now declared it.
As for the save error, I believe it is what Microsoft would call "behavior by design". Here is what the SDK said:
"GDI+ does not allow you to save an image to the same file that you used to construct the image. The following code creates an Image object by passing the file name MyImage.jpg to an Image constructor. That same file name is passed to the Save method of the Image object, so the Save method fails."
You should also know that once you open an image, GDI+ write locks the image file until you dispose of the image, so you won't be able to delete it. You could save to a temp file, close the image then move/rename the temp file. I'm not sure how else to handle that. (If this comment was disrespectful, please report it.)
I finally tried the thumbnail sample I had ported (I never tested it) and discovered that it was using the wrong image to gather the height/width info from, so it was shrinking the image to create the thumbnail and then drawing the thumbnail at the original size, which enlarged it and looked extremely bad. I fixed that error, even though retrieving the thumbnail height/width is not needed since the values are known and could be hard-coded. (If this comment was disrespectful, please report it.)
9/2/2002 6:57:39 AM:
Hello,Avery,now I have a problem about the function GdipGetPathData. my codes are listed as follows:
Dim path,matrix As long Call GdipCreatePath(FillModeAlternate, path) Call GdipAddPathCurveI(path, points(1), PointsOfPath) Call GdipFlattenPath(path, matrix, 1.0) Call GdipDrawPath(graphics, pen, path) Dim pdata As PathData Call GdipGetPathData(path, pdata) Dim aaa As long aaa = pdata.count
when i get aaa,the result returns is 0 i dont know why,then,i write codes to test the whether the call is right,and the GpStatus returns 2(InvalidParameter) HELP ME!I am in confusion. My English is poor,sorry about it (If this comment was disrespectful, please report it.)
I too was baffled for a bit, until I saw that the C++ version of PathData (which is actually a class in C++) along with the C++ GraphicsPath.GetPathData() class function perform all sorts of things in the background, such as retrieve the count value, and allocate/deallocate arrays. The reason you are getting status/error code 2 is because GdipGetPathData() expects all three PathData members to be filled in beforehand. To obtain the point count, use GdipGetPointCount(), then allocate two arrays using the count value; one array should be of type POINTF and the other a simple byte array. After you set the PathData pointers to those new arrays, then you may call GdipGetPathData(). I added an example entitled RetrievePathData so you can quickly see what is needed. Hope this helps, and as always, everyone should feel free to ask questions! (If this comment was disrespectful, please report it.)
9/3/2002 3:30:41 PM:
Avery,thanks for your reply and the new function,but i had another question about the function
GdipCreateBitmapFromGraphics GdipCreateCachedBitmap GdipDrawCachedBitmap code as follows:
I wanted to "redraw" the line but it didn't work,and I Tested these functions' return values.They were all 0(Ok).Then I saw SDK,curiously,there was no method FromGraphics in the Bitmap Class,then I checked MSDN,in the item of Flat API,I found the function GdipCreateBitmapFromGraphics. Help me again!!!
(If this comment was disrespectful, please report it.)
Your code should not raise any errors, as you called all the APIs correctly. The problem probably is that the APIs don't do what one would think given their names! GdipCreateBitmapFromGraphics creates a new, blank, usually solid black bitmap in memory which can be used with GDI+. I believe it also assumes the attributes of the graphics object, such as palette, color depth, etc. (One would think that it would make GDI+ create new bitmap and copy the data visible in the graphics region to the new bitmap.) The cached bitmaps are, as you probably know, used for quick painting of a GDI+ image and it seems you know how to use those, so I won't go into it more unless you want me to. I don't really know what your goal is...is it to draw on the picture box and save the image to a GDI+ bitmap? If that is your goal, you will have to perform the copying of the data to the bitmap yourself. Some suggestions would be: ...(Continued Below)... (If this comment was disrespectful, please report it.)
...(Continued)... 1) Create a new bitmap based on the PictureBox's Image or Picture member handles via GdipCreateBitmapFromHBITMAP (I used it in some of the samples, such as DrawClipping). From what I can tell, this seems to be what you want. If you only want a btiamp that has a portion of what is inside the new bitmap (or any GDI+ bitmap object), you could clone the image via GdipCloneImage. This creates yet another bitmap ojbect of the specified size and image. 2) You could still use GdipCreateBitmapFromGraphics, then create a HBITMAP handle on it via GdipCreateHBITMAPFromBitmap, and use some standard GDI functions to copy the PictureBox image, such as BitBlt. Remember to delete the HBITMAP handle with DeleteObject.
Does this help any? (If this comment was disrespectful, please report it.)
9/3/2002 9:41:20 PM:
I'd better tell you what my goal is,i am doing a drawing application,the application requires the mouse to draw free-style curves,then I call the GdipDrawCurveI fucntion,in order to let the curve that will be drew transform with the position of the mouse,i draw the curve twice,for the first time i draw the curve with a black pen,then i draw the curve with a "background color" pen and it works well. then when i draw another new curve,with mouse moving,the new curve will delete the curve that has been drew. (If this comment was disrespectful, please report it.)
9/3/2002 9:42:04 PM:
so i want to store the curve that has been drew to a CachedBitmap and draw the CachedBitmap when a new curve being drew.I am new to graphic application,I don't know how to settle the problem,my friend that do game programme tell me i can store the curve that has been draw in a bitmap and load it when needed.For the first time i want to use the drawmode setting with an XOR pen to settle the problem but it seem that the drawmode setting doesn't work when use gdiplus in vb.This is my goal,i don't know whether i express it clear. (If this comment was disrespectful, please report it.)
Ah, I understand now! The CachedBitmap, to my knowledge, works on the assumption that the bitmap you created a cached bitmap from will never change, so you'd have to make many of these, so you might want to reconsider using cached bitmaps, but rather keep it stored as a standard bitmap or create a HBITMAP handle which can be manipulated more easily by just about everything! (Just remember to delete the handle with DeleteObject when you are done) And with a few more standard GDI API calls and a BitBlt it should paint about as fast. If you value simplicity over a 80ms delay, keep a standard GDI+ bitmap object and just use the DrawImage functions. As for XOR, GDI+ v1.0 does not support it, but rather only SourceOver or SourceCopy, which is bad. For more info on that topic, do a Google.com newsgroup search for "No XOR in GDI+" (no quotes). ...(Continued Below)... (If this comment was disrespectful, please report it.)
...(Continued)... Sidenote: You might want to keep an array of arrays for all objects you draw, so you can have an undo/delete feature and so you can totally redraw all shapes if need be.
Now I will try to explain the problem. For some reason (which one day I will hopefully know), VB AutoRedraw is conflicting with GDI+'s way of drawing. If AutoRedraw is True, it is hard to make a GDI+ curve stay around, but if it is False the GDI+ curve will stay! I think you say that you draw the line twice, since when you draw the curve with the mouse, nothing appears until you refresh - it is not drawn twice (once with the background color and then your pen color), but rather not drawn at all the first time! I believe you may have AutoRedraw set to True? I also added a *very bad* demo that allows you to draw curves with the mouse, which may also help you out. I'll need more time to figure out why, exactly, this is happening and find a solution. (If this comment was disrespectful, please report it.)
Wait a second! My DrawCurves function in the demo that is called on the form Paint is making the curves stay persistant since AutoRedraw is false, so GDI+ curves will not stay even if AutoRedraw is False, unlike what I said above. (I was in a hurry when I made that demo and posted the text, but now I have a bit more time to evaluate what is going on...) I also read that AutoRedraw can cause problems with GDI APIs, so it is not so much GDI+'s fault as I originally thought, but there are workarounds documented in the MS Knowledge Base. The workaround is to call a .Refresh, like you are, right after you draw via API, but make sure that the AutoRedraw is True for that approach! If you don't want AutoRedraw as True, you'll have to redraw the shapes yourself, kind of like the demo does, but there are many, many ways to optimize it. Hopefully eliminate the need for CachedBitmaps and/or HBITMAPs and will help you, and if not, then I am a dunce and still don't really know what is going on! (If this comment was disrespectful, please report it.)
9/4/2002 9:55:56 PM:
Avery,thank you very much!!!
But......can i send you my code to your email?Maybe,when you see my code,you will know more about my goal and my fault.
firstname.lastname@example.org send me your email if you would like. thank you again,you help me a lot! (If this comment was disrespectful, please report it.)
For those who care, I was still wrong in the above and thus a dunce - he was rubber-banding curves, which needless to say is very challenging with GDI+ as ROPs are not supported by GDI+ v1.0, but I think he found a workaround.
Below is some interesting info from Dana Seaman via email: To see Unicode in 2K/Xp you may need to go to Control Panel, Regional Configurations, Languages, and check the box for Far east support.
I was under the impression from numerous sources that you couldn't display Far East glyphs on 95/98/ME. I installed the Arial Unicode font in ME and sent some Unicode to GDI+ and it works perfectly.
Haven't tried this but I found Unicows.Dll (distribution unicows.exe) that can be used in other situations in 95/98/ME to allow Unicode to be displayed. The Dll contains Wide character API's to be used in lieu of those from GDI. The link is http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdkredist.htm
(If this comment was disrespectful, please report it.)
Added the PixelFormat constants and for what it worth, the two GDI+ memory management APIs, GdipAlloc and GdipFree, of which I don't know the importance. (If this comment was disrespectful, please report it.)
9/20/2002 4:44:05 AM:
You really have helped me out here. I needed to save as PNG and now I can from VB6!
Do you know if it is possible to change the linespacing of a font?
Thanks, Filip (If this comment was disrespectful, please report it.)
Uh oh... a question that I cannot answer. I tried looking for a bit for something on using GDI+ and line spacing and didn't turn up anything. I saw that some people even resorted to drawing a string character by character. I would like to say you could use the GdipGetFontHeight or GdipGetLineSpacing APIs, among some other GDI+ font measurement APIs, and simply determine where to place the next GdipDrawString call, but I have a feeling you might be using text wrapping so you'd rather have GDI+ perform all the calculations instead of implementing manual text wrapping. Maybe doing a search of google.com or something might give you some more helpful insight on line spacing. Sorry that I couldn't be of more help! (If this comment was disrespectful, please report it.)
9/24/2002 8:08:32 AM:
Thanks for the reply. As you had guessed I also searched and didn't find a solution.
So I've Created a wrapper function that draws line by line and still supports the LineTrimming property.
One other thing: my VB crashes a lot, I know I don't properly cleanup GDI objects yet. Is that the reason or do you also have these crashes.
(If this comment was disrespectful, please report it.)
The only times GDI+ crashed on me was when I didn't pass a Unicode string to APIs expecting a string and when I accidentally pass a bad parameter. So, basically every time GDI+ crashes on me, it is usually my fault. Also, you need to cleanup all the objects, no matter what! Besides creating a memory leak, all GDI+ objects must be destroyed before calling GdiplusShutdown. The documentation never says what will happen if you don't, but I'll bet it crashes, and if you aren't calling GdiplusShutdown for each GdiplusStartup call it will again probably crash. It might also crash if you call a GDI+ API without first calling GdiplusStartup. If the strings are passed to GDI+ in unicode, parameters are correct, and you are properly cleaning up the GDI+ objects, and you still get crashes then blame me, as the API declaration might be off (ByRef parameter instead of ByVal, or vice versa), though the odds of this are somewhat low but not impossible. (Continued Below)... (If this comment was disrespectful, please report it.)
...(Continued)... I was also told by Dana Seaman, however, that placing GdiplusStartup & GdiplusShutdown calls inside Initialize & Terminate events the for an ActiveX control might result in some crashing only within the VB IDE in WinXP, though one could simply place the Startup/Shutdown calls within functions that use GDI+ to eliminate this threat. I'm pretty sure, however, that the lack of cleanup is causing the crashes, though I acknowledge it is somewhat annoying to have to remember to insert cleanup code for each created object. Does this help any? (If this comment was disrespectful, please report it.)
9/25/2002 10:50:01 AM:
I've added the code to cleanup GDI+ objects and didn't experience any crashes anymore. (I still have the GdiplusStartup & GdiplusShutdown calls inside Initialize & Terminate events though, but thanks for the warning).
Note or hint: The right and bottom fields for RECTF are not coordinates, but width and height values. (at least for the functions where I used them, such as GdipDrawString).
Thanks again for your GDI+ code, you did a great and useful job for non-VB.NET coding! (If this comment was disrespectful, please report it.)
10/8/2002 8:30:18 PM:
I just ran into something that may help others using the GDI+ functions: it appears that when declaring GDI+ functions, the use of U/L case letters in the function names is important(unlike other W32 API's). I ran into this after I did a global edit on my project to change (If this comment was disrespectful, please report it.)
10/8/2002 8:36:35 PM:
(rest of submittal) ...to change "hdc" everywhere in the project to "hDC". This changed this string in your GDI+ declares, too. When I ran the program, I got a message saying "the dll entry point ...hDC... [gdip function] could not be found." So I went back to your original .bas module and pasted the name exactly as you had it into the declare statment, and the error disappeared. There's an MSDN article, Q188541, "INFO: Visual Basic Requirements for Using Exported DLLs" that seems to explain this (although I'm not sure what "exported dll" means). If I'm misinterpreting anything here, please let me know. Thanks
(If this comment was disrespectful, please report it.)
After reading the KB article it appears that you are correct - the function declarations or the original function name in the case of an aliased declaration are indeed case sensitive (I didn't know that, but I had a suspicion it was so). I think "exported dll", in the KB title, was a shortened form of "exported dll functions", which is another way of saying "functions exported from a dll" - any function that a dll makes visible to applications in order for those applications to use the dll. If you'd like to see the function names that a dll (or an exe) exported, use QuickView or Dependency Walker, but of course you must know the function arguments in order to properly call the functions that you see. (If this comment was disrespectful, please report it.)
I saw that you could alphablend a brush or pen when drawing and so I searched the MDN to see if there was a way to alphablend graphic/image/hdc to another graphic. But unfortunally I didn't found one =( So may question is if there is way to alphablend a graphic with GDI+ with out the GDI DIBbits function? (If this comment was disrespectful, please report it.)
Yes, indeed there is. One easier method is to use a color matrix (my knowledge is a bit sketchy on these GDI+ matrices), however I did find out enough to alpha-blend an image. You might not have looked at the weird seeming demo function named DrawColorMatrix, but it blends an image over a black line and the window background with 80% (0.8) opacity. This is done using a GDI+ ColorMatrix which allows us to set the alpha-blending for all pixels of an image at once, in conjunction with the ImageAttributes class. For the color matrix, the aplha value at [3,3] must be between 0 (transparent) and 1 (opaque). Luckily, the color matrix uses floats (VB Singles), so you can extend the decimal places a bit for more accurate blending percentages. The actual pixels of the original bitmap are unchanged using this method, as the alpha-blending is applied only at the time of drawing. (Continued below - I wish they'd make the limit 2000 chars)... (If this comment was disrespectful, please report it.)
...(Continued)... This is the link to the C++ SDK sample on which DrawColorMatrix was based which hopefully might provide you with a better understanding of what is happening: http://msdn.microsoft.com/library/en-us/gdicpp/gdi+/usinggdi+/alphablendinglinesandfills /usingacolormatrixtosetalphavaluesinimages.asp?frame=true
Some other alternative methods would be to call GdipBitmapSetPixel or GdipBitmapLockBits to set the alpha of individual pixels in an image, however this will change the pixels in the image and is slower than the ColorMatrix method.
Now, to blend images in memory, you need to create a graphics object off of one of the images (or create another destination image to hold the result) using GdipGetImageGraphicsContext. There are some image pixel formats which cannot have a graphics object created on them, however you can use GdipCreateBitmapFromScan0 to create a high-res bitmap in memory to get a graphics object for drawing, if need be. Does this help any? (If this comment was disrespectful, please report it.)
Hey Thanks! I think this might help =) (If this comment was disrespectful, please report it.)
10/24/2002 5:31:58 AM:
Hi Avery, I decided to post this on the board too so that if anyone else is experiencing the same problem a public solution (if there is one) might be of better service to everyone. I have a handle to the device context of an image (external to my app) which I am bitblt-ing to a picture box and then converting to a bitmap using the GdipCreateBitmapfromHbitmap function. All is fine and this works beautifully. My problem is this...in the process of blt-ing the image to a picture box I'm having to use the picture1.picture=picture1.image method to make the image available to the gid+ method. After executing this 500 times in a loop I'm experiencing a MAJOR slowdown in loop execution times. Inititally the first save of image takes 47 milliseconds...at the end of the loop that time grows to a totally unacceptable 250-300 milliseconds. I believe the problem is in that picture1.picture=picture1.image statement I use...somehow its taking longer and longer to perform.
(If this comment was disrespectful, please report it.)
10/24/2002 5:33:01 AM:
(contd) I was just wondering if there's another way u could suggest of achieveing this solely using gdi+ functions so I could bypass this statement? I appreciate your time and help, and am in your debt for creating this excellent software in the first place. Thanks, Brommers (If this comment was disrespectful, please report it.)
10/24/2002 2:28:09 PM:
Oh my...do I feel like a fool :O Erm...it was bad programming causing the slow down and its taken me a week to find that out. *sigh* Anyway...sorry to bother you Avery and again thankyou so much for your help in making these functions available. Brommers
(If this comment was disrespectful, please report it.)
Hmm... You solved the problem? That was relatively fast, if the case. How did you fix it? Just curious in case I ever make a similar mistake, which is entirely possible! =/ (If this comment was disrespectful, please report it.)
10/26/2002 5:47:41 AM:
Hi Avery, the problem I experienced was actually less a programming fault and more a symptom of windows design itself. When writing a large number of files to a directory I discovered the mentioned time slip and increase in loop execution times...after writing several thousand images to disc the increase was HUGE. The problem was solved in this manner...windows writes to disc quicker than it can update its own directory structure. And if that directory structure is continuously being updated and growing in size, windows actually slows itself down to compensate for the 'lag' in relation to write speed and update speed. Solution was to create several directories as subdirectories, and calculate a manageable amount of images to place into each. I place 500 images in each directory now, when 501 is reached I increment and move on to the next directory. The time lag has completely disappeared. Again, thanks for this :) Brommers (If this comment was disrespectful, please report it.)
It would seem that Dana has found his answer, so I added a quick demo based on his efforts entitled SaveGrayscale.
As for the speed between drawing horizontal and vertical lines... I am speechless, so I opted to post your question in the microsoft.public.win32.programmer.gdi newsgroup, entitled "GDI+ Horizontal and Vertical DrawLine issue". I hope it was a good representation of your question, and I'll try to post to answer here as well. (If this comment was disrespectful, please report it.)
If you paste Unicode into a string property of a Vb6 control then it is displayed as '?????' in the property page. To fix this use a custom property page using a textbox control from FM20.DLL which is part of MS Office. If you don' have Office then you can get FM20.DLL at http://download.microsoft.com/download/activexcontrolpad/Install/22.214.171.1240/WIN98MeXP /EN-US/setuppad.exe (If this comment was disrespectful, please report it.)
Dana updated the paths to include App.Path so as to help reduce the problem mentioned previously about getting file not found error messages along with adding a function entitled DEFINE_GUID to return the CLSID/GUI structure from a CLSiD/GUI string constant. This allows for easier maintainability and use.
Don't know what to say about that DrawLine problem except it occurs in the GDI+ C++ classes as well. I wonder if it occurs in the .Net framework... (If this comment was disrespectful, please report it.)
hi, a bug i noticed -- most of the GUIDs defined end up with ")" (bracket) instead of "}" (curly bracket). this is actually not affecting the samples because you ignore the HRESULT from CLSIDFromString :-)))
Just my $.02 </wqw> (If this comment was disrespectful, please report it.)
The matrix operations are then available. (If this comment was disrespectful, please report it.)
1/16/2003 7:24:31 PM:
Hi Avery, I'm probably being silly and overlooking something simple...but how do I set the 'paper' (background) colour when I draw out text using the GDI+? Thanks, Brommers (If this comment was disrespectful, please report it.)
Sorry for the wait; haven't used the Internet/computer much this week. I'm thinking that you'll have to just get the text's display rectangle (using GdipMeasureString) and then draw the background yourself. I added a sample entitled DrawTextBackColor that does just that... I hope it is what you are after. (If this comment was disrespectful, please report it.)
1/19/2003 1:41:28 PM:
Thanks ever so much Avery once again, I suspected this might be the way to proceed I just wasn't sure if there would be a text.backcolor property that wouldnt require the rectangle. The example is just great though :) Brommers (If this comment was disrespectful, please report it.)
1/31/2003 2:26:54 AM:
Hi again, you must think me a real pain lol. Sorry. The BMPtoJPEG and BMPtoJPEGParams examples...they seem to be saving an image at the lowest possible compression rate. Both save images that look totally blocky. Is it just my computer? I can use the SaveScaling and save a jpeg through that method just fine...I can't see whats wrong with those two demos even though the results arent as imagined no matter what value I pass for .value Have I missed something? Thanks Avery, Brommers
(If this comment was disrespectful, please report it.)
Two words: uh oh! Seems that it doesn't like the hard-coded constants in the VarPtr, such as VarPtr(90), while this is perfectly fine: Dim lngQuality As Long lngQuality = 90 .value = VarPtr(lngQuality)
I wonder how that happened...it must have been working at some point like that for me. Sorry about that! I fixed the demos, however the BMPtoJPEG_Params seems to ingore the quality amount there. I have a feeling that is due to the rotation parameter that is also combined, which rotates the jpeg without losing the current JPEG quality; probably overrides the quality parameter. I don't mind the questions as I learn more along with you! (If this comment was disrespectful, please report it.)
2/3/2003 1:34:26 AM:
Hehe...thanks Avery :) (If this comment was disrespectful, please report it.)
2/7/2003 6:56:17 PM:
Most GDI+ image manipulations use DrawImage to draw the manipulated image to a graphics object. How does one save the manipulated image (not the original) or otherwise access it? GdipSaveImageToFile seems to be saving the original image. (If this comment was disrespectful, please report it.)
2/7/2003 6:59:41 PM:
How does one save the altered image (after a drawimage operation with or without imageattributes). GdipSaveImageToFile seems to be saving the original image. (If this comment was disrespectful, please report it.)
From what was the graphics object created? Did you have another GDI+ image object and pass the wrong object to the save function?
If the graphics object was created from a window DC, the only way to get what you drew to the window is via some standard GDI calls, such as BitBlt or the like (see Q311221 for some GDI/GDI+ interoperability issues), but you would lose some information GDI+ might store, such as alpha values. One alternative would be to create a new GDI+ image in memory (preferrably 32bpp, as GDI+ can't create graphics objects for all formats), create a graphics object from the image, and draw to that image, and then to the screen if needed. ...(Continued Below)... (If this comment was disrespectful, please report it.)
...(Continued)... You may then wonder if there was a way to eliminate the intermediate image, and there is at least one possible way I know of, but it may not always work or produce desired results: you could try to create a graphics object from the original image, and if the graphics object was created, draw on the original, and then the screen, if needed. The drawbacks are that the image may not be of an acceptable format to have a graphics object, alpha may be ignored, or you need to have the original image intact. I'm partial to the new bitmap method myself, even if it uses more memory. Does this help any? (If this comment was disrespectful, please report it.)
2/8/2003 6:51:24 PM:
Thanks, exactly the tip I needed. Just one more thing: I can't seem to find the declare for creating a graphics object from image. I can see create from HDC, HWND and HWNDICM, but no FromImage. (If this comment was disrespectful, please report it.)
Use GdipGetImageGraphicsContext (not exactly a function name you'd expect, eh?). The SaveScaling and SaveGrayscale show that API in action, both to get a graphics object to draw on in order to save, and to get a graphics object in order to create a new bitmap of the same type as the original (remember this may not always work, but I know the demo image is loaded in a compatible pixel format so I get away with it). There are other ways to create a new image object with the same properties of the original as well, such as retrieving the original image's pixel format and calling GdipCreateBitmapFromScan0, to name one. (If this comment was disrespectful, please report it.)
2/9/2003 4:58:55 AM:
Thanks a ton. Are there any more guys like you out there? (If this comment was disrespectful, please report it.)
2/16/2003 7:30:31 PM:
Please comment: LoadImageFromFile just allocates memory. The file is not read from disk until there is a call to draw to screen. Even DrawImage to a memory graphics object seems to do nothing. Is Gdi+ queueing instructions until a visible output (or Save) is requested? (If this comment was disrespectful, please report it.)
I was under the impression that GDI+ likes to wait until it can wait no longer to read image files. Why this happens, I don't know. Armed with FileMon (available free at www.sysinternals.com), I performed a quick test to see if in-memory drawing actually prompted GDI+ to read the image file. My result: GDI+ did read the image file when drawing into a memory image graphics object. You could try using FileMon to view the file activity with your tests and see what happens. Someone from Microsoft who knows more about the GDI+ internals may be able to provide more info over at the microsoft.public.win32.programmer.gdi newsgroup. Hope this helps a bit... (If this comment was disrespectful, please report it.)
2/17/2003 7:06:08 PM:
You're right about the memory context; my mistake. Is there a workaround? I like to do background processing so that when the user actually clicks the command, the response is instant. GdipFlush doesn't seem to work, but maybe I'm not doing it right. (If this comment was disrespectful, please report it.)
So you are trying to force GDI+ to read the image file? Is this what you want a workaround for, but with minimal overhead? If so, I'd think that when you try to access any information not available from the image header, such as pixel data, GDI+ would have to read the image file. I quickly tested that theory by calling GdipBitmapGetPixel and seeing what happened, and the result was that GDI+ had to read the image file. GdipFlush, however, is used to flush only graphics object related actions, so if you were using it to force the image file to be read it will probably not work, as you indicated. Am I on target here? (If this comment was disrespectful, please report it.)
2/18/2003 4:56:39 PM:
Great! This should definitely work. Now about thumbnails: The documentation mentions that "Some image files have a thumbnail image embedded in the file. In such cases, this method retrieves the embedded thumbnail image." How does one create an image which has a thumbnail embedded in it? (in other words, how to create a thumbnail and save the original image along with the thumbnail so gdi+ reads only the thumbnail and not the entire bitmap?)
(If this comment was disrespectful, please report it.)
Good question! I'm thinking you may be able to use GdipSetPropertyItem along with the PropertyTagThumbnail* tags (there are quite a number of them) to create the thumbnail, but I can't test that theory at the moment. I could try to make a demo or prove my theory wrong by Friday [2-21] if you have some trouble implementing it or feel like waiting a bit to see what I come up with. However, GDI+ may still read the entire image file even though a thumbnail is present within the image file - you'd have to do some testing to find out for sure. (If this comment was disrespectful, please report it.)
I couldn't find any helpful info or figure it out myself...well, I did get GDI+ to save whatever was passed with the PropertyTagThumbnailData property, but it appears that all the other properties were not saved, so I unfortunately can't tell you how to do it. I also don't have any images that have thumbnails embedded in them, or any applications to create them, so I couldn't do much testing. You could try posting that question in the microsoft.public.win32.programmer.gdi newsgroup, or maybe someone reading this knows the answer? (If this comment was disrespectful, please report it.)
2/22/2003 8:23:34 PM:
Ok. Thanks for trying. Next question: Bitmaps saved to file using GDI+ (as per your demo), are not showing properties (checked on WinMe, right click and select properties and the summary tab). Grapebunch.bmp (Windows3x bitmap) has them, grapebunchsmall.bmp (Windows or OS/2 Bitmap) doesn't. Please comment. Also, any way to specify saving images in a particular format (RGB24bpp)? I'm having to load a bitmap of the format I want, get its graphics context, and use that for drawing and subsequent saving. (If this comment was disrespectful, please report it.)
I only have Win98 and Win2k Pro, so I can't really say too much about the image properties in WinME. Win2k says only one thing for both images in the summary tab (Image Property -> File Type: Windows or OS/2 Bitmap). Maybe I'm missing something? Since saving in a format requires the bitmap/image object to be in the desired format (or so I believe), yes, there is a better way; I'm partial to using GdipCreateBitmapFromScan0 for such tasks. Here is a sample call to create a new bitmap of any desired format, height, and width:
Remember to place the ByVal 0 for the scan0 as that value expects a pointer, which we aren't using here. For paletted images created this way you'll need to set the palette manually via GdipSetImagePalette or you'll get the halftone palette. You might also consider trying GdipCloneBitmapArea* as an alternative. Does this help any? (If this comment was disrespectful, please report it.)
3/24/2003 3:40:07 PM:
Avery, I would like to use gdi to save into different image formats from a VB stdpicture in memory instead of going thru the process of loading off the file system, can you share on this possiblilty? Thanks for the great demo here. (If this comment was disrespectful, please report it.)
It is basically the same as saving an image loaded from a file into another image format. However, instead of loading the image into GDI+ by using GdipLoadImageFromFile, you can use GdipCreateBitmapFromHBITMAP to load the StdPicture into a GDI+ image object. Here is a sample usage:
Since StdPictures are stored as bitmaps in memory, their Handle property is a bitmap handle (HBITMAP). The above function is the easy way (I think), but it is surely not the only method you could use! There are some other GdipCreateBitmapFrom* APIs to load a bitmap into GDI+ using other methods, if you desire. Hope this helps! (If this comment was disrespectful, please report it.)
3/26/2003 12:12:58 PM:
Avery, what do you suggest to be used as a viewing port for diplaying many image thumbnails? I am currently using a control and displaying images rigth on that control. I only display the images that can be viewed one the screen. I feel this process is slow. One of the problems is also the fact that when you scroll, the entire area needs to be repainted.
I have seen programs where they use a viewport which appears to have painted all of the images on the screen. This appears to be a problem in a form as it has a limit of the size that you can use. The same applies to a picturebox.
Any information on this would be great! Also, I am planning to take a look at this GDIPlus for the cachedbitmap functions.
Thanks Cisco (If this comment was disrespectful, please report it.)
Many programs seem to use either an owner-drawn listview control in like large icon view or what someone called a "filmstrip" style control (one row or column of images with scroll bar; like an owner-drawn listbox control). You could clone one of those controls, or maybe look at some thumbnail viewers on PSC for some other ideas? You could disallow smooth scrolling and only scoll in increments (eg: clicking the scrollbar down moves everything one entire image instead of a certain number of pixels) to possibly improve performance. CachedBitmaps should speed up the drawing of the images, but watch out if AutoRedraw is True when using them; for me, the speed reduced greatly.
You can get around the design-time size restrictions by typing in values for the control's height and width property values or alter the size at run-time when the form loads. I haven't really made a (decent) image viewer before, so I'm not too sure on what is the best approach. Hopefully this will help some? (If this comment was disrespectful, please report it.)
What a code of both quality and substnace! BTW, I happen to be the same: (i) don't like typelib, compile or use; and (ii) generally prefer the use of bas to the hybrid vb cls. (If this comment was disrespectful, please report it.)
6/12/2003 11:08:51 AM:
Hi Avery and all, I was just wondering if it is possible at all using this gid+ translation to make alpha text...ie text with a transparency value? Thanks, Brommers (If this comment was disrespectful, please report it.)
6/14/2003 9:33:43 PM:
Hi Avery, found the info I needed (and an excellent example) in Dana Seaman's TLB logo submission at http://www.Planet-Source-Code.com/vb/scripts/ShowCode.asp?txtCodeId=42861&lngWId=1 Thanks to both of you :) (If this comment was disrespectful, please report it.)
7/18/2003 5:32:03 AM:
Thanks for these great examples.
As a beginner on this subject there I have some questions about using palettes
I make a very simple drawing on a picturebox. The colors used are limited and known so I can make a palette. I use the BMPtoPNG example to save the file.
This file does not have a palette attached to it. If I save it as a gif it gets some default palette.
Q1: How to save the image with my own palette Q2: How to map the pixels to the palette
(If this comment was disrespectful, please report it.)
7/23/2003 11:05:13 PM:
Hi, Avery - I've been successfully using your work posted here for some time. Everything seems to work fine on XP machines. However, I've run into a problem on Win 98 - things seem to work OK (images saved to files and clipboard), but when my code gets to the point where it calls GDIPlusShutDown (VGPStatus = GdiplusShutdown(GdipToken), and then I check for VGPStatus <> Ok), I notice that VGPStatus value = -2120633560 (negative). VGPStatus is declared as Long. I'm also using this in conjunction with regular GDI calls. I've looked through the code for causes but can't find any. Any ideas what might be causing this? Thanks very much. (If this comment was disrespectful, please report it.)
Rene, sorry for the delay. A PNG file, to my knowledge, does save in a format that allows palettes. To save an image with a custom palette in a format that allows it, such as a GIF or bitmap, you need to create a new paletted (8bpp) image using GdipCreateBitmapFromScan0, then fill in a ColorPalette object with the appropriate colors, call GdipSetImagePalette, and then you could now save the image, assuming you somehow copied the original image data to the new image object (else you'd get a blank, paletted image). Carles P.V. created a GIF program that demonstrates how to do this:
You could also check out Q315780 for more information on custom palettes with GDI+ (Note that the sample code in this article is C++):
http://support.microsoft.com/?id=315780 (If this comment was disrespectful, please report it.)
9/14/2003 1:05:45 PM:
Hi Avery, I'm having a look at the GdipCreateBitmapFromGdiDib function but am having problems with it. I have created a 24 bit DIB which works fine; can save it display it etc. Now I'm trying to create a GDI+ image from that DIB...my problem lies in the gdiBitmapInfo As BITMAPINFO part of the declare...I've correctly transferred the BITMAPINFOHEADER part of BITMAPINFO into a type, but don't know what the RGBQUAD part of it is or where I get it from. I know its 3 RGB bytes, tried setting them to different values 0-255 but my image always comes out...purple-ish...I can see and draw the image using GDI+ but the colours are all messed up. Any ideas on what I use/set RGBQUAD type to? Thanks, Brommers (If this comment was disrespectful, please report it.)
I tried to do a quick reproduction of the problem by loading a 24bit bitmap straight from a file, however, it worked fine for me! However, I tried to figure out what could go wrong and determined that the pointer could be off for the pixel data (gdiBitmapData function argument). When I altered the pointer to a bad location (ex: ptr - 1), the colors were off. Here is the successful function call that I made (rgba is a RGBQUAD array, bi is the BITMAPINFO, img is the Long for the image object):
The bmiColors member of the BITMAPINFO structure holds the colors of paletted images. Since this is more than 8bits, it has no meaning to us or GDI+ in this case and is not used. If the image was paletted, you'd probably have to alter my declaration of it to make an array of bmiColors, or something.
Hope this helped some! (If this comment was disrespectful, please report it.)
9/16/2003 7:58:49 AM:
Hi Avery, well I'm not sure how or why it works now but it does...I'm still learning about all this 'pointer' business. I initially had the line Call GdipCreateBitmapFromGdiDib(bi, ByVal pDIB(onsource) , img) ... where bi is the BITMAPINFO and pDIB(onsource) is a long pointer to the actual dib...the results of that call gave the colour offset. Having a look at your reply to my question, I just decided to try Call GdipCreateBitmapFromGdiDib(bi, ByVal pDIB(onsource) + 1, img) and it worked first time! Colours perfect now. However...I'm concerned that I am still doing something wrong, because in your example the 2nd part of the call is an RGQUAD array whereas mine is the pointer to the DIB...and yet it works fine...am I misunderstanding the gdiBitmapData part of the call? Might I say that throughout the life of your posting this GDI+ API on PSC, your assistance and behaviour have been nothing short of brilliant. Thanks, Brommers (If this comment was disrespectful, please report it.)
I had a RGBQUAD array because I wasn't using DIBs, so I didn't have DIB pointers. It would seem that I overlooked DIBs in my haste to make a test function. Anyway, these "off by one" errors are fairly common and can be difficult to track down, so I'm not sure why it is happening, but I'll now give some possible causes:
How are the DIB bits allocated? If you do them, how did you get the pointer? If the pointer is from a VB array, did you get the pointer of the first item in the array? Ex: VarPtr(arData(0)) or similar.
Does the declaration for gdiBitmapData say ByVal? If it does, why do "ByVal pDIB(onsource)" when calling the function?
I don't think pDIB being an array has any significance; at least if it is only an array to hold Long pointers for DIBs and not pixels.
If you had time, or felt like wasting some, you could try using GdipCreateBitmapFromScan0 with the DIB and see if that required the +1 offset. If it works fine without an offset, I'd say problem = GDI+ (If this comment was disrespectful, please report it.)
9/17/2003 7:47:14 PM:
Hi Avery, thanks for offering those suggestions..the pdib() array is just an array of pointers created as the DIB itself is made. Sorry if I was unclear in my questions...as it stands the function works just fine and I guess like you say its just a GDI/GDI+ anomaly because the +1 for the pointer isnt needed at all using standard VB functions. You'll have to forgive me, I'm not too correct with the 'lingo'. You've cleared up any questions I had and again thanks...your idea of the pointer being out causing the colour discrepancies was a real help. Thanks, Brommers (If this comment was disrespectful, please report it.)
WOW! I can't even understand the description, so it MUST be good! 5x (If this comment was disrespectful, please report it.)
4/10/2004 4:53:26 PM:
Very glad to see this getting code of the day Avery...in my opinion if people understand its true potential it would have and will have get code of the year. Guys, this isn't just Win98 either. I am successfully using this port of the GDI+ API's in an XP project direct from VB6...and I have to say the functionality its provided is amazing. (If this comment was disrespectful, please report it.)
4/14/2004 7:00:47 PM:
Hi. Im try save tif with 1 bit and CCITT3 compression, but oly create a file with 1 k , save only 24 bits with LZW compression. Do you can help me ? (If this comment was disrespectful, please report it.)
Excellent Code....Good work. Could you please tell me how i can use the same GDI for continuous data instead of using fix array.
Regards Dhaval Shah. (If this comment was disrespectful, please report it.)
12/10/2004 5:34:59 PM:
This is a treasure!
(You can respond directly to: email@example.com)
Anybody know how to save the image that DrawAntiAliasedText routine creates to a bitmap in a DC tha I can BitBlt from using plain old GDI BitBlt?
: ) (If this comment was disrespectful, please report it.)
12/13/2004 12:45:37 AM:
Me again. There are two things in particular I am after...
1: Being able to draw the antialiased text in a way initially invisible to the screen.
2: Being able to copy the nice pretty image of antialiased text from the graphics class to a bitmap in a DC that I can manipulate and process using old fashioned GDI functions and ultimately Bitblt to an output screen. I don't know if the first point is successful because I can't get the second to work I think only the Jedi master can answer this one. I tried to write Avery but I don't know if he answered during an email brownout or if he did not receive it. So I post the question here.
Robison (If this comment was disrespectful, please report it.)
12/22/2004 2:20:34 AM:
So here is a change that made your code work in VB5
in PlayGif Dim ... arDelay As Variant 'instead of long
'then this is for VB5 that 'maybe you didn't know about
arDelay = Array() (If this comment was disrespectful, please report it.)
Good Job, Avery. Its such an amazing thing that you've done. I just saw it after some long time while searching for Unicode retrieval procedures. I should write you some emails i regard to it. Just good job done. Five from me. (If this comment was disrespectful, please report it.)
Hello Just one precision, in the DrawGrid() sub, you said that vertical lines are drawn more slowly than horizontal one ==> the reason is that horizontal pixels are stored one after the other in memory, and not for vertical ! Great job (If this comment was disrespectful, please report it.)
Hi Avery, Being a bit of a vb newbie can you show me how to load a jpg resize it to say 1024x764 and then save it back to jpg. I don't need to see it I just want to reduce its size for e-mailing. Regards, Chris (If this comment was disrespectful, please report it.)
Add Your Feedback
Your feedback will be posted below and an email sent to
the author. Please remember that the author was kind enough to
share this with you, so any criticisms must be stated politely, or they
will be deleted. (For feedback not related to this particular code, please
click here instead.)