2009-02-20 17 views
11

我正在尝试编写一个轻量级的图像查看应用程序。但是,.NET存在系统内存限制。如何在.NET中使用大型位图?

当试图加载大的位图(9000 x 9000 px或更大,24位)时,我得到一个System.OutOfMemoryException。这是在具有2GB RAM的Windows 2000 PC上(其中1.3GB用完)。它也需要很长时间来尝试加载文件。

下面的代码生成此错误:

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

由于这段代码:

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); 
} 

而且,就足以做到这本:

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

后者代码打算用于GDI。 在研究这个时,我发现这实际上是一个内存问题,.NET试图在单个连续的内存块中分配两倍的内存。

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

我知道从其他应用程序(Internet Explorer中,MS涂料等),它可以打开大的图像,并相当迅速。我的问题是,如何在.NET中使用大型位图?

是否有反正流它们,或非内存加载它们?

回答

8

这是一个两部分问题。第一个问题是如何在不耗尽内存的情况下加载大图像(1),第二个问题是提高加载性能(2)。 (1)Concider像Photoshop这样的应用程序,您可以在文件系统上使用巨大图像来处理巨大图像。在大多数系统(甚至是8GB x64系统)上,将整个映像保留在内存中并且仍然具有足够的可用内存来执行操作(过滤器,图像处理等等,甚至仅添加图层)是不可能的。

这就是为什么这样的应用程序使用交换文件的概念。在内部,我假设photoshop使用专有的文件格式,适合他们的应用程序设计,并构建为支持交换的部分加载,使他们能够将文件的某些部分加载到内存中进行处理。 (2)通过为每种文件格式编写自定义加载程序,可以改进(相当多)性能。这要求您阅读您要使用的文件格式的文件标题和结构。一旦你掌握了它,它就不会那么难,但它并不像做一个方法调用那么简单。

例如,您可以谷歌FastBitmap查看如何可以非常快地加载位图(BMP)文件的示例,它包括解码位图标头。这涉及的PInvoke,并给予你你是什么了一些想法对你需要定义位图structues如

 [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; 
     } 

可能与创建DIB(http://www.herdsoft.com/ti/davincie/imex3j8i.htm)工作,古怪像被存储的数据“倒挂“在你需要考虑的位图中,或者当你打开它时你会看到一个镜像:-)

现在,这只是位图。假设你想做PNG,那么你需要做类似的事情,但解码PNG头,它最简单的形式并不难,但如果你想获得完整的PNG规范支持,那么你需要一个有趣的旅程: - )

PNG不同于说一个位图,因为它使用基于块的格式,它具有可以用来查找不同数据的“标题”。一些块,而与格式打我用的例子是

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

也将不得不了解的Adler32校验和PNG文件。所以你想要做的每种文件格式都会添加一组不同的挑战。

我真的希望我可以在我的回复中提供更完整的源代码示例,但这是一个复杂的主题,说实话,我自己并没有实施交换,所以我无法给出太多可靠的建议那。

简而言之,BCL中的图像处理能力并不那么热。中等的答案是尝试找出是否有人编写了一个可以帮助你的图像库,而长时间的回答是拉起袖子并自己编写应用程序的核心。

既然你知道我在现实生活中,你知道在哪里可以找到我;)

0

关于什么:

Image image = new Bitmap(filename); 
using (Graphics gfx = Graphics.FromImage(image)) 
{  
// Stuff 
} 
+0

我不认为这将适用于基于限制的32位系统 – WiiMaxx 2014-01-31 13:04:05

0

只是权宜之计,我有同样的问题,我创建了一个第二位图实例,并在构造函数中传递的位图。

0

您可以创建一个尺寸和颜色深度相同的新空白位图吗?如果是这样,你至少知道你的环境可以处理图像。那么问题就出现在图像加载子系统中,当然你的链接可能就是这种情况。

我想你可以编写你自己的位图加载器,但对于非平凡格式来说这是很多工作,所以我不会建议它。

也许有替代库可以解决这些问题与标准装载机?

0

所以你说这不是加载位图,而是导致内存不足的渲染?

如果是这样,你可以使用Bitmap.LockBits来获取像素的位置并自己编写一个基本的图像缩放器吗?

3

为了得到一个非常全面的答案,我将使用Reflector来查看Paint.NET的源代码(http://www.getpaint.net/);一个用C#编写的高级图形编辑程序。

(正如在评论中指出的,Paint.NET曾经是开源的,但现在是封闭源代码)。

+0

Paint.net是一个从2007年底开始的封闭源代码程序(http://blog.getpaint。net/2007/12/04/freeware-authors-beware-of-%E2%80%9Cbackspaceware%E2%80%9D /) – zihotki 2009-02-21 06:00:13

0

一两件事只是让我吃惊。您是否绘制整个图像而不仅是可​​见部分?您不应绘制比您的应用程序中显示的图像更大的部分,请使用x,y,width和heigth参数来限制绘制的区域。