I have a function which draws a list of objects onto a bitmap to create a crude map. Code analysis throws a warning (CA2000) which says the object 'drawPen' isn't disposed along all exception paths. As far as I can see it is disposed at the end of the function and there is no inaccessible code where it could be missed.
Does anyone know why the compiler thinks it is not being disposed of properly?
public void drawUpdates(List<areaObjects> objectLocations)
{
Rectangle areaToClone = new Rectangle(0, 0, writeOnceMap.Width, writeOnceMap.Height);
var pixelFormat = writeOnceMap.PixelFormat;
areaBitMap = writeOnceMap.Clone(areaToClone, pixelFormat);
Pen drawPen = new Pen(Color.Black, 2);
drawPen.Width = 2;
foreach(areaObjectsop2d in objectLocations)
{
int xPosition = (int)(op2d.XYZ.xPos * mapScale);
int yPosition = (int)(op2d.XYZ.yPos * mapScale);
Point[] crossMarker = getCrossShape(xPosition, yPosition);
using (var graphics = Graphics.FromImage(areaBitMap))
{
graphics.DrawPolygon(drawPen, crossMarker);
}
}
drawPen.Dispose();
}
You get the warning because in the case of an exception in your function the drawPen will not be disposed.
You can wrapp your code in a try finally and in the finally call the .Dispose() or better - use the using which does precisely that.
public void drawUpdates(List<areaObjects> objectLocations)
{
Rectangle areaToClone = new Rectangle(0, 0, writeOnceMap.Width, writeOnceMap.Height);
var pixelFormat = writeOnceMap.PixelFormat;
areaBitMap = writeOnceMap.Clone(areaToClone, pixelFormat);
using(Pen drawPen = new Pen(Color.Black, 2))
{
foreach(areaObjectsop2d in objectLocations)
{
int xPosition = (int)(op2d.XYZ.xPos * mapScale);
int yPosition = (int)(op2d.XYZ.yPos * mapScale);
Point[] crossMarker = getCrossShape(xPosition, yPosition);
using (var graphics = Graphics.FromImage(areaBitMap))
{
graphics.DrawPolygon(drawPen, crossMarker);
}
}
}
}
The above is equivalent to:
Pen drawPen = new Pen(Color.Black, 2);
try
{
/*Your code*/
}
finally
{
drawPen.Dispose();
}
Imagine what happens if the code between creating the pen and disposing it throws an exception. The pen will not be disposed. The compiler warns you to make sure that the pen will get disposed even when an exception occurs. You have two ways to do so: using and try...finally (which is in fact the implementation of using).
using (Pen drawPen = ...)
{
} // now the code makes sure it gets disposed
Related
I am trying to combine multiple images to a single one. this is my code right now:
public static Bitmap Merge(List<Image> imgs)
{
int outputImageWidth = 0;
int outputImageHeight = 0;
foreach (var img in imgs)
{
if(outputImageWidth < img.Width)
{
outputImageWidth = img.Width;
}
outputImageHeight += img.Height;
}
outputImageHeight += 1;
using (Bitmap outputImage = new Bitmap(outputImageWidth, outputImageHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
foreach (var img in imgs)
{
Graphics graphics = Graphics.FromImage(outputImage);
graphics.DrawImage(img, new Rectangle(new Point(0, outputImage.Height + 1), img.Size), new Rectangle(new Point(), img.Size), GraphicsUnit.Pixel);
graphics.Dispose();
graphics = null;
img.Dispose();
}
foreach (var img in imgs)
{
img.Dispose();
}
GC.Collect();
return (Bitmap)outputImage.Clone();
}
}
While debugging, i figgured out, then whenever I call graphics.DrawImage(...) about 100-300mb of memory were allocated. I expected it to get liberated whenever i dispose the object, but with every iteration, the process allocates more memory, until I get an out of memory exceptio (approximately after 30 pages on a 32 Bit process).
Any ideas?
Try this instead
public static Bitmap Merge(List<Image> imgs)
{
...
var outputImage = new Bitmap(outputImageWidth, outputImageHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb));
using (var graphics = Graphics.FromImage(outputImage))
{
foreach (var img in imgs)
{
using (img)
graphics.DrawImage(img, new Rectangle(new Point(0, outputImage.Height + 1), img.Size), new Rectangle(new Point(), img.Size), GraphicsUnit.Pixel);
}
return outputImage;
}
}
This
features using blocks that allow for Dispose to always be called, even if there are exceptions thrown part of the way through
uses a single Graphics throughout the process
doesn't allocate the (huge) bitmap twice
You will, of course, have to dispose of the resulting bitmap outside of this method once you're done with it.
I am using C#,MVC5 and I am uploading image from my web application but I realize that I have performance issues because I don't optimize them and I need to fix it and is important to keep the quality.
Below you can see the results of the report why is slow.
How can I do it?
I am saving the files into a path locally with the below code.
string imgpathvalue = ConfigurationManager.AppSettings["RestaurantPath"];
string path = System.IO.Path.Combine(Server.MapPath(imgpathvalue));
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
string pic = System.IO.Path.GetFileName(restaurantImg.FileName.Replace(" ", "_").Replace("%", "_"));
path = System.IO.Path.Combine(Server.MapPath(imgpathvalue), pic);
// file is uploaded
restaurantImg.SaveAs(path);
I have try the code below but I am getting the error "A generic error occurred in GDI+."
System.Drawing.Bitmap bmpPostedImage = new System.Drawing.Bitmap(restaurantImg.InputStream);
System.Drawing.Image objImage = ResizeImages.ScaleImage(bmpPostedImage, 81);
using (var ms = new MemoryStream())
{
objImage.Save(ms, objImage.RawFormat);
//ResizeImages.getImage(ms.ToArray());
}
public static System.Drawing.Image ScaleImage(System.Drawing.Image image, int maxHeight)
{
var ratio = (double)maxHeight / image.Height;
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
using (var g = Graphics.FromImage(newImage))
{
g.DrawImage(image, 0, 0, newWidth, newHeight);
}
return newImage;
}
You are missing some of the code to resize your image correctly. Appending is a function that correctly resizes images depending on the Width and Height Values you give to it (in this example the image gets resized to 120*120 if possible).
Function Call:
ResizeImage("Path to the Image you want to resize",
"Path you want to save resizes copy into", 120, 120);
To make a function call like that possible we need to write our function. Which takes the image from the sourceImagePath and creates a new Bitmap.
Then it calculates the factor to resize the image and depending on if either the width or height is bigger it gets adjusted accordingly.
After that is done we create a new BitMap fromt he sourceImagePath and resize it. At the end we also need to dispose the sourceImage, the destImage and we also need to dispose of the Graphics Element g that we used for different Quality Settings.
Resize Function:
private void ResizeImage(string sourceImagePath, string destImagePath,
int wishImageWidth, int wishImageHeight)
{
Bitmap sourceImage = new Bitmap(sourceImagePath);
Bitmap destImage = null;
Graphics g = null;
int destImageWidth = 0;
int destImageHeight = 0;
// Calculate factor of image
double faktor = (double) sourceImage.Width / (double) sourceImage.Height;
if (faktor >= 1.0) // Landscape
{
destImageWidth = wishImageWidth;
destImageHeight = (int) (destImageWidth / faktor);
}
else // Port
{
destImageHeight = wishImageHeight;
destImageWidth = (int) (destImageHeight * faktor);
}
try
{
destImage = new Bitmap(sourceImage, destImageWidth, destImageHeight);
g = Graphics.FromImage(destImage);
g.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
g.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.PixelOffsetMode =
System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.CompositingQuality =
System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.DrawImage(sourceImage, 0, 0, destImageWidth, destImageHeight);
// Making sure that the file doesn't already exists.
if (File.Exists(destImagePath)) {
// If it does delete the old version.
File.Delete(destImagePath);
}
destImage.Save(destImagePath);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("*** ERROR-Terror: " + ex.Message)
}
finally
{
if (g != null) { g.Dispose(); g = null; }
if (destImage != null) { destImage.Dispose(); destImage = null; }
}
sourceImage.Dispose();
sourceImage = null;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics l = e.Graphics;
Pen p = new Pen(Color.Black, 1);
float angle = 0;
float len = 100;
PointF ori = new PointF(Width/2, 0);
PointF bob = new PointF(Width/2, len);
while(true)
{
bob.X = ori.X + len * (float)Math.Sin(angle);
bob.Y = ori.Y + len * (float)Math.Cos(angle);
angle += 0.001F;
l.DrawLine(p, ori.X, ori.Y, bob.X, bob.Y);
l.DrawEllipse(p, bob.X - 15, bob.Y, 30, 30);
if(angle == 360)
{
break;
}
l.Dispose();
}
}
The error line is l.DrawLine(p, ori.X, ori.Y, bob.X, bob.Y).
Error type: System.ArgumentException. Error Message: Parameter is not valid.
At first I thought the issue was with the floats but the DrawLine allows for such datatypes. It loops through once the error seems to occur when angle>0. Its magnitude doesn't seem to be the issue. Any help would be much appreciated. Thanks in advance. [UPDATE] Error seems to be with the l.Dispose
The problem is in the wrong Dispose call:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics l = e.Graphics;
// Pen is IDisposable, that's why why wrap it into "using": it's you who created it
using (Pen p = new Pen(Color.Black, 1)) {
float angle = 0;
float len = 100;
PointF ori = new PointF(Width/2, 0);
PointF bob = new PointF(Width/2, len);
while(true)
{
bob.X = ori.X + len * (float)Math.Sin(angle);
bob.Y = ori.Y + len * (float)Math.Cos(angle);
angle += 0.001F;
l.DrawLine(p, ori.X, ori.Y, bob.X, bob.Y);
l.DrawEllipse(p, bob.X - 15, bob.Y, 30, 30);
// angle is float, that's why == is not recommended:
// (you can well have 359.99999999999999) and thus == will never be true
if (angle >= 360)
break;
// l.Dispose(); // <- Wrong: it's not you who've created it
// (let system Dispose it)
}
}
}
As an enhancement to #Dmitry's answer, I can offer this advice:
As a general rule, you shouldn't call Dispose on an object that you do not control the lifetime of. You are getting an existing Graphics instance from the Paint event. The control that raised the Paint event is what created that Graphics object, so it is responsible for calling Dispose on it when it is done with it, not you.
When you call Dispose on an object that you don't control, you effectively "rip the rug out from under" that code. This is bad, because that code could be expecting the instance to still be alive so that it can perform other operations on it. By disposing it, you don't give it that chance.
You are doing the correct thing with your Pen instance, p, though. You create it during the using statement, so you are responsible for it. The Using handles that by automatically calling Dispose when execution leave the block.
If you had created the Graphicsinstance yourself, with something likeGraphics.FromImage`, then you would be responsible for cleaning it up.
I have put this simple code together to draw a line. Now I want to apply a ScaleTransform to it by a factor of 10; but the code below doesn't work.
var bitmap = new Bitmap(pictureBox1.Size.Width, pictureBox1.Size.Height);
var g = Graphics.FromImage(bitmap);
pictureBox1.Image = bitmap;
var pn = new Pen(Color.Wheat, -1);
g.DrawLine(pn, 0, 0, 10, 10);
pn.Dispose();
// I'm trying to scaletransform here!
g.ScaleTransform(10, 10);
Update:
What is the correct way to update the changes? I'm not getting any results from this :(
g.ScaleTransform(1, 1);
pictureBox1.Invalidate();
You must apply the transformation BEFORE drawing the line!
var g = Graphics.FromImage(bitmap);
g.ScaleTransform(10, 10);
using (pn = new Pen(Color.Wheat, -1)) {
g.DrawLine(pn, 0, 0, 10, 10);
}
Transformations are applied to the transformation matrix of the graphics object (g.Transform).
Also make use of the using statement in order to dispose the resources. It will even dispose the pen if an exception should occur or if the using statement-block should be left with a return or break statement.
I have this piece of Code ("ruta1" and "ruta2" are strings containing the path to different images):
Bitmap pic;
Bitmap b = new Bitmap(SomeWidth, SomeHeight);
g = Graphics.FromImage(b);
g.FillRectangle(new SolidBrush(Color.White), 0, 0, b.Width, b.Height);
b.Save(ruta2, ImageFormat.Jpeg);
g.Dispose();
b.Dispose();
b = new Bitmap(OtherWidth, OtherHeight);
pic = new Bitmap(ruta2);
g = Graphics.FromImage(b);
g.FillRectangle(new SolidBrush(Color.White), 0, 0, b.Width, b.Height);
g.DrawImage(pic, 0, 0, pic.Height, pic.Width);
pic.Dispose();
b.Save(ruta2, ImageFormat.Jpeg);
g.Dispose();
b.Dispose();
pic = new Bitmap(ruta1);
b = new Bitmap(AnotherWidth, AnotherHeight);
g = Graphics.FromImage(b);
g.FillRectangle(new SolidBrush(Color.White), 0, 0, b.Width, b.Height);
int k = 1;
// I just draw the "pic" in a pattern on "b"
for (h = 0; h <= b.Height; h += pic.Height)
for (w = 0; w <= b.Width; w += pic.Width)
g.DrawImage(pic, w, h, k * pic.Width, k * pic.Height);
pic.Dispose();
GC.Collect(); // If I comment this line, I get a "generic GDI+ Error" Exception
b.Save(ruta1, ImageFormat.Jpeg);
g.Dispose();
b.Dispose();
It doesn't matter if I set pic = null after disposing it, if I don't call the Garbage Collector, I got a "Generic GDI+ Error" exception. Only when I call the Garbage Collector, my program goes ok, all times.
Can somebody explain this behavior? Does it depend on the .Net framework version?
I'm using Visual C# Express 2008, with .Net framework 3.5
First, it would be nice if you were using the "using" keyword to scope the use of your disposable objects (like Bitmap and Graphics), instead of calling Dispose() manually on each. Using "using" is better for lots of reasons like cleaning up stuff when an exception is thrown, but it also greatly helps for code readability.
Second, GDI brushes are also IDisposable objects, so you shouldn't create-and-forget them. Do this instead:
using (var brush = new SolidBrush(Color.White))
{
g.FillRectangle(brush, 0, 0, width, height)
}
...or better yet, create your brushes at the start of your application, and hang on to them until the end (but don't forget to dispose of them too). IIRC, creating/disposing brushes impacts performance quite a lot if done frequently.
Third, I believe your bug is in the 2nd section:
You open image "ruta2"
You create a new image
You draw the contents of "ruta2" inside that new image
You save that new thing on top of the "ruta2" file, but the original image from step 1 is still loaded, and probably has some handle on the "ruta2" file, so you need to dispose of it before you overwrite the file.
If you refactor that 2nd section like this, it should work:
using (var b = new Bitmap(OtherWidth, OtherHeight))
using (var g = Graphics.FromImage(b))
{
using (var brush = new SolidBrush(Color.Red))
{
g.FillRectangle(brush, 0, 0, b.Width, b.Height);
}
using (var pic = new Bitmap(ruta2))
{
g.DrawImage(pic, 0, 0, pic.Height, pic.Width);
}
b.Save(ruta2, ImageFormat.Jpeg);
}