在图片框中移动位图

本文关键字:移动 位图 | 更新日期: 2023-09-27 18:09:09

我用LockBits从数组中创建图像,并按比例缩放到PictureBox。宽度* n,高度:

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);
}

在下一次迭代中:

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();

简而言之:我剪切并复制了图像的一部分,这将移动图像,但没有得到任何东西。是否因为上一步的比例问题?也许我错了。建议将新的位图移动和添加到末尾的算法

在图片框中移动位图

我建议你在Form实例中使用control . creategraphics()方法,并直接在Form上编写,因为PaintBox控件相当慢。

尝试使用我的辅助函数,这将允许您使用StretchBlt(拉伸)或BitBlt(不拉伸)使用Win32互操作粘贴位图的一部分:

示例用法:

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
);
源代码:

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);
}

注意:这不是问题的完整答案,但我想为现有的答案提供一些有用的信息。

与Artur Mustafin的回答相反,我建议不要直接使用Windows GDI,而是使用Graphics类的。net方法。在我的测试中,它们的性能比GDI功能要好得多。

GDI代码摘自Artur Mustafin的回答。
该测试通过在Paint事件中绘制,在1k x 1k Picturebox中显示1Mpix (1k x 1k)位图或100Mpix (10k x 10k)位图的图像。
测试是在Windows 7 Pro x64 SP1上的Visual Studio 2015.3中进行的,调试模式,英特尔酷睿i7-3630QM CPU @ 2.4 GHz, 16 GB RAM。

结果:

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).

测试代码:

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);
}