使用sharpdx和aforge减少CPU使用率,提高帧率

This commit is contained in:
snltty
2023-10-01 21:37:27 +08:00
parent 5f6f822756
commit f47b615210
21 changed files with 1703 additions and 247 deletions

View File

@@ -265,7 +265,7 @@ namespace cmonitor
public int ReportDelay { get; set; } = 30;
public float ScreenScale { get; set; } = 0.2f;
public int ScreenDelay { get; set; } = 200;
public int ScreenDelay { get; set; } = 30;
public string Version { get; set; } = "1.0.0.1";
public bool IsCLient { get; set; }
@@ -396,7 +396,7 @@ namespace cmonitor
}
if (dic.ContainsKey("screen-delay") == false || string.IsNullOrWhiteSpace(dic["screen-delay"]))
{
dic["screen-delay"] = "200";
dic["screen-delay"] = "100";
}
return true;
}

View File

@@ -14,6 +14,6 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>false</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<History>True|2023-09-04T10:19:54.7492652Z;True|2023-09-04T18:19:32.2969345+08:00;False|2023-09-04T18:18:51.7827366+08:00;True|2023-09-04T18:15:31.6783417+08:00;True|2023-09-04T18:14:40.9964104+08:00;</History>
<History>True|2023-10-01T09:27:31.0065885Z;True|2023-09-04T18:19:54.7492652+08:00;True|2023-09-04T18:19:32.2969345+08:00;False|2023-09-04T18:18:51.7827366+08:00;True|2023-09-04T18:15:31.6783417+08:00;True|2023-09-04T18:14:40.9964104+08:00;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>

View File

@@ -64,6 +64,7 @@
<ItemGroup>
<PackageReference Include="MemoryPack" Version="1.9.16" />
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
<PackageReference Include="System.Management" Version="7.0.2" />
<TrimmerRootAssembly Include="System.Management" />
</ItemGroup>

View File

@@ -0,0 +1,154 @@
using cmonitor.server.client.reports.screen.aforge;
using System.Buffers;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using Image = System.Drawing.Image;
namespace cmonitor.server.client.reports.screen
{
public sealed class GdiCapture
{
static ResizeBilinear resizeFilter;
public static byte[] Capture(float configScale, out int length)
{
length = 0;
if (GetScale(out int scaleX, out int scaleY, out int sourceWidth, out int sourceHeight) == false)
{
return Array.Empty<byte>();
}
if (OperatingSystem.IsWindows())
{
IntPtr hdc = GetDC(IntPtr.Zero);
using Bitmap source = new Bitmap(sourceWidth, sourceHeight);
using (Graphics g = Graphics.FromImage(source))
{
g.CopyFromScreen(0, 0, 0, 0, source.Size, CopyPixelOperation.SourceCopy);
DrawCursorIcon(g, sourceWidth, scaleX, scaleY, configScale);
g.Dispose();
}
ReleaseDC(IntPtr.Zero, hdc);
GetNewSize(sourceWidth, sourceHeight, scaleX, scaleY, configScale, out int width, out int height);
if (resizeFilter == null)
{
resizeFilter = new ResizeBilinear(width, height);
}
Bitmap bmp = resizeFilter.Apply(source);
using Image image = bmp;
using MemoryStream ms = new MemoryStream();
image.Save(ms, ImageFormat.Jpeg);
ms.Seek(0, SeekOrigin.Begin);
length = (int)ms.Length;
byte[] bytes = ArrayPool<byte>.Shared.Rent((int)ms.Length);
ms.Read(bytes);
return bytes;
}
return Array.Empty<byte>();
}
public static void Return(byte[] bytes)
{
ArrayPool<byte>.Shared.Return(bytes);
}
public static bool GetScale(out int x, out int y, out int sourceWidth, out int sourceHeight)
{
x = 1;
y = 1;
sourceWidth = 0;
sourceHeight = 0;
IntPtr hdc = GetDC(IntPtr.Zero);
if (hdc != IntPtr.Zero)
{
sourceWidth = GetDeviceCaps(hdc, DESKTOPHORZRES);
sourceHeight = GetDeviceCaps(hdc, DESKTOPVERTRES);
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
x = (int)(sourceWidth * 1.0 / screenWidth);
y = (int)(sourceHeight * 1.0 / screenHeight);
ReleaseDC(IntPtr.Zero, hdc);
return true;
}
return false;
}
public static bool GetNewSize(int sourceWidth, int sourceHeight, int scaleX, int scaleY, float configScale, out int width, out int height)
{
width = (int)(sourceWidth * 1.0 / scaleX * configScale);
height = (int)(sourceHeight * 1.0 / scaleY * configScale);
return true;
}
public static void DrawCursorIcon(Graphics g, int sourceWidth, int scaleX, int scaleY, float configScale)
{
if (OperatingSystem.IsWindows())
{
int curWidth = (int)(sourceWidth * configScale * configScale);
CURSORINFO pci;
pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out pci))
{
if (pci.flags == CURSOR_SHOWING)
{
var hdc1 = g.GetHdc();
DrawIconEx(hdc1, pci.ptScreenPos.x * scaleX, pci.ptScreenPos.y * scaleY, pci.hCursor, curWidth, curWidth, 0, IntPtr.Zero, DI_NORMAL);
g.ReleaseHdc();
}
}
}
}
private const int DESKTOPVERTRES = 117;
private const int DESKTOPHORZRES = 118;
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr ptr);
[DllImport("user32.dll", EntryPoint = "ReleaseDC")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("gdi32.dll", EntryPoint = "GetDeviceCaps", SetLastError = true)]
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
private static extern int GetSystemMetrics(int mVal);
[StructLayout(LayoutKind.Sequential)]
private struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public POINTAPI ptScreenPos;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINTAPI
{
public int x;
public int y;
}
private const Int32 CURSOR_SHOWING = 0x0001;
private const Int32 DI_NORMAL = 0x0003;
[DllImport("user32.dll")]
private static extern bool GetCursorInfo(out CURSORINFO pci);
[DllImport("user32.dll", SetLastError = true)]
static extern bool DrawIconEx(IntPtr hdc, int xLeft, int yTop, IntPtr hIcon, int cxWidth, int cyHeight, int istepIfAniCur, IntPtr hbrFlickerFreeDraw, int diFlags);
}
}

View File

