.NET (296) administrative (41) Ajax (42) AngularJS (2) ASP.NET (144) bicycle (2) books (180) browser (8) C# (134) cars (1) chess (28) CodePlex (10) Coma (8) database (47) deployment (3) Entity Framework (2) essay (112) flash/shockwave (2) flex (1) food (3) friend (2) game (20) idea (5) IIS (8) javascript (82) LInQ (2) Linux (6) management (4) manga (42) misc (672) mobile (1) movies (91) MsAccess (1) murder (2) music (64) mysql (1) news (100) permanent (1) personal (68) PHP (1) physics (2) picture (307) places (12) politics (13) programming (504) rant (120) religion (3) science (43) Sharepoint (3) software (58) space (1) T4 (2) technology (11) Test Driven Development (4) translation (2) VB (2) video (97) Visual Studio (44) web design (46) Windows API (8) Windows Forms (3) Windows Server (5) WPF/Silverlight (63) XML (11)

Tuesday, September 22, 2009

OutOfMemoryException in System.Drawing.Graphics.DrawImage

I was using these GIF images stored as embedded resources and suddenly I got an Out of memory exception from a component that I had no control over. All images were icon size, 16x16 or a little more, so a lot of the explanations for the error based on "you don't have enough memory!" (duh!) were not helpful. The images did have transparent pixels, and my gut feeling is that it all came from there.

I still don't know what caused it, but I did find a solution. Where I get the image I add an additional
image = image.GetThumbnailImage(image.Width, image.Height, null, IntPtr.Zero);
I know it's not something very intuitive, but it solved the problem.

The only significant difference between the image before and after the thumbnailization is the PixelFormat property that changed from PixelFormat.Format8bppIndexed to PixelFormat.Format32bppArgb.

The stack looks like this:
   at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
at System.Drawing.Graphics.DrawImage(Image image, Rectangle destRect, Int32 srcX, Int32 srcY, Int32 srcWidth, Int32 srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttrs, DrawImageAbort callback, IntPtr callbackData)
at System.Drawing.Graphics.DrawImage(Image image, Rectangle destRect, Int32 srcX, Int32 srcY, Int32 srcWidth, Int32 srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttr, DrawImageAbort callback)
at System.Drawing.Graphics.DrawImage(Image image, Rectangle destRect, Int32 srcX, Int32 srcY, Int32 srcWidth, Int32 srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttr)


And, using Reflector, I could go as far as pinpointing the error to the method System.Drawing.SafeNativeMethods.Gdip.GdipDrawImageRectRectI which wraps the gdiplus.dll GdipDrawImageRectRectI function. It returns error status 3, which is Out of memory, and that is that. I couldn't find a C++ code for the GdiPlus library and even if I had, who I am kidding? :)

7 comments:

Jeff said...

Thanks, I was having the exact same problem. I had embedded png icons 16x16 and when I changed the enabled property of their parent I got this obscure error. Your workaround has solved the problem. Is this a bug within the framework?

Siderite said...

I can only assume it is a bug from GDI plus. At least there I lost the trace for the error.

Poet For Life said...

hi,

I used the Getthumbnail method , but the quality of image deterirates a lot and text on image is not readable , Please suggest what can be done

Siderite said...

So you are resizing the image to a thumbnail of the exact same dimensions and the image deteriorates? If you can replicate the out of memory issue consistently or share the image that thumbnailed deteriorates, I can try some things out on my local machine.

Poet For Life said...

Yes , i copy pasted the code above , the pixel format of images changes , so out of memory issue is not coming anymore but image quality deteriorats
image is 96 DPI , cant share the image sorry
if (image.PixelFormat.ToString() == "8207")
{
image = image.GetThumbnailImage(image.Width, image.Height, null, IntPtr.Zero);
}

Siderite said...

Try
var newImage = new Bitmap(newWidth, newHeight);
using (var gr = Graphics.FromImage(newImage))
{
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(srcImage, new Rectangle(0, 0, newWidth, newHeight));
}
in other words, your own thumbnail image generation. Please let me know if it worked.
I understand that some image formats support embedded thumbnail images. In that case GetThumbnailImage could use one of those, maybe, therefore the loss of quality. Or maybe it's just a terrible method. :)

Stefan said...

Spend more than a day trying to solve this problem. Your solution worked perfectly. Thanks.