2015-08-15 32 views
1

我试图做一种Grahpics.DrawImage实现使用不安全的代码和指针当然。C#绘制图像的实现

在这种情况下,即时通讯试图绘制一个更大的宽度(都32bppArgb)的小位图。

这是我的代码

private static unsafe void Draw(Bitmap bmp, Bitmap bmp2, int xPoint, int yPoint) 
    { 

     BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, bmp.PixelFormat); 
     BitmapData bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat); 

     IntPtr scan0 = bmData.Scan0; 
     IntPtr scan02 = bmData2.Scan0; 

     int stride = bmData.Stride; 
     int stride2 = bmData2.Stride; 

     int nWidth = bmp2.Width; 
     int nHeight = bmp2.Height; 

     int sourceX = 0; 
     int sourceY = 0; 
     byte* p = (byte*)scan0.ToPointer(); 
     p += yPoint * stride + xPoint * 4; 
     byte* p2 = (byte*)scan02.ToPointer(); 
     p2 += sourceY * stride2 + sourceX * 4; 
     int bytes = nWidth * 4; 

     for (int y = 0; y < nHeight; y++) 
     { 

      for (int x = 0; x <nWidth; x++) 
      { 


       p[0] = p2[0]; 
       p[1] = p2[1]; 
       p[2] = p2[2]; 
       p[3] = p2[3]; 

      } 

      p += 4; 
      p2 += 4; 
     } 

     bmp.UnlockBits(bmData); 
     bmp2.UnlockBits(bmData2); 
    } 

这是更新的代码

+0

你把它拷贝到自身。对于'b2',你应该使用'scan02'而不是'scan0'。您还应该为'b2'使用'stride2'。然后你应该使用'stride'和'stride2'将'b'和'b2'移到下一行,而不是假设下一行在当前之后立即开始。线条之间可以填充,当图像在内存中倒置时,步幅甚至可以为负值。 – Guffa

+0

@Guffa ohh ..没有看到它......谢谢你! – Slashy

+0

@Guffa等待..现在我在这里得到一个异常'p [0] = p2 [0]; **试图读取或写入受保护的内存** – Slashy

回答

2

我做了一些改动,使其工作:

  • 使用ImageLockMode.WriteOnlyLockBits呼吁像你想写信给。
  • 请勿移动p2根据xPointyPoint

我看到你在外循环内设置了指针,所以你不需要在循环结尾移动指针。我建议你在外层循环之外计算起点,并将指针移动到其中。

我还建议这些变化,我做到了:

  • 添加检查图像的界限,让你不小心尝试将图像之外绘制。

  • 使方法void。返回一个位图表明它创建一个新的位图,而不是改变传入它的一个位图。

我添加了一个pixelBytes参数,以使它适用于不同的像素格式。 (主要是因为我有发生的JPEG,PNG图像不与测试。)

代码:

private static unsafe void Draw(Bitmap bmp, Bitmap bmp2, int xPoint, int yPoint, int pixelBytes) { 

    BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, bmp.PixelFormat); 
    BitmapData bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat); 

    IntPtr scan0 = bmData.Scan0; 
    IntPtr scan02 = bmData2.Scan0; 

    int stride = bmData.Stride; 
    int stride2 = bmData2.Stride; 

    int nWidth = bmp2.Width; 
    int nHeight = bmp2.Height; 

    int sourceX = 0; 
    int sourceY = 0; 

    if (xPoint < 0) { 
    sourceX = -xPoint; 
    nWidth -= sourceX; 
    xPoint = 0; 
    } 
    if (yPoint < 0) { 
    sourceY = -yPoint; 
    nHeight -= sourceY; 
    yPoint = 0; 
    } 
    if (xPoint + nWidth > bmp.Width) { 
    nWidth = bmp.Width - xPoint; 
    } 
    if (yPoint + nHeight > bmp.Height) { 
    nHeight = bmp.Height - yPoint; 
    } 

    if (nWidth > 0 && nHeight > 0) { 

    byte* p = (byte*)scan0.ToPointer(); 
    p += yPoint * stride + xPoint * pixelBytes; 
    byte* p2 = (byte*)scan02.ToPointer(); 
    p2 += sourceY * stride2 + sourceX * pixelBytes; 

    int bytes = nWidth * pixelBytes; 

    for (int y = 0; y < nHeight; y++) { 
     for (int x = 0; x < bytes; x++) { 
     p[0] = p2[0]; 
     p++; 
     p2++; 
     } 
     p += stride - nWidth * pixelBytes; 
     p2 += stride2 - nWidth * pixelBytes; 
    } 

    } 

    bmp.UnlockBits(bmData); 
    bmp2.UnlockBits(bmData2); 
} 
+0

谢谢你,真棒!你的代码是直截了当的!+1 – Slashy

+0

最后一件事就是确定了这种方法的解析时间,它比我的速度慢(我知道它没有工作,但仍然)。我认为主要的瓶颈是内部循环运行'字节'时间...它可以运行得更快吗?你能更新我的代码中的某些东西吗?巨大的巨大感谢兄弟!你的回答很棒,但是有点慢......先谢谢了! – Slashy

+0

@Slashy:如果您删除'pixelBytes'参数并将其设为循环像素而不是字节,则应该返回该性能。 – Guffa