C # Переместить растровое изображение в картинке - PullRequest
2 голосов
/ 20 сентября 2011

Я создаю изображение с помощью 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);
}

В следующей итерации:

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

Короче: Iвырезать и скопировать часть изображения, что бы сдвинуть картинку, но ничего не получить.Возможно ли это из-за масштаба на предыдущем шаге?Может я не прав.Посоветуйте алгоритм для сдвига и добавления нового растрового изображения в конец.

Ответы [ 2 ]

1 голос
/ 20 сентября 2011

Я советую вам использовать метод Control.CreateGraphics () в экземпляре Form и писать непосредственно в Form, потому что элемент управления PaintBox довольно медленный.

Попробуйте использовать мою вспомогательную функцию, это позволит вам вставить часть вашего растрового изображения, используя StretchBlt (растяжение) или BitBlt (без растяжения), используя Win32 Interop:

Пример использования:

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);
}
0 голосов
/ 13 ноября 2018

Примечание. Это не полный ответ на вопрос, но я хочу предоставить некоторую полезную информацию для существующего ответа.

В отличие от ответа Артура Мустафина, я предлагаю не использовать Windows GDI напрямую, а использовать методы .net класса Graphics. В моих тестах они работали намного лучше, чем функции GDI.

Код GDI взят из ответа Артура Мустафина.
В ходе теста было отображено изображение с растровым изображением размером 1 Мпикс (1 кбит / с) и растровым изображением 100 Мпикс (10 к 10 кбит) в Picturebox размером 1 кбит / с при рисовании в событии Paint.
Тесты проводились в Visual Studio 2015.3 для Windows 7 Pro x64 SP1, режим отладки, на процессоре Intel Core i7-3630QM с частотой 2,4 ГГц и 16 ГБ ОЗУ.

Результаты:

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