this is my first question on here.
I'm trying to build a dial control as a custom user control in VB.NET. I'm using VS2008.
so far I have managed to rotate image using graphics.rotatetransform . however, this rotate everything. Now I have a Bitmap for the dial which should stay stable and another Bitmap for the needle which I need to rotate.
so far i've tried this:
Dim gL As Graphics = Graphics.FromImage(bmpLongNeedle)
gL.TranslateTransform(bmpLongNeedle.Width / 2, bmpLongNeedle.Height * 0.74)
gL.RotateTransform(angleLongNeedle)
gL.TranslateTransform(-bmpLongNeedle.Width / 2, -bmpLongNeedle.Height * 0.74)
gL.DrawImage(bmpLongNeedle, 0, 0)
As I understand it, the image of the needle should be rotated at angle "angleLongNeedle" although i'm placing the rotated image at 0,0. However, the result is that the Needle doesn't get drawn on the control.
any pointers as to where I might be going wrong or something else I should be doing?
Thanks in advance
First of all, why do you allocate the Graphics object from a bitmap that you then proceed to draw onto the graphics? That doesn’t make sense.
Dim gL As Graphics = Graphics.FromImage(bmpLongNeedle)
' … '
gL.DrawImage(bmpLongNeedle, 0, 0)
What you probably want is a graphics context for the whole image. You then apply the transformations to it and finally draw the bmpLongNeedle image.
Secondly, your translations look inversed: in the first step, you need to move the image to the origin (0, 0); then you rotate it, and then move it back. So the transformation should look like this:
gL.TranslateTransform(-bmpLongNeedle.Width * 0.5, -bmpLongNeedle.Height * 0.5)
gL.RotateTransform(angleLongNeedle)
gL.TranslateTransform(bmpLongNeedle.Width * 0.5, bmpLongNeedle.Height * 0.5)
Notice the inversed order of the TranslateTransforms. Also, why did you translate by 0.74 times the height, instead of half?
oh the bitmap for needle has the pivot point at 0.74 * height.
may be I should have posted this before. but this is what i've done.
Public Class Altimeter
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim bmpBezel As New Bitmap("{path}\Altimeter_Background.bmp")
Dim bmpLongNeedle As New Bitmap("{path}\LongNeedle.bmp")
Dim rect2 As New Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width, e.ClipRectangle.Height)
'make transparent
bmpBezel.MakeTransparent(Color.Yellow)
bmpLongNeedle.MakeTransparent(Color.Yellow)
Dim angleLongNeedle As Single = (Altitude / 50) * 360
'draw bezel
e.Graphics.DrawImage(bmpBezel, rect2)
'rotate long needle
Dim gL As Graphics = Graphics.FromImage(bmpLongNeedle)
gL.TranslateTransform(bmpLongNeedle.Width / 2, bmpLongNeedle.Height * 0.74)
gL.RotateTransform(angleLongNeedle)
gL.TranslateTransform(-bmpLongNeedle.Width / 2, -bmpLongNeedle.Height * 0.74)
gL.DrawImage(bmpLongNeedle, 0, 0)
MyBase.OnPaint(e)
End Sub
i use e.graphics.drawimage to paint the whole image. i don't really understand what you said about having graphics object for all the images and then drawing the needle? do you have any pseudo code?
thanks
Related
I have a Picturebox which I draw a view to (Gantt View in this case) and it works OK - i.e., the view is drawn and the AutoScroll property allows the image in the PictureBox to be smoothly scrolled.
My problem is, the header of the image (e.g., the date headers in this case) scroll off the top of the display when I scroll down the image.
What I can't work out is how to fix a header to the top. I thought about simply drawing a header into another Picturebox, but then I am not sure how to sync the header with the left-right scrolling of the main PictureBox
Can someone suggest the best approach to handling this, or do I need to revert to doing a direct draw and handle the scrolling myself?
I am using VB with VS 2015.
Many thanks
Phil
Updated - I am now using an off-screen Bitmap, but can someone look at the code below and let me know if there is a faster/better way to do this? It all works, but still learning and so always looking to do things the best way
Public Sub MoveViewPoint(G As Graphics)
' G passed in from controls Paint
G.Clear(Color.WhiteSmoke)
' _Plan is off-screen bitmap of image
' _HeaderHeight is height of the Header area in _Plan
Dim Header_src_rect As New Rectangle(_HScroll.Value, 0, _Plan.Width, _HeaderHeight)
Dim Header_dst_rect As New Rectangle(0, 0, _Plan.Width, _HeaderHeight)
G.DrawImage(_Plan, Header_dst_rect, Header_src_rect, GraphicsUnit.Pixel)
Dim src_rect As New Rectangle(_HScroll.Value, _HeaderHeight + 1 + _VScroll.Value, _Plan.Width, _Plan.Height)
Dim dst_rect As New Rectangle(0, _HeaderHeight + 1, _Plan.Width, _Plan.Height)
G.DrawImage(_Plan, dst_rect, src_rect, GraphicsUnit.Pixel)
_HScroll.LargeChange = G.ClipBounds.Width * 0.9
_VScroll.LargeChange = G.ClipBounds.Height * 0.9
End Sub
I would do all your drawing to an off-screen bitmap using the GDI graphics system. You can draw your headers and the rest of the chart as 2 distinct stages in the same bitmap. You would have to handle the scrolling yourself by watching the MouseMove event and checking the buttons status.
I have a ortho set up at the moment for 2D, when I resize the window it stretches anything that is drawn in the window, is there a way to either just have black bars show when the window is resized or at least maintain the aspect ratio of the contents, so they dont stretch at all. I have tried a few implementations that I have seen on here, but nothing really works.
EDIT: Sorry guys had a bit of a blonde moment
Protected Overrides Sub OnResize(ByVal e As EventArgs)
MyBase.OnResize(e)
GL.Viewport(0, 0, Width, Height)
GL.MatrixMode(MatrixMode.Projection)
GL.LoadIdentity()
GL.Ortho(-1.0, testvalue, testvalue , 1.0, 0.0, 4.0)
End Sub
testvalue at the moment is 5000, window size is 800x800
I figured out what I had to do. Rather than utilizing the aspect ratio on the Ortho part, I used it on the view port as such:
Dim ar As Single = Width/Height
GL.Viewport( 0, 0, 800 * ar, 800 * ar)
This prevents all stretching and simply places a black bar on the right hand side when the width is greater than height.
When you resize, Windows creates its own message pump to handle events, bypassing your message pump. There are work-arounds (hacks) to get it to render whilst sizing, including running your update/rendering on a thread. Note that this is a problem for D3D as well as OpenGL.
There's a discussion here on an old Gamedev thread.
the ortho command comprises of: the following parameters: left, right, bottom, top, zNear, zFar further info: http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml
You will want to plug in your 'Width' into right and your 'Height' into bottom and ensure the Width and Height values reflect the new window size.
E.g.:
GL.Viewport(0, 0, Width, Height)
GL.MatrixMode(MatrixMode.Projection)
GL.LoadIdentity()
GL.Ortho(0.0, Width, Height, 0.0, 1.0, 2.0)
Since you're only after 2D your zNear and zFar can be quite small, just make sure to render between zNear and zFar and do not use 0.0 as your zNear, I would recommend using 0.1 or larger for your zNear.
I am attempting to overlay two transparent images within a winform, but it keeps rendering the form's background image behind the top transparent image, as opposed to the second image...
My basic set up is I have two panels and in each panel is a picturebox. Each image in the picture boxes have some transparent areas. I've set the BackColor of the panels to color.transparent.
When I make one panel overlay the other I'm seeing the form's backcolor come through as opposed to the underlaying image.
Am I missing a property that I can set?
You only need one picture box. The overlay can be done with graphics.
Imports System.Drawing
Dim OverlayImage As New Bitmap("Some Path", True)
Dim BackImage As New Bitmap("Some Path", True)
g As Graphics = Graphics.FromImage(BackImage)
g.DrawImage(OverlayImage, 0, 0)
pictureBox1.Image = BackImage
If you want to have the timer move the overlayed image, then first, make a variable Dim posX As Integer = 0
then use g.DrawImage(OverlayImage, posX, 0) Now when your timer ticks, increment posX by 10
Here's a complete function to overlay two images (adapted from Blue0500's answer):
''' <summary> Return a new image with one superimposed over the other. </summary>
Function OverlayImgs(ByVal BackgroundImg As System.Drawing.Bitmap, ByVal OverlayImg As System.Drawing.Bitmap, Position As System.Drawing.Point) As System.Drawing.Bitmap
Dim g = System.Drawing.Graphics.FromImage(BackgroundImg)
g.DrawImage(OverlayImg, Position)
Return BackgroundImg
End Function
Usage:
lblTest.Image = OverlayImgs(Img1, Img2, New Point(16, 16))
You don't need a PictureBox for this unless you are using it for the canvas. You can draw all images to a Rectangle structure and move them around. Personally I would create a class object that has a Rectangle, Image and other properties and hold them in a collection. Then you simply draw the class objects by there properties including location. If there images contain transparencies they will overlay for you. This also gives you a method to check for collisions via the Rectangle.IntersectsWith function.
I am creating program with multileyered picturebox, the image of picturebox is update dynamically from bitmap in memory, and i want to clear the selected part on bitmap to transparent color so i can see image of picturebox behind it.
Here is my code
Dim gBmp As Graphics = Graphics.FromImage(GraphLayer(LayerArray))
Dim TileSrcCrop As New Rectangle(nVal(xTile), nVal(yTile), TileSize, TileSize)
Dim TileDrawSize As New Rectangle(nVal(H), nVal(V), TileSize, TileSize)
gBmp.DrawImage(GraphImage(LayerArray), TileDrawSize, TileSrcCrop, GraphicsUnit.Pixel)
PicMap(LayerArray).Image = GraphLayer(LayerArray)
Thanks
What I'm about to say may only be for Windows icons, but IIRC, the top left pixel must be assigned the color that you want to designate as the color for transparency. You'll usually see MS use Magenta (255, 0, 255). This is called a transparency mask. Then, anywhere you want the color to be transparent, you use the color you placed in the top left pixel.
HTH -- and let me know if it's only for icons, but I think it's for Windows bitmap files, too.
I toying with a project where I can position objects runtime on the screen. I also have an small "preview"-window, where I want to see the outline of the objects.
I have an object array who have size and location on the "scene". Now I want to loop through my scene-objects and create one picturebox in the preview-window for each object in the "scene".
So, I have the size of the "Scene" and an array of the objects that are visible on the scene. Every object have an size and location mapped to the scene.
I also have a preview window that have an dynamic size. How do I create a preview that scales down to the preview window? To make it easy, I loop through the scene objects in the scene and create a picturebox on the preview-form for each object, that uses the preview-forms size to do the scaling.
SceneSize=new size(800,600)
PreviewSize=new size(80,60)
For each obj as SceneObject in Scene
dim p as new picturebox
p.size=?? scaling here (we have obj.size and obj.location)
p.location=?? scaling here (we have obj.size and obj.location)
previewWindow.controls.add (p)
next
Any example in .Net would be nice.
Thanks.
Bob Powell's site on GDI+ might have some useful information on how to proceed. Check out his page on Matrix Transformations. This article might provide you with some good ideas on how to proceed
Bob Powell GDI+ Transformations
This did the trick, it wasn't harder than that.
Function GetPreviewRectangleForObject(ByVal ScreenSize As Size, ByVal PreviewSize As Size, ByVal OriginalRect As Rectangle) As Rectangle
Dim xFactor As Integer = ScreenSize.Width / PreviewSize.Width
Dim yFactor As Integer = ScreenSize.Height / PreviewSize.Height
With OriginalRect
Return New Rectangle(.X / xFactor, .Y / yFactor, .Width / xFactor, .Height / yFactor)
End With
End Function