Question

I create image with LockBits from array in cycle and scale to PictureBox.Width * n and 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.

Was it helpful?

Solution

I advice you to use Control.CreateGraphics() method at Form instance, and write on a Form directly, because PaintBox control is quite slow.

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:

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

OTHER TIPS

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. In my tests they have performed a lot better than the GDI functions.

The GDI code is taken from the answer of 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.
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.

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);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top