views:

1829

answers:

7

Hello!

I'm trying to write a light-weight image viewing application. However, there are system memory limitations with .NET.

When trying to load large bitmaps (9000 x 9000 px or larger, 24-bit), I get a System.OutOfMemoryException. This is on a Windows 2000 PC with 2GB of RAM (of which 1.3GB is used up). It also takes a lot of time to attempt loading the files.

The following code generates this error:

Image image = new Bitmap(filename);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Point(0, 0));
}

As does this code:

Stream stream = (Stream)File.OpenRead(filename);
Image image = Image.FromStream(stream, false, false);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Rectangle(0, 0, 100, 100), 4000, 4000, 100, 100, GraphicsUnit.Pixel);
}

Also, it is enough to do just this:

Bitmap bitmap = new Bitmap(filename);
IntPtr handle = bitmap.GetHbitmap();

The latter code was intended for use with GDI. While researching this, I found out that this is in fact a memory issue where .NET tries to allocate twice as much as is needed in a single contigous block of memory.

http://bytes.com/groups/net-c/279493-drawing-large-bitmaps

I know from other applications (Internet Explorer, MS Paint etc.) that it IS possible to open large images, and rather quickly. My question is, how do I use large bitmaps with .NET?

Is there anyway to stream them, or non-memory load them?

A: 

What about:

Image image = new Bitmap(filename);
using (Graphics gfx = Graphics.FromImage(image))
{    
// Stuff
}
Bart S.
A: 

just a quick fix, i had the same problem, i created a second bitmap instance and passed the bitmap in the constructor.

Elijah Glover
A: 

Can you create a new, blank, bitmap of the same dimensions and color depth? If that is the case, you at least know that your environment can handle the image. Then the problem resides in the image loading subsystem, as of course your link indicated might be the case.

I guess you could write your own bitmap loaders, but that is a lot of work for non-trivial formats, so I wouldn't suggest it.

Perhaps there are replacement libraries available that work around these issues with the standard loaders?

unwind
A: 

So you're saying that it is not the loading of the bitmap but the rendering that causes an out of memory?

If so, can you use Bitmap.LockBits to get ahold of the pixels and write a basic image resizer yourself?

danbystrom
+2  A: 

For a really comprehensive answer, I would use Reflector to look at the source code of Paint.NET (http://www.getpaint.net/); an advanced graphics editing program written in C#.

(as pointed out in the comment, Paint.NET used to be open source but is now closed source).

amdfan
Paint.net is a closed-source program from the end of 2007 ( http://blog.getpaint.net/2007/12/04/freeware-authors-beware-of-%E2%80%9Cbackspaceware%E2%80%9D/ )
zihotki
That's too bad. I edited to reflect your comment.
amdfan
+4  A: 

This is a two part question. The first question is how you can load large images without running out of memory (1), the second one is on improving loading performance (2).

(1) Concider an application like Photoshop where you have the ability to work with huge images consuming gigabites on the filesystem. Keeping the entire image in memory and still have enough free memory to perform operations (filters, image processing and so on, or even just adding layers) would be impossible on most systems (even 8gb x64 systems).

That is why applications such as this uses the concept of swap files. Internally I'm assuming that photoshop uses a proprietary file format, suitable for their application design and built to support partial loads from the swap, enabling them to load parts of a file into memory to process it.

(2) Performande can be improved (quite a lot) by writing custom loaders for each file format. This requires you to read up on the file headers and structure of the file formats you want to work with. Once you've gotten the hand of it its not **that** hard, but it's not as trivial as doing a method call.

For example you could google for FastBitmap to see examples on how you can load a bitmap (BMP) file very fast, it included decoding the bitmap header. This involved pInvoke and to give you some idea on what you are up against you will need to define the bitmap structues such as

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct BITMAPFILEHEADER
        {
            public Int16 bfType;
            public Int32 bfSize;
            public Int16 bfReserved1;
            public Int16 bfReserved2;
            public Int32 bfOffBits;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            public RGBQUAD bmiColors;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public BitmapCompression biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;
        }

Possibly work with creating a DIB (http://www.herdsoft.com/ti/davincie/imex3j8i.htm) and oddities like data being stored "upside down" in a bitmap which you need to take into account or you'll see a mirror image when u open it :-)

Now that's just for bitmaps. Say you wanted to do PNG then you'd need to do similar stuff but decoding the PNG header, which in its simplest form isnt that hard, but if you want to get full PNG specification support then you are in for a fun ride :-)

PNG is different to say a bitmap since it uses a chunk based format where it has "headers" you can loacate to find the diffrent data. Example of some chunks I used while playing with the format was

    string[] chunks =  
new string[] {"?PNG", "IHDR","PLTE","IDAT","IEND","tRNS",
"cHRM","gAMA","iCCP","sBIT","sRGB","tEXt","zTXt","iTXt",
"bKGD","hIST","pHYs","sPLT","tIME"};

You are also going to have to learn about Adler32 checksums for PNG files. So each file format you'd want to do would add a different set of challenges.

I really wish I could give more complete source code examples in my reply but it's a complex subject, and to be honest I've not implemented a swap myself so I wouldn't be able to give too much solid advice on that.

The short answer is that the image processing cababilities in the BCL isn't that hot. The medium answer would be to try and find if someone has written an image library that could help you and the long answer would be to pull up your sleeves and write the core of your application yourself.

Since you know me in real-life you know where to find me ;)

TheCodeJunkie
A: 

One thing just struck me. Are you drawing the entire image and not only the visible part? You should not draw a bigger portion of the image than you are showing in your application, use the x, y, width and heigth parameters to restrict the drawn area.

TheCodeJunkie