C# 如何在 .NET 中使用大位图?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/569889/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I use large bitmaps in .NET?
提问by Reyhn
I'm trying to write a light-weight image viewing application. However, there are system memory limitations with .NET.
我正在尝试编写一个轻量级的图像查看应用程序。但是,.NET 存在系统内存限制。
When trying to load large bitmaps (9000 x 9000 pxor 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.
尝试加载大型位图(9000 x 9000 像素或更大,24 位)时,出现 System.OutOfMemoryException。这是在具有 2GB RAM(其中 1.3GB 已用完)的 Windows 2000 PC 上进行的。尝试加载文件也需要花费大量时间。
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.
后一个代码旨在与 GDI 一起使用。在研究这一点时,我发现这实际上是一个内存问题,其中 .NET 试图在单个连续内存块中分配两倍于所需的内存。
http://bytes.com/groups/net-c/279493-drawing-large-bitmaps
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?
我从其他应用程序(Internet Explorer、MS Paint 等)知道可以打开大图像,而且速度相当快。我的问题是,如何在 .NET 中使用大位图?
Is there anyway to stream them, or non-memory load them?
无论如何要流式传输它们,或者非内存加载它们?
采纳答案by TheCodeJunkie
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),第二个问题是提高加载性能(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).
(1) 考虑像 Photoshop 这样的应用程序,您可以在其中处理文件系统上消耗千兆位的巨大图像。在大多数系统(甚至 8gb x64 系统)上,将整个图像保存在内存中并仍然有足够的可用内存来执行操作(过滤器、图像处理等,甚至只是添加层)是不可能的。
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.
这就是诸如此类的应用程序使用交换文件概念的原因。在内部,我假设 photoshop 使用一种专有的文件格式,适合他们的应用程序设计,并构建为支持来自交换的部分加载,使他们能够将文件的一部分加载到内存中以进行处理。
(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.
(2) Performande 可以通过为每种文件格式编写自定义加载器来改进(相当多)。这要求您阅读要使用的文件格式的文件头和结构。一旦你掌握了它,它就不是那么难了,但这并不像调用方法那么简单。
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
例如,您可以搜索 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;
}
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 :-)
可能会创建一个 DIB ( http://www.herdsoft.com/ti/davincie/imex3j8i.htm) 和奇怪的东西,比如数据被“颠倒”存储在位图中,你需要考虑或者你会看到打开它时的镜像:-)
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,那么你需要做类似的事情,但解码 PNG 标头,最简单的形式并不那么难,但如果你想获得完整的 PNG 规范支持,那么你就可以玩得很开心:- )
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
PNG 与位图不同,因为它使用基于块的格式,其中具有“标题”,您可以定位以查找不同的数据。我在使用格式时使用的一些块的示例是
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.
您还必须了解 PNG 文件的 Adler32 校验和。因此,您想要使用的每种文件格式都会增加一组不同的挑战。
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.
简短的回答是 BCL 中的图像处理能力并不那么热门。中等的答案是尝试找出是否有人编写了可以帮助您的图像库,而长期的答案是挽起袖子自己编写应用程序的核心。
Since you know me in real-life you know where to find me ;)
因为你在现实生活中认识我,所以你知道在哪里可以找到我 ;)
回答by Bart S.
What about:
关于什么:
Image image = new Bitmap(filename);
using (Graphics gfx = Graphics.FromImage(image))
{
// Stuff
}
回答by Elijah Glover
just a quick fix, i had the same problem, i created a second bitmap instance and passed the bitmap in the constructor.
只是一个快速修复,我遇到了同样的问题,我创建了第二个位图实例并在构造函数中传递了位图。
回答by unwind
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?
也许有可用的替代库可以解决标准加载程序的这些问题?
回答by Dan Bystr?m
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?
如果是这样,您可以使用 Bitmap.LockBits 来获取像素并自己编写一个基本的图像调整器吗?
回答by ine
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#.
对于真正全面的答案,我会使用 Reflector 查看 Paint.NET ( http://www.getpaint.net/)的源代码;用 C# 编写的高级图形编辑程序。
(as pointed out in the comment, Paint.NET used to be open source but is now closed source).
(正如评论中所指出的,Paint.NET 曾经是开源的,但现在是闭源的)。
回答by TheCodeJunkie
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.
一件事让我印象深刻。您是否在绘制整个图像而不仅仅是可见部分?您不应该绘制比您在应用程序中显示的更大的图像部分,使用 x、y、宽度和高度参数来限制绘制区域。