Results 1 to 6 of 6

Thread: Multiple graphics images on one picture box

  1. #1

    Thread Starter
    New Member
    Join Date
    Jun 2016

    Multiple graphics images on one picture box

    Hi, Any help is appreciated.

    I what to make an application where i can add multiple images (from a png file) in certain positions on a picturebox at runtime.

    I understand how to create a graphics object and this graphic is displayed in the picturebox.

    What is the best way to do this if i want to add one image then place another one of the same but rotated 45 degrees, then another rotated a bit more etc... all on the same picturebox, and in the position i click.

    Do i use one 'master' graphics object which is what the picture box is showing.

    Then when i add a new rotated image, i create a temp graphics object containing the image, do the rotate on that object.. and then somehow add this to the 'master' graphics object.. which is then displayed in the picture box.

    At times i may want to remove one of the images added and add a different one.

    Any help would be great,
    Thanks, Simon.

  2. #2
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008

    Re: Multiple graphics images on one picture box

    You can do all those things using Graphics commands in the Paint Event handler of the PictureBox. There's a lot to learn about using Graphics commands, so if you haven't done it before it would be worth looking for a tutorial (I think DDay has one in this forum's CodeBank). To give you an idea of what the code might look like:
       Dim boa As Image = Image.FromFile(filename1)
       Dim cat As Image = Image.FromFile(filename2)
       Dim dog As Image = Image.FromFile(filename3)
       Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
          Dim g As Graphics = e.Graphics
          'draw boa at position (15, 20)
          g.DrawImage(boa, 15, 20)
          'draw cat at position 50,60 at twice actual size:
          g.DrawImage(cat, 50, 60, cat.Width * 2, cat.Height * 2)
          'draw dog rotated 37 degrees around point 100,100:
          g.TranslateTransform(100, 100)
          g.TranslateTransform(-100, -100)
          g.DrawImage(dog, 100 - dog.Width \ 2, 100 - dog.Width \ 2)
       End Sub

  3. #3
    Sinecure devotee
    Join Date
    Aug 2013
    Southern Tier NY

    Re: Multiple graphics images on one picture box

    You don't draw on a graphics object, you use a graphics object to draw on something.
    You probably want to use a number of bitmaps, and use a graphics object created for that bitmap to draw your src images (or just load them into bitmaps from the file).
    Then, wherever you plan to draw that image, rotated, scaled, etc. you would have a graphics object for the thing you're going to draw on, setup the matrix to rotate, move and scale the coordinate system (that is the way I think of it) and then draw the image.
    If you want to remove it later, you just redraw your image, not drawing the one that you don't want, so it is "removed".

    There is an example I wrote some time back (it is in VB 2005, although written in late 2011 which is about a year after I started messing with VB.Net). I checked my previous attachments, and it looks like I haven't posted it before.
    It is not directly related to your situation, but was written as a kind of tutorial for a co-worker, and I wrote the original version of it in C#, even though I don't work in C# (then or now). The reason I wrote it is because he saw some other type graphic work I had done in VB.Net and wondered it he could do the same thing in C#. I said I'm sure he could since it is all done with the same Managed GDI+ access in either language. So, this example (now in VB) was pretty much the first C# program I ever wrote, and is still basically the only C# program I have written. It was about a week after writing the C# version that I figured a VB.Net version of it would also be handy, so wrote what is attached here.

    The part I though made it unique as a "tutorial" type example, is that after the primary drawing example was done, I replicated the code and added a "step" mode, so that you can step through the drawing process a little bit at a time, while the program prints some information about what it is doing at that step, and shows the line or two of code that is being executed at that step. Also, even as you are "stepping" through the code, at any given step, the user interface to the drawing is still active, so you can manipulate the panning, zooming, rotation, etc... and see how those inputs affect the drawing up to that step.

    There are a number of user inputs. Below the image is a plain picturebox that is used as a dragging surface.
    If you drag on it with the left mouse button it rotates the horizon angle (the roll of an aircraft) and the pitch.
    If you drag on it with the right mouse button, it scales the image up and down in size. If you scale it down, you can see how the horizon is always drawn as two large rectangle. And when you look at the code, you will see the coordinates and drawing of those two rectangles is always the same. You don't change how you draw the rectangles, you change the environement (the coordinate system is rotated, panned and scaled by the matrix, not the points you use to do the drawing).

    That may not be clear, which is why I included the step mode to try to interactively explain how I view the drawing process each step of the way.

    If you drag on the image itself, the left button will allow panning the drawing (moving the center of rotation to some other area of the visible area.
    If you drag with the right button you change the size of the picturebox the image is being drawn into.

    Pressing the Reset button gets the scale and offsets back into their original values.
    It doesn't address your immediate desire, but I think it could still be interesting all the same and you might learn something related.

    Perhaps something a little closer would be the CardGame from post #12 of this thread, which implements the drawing of the cards by dragging and moving images around. Of course it doesn't do any scaling or rotation and if I had the time, I could put a custom example tailored to your situation, but I don't really have it at the moment.
    Attached Files Attached Files
    Last edited by passel; Jun 10th, 2016 at 06:02 PM.

  4. #4

    Thread Starter
    New Member
    Join Date
    Jun 2016

    Re: Multiple graphics images on one picture box

    Hi all, thanks very much for the messages, all very helpful.

    I have got the code to rotate the image on each click now and place it on the panel where i click... to do this i have declared a tmp image, tmp bitmap and tmp graphics object. I don't know if there is a better way to do this but this seems to work.

    In my code you will see i made the bitmap twice the size on the original image, this is because a rotated image width and height is larger than a non rotated image.

    tmp_modifying_image = Instance_Struct_Images.TheImage.Clone()
    tmp_modifying_bitmap = New Bitmap(ImageSizeXY * 2, ImageSizeXY * 2)
    tmp_modifying_graphics = Graphics.FromImage(tmp_modifying_bitmap)
    tmp_modifying_graphics.TranslateTransform(ImageSizeXY / 2, ImageSizeXY / 2)
    tmp_modifying_graphics.TranslateTransform(-ImageSizeXY / 2, -ImageSizeXY / 2)
    tmp_modifying_graphics.DrawImage(tmp_modifying_image, draw_posxy, draw_posxy) ', ImageBounds.Width, ImageBounds.Height)
    Instance_Struct_Images.TheImage = tmp_modifying_bitmap.Clone()




    If you have any better suggestions that would be great. At the min i make the bitmap x2 bigger so the rotated image will fit in it... Is there a way after rotating, to resize the bitmap back to the size of the rotate image in it?? I guess cropping to the bounds of the image would be the way??

    Many thanks,

  5. #5
    Sinecure devotee
    Join Date
    Aug 2013
    Southern Tier NY

    Re: Multiple graphics images on one picture box

    Cropping to the bounds of the image is difficult, because how do you know what they are?

    Depending on what you need, the "normal" method would not be to rotate your source image and store it in your list, but just keep track of the rotation, and do the rotation when you draw it (the original image is never touched, or a copy of it is never touched).
    That is what boops boops was showing. The rotation was done when you draw the image wherever you are going to draw it.

    If the rotations are going to be fairly static, i.e. you set them once and use them for a long time with that orientation, then it will probably save some drawing time, so if you are going to be drawing a lot of images, and refreshing often, then perhaps it makes sense to keep multiple copies of the image, pre-rotated, in a list, but each bitmap will take up a fair amount of memory.

    In a traditional environment where you might one the same image used multiple times with different rotations you would just keep the one source image and draw it rotated where needed. (I'm repeating myself here). In that case you don't have to worry about having a larger (i.e. 2x) image to hold a rotated version of the image as you will be rotating it when drawing on your larger display area.

    That aside, I've never liked the idea of continuously modifying an image, reusing the results of the previous processing as input to the next processing.
    You may not be modifying the same image enough times currently to see any degradation, but basically,
    1. You grab the image from Instance_Struct_Images.TheImage.
    2. You rotate the image, which by the nature of bitmap graphics, means that some pixels will have to change color slightly since the pixels won't map one to one compared to the unrotated image.
    3. You set Instance_Struct_Images.TheImage to the result.

    If you do this repeatedly, the pixels color changes will accumulate and be noticeable as distortions or noise artifacts in the image in short order.
    You would normally want a "master" that you don't modify, to be the input to any transformations, and use the output from that set of transformations for display. If you need to transform the image again, you would accumulate the "transformations", then apply those to the original master image to get the new output. You never modify your original image and reuse it, unless you're going for some kind of modern art image distortion effect.

    By the way, the ideal size the picturebox should be increased (assuming you have meaningful pixels going all the way to the edge of the corners of the picturebox) is the square root of two larger (i.e. 1.4142135....). If you took your source image, and centered it in a bitmap that was that much larger (probably plus 1 or 2 pixels for an edge margin), then you would have a source image already sized to be rotated without loosing any pixels, and I wouldn't worry about trying to crop it any smaller. The pixels that are not part of the "image", i.e background pixels, should have their alpha values set to 0, so they don't show up when you draw the image anyway, so cropping the image is essentially a wasted exercise.
    Last edited by passel; Jun 13th, 2016 at 02:54 PM.

  6. #6
    Sinecure devotee
    Join Date
    Aug 2013
    Southern Tier NY

    Re: Multiple graphics images on one picture box

    I meant to post another example of some code that was generated in regards to another thread on this forum.
    This example I don't think was entirely relevant to the question, but was a "fun" extension to the example.
    The question had to do with rotating an image around its center, and the code answered that, using the mouse to change the rotation, but then I extended the code since it already had the capability to rotate one instance of the object, to chain a number of the objects together, and rotate each based on where it was and where its hookup connected to the hitch of the previous object in the chain. The first object follows the mouse when dragging, and all the other objects are "dragged" along in a chain.
    Fairly simple code, but at the same time somewhat fun.

    I think this is a better example (closer to what you are trying to do), compared to the previous one.
    Despite the name, it is not a game. The images came from the original poster, and the "truck" refers to the image being dragged which look like the trucks that hold the wheels that support the cars of a train.

    The technique is not to update the drawing in the paint event (which should work equally well actually), but to draw to a bitmap and invalidate the form periodically, and the paint event just draws the bitmap to the form. I believe this is "safe", because you can't be in a timer event and a paint event at the same time, so you should not be in the middle of drawing when a paint event occurs.
    That said, if the drawing was done in the paint event, there would be less resources involved and would look the same but actually be quicker, I think, overall although since the timer is regulating the update speed, it should look no different to the user. It would remove a few lines of code, and there would be less drawing for the most part.

    The changes are simple enough, I'll just post the code here, which should be able to replace all the code in zip version if you want to see it done the way that boops boops described.
    Option Strict On
    Imports System.Math
    Public Class Form1
      Private background As New Bitmap(My.Resources.Background)
      Private TruckPosition As New Point(500, 500)      'Truck's Position (where center of truck should be in the game)
      Const NumTrucks = 5
      Private Trucks(NumTrucks) As TruckClass
      Private Class TruckClass
        Public Shared Rad2Deg As Double = 180 / Math.PI
        Public Shared TruckCenterOffset As New Point(-107, -31) 'Offset from Center to topleft corner of truck bitmap
        Public Shared truck As New Bitmap(My.Resources.Truck)
        Public Pos As Point
        Public Ang As Single
        Public Hitch As Point
        Public Sub New()
          TruckClass.truck.MakeTransparent() 'Not saved with transparency, change background color (color at 0,0) transparent
        End Sub
        Public Sub draw(g As Graphics)
          Dim gc As Drawing2D.GraphicsContainer = g.BeginContainer()  'save graphics State
          g.TranslateTransform(Pos.X, Pos.Y)           'Translate to where we want to draw the truck
          g.RotateTransform(Ang)                       'Rotate the coordinate system
          g.DrawImage(truck, TruckCenterOffset)        'Draw the truck
          g.EndContainer(gc)                           'Restore graphics State
        End Sub
        Public Sub drag(dragPos As Point)
          Dim RadRotation As Double
          RadRotation = Math.Atan2(dragPos.Y - Pos.Y, dragPos.X - Pos.X) 'determine the angle between truck center and drag point
          Ang = CSng(RadRotation * Rad2Deg)        'Set the trucks rotation to that angle.
          Pos.X = dragPos.X - CInt(100 * Math.Cos(RadRotation)) 'reposition the truck's center 100 pixels distance
          Pos.Y = dragPos.Y - CInt(100 * Math.Sin(RadRotation)) 'from the drag point(mouse position) along that angle
          Hitch.X = dragPos.X - CInt(200 * Math.Cos(RadRotation)) 'The hitch position is 200 pixels away from the drag position
          Hitch.Y = dragPos.Y - CInt(200 * Math.Sin(RadRotation)) 'i.e. the drag is from the front, the hitch is at the back
        End Sub
      End Class
      Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        DoubleBuffered = True  'Don't want form flashing during update
        WindowState = FormWindowState.Maximized
        Trucks(0) = New TruckClass
        Trucks(0).Pos = TruckPosition
        Trucks(0).drag(New Point(TruckPosition.X + 100, TruckPosition.Y))
        For i = 1 To NumTrucks
          Trucks(i) = New TruckClass
          Trucks(i).drag(Trucks(i - 1).Hitch)
        Timer1.Interval = 10            'Have the form update itself at regular intervals
      End Sub
      Private Sub DrawGameSurface(_gameGraph As Graphics)
        _gameGraph.DrawImage(background, 0, 0, ClientSize.Width, ClientSize.Height)  'Draw the backtground in the bitmap to clear old truck position
        For Each truck As TruckClass In Trucks
      End Sub
      Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
        Invalidate()       'Have the form update the screen
      End Sub
      Private Sub Form1_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        If e.Button = Windows.Forms.MouseButtons.Left Then
          For i = 1 To NumTrucks
            Trucks(i).drag(Trucks(i - 1).Hitch)
        End If
      End Sub
      Private Sub Form1_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
      End Sub
    End Class
    You might want to compare the two to see if you understand the differences between the two approaches.

    p.s. I also noticed that the original zip file had an undefined reference because I removed a bitmap from the project, which didn't cause a failure for me originally since I was in the IDE when I removed it (it was already in my resources), but when I reloaded the project, and tried to run, then the missing file backing the resource was not found. I reassigned the reference to the other background bitmap in the resources that I wanted to use, and updated the attached zip file.
    Attached Files Attached Files
    Last edited by passel; Jun 17th, 2016 at 12:44 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts


Click Here to Expand Forum to Full Width