Reputation: 663
I'm trying to use a PictureBox to create an animation in a Windows Forms app (C#) by drawing on the Bitmap using Graphics. Unfortunately, it looks like I am leaking memory somewhere or allocating too much based on this error:
An unhandled exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll
Additional information: Out of memory.
This is the source code. Desired behavior is a popup with a glitchy animation.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AnimationApp
{
public partial class Form1 : Form
{
Timer timer = new Timer();
Form1 form1;
//Bitmap bmp;
public Form1()
{
InitializeComponent();
form1 = this;
form1.TransparencyKey = Color.White;
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
pictureBox1.Left = 0;
pictureBox1.Top = 0;
pictureBox1.Width = Screen.PrimaryScreen.Bounds.Width;
pictureBox1.Height = Screen.PrimaryScreen.Bounds.Height;
timer.Enabled = true;
timer.Interval = 10;
timer.Tick += timer_tick;
}
Random rand = new Random();
int partsection = 0;
int waitint = 0;
int ran1 = 0;
int ran2 = 0;
int intensifies = 0;
public void timer_tick(object e, EventArgs ea)
{
Bitmap bmp;
bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
if (partsection == -1)
{
}
else if (partsection<300)
{
if (waitint == 0)
{
ran1 = rand.Next(0, pictureBox1.Width);
ran2 = rand.Next(0, pictureBox1.Width);
waitint = 10;
}
else
{
waitint--;
}
g.FillRectangle(new SolidBrush(Color.Green), (float)ran1, (float)0, 3, pictureBox1.Height);
g.FillRectangle(new SolidBrush(Color.DarkGreen), (float)ran2, (float)0, 3, pictureBox1.Height);
partsection++;
}
else if (partsection < 1000)
{
if (intensifies < 255)
{
intensifies++;
}
else
{
}
g.FillRectangle(new SolidBrush(Color.FromArgb(intensifies,Color.Black)), (float)0, (float)0, pictureBox1.Width, pictureBox1.Height);
}
}
pictureBox1.Image = bmp;
}
}
}
Thank you for any help.
Upvotes: 0
Views: 3749
Reputation: 30813
This happens because some of the resources you use are unmanaged and you do not put it under using
block or any alternative way to dispose your unmanaged resource after use. The memory is leaked due to that.
For instance, these lines,
g.FillRectangle(new SolidBrush(Color.Green), (float)ran1, (float)0, 3, pictureBox1.Height);
g.FillRectangle(new SolidBrush(Color.DarkGreen), (float)ran2, (float)0, 3, pictureBox1.Height);
Can be changed to
using (SolidBrush greenBrush = new SolidBrush(Color.Green))
using (SolidBrush darkGreenBrush = new SolidBrush(Color.DarkGreen)) {
g.FillRectangle(greenBrush , (float)ran1, (float)0, 3, pictureBox1.Height);
g.FillRectangle(darkGreenBrush, (float)ran2, (float)0, 3, pictureBox1.Height);
}
And similarly, this line
g.FillRectangle(new SolidBrush(Color.FromArgb(intensifies,Color.Black)), (float)0, (float)0, pictureBox1.Width, pictureBox1.Height);
To this
using (SolidBrush blackBrush = new SolidBrush(Color.FromArgb(intensifies, Color.Black)))
g.FillRectangle(blackBrush, (float)0, (float)0, pictureBox1.Width, pictureBox1.Height);
Another advise is you should check the unmanaged resources on System.Drawing
. Since there might be more of the unmanaged resources which you show but are not pointed out - or - you do not put in your question but are actually in the other parts of your codes.
Here is more of unmanaged resources:
Here is another explanation for the possibility of Bitmap
causes the leak (due to small-sized managed Bitmap
class wrapper, out of memory can be caused because the Garbage Collector is late-triggered), see explanation by Mr. Hans Passant
Upvotes: 2
Reputation: 1041
Your bitmap needs to be declared as an instance variable of the form.
EDIT
Specifically, uncomment your declaration of the Bitmap
in the class instance variables. Next, add the following code:
protected override void OnResize(EventArgs e)
{
pictureBox1.Left = 0;
pictureBox1.Top = 0;
pictureBox1.Width = Screen.PrimaryScreen.Bounds.Width;
pictureBox1.Height = Screen.PrimaryScreen.Bounds.Height;
bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
base.OnResize(e);
}
Why? Because you need to resize your picture box when the form resizes, or the user changes the screen resolution. Now, the Bitmap
is only declared a single time. I ran the code for several minutes with no ill effects.
BTW, the pictureBox1
resizing code was removed from the constructor.
Upvotes: 1