-
Feb 21st, 2012, 05:19 AM
#1
Thread Starter
Fanatic Member
Problem with Image handle and GDIPlus
I have a commandbutton that reads a bitmap file and shows its picture enlarged 4 times in a Picturebox named Picture2. (There is no Picture1 yet. There is only a PictureBox named Picture2 for now)
The enlargement is done using GDIPlus techniques.
Here is a the code, and it works perfectly:
Code:
Private Sub cmdShowPic3_Click()
Dim DoScale As Boolean
Dim Render As Boolean
Dim hGraphics As Long
Dim hMatrix As Long
Dim tHandle As Long
Dim destX As Long
Dim destY As Long
Dim destWidth As Long
Dim destHeight As Long
Dim srcX As Long
Dim srcY As Long
Dim srcWidth As Long
Dim srcHeight As Long
Dim srcRect As RECTF
Dim GDIplusEffectsHandle As Long
Dim file_name As String
Dim scalerX As Single
Dim scalerY As Single
Dim ZoomFactor As Long
Const UnitPixel As Long = 2&
DoScale = True
Picture2.AutoRedraw = True
GDIplusEffectsHandle = 0&
ZoomFactor = 4#
file_name = "C:\MyFiles\MyPics\temp1\pic001.bmp"
scalerX = ZoomFactor
scalerY = ZoomFactor
If Not DoScale Then
ZoomFactor = 1
End If
Picture2.Height = Picture2.Height * ZoomFactor
Picture2.Width = Picture2.Width * ZoomFactor
If m_Image Then GdipDisposeImage m_Image: m_Image = 0
If GdipLoadImageFromFile(StrPtr(file_name), m_Image) Then
' error; failed to load image
Exit Sub
End If
If GdipCreateFromHDC(Picture2.hdc, hGraphics) Then ' must release DC with GdipDeleteGraphics
' error; should never get this if passing a valid DC
Exit Sub
End If
Call GdipSetInterpolationMode(hGraphics, RenderInterpolation.lvicHighQualityBicubic)
Call GdipSetPixelOffsetMode(hGraphics, SmoothingModeAntiAlias)
destX = 0
destY = 0
destWidth = 150 ' * ZoomFactor
destHeight = 150 ' * ZoomFactor
srcX = 0
srcY = 0
srcWidth = 150 ' * ZoomFactor
srcHeight = 150 ' * ZoomFactor
srcRect.nTop = srcY ' * ZoomFactor
srcRect.nLeft = srcX ' * ZoomFactor
srcRect.nHeight = srcHeight ' * ZoomFactor
srcRect.nWidth = srcWidth ' * ZoomFactor
If DoScale Then
Call GdipCreateMatrix2(scalerX, 0&, 0&, scalerY, destX, destY, hMatrix)
Render = (GdipDrawImageFX(hGraphics, m_Image, srcRect, hMatrix, GDIplusEffectsHandle, 0, UnitPixel) = 0&)
Else
GdipDrawImageRectRectI hGraphics, m_Image, destX, destY, destWidth, destHeight, srcX, srcY, srcWidth, srcHeight, UnitPixel, 0, 0, 0
End If
GdipDeleteGraphics hGraphics
If hMatrix Then GdipDeleteMatrix hMatrix
Picture2.Refresh
End Sub
But then I copied the above code and pasted it under another commandbutton, and tried to change it.
The change that I need is that instead of getting the picture from a file (as in the above code), I need to get the picture from another PictureBox (named Picture1) that has been already showing a picture on the screen.
In other words, a picture is already showing in a PictureBox named Picture1, and I need to show an enlarged copy of that (enlarged by a factor of 4) in another PictureBox named Picture2.
Here is the new code (under a separate commandbutton):
Code:
Private Sub cmdShowPic5_Click()
Dim DoScale As Boolean
Dim Render As Boolean
Dim hGraphics As Long
Dim hMatrix As Long
Dim tHandle As Long
Dim destX As Long
Dim destY As Long
Dim destWidth As Long
Dim destHeight As Long
Dim srcX As Long
Dim srcY As Long
Dim srcWidth As Long
Dim srcHeight As Long
Dim srcRect As RECTF
Dim GDIplusEffectsHandle As Long
Dim file_name As String
Dim scalerX As Single
Dim scalerY As Single
Dim ZoomFactor As Long
Const UnitPixel As Long = 2&
DoScale = True
Picture2.AutoRedraw = True
Picture2.Picture = Picture1.Picture
Picture2.Height = Picture1.Height * ZoomFactor
Picture2.Width = Picture1.Width * ZoomFactor
GDIplusEffectsHandle = 0&
ZoomFactor = 4#
scalerX = ZoomFactor
scalerY = ZoomFactor
If Not DoScale Then
ZoomFactor = 1
End If
If m_Image Then GdipDisposeImage m_Image
' m_Image = Picture2.Image.hPal
m_Image = Picture2.Image.Handle
' If GdipLoadImageFromFile(StrPtr(file_name), m_Image) Then
' ' error; failed to load image
' Exit Sub
' End If
If GdipCreateFromHDC(Picture2.hdc, hGraphics) Then ' must release DC with GdipDeleteGraphics
' error; should never get this if passing a valid DC
Exit Sub
End If
Call GdipSetInterpolationMode(hGraphics, RenderInterpolation.lvicHighQualityBicubic)
Call GdipSetPixelOffsetMode(hGraphics, SmoothingModeAntiAlias)
destX = 0
destY = 0
destWidth = 150 ' * ZoomFactor
destHeight = 150 ' * ZoomFactor
srcX = 0
srcY = 0
srcWidth = 150 ' * ZoomFactor
srcHeight = 150 ' * ZoomFactor
srcRect.nTop = srcY ' * ZoomFactor
srcRect.nLeft = srcX ' * ZoomFactor
srcRect.nHeight = srcHeight ' * ZoomFactor
srcRect.nWidth = srcWidth ' * ZoomFactor
If DoScale Then
Call GdipCreateMatrix2(scalerX, 0&, 0&, scalerY, destX, destY, hMatrix)
Render = (GdipDrawImageFX(hGraphics, m_Image, srcRect, hMatrix, GDIplusEffectsHandle, 0, UnitPixel) = 0&)
Else
GdipDrawImageRectRectI hGraphics, m_Image, destX, destY, destWidth, destHeight, srcX, srcY, srcWidth, srcHeight, UnitPixel, 0, 0, 0
End If
GdipDeleteGraphics hGraphics
If hMatrix Then GdipDeleteMatrix hMatrix
Picture2.Refresh
End Sub
But this does NOT work, and I don't understand why.
If you note, the main change between the above pieces of code is that in the first one there is a section like this:
Code:
If m_Image Then GdipDisposeImage m_Image: m_Image = 0
If GdipLoadImageFromFile(StrPtr(file_name), m_Image) Then
' error; failed to load image
Exit Sub
End If
And in the second piece of code, I have replaced it with this:
Code:
If m_Image Then GdipDisposeImage m_Image
' m_Image = Picture2.Image.hPal
m_Image = Picture2.Image.Handle
' If GdipLoadImageFromFile(StrPtr(file_name), m_Image) Then
' ' error; failed to load image
' Exit Sub
' End If
because the image handle cannot come from the file, as there is no file in here and the original picture is already showing in Picture1 and copied into Picture2 by this line of code:
Code:
Picture2.Picture = Picture1.Picture
So, I assumed that I could easily get the image handle like this:
Code:
m_Image = Picture2.Image.hPal
or like this:
Code:
m_Image = Picture2.Image.Handle
And nothing works.
What is it that I am missing?
How can I fix this?
thanks,
Ilia
-
Feb 21st, 2012, 08:52 AM
#2
Re: Problem with Image handle and GDIPlus
I have tried to explain to you in previous threads. The GDI+ image handle is not the same as a standard image handle. It must be created via GDI+ only.
If you want to create a NEW GDI+ image from a picturebox .Image or .Picture property, use:
GdipCreateBitmapFromHBITMAP Picture2.Picture.Handle, m_NewImage
I see you are using some code from my Alpha Image Control project. You can find the GdipCreateBitmapFromHBITMAP declaration there too
Remember to destroy, at some point, anything you create with GDI+.
Edited:
1) GdipDrawImageFX function only exists with v1.1 of GDI+ (typically not on XP). Any calls to it without correct version installed on user's PC will error.
2) FYI: .hPal is a palette handle
Last edited by LaVolpe; Feb 21st, 2012 at 01:29 PM.
-
Feb 22nd, 2012, 05:17 AM
#3
Thread Starter
Fanatic Member
Re: Problem with Image handle and GDIPlus
Hi LaVolpe,
Let me first thank you a lot.
I implemented your advice, and it worked perfectly.
Just a couple of questions if you don't mind:
1. My PC runs Windows 7 (64 bit) and this code worked on it perfectly. I understand from your post that it should not work on a PC running XP or an earlier version of Windows. But, I guess I should be safe to assume that in future versions of Windows it should work. Do you have any other oppinion?
In other words, had you been in my situation, would you have used this code as is, or would you have somehow changed it for BETTER future compatibility?
Is there anything in my code that you find MESSY or un-recommendable that you would have replaced with something with a better possibility of future compatibility?
2. I have added code to destroy the image handle as you said.
But, I am wondering, what the risk is.
What if I don't destroy that handle?
What is the bad thing that can happen in that case?
Bottom line is that when the vbp application (or the exe file) ends, all the resources will return to the operating system, so there should'nt be a major mess.
So the risk of not destrying the image handle should be minimal. Shouldnt it?
Please advise.
thanks,
Ilia
-
Feb 22nd, 2012, 08:37 AM
#4
Re: Problem with Image handle and GDIPlus
That one function (GdipDrawImageFX) will almost certainly create an error regardless of the operating system used. This is because an application manifest is needed to load v1.1 of GDI+. Without the manifest, v1.0 will always be loaded & that function doesn't exist in v1.0. I'd recommend either 1) simply don't code for GdipDrawImageFX or 2) test the version of GDI+ v1.1 before attempting to call that function. v1.1 functions should be considered optional at best
Destroy GDI+ objects you create. If you don't you can end up crashing your application. If you continue to create without destroying and your app doesn't crash, you are wasting resources by having those things hang around. Waste enough resources and you risk "out of memory" errors
-
Feb 23rd, 2012, 04:42 AM
#5
Thread Starter
Fanatic Member
Re: Problem with Image handle and GDIPlus
Hi LaVolpe,
In your first post (post #2) you said:
GdipDrawImageFX function only exists with v1.1 of GDI+ (typically not on XP). Any calls to it without correct version installed on user's PC will error
And in your latest post you said:
test the version of GDI+ v1.1 before attempting to call that function
Now, the thing is, I used this code including that GdipDrawImageFX and it worked perfectly.
1. Am I correct in assuming that based on this test, it is certain that I have the proper version of GDIPlus installed on my PC?
2. If my application is to run on any other PCs, how do I write a code to ensure that the right version of GDI+ is installed (so that if the right version is not there, I call a separate procedure to simply use the VB6's picture enlargement feature instead of my new GDI+ procedure)?
3. If this GdipDrawImageFX does not exist in previous versions of Windows, I guess it surely will exist in future versions. My PC, running Windows 7 (64 bit) can call it and use it, so I guess future versions of Windows are not going to take this function away. Or are they?
Please advise,
thanks,
-
Feb 23rd, 2012, 08:48 AM
#6
Re: Problem with Image handle and GDIPlus
I haven't played with Win7 much. I simply don't have ready access to it. In Vista and below, GDI+ v1.1 won't load when GdiplusStartup is called, unless an application manifest is used to specifically define v1.1. If Win7 loads it by default; great. If you are going to use your app on older O/S, be warned.
GdipDrawImageRectRectI exists in any version. GdipDrawImageFX is used to create visual effects like blurring, tinting, sharpening, red-eye correction, etc. Not sure if it even applies to your program
-
Feb 24th, 2012, 04:15 AM
#7
Thread Starter
Fanatic Member
Re: Problem with Image handle and GDIPlus
The only problem left with this is that the enlargement factor is not being understood properly by this GdipDrawImageFX function.
When I use it (as in my post #1) to enlarge the picture 4 times, it actually enlarges the image as much as 3.915 times instead.
My post#1 code enlarges the borders of Picture2 as much as 4 times:
Code:
Picture2.Height = Picture2.Height * ZoomFactor
Picture2.Width = Picture2.Width * ZoomFactor
But, the actual image (the result of GdipDrawImageFX) is slightly smaller than that, so the Picture2 with its borders slightly bigger than the actual image displayed looks awkward.
When I put a breakpoint and try to find the REAL magnification factor that GdipDrawImageFX has achieved, I see that it has actually enlarged the picture as much as 3.915 times (instead of 4 times), because the following code results in the best way that the picture fits within the PictureBox:
Code:
picture2.Width = picture1.Width * 3.915
picture2.height = picture1.height * 3.915
I can probably live with the slight difference in enlargement, for example 3.915 instead of 4.0 would be ok, but I have to make sure that the image fits perfectly within the borders of the PictureBox. This problem I cannot live with.
Given that in reality the enlargement factor is not always 4, and could be different), I cannot hard-code the number 3.915 (like in the little snippet of code above)
So, I have to do one of the following fixes:
1. Based on the enlargement factor that I pass to this code, I somehow find/calculate/figure out the REAL enlargement factor, so that I can set the width and height of Picture2
2. Somehow find out the actual width and height of the resulting image that comes out of this GdipDrawImageFX. There should be a way, it could output the resulting width and height.
3. Or somehow force this GdipDrawImageFX to understand this enlargement factor correctly, and enlarge as much as this factor.
Please note that I have tried the following, and none of them works:
Code:
Picture2.Height = Picture2.Picture.Height
Picture2.Width = Picture2.Picture.Width
Code:
Picture2.Height = Picture2.Image.Height
Picture2.Width = Picture2.Image.Width
Code:
Picture2.Height = Picture2.Picture.Height / Picture2.Image.Height * Picture2.Height
Picture2.Width = Picture2.Picture.Width / Picture2.Image.Width * Picture2.Width
I have also tried this:
Code:
Picture2.AutoSize = True
And this does not work either. When I set the Picture2.AutoSize = True, the PictureBox Picture2 will be the same size as the original PictureBox (Picture1), which would show only 1/4 th of the enlarged picture !!
By the way, please note that in my Post #1, there was a little bug:
Code:
Dim ZoomFactor As Long
that I have ALREADY fixed as follows:
Code:
Dim ZoomFactor As Single
So, this bug could not be the reason for this sizing problem.
How do I fix this sizing problem?
thanks,
Ilia
-
Feb 27th, 2012, 06:47 AM
#8
Thread Starter
Fanatic Member
Re: Problem with Image handle and GDIPlus
Can anybody help me on this sizing problem?
I think that hMatrix refers to a matrix, and that the dimensions of that matrix (height and width) can be somehow retrieved. If we can retrieve those height and width from the matrix, those would be the number of pixels, and then the problem is easily solved, but I don't know how to access that matrix and retrieve the height and width from it.
Can anybody help on this?
thanks,
Ilia
-
Feb 27th, 2012, 02:03 PM
#9
Re: Problem with Image handle and GDIPlus
Some notes
1. Picture2.Picture.Height & Width are in a scalmode call HiMetrics. You should not use this value directly without converting it to another scalemode
2. If your picturebox has borders, you should not resize it by it's Width or Height properties. Doing so will give you the wrong values.
For example:
- Picture1.Width is 102 pixels using a 1 pixel border (Appearance property = 0 Flat)
- So, the horizontal area to draw on is 100 pixels, less the 1 pixel for the left/right edges of the picturebox
- If you multiply Picture1.Width (102) * 4 you get 408. Subtract the 2 pixels from the left/right edges & you end up with 406 pixels to draw on. The drawing surface was increased more than 4x, it should have resulted to 400, not 406
Suggest you use this calculation to resize your picturebox. If picturebox is in a frame, change Picture2.Container.ScaleMode to vbTwips. Otherwise, it is flexible enough that your picturebox's container's scalemode should not matter. Similar algo used to zoom the height
Code:
Dim lBorderSize As Long
lBorderSize = (Picture2.Width - ScaleX(Picture2.ScaleWidth, Picture2.ScaleMode, Picture2.Container.ScaleMode)) / 2
Picture2.Width = ScaleX(Picture2.ScaleWidth, Picture2.ScaleMode, Picture2.Container.ScaleMode) * zoomFactor + lBorderSize * 2
-
Mar 5th, 2012, 02:53 AM
#10
Thread Starter
Fanatic Member
Re: Problem with Image handle and GDIPlus
Thanks for the help.
Everything works fine now.
Just I am wondering how I can add the other visual improvements that this GdipDrawImageFX can provide.
For example I know that I can add sharpness, change brightness, and change contrast, but how?
How can I make this GdipDrawImageFX function to create these visual effects?
thanks,
-
Mar 5th, 2012, 10:41 AM
#11
Re: Problem with Image handle and GDIPlus
Originally Posted by IliaPreston
Just I am wondering how I can add the other visual improvements that this GdipDrawImageFX can provide.
For example I know that I can add sharpness, change brightness, and change contrast, but how?
How can I make this GdipDrawImageFX function to create these visual effects?
thanks,
You are already using some of the code from my Alpha Image Control. Within that control is the GDIpEffects class. And within that class you will find the following functions you can play with or learn from
CreateBlurEffect
CreateBrightnessContrastEffect
CreateColorBalanceEffect
CreateColorCurveEffect
CreateHueSaturationLightnessEffect
CreateRedEyeCorrectionEffect
CreateSharpenEffect
CreateTintEffect
CreateToneLevelEffect
The above functions create the effects. Within each function are comments and links to the MSDN official documentation for that GDI+ related function. GdipDrawImageFX requires an effects handle. You must destroy that also at some point. The downside of GdipDrawImageFX, it only accepts 1 effects handle. To render more than 1 effect, you will need to buffer your image, render it with 1 effect, than render that new image with another effect, etc, etc.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|