0

这可能不是一个措辞恰当的问题,因为我不确定发生了什么,所以我不知道如何有针对性地问它。我正在努力学习,我希望我能在这方面得到一些指导。感谢您对新手的耐心。

我有一段代码正在修改。它显示图像。我想修改图像,并将其显示在不同的窗口中。我复制显示图像的代码,进行修改,然后它显示原始图像和修改后的图像的修改后的图像。

似乎 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
4

1 回答 1

2

你所做的肯定是不安全的。你为什么做这个?离开安全、受管理的环境,您有什么理由如此自在?

位图是围绕它创建的byte[]。只要您不介意将一个固定byte[]在托管内存中,就可以了(暂时可以,而不是在应用程序的持续时间等)。但是,在下一行,您释放指针!

然后你使用相同的byte[],修改它,并将它用于另一个位图。繁荣,它仍然是相同的字节数组。两个位图具有相同的内容应该不足为奇 - 您要求这样做。

它有时有效而有时无效的原因是,如果 GC 不移动句柄,则两个位图都是正确的。但是,如果 GC 移动字节数组,Bitmap 将无法调整 - 它们仍将指向内存中的相同位置(现在是错误的)。

您必须了解的是 GCHandle 不会创建新对象。只要 GCHandle 存在,它只是指示 GC 不要弄乱物理位置(嗯,在虚拟内存中,但是......)。如果要创建一个新对象,请执行类似byte[].Clone(). 但是,您仍然必须在位图的整个生命周期内固定句柄,这通常是个坏主意。相反,尝试以通常的方式创建位图,然后执行Bitmap.LockBits,然后使用Marshal.Copy将位图数组复制到位图的非托管内存中,这样就完成了,很好而且相对安全。

这是一个说明整个概念的代码片段:

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它仍然是相当高级的,但它本身解释的不仅仅是这个答案。

于 2014-02-03T08:52:42.983 回答