这可能不是一个措辞得很好的问题,因为我不确定发生了什么,所以我不知道如何有针对性地提出这个问题。我正在努力学习,我希望我能就此得到一些指导。您对初学者的耐心表示赞赏。
我有一段我正在修改的代码。它显示一个图像。我想修改图像,并在不同的窗口中显示它。我复制显示图像的代码,进行修改,并显示原始图像和修改图像的修改图像。
GCHandle似乎一直在指同一个记忆?我是不是真的通过更改手柄名称来制作新手柄?很抱歉这段代码很长,但我很遗憾。
出了什么问题?
最令人困惑的是它正在工作,然后我改变了一些东西,现在无法回到工作版本,我认为我的代码与工作的代码相同。有些设置在哪里?
System.Runtime.InteropServices.GCHandle gch3 = System.Runtime.InteropServices.GCHandle.Alloc(scaled, System.Runtime.InteropServices.GCHandleType.Pinned);
int pitch = mImageWidth;
if (pitch % 4 != 0)
pitch = ((pitch / 4) + 1) * 4;
System.Drawing.Bitmap bitmap = new Bitmap(mImageWidth, mImageHeight, pitch, System.Drawing.Imaging.PixelFormat.Format8bppIndexed, gch3.AddrOfPinnedObject());
gch3.Free();
if (pal == null)
{
System.Drawing.Imaging.ColorPalette cp = bitmap.Palette;
for (i = 0; i < cp.Entries.Length; ++i)
{
cp.Entries[i] = Color.FromArgb(i, i, i);
}
pal = cp;
}
bitmap.Palette = pal;
FirstImageDisplay.Image = bitmap;
//second image here
for (i = 0; i < frame.Length; ++i)
scaled[i] = (byte)(.5 * scaled[i]);
System.Runtime.InteropServices.GCHandle gch4 = System.Runtime.InteropServices.GCHandle.Alloc(scaled, System.Runtime.InteropServices.GCHandleType.Pinned);
int pitch1 = mImageWidth;
if (pitch1 % 4 != 0)
pitch1 = ((pitch1 / 4) + 1) * 4;
System.Drawing.Bitmap bitmap2 = new Bitmap(mImageWidth, mImageHeight, pitch, System.Drawing.Imaging.PixelFormat.Format8bppIndexed, gch4.AddrOfPinnedObject());
gch4.Free();
if (pal == null)
{
System.Drawing.Imaging.ColorPalette cp = bitmap.Palette;
for (i = 0; i < cp.Entries.Length; ++i)
{
cp.Entries[i] = Color.FromArgb(i, i, i);
}
pal = cp;
}
bitmap.Palette = pal;
SecondImageDisplay.Image = bitmap;
//end second image code
答案 0 :(得分:1)
你所做的绝对不安全。你为什么做这个?您是否有理由离开安全,有管理的环境?
位图是围绕byte[]
创建的。只要您不介意在托管内存中固定byte[]
(暂时还好,在应用程序的持续时间内等等),这是可以的。但是,在下一行,你释放指针!
然后使用相同的byte[]
,修改它,并将其用于另一个位图。 Boom,它仍然是相同的字节数组。两个位图具有相同的内容并不奇怪 - 你要求它。
它有时可以工作的原因有时并不是因为如果GC没有移动手柄,两个位图都是正确的。但是,如果GC移动字节数组,则位图无法调整 - 它们仍然指向内存中的相同位置(现在是错误的)。
您必须了解的是GCHandle不会创建新对象。只要GCHandle存在,它只是指示GC不要弄乱物理位置(好吧,在虚拟内存中,但是......)。如果要创建新对象,请执行byte[].Clone()
之类的操作。但是,你仍然需要在Bitmap的所有生命周期中固定句柄,这通常是一个坏主意。相反,尝试以通常的方式创建Bitmap,然后执行Bitmap.LockBits
,然后使用Marshal.Copy
将位图数组复制到Bitmap的非托管内存,这样就完成了,很好并且相对安全。< / p>
这是一个代码片段,说明了整个概念:
byte[] data = new byte[320 * 200 * 1];
Bitmap bmp1 = new Bitmap(320, 200,
System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
Bitmap bmp2 = new Bitmap(320, 200,
System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
var bdata = bmp1.LockBits(new Rectangle(new Point(0, 0), bmp1.Size),
ImageLockMode.WriteOnly, bmp1.PixelFormat);
try
{
Marshal.Copy(data, 0, bdata.Scan0, data.Length);
}
finally
{
bmp1.UnlockBits(bdata);
}
// Do your modifications
bdata = bmp2.LockBits(new Rectangle(new Point(0, 0), bmp2.Size),
ImageLockMode.WriteOnly, bmp2.PixelFormat);
try
{
Marshal.Copy(data, 0, bdata.Scan0, data.Length);
}
finally
{
bmp2.UnlockBits(bdata);
}
这不是性能最佳的代码(它确实需要一些复制),但唯一真正的替代方法是使用unsafe
代码 - 鉴于您当前的明显知识,您实际上不应该这样做关于托管环境。如果你没有正确使用它,它可能会导致一些令人讨厌的问题。在任何情况下,性能提升可能都是微不足道的 - 在你采取不安全的方式之前,先了解你是否真正关心。
有关问题以及使用托管和非托管内存的复杂性的更多信息,您可以查看我的博客http://www.luaan.cz/2014/07/a-quick-introduction-to-managed-and.html它仍然是相当高级别的,但它解释的不仅仅是这个答案。自己的。