Here's a code snippet:
Dim BMf As Bitmap
BMf = New Bitmap(PicBox.Width, PicBox.Height)
Call FourierPlot(PlotHeight, PlotWidth, RPMArray, CoefArray, Count, 2, BMf)
Me.PicBox.Image = BMf
BMf.Dispose()
within the subroutine is code such as:
Dim myGraphics As Graphics = Graphics.FromImage(BMf)
Dim myPen As New Pen(colr)
....
myGraphics.DrawLine(myPen, lastx, lasty, temp1, temp2)
....
myPen.Dispose()
myGraphics.Dispose()
The displayed PicBox consists of a boundary rectangle with lines draw from opposing corners (what I presume is an "error image.)
Any suggestions (hopefully VERY simple ones) as to what I'm doing wrong?
You shouldn't call BMf.Dispose(), because the image is going to be used by the picture box.
Update:
OK, small update because this seems to be confusing to the OP and others.
When you say Me.PicBox.Image = BMf you set a reference to the bitmap. Both BMf and PicBox.Image now point to the same object. Thus, you should not dispose it, because then it can't be used anymore. Again - this is not a copy, this is same object.
And about leaks. Removing this line does not cause any memory or other leaks. .NET is managing this for you. As soon as the bitmap will not be needed (which means nobody has a reference to it = your picture box will load another image), it will be unneeded and disposed at the next run of the garbage collector. This is automatic.
You might want to disposed it manually if you really need to care about resource, for example if you would invoked your code and create new bitmaps often, many times. But in this case you probably would be better off forcing garbage collector to run in places, where you can cause some delay to the user. You can do this like this:
GC.Collect()
Related
As a challenge, I am planning to make a game purely in Visual Studio (using Visual Basic) but in the past, I have noted that GDI+ is not the best rendering tool, but for what it is, it's all right.
What settings should I use and not use when working with GDI+? Here are my assumptions:
InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor It would make sense that this option renders the fastest.
PixelOffsetMode = Drawing2D.PixelOffsetMode.Half This seems like it must be used with NearestNeighbor.
CompositingQuality = ? I am not sure about this one.
CompositingMode = ? Again, I can't tell.
SmoothingMode = Drawing2D.SmoothingMode.None Specifying no smoothing mode would probably run the fastest.
SetClip(ClientRectangle) It would make sense to set this as the clip so the graphics would not draw outside of the form or control.
My next point: I've noticed that adding all the graphics code always works the fastest in Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Is there any benefit to doing something like this inside that void: (Creating a temporary bitmap and drawing it onto the screen.)
Using B As New Bitmap(ClientSize.Width, ClientSize.Height, Imaging.PixelFormat.Format32bppPArgb)
Using G As Graphics = Graphics.FromImage(B)
' Draw objects onto the bitmap.
End Using
e.Graphics.DrawImage(B, 0, 0)
End Using
I ask this because I noticed that when creating a bitmap, you can specify a Imaging.PixelFormat. Is there one type of format that renders faster than the rest?
Thank you all in advance for the help.
~Nic
I have the following code which I am using to populate a ImageList from a SQLite database with images stored as blobs.
Public Sub populateImagesStyles()
ShoeImages1.Images.Clear()
StyleImagesLView.Items.Clear()
Dim s As SQLiteDataReader
Dim rcount As Integer = 0
dbLocalQuery = New SQLiteCommand("SELECT id, image FROM tblImages", dbLocal)
s = dbLocalQuery.ExecuteReader()
While s.Read()
rcount += 1
ShoeImages1.Images.Add(CStr(s("id")), byte2img(s("image")))
StyleImagesLView.Items.Add(CStr(s("id")), CStr(s("id")))
End While
s.Close()
Here is the byte2img function...
Public Function byte2img(ByVal imgByte As Byte()) As Image
Dim imgMemoryStream As System.IO.MemoryStream = New System.IO.MemoryStream(imgByte)
byte2img = Drawing.Image.FromStream(imgMemoryStream)
End Function
The database contains over 250 images and this process is completed twice on load to populate two different ImageList, because I need the images displayed at two different sizes.
When the process runs on loading the form, it causes the process to consume between 800MB and 1GB of system memory, unless I manually run the process again from an form control, which seems to trigger garbage collection.
Stepping through the loading process, it is clear that it is the byte2img process that is causing the memory usage to escalate - what is the best way to mitigate this?
Also, if anyone can think of a more efficient process to execute this, i'm all ears. The images have to be stored in the database file because I need to be able to just package the .db file and send it to a remote location at a moments notice, so I can't mess with folders with images.
All help appreciated.
You are creating a lot of memory streams without disposing of them. Try this:
Public Function byte2img(ByVal imgByte As Byte()) As Image
Dim img As Image
Try
Using ms As New MemoryStream(imgbyte)
img = Drawing.Image.FromStream(ms)
End Using ' auto dispose of the MS
Catch ex As Exception
' report possibly bad/missing imgByte()
' resulting in an error in either place
End Try
Return img
End Function
An imprecise way to detect this kind of thing is to watch the HANDLES count in TaskManager.
Ok, I've found a solution/workaround that seems to work - call the PopulateImageStyles sub when a user visits the specific TabPage the ImageList resides on.
For some arbitrary reason, when run this way (as above, when called on the form), the process never proceeds to consume more than 50-60 MB of working memory.
I'll add a Background Worker so that the process can execute without hanging the form.
I have a Sub that is called very frequently, while at the same time a secondary thread change the values in all the Lists it uses. Obviously, that would cause an InvalidOperationException, so I put a .ToList in the For Each loop so it copies the List before looping through it. That didn't work, and I was getting InvalidOperationException: "Collection was modified; enumeration operation may not execute." (and by the way, I don't know why, but this exception is only thrown when running the app outside of VS, making it very difficult to debug. Note that the error still exists when debugging the app on VS, only the exception isn't thrown). Then I tried explicitly copying the List, with no success, the error still persists. This is the faulty Sub:
Private Sub MasmorraPictureBox_Paint(sender As Object, e As PaintEventArgs) Handles MasmorraPictureBox.Paint
Dim c As New Integer
Dim DGridCopy As List(Of Point) = DGrid.ToList
Dim DPointsCopy As List(Of Point) = DPoints.ToList
Dim DGridColCopy As List(Of Brush) = DGridCol.ToList
Dim DPointsTextCopy As List(Of String) = DPointsText.ToList
For Each Point As Point In DGridCopy
e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
e.Graphics.FillRectangle(DGridColCopy(c), Point.X, Point.Y, 35, 35)
c += 1
Next
c = 0
For Each Point As Point In DPointsCopy
e.Graphics.FillEllipse(Brushes.Blue, Point.X - 5, Point.Y - 5, 10, 10)
e.Graphics.DrawString(DPointsTextCopy(c), MyFont, Brushes.Black, Point.X - 5, Point.Y + 10, StrFormat)
c += 1
Next
End Sub
What is the problem?
UPDATE: The problem was already corrected by declaring the copies as .ToList of the original lists. I thought it wasn't because there was still some errors, but as I said previously, the debugger isn't throwing InvalidOperationException's (even though they were ocurring), so I only discovered the error wasn't in this Sub anymore when I ran the app outside of VS in order to discover which line was throwing it as asked in the comments.
You cannot write to a list while reading from it at the same time. That's a race. ToList counts as a reader because anything looking at the list is a reader.
Use proper synchronization like locks. Or immutable structures.
For Each loops call the enumerator of the list and enumerators don't like to be enumerated while the underlying collection is being changed (InvalidOperationException is thrown if this happens). Accessing list items directly does not throw an exception, only using the lists enumerator does.
Replace the For Each loops by simple For loops and access the list items through the indexer. As long as no items are removed from the lists, you should not tap into an invalid index. But of course the contents of the list might change under way, so this is not really thread safe.
The remarks in IEnumerator.MoveNext Method say:
An enumerator remains valid as long as the collection remains
unchanged. If changes are made to the collection, such as adding,
modifying, or deleting elements, the enumerator is irrecoverably
invalidated and the next call to MoveNext or Reset throws an
InvalidOperationException.
I hope you can help with what is probably a naive use of Streams in VB.net.
I have a program that batch processes images and am running out of memory making repeated use (in a loop) of the following:
Using str As Stream = File.OpenRead(file_stem + CStr(file_number) + "." + file_extension)
temp_img = Image.FromStream(str)
str.Close()
End Using
PictureBox1.Image = temp_img
bm = PictureBox1.Image.Clone
temp_img is globally declared Dim temp_img As Image. bm is declared in the same Sub routine as the loop Dim bm As Bitmap.
As the program runs I can see in Task Manager the memory usage rising and then it crashes with an out of memory error. It's as if each time I am using the Stream it is keeping the memory used. What am I doing wrong here?
EDIT:
This thread appears to have gone cold now, but I thought I would share my "work around" for this. It would seem that this is a VB.net bug as the way I have fixed it is to add a MsgBox which is shown only one, immediately prior to the first call to the subroutine that processes sets of 60 images. I ran a single job that processed 96 sets of 60 images and the memory usage didn't rise above about 45MB. The important thing that makes me think it is a bug is that I only show the MsgBox before the first set of 60 and all are run in series. Showing an MsgBox shouldn't fix anything in itself anyway!
I have to draw something on a Image wich is captured by the Camera. This works on many Devices, but sometimes the RAM is too small or the pictures too big and the function crashed with a OutOfMemory Exception.
How am I able to:
a) optimize the code to prevent this Exceptions
b) Handle this Exceptions (making pictures smaller, free Ram etc.
here is the code:
Dim from_bmp As Bitmap
Dim bmp As Bitmap
from_bmp = New Bitmap(picname)
'I also tryed this with a function which is creating the Bitmap from Filestream
'I also tryed this with the OpenNETCF.Drawing.Imaging.IImage
'If the Original Pictiure is too big, the function will crash here.
bmp = New Bitmap(from_bmp.Width, from_bmp.Height + stampheight)
'now I Create a bitmap which is higher than the original, in order to write the stamp beneth the picture
Dim font As New System.Drawing.Font("Arial", 30, FontStyle.Regular)
gr = Graphics.FromImage(bmp)
gr.DrawImage(from_bmp, 0, 0)
from_bmp.Dispose()
'here I draw somethin in the Bitmap
bmp.Save(deststring, System.Drawing.Imaging.ImageFormat.Jpeg)
gr.Dispose()
bmp.Dispose()
I'd likely use a "using" pattern for your Bitmaps. Also, be aware that an OOM on Bitmap creation can often be overcome by simply trying again (here's a diatribe as to why). My bet is that the GC Heap is too full and a second request, especially after a collection, would succeed.
I'd go through the rest of the code (that not shown) and make sure that all other graphics objects are getting properly Disposed - the CF doesn't handle auto clean-up of the native resources of graphic objects very well and often needs a bit of help (again, see the above link).