none
Out of Memory When Rotating Bitmap Iamge RRS feed

  • Question

  • Public Class Form1
    
        Private normalImage As Image = New Bitmap(Image.FromFile("T:\..."))
    
        Private Sub rotateDialBttn_Click(sender As Object, e As EventArgs) Handles rotateDialBttn.Click
    
            Dim newImage As Image = Me.RotateImage(normalImage, Convert.ToInt32(degreeTxt.Text))
            imgBox.Image = newImage
    
        End Sub
    
        Private Function RotateImage(ByVal image As Image, ByVal angle As Single) As Bitmap
    
            If image Is Nothing Then
                Throw New ArgumentNullException("image")
            End If
    
            Dim upperLeftDrawPoint As Point = New Point(0, 0)
            Dim imageCenterOffset As Point = New Point(image.Width / 2, image.Height / 2)
    
            'create new empty bitmap to hold rotate image
            Dim rotatedBmp As Bitmap = New Bitmap(image.Width, image.Height)
            rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution)
    
            'make graphics object from the empty bitmap
            Dim g As Graphics = Graphics.FromImage(rotatedBmp)
    
            'put rotation point in center of image
            g.TranslateTransform(upperLeftDrawPoint.X + imageCenterOffset.X, upperLeftDrawPoint.Y + imageCenterOffset.Y)
    
            'rotate image
            g.RotateTransform(angle)
    
            'translate back to original positioning
            g.TranslateTransform((upperLeftDrawPoint.X + imageCenterOffset.X) * -1, (upperLeftDrawPoint.Y + imageCenterOffset.Y) * -1)
    
            'draw passed in image onto graphics object
            g.DrawImage(image, upperLeftDrawPoint)
            g.ResetTransform()
    
            Return rotatedBmp
    
        End Function
    End Class
    I am having an issue understanding exactly how I should accomplish a specific task using Bitmaps and images. I have a picturebox that has an image of a dial in my form as well as a textbox and button. Basically as it is now the button rotates the image in the picture box based on the degrees inside the textbox. All of the rotating seems to be working correctly but the issue comes in when I hit the rotate button enough times I get an out of memory issue. How do I get around this problem. I've been looking and reading up on the Dispose() method but wherever I have tried to use it does not help. If anyone can help me or explain to me how to use the Dispose method correctly it would be greatly appreciated.
    Wednesday, June 20, 2018 7:07 PM

Answers

  • Probably over 100.  I realize its a lot but theoretically I would like to make sure if there are any memory issues I can solve that now before the code gets more complicated as I am going to have it rotating quite a lot to correspond to a motor device that I will be using. 

    how many mb is your image?

    You must need to dispose of the new images you are making. Try something like this.

    Dim newImage As Image = Me.RotateImage(normalImage, Convert.ToInt32(degreeTxt.Text))

    Dim tmp As Image = imgBox.Image imgBox.Image = Nothing imgBox.Image = newImage tmp.Dispose()


    Wednesday, June 20, 2018 9:24 PM
  • As noted, you aren't disposing your GDI objects... both the old image and the graphics instance used to create the new one must be disposed.  You'll need to keep track of the first time the image is changed so that you are only disposing the new rotated image and not the original.  You can also simply your drawing code.  Example:

    Private normalImage As Image = New Bitmap(Image.FromFile("T:\..."))
    
    Private Sub rotateDialBttn_Click(sender As Object, e As EventArgs) Handles rotateDialBttn.Click
        Static firstClick As Boolean
        Dim newImage As Image = Me.RotateImage(normalImage, Convert.ToInt32(degreeTxt.Text))
        If firstClick Then
            Dim oldImage = imgBox.Image
            imgBox.Image = Nothing
            oldImage.Dispose()
        Else
            firstClick = True
        End If
        imgBox.Image = newImage
    End Sub
    
    Private Function RotateImage(ByVal image As Image, ByVal angle As Single) As Bitmap
        If image Is Nothing Then Throw New ArgumentNullException("image")
        Dim imageCenterOffset As Point = New Point(image.Width / 2, image.Height / 2)
    
        'create new empty bitmap to hold rotate image
        Dim rotatedBmp As Bitmap = New Bitmap(image.Width, image.Height)
        rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution)
    
        'make graphics object from the empty bitmap
        Using g As Graphics = Graphics.FromImage(rotatedBmp)
            'put rotation point in center of image
            g.TranslateTransform(imageCenterOffset.X, imageCenterOffset.Y)
            'rotate image
            g.RotateTransform(angle)
            'translate back to original positioning
            g.TranslateTransform(-imageCenterOffset.X, -imageCenterOffset.Y)
            'draw passed in image onto graphics object
            g.DrawImage(image, Point.Empty)
            g.ResetTransform()
        End Using
    
        Return rotatedBmp
    End Function
    


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    • Marked as answer by SKBone Thursday, June 21, 2018 1:26 PM
    Thursday, June 21, 2018 12:07 PM
    Moderator

