Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Graphics.DrawImage: Out of memory exception

I just can't for the life of me figure out why I'm getting an out of memory exception here, even after much research on various websites and forums. Is anyone able to shed some light on the root of all evil in this code? The exception is thrown by the call to the Graphics.DrawImage() method, line 79.

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult EditImage(FileModel model)
    {
        var fileData = new MySite.Models.File(model.FileID, model.ClientID, ConfigurationManager.ConnectionStrings["MySite"].ConnectionString).Data;
        Image image = null;

        using (var memStream = new MemoryStream())
        {
            memStream.Write(fileData, 0, fileData.Length);
            image = Image.FromStream(memStream);
        }

        using (var graphics = Graphics.FromImage(image))
        {
            graphics.DrawImage(image, model.x1, model.y1, (model.x2 - model.x1), (model.y2 - model.y1));
            graphics.Save();
        }

        using (var memStream = new MemoryStream())
        {
            image.Save(memStream, System.Drawing.Imaging.ImageFormat.Jpeg);
            return File(memStream.ToArray(), "image/jpeg");
        }
    }

Stack trace:

[OutOfMemoryException: Out of memory.]
System.Drawing.Graphics.CheckErrorStatus(Int32 status) +1143476
System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y, Int32 width, Int32 height) +141
ProPilot.Controllers.DocumentsController.EditImage(FileModel model) in C:\DEV\Web\Controllers\DocumentsController.cs:79
lambda_method(Closure , ControllerBase , Object[] ) +104
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +14
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +211
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
System.Web.Mvc.Async.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41() +28
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +10
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +57
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +48
System.Web.Mvc.Async.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33() +57
System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +223
System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__36(IAsyncResult asyncResult) +10
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +57
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +48
System.Web.Mvc.Async.<>c__DisplayClass2a.<BeginInvokeAction>b__20() +24
System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult) +102
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +57
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +43
System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult) +14
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +57
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +47
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult) +25
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +47
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9629296
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155 
like image 995
Maritim Avatar asked Apr 17 '13 08:04

Maritim


2 Answers

@Sayse hit the nail on the head with his comment above.

When using Image.FromStream:

You must keep the stream open for the lifetime of the Image.

Source: http://msdn.microsoft.com/en-AU/library/93z9ee4x.aspx

using (var memStream = new MemoryStream())
{
    memStream.Write(fileData, 0, fileData.Length);
    using(Image image = Image.FromStream(memStream))
    {
        using (var graphics = Graphics.FromImage(image))
        {
            graphics.DrawImage(image, model.x1, model.y1, (model.x2 - model.x1), (model.y2 - model.y1));
            graphics.Save();
        }

        using (var outStream = new MemoryStream())
        {
            image.Save(outStream, System.Drawing.Imaging.ImageFormat.Jpeg);
            return File(outStream.ToArray(), "image/jpeg");
        }
    }
}
like image 155
Lummo Avatar answered Sep 16 '22 13:09

Lummo


If you work with the Bitmap or Image class you will find several problems.

If you load an image with Image.FromFile() the framework does not close the file handle after loading the image. The file stays open until the garbage collector recollects the image. This is a severe misdesign in GDI+. There is no need to keep a file open after the image has already been read into memory.

So I tried to work around this problem by using Image.FromStream() instead. But this is no solution because when the stream has been closed a DrawImage() operation on that image will fail with "Out of memory".

So if you want to read for example a Bitmap from a file and you want to assure that the file handle is closed you have to use an ugly workaround like this:

Bitmap ReadBitmapFromFile(String s_Path)
{
    using (FileStream i_Stream = new FileStream(s_Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        using (Bitmap i_Bmp = new Bitmap(i_Stream))
        {
            return new Bitmap(i_Bmp);
        }
    }
}

By the way: Image.Clone() produces a similar problem.

Instead of

Bitmap i_Clone = (Bitmap)i_Bmp.Clone() 

I had to use:

Bitmap i_Clone = new Bitmap(i_Bmp);
like image 28
Elmue Avatar answered Sep 16 '22 13:09

Elmue