如何在.NET中使用大位图?

我正在尝试编写轻量级图像查看应用程序。 但是,.NET存在系统内存限制。

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

以下代码生成此错误:

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

后一个代码旨在与G​​DI一起使用。 在研究这个问题时,我发现这实际上是一个内存问题,.NET试图在单个内存块中分配两倍的内存。

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

我从其他应用程序(Internet Explorer,MS Paint等)了解到,可以打开大图像,而且很快。 我的问题是, 如何在.NET中使用大型位图?

无论如何流式传输它们,还是非内存加载它们?

这是一个两部分问题。 第一个问题是如何在不耗尽内存的情况下加载大型图像(1),第二个问题是提高加载性能(2)。

(1)Concider像Photoshop一样的应用程序,你可以在文件系统上使用巨大的图像消耗gigabite。 在大多数系统(甚至8gb x64系统)上,将整个映像保留在内存中并且仍然有足够的可用内存来执行操作(filter,图像处理等,甚至只是添加层)是不可能的。

这就是为什么像这样的应用程序使用交换文件的概念。 在内部我假设photoshop使用专有的文件格式,适合他们的应用程序设计,并构建为支持交换的部分加载,使他们能够将文件的一部分加载到内存中进行处理。

(2)通过为每种文件格式编写自定义加载器,可以改进(非常多)Performande。 这需要您阅读要使用的文件格式的文件头和结构。 一旦你掌握了它,那就不是****,但它并不像做方法调用那样微不足道。

例如,您可以google for FastBitmap查看有关如何快速加载位图(BMP)文件的示例,其中包括解码位图标头。 这涉及到pInvoke并且为了让你对你所面对的内容有所了解,你需要定义位图结构,例如

  [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标题,这是最简单的forms并不难,但如果你想得到完整的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"}; 

您还必须了解PNG文件的Adler32校验和。 因此,您想要做的每种文件格式都会增加一系列不同的挑战。

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

简短的回答是BCL中的图像处理能力并不高。 中等答案是尝试找出是否有人编写了可以帮助您的图像库,而长期的答案就是拉起袖子并自己编写应用程序的核心。

既然你在现实生活中认识我,你就知道在哪里找到我;)

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

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

关于什么:

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

只是一个快速修复,我遇到了同样的问题,我创建了第二个位图实例并在构造函数中传递了位图。

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

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

也许有替换库可用于解决标准加载器的这些问题?

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

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

有一件事让我印象深刻。 您是在绘制整个图像而不仅仅是可见部分吗? 您不应该绘制比在应用程序中显示的图像更大的部分,使用x,y,width和heigth参数来限制绘制区域。