All replies

  • Hi

    I have tried your code and cannot get it to throw an exception. Maybe upwards of 60 rotations tried.

    How many times did you try before getting an exception?


    Regards Les, Livingston, Scotland

    Wednesday, June 20, 2018 8:11 PM
  • Probably over 100.  I realize its a lot but theoretically I would like to make sure if there are any memory issues I can solve that now before the code gets more complicated as I am going to have it rotating quite a lot to correspond to a motor device that I will be using. 
    Wednesday, June 20, 2018 8:24 PM
  • Hi

    OK, I added a Timer and let it run for over 4100 rotations without issue.

    Here is the code I used (all yours other than the Timer code)

    There was one alteration I made in your code to use Integer division (I doubt it was responsible for a memory issue?):

        Dim imageCenterOffset As Point = New Point(image.Width \ 2, image.Height \ 2)
    

    Option Strict On
    Option Explicit On
    Public Class Form1
      Private normalImage As Image = New Bitmap(Image.FromFile("C:\Users\lesha\Documents\VB Resources\Images\Architecture XI.jpg"))
    
      Private Sub rotateDialBttn_Click(sender As Object, e As EventArgs) Handles rotateDialBttn.Click
    
        Dim newImage As Image = Me.RotateImage(normalImage, Convert.ToInt32(degreeTxt.Text))
        imgBox.Image = newImage
    
      End Sub
    
      Private Function RotateImage(ByVal image As Image, ByVal angle As Single) As Bitmap
    
        If image Is Nothing Then
          Throw New ArgumentNullException("image")
        End If
    
        Dim upperLeftDrawPoint As Point = New Point(0, 0)
        Dim imageCenterOffset As Point = New Point(image.Width \ 2, image.Height \ 2)
    
        'create new empty bitmap to hold rotate image
        Dim rotatedBmp As Bitmap = New Bitmap(image.Width, image.Height)
        rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution)
    
        'make graphics object from the empty bitmap
        Dim g As Graphics = Graphics.FromImage(rotatedBmp)
    
        'put rotation point in center of image
        g.TranslateTransform(upperLeftDrawPoint.X + imageCenterOffset.X, upperLeftDrawPoint.Y + imageCenterOffset.Y)
    
        'rotate image
        g.RotateTransform(angle)
    
        'translate back to original positioning
        g.TranslateTransform((upperLeftDrawPoint.X + imageCenterOffset.X) * -1, (upperLeftDrawPoint.Y + imageCenterOffset.Y) * -1)
    
        'draw passed in image onto graphics object
        g.DrawImage(image, upperLeftDrawPoint)
        g.ResetTransform()
    
        Return rotatedBmp
    
      End Function
      Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        degreeTxt.Text = (CInt(degreeTxt.Text) + 5).ToString
        rotateDialBttn.PerformClick()
      End Sub
    End Class


    Regards Les, Livingston, Scotland

    Wednesday, June 20, 2018 8:44 PM
  • Probably over 100.  I realize its a lot but theoretically I would like to make sure if there are any memory issues I can solve that now before the code gets more complicated as I am going to have it rotating quite a lot to correspond to a motor device that I will be using. 

    how many mb is your image?

    You must need to dispose of the new images you are making. Try something like this.

    Dim newImage As Image = Me.RotateImage(normalImage, Convert.ToInt32(degreeTxt.Text))

    Dim tmp As Image = imgBox.Image imgBox.Image = Nothing imgBox.Image = newImage tmp.Dispose()


    Wednesday, June 20, 2018 9:24 PM
  • Hi

    In the test code I used above, it was a 50kb image I was using.


    Regards Les, Livingston, Scotland

    Wednesday, June 20, 2018 9:37 PM
  • Hi

    In the test code I used above, it was a 50kb image I was using.


    Regards Les, Livingston, Scotland

    Try a 4 mb image. Or larger. Like digital photo size. Somewhere around 100 - 500 images or so it will run out. Not sure exactly. Also may depend on the system.


    PS I have not actually tested op's example. Just going from memory.
    Wednesday, June 20, 2018 9:43 PM
  • As noted, you aren't disposing your GDI objects... both the old image and the graphics instance used to create the new one must be disposed.  You'll need to keep track of the first time the image is changed so that you are only disposing the new rotated image and not the original.  You can also simply your drawing code.  Example:

    Private normalImage As Image = New Bitmap(Image.FromFile("T:\..."))
    
    Private Sub rotateDialBttn_Click(sender As Object, e As EventArgs) Handles rotateDialBttn.Click
        Static firstClick As Boolean
        Dim newImage As Image = Me.RotateImage(normalImage, Convert.ToInt32(degreeTxt.Text))
        If firstClick Then
            Dim oldImage = imgBox.Image
            imgBox.Image = Nothing
            oldImage.Dispose()
        Else
            firstClick = True
        End If
        imgBox.Image = newImage
    End Sub
    
    Private Function RotateImage(ByVal image As Image, ByVal angle As Single) As Bitmap
        If image Is Nothing Then Throw New ArgumentNullException("image")
        Dim imageCenterOffset As Point = New Point(image.Width / 2, image.Height / 2)
    
        'create new empty bitmap to hold rotate image
        Dim rotatedBmp As Bitmap = New Bitmap(image.Width, image.Height)
        rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution)
    
        'make graphics object from the empty bitmap
        Using g As Graphics = Graphics.FromImage(rotatedBmp)
            'put rotation point in center of image
            g.TranslateTransform(imageCenterOffset.X, imageCenterOffset.Y)
            'rotate image
            g.RotateTransform(angle)
            'translate back to original positioning
            g.TranslateTransform(-imageCenterOffset.X, -imageCenterOffset.Y)
            'draw passed in image onto graphics object
            g.DrawImage(image, Point.Empty)
            g.ResetTransform()
        End Using
    
        Return rotatedBmp
    End Function
    


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    • Marked as answer by SKBone Thursday, June 21, 2018 1:26 PM
    Thursday, June 21, 2018 12:07 PM
    Moderator
  • Sorry for the propose/unpropose Tommy... just realized that you can't dispose the original image or it won't be reusable... need to track the change and only dispose the rotated images.

    -EDIT-

    Sorry again lol, I guess I assumed that the original image is in the picturebox, but maybe not...  I suppose we both should have tested to see if there was an image assigned.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"


    Thursday, June 21, 2018 12:09 PM
    Moderator
  • Sorry for the propose/unpropose Tommy... just realized that you can't dispose the original image or it won't be reusable... need to track the change and only dispose the rotated images.

    -EDIT-

    Sorry again lol, I guess I assumed that the original image is in the picturebox, but maybe not...  I suppose we both should have tested to see if there was an image assigned.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"


    Hi Reed,

    Well since it is dim new image and then pic.image = that is not disposed yet. It is disposing the old image.

    Then next time through the loop dim new again.

    Or you could clone it and delete the temp.

    PS      More early testing on this.

    Or something like that. You could be right now that I think about it. Need to use

       dim NewImage as new image in my first example I forgot the new. More like this:

            Dim newImage As new Image
            newImage = Me.RotateImage(normalImage, Convert.ToInt32(degreeTxt.Text))
    
            Using tmp As New Bitmap(newImage.Width, newImage.Height)
                tmp = imgBox.Image
                imgBox.Image = newImage.Clone
    
            End Using
    
            newImage.Dispose()

    I think you could leave out the newimage.dispose in this case as it is disposed next time with old picbox image is disposed.

    But need an OP to work on it otherwise I am done. :)

    PS One needs to test it.


    Thursday, June 21, 2018 1:13 PM
  • Thank you to everyone who gave suggestions.  My image was quite large which was why it would only iterate 100 times or so but the issue was with the disposing of the unused Bitmaps.  Thanks again!
    Thursday, June 21, 2018 1:30 PM
  • Thanks! definitely had to do with using the Dispose correctly
    Thursday, June 21, 2018 1:31 PM
  • Thanks! definitely had to do with using the Dispose correctly

    Oh good you are back.

    You should try my second example or show us what you did.

    PS Oh I did not see Reed's example.

    Don't give me these first thing in the morning.

    :)


    Thursday, June 21, 2018 1:47 PM