简体   繁体   English

C#在图片框中移动位图

[英]C# Move bitmap in picturebox

I create image with LockBits from array in cycle and scale to PictureBox.Width * n and Height: 我使用循环从数组中的LockBits创建图像,并缩放到PictureBox.Width * n和Height:

using (var bmp = new Bitmap(len, _height, PixelFormat.Format24bppRgb))
{
            var data = bmp.LockBits(new Rectangle(0, 0, len, _height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            var bytes = data.Stride * data.Height;
            var rgb = new byte[bytes];
            var ptr = data.Scan0;
            Marshal.Copy(data.Scan0, rgb, 0, bytes);


            // …fill array „rgb“

            Marshal.Copy(rgb, 0, ptr, bytes);
            bmp.UnlockBits(data);

            g = _pictureBox.CreateGraphics();
            g.InterpolationMode = InterpolationMode.Default;
            g.DrawImage(bmp, _pictureBox.Width - len * _scaleWidth, 0, len * _scaleWidth, _pictureBox.Height);
}

In the next iterateration: 在下一个迭代中:

Graphics g;
        using (var bmp = new Bitmap(_pictureBox.Image))
        {
            g = _pictureBox.CreateGraphics();
            g.InterpolationMode = InterpolationMode.Default;
            g.DrawImage(_pictureBox.Image, new RectangleF(0, 0, _pictureBox.Width - len * _scaleWidth, _pictureBox.Height), new RectangleF(len * _scaleWidth, 0, _pictureBox.Width * _scaleWidth - len, _height), GraphicsUnit.Pixel);
        }
        g.Dispose();

In short: I cut and copy part of the image that would shift the picture, but do not get anything. 简而言之:我剪切并复制了会移动图片的部分图像,但是什么也没得到。 Is it possible because of the scale in the previous step? 是否可能由于上一步的规模? Maybe I'm wrong. 也许我错了。 Advise the algorithm for shifting and adding a new Bitmap into an end. 建议用于将新的位图平移并添加到末端的算法。

I advice you to use Control.CreateGraphics() method at Form instance, and write on a Form directly, because PaintBox control is quite slow. 我建议您在Form实例上使用Control.CreateGraphics()方法,并直接在Form上编写,因为PaintBox控件非常慢。

Try using my helper function, this will allow you to paste a portion of your bitmap using StretchBlt (stretching) or BitBlt (no stretching) using Win32 Interop: 尝试使用我的辅助函数,这将允许您使用StretchBlt(拉伸)或BitBlt(不拉伸)使用Win32 Interop 粘贴部分位图:

Sample usage: 用法示例:

Graphics graphics = ...;
graphics.GdiDrawImage
(
    image, 
    new Rectangle(
        (int)rectangle.Left, 
        (int)rectangle.Top, 
        (int)rectangle.Width, 
        (int)rectangle.Height
    ), 
    0, 0, image.Width, image.Height
);

Source code: 源代码:

public static class GraphicsHelper
{
    public static void GdiDrawImage(this Graphics graphics, Bitmap image, Rectangle rectangleDst, int nXSrc, int nYSrc, int nWidth, int nHeight)
    {
        IntPtr hdc = graphics.GetHdc();
        IntPtr memdc = GdiInterop.CreateCompatibleDC(hdc);
        IntPtr bmp = image.GetHbitmap();
        GdiInterop.SelectObject(memdc, bmp);
        GdiInterop.SetStretchBltMode(hdc, 0x04);
        GdiInterop.StretchBlt(hdc, rectangleDst.Left, rectangleDst.Top, rectangleDst.Width, rectangleDst.Height, memdc, nXSrc, nYSrc, nWidth, nHeight, GdiInterop.TernaryRasterOperations.SRCCOPY);
        //GdiInterop.BitBlt(..) put it here, if you did not mention stretching the source image
        GdiInterop.DeleteObject(bmp);
        GdiInterop.DeleteDC(memdc);
        graphics.ReleaseHdc(hdc);
    }
}

public class GdiInterop
{
    /// <summary>
    /// Enumeration for the raster operations used in BitBlt.
    /// In C++ these are actually #define. But to use these
    /// constants with C#, a new enumeration _type is defined.
    /// </summary>
    public enum TernaryRasterOperations
    {
        SRCCOPY = 0x00CC0020, // dest = source
        SRCPAINT = 0x00EE0086, // dest = source OR dest
        SRCAND = 0x008800C6, // dest = source AND dest
        SRCINVERT = 0x00660046, // dest = source XOR dest
        SRCERASE = 0x00440328, // dest = source AND (NOT dest)
        NOTSRCCOPY = 0x00330008, // dest = (NOT source)
        NOTSRCERASE = 0x001100A6, // dest = (NOT src) AND (NOT dest)
        MERGECOPY = 0x00C000CA, // dest = (source AND pattern)
        MERGEPAINT = 0x00BB0226, // dest = (NOT source) OR dest
        PATCOPY = 0x00F00021, // dest = pattern
        PATPAINT = 0x00FB0A09, // dest = DPSnoo
        PATINVERT = 0x005A0049, // dest = pattern XOR dest
        DSTINVERT = 0x00550009, // dest = (NOT dest)
        BLACKNESS = 0x00000042, // dest = BLACK
        WHITENESS = 0x00FF0062, // dest = WHITE
    };

    /// <summary>
    /// Enumeration to be used for those Win32 function 
    /// that return BOOL
    /// </summary>
    public enum Bool
    {
        False = 0,
        True
    };

    /// <summary>
    /// Sets the background color.
    /// </summary>
    /// <param name="hdc">The HDC.</param>
    /// <param name="crColor">Color of the cr.</param>
    /// <returns></returns>
    [DllImport("gdi32.dll")]
    public static extern int SetBkColor(IntPtr hdc, int crColor);

    /// <summary>
    /// CreateCompatibleDC
    /// </summary>
    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern IntPtr CreateCompatibleDC(IntPtr hDC);

    /// <summary>
    /// DeleteDC
    /// </summary>
    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern Bool DeleteDC(IntPtr hdc);

    /// <summary>
    /// SelectObject
    /// </summary>
    [DllImport("gdi32.dll", ExactSpelling = true)]
    public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

    /// <summary>
    /// DeleteObject
    /// </summary>
    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern Bool DeleteObject(IntPtr hObject);

    /// <summary>
    /// CreateCompatibleBitmap
    /// </summary>
    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern IntPtr CreateCompatibleBitmap(IntPtr hObject, int width, int height);

    /// <summary>
    /// BitBlt
    /// </summary>
    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern Bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjSource, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

    /// <summary>
    /// StretchBlt
    /// </summary>
    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern Bool StretchBlt(IntPtr hObject, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, IntPtr hObjSource, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, TernaryRasterOperations dwRop);

    /// <summary>
    /// SetStretchBltMode
    /// </summary>
    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern Bool SetStretchBltMode(IntPtr hObject, int nStretchMode);
}

Notice: This is no full answer to the question, but I want to provide some useful information to the existing answer. 注意:这不是问题的完整答案,但是我想为现有答案提供一些有用的信息。

In contrast to the answer of Artur Mustafin, I suggest not to use the Windows GDI directly, but to use the .net methods of the Graphics class. 与Artur Mustafin的答案相反,我建议不要直接使用Windows GDI,而应使用Graphics类的.net方法。 In my tests they have performed a lot better than the GDI functions. 在我的测试中,它们的性能比GDI功能要好得多。

The GDI code is taken from the answer of Artur Mustafin. GDI代码取自Artur Mustafin的答案。
The test was displaying an image of 1Mpix (1k x 1k) Bitmap or 100Mpix (10k x 10k) Bitmap in 1k x 1k Picturebox by drawing in the Paint event. 该测试通过绘制Paint事件在1k x 1k Picturebox中显示1Mpix(1k x 1k)位图或100Mpix(10k x 10k)位图的图像。
The tests were done in Visual Studio 2015.3 on Windows 7 Pro x64 SP1, Debug mode, on an Intel Core i7-3630QM CPU @ 2.4 GHz, 16 GB RAM. 测试是在Windows 7 Pro x64 SP1调试模式下的Visual Studio 2015.3中,在Intel Core i7-3630QM CPU @ 2.4 GHz,16 GB RAM上进行的。

Results: 结果:

1000x Graphics.DrawImage() unscaled       of 100M Bitmap:   36.8s (x86),  24.2s (x64).
1000x Graphics.DrawImage() unscaled       of   1M Bitmap:    5.2s (x86),   3.8s (x64).
 100x Graphics.DrawImage() scaled         of 100M Bitmap:   62.8s (x86),  39.0s (x64).
1000x Graphics.DrawImage() scaled         of   1M Bitmap:    5.2s (x86),   3.8s (x64).
 100x GdiDrawImage() StretchBlockTransfer of 100M Bitmap:  OutOfMem@x86,  88.5s (x64).
1000x GdiDrawImage() StretchBlockTransfer of   1M Bitmap:   12.9s (x86),  11.5s (x64).
 100x GdiDrawImage() BitBlockTransfer     of 100M Bitmap:  OutOfMem@x86,  49.7s (x64).
1000x GdiDrawImage() BitBlockTransfer     of   1M Bitmap:    7.2s (x86),   5.8s (x64).

Test code: 测试代码:

public partial class FormPictureboxPaint : Form
{
  private Bitmap m_oBitmap;

  public FormPictureboxPaint ()
  {
    InitializeComponent ();

    string sFile = Application.StartupPath + @"\..\..\..\bitmap.png";  // The bitmap file contains an image with 10k x 10k pixels.
    m_oBitmap = new Bitmap (sFile);

    if (false)  // CHANGE TO TRUE IF TESTING WITH 1k x 1k BITMAPS
    {
      var oBitmap = new Bitmap (m_oBitmap, new Size (1000, 1000));

      m_oBitmap.Dispose ();
      m_oBitmap = null;
      GC.Collect ();
      GC.WaitForFullGCComplete ();
      GC.WaitForPendingFinalizers ();

      m_oBitmap = oBitmap;
    }
  }

  private void pictureBox1_Paint (object sender, PaintEventArgs e)
  {
    var oGraphics = e.Graphics;
    DateTime dtNow = DateTime.Now;

    // UNCOMMENT THE FOLLOWING LINES FOR TESTS WITH DrawImage
    // COMMENT THE FOLLOWING LINES FOR TESTS WITH GDI
    //for (int ixCnt = 0; ixCnt < 1000; ixCnt++)
    //  PictureboxPaint01 (oGraphics);

    // COMMENT THE FOLLOWING LINES FOR TESTS WITH DrawImage
    // UNCOMMENT THE FOLLOWING LINES FOR TESTS WITH GDI
    for (int ixCnt = 0; ixCnt < 100; ixCnt++)
      PictureboxPaint02 (oGraphics);

    TimeSpan ts = (DateTime.Now - dtNow);
  }

  private void PictureboxPaint01 (Graphics i_oGraphics)
  {
    //_oGraphics.DrawImage (m_oBitmap, new Point ());
    i_oGraphics.DrawImage (m_oBitmap, new Rectangle (0, 0, 1000, 1000));
  }

  private void PictureboxPaint02 (Graphics i_oGraphics)
  {
    // from https://stackoverflow.com/a/7481071
    i_oGraphics.GdiDrawImage
    (
        m_oBitmap,
        new Rectangle (
            (int)pictureBox1.Left,
            (int)pictureBox1.Top,
            (int)pictureBox1.Width,
            (int)pictureBox1.Height
        ),
        0, 0, m_oBitmap.Width, m_oBitmap.Height
    );
  }
}

public static class GraphicsHelper
{
  public static void GdiDrawImage (this Graphics graphics, Bitmap image, Rectangle rectangleDst, int nXSrc, int nYSrc, int nWidth, int nHeight)
  {
    IntPtr hdc = graphics.GetHdc ();
    IntPtr memdc = GdiInterop.CreateCompatibleDC (hdc);
    IntPtr bmp = image.GetHbitmap ();
    GdiInterop.SelectObject (memdc, bmp);
    GdiInterop.SetStretchBltMode (hdc, 0x04);
    GdiInterop.StretchBlt (hdc, rectangleDst.Left, rectangleDst.Top, rectangleDst.Width, rectangleDst.Height, memdc, nXSrc, nYSrc, nWidth, nHeight, GdiInterop.TernaryRasterOperations.SRCCOPY);
    //GdiInterop.BitBlt (hdc, rectangleDst.Left, rectangleDst.Top, rectangleDst.Width, rectangleDst.Height, memdc, nXSrc, nYSrc, GdiInterop.TernaryRasterOperations.SRCCOPY); //put it here, if you did not mention stretching the source image
    GdiInterop.DeleteObject (bmp);
    GdiInterop.DeleteDC (memdc);
    graphics.ReleaseHdc (hdc);
  }
}

public class GdiInterop
{
  public enum TernaryRasterOperations
  {
    SRCCOPY = 0x00CC0020, // dest = source
  };

  public enum Bool
  {
    False = 0,
    True
  };

  [DllImport ("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  public static extern IntPtr CreateCompatibleDC (IntPtr hDC);

  [DllImport ("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  public static extern Bool DeleteDC (IntPtr hdc);

  [DllImport ("gdi32.dll", ExactSpelling = true)]
  public static extern IntPtr SelectObject (IntPtr hDC, IntPtr hObject);

  [DllImport ("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  public static extern Bool DeleteObject (IntPtr hObject);

  [DllImport ("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  public static extern Bool BitBlt (IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjSource, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

  [DllImport ("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  public static extern Bool StretchBlt (IntPtr hObject, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, IntPtr hObjSource, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, TernaryRasterOperations dwRop);

  [DllImport ("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  public static extern Bool SetStretchBltMode (IntPtr hObject, int nStretchMode);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM