我在循环中使用数组中的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();
简而言之:我剪切并复制了可能会移动图片的部分图像,但却没有得到任何结果。是否有可能因为上一步的规模? 也许我错了。建议用于移位和添加新Bitmap的算法。
答案 0 :(得分:1)
我建议你在Form实例中使用Control.CreateGraphics()方法,并直接在Form上写,因为PaintBox控件很慢。
尝试使用我的帮助函数,这将允许您使用Win32 Interop使用StretchBlt(拉伸)或BitBlt(无拉伸)粘贴部分位图:
样本用法:
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);
}
答案 1 :(得分:0)
注意:这不是问题的完整答案,但是我想为现有答案提供一些有用的信息。
与Artur Mustafin的回答相反,我建议不要直接使用Windows GDI,而应使用Graphics
类的.net方法。在我的测试中,它们的性能比GDI功能要好得多。
GDI代码取自Artur Mustafin的回答。
该测试通过绘制Paint事件在1k x 1k Picturebox中显示1Mpix(1k x 1k)位图或100Mpix(10k x 10k)位图的图像。
这些测试是在Visual Studio 2015.3的Windows 7 Pro x64 SP1调试模式下,在Intel Core 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);
}