-
2 Attachment(s)
[VB6] GDI+ Usage & Samples
This project is a teaser to get one's feet wet. For a fully functional usercontrol that is based almost entirely on GDI+, see my alpha image control project.
If you have questions on how to do something with GDI+ that the attached project does not do, please post your question in the Graphics portion of the forum. I do not want, nor intend that this posting become a proxy "GDI+ Forum"
If you have questions about this project then by all means ask away. There may be a few logic errors in the project; as we find those I'll continue to update. Jump to bottom to determine when last updated.
GDI+ is a very handy tool and many coders stay away from it because it appears difficult, at first. So, the classes in this project are aimed at easing you along a bit. GDI+ has 100's of functions and the project herein only touches a small percentage of them. But should be enough for you to get your feet wet without being overwhelmed.
A great site for descriptions of the GDI+ API functions.
I do plan on updating this project from time to time. Things I have knowledge on and have not yet added to the project include GDI+ paths, strings, and regions. The project does include a very complete image processing class, a rendering class, and a pens/brushes class to get you started.
If you want the absolute minimal code to read and display an image at actual size, see post #8 below
Attachment 75528
Some advantages of using GDI+
:: Load/Save PNGs and TIFFs, even multi-page TIFFs
:: Load and display animated GIFs
:: Alphablending - images, lines, shapes, everything. GDI+ does not use standard RGB colors. All colors are ARGB which includes level of alphablending.
:: Built-in niffty options like rotation, mirroring, on-the-fly pixel transformations and more
:: No manual premultiplication of alpha values
Some disadvantages of GDI+
:: Crashes easy if not used properly. Objects created around other objects (i.e., images) are not actually stand-alone. GDI+ requires the source data to remain present until the GDI+ object is destroyed.
-- Here is the MS KB Article link: http://support.microsoft.com/kb/814675
-- The workaround is to create a DIBSection, then create a GDI+ bitmap from it and destroy the DIBSection
-- But that workaround won't allow access to other images in a multi-image formats (GIFs, TIFFs) and 32bpp become pARGB.
:: In IDE, can crash easily too -- like subclassing, you cannot just hit End or terminate your app from the debug window while GDI+ is running.
:: As mentioned above, a little complicated for new users
:: Does not support all older GDI functions. For example, you can't create an XOR pen with GDI+
:: Requires extra steps to setup for drawing; but like anything new to you, just takes a little getting used to
___________________________________________________________________________
Last Edited: Jan 21, 2010 8:50 PM GMT/Zulu
:: Modified primarily for TIFF saving
-- Can now save multiple images with/without attributes applied
-- TIFF images can be saved in 1,4,8,24,32 bit depths - dependent upon source image depth
:: Added another optional rendering quality setting when drawing: PixelOffsetMode
:: Removed requirement of cGDIpRenderer to be passed token class during its function calls.
-- Now entire class can be passed it one time via its AttachTokenClass method
:: Single page TIFFs no longer cache their original source data; more memory savings
Jan 19, 2010 2:20 AM GMT/Zulu
:: Revamped quite a bit
-- To reduce memory usage, PNG, BMP, & JPG no longer cache original data
-- CloneImage routine still had some shortcomings when trying to clone without applying image attributes; fixed
-- Fixed logic error reported by Jonney when cloning rotated images
:: Added additional image types: imageIconPNG & imageCursorPNG to distinguish between PNGs loaded from ico/cur resources
Jan 14, 2010 19:10 PM GMT/Zulu
:: Found 2 memory leaks, one each in 2 different functions; fixed
:: Changed logic when loading new images
-- If new image then all attributes are reset automatically (were not before). Optional load flag prevents this.
-- If multi-image format displays another image within same source; attributes remain (no change from previous logic)
:: CreateCloneWithAttributes renamed to CreateClone. WithAttributes now an optional parameter
:: ImageType property distinguishes between icons and cursors now
:: Reorganized and cleaned up some routines
:: Demo reworked a tad
-- You can now apply multiple attributes to the same image and see results
-- Included sample of saving a multi-page TIFF
Jan 12, 2010 01:10 AM GMT/Zulu
:: Added a little more functionality to the cGDIpImage class
-- GetPixel, SetPixel, ExtraTransparentColor (similar functionality of TransparentBLT)
:: Mirroring was off, had my constants swapped around, fixed
:: When SaveAsOriginalFormat was applied for 32bpp alphablended bitmaps, was saved as 24bpp vs 32pp, fixed
:: Updated demo a bit
-- included SaveAsOriginalFormat, TransparentBLT examples
-- included option to view any image in a multiple image format
-- included option to view image info: size, depth, type
Jan 11, 2010 01:30 AM GMT/Zulu
:: Jonney helped identify a logic error when deciding whether 32bpp images use the Alpha channel and how it is used. Thanx Jonney
...
-
Re: [VB6] GDI+ Usage & Samples
There's problem with opening 32bpp picture.
Offset = LBound(inArray) instead of Offset = 14& in function ProcessAlphaBitmap.
-
Re: [VB6] GDI+ Usage & Samples
Another place in LoadPicture_DIBSection
"If ProcessAlphaBitmap(bData(), 14&, BHI.bmiHeader.biWidth, BHI.bmiHeader.biHeight) = True Then"
should be "If ProcessAlphaBitmap(bData(), 0&, BHI.bmiHeader.biWidth, BHI.bmiHeader.biHeight) = True Then"
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
Jonney
Another place in LoadPicture_DIBSection
"If ProcessAlphaBitmap(bData(), 14&, BHI.bmiHeader.biWidth, BHI.bmiHeader.biHeight) = True Then"
should be "If ProcessAlphaBitmap(bData(), 0&, BHI.bmiHeader.biWidth, BHI.bmiHeader.biHeight) = True Then"
Jonney, you'd have to send me a sample bitmap that it doesn't work with. I suspect you were sending the arrays that contain BitmapInfo + PixelData not a proper bitmap file format. Note my final comments in last paragraph below.
Regarding your 2 comments
1. When passing offset as 14 vs 0, this is required because the array being passed to that function has a BITMAPFILEHEADER attached, where the 1st 14 bytes are an additional header in front of the typical BITMAPINFO. We are passing bitmap data in a file format, not DIBSECTION format. If you noticed a couple lines before that call, you will see the pixel data is being added 54 bytes + color table, into the array. The 54 bytes are the 40 for the bitmapinfo and 14 for fileheader. So 14, is correct, not 0.
Code:
If GetDIBits(dDC, DIBhandle, 0&, BHI.bmiHeader.biHeight, bData(54& + clrUsed * 4&), BHI, 0&) Then
2. And in your previous post, again, for same reasons, offset cannot be LBound(Array) because the routine should be being passed an array that contains a bitmap/DIB with the BITMAPFILEHEADER in play.
If anything I may have misled you in the function name. It probably would be better named as LoadPicture_DIBHandle vs LoadPicture_DIBSection. The ProcessAlphaBitmap routine was not designed to pass some VB array that contains just the bitmapinfoheader and pixel data, it was designed to process bitmaps file formats. If you are using it for a custom purpose, simply add an additional 14 bytes in front of your VB array before sending it to be parsed/loaded. But the 1st 2 bytes of that 14 byte buffer you may add must contain this value: &H4D42. That is the magic number for a bitmap file. You should also populate bytes the final 8 bytes with real values and there are examples on what those values are suppose to be in several of the routines. Bottom line: All loading functions in the classes assume proper file format of whatever was passed, not portions of that file format.
-
1 Attachment(s)
Re: [VB6] GDI+ Usage & Samples
Attached a 32bpp BMP file
-
Re: [VB6] GDI+ Usage & Samples
Jonney, thanx for the bitmap. The issue isn't with offsets, the ProcessAlphaBitmap is identifying yours as 32bpp ARGB bitmap but GDI+ says it is 32bpp RGB (no alpha channel used). I'll have to re-examine my ARGB, pARGB loop to see what happened and why.
Thanx again for the bug report.
Edited: Fixed and updated zip. Note that logic was flawed twice, not only did it fail when all alpha bytes were zero, it would have failed if all alpha bytes were 255. I threw that part of the project in as a final thought. I was always annoyed that GDI+ doesn't seem to want to process 32bpp alphablended bitmaps; though in the real-world, they are hard to find..
FYI. Why your tweaks worked. Because my routine was flawed it told GDI+ that it used the alpha channel. Since the alpha channel was all zeros, GDI+ interpretted it as a 100% transparent bitmap. The reason why changing the offsets worked for you is that the routine failed on purpose because the information retrieved was offset by -14 bytes and basically retrieved garbage. Because it failed, it simply passed it off to GDI+ to see if it could load it, which it did because the image was fine, just my logic was flawed. However, those tweaks would have prevented an alphablended bitmap from loading correctly.
-
Re: [VB6] GDI+ Usage & Samples
Your fixes are working well with 32bpp BMP and alphablended bitmap now.
-
Re: [VB6] GDI+ Usage & Samples
A little bonus code. If you only want to render a PNG, TIFF to a DC of your choosing and render it full size, this is the minimal code you need. You could also render jpg, bmp, wmf, emf and first frame of a gif. Not all icons will be loaded by GDI+. To render at a size other than full size, you will need more GDI+ API calls.
Edited: See post #19 & post #21 below for potentially weird behavior using GdipDrawImage.
Code:
Private Declare Function GdiplusStartup Lib "gdiplus" (Token As Long, inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0) As Long
Private Declare Function GdipLoadImageFromFile Lib "GdiPlus.dll" (ByVal mFilename As Long, ByRef mImage As Long) As Long
Private Declare Function GdipDeleteGraphics Lib "GdiPlus.dll" (ByVal mGraphics As Long) As Long
Private Declare Function GdipCreateFromHDC Lib "gdiplus" (ByVal hDC As Long, hGraphics As Long) As Long
Private Declare Function GdipDrawImage Lib "GdiPlus.dll" (ByVal mGraphics As Long, ByVal mImage As Long, ByVal mX As Single, ByVal mY As Single) As Long
Private Declare Function GdipDisposeImage Lib "gdiplus" (ByVal Image As Long) As Long
Private Declare Sub GdiplusShutdown Lib "gdiplus" (ByVal Token As Long)
Private Type GdiplusStartupInput
GdiplusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type
Private Function RenderPNG(FileName As String, hDC As Long, X As Long, Y As Long) As Boolean
On Error Resume Next
Dim GDIsi As GdiplusStartupInput, gToken As Long, hGraphics As Long, hBitmap As Long
GDIsi.GdiplusVersion = 1&
GdiplusStartup gToken, GDIsi
If Err Then
Err.Clear
Exit Function
ElseIf gToken = 0& Then
Exit Function
End If
On Error Goto 0
Call GdipCreateFromHDC(hDC, hGraphics)
If hGraphics Then
Call GdipLoadImageFromFile(StrPtr(FileName), hBitmap)
If hBitmap Then
GdipDrawImage hGraphics, hBitmap, X, Y
GdipDisposeImage hBitmap
RenderPNG = True
End If
GdipDeleteGraphics hGraphics
End If
GdiplusShutdown gToken
End Function
-
Re: [VB6] GDI+ Usage & Samples
Keith, thanks for sharing this fine work. Over the years I've
learned a lot from your PSC projects and this is no exception.
This really takes a lot of the mystery out of GDIP.
-
Re: [VB6] GDI+ Usage & Samples
hello,
in the site u listed , some of them use code in c++ like this one
http://www.com.it-berater.org/gdiplu..._functions.htm
how do i translate this to vb
thanks
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
coolcurrent4u
Just gotta know how ;)
Look at the PowerBasic declarations, not the C decs. They are far easier, DWORD is Long. Also use declarations in the project as a guide to help.
-
Re: [VB6] GDI+ Usage & Samples
I didn't see Attributes being changed with a Rotation (m_Angle) in pvModifyAttributes. Does a Rotation cause Image Attribute being modified? It seems yes. A Rotation could cause changes of Image Width or Height.
Code:
' Return the GDI+ image angle
' Attribute permanent until Destroy is called
Public Property Get Rotation() As Single
Rotation = m_Angle
End Property
Public Property Let Rotation(newVal As Single)
If m_Angle <> newVal Then
m_Angle = newVal
pvModifyAttributes 'Called,but no Rotation code inside
End If
End Property
-
Re: [VB6] GDI+ Usage & Samples
Nice to have 2nd set of eyes.
No, attributes are not modified when rotation is in play or not. What handles rotation is the RenderImageClassToDC function in cGDIpRenderer class. Rotation is done via a world transformation on the DC.
So, the call to pvModifyAttributes is useless in that one spot & should be removed from that property Let statement & will do next update (a result of copy & pasting one property to the next and to the next). Will re-think a bit to see if having Rotation property non-zero has any ill effects elsewhere in the routines. If it does, then I will update sooner than later. I am thinking the answer is no, otherwise I would have seen those ill effects by now; but will think about it in my sleep tonight.
Edited: I didn't answer both of your questions. The one regarding the size, you are very correct in that it can affect the size.
When rendering to a DC, it isn't the code's responsibility to ensure the rotated image fits the DC area, much like any rendering routine. A simpler example is if you have a 64x64 drawing surface and load a 126x126 image and render it, it will not fit the surface. It is the coder's responsibility to dictate what dimensions the rendering should be done at.
Now in the CloneImage routine where the image is actually rotated and/or resized, it matters very much because I am taking the responsibility of ensuring the end result is large enough for the rotated image. I admit I did take a shortcut where I resized the image to SQR(W*W + H*H) which is the maximum size any dimension can have and be able to rotate the image at any angle without chopping it off. Because that size may be larger than the actual rotated image, a fixer-upper section in that routine trims excess transparency. Now, I could have taken the time to calculate the true bounds of the non-alpha pixels and then calculated their rotated dimensions to create the end result exactly sized without the fixer-upper stuff and wouldn't require a 2nd temp image because the 1st one would have been correct from the start. But 1) I really hate trig, 2) pretty much the same amount of code to determine opacity bounds, and 3) I just wanted to draw the line on how advanced some of the functions were going to be. I took out hundreds of lines of more advanced code from this project that no-one has seen, simply because, in my opinion, it was too advanced for a "get your feet wet" type of project.
-
Re: [VB6] GDI+ Usage & Samples
Since What I indicated previously,rotation don't change attributes, so I can't figure out how to save a rotated picture directly using one of the cGDIpImage functions.
Here is a way how to save a rotated picture based on your demo:
1. Change DoRotationExample:
Code:
Private Sub DoRotationExample()
If cImage.Handle = 0& Then LoadResourceImage "Spider"
cImage.Rotation = cboSample.ListIndex * 5
RenderTheImage
cImage.ExtraTransparentColor = cImage.GetPixel(0, 0)
RenderTheImage 'Apply m_TransColor so that we can modify m_Attr so that we can call below codes
'(
'If m_Attr Then ' any attributes to apply?
'Set cNewImage = New cGDIpImage
'CloneImage cNewImage, , , True
'hImage = cNewImage.Handle
'Else
'hImage = m_Image(m_SourceIndex)
'End If
')
End Sub
2. Save the rotated picture using DoSaveExample.
eg.Case 0: bOk = cImage.SaveAsBMP(bData(), True) to save as bmp file.
3.If the original Boundary is 'bigger' than Boundary of after-rotated picture,then the picture will save 'rightly' else some of part will cut off.
4.Your extra routine will need to cope with Saving the rotated picture.
-
Re: [VB6] GDI+ Usage & Samples
Jonney, thanx for the bug report.
Regarding your previous post and rotation, there is something I need to update in multiple routines. The code is already present & suitable
1. In the SaveAs.... routines, there is a statement that needs to be changed in each of them (except SaveAsOriginalFormat).
From: If m_Attr Then
To: If m_Attr <> 0& Or m_Angle <> 0! Then
or depending on taste: If Not (m_Attr = 0& And m_Angle = 0!) Then
2. In CloneImage, need to offset the rendering so it doesn't get chopped off; oversight.
From: cRenderer.RenderImageClassToDC ....
To:
Code:
cRenderer.RenderImageClassToDC Me, tDC, (cX - Width) \ 2, (cY - Height) \ 2, _
Width, Height, , , , , InterpolationModeHighQualityBicubic, SmoothingModeAntiAlias
Edited: Tweaks incorporated into latest Zip in post #1
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
LaVolpe
Jonney, thanx for the bug report.
Regarding your previous post and rotation, there is something I need to update in multiple routines. The code is already present & suitable
1. In the SaveAs.... routines, there is a statement that needs to be changed in each of them (except SaveAsOriginalFormat).
From:
If m_Attr Then
To:
If m_Attr <> 0& Or m_Angle <> 0! Then
or depending on taste:
If Not (m_Attr = 0& And m_Angle = 0!) Then
2. In CloneImage, need to offset the rendering so it doesn't get chopped off; oversight.
From:
cRenderer.RenderImageClassToDC ....
To:
Code:
cRenderer.RenderImageClassToDC Me, tDC, (cX - Width) \ 2, (cY - Height) \ 2, _
Width, Height, , , , , InterpolationModeHighQualityBicubic, SmoothingModeAntiAlias
Yes,it works now.
-
Re: [VB6] GDI+ Usage & Samples
-
Re: [VB6] GDI+ Usage & Samples
Jonney brought up a good example where the classes I supplied may not be ideal as is.
Scenario: Loading some massive compressed image like a TIFF or JPG or PNG, for example, that is 5-10x screen size.
Issue: Takes quite awhile for the image to eventually be displayed.
Reasons: The classes create stand-alone images to prevent accidental GDI+ crashes if the source image is removed while the GDI+ image is bound to that source. See this link that describes the issue and workarounds by MSDN.
1. Because of this the image file is processed (bytes read from file - potential bottleneck for massive images). The image is not processed directly from file because GDI+ has issues with many types of image formats. Therefore the bytes are sent to another routine that will handle those issues.
2. In order to eventually create the source image, an IStream interface is created and the bytes copied to that interface so that GDIpLoadFromStream API can be used by GDI+ to load image from an array. Another potential bottleneck for massive images.
3. The stand-alone image is a bitmap, same size and bit depth of the original, then the source is rendered into the stand-alone or a data-copy from source to stand-alone is performed. For typical sized image, this process is quick but not for massive images.
4. Now, if scaling or attributes are applied (i.e., flipping, grayscaling, rotation, etc), GDI+ must process that massive image pixel by pixel and this will take time, no matter what.
5. Also, if one were to use one or more of the high-quality rendering options, that time is increased yet again.
Possible Solutions.
1. Since these massive image sources will undoubtedly be files vs byte arrays... Create a new procedure or rework existing ones that create a GDI+ image directly from file (GDIpLoadImageFromFile API) and do not move, rename or delete the file while the GDI+ image is active. This should speed things up dramatically.
2. Create a new procedure or rework existing ones that will create a destination sized or a maximum of screen-sized GDI+ 32bpp bitmap, then load the file using GDIpLoadImageFromFile API and render that image into the 32bpp image, releasing the image created from the file and using the 32bpp GDI+ image. This will be a bit slower than option 1 initially, but since the image used by GDI+ is now no larger than the screen, any future rendering and applied attributes will be far faster.
3. Any tweaks to existing procedures may require tweaks to supporting procedures within the classes; so before tweaking ensure you fully understand how the classes really work.
Note: GDIpLoadFromFile should be used for formats GDI+ handles easily: TIFF, PNG for example, not all bitmaps, icons, and not cursors.
Just some ideas. The classes were designed for every day use and ideally handling relatively normal-sized images, say screen-size or smaller. Because the classes are designed that way, they may not be optimal for special situations and that is where you may wish to modify/tweak the code for those circumstances.
-
Re: [VB6] GDI+ Usage & Samples
Hi LaVolpe
First I want to thank you for sharing this knowledge.
I just want to render a full size PNG file to my Form DC using your 'Bonus Code' :)
But the image displayed in my form is the mini version the original image.
My image resolution is 787x765, the displayed one is 252x245... how come?
I just simply copy paste your code, nothing changed.
any tought?
-
Re: [VB6] GDI+ Usage & Samples
I haven't a clue. Obviously GDI+ sees the image as 252x245. Maybe the image may have multiple resolutions/pages? Try loading it with the main classes in post #1, double click on the picturebox and get the image details; they may explain it and may not.
Edited: See post #22 below for the reason
-
Re: [VB6] GDI+ Usage & Samples
I follow your instruction and the image info is:
Image Type: PNG
Size : 787 x 765
Color Type : True Color + Alpha
BTW.. i change the draw function:
GdipDrawImage hGraphics, hBitmap, X, Y
to:
Call GdipGetImageBounds(hBitmap, m_Size, UnitPixel)
Call GdipDrawImageRectRectI(hGraphics, hBitmap, 0, 0, m_Size.nWidth, m_Size.nWidth, 0, 0, m_Size.nWidth, m_Size.nHeight, UnitPixel, 0&, 0&, 0&)
and it display the image correctly.
Thank's
-
Re: [VB6] GDI+ Usage & Samples
Ah, so GDI+ gdipDrawImage was the culprit?
Well, I'm glad I never use that function in real life. Always have used gdipDrawImageRectRectI since it allows much more flexibility.
Edited: Just another thing to chalk up to GDI+'s sometimes strange behavior. There is probably a reason why it did what it did, would require a bit of research for the answer.
Edited again. I modified post #8 to reference your #19 & #21 should someone else have a similar experience.
Follow up: I believe the issue is with GDI+ auto-DPI scaling. Per MSDN:
Quote:
Another feature of GDI+ is that images carry around the DPI they were designed for (as in Image:GetPhysicalDimension and Bitmap:SetResolution, for example). You can use this information to scale images properly, or you can let GDI+ do it. If you don’t specify a height and width when you call DrawImage, GDI+ calculates them based on the screen’s DPI and the image’s DPI.
GdipDrawImage does not have width/height parameters. Per reply #19 above the image reports being 787x765 but rendered at 252x245. Well, if the image's DPI property/value reported 300 DPI, then using DPI scaling formula:
logicalSize = (physicalSize * screenDPI) / imageDPI
Width: (787 * 96) / 300 = 251.84 rounded to 252
Height: (765 * 96) / 300 = 244.8 rounded to 245
So, 787x765 = 252x245 @ source image DPI of 300
-
Re: [VB6] GDI+ Usage & Samples
Thanks LaVolpe...
This all looks very interesting. I'm working on a old old vb app which I need to get transparency working and we always resorted to using GIF files before and always wondered whether we could get true alpha channels working, this will be quite exciting to test out, especially as we can change brightness as well!
Does GDI+ allow for blending modes? Multiply/add/screen/overlay?
-
Re: [VB6] GDI+ Usage & Samples
GDI+ does allow this via matrices if I am understanding your question correctly. If so, it can be a little complex but not too much. In post #1, I provided a link that describes GDI+ API functions. At the link are also some examples. They are in PowerBasic syntax, but I found them fairly easy to translate to VB.
May be worth looking at... Some functions you may want to look at (Graphics section of link): GdipMultiplyWorldTransform, GdipScaleWorldTransform, GdipSetWorldTrfansform, GdipTranslateWorldTransform. Also in the Matrix section of the link are the Matrix modification routines.
As far as overlay goes? If you are talking about transaprency, then the classes already have that built in.
Edited: Additional questions regarding GDI+ capabilities and usage of the functions I mentioned above should be posted in the Graphics portion of the forum.
-
Re: [VB6] GDI+ Usage & Samples
Just one small question on your post 8 code, I'm trying to get it to load the png file into a Picturebox and don't seem to be able to find the correct syntax... (sorry it's been a while since I've dabbled in vb, moved on to more graphics based things)
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
GPRM_Dave
Just one small question on your post 8 code, I'm trying to get it to load the png file into a Picturebox and don't seem to be able to find the correct syntax... (sorry it's been a while since I've dabbled in vb, moved on to more graphics based things)
You might want to make the picturebox's AutoRedraw=True. Then refresh it after the call. Note the remarks in that posting too; it is bare-bones code and offers no flexibility. You can't even get the image dimensions with it. However, you can modify the code to include more functionality. Review the main classes in post #1 and pull what you need from there, as appropriate.
Code:
Call RenderPNG("C:\TestImage.png", Picture1.hDC, 0, 0)
-
1 Attachment(s)
Re: [VB6] GDI+ Usage & Samples
I do believe you are adding more stuff into the class.But how about some basic tutorial for new babies like Path/Region/Pen/Brush/Font/Matrix...etc?
-
Re: [VB6] GDI+ Usage & Samples
I like your structure/outline. Couple things
1. MetaFiles (nope, I don't deal with them and IMO they are rarely used (creation) vs PNGs, Bmps, etc)
2. The Effect branch is something I'm not familiar with and haven't played with in great detail. Those effects are only available with GDI+ v2 (I believe on Vista and above). Whether it works on XP or not, I don't know.
3. With the exception of strings, regions & paths, the other stuff is provided in various degrees of completeness. I do have a semi-complete paths class which I know you are a bit familiar with. When I find the time, I'll add it to this project. Right now, I am focused on another project at the moment.
Note: My intention, as stated in post #1, is to get one's feet wet; not to attempt a comprehensive "how to" tutorial.
-
Re: [VB6] GDI+ Usage & Samples
Regarding GetScaledImageSizes function, if Rotation is being set,e.g. 45 degree,How to calculate the BestFit size? GetScaledImageSizes doesn't consider Rotation. In you demo,can you try to Rotation Example?part of picture will cut visually.
How can we scale the picture to best fit the picture1.width and picture1.height with any angles rotation?
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
LaVolpe
A more effective method may be one of these:
1. If no transparency is in effect...
2. Transparency is different. ...
It's a little complicated. I will probably give up until you can give a good solution:D.
-
Re: [VB6] GDI+ Usage & Samples
Ok, here is a routine you can tweak/add to your routines.
First the remarks.
1. Rotating and scaling an image relative to a destination rectangle can produce undesirable effects. For example rotating a square within the same square without having the rotated shape escape the original square's bounds, produces a rubber-band effect on the rotated shape -- changing sizes as angle changes.
2. In most cases, I would think rubber-banding would not be desirable. For example, a clock hand rotating.
Anyway, here is the algo that will rubberband. Adjust as needed.
a. New form, add 2 shape controls, 1 command button
b. Leave all controls at default names, properties, positions
c. Copy & paste the following into the form and run it
Code:
Option Explicit
Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Sub Form_Load()
Me.ForeColor = vbBlue
With Shape1
.Move 135, 135, 2550, 1485
End With
With Shape2
.Move 3105, 135, 1485, 1485
End With
With Command1
.Move 2250, 1710, 1215, 495
.Caption = "Do It"
End With
Me.Move Me.Left, Me.Top, 4875, 2805
End Sub
Private Sub Command1_Click()
Dim Angle As Single, A As Single, d2r As Double
Dim sinT As Double, cosT As Double
Dim h1 As Long, h2 As Long, hh As Long, ww As Long
d2r = (4& * Atn(1)) / 180 ' conversion factor for degree>radian
For A = 0 To 360
Angle = Abs(A) ' keep range between -90 to 90 for bounds calcs
Select Case Angle
Case Is < 91
Case Is < 181
Angle = 180 - Angle
Case Is < 271
Angle = Angle - 180
Case Else
Angle = 360 - Angle
End Select
sinT = Sin(Angle * d2r)
cosT = Cos(Angle * d2r)
With Shape1
h1 = .Height * .Height / (.Width * sinT + .Height * cosT)
h2 = .Width * .Width / (.Width * cosT + .Height * sinT)
If h1 < h2 Then hh = h1 Else hh = h2
ww = hh * .Width / .Height
End With
Me.Cls
Call ShowResults(A, ww, hh, Shape1)
With Shape2
h1 = .Height * .Height / (.Width * sinT + .Height * cosT)
h2 = .Width * .Width / (.Width * cosT + .Height * sinT)
If h1 < h2 Then hh = h1 Else hh = h2
ww = hh * .Width / .Height
End With
Call ShowResults(A, ww, hh, Shape2)
Me.Caption = A & " Degree Rotation"
DoEvents
Sleep 10
Next
Me.Caption = "Done"
End Sub
Private Sub ShowResults(Angle As Single, ScaledCx As Long, ScaledCy As Long, sampleShape As Shape)
' following is not part of the rotation/resizing routine
Dim tPTs(1 To 4) As POINTAPI, X As Long
Dim ctrSrc As POINTAPI, ctrDest As POINTAPI
Dim d2r As Double
Dim sinT As Double, cosT As Double
ctrSrc.X = ScaledCx \ 2
ctrSrc.Y = ScaledCy \ 2
With sampleShape
ctrDest.X = .Width \ 2 + .Left
ctrDest.Y = .Height \ 2 + .Top
End With
d2r = (4& * Atn(1)) / 180
sinT = Sin((Angle Mod 360) * d2r)
cosT = Cos((Angle Mod 360) * d2r)
tPTs(1).X = (-ctrSrc.X * cosT) - (-ctrSrc.Y * sinT) + ctrDest.X
tPTs(1).Y = (-ctrSrc.X * sinT) + (-ctrSrc.Y * cosT) + ctrDest.Y
tPTs(2).X = (ScaledCx - ctrSrc.X) * cosT - (-ctrSrc.Y * sinT) + ctrDest.X
tPTs(2).Y = (ScaledCx - ctrSrc.X) * sinT + (-ctrSrc.Y * cosT) + ctrDest.Y
tPTs(3).X = (ScaledCx - ctrSrc.X) * cosT - (ScaledCy - ctrSrc.Y) * sinT + ctrDest.X
tPTs(3).Y = (ScaledCx - ctrSrc.X) * sinT + (ScaledCy - ctrSrc.Y) * cosT + ctrDest.Y
tPTs(4).X = (-ctrSrc.X * cosT) - (ScaledCy - ctrSrc.Y) * sinT + ctrDest.X
tPTs(4).Y = (-ctrSrc.X * sinT) + (ScaledCy - ctrSrc.Y) * cosT + ctrDest.Y
For X = 1 To 3
Me.Line (tPTs(X).X, tPTs(X).Y)-(tPTs(X + 1).X, tPTs(X + 1).Y)
Next
Me.Line (tPTs(X).X, tPTs(X).Y)-(tPTs(1).X, tPTs(1).Y)
End Sub
Edited: ww & hh would be the scaled width/height in relation to the target rectangle and angle of rotation. Rendering must be done from center of target rectangle to prevent image drawing over target bounds.
Edited yet again. Now if you wanted to know what would be the scaled size of an image in a target rectangle so that the image can be rotated at any angle without rubberbanding, you can use this formula (refering to the GetScaledImageSizes code):
Code:
Dim rotSize As Long
rotSize = Sqr(m_Size.nWidth *m_Size.nWidth + m_Size.nHeight * m_Size.nHeight)
xRatio = destWidth / rotSize
yRatio = destHeight / rotSize
If xRatio > yRatio Then xRatio = yRatio
.... rest of code
-
1 Attachment(s)
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
LaVolpe
Edited yet again. Now if you wanted to know what would be the scaled size of an image in a target rectangle so that the image can be rotated at any angle without rubberbanding, you can use this formula (refering to the GetScaledImageSizes code):
Code:
Dim rotSize As Long
rotSize = Sqr(m_Size.nWidth *m_Size.nWidth + m_Size.nHeight * m_Size.nHeight)
xRatio = destWidth / rotSize
yRatio = destHeight / rotSize
If xRatio > yRatio Then xRatio = yRatio
.... rest of code
You missed considering the rotation angle? e.g. Rotate 5 degree,the image boundary is much less than picturebox size. We have to calculate the boundary of 4 corner of image with a rotation. See screenshot.
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
Jonney
You missed considering the rotation angle? ...
Nope. I gave you two options
The first option I provided accounts for dynamic sizes and if you run the code in a test form, you will see that.
The second option is a shortcut, its size is static. The minimum size is determined that will allow the image to be rotated at any angle. This means that the size can be larger, than needed, for some angles but will ensure all angles can be rotated within that size if rendered from the center out. In other words, the scaled size ensures the image can be rotated at any angle without rubberbanding the image.
Edited: Here is something you can play with:
Try the following, remarks follow the code
1. Replace: GetScaledImageSizes
Code:
Public Function GetScaledImageSizes(ByVal destWidth As Long, ByVal destHeight As Long, ScaledWidth As Long, ScaledHeight As Long, _
Optional ByVal CanScaleUp As Boolean = True, Optional ByVal ClipRotation As Boolean = True) As Boolean
' Function returns scaled (maintaining scale ratio) for passed destination width/height
' The CanScaleUp when set to false will never return scaled sizes > than 1:1
' The ClipRotation parameter, if False, will ensure rotated image does not exceed destination width/height
If m_Image(m_SourceIndex) = 0& Then Exit Function
Dim xRatio As Single, yRatio As Single
Dim sinT As Double, cosT As Double, d2r As Double
Dim h1 As Long, h2 As Long, A As Single
xRatio = destWidth / m_Size.nWidth
yRatio = destHeight / m_Size.nHeight
If xRatio > yRatio Then xRatio = yRatio
If Abs(Int(m_Angle + 0.99!) Mod 360) = 0! Or ClipRotation = True Then
If xRatio >= 1! And CanScaleUp = False Then
ScaledWidth = m_Size.nWidth
ScaledHeight = m_Size.nHeight
Else
ScaledWidth = m_Size.nWidth * xRatio
ScaledHeight = m_Size.nHeight * xRatio
End If
Else
ScaledWidth = m_Size.nWidth * xRatio
ScaledHeight = m_Size.nHeight * xRatio
A = Abs(m_Angle)
Select Case A
Case Is < 91
Case Is < 181: A = 180 - A
Case Is < 271: A = A - 180
Case Else: A = 360 - A
End Select
d2r = (4& * Atn(1)) / 180 ' conversion factor for degree>radian
sinT = Sin(A * d2r)
cosT = Cos(A * d2r)
h1 = destHeight * destHeight / (ScaledWidth * sinT + ScaledHeight * cosT)
h2 = destWidth * destHeight / (ScaledWidth * cosT + ScaledHeight * sinT)
If h1 < h2 Then h2 = h1
h1 = h2 * destWidth / destHeight
If xRatio >= 1& And CanScaleUp = False Then
If Not (h1 < m_Size.nWidth Or h2 < m_Size.nHeight) Then
ScaledWidth = m_Size.nWidth
ScaledHeight = m_Size.nHeight
GetScaledImageSizes = True
Exit Function
End If
End If
xRatio = h1 / m_Size.nWidth
yRatio = h2 / m_Size.nHeight
If xRatio > yRatio Then xRatio = yRatio
ScaledWidth = m_Size.nWidth * xRatio
ScaledHeight = m_Size.nHeight * xRatio
End If
GetScaledImageSizes = True
End Function
Note remarks at top of new function above.
2. Test it by hardcoding the final two parameters in the form's RenderTheImage function. Try different combinations.
Code:
cImage.GetScaledImageSizes sampleCx, sampleCy, Cx, Cy, False, False
Edited: Had formula a bit hosed if destination rect is not square; changed the "h2 = " line to fix that.
-
Re: [VB6] GDI+ Usage & Samples
I believe he algorithm is the first release in GDI+ world and VB world:). (You always create marvels in/for VB world!!!:thumb:)
On best fit mode (rotated picture will fit the canvas,will re-size the rotated picture),it works seamless.How about non-bestfit mode (picturebox force to fit rotated picture, will auto re-size the picture box)?
scenario:
Auto-resize canvas (e.g.picturebox) when rotating a raw size picture or fixed ratio picture. What is the algorithm to calculate new size of canvas (in this case,picture is always on center of canvas or with a fixed offset X/Y) with a rotation?
:lol: I talks incessantly.
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
Jonney
...How about non-bestfit mode (picturebox force to fit rotated picture, will auto re-size the picture box)?
Good question, try this.
1. Add this as new function in cGDIpImage class
Code:
Public Sub GetScaledCanvasSize(ByVal imgWidth As Long, ByVal imgHeight As Long, ByRef CanvasWidth As Long, ByRef CanvasHeight As Long)
Dim sinT As Double, cosT As Double
Dim A As Double, d2r As Double
Dim ctrX As Double, ctrY As Double
A = Abs(Int(m_Angle) Mod 360)
Select Case A
Case Is < 91#
Case Is < 181#: A = 180# - A
Case Is < 271#: A = A - 180#
Case Else: A = 360# - A
End Select
A = A + m_Angle - Int(m_Angle)
d2r = (4# * Atn(1)) / 180# ' conversion factor for degree>radian
sinT = Sin(A * d2r)
cosT = Cos(A * d2r)
ctrX = imgWidth / 2#
ctrY = imgHeight / 2#
A = (-ctrX * sinT) + (-ctrY * cosT)
CanvasHeight = (imgWidth - ctrX) * sinT + (imgHeight - ctrY) * cosT - A
A = ((-ctrX * cosT) - (imgHeight - ctrY) * sinT)
CanvasWidth = (imgWidth - ctrX) * cosT - (-ctrY * sinT) - A
End Sub
2. In the form's RenderThePicture routine, tweak to show bounding canvas size. Add this before Picture1.Refresh:
Code:
Dim canvasCx As Long, canvasCy As Long
cImage.GetScaledCanvasSize Cx, Cy, canvasCx, canvasCy
Picture1.Line (X + (Cx - canvasCx) \ 2, Y + (Cy - canvasCy) \ 2)-(X + (Cx + canvasCx) \ 2, Y + (Cy + canvasCy) \ 2), vbBlue, B
3. You will see the canvas rectangle adjust when you rotate the image.
Suggest selecting a rectangular, non-transparent image < 256x256 and pass CanScaleUp=False to the render function. This way you will easily see the canvas rectangle.
Edited: The canvas rectangle would be your picturebox (question in your previous reply). However, remember that the CanvasWidth/CanvasHeight is returned in pixels and you should convert to needed scalemode to resize picturebox. Also note that you need to include any extra pixels for borders if the picturebox has borders.
Also remember that images with transparency often have buffers of transparent pixels. This gives the illusion that the canvas rectangle is much larger than required. No algo is going to correct for that and you really only have 3 choices that I can think of: 1) accept it, 2) run a trimming routine to transfer only non-transparent pixels to another appropriately-sized image & use that image, 3) process the image to determine the tightest rectangle that excludes all fully-transparent pixels and use that size to pass to your algos (complicated because rendering offsets will most likely be necessary).
-
Re: [VB6] GDI+ Usage & Samples
Hi, I would like to make a large number of multiple shapes with the same parameters, except location (left and top) and color. Is there a way to easily make multiple rectangles, say?
TomT
-
Re: [VB6] GDI+ Usage & Samples
Tom, I am not sure I understand your question. If this is not related to my posted project, you should post your question in the appropriate language forum: VB6 & earlier, .Net, VBA, C++, etc, etc.
-
Re: [VB6] GDI+ Usage & Samples
Sorry, I'm new here. I thought I was in a VB6 forum, and that my question related peripherally to the thread.
-
1 Attachment(s)
Re: [VB6] GDI+ Usage & Samples
Code:
Private Sub Label1_Click()
'
cImage.LoadPicture_stdPicture Clipboard.GetData(vbCFBitmap), cGDIplus, 1
Call RenderTheImage
End Sub
The picture look weird with <Black> transparent color. The Return of lColorFormat in pvProcessAlphaBitmap is PixelFormat32bppARGB instead of PixelFormat32bppRGB.
-
Re: [VB6] GDI+ Usage & Samples
I don't know how you created the clipboard bitmap.
Here's the only way pvProcessAlphaBitmap will return ARGB vs pARGB or RGB.
-- One of the R,G,B values was > the alpha channel value & the alpha value is non-zero
In that routine, you can place a break on this line: lColorFormat = PixelFormat32bppARGB
That should help indicate that what I said above is correct.
If it is correct and you are creating your own DIBs with CreateDIBSection API, don't assume the DIB is created with all bytes zeroed out. I have seen similar issues before in other parsing engines I've written in the past. As a matter of habit, it might be wise to call FillMemory and zero out the DIB.
Let's say DIB was 256x256 & 32 bit.
Code:
Private Declare Sub FillMemory Lib "kernel32.dll" Alias "RtlFillMemory" (ByRef Destination As Any, ByVal Length As Long, ByVal Fill As Byte)
Dim lSize As Long
lSize = 256& * 256& * 4&
FillMemory ByVal DibPtr, lSize, 0
P.S. In that routine, RGB vs. pARGB/ARGB is only returned if ALL alpha values are either 0 or 255. It does appear the bitmap you passed is using the alpha channel, whether on purpose, accidentally, or dirty when initialized, I can't tell you.
-
Re: [VB6] GDI+ Usage & Samples
Quote:
I don't know how you created the clipboard bitmap.
By press <Prt Sr> key. You give a try then you see what I means. Your 32bpp code also has the same issue.
-
Re: [VB6] GDI+ Usage & Samples
I see what you are talking about now. Honestly, there are only a few things that can be done
1. Process ALL 32bpp bitmaps as 24bpp
2. Build a better algo that can guess better whether alpha is in use or not
3. Live with it
4. Pass optional parameters to prevent the pvProcessAlphaBitmap routine from running
The problem is as I described. The print-screen created a 32bpp DIB that wrote values in the alpha channel. Even though the image is obviously not an alpha bitmap and is meant to be a 24bpp bitmap, the alpha channel contains non-zero values. The alpha bitmap processing algo sees this and must assume the bitmap uses the alpha channel.
When running this for multiple screenshots, I consistently got over 2500 pixels with the alpha channel filled in with values that range from 2 to 253. The unfortunate fact is that the bitmap does populate the alpha channel and therefore, by definition, is an alpha bitmap. Now in real life, we know this is not true, but there is no way for the routines to know that.
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
LaVolpe
I see what you are talking about now. Honestly, there are only a few things that can be done
1. Process ALL 32bpp bitmaps as 24bpp
2. Build a better algo that can guess better whether alpha is in use or not
3. Live with it
4. Pass optional parameters to prevent the pvProcessAlphaBitmap routine from running
The problem is as I described. The print-screen created a 32bpp DIB that wrote values in the alpha channel. Even though the image is obviously not an alpha bitmap and is meant to be a 24bpp bitmap, the alpha channel contains non-zero values. The alpha bitmap processing algo sees this and must assume the bitmap uses the alpha channel.
When running this for multiple screenshots, I consistently got over 2500 pixels with the alpha channel filled in with values that range from 2 to 253. The unfortunate fact is that the bitmap does populate the alpha channel and therefore, by definition, is an alpha bitmap. Now in real life, we know this is not true, but there is no way for the routines to know that.
Because <Print Screen> will capture whole screen,including Windows Captionbar,Start Menu,Task bar and System tray whose are alpha unfortunately.
I have made a funny test:
Quote:
Code:
For B = X - 3& To X - 1&
If inArray(B) > inArray(X) Then
' pARGB can never have a R,G,B value > alpha value so this must be ARGB
lColorFormat = PixelFormat32bppARGB ' ARGB format; done checking
X = X + scanWidth ' exit X loop
Y = Height ' exit Y loop
alphaNum = alphaNum + 1
Debug.Print inArray(B), inArray(X)
'Exit For
End If
Next
1.Windows <Start Menu> bar,Alpha number is 3
2.Windows <Task bar>,Alpha number is 2
3.Windows <System Tray>,Alpha number is 1
4.Windows <Desktop>, Alpha number is 2
5.Windows Caption bar,Alpha number is 0 (exclude the min/max/close button)
6.Windows Caption bar,Alpha number is 1 (including the min/max/close button)
-
Re: [VB6] GDI+ Usage & Samples
Makes sense, but doesn't help. I think the only way to "fix" this is to not process alpha bitmaps automatically, rather provide a parameter for 32bpp images that either skips or enters the pvProcessAlphaBitmap.
Thinking out loud, this may be a better option. The routine that processes stdPicture objects takes a shortcut, for stdPicture bitmaps, it simply passes the .Handle off to the routine that processes DIB handles. The odds of someone trying to load a real 32bpp alpha bitmap into a stdPicture would be rare I would think. Therefore the routine could be modified to not take that shortcut and process them a bit differently as either paletted or 24bpp bitmaps, ignoring any alpha channels. A thought.
Continuing with that thought... If someone does load a true alphablended bitmap into a stdPicture object (possible), to process it as alphablended, they would call the routine that loads DIBhandles instead, passing the .Handle property. This new process/option takes some of the automated picture type recognition out for stdPictures (bitmaps only), but provides the coder with both options.
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
LaVolpe
Makes sense, but doesn't help. I think the only way to "fix" this is to not process alpha bitmaps automatically, rather provide a parameter for 32bpp images that either skips or enters the pvProcessAlphaBitmap.
Thinking out loud, this may be a better option. The routine that processes stdPicture objects takes a shortcut, for stdPicture bitmaps, it simply passes the .Handle off to the routine that processes DIB handles. The odds of someone trying to load a real 32bpp alpha bitmap into a stdPicture would be rare I would think. Therefore the routine could be modified to not take that shortcut and process them a bit differently as either paletted or 24bpp bitmaps, ignoring any alpha channels. A thought.
Continuing with that thought... If someone does load a true alphablended bitmap into a stdPicture object (possible), to process it as alphablended, they would call the routine that loads DIBhandles instead, passing the .Handle property. This new process/option takes some of the automated picture type recognition out for stdPictures (bitmaps only), but provides the coder with both options.
Put an extra Optional ByVal IgnoreAlpha As Boolean to force LoadPicture_stdPicture to ignore Alpha channel when copy picture from clipboard or screen-capture. I guess you will come out a better solution again.:D
Quote:
Public Function LoadPicture_DIBhandle(DibHandle As Long, TokenClass As cGDIpToken, Optional ByVal ClearAttributes As Boolean = True, Optional ByVal IgnoreAlpha As Boolean = False) As Boolean
Quote:
Public Function LoadPicture_stdPicture(Picture As StdPicture, TokenClass As cGDIpToken, Optional ClearAttributes As Boolean = True, Optional IgnoreAlpha As Boolean) As Boolean
Quote:
Private Function pvProcessAlphaBitmap(inArray() As Byte, Width As Long, Height As Long, resetFlag As pvCleanUpEnum, Optional IgnoreAlpha As Boolean = False) As Boolean
-
Re: [VB6] GDI+ Usage & Samples
In other classes/projects I've written, I have a more generic LoadPicture routine that accepts a variant. That routine tests the variant to see if its an array, long handle, stdPic, ClipBoard, Data (drag/drop) object, filename, or IStream/IUnknown object.
But that is just a tip. I do not plan on making this thread's project commercial quality. It was an attempt to get some people thinking GDI+ that were intimidated by it and I think it can do that.
There are many advanced functions that I have not included in this project and you are aware of a few of them. I don't mind giving you hints/tips to improve this project, but I don't plan on supersizing this one.
-
1 Attachment(s)
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
Jonney
Because <Print Screen> will capture whole screen,including Windows Captionbar,Start Menu,Task bar and System tray whose are alpha unfortunately.
I have made a funny test:
1.Windows <Start Menu> bar,Alpha number is 3
2.Windows <Task bar>,Alpha number is 2
3.Windows <System Tray>,Alpha number is 1
4.Windows <Desktop>, Alpha number is 2
5.Windows Caption bar,Alpha number is 0 (exclude the min/max/close button)
6.Windows Caption bar,Alpha number is 1 (including the min/max/close button)
One thought:
According to above trail,Why GDI+ display non-Alpha area with <BLACK> color except of <Start Menu> and <Task bar> and <Desktop> and Windows Caption bar?
-
Re: [VB6] GDI+ Usage & Samples
I couldn't tell you. Except that the alpha values are probably non-zero and low values like 3, 5, etc. Alpha of zero means 100% transparency. So since low alpha values, they show up nearly transparent. When alpha value are used I think any 32bpp-aware graphics engine is going to assume they are indeed alpha. And if any alpha values are used, must assume all alpha channel values are used as alpha, nothing else.
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
LaVolpe
And if any alpha values are used, must assume all alpha channel values are used as alpha, nothing else.
Can we manually process?
-
Re: [VB6] GDI+ Usage & Samples
Sure, you can use GetDIBits to return a byte array in 24bpp format. That will truncate the alpha channels.
-
Re: [VB6] GDI+ Usage & Samples
How to resize the picturebox and maintain the aspect ratio of the picture when resizing the form?
-
Re: [VB6] GDI+ Usage & Samples
Resizing a picturebox? Use Picture1.Move x, y, w, h
Maintain aspect ratio? Lots of examples in this forum & on the net.
I've also included 2 functions in that project that you can use: GetScaledImageSizes & GetScaledCanvasSize
-
Re: [VB6] GDI+ Usage & Samples
Please, can you add sample code for GetScaledImageSizes & GetScaledCanvasSize.
Thanks.
-
Re: [VB6] GDI+ Usage & Samples
In the sample project, the form's RenderTheImage sub has an example. Otherwise, you might want to post what you are trying and exactly which you want to do:
1) size the image to the container's dimensions, keeping aspect ratio
2) size the container to the image's dimensions, keeping aspect ratio
-
Re: [VB6] GDI+ Usage & Samples
I want to do number 1, resize the image when the container is resizing, for example when I resize the form (dragging the edges of the windows) the container resizes, I want also the image inside the container resize to its dimensions.
-
Re: [VB6] GDI+ Usage & Samples
Real short simple example. Note that you'll see flicker. If you want to prevent flicker, you'll want a backbuffer to render to then render the backbuffer to the form. Tons of examples about.
Code:
Option Explicit
Dim cImage As cGDIpImage
Dim cToken As cGDIpToken
Private Sub Form_Load()
Me.ScaleMode = vbPixels ' cGDIpImage class expects pixel values, not twips
Set cToken = New cGDIpToken
If cToken.Token = 0 Then
MsgBox "GDI+ failed to load", vbExclamation + vbOKOnly
Unload Me
Exit Sub
End If
Set cImage = New cGDIpImage
' load your image into cImage now...
End Sub
Private Sub Form_UnLoad(Cancel As Integer)
Set cImage = Nothing
Set cToken = Nothing
End Sub
Private Sub Form_Resize()
Dim newCx As Long, newCy As Long
Dim cRenderer As New cGDIpRenderer
cImage.GetScaledImageSizes Me.ScaleWidth, Me.ScaleHeight, newCx, newCy
Me.Cls
cRenderer.RenderImageClassToDC cImage, Me.hDC, (Me.ScaleWidth - newCx) \ 2, (Me.ScaleHeight - newCy) \ 2, newCx, newCy
' ^^ drawing from center of form. If not desired, simply change to 0,0 or whatever coords you want
Set cRenderer = Nothing
End Sub
You may be interested in my Alpha Image Control, linked in my signature below. It takes the idea of this project and combines it into a usercontrol that can produce very professional results, flicker-free...
If using my Alpha Image Control, you can load an image into the control during form design.
Then the code would be as simple as...
Code:
Private Sub Form_Load()
AlphaImgCtl1.Aspect = lvicScaled
' if wanting to draw the image from top/left of the control vs. centered within, then unrem following line
'AlphaImgCtl1.AlignCenter = False
'^^ both above properties can be set during design time & not required to be set here in Form_Load
End Sub
Private Sub Form_Resize()
AlphaImgCtl1.Move 0, 0, Me.ScaleWidth, Me.ScaleHeight
End Sub
-
Re: [VB6] GDI+ Usage & Samples
thanks on the code, it was very helpful, on Alpha Image Control how to put a checkered board on the background just like on the sample on the first page
-
Re: [VB6] GDI+ Usage & Samples
Quote:
Originally Posted by
rojaldearintok
thanks on the code, it was very helpful, on Alpha Image Control how to put a checkered board on the background just like on the sample on the first page
Two immediate options come to play
1) Simply create a checkerboard PNG or jpg/bmp in Paint or something else. Load that image into the control at design time or runtime
2) Runtime: Create your own checkerboard pattern to a hidden picturebox (AutoRedraw=True & sized appropriately) or offscreen memory DC/bitmap. Then send the image to LoadPictureGDIplus(), passing the Picturebox.Image property or memory bitmap handle, depending which method you used.
-
Re: [VB6] GDI+ Usage & Samples
-
1 Attachment(s)
Re: [VB6] GDI+ Usage & Samples
Hi
Today just came across a wired bug. The Class fails to load a 8bit 256color JPEG as soon it is compiled. In the IDE it works fine.
After some debugging, I found that the variable Depth in pvCreateSourcelessImage is reseted to 0 by the following Line when running as .exe and not in die IDE:
Code:
CopyMemory BHI.bmiColors(0), pal(8), .biClrImportant
This makes then GdipBitmapLockBits fail.
The work around is to make a copy of the variable Depth and to use this in GdipBitmapLockBits.
Below the modified function:
Code:
Private Function pvCreateSourcelessImage(hImage As Long, Width As Long, Height As Long, flipFlag As Long) As Long
' function creates a stand-alone GDI+ image from a linked image
' The routine here follows the instructions given at this MS KB article link
' http://support.microsoft.com/kb/814675
Dim tSize As RECTF, tSizeI As RECTI, BHI As BITMAPINFO
Dim tBMPsrc As BitmapData, tBMPdst As BitmapData
Dim cRender As cGDIpRenderer, pal() As Byte
Dim oldImage As Long, newImage As Long, hObj As Long
Dim dDC As Long, tDC As Long, Depth As Long, hGraphics As Long, dibPtr As Long
Dim DepthSave As Long
If Width = 0& Or Height = 0& Then ' get size of image
Call GdipGetImageBounds(hImage, tSize, UnitPixel)
Width = tSize.nWidth: Height = tSize.nHeight
End If
Call GdipGetImagePixelFormat(hImage, Depth)
DepthSave = Depth 'Save as CopyMemory BHI.bmiColors(0), pal(8), .biClrImportant resets Depth to 0
Select Case Depth
Case PixelFormat1bppIndexed, PixelFormat4bppIndexed, PixelFormat8bppIndexed
With BHI.bmiHeader
If Depth = PixelFormat1bppIndexed Then
.biBitCount = 1
ElseIf Depth = PixelFormat4bppIndexed Then
.biBitCount = 4
Else
.biBitCount = 8
End If
Call GdipGetImagePaletteSize(hImage, .biClrImportant)
ReDim pal(0 To .biClrImportant + 7&)
GdipGetImagePalette hImage, pal(0), .biClrImportant
CopyMemory BHI.bmiColors(0), pal(8), .biClrImportant ' Does reset Depths to 0 when running compiled
.biClrImportant = .biClrImportant \ 4&
.biClrUsed = 2 ^ .biBitCount
Erase pal()
End With
Case PixelFormat24bppRGB, PixelFormat16bppGrayScale, PixelFormat16bppRGB555, PixelFormat16bppRGB565, PixelFormat48bppRGB
BHI.bmiHeader.biBitCount = 24
Case Else
Set cRender = New cGDIpRenderer
cRender.AttachTokenClass m_Token
oldImage = m_Image(m_SourceIndex) ' using the cGDIpRenderer class
m_Image(m_SourceIndex) = hImage ' which requires this class to reflect correct image handle
hGraphics = cRender.CreateGraphicsFromImageClass(Me) ' create a generic graphics object
If hGraphics Then ' then create new GDI+ bitmap
If GdipCreateBitmapFromGraphics(Width, Height, hGraphics, newImage) = 0& Then
cRender.DestroyHGraphics hGraphics
Call GdipGetImageBounds(hImage, tSize, UnitPixel)
m_Image(m_SourceIndex) = newImage ' create graphics object around new bitmap
hGraphics = cRender.CreateGraphicsFromImageClass(Me)
If hGraphics Then ' draw the passed bitmap onto the new bitmap & clean up
Call cRender.RenderToHGraphics(hImage, hGraphics, 0&, 0&, Width, Height, tSize.nLeft, tSize.nTop, tSize.nWidth, tSize.nHeight)
cRender.DestroyHGraphics hGraphics
If flipFlag Then GdipImageRotateFlip newImage, flipFlag
pvCreateSourcelessImage = newImage
End If
Else
cRender.DestroyHGraphics hGraphics
End If
End If
m_Image(m_SourceIndex) = oldImage
End Select
If BHI.bmiHeader.biBitCount Then ' handle paletted & 24bit bitmaps here
tSizeI.nHeight = Height: tSizeI.nWidth = Width
With BHI.bmiHeader
.biHeight = Height
.biPlanes = 1
.biSize = 40
.biWidth = Width
If .biBitCount = 24 Then
If GdipBitmapLockBits(hImage, tSizeI, ImageLockModeRead, PixelFormat24bppRGB, tBMPsrc) Then .biBitCount = 0& 'flag meaning failure
Else
If GdipBitmapLockBits(hImage, tSizeI, ImageLockModeRead, DepthSave, tBMPsrc) Then .biBitCount = 0& 'Use DepthSave as Depth is 0 here when running compiled.
End If
End With
If BHI.bmiHeader.biBitCount Then
dDC = GetDC(GetDesktopWindow())
hObj = CreateDIBSection(dDC, BHI, 0&, dibPtr, 0&, 0&)
ReleaseDC GetDesktopWindow(), dDC
If hObj Then
CopyMemory ByVal dibPtr, ByVal tBMPsrc.Scan0Ptr, tBMPsrc.Stride * Height
GdipBitmapUnlockBits newImage, tBMPsrc
GdipCreateBitmapFromHBITMAP hObj, 0&, newImage
DeleteObject hObj
GdipImageRotateFlip newImage, (flipFlag Xor 6&)
pvCreateSourcelessImage = newImage
End If
End If
End If
GdipDisposeImage hImage
End Function
I'm not sure what is causing this behavior. Attached the Image that triggers this bug.
Kind regards
Beat