mirror of
https://github.com/snltty/linker.git
synced 2025-11-01 21:13:04 +08:00
使用sharpdx和aforge减少CPU使用率,提高帧率
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
154
cmonitor/server/client/reports/screen/GdiCapture.cs
Normal file
154
cmonitor/server/client/reports/screen/GdiCapture.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
253
cmonitor/server/client/reports/screen/aforge/ColorConverter.cs
Normal file
253
cmonitor/server/client/reports/screen/aforge/ColorConverter.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
cmonitor/server/client/reports/screen/aforge/DoublePoint.cs
Normal file
50
cmonitor/server/client/reports/screen/aforge/DoublePoint.cs
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
60
cmonitor/server/client/reports/screen/aforge/Image.cs
Normal file
60
cmonitor/server/client/reports/screen/aforge/Image.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
cmonitor/server/client/reports/screen/aforge/IntPoint.cs
Normal file
30
cmonitor/server/client/reports/screen/aforge/IntPoint.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
139
cmonitor/server/client/reports/screen/aforge/Point.cs
Normal file
139
cmonitor/server/client/reports/screen/aforge/Point.cs
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
cmonitor/server/client/reports/screen/aforge/SystemTools.cs
Normal file
97
cmonitor/server/client/reports/screen/aforge/SystemTools.cs
Normal 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
|
||||
}
|
||||
}
|
||||
277
cmonitor/server/client/reports/screen/aforge/UnmanagedImage.cs
Normal file
277
cmonitor/server/client/reports/screen/aforge/UnmanagedImage.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace cmonitor.server.client.reports.screen.sharpDX
|
||||
{
|
||||
public class DesktopDuplicationException : Exception
|
||||
{
|
||||
public DesktopDuplicationException(string message)
|
||||
: base(message) { }
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
11
cmonitor/server/client/reports/screen/sharpDX/MovedRegion.cs
Normal file
11
cmonitor/server/client/reports/screen/sharpDX/MovedRegion.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
15
cmonitor/server/client/reports/screen/sharpDX/PointerInfo.cs
Normal file
15
cmonitor/server/client/reports/screen/sharpDX/PointerInfo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user