@@ -2,13 +2,8 @@
using common.libs;
using cmonitor.server.service.messengers.screen;
using System.Runtime.InteropServices;
using System.Buffers;
using MemoryPack;
#if DEBUG || RELEASE
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing;
#endif
using cmonitor.server.client.reports.screen.sharpDX;
namespace cmonitor.server.client.reports.screen
{
@@ -21,6 +16,7 @@ namespace cmonitor.server.client.reports.screen
private readonly Config config;
private ScreenReportInfo report = new ScreenReportInfo();
private readonly DesktopDuplicator desktopDuplicator;
public ScreenReport(ClientSignInState clientSignInState, MessengerSender messengerSender, Config config)
{
@@ -32,6 +28,7 @@ namespace cmonitor.server.client.reports.screen
ScreenCaptureTask();
InitLastInputInfo();
}
desktopDuplicator = new DesktopDuplicator(0);
}
public object GetReports()
{
@@ -70,7 +67,7 @@ namespace cmonitor.server.client.reports.screen
}
private async Task SendScreenCapture()
{
byte[] bytes = ScreenCapture(out int length);
byte[] bytes = ScreenCapture2(out int length);
if (length > 0)
{
await messengerSender.SendOnly(new MessageRequestWrap
@@ -79,253 +76,31 @@ namespace cmonitor.server.client.reports.screen
MessengerId = (ushort)ScreenMessengerIds.Report,
Payload = bytes.AsMemory(0, length),
});
Return(bytes);
GdiCapture.Return(bytes);
}
}
private byte[] ScreenCapture(out int length)
{
length = 0;
#if DEBUG || RELEASE
if (OperatingSystem.IsWindows())
{
IntPtr hdc = GetDC(IntPtr.Zero);
if (hdc != IntPtr.Zero)
{
int sourceWidth = GetDeviceCaps(hdc, DESKTOPHORZRES);
int sourceHeight = GetDeviceCaps(hdc, DESKTOPVERTRES);
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int scaleX = (int)(sourceWidth * 1.0 / screenWidth);
int scaleY = (int)(sourceHeight * 1.0 / screenHeight);
int newWidth = (int)(sourceWidth * 1.0 / scaleX * config.ScreenScale);
int newHeight = (int)(sourceHeight * 1.0 / scaleY * config.ScreenScale);
int curWidth = (int)(sourceWidth * config.ScreenScale * config.ScreenScale);
using Bitmap source = new Bitmap(sourceWidth, sourceHeight);
using (Graphics g = Graphics.FromImage(source))
{
g.CopyFromScreen(0, 0, 0, 0, source.Size, CopyPixelOperation.SourceCopy);
CURSORINFO pci;
pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out pci))
{
if (pci.flags == CURSOR_SHOWING)
{
var hdc1 = g.GetHdc();
DrawIconEx(hdc1, pci.ptScreenPos.x * scaleX, pci.ptScreenPos.y * scaleY, pci.hCursor, curWidth, curWidth, 0, IntPtr.Zero, DI_NORMAL);
g.ReleaseHdc();
}
}
g.Dispose();
}
ReleaseDC(IntPtr.Zero, hdc);
Bitmap bmp = new Bitmap(newWidth, newHeight);
bmp.SetResolution(source.HorizontalResolution, source.VerticalResolution);
using Graphics graphic = Graphics.FromImage(bmp);
graphic.SmoothingMode = SmoothingMode.HighQuality;
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.DrawImage(source, new Rectangle(0, 0, newWidth, newHeight));
using Image image = bmp;
using MemoryStream ms = new MemoryStream();
image.Save(ms, ImageFormat.Jpeg);
ms.Seek(0, SeekOrigin.Begin);
length = (int)ms.Length;
byte[] bytes = ArrayPool<byte>.Shared.Rent((int)ms.Length);
ms.Read(bytes);
return bytes;
}
}
return GdiCapture.Capture(config.ScreenScale, out length);
#else
return Array.Empty<byte>();
#endif
}
private byte[] ScreenCapture2(out int length)
{
length = 0;
if (GdiCapture.GetScale(out int x, out int y, out int sourceWidth, out int sourceHeight))
{
GdiCapture.GetNewSize(sourceWidth, sourceHeight, x, y, config.ScreenScale, out int width, out int height);
return desktopDuplicator.GetLatestFrame(width, height, out length);
}
return Array.Empty<byte>();
}
/*
Bitmap previousFrame;
private Bitmap DiffArea(Bitmap currentFrame, out int x, out int y)
{
Bitmap bmp;
x = 0; y = 0;
if (previousFrame == null)
{
int newWidth = (int)(currentFrame.Width * config.ScreenScale);
int newHeight = (int)(currentFrame.Height * config.ScreenScale);
bmp = new Bitmap(newWidth, newHeight);
}
else
{
Rectangle rectangle = DiffArea(currentFrame, previousFrame);
bmp = new Bitmap(rectangle.Width, rectangle.Height);
x = rectangle.X;
y = rectangle.X;
previousFrame.Dispose();
previousFrame = null;
}
bmp.SetResolution(currentFrame.HorizontalResolution, currentFrame.VerticalResolution);
using Graphics graphic = Graphics.FromImage(bmp);
graphic.SmoothingMode = SmoothingMode.HighQuality;
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle rectangle1 = new Rectangle(x, y, bmp.Width, bmp.Height);
graphic.DrawImage(currentFrame, rectangle1);
//previousFrame = currentFrame;
return bmp;
}
private Rectangle DiffArea(Bitmap currentFrame, Bitmap previousFrame)
{
if (currentFrame.Height != previousFrame.Height || currentFrame.Width != previousFrame.Width)
{
throw new Exception("Bitmaps are not of equal dimensions.");
}
if (!Bitmap.IsAlphaPixelFormat(currentFrame.PixelFormat) || !Bitmap.IsAlphaPixelFormat(previousFrame.PixelFormat) ||
!Bitmap.IsCanonicalPixelFormat(currentFrame.PixelFormat) || !Bitmap.IsCanonicalPixelFormat(previousFrame.PixelFormat))
{
throw new Exception("Bitmaps must be 32 bits per pixel and contain alpha channel.");
}
var width = currentFrame.Width;
var height = currentFrame.Height;
int left = int.MaxValue;
int top = int.MaxValue;
int right = int.MinValue;
int bottom = int.MinValue;
BitmapData bd1 = null;
BitmapData bd2 = null;
try
{
bd1 = previousFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, currentFrame.PixelFormat);
bd2 = currentFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, previousFrame.PixelFormat);
var bytesPerPixel = Bitmap.GetPixelFormatSize(currentFrame.PixelFormat) / 8;
var totalSize = bd1.Height * bd1.Width * bytesPerPixel;
unsafe
{
byte* scan1 = (byte*)bd1.Scan0.ToPointer();
byte* scan2 = (byte*)bd2.Scan0.ToPointer();
for (int counter = 0; counter < totalSize - bytesPerPixel; counter += bytesPerPixel)
{
byte* data1 = scan1 + counter;
byte* data2 = scan2 + counter;
if (data1[0] != data2[0] ||
data1[1] != data2[1] ||
data1[2] != data2[2] ||
data1[3] != data2[3])
{
var pixel = counter / 4;
var row = (int)Math.Floor((double)pixel / bd1.Width);
var column = pixel % bd1.Width;
if (row < top)
{
top = row;
}
if (row > bottom)
{
bottom = row;
}
if (column < left)
{
left = column;
}
if (column > right)
{
right = column;
}
}
}
}
if (left < right && top < bottom)
{
left = Math.Max(left - 10, 0);
top = Math.Max(top - 10, 0);
right = Math.Min(right + 10, width);
bottom = Math.Min(bottom + 10, height);
return new Rectangle(left, top, right - left, bottom - top);
}
else
{
return Rectangle.Empty;
}
}
catch
{
return Rectangle.Empty;
}
finally
{
currentFrame.UnlockBits(bd1);
previousFrame.UnlockBits(bd2);
}
}
*/
private void Return(byte[] bytes)
{
ArrayPool<byte>.Shared.Return(bytes);
}
private const int DESKTOPVERTRES = 117;
private const int DESKTOPHORZRES = 118;
private const int LOGPIXELSX = 88;
private const int LOGPIXELSY = 90;
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr ptr);
[DllImport("user32.dll", EntryPoint = "ReleaseDC")]
static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("gdi32.dll", EntryPoint = "GetDeviceCaps", SetLastError = true)]
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
private static extern int GetSystemMetrics(int mVal);
[StructLayout(LayoutKind.Sequential)]
private struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public POINTAPI ptScreenPos;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINTAPI
{
public int x;
public int y;
}
private const Int32 CURSOR_SHOWING = 0x0001;
private const Int32 DI_NORMAL = 0x0003;
[DllImport("user32.dll")]
private static extern bool GetCursorInfo(out CURSORINFO pci);
[DllImport("user32.dll", SetLastError = true)]
static extern bool DrawIconEx(IntPtr hdc, int xLeft, int yTop, IntPtr hIcon, int cxWidth, int cyHeight, int istepIfAniCur, IntPtr hbrFlickerFreeDraw, int diFlags);
#endregion

View File

@@ -0,0 +1,34 @@
using System.Drawing;
namespace cmonitor.server.client.reports.screen.aforge
{
public abstract class BaseResizeFilter : BaseTransformationFilter
{
protected int newWidth;
protected int newHeight;
public int NewWidth
{
get { return newWidth; }
set { newWidth = Math.Max( 1, value ); }
}
public int NewHeight
{
get { return newHeight; }
set { newHeight = Math.Max( 1, value ); }
}
protected BaseResizeFilter( int newWidth, int newHeight )
{
this.newWidth = newWidth;
this.newHeight = newHeight;
}
protected override Size CalculateNewImageSize()
{
return new Size( newWidth, newHeight );
}
}
}

View File

@@ -0,0 +1,124 @@
using System.Drawing;
using System.Drawing.Imaging;
namespace cmonitor.server.client.reports.screen.aforge
{
public abstract class BaseTransformationFilter
{
public abstract Dictionary<PixelFormat, PixelFormat> FormatTranslations { get; }
public Bitmap Apply(Bitmap image)
{
if (OperatingSystem.IsWindows())
{
BitmapData srcData = image.LockBits(
new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadOnly, image.PixelFormat);
Bitmap dstImage = null;
try
{
dstImage = Apply(srcData);
if ((image.HorizontalResolution > 0) && (image.VerticalResolution > 0))
{
dstImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
}
}
finally
{
image.UnlockBits(srcData);
}
return dstImage;
}
return null;
}
public Bitmap Apply(BitmapData imageData)
{
if (OperatingSystem.IsWindows())
{
CheckSourceFormat(imageData.PixelFormat);
PixelFormat dstPixelFormat = FormatTranslations[imageData.PixelFormat];
Size newSize = CalculateNewImageSize();
Bitmap dstImage = (dstPixelFormat == PixelFormat.Format8bppIndexed) ?
cmonitor.server.client.reports.screen.aforge.Image.CreateGrayscaleImage(newSize.Width, newSize.Height) :
new Bitmap(newSize.Width, newSize.Height, dstPixelFormat);
BitmapData dstData = dstImage.LockBits(
new Rectangle(0, 0, newSize.Width, newSize.Height),
ImageLockMode.ReadWrite, dstPixelFormat);
try
{
ProcessFilter(new UnmanagedImage(imageData), new UnmanagedImage(dstData));
}
finally
{
dstImage.UnlockBits(dstData);
}
return dstImage;
}
return null;
}
public UnmanagedImage Apply(UnmanagedImage image)
{
if (OperatingSystem.IsWindows())
{
CheckSourceFormat(image.PixelFormat);
Size newSize = CalculateNewImageSize();
UnmanagedImage dstImage = UnmanagedImage.Create(newSize.Width, newSize.Height, FormatTranslations[image.PixelFormat]);
ProcessFilter(image, dstImage);
return dstImage;
}
return null;
}
public void Apply(UnmanagedImage sourceImage, UnmanagedImage destinationImage)
{
if (OperatingSystem.IsWindows())
{
CheckSourceFormat(sourceImage.PixelFormat);
if (destinationImage.PixelFormat != FormatTranslations[sourceImage.PixelFormat])
{
throw new Exception("Destination pixel format is specified incorrectly.");
}
Size newSize = CalculateNewImageSize();
if ((destinationImage.Width != newSize.Width) || (destinationImage.Height != newSize.Height))
{
throw new Exception("Destination image must have the size expected by the filter.");
}
ProcessFilter(sourceImage, destinationImage);
}
}
protected abstract System.Drawing.Size CalculateNewImageSize();
protected abstract unsafe void ProcessFilter(UnmanagedImage sourceData, UnmanagedImage destinationData);
private void CheckSourceFormat(PixelFormat pixelFormat)
{
if (OperatingSystem.IsWindows())
{
if (!FormatTranslations.ContainsKey(pixelFormat))
throw new Exception("Source pixel format is not supported by the filter.");
}
}
}
}

View File

@@ -0,0 +1,253 @@
using System;
using System.Drawing;
namespace cmonitor.server.client.reports.screen.aforge
{
public class RGB
{
public const short R = 2;
public const short G = 1;
public const short B = 0;
public const short A = 3;
public byte Red;
public byte Green;
public byte Blue;
public byte Alpha;
public System.Drawing.Color Color
{
get { return Color.FromArgb( Alpha, Red, Green, Blue ); }
set
{
Red = value.R;
Green = value.G;
Blue = value.B;
Alpha = value.A;
}
}
public RGB( )
{
Red = 0;
Green = 0;
Blue = 0;
Alpha = 255;
}
public RGB( byte red, byte green, byte blue )
{
this.Red = red;
this.Green = green;
this.Blue = blue;
this.Alpha = 255;
}
public RGB( byte red, byte green, byte blue, byte alpha )
{
this.Red = red;
this.Green = green;
this.Blue = blue;
this.Alpha = alpha;
}
public RGB( System.Drawing.Color color )
{
this.Red = color.R;
this.Green = color.G;
this.Blue = color.B;
this.Alpha = color.A;
}
}
public class HSL
{
public int Hue;
public float Saturation;
public float Luminance;
public HSL( ) { }
public HSL( int hue, float saturation, float luminance )
{
this.Hue = hue;
this.Saturation = saturation;
this.Luminance = luminance;
}
public static void FromRGB( RGB rgb, HSL hsl )
{
float r = ( rgb.Red / 255.0f );
float g = ( rgb.Green / 255.0f );
float b = ( rgb.Blue / 255.0f );
float min = Math.Min( Math.Min( r, g ), b );
float max = Math.Max( Math.Max( r, g ), b );
float delta = max - min;
// get luminance value
hsl.Luminance = ( max + min ) / 2;
if ( delta == 0 )
{
// gray color
hsl.Hue = 0;
hsl.Saturation = 0.0f;
}
else
{
// get saturation value
hsl.Saturation = ( hsl.Luminance <= 0.5 ) ? ( delta / ( max + min ) ) : ( delta / ( 2 - max - min ) );
// get hue value
float hue;
if ( r == max )
{
hue = ( ( g - b ) / 6 ) / delta;
}
else if ( g == max )
{
hue = ( 1.0f / 3 ) + ( ( b - r ) / 6 ) / delta;
}
else
{
hue = ( 2.0f / 3 ) + ( ( r - g ) / 6 ) / delta;
}
// correct hue if needed
if ( hue < 0 )
hue += 1;
if ( hue > 1 )
hue -= 1;
hsl.Hue = (int) ( hue * 360 );
}
}
public static HSL FromRGB( RGB rgb )
{
HSL hsl = new HSL( );
FromRGB( rgb, hsl );
return hsl;
}
public static void ToRGB( HSL hsl, RGB rgb )
{
if ( hsl.Saturation == 0 )
{
// gray values
rgb.Red = rgb.Green = rgb.Blue = (byte) ( hsl.Luminance * 255 );
}
else
{
float v1, v2;
float hue = (float) hsl.Hue / 360;
v2 = ( hsl.Luminance < 0.5 ) ?
( hsl.Luminance * ( 1 + hsl.Saturation ) ) :
( ( hsl.Luminance + hsl.Saturation ) - ( hsl.Luminance * hsl.Saturation ) );
v1 = 2 * hsl.Luminance - v2;
rgb.Red = (byte) ( 255 * Hue_2_RGB( v1, v2, hue + ( 1.0f / 3 ) ) );
rgb.Green = (byte) ( 255 * Hue_2_RGB( v1, v2, hue ) );
rgb.Blue = (byte) ( 255 * Hue_2_RGB( v1, v2, hue - ( 1.0f / 3 ) ) );
}
rgb.Alpha = 255;
}
public RGB ToRGB( )
{
RGB rgb = new RGB( );
ToRGB( this, rgb );
return rgb;
}
#region Private members
// HSL to RGB helper routine
private static float Hue_2_RGB( float v1, float v2, float vH )
{
if ( vH < 0 )
vH += 1;
if ( vH > 1 )
vH -= 1;
if ( ( 6 * vH ) < 1 )
return ( v1 + ( v2 - v1 ) * 6 * vH );
if ( ( 2 * vH ) < 1 )
return v2;
if ( ( 3 * vH ) < 2 )
return ( v1 + ( v2 - v1 ) * ( ( 2.0f / 3 ) - vH ) * 6 );
return v1;
}
#endregion
}
public class YCbCr
{
public const short YIndex = 0;
public const short CbIndex = 1;
public const short CrIndex = 2;
public float Y;
public float Cb;
public float Cr;
public YCbCr( ) { }
public YCbCr( float y, float cb, float cr )
{
this.Y = Math.Max( 0.0f, Math.Min( 1.0f, y ) );
this.Cb = Math.Max( -0.5f, Math.Min( 0.5f, cb ) );
this.Cr = Math.Max( -0.5f, Math.Min( 0.5f, cr ) );
}
public static void FromRGB( RGB rgb, YCbCr ycbcr )
{
float r = (float) rgb.Red / 255;
float g = (float) rgb.Green / 255;
float b = (float) rgb.Blue / 255;
ycbcr.Y = (float) ( 0.2989 * r + 0.5866 * g + 0.1145 * b );
ycbcr.Cb = (float) ( -0.1687 * r - 0.3313 * g + 0.5000 * b );
ycbcr.Cr = (float) ( 0.5000 * r - 0.4184 * g - 0.0816 * b );
}
public static YCbCr FromRGB( RGB rgb )
{
YCbCr ycbcr = new YCbCr( );
FromRGB( rgb, ycbcr );
return ycbcr;
}
public static void ToRGB( YCbCr ycbcr, RGB rgb )
{
// don't warry about zeros. compiler will remove them
float r = Math.Max( 0.0f, Math.Min( 1.0f, (float) ( ycbcr.Y + 0.0000 * ycbcr.Cb + 1.4022 * ycbcr.Cr ) ) );
float g = Math.Max( 0.0f, Math.Min( 1.0f, (float) ( ycbcr.Y - 0.3456 * ycbcr.Cb - 0.7145 * ycbcr.Cr ) ) );
float b = Math.Max( 0.0f, Math.Min( 1.0f, (float) ( ycbcr.Y + 1.7710 * ycbcr.Cb + 0.0000 * ycbcr.Cr ) ) );
rgb.Red = (byte) ( r * 255 );
rgb.Green = (byte) ( g * 255 );
rgb.Blue = (byte) ( b * 255 );
rgb.Alpha = 255;
}
public RGB ToRGB( )
{
RGB rgb = new RGB( );
ToRGB( this, rgb );
return rgb;
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
namespace cmonitor.server.client.reports.screen.aforge
{
[Serializable]
public struct DoublePoint
{
public double X;
public double Y;
public DoublePoint( double x, double y )
{
this.X = x;
this.Y = y;
}
public static bool operator ==( DoublePoint point1, DoublePoint point2 )
{
return ( ( point1.X == point2.X ) && ( point1.Y == point2.Y ) );
}
public static bool operator !=( DoublePoint point1, DoublePoint point2 )
{
return ( ( point1.X != point2.X ) || ( point1.Y != point2.Y ) );
}
public override bool Equals( object obj )
{
return ( obj is DoublePoint ) ? ( this == (DoublePoint) obj ) : false;
}
public override int GetHashCode( )
{
return X.GetHashCode( ) + Y.GetHashCode( );
}
public static explicit operator IntPoint( DoublePoint point )
{
return new IntPoint( (int) point.X, (int) point.Y );
}
public static explicit operator Point( DoublePoint point )
{
return new Point( (float) point.X, (float) point.Y );
}
}
}

View File

@@ -0,0 +1,60 @@
using System.Drawing;
using System.Drawing.Imaging;
namespace cmonitor.server.client.reports.screen.aforge
{
public static class Image
{
public static bool IsGrayscale(Bitmap image)
{
bool ret = false;
if (OperatingSystem.IsWindows())
{
if (image.PixelFormat == PixelFormat.Format8bppIndexed)
{
ret = true;
ColorPalette cp = image.Palette;
Color c;
for (int i = 0; i < 256; i++)
{
c = cp.Entries[i];
if ((c.R != i) || (c.G != i) || (c.B != i))
{
ret = false;
break;
}
}
}
}
return ret;
}
public static Bitmap CreateGrayscaleImage(int width, int height)
{
if (OperatingSystem.IsWindows())
{
Bitmap image = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
SetGrayscalePalette(image);
return image;
}
return null;
}
public static void SetGrayscalePalette(Bitmap image)
{
if (OperatingSystem.IsWindows())
{
if (image.PixelFormat != PixelFormat.Format8bppIndexed)
throw new Exception("Source image is not 8 bpp image.");
ColorPalette cp = image.Palette;
for (int i = 0; i < 256; i++)
{
cp.Entries[i] = Color.FromArgb(i, i, i);
}
image.Palette = cp;
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Drawing;
namespace cmonitor.server.client.reports.screen.aforge
{
[Serializable]
public struct IntPoint
{
public int X;
public int Y;
public IntPoint(int x, int y)
{
this.X = x;
this.Y = y;
}
public static implicit operator Point(IntPoint point)
{
return new Point(point.X, point.Y);
}
public static implicit operator DoublePoint(IntPoint point)
{
return new DoublePoint(point.X, point.Y);
}
}
}

View File

@@ -0,0 +1,139 @@
using System;
namespace cmonitor.server.client.reports.screen.aforge
{
[Serializable]
public struct Point
{
public float X;
public float Y;
public Point( float x, float y )
{
this.X = x;
this.Y = y;
}
public float DistanceTo( Point anotherPoint )
{
float dx = X - anotherPoint.X;
float dy = Y - anotherPoint.Y;
return (float) System.Math.Sqrt( dx * dx + dy * dy );
}
public float SquaredDistanceTo( Point anotherPoint )
{
float dx = X - anotherPoint.X;
float dy = Y - anotherPoint.Y;
return dx * dx + dy * dy;
}
public static Point operator +( Point point1, Point point2 )
{
return new Point( point1.X + point2.X, point1.Y + point2.Y );
}
public static Point Add( Point point1, Point point2 )
{
return new Point( point1.X + point2.X, point1.Y + point2.Y );
}
public static Point operator -( Point point1, Point point2 )
{
return new Point( point1.X - point2.X, point1.Y - point2.Y );
}
public static Point Subtract( Point point1, Point point2 )
{
return new Point( point1.X - point2.X, point1.Y - point2.Y );
}
public static Point operator +( Point point, float valueToAdd )
{
return new Point( point.X + valueToAdd, point.Y + valueToAdd );
}
public static Point Add( Point point, float valueToAdd )
{
return new Point( point.X + valueToAdd, point.Y + valueToAdd );
}
public static Point operator -( Point point, float valueToSubtract )
{
return new Point( point.X - valueToSubtract, point.Y - valueToSubtract );
}
public static Point Subtract( Point point, float valueToSubtract )
{
return new Point( point.X - valueToSubtract, point.Y - valueToSubtract );
}
public static Point operator *( Point point, float factor )
{
return new Point( point.X * factor, point.Y * factor );
}
public static Point Multiply( Point point, float factor )
{
return new Point( point.X * factor, point.Y * factor );
}
public static Point operator /( Point point, float factor )
{
return new Point( point.X / factor, point.Y / factor );
}
public static Point Divide( Point point, float factor )
{
return new Point( point.X / factor, point.Y / factor );
}
public static bool operator ==( Point point1, Point point2 )
{
return ( ( point1.X == point2.X ) && ( point1.Y == point2.Y ) );
}
public static bool operator !=( Point point1, Point point2 )
{
return ( ( point1.X != point2.X ) || ( point1.Y != point2.Y ) );
}
public override bool Equals( object obj )
{
return ( obj is Point ) ? ( this == (Point) obj ) : false;
}
public override int GetHashCode( )
{
return X.GetHashCode( ) + Y.GetHashCode( );
}
public static explicit operator IntPoint( Point point )
{
return new IntPoint( (int) point.X, (int) point.Y );
}
public static implicit operator DoublePoint( Point point )
{
return new DoublePoint( point.X, point.Y );
}
public IntPoint Round( )
{
return new IntPoint( (int) Math.Round( X ), (int) Math.Round( Y ) );
}
public override string ToString( )
{
return string.Format( System.Globalization.CultureInfo.InvariantCulture, "{0}, {1}", X, Y );
}
public float EuclideanNorm( )
{
return (float) System.Math.Sqrt( X * X + Y * Y );
}
}
}

View File

@@ -0,0 +1,98 @@
using System.Drawing.Imaging;
namespace cmonitor.server.client.reports.screen.aforge
{
public class ResizeBilinear : BaseResizeFilter
{
private Dictionary<PixelFormat, PixelFormat> formatTranslations;
public override Dictionary<PixelFormat, PixelFormat> FormatTranslations
{
get { return formatTranslations; }
}
public ResizeBilinear( int newWidth, int newHeight ) :
base( newWidth, newHeight )
{
if (OperatingSystem.IsWindows())
{
formatTranslations = new Dictionary<PixelFormat, PixelFormat>();
formatTranslations[PixelFormat.Format8bppIndexed] = PixelFormat.Format8bppIndexed;
formatTranslations[PixelFormat.Format24bppRgb] = PixelFormat.Format24bppRgb;
formatTranslations[PixelFormat.Format32bppRgb] = PixelFormat.Format32bppRgb;
formatTranslations[PixelFormat.Format32bppArgb] = PixelFormat.Format32bppArgb;
}
}
protected override unsafe void ProcessFilter( UnmanagedImage sourceData, UnmanagedImage destinationData )
{
if (OperatingSystem.IsWindows())
{
// get source image size
int width = sourceData.Width;
int height = sourceData.Height;
int pixelSize = System.Drawing.Image.GetPixelFormatSize(sourceData.PixelFormat) / 8;
int srcStride = sourceData.Stride;
int dstOffset = destinationData.Stride - pixelSize * newWidth;
double xFactor = (double)width / newWidth;
double yFactor = (double)height / newHeight;
// do the job
byte* src = (byte*)sourceData.ImageData.ToPointer();
byte* dst = (byte*)destinationData.ImageData.ToPointer();
// coordinates of source points
double ox, oy, dx1, dy1, dx2, dy2;
int ox1, oy1, ox2, oy2;
// width and height decreased by 1
int ymax = height - 1;
int xmax = width - 1;
// temporary pointers
byte* tp1, tp2;
byte* p1, p2, p3, p4;
// for each line
for (int y = 0; y < newHeight; y++)
{
// Y coordinates
oy = (double)y * yFactor;
oy1 = (int)oy;
oy2 = (oy1 == ymax) ? oy1 : oy1 + 1;
dy1 = oy - (double)oy1;
dy2 = 1.0 - dy1;
// get temp pointers
tp1 = src + oy1 * srcStride;
tp2 = src + oy2 * srcStride;
// for each pixel
for (int x = 0; x < newWidth; x++)
{
// X coordinates
ox = (double)x * xFactor;
ox1 = (int)ox;
ox2 = (ox1 == xmax) ? ox1 : ox1 + 1;
dx1 = ox - (double)ox1;
dx2 = 1.0 - dx1;
// get four points
p1 = tp1 + ox1 * pixelSize;
p2 = tp1 + ox2 * pixelSize;
p3 = tp2 + ox1 * pixelSize;
p4 = tp2 + ox2 * pixelSize;
// interpolate using 4 points
for (int i = 0; i < pixelSize; i++, dst++, p1++, p2++, p3++, p4++)
{
*dst = (byte)(
dy2 * (dx2 * (*p1) + dx1 * (*p2)) +
dy1 * (dx2 * (*p3) + dx1 * (*p4)));
}
}
dst += dstOffset;
}
}
}
}
}

View File

@@ -0,0 +1,97 @@
using System;
using System.Runtime.InteropServices;
namespace cmonitor.server.client.reports.screen.aforge
{
public static class SystemTools
{
public static IntPtr CopyUnmanagedMemory(IntPtr dst, IntPtr src, int count)
{
unsafe
{
CopyUnmanagedMemory((byte*)dst.ToPointer(), (byte*)src.ToPointer(), count);
}
return dst;
}
public static unsafe byte* CopyUnmanagedMemory(byte* dst, byte* src, int count)
{
#if !MONO
return memcpy(dst, src, count);
#else
int countUint = count >> 2;
int countByte = count & 3;
uint* dstUint = (uint*) dst;
uint* srcUint = (uint*) src;
while ( countUint-- != 0 )
{
*dstUint++ = *srcUint++;
}
byte* dstByte = (byte*) dstUint;
byte* srcByte = (byte*) srcUint;
while ( countByte-- != 0 )
{
*dstByte++ = *srcByte++;
}
return dst;
#endif
}
public static IntPtr SetUnmanagedMemory(IntPtr dst, int filler, int count)
{
unsafe
{
SetUnmanagedMemory((byte*)dst.ToPointer(), filler, count);
}
return dst;
}
public static unsafe byte* SetUnmanagedMemory(byte* dst, int filler, int count)
{
#if !MONO
return memset(dst, filler, count);
#else
int countUint = count >> 2;
int countByte = count & 3;
byte fillerByte = (byte) filler;
uint fiilerUint = (uint) filler | ( (uint) filler << 8 ) |
( (uint) filler << 16 );// |
//( (uint) filler << 24 );
uint* dstUint = (uint*) dst;
while ( countUint-- != 0 )
{
*dstUint++ = fiilerUint;
}
byte* dstByte = (byte*) dstUint;
while ( countByte-- != 0 )
{
*dstByte++ = fillerByte;
}
return dst;
#endif
}
#if !MONO
[DllImport("ntdll.dll", CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern byte* memcpy(
byte* dst,
byte* src,
int count);
[DllImport("ntdll.dll", CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern byte* memset(
byte* dst,
int filler,
int count);
#endif
}
}

View File

@@ -0,0 +1,277 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
namespace cmonitor.server.client.reports.screen.aforge
{
public class UnmanagedImage : IDisposable
{
private IntPtr imageData;
private int width, height;
private int stride;
private PixelFormat pixelFormat;
private bool mustBeDisposed = false;
public IntPtr ImageData
{
get { return imageData; }
}
public int Width
{
get { return width; }
}
public int Height
{
get { return height; }
}
public int Stride
{
get { return stride; }
}
public PixelFormat PixelFormat
{
get { return pixelFormat; }
}
public UnmanagedImage( IntPtr imageData, int width, int height, int stride, PixelFormat pixelFormat )
{
this.imageData = imageData;
this.width = width;
this.height = height;
this.stride = stride;
this.pixelFormat = pixelFormat;
}
public UnmanagedImage( BitmapData bitmapData )
{
if (OperatingSystem.IsWindows())
{
this.imageData = bitmapData.Scan0;
this.width = bitmapData.Width;
this.height = bitmapData.Height;
this.stride = bitmapData.Stride;
this.pixelFormat = bitmapData.PixelFormat;
}
}
~UnmanagedImage( )
{
Dispose( false );
}
public void Dispose( )
{
Dispose( true );
// remove me from the Finalization queue
GC.SuppressFinalize( this );
}
protected virtual void Dispose( bool disposing )
{
if ( disposing )
{
// dispose managed resources
}
// free image memory if the image was allocated using this class
if ( ( mustBeDisposed ) && ( imageData != IntPtr.Zero ) )
{
System.Runtime.InteropServices.Marshal.FreeHGlobal( imageData );
System.GC.RemoveMemoryPressure( stride * height );
imageData = IntPtr.Zero;
}
}
public static UnmanagedImage Create( int width, int height, PixelFormat pixelFormat )
{
if (OperatingSystem.IsWindows())
{
int bytesPerPixel = 0;
// calculate bytes per pixel
switch (pixelFormat)
{
case PixelFormat.Format8bppIndexed:
bytesPerPixel = 1;
break;
case PixelFormat.Format16bppGrayScale:
bytesPerPixel = 2;
break;
case PixelFormat.Format24bppRgb:
bytesPerPixel = 3;
break;
case PixelFormat.Format32bppRgb:
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
bytesPerPixel = 4;
break;
case PixelFormat.Format48bppRgb:
bytesPerPixel = 6;
break;
case PixelFormat.Format64bppArgb:
case PixelFormat.Format64bppPArgb:
bytesPerPixel = 8;
break;
default:
throw new Exception("Can not create image with specified pixel format.");
}
// check image size
if ((width <= 0) || (height <= 0))
{
throw new Exception("Invalid image size specified.");
}
// calculate stride
int stride = width * bytesPerPixel;
if (stride % 4 != 0)
{
stride += (4 - (stride % 4));
}
// allocate memory for the image
IntPtr imageData = System.Runtime.InteropServices.Marshal.AllocHGlobal(stride * height);
cmonitor.server.client.reports.screen.aforge.SystemTools.SetUnmanagedMemory(imageData, 0, stride * height);
System.GC.AddMemoryPressure(stride * height);
UnmanagedImage image = new UnmanagedImage(imageData, width, height, stride, pixelFormat);
image.mustBeDisposed = true;
return image;
}
return null;
}
public void SetPixel( IntPoint point, Color color )
{
SetPixel( point.X, point.Y, color );
}
public void SetPixel( int x, int y, Color color )
{
SetPixel( x, y, color.R, color.G, color.B, color.A );
}
public void SetPixel( int x, int y, byte value )
{
SetPixel( x, y, value, value, value, 255 );
}
private void SetPixel( int x, int y, byte r, byte g, byte b, byte a )
{
if (OperatingSystem.IsWindows())
{
if ((x >= 0) && (y >= 0) && (x < width) && (y < height))
{
unsafe
{
int pixelSize = Bitmap.GetPixelFormatSize(pixelFormat) / 8;
byte* ptr = (byte*)imageData.ToPointer() + y * stride + x * pixelSize;
ushort* ptr2 = (ushort*)ptr;
switch (pixelFormat)
{
case PixelFormat.Format8bppIndexed:
*ptr = (byte)(0.2125 * r + 0.7154 * g + 0.0721 * b);
break;
case PixelFormat.Format24bppRgb:
case PixelFormat.Format32bppRgb:
ptr[RGB.R] = r;
ptr[RGB.G] = g;
ptr[RGB.B] = b;
break;
case PixelFormat.Format32bppArgb:
ptr[RGB.R] = r;
ptr[RGB.G] = g;
ptr[RGB.B] = b;
ptr[RGB.A] = a;
break;
case PixelFormat.Format16bppGrayScale:
*ptr2 = (ushort)((ushort)(0.2125 * r + 0.7154 * g + 0.0721 * b) << 8);
break;
case PixelFormat.Format48bppRgb:
ptr2[RGB.R] = (ushort)(r << 8);
ptr2[RGB.G] = (ushort)(g << 8);
ptr2[RGB.B] = (ushort)(b << 8);
break;
case PixelFormat.Format64bppArgb:
ptr2[RGB.R] = (ushort)(r << 8);
ptr2[RGB.G] = (ushort)(g << 8);
ptr2[RGB.B] = (ushort)(b << 8);
ptr2[RGB.A] = (ushort)(a << 8);
break;
default:
throw new Exception("The pixel format is not supported: " + pixelFormat);
}
}
}
}
}
public Color GetPixel( IntPoint point )
{
return GetPixel( point.X, point.Y );
}
public Color GetPixel( int x, int y )
{
if (OperatingSystem.IsWindows())
{
if ((x < 0) || (y < 0))
{
throw new ArgumentOutOfRangeException("x", "The specified pixel coordinate is out of image's bounds.");
}
if ((x >= width) || (y >= height))
{
throw new ArgumentOutOfRangeException("y", "The specified pixel coordinate is out of image's bounds.");
}
Color color = new Color();
unsafe
{
int pixelSize = Bitmap.GetPixelFormatSize(pixelFormat) / 8;
byte* ptr = (byte*)imageData.ToPointer() + y * stride + x * pixelSize;
switch (pixelFormat)
{
case PixelFormat.Format8bppIndexed:
color = Color.FromArgb(*ptr, *ptr, *ptr);
break;
case PixelFormat.Format24bppRgb:
case PixelFormat.Format32bppRgb:
color = Color.FromArgb(ptr[RGB.R], ptr[RGB.G], ptr[RGB.B]);
break;
case PixelFormat.Format32bppArgb:
color = Color.FromArgb(ptr[RGB.A], ptr[RGB.R], ptr[RGB.G], ptr[RGB.B]);
break;
default:
throw new Exception("The pixel format is not supported: " + pixelFormat);
}
}
return color;
}
return new Color();
}
}
}

View File

@@ -0,0 +1,8 @@
namespace cmonitor.server.client.reports.screen.sharpDX
{
public class DesktopDuplicationException : Exception
{
public DesktopDuplicationException(string message)
: base(message) { }
}
}

View File

@@ -0,0 +1,305 @@
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Device = SharpDX.Direct3D11.Device;
using MapFlags = SharpDX.Direct3D11.MapFlags;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using SharpDX.Mathematics.Interop;
using System.Buffers;
using Rectangle = System.Drawing.Rectangle;
using Point = System.Drawing.Point;
using cmonitor.server.client.reports.screen.aforge;
namespace cmonitor.server.client.reports.screen.sharpDX
{
public class DesktopDuplicator
{
private Device mDevice;
private Texture2DDescription mTextureDesc;
private OutputDescription mOutputDesc;
private OutputDuplication mDeskDupl;
private Texture2D desktopImageTexture = null;
private OutputDuplicateFrameInformation frameInfo = new OutputDuplicateFrameInformation();
private int mWhichOutputDevice = -1;
public DesktopDuplicator(int whichMonitor)
: this(0, whichMonitor) { }
public DesktopDuplicator(int whichGraphicsCardAdapter, int whichOutputDevice)
{
this.mWhichOutputDevice = whichOutputDevice;
Adapter1 adapter = null;
try
{
adapter = new Factory1().GetAdapter1(whichGraphicsCardAdapter);
}
catch (SharpDXException)
{
throw new DesktopDuplicationException("Could not find the specified graphics card adapter.");
}
this.mDevice = new Device(adapter);
Output output = null;
try
{
output = adapter.GetOutput(whichOutputDevice);
}
catch (SharpDXException)
{
throw new DesktopDuplicationException("Could not find the specified output device.");
}
var output1 = output.QueryInterface<Output1>();
this.mOutputDesc = output.Description;
this.mTextureDesc = new Texture2DDescription()
{
CpuAccessFlags = CpuAccessFlags.Read,
BindFlags = BindFlags.None,
Format = Format.B8G8R8A8_UNorm,
Width = this.mOutputDesc.DesktopBounds.Right - this.mOutputDesc.DesktopBounds.Left,
Height = this.mOutputDesc.DesktopBounds.Bottom - this.mOutputDesc.DesktopBounds.Top,
OptionFlags = ResourceOptionFlags.None,
MipLevels = 1,
ArraySize = 1,
SampleDescription = { Count = 1, Quality = 0 },
Usage = ResourceUsage.Staging
};
try
{
this.mDeskDupl = output1.DuplicateOutput(mDevice);
}
catch (SharpDXException ex)
{
if (ex.ResultCode.Code == SharpDX.DXGI.ResultCode.NotCurrentlyAvailable.Result.Code)
{
throw new DesktopDuplicationException("There is already the maximum number of applications using the Desktop Duplication API running, please close one of the applications and try again.");
}
}
}
public byte[] GetLatestFrame(int width, int height, out int length)
{
length = 0;
//var frame = new DesktopFrame();
bool retrievalTimedOut = RetrieveFrame();
if (retrievalTimedOut)
return null;
try
{
// RetrieveFrameMetadata(frame);
// RetrieveCursorMetadata(frame);
return ProcessFrame(width, height, out length);
}
catch
{
ReleaseFrame();
}
finally
{
try
{
ReleaseFrame();
}
catch
{
}
}
return Array.Empty<byte>();
}
private bool RetrieveFrame()
{
if (desktopImageTexture == null)
desktopImageTexture = new Texture2D(mDevice, mTextureDesc);
SharpDX.DXGI.Resource desktopResource = null;
frameInfo = new OutputDuplicateFrameInformation();
try
{
mDeskDupl.TryAcquireNextFrame(500, out frameInfo, out desktopResource);
}
catch (SharpDXException ex)
{
if (ex.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
{
return true;
}
if (ex.ResultCode.Failure)
{
throw new DesktopDuplicationException("Failed to acquire next frame.");
}
}
using (var tempTexture = desktopResource.QueryInterface<Texture2D>())
mDevice.ImmediateContext.CopyResource(tempTexture, desktopImageTexture);
desktopResource.Dispose();
return false;
}
private void RetrieveFrameMetadata(DesktopFrame frame)
{
if (frameInfo.TotalMetadataBufferSize > 0)
{
int movedRegionsLength = 0;
OutputDuplicateMoveRectangle[] movedRectangles = new OutputDuplicateMoveRectangle[frameInfo.TotalMetadataBufferSize];
mDeskDupl.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out movedRegionsLength);
frame.MovedRegions = new MovedRegion[movedRegionsLength / Marshal.SizeOf(typeof(OutputDuplicateMoveRectangle))];
for (int i = 0; i < frame.MovedRegions.Length; i++)
{
int width = movedRectangles[i].DestinationRect.Right - movedRectangles[i].DestinationRect.Left;
int height = movedRectangles[i].DestinationRect.Bottom - movedRectangles[i].DestinationRect.Top;
frame.MovedRegions[i] = new MovedRegion()
{
Source = new Point(movedRectangles[i].SourcePoint.X, movedRectangles[i].SourcePoint.Y),
Destination = new Rectangle(movedRectangles[i].DestinationRect.Left, movedRectangles[i].DestinationRect.Top, width, height)
};
}
int dirtyRegionsLength = 0;
RawRectangle[] dirtyRectangles = new RawRectangle[frameInfo.TotalMetadataBufferSize];
mDeskDupl.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out dirtyRegionsLength);
frame.UpdatedRegions = new Rectangle[dirtyRegionsLength / Marshal.SizeOf(typeof(Rectangle))];
for (int i = 0; i < frame.UpdatedRegions.Length; i++)
{
int width = dirtyRectangles[i].Right - dirtyRectangles[i].Left;
int height = dirtyRectangles[i].Bottom - dirtyRectangles[i].Top;
frame.UpdatedRegions[i] = new Rectangle(dirtyRectangles[i].Left, dirtyRectangles[i].Top, width, height);
}
}
else
{
frame.MovedRegions = new MovedRegion[0];
frame.UpdatedRegions = new System.Drawing.Rectangle[0];
}
}
private void RetrieveCursorMetadata(DesktopFrame frame)
{
var pointerInfo = new PointerInfo();
if (frameInfo.LastMouseUpdateTime == 0)
return;
bool updatePosition = true;
if (!frameInfo.PointerPosition.Visible && (pointerInfo.WhoUpdatedPositionLast != this.mWhichOutputDevice))
updatePosition = false;
if (frameInfo.PointerPosition.Visible && pointerInfo.Visible && (pointerInfo.WhoUpdatedPositionLast != this.mWhichOutputDevice) && (pointerInfo.LastTimeStamp > frameInfo.LastMouseUpdateTime))
updatePosition = false;
if (updatePosition)
{
pointerInfo.Position = new Point(frameInfo.PointerPosition.Position.X, frameInfo.PointerPosition.Position.Y);
pointerInfo.WhoUpdatedPositionLast = mWhichOutputDevice;
pointerInfo.LastTimeStamp = frameInfo.LastMouseUpdateTime;
pointerInfo.Visible = frameInfo.PointerPosition.Visible;
}
if (frameInfo.PointerShapeBufferSize == 0)
return;
if (frameInfo.PointerShapeBufferSize > pointerInfo.BufferSize)
{
pointerInfo.PtrShapeBuffer = new byte[frameInfo.PointerShapeBufferSize];
pointerInfo.BufferSize = frameInfo.PointerShapeBufferSize;
}
try
{
unsafe
{
fixed (byte* ptrShapeBufferPtr = pointerInfo.PtrShapeBuffer)
{
mDeskDupl.GetFramePointerShape(frameInfo.PointerShapeBufferSize, (IntPtr)ptrShapeBufferPtr, out pointerInfo.BufferSize, out pointerInfo.ShapeInfo);
}
}
}
catch (SharpDXException ex)
{
if (ex.ResultCode.Failure)
{
throw new DesktopDuplicationException("Failed to get frame pointer shape.");
}
}
frame.CursorLocation = new System.Drawing.Point(pointerInfo.Position.X, pointerInfo.Position.Y);
}
ResizeBilinear resizeFilter;
private unsafe byte[] ProcessFrame(int width, int height, out int length)
{
length = 0;
if (OperatingSystem.IsWindows())
{
try
{
var mapSource = mDevice.ImmediateContext.MapSubresource(desktopImageTexture, 0, MapMode.Read, MapFlags.None);
int sourceWidth = mOutputDesc.DesktopBounds.Right - mOutputDesc.DesktopBounds.Left;
int sourceHeight = mOutputDesc.DesktopBounds.Bottom - mOutputDesc.DesktopBounds.Top;
using Bitmap image = new Bitmap(sourceWidth, sourceHeight, PixelFormat.Format32bppRgb);
Rectangle boundsRect = new Rectangle(0, 0, sourceWidth, sourceHeight);
var mapDest = image.LockBits(boundsRect, ImageLockMode.WriteOnly, image.PixelFormat);
var sourcePtr = mapSource.DataPointer;
var destPtr = mapDest.Scan0;
for (int y = 0; y < sourceHeight; y++)
{
Utilities.CopyMemory(destPtr, sourcePtr, sourceWidth * 4);
sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
destPtr = IntPtr.Add(destPtr, mapDest.Stride);
}
image.UnlockBits(mapDest);
mDevice.ImmediateContext.UnmapSubresource(desktopImageTexture, 0);
if(resizeFilter == null)
{
resizeFilter = new ResizeBilinear(width, height);
}
Bitmap bmp = resizeFilter.Apply(image);
using System.Drawing.Image image1 = bmp;
using MemoryStream ms = new MemoryStream();
image1.Save(ms, ImageFormat.Jpeg);
ms.Seek(0, SeekOrigin.Begin);
length = (int)ms.Length;
byte[] bytes = ArrayPool<byte>.Shared.Rent((int)ms.Length);
ms.Read(bytes);
return bytes;
}
catch (Exception ex)
{
Console.WriteLine(ex + "");
}
}
return Array.Empty<byte>();
// frame.DesktopImage = image;
}
private void ReleaseFrame()
{
try
{
mDeskDupl.ReleaseFrame();
}
catch (SharpDXException ex)
{
if (ex.ResultCode.Failure)
{
throw new DesktopDuplicationException("Failed to release frame.");
}
}
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Drawing;
using Point = System.Drawing.Point;
using Rectangle = System.Drawing.Rectangle;
namespace cmonitor.server.client.reports.screen.sharpDX
{
public class DesktopFrame
{
public Bitmap DesktopImage { get; internal set; }
public MovedRegion[] MovedRegions { get; internal set; }
public Rectangle[] UpdatedRegions { get; internal set; }
public int AccumulatedFrames { get; internal set; }
public Point CursorLocation { get; internal set; }
public bool CursorVisible { get; internal set; }
public bool ProtectedContentMaskedOut { get; internal set; }
public bool RectanglesCoalesced { get; internal set; }
}
}

View File

@@ -0,0 +1,11 @@
using System.Drawing;
namespace cmonitor.server.client.reports.screen.sharpDX
{
public struct MovedRegion
{
public System.Drawing.Point Source { get; internal set; }
public System.Drawing.Rectangle Destination { get; internal set; }
}
}

View File

@@ -0,0 +1,15 @@
using SharpDX.DXGI;
namespace cmonitor.server.client.reports.screen.sharpDX
{
internal class PointerInfo
{
public byte[] PtrShapeBuffer;
public OutputDuplicatePointerShapeInformation ShapeInfo;
public System.Drawing.Point Position;
public bool Visible;
public int BufferSize;
public int WhoUpdatedPositionLast;
public long LastTimeStamp;
}
}