diff options
Diffstat (limited to 'Software/Visual_Studio/Tango.ScreenCapture/DXScreenCapture.cs')
| -rw-r--r-- | Software/Visual_Studio/Tango.ScreenCapture/DXScreenCapture.cs | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Tango.ScreenCapture/DXScreenCapture.cs b/Software/Visual_Studio/Tango.ScreenCapture/DXScreenCapture.cs new file mode 100644 index 000000000..77428fd8d --- /dev/null +++ b/Software/Visual_Studio/Tango.ScreenCapture/DXScreenCapture.cs @@ -0,0 +1,436 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpDX; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using Device = SharpDX.Direct3D11.Device; +using MapFlags = SharpDX.Direct3D11.MapFlags; +using System.IO; +using System.Windows.Media.Imaging; +using System.Runtime.InteropServices; +using System.Windows.Media; +using System.Runtime.ExceptionServices; +using System.Diagnostics; + +namespace Tango.RemoteDesktop +{ + public class DXScreenCapture : IDisposable + { + #region INTERNALS + + internal static class User32 + { + public const Int32 CURSOR_SHOWING = 0x00000001; + + [StructLayout(LayoutKind.Sequential)] + public struct ICONINFO + { + public bool fIcon; + public Int32 xHotspot; + public Int32 yHotspot; + public IntPtr hbmMask; + public IntPtr hbmColor; + } + + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public Int32 x; + public Int32 y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CURSORINFO + { + public Int32 cbSize; + public Int32 flags; + public IntPtr hCursor; + public POINT ptScreenPos; + } + + [DllImport("user32.dll")] + public static extern bool GetCursorInfo(out CURSORINFO pci); + + [DllImport("user32.dll")] + public static extern IntPtr CopyIcon(IntPtr hIcon); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool DestroyIcon(IntPtr hIcon); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool DrawIconEx(IntPtr hdc, int xLeft, int yTop, IntPtr hIcon, int cxWidth, int cyHeight, int istepIfAniCur, IntPtr hbrFlickerFreeDraw, int diFlags); + + [DllImport("user32.dll")] + public static extern bool DrawIcon(IntPtr hdc, int x, int y, IntPtr hIcon); + + [DllImport("user32.dll")] + public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo); + } + + #endregion + + #region Internal Classes + + [StructLayout(LayoutKind.Sequential)] + struct CURSORINFO + { + public Int32 cbSize; + public Int32 flags; + public IntPtr hCursor; + public POINTAPI ptScreenPos; + } + + [StructLayout(LayoutKind.Sequential)] + struct POINTAPI + { + public int x; + public int y; + } + + [DllImport("user32.dll")] + static extern bool GetCursorInfo(out CURSORINFO pci); + + [DllImport("user32.dll")] + static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon); + + const Int32 CURSOR_SHOWING = 0x00000001; + + internal struct SIZE + { + public int cx; + public int cy; + } + + internal class GDIStuff + { + #region Class Variables + public const int SRCCOPY = 13369376; + #endregion + + + #region Class Functions + [DllImport("gdi32.dll", EntryPoint = "CreateDC")] + public static extern IntPtr CreateDC(IntPtr lpszDriver, string lpszDevice, IntPtr lpszOutput, IntPtr lpInitData); + + [DllImport("gdi32.dll", EntryPoint = "DeleteDC")] + public static extern IntPtr DeleteDC(IntPtr hDc); + + [DllImport("gdi32.dll", EntryPoint = "DeleteObject")] + public static extern IntPtr DeleteObject(IntPtr hDc); + + [DllImport("gdi32.dll", EntryPoint = "BitBlt")] + public static extern bool BitBlt(IntPtr hdcDest, int xDest, + int yDest, int wDest, + int hDest, IntPtr hdcSource, + int xSrc, int ySrc, int RasterOp); + + [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")] + public static extern IntPtr CreateCompatibleBitmap + (IntPtr hdc, int nWidth, int nHeight); + + [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC")] + public static extern IntPtr CreateCompatibleDC(IntPtr hdc); + + [DllImport("gdi32.dll", EntryPoint = "SelectObject")] + public static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp); + #endregion + } + + internal class Win32Stuff + { + + #region Class Variables + + public const int SM_CXSCREEN = 0; + public const int SM_CYSCREEN = 1; + + public const Int32 CURSOR_SHOWING = 0x00000001; + + [StructLayout(LayoutKind.Sequential)] + public struct ICONINFO + { + public bool fIcon; // Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies + public Int32 xHotspot; // Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot + public Int32 yHotspot; // Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot + public IntPtr hbmMask; // (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, + public IntPtr hbmColor; // (HBITMAP) Handle to the icon color bitmap. This member can be optional if this + } + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public Int32 x; + public Int32 y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CURSORINFO + { + public Int32 cbSize; // Specifies the size, in bytes, of the structure. + public Int32 flags; // Specifies the cursor state. This parameter can be one of the following values: + public IntPtr hCursor; // Handle to the cursor. + public POINT ptScreenPos; // A POINT structure that receives the screen coordinates of the cursor. + } + + #endregion + + + #region Class Functions + + [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")] + public static extern IntPtr GetDesktopWindow(); + + [DllImport("user32.dll", EntryPoint = "GetDC")] + public static extern IntPtr GetDC(IntPtr ptr); + + [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")] + public static extern int GetSystemMetrics(int abc); + + [DllImport("user32.dll", EntryPoint = "GetWindowDC")] + public static extern IntPtr GetWindowDC(Int32 ptr); + + [DllImport("user32.dll", EntryPoint = "ReleaseDC")] + public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc); + + + [DllImport("user32.dll", EntryPoint = "GetCursorInfo")] + public static extern bool GetCursorInfo(out CURSORINFO pci); + + [DllImport("user32.dll", EntryPoint = "CopyIcon")] + public static extern IntPtr CopyIcon(IntPtr hIcon); + + [DllImport("user32.dll", SetLastError = true)] + static extern bool DestroyIcon(IntPtr hIcon); + + [DllImport("user32.dll", EntryPoint = "GetIconInfo")] + public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo); + + + #endregion + } + + #endregion + + // # of graphics card adapter + private int numAdapter = 0; + + // # of output device (i.e. monitor) + private int numOutput = 0; + + private Device device; + + private Output1 output1; + + private Texture2DDescription textureDesc; + + private OutputDuplication duplicatedOutput; + + private int widthDX; + + private int heightDX; + + public void Initialize() + { + // Create DXGI Factory1 + var factory = new Factory1(); + var adapter = factory.GetAdapter1(numAdapter); + + // Create device from Adapter + device = new Device(adapter); + + // Get DXGI.Output + var output = adapter.GetOutput(numOutput); + output1 = output.QueryInterface<Output1>(); + + // Width/Height of desktop to capture + widthDX = ((Rectangle)output.Description.DesktopBounds).Width; + heightDX = ((Rectangle)output.Description.DesktopBounds).Height; + + // Create Staging texture CPU-accessible + textureDesc = new Texture2DDescription + { + CpuAccessFlags = CpuAccessFlags.Read, + BindFlags = BindFlags.None, + Format = Format.B8G8R8A8_UNorm, + Width = widthDX, + Height = heightDX, + OptionFlags = ResourceOptionFlags.None, + MipLevels = 1, + ArraySize = 1, + SampleDescription = { Count = 1, Quality = 0 }, + Usage = ResourceUsage.Staging + }; + + // Duplicate the output + duplicatedOutput = output1.DuplicateOutput(device); + } + + /// <summary> + /// Captures the bitmap source. + /// </summary> + /// <param name="left">The left.</param> + /// <param name="top">The top.</param> + /// <param name="width">The width.</param> + /// <param name="height">The height.</param> + /// <param name="captureCursor">Append cursor.</param> + /// <returns></returns> + [HandleProcessCorruptedStateExceptions] + [DebuggerStepThrough] + [DebuggerHidden] + public BitmapSource CaptureBitmapSource(int left, int top, int width, int height, bool captureCursor) + { + var screenTexture = new Texture2D(device, textureDesc); + + try + { + SharpDX.DXGI.Resource screenResource; + OutputDuplicateFrameInformation duplicateFrameInformation; + + // Try to get duplicated frame within given time + duplicatedOutput.AcquireNextFrame(5, out duplicateFrameInformation, out screenResource); + + //if (i > 0) + //{ + // copy resource into memory that can be accessed by the CPU + using (var screenTexture2D = screenResource.QueryInterface<Texture2D>()) + device.ImmediateContext.CopyResource(screenTexture2D, screenTexture); + + // Get the desktop capture texture + var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, MapFlags.None); + + // Create Drawing.Bitmap + var bitmap = new System.Drawing.Bitmap(widthDX, heightDX, System.Drawing.Imaging.PixelFormat.Format32bppRgb); + var boundsRect = new System.Drawing.Rectangle(0, 0, widthDX, heightDX); + + // Copy pixels from screen capture Texture to GDI bitmap + var mapDest = bitmap.LockBits(boundsRect, System.Drawing.Imaging.ImageLockMode.WriteOnly, bitmap.PixelFormat); + var sourcePtr = mapSource.DataPointer; + var destPtr = mapDest.Scan0; + for (int y = 0; y < height; y++) + { + // Copy a single line + Utilities.CopyMemory(destPtr, sourcePtr, widthDX * 4); + + // Advance pointers + sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch); + destPtr = IntPtr.Add(destPtr, mapDest.Stride); + } + + // Release source and dest locks + bitmap.UnlockBits(mapDest); + device.ImmediateContext.UnmapSubresource(screenTexture, 0); + + if (left > 0 || top > 0 || width < widthDX || height < heightDX) + { + CropImage(bitmap, new System.Drawing.Rectangle(left, top, width, height)); + } + + if (captureCursor) + { + using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap)) + { + ApplyCursor(g, bitmap, left, top); + } + } + + MemoryStream ms = new MemoryStream(); + bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); + ms.Position = 0; + var img = GetBitmapImage(ms); + img.Freeze(); + + bitmap.Dispose(); + screenTexture.Dispose(); + screenResource.Dispose(); + duplicatedOutput.ReleaseFrame(); + + + + return img; + } + catch (SharpDXException e) + { + if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) + { + Trace.TraceError(e.Message); + Trace.TraceError(e.StackTrace); + } + + throw; + } + } + + /// <summary> + /// Applies the cursor icon. + /// </summary> + /// <param name="g">The graphics context.</param> + /// <param name="bitmap">The bitmap.</param> + /// <param name="left">The left.</param> + /// <param name="top">The top.</param> + [DebuggerHidden] + [DebuggerStepThrough] + private void ApplyCursor(System.Drawing.Graphics g, System.Drawing.Bitmap bitmap, int left, int top) + { + try + { + CURSORINFO pci; + pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO)); + + if (GetCursorInfo(out pci)) + { + if (pci.flags == CURSOR_SHOWING) + { + //var iconPointer = User32.CopyIcon(pci.hCursor); + //User32.ICONINFO iconInfo; + //User32.GetIconInfo(iconPointer, out iconInfo); + + var hdc = g.GetHdc(); + User32.DrawIconEx(hdc, pci.ptScreenPos.x - left, pci.ptScreenPos.y - top, pci.hCursor, 0, 0, 0, IntPtr.Zero, 0x0003); + + //User32.DestroyIcon(iconPointer); + GDIStuff.DeleteObject(pci.hCursor); + } + } + g.ReleaseHdc(); + } + catch { } + } + + /// <summary> + /// Gets the bitmap image. + /// </summary> + /// <param name="ms">The ms.</param> + /// <returns></returns> + private BitmapImage GetBitmapImage(MemoryStream ms) + { + var bitmapImage = new BitmapImage(); + bitmapImage.BeginInit(); + bitmapImage.StreamSource = ms; + bitmapImage.EndInit(); + return bitmapImage; + } + + /// <summary> + /// Crops the image. + /// </summary> + /// <param name="img">The img.</param> + /// <param name="cropArea">The crop area.</param> + /// <returns></returns> + private System.Drawing.Bitmap CropImage(System.Drawing.Bitmap img, System.Drawing.Rectangle cropArea) + { + System.Drawing.Bitmap bmpImage = new System.Drawing.Bitmap(img); + return bmpImage.Clone(cropArea, bmpImage.PixelFormat); + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + duplicatedOutput.Dispose(); + device.Dispose(); + } + } +} |
