Skip to content

Commit

Permalink
新增 支持直接截图窗口
Browse files Browse the repository at this point in the history
  • Loading branch information
MakesYT committed Jan 4, 2025
1 parent 5a2a70a commit 82f5367
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 48 deletions.
57 changes: 33 additions & 24 deletions Core.Window/ScreenCapture/CaptureTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ public static unsafe byte[] GetBytesSpan(MappedSubresource mappedSubresource, Ou
int startY = Math.Clamp(screenCaptureInfo.Y, 0, outputDesc.DesktopCoordinates.Size.Y - 1);
int endX = Math.Clamp(screenCaptureInfo.X+screenCaptureInfo.Width, 0, outputDesc.DesktopCoordinates.Size.X);
int endY = Math.Clamp(screenCaptureInfo.Y+screenCaptureInfo.Height, 0, outputDesc.DesktopCoordinates.Size.Y);
if (screenCaptureInfo.ScreenCaptureType==ScreenCaptureType.窗口)
{
sizeX=(screenCaptureInfo.WindowInfo.Rect.Width + 3) & ~3;
startX = 0;
startY = 0;
endX = (screenCaptureInfo.WindowInfo.Rect.Width + 3) & ~3;
endY = (int)screenCaptureInfo.WindowInfo.Rect.Height;
}

screenCaptureInfo.Height = endY - startY;
screenCaptureInfo.Width = endX - startX;
screenCaptureInfo.X = startX;
Expand Down Expand Up @@ -61,39 +70,39 @@ public static unsafe byte[] GetBytesSpan(MappedSubresource mappedSubresource, Ou
);
var span = new ReadOnlySpan<Half>(mappedSubresource.PData,
(int)mappedSubresource.DepthPitch / 2).ToArray();
// ReadOnlyMemory<Half> readOnlyMemory = new ReadOnlyMemory<Half>(span);
// ReadOnlyMemory<Half> readOnlyMemory = new ReadOnlyMemory<Half>(span);
Parallel.For(startY, endY, y =>
//for (int y = startY; y < endY; y++)
{
//for (int y = startY; y < endY; y++)
{

int yOffset = y * sizeX;
int targetYOffset = (y - startY) * regionWidth;
int yOffset = y * sizeX;
int targetYOffset = (y - startY) * regionWidth;

for (int x = startX; x < endX; x++)
{
int sourceIndex = (yOffset + x) * 4;
int targetIndex = (targetYOffset + (x - startX)) * 4;
for (int x = startX; x < endX; x++)
{
int sourceIndex = (yOffset + x) * 4;
int targetIndex = (targetYOffset + (x - startX)) * 4;

// 读取并归一化 RGBA 值
// 读取并归一化 RGBA 值

float r = float.Log(1 + (float)span[sourceIndex]) / 1.749199854809259f;
float g = float.Log(1 + (float)span[sourceIndex+1]) / 1.749199854809259f;
float b = float.Log(1 + (float)span[sourceIndex+2]) / 1.749199854809259f;
float r = float.Log(1 + (float)span[sourceIndex]) / 1.749199854809259f;
float g = float.Log(1 + (float)span[sourceIndex+1]) / 1.749199854809259f;
float b = float.Log(1 + (float)span[sourceIndex+2]) / 1.749199854809259f;

// 应用色彩转换矩阵
float bt2020R = matrix[0, 0] * r + matrix[0, 1] * g + matrix[0, 2] * b;
float bt2020G = matrix[1, 0] * r + matrix[1, 1] * g + matrix[1, 2] * b;
float bt2020B = matrix[2, 0] * r + matrix[2, 1] * g + matrix[2, 2] * b;
// 应用色彩转换矩阵
float bt2020R = matrix[0, 0] * r + matrix[0, 1] * g + matrix[0, 2] * b;
float bt2020G = matrix[1, 0] * r + matrix[1, 1] * g + matrix[1, 2] * b;
float bt2020B = matrix[2, 0] * r + matrix[2, 1] * g + matrix[2, 2] * b;

// 转换并填充结果
// 转换并填充结果

result[targetIndex ] = (byte)Math.Clamp(bt2020B * 255,0,255);
result[targetIndex + 1] = (byte)Math.Clamp(bt2020G * 255,0,255);
result[targetIndex+ 2] = (byte)Math.Clamp(bt2020R * 255,0,255);
result[targetIndex + 3] = 255; // Alpha 固定为 255
result[targetIndex ] = (byte)Math.Clamp(bt2020B * 255,0,255);
result[targetIndex + 1] = (byte)Math.Clamp(bt2020G * 255,0,255);
result[targetIndex+ 2] = (byte)Math.Clamp(bt2020R * 255,0,255);
result[targetIndex + 3] = 255; // Alpha 固定为 255
}
}
}
);
);

}

Expand Down
1 change: 1 addition & 0 deletions Core.Window/ScreenCapture/ScreenCaptureByDx11.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
using SixLabors.ImageSharp.PixelFormats;
using Vanara.Extensions.Reflection;
using Vanara.PInvoke;
using Rect = PluginCore.Rect;


namespace Core.Window;
Expand Down
62 changes: 55 additions & 7 deletions Core.Window/ScreenCapture/ScreenCaptureByWGC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using IDirect3DDevice = Windows.Graphics.DirectX.Direct3D11.IDirect3DDevice;
using IDirect3DSurface = Windows.Graphics.DirectX.Direct3D11.IDirect3DSurface;
using IInspectable = WinRT.IInspectable;
using Rect = PluginCore.Rect;

namespace Core.Window;

Expand Down Expand Up @@ -74,12 +75,12 @@ public List<WindowInfo> GetAllWindowInfo()
zIndex++;
currentHwnd = User32.GetWindow(currentHwnd, User32.GetWindowCmd.GW_HWNDNEXT);
// 忽略有父窗口的和不可见的窗口
if (!User32.GetParent(currentHwnd).IsNull || !User32.IsWindowVisible(currentHwnd)|| User32.IsIconic(currentHwnd))
if (!User32.GetParent(currentHwnd).IsNull || !User32.IsWindowVisible(currentHwnd))
{
continue;
}
int style = User32.GetWindowLong(currentHwnd,User32.WindowLongFlags.GWL_STYLE);
if ((style & WS_POPUP) != 0 || (style & WS_CHILD) != 0)
if ( (style & WS_CHILD) != 0)
{
continue;
}
Expand Down Expand Up @@ -264,17 +265,61 @@ public static (ComPtr<IDXGIAdapter1>,OutputDesc1) GetAdapterForMonitor(ComPtr<ID

public unsafe ScreenCaptureResult CaptureScreenBytes(ScreenCaptureInfo screenCaptureInfo)
{
if (screenCaptureInfo.ScreenInfo.hMonitor==null || screenCaptureInfo.ScreenInfo.hMonitor ==IntPtr.Zero)
switch (screenCaptureInfo.ScreenCaptureType)
{
screenCaptureInfo.ScreenInfo.hMonitor = FindHMonitor(screenCaptureInfo.ScreenInfo);
if (screenCaptureInfo.ScreenInfo.hMonitor==IntPtr.Zero)
case ScreenCaptureType.屏幕:
{
throw new Exception("目标显示器不存在");
if (screenCaptureInfo.ScreenInfo.hMonitor==null || screenCaptureInfo.ScreenInfo.hMonitor ==IntPtr.Zero)
{
screenCaptureInfo.ScreenInfo.hMonitor = FindHMonitor(screenCaptureInfo.ScreenInfo);
if (screenCaptureInfo.ScreenInfo.hMonitor==IntPtr.Zero)
{
throw new Exception("目标显示器不存在");
}
}
break;
}
case ScreenCaptureType.窗口:
{
if (!User32.IsWindow(screenCaptureInfo.WindowInfo.Hwnd))
{
var findWindow = User32.FindWindow(null,screenCaptureInfo.WindowInfo.Title);
if (findWindow.IsNull)
{
throw new Exception("目标窗口不存在");
}

screenCaptureInfo.WindowInfo.Hwnd = findWindow.DangerousGetHandle();

}

User32.GetWindowRect(screenCaptureInfo.WindowInfo.Hwnd, out var rect);
screenCaptureInfo.WindowInfo.Rect=new Rect(0,0,rect.Width,rect.Height);
var monitorFromWindow = User32.MonitorFromWindow(screenCaptureInfo.WindowInfo.Hwnd,User32.MonitorFlags.MONITOR_DEFAULTTONEAREST);
screenCaptureInfo.ScreenInfo.hMonitor = monitorFromWindow.DangerousGetHandle();
break;
}

}

var factory2 = ActivationFactory.Get(typeof(GraphicsCaptureItem).FullName);
var interop = factory2.AsInterface<IGraphicsCaptureItemInterop>();
var itemPointer = interop.CreateForMonitor(screenCaptureInfo.ScreenInfo.hMonitor, GraphicsCaptureItemGuid);
IntPtr itemPointer = IntPtr.Zero;
switch (screenCaptureInfo.ScreenCaptureType)
{
case ScreenCaptureType.屏幕:
{
itemPointer= interop.CreateForMonitor(screenCaptureInfo.ScreenInfo.hMonitor, GraphicsCaptureItemGuid);
break;
}
case ScreenCaptureType.窗口:
{

itemPointer= interop.CreateForWindow(screenCaptureInfo.WindowInfo.Hwnd, GraphicsCaptureItemGuid);
break;
}

}
var item = MarshalInterface<GraphicsCaptureItem>.FromAbi(itemPointer);
var dxgi = new DXGI(new DefaultNativeContext("dxgi"));

Expand Down Expand Up @@ -375,6 +420,9 @@ IDirect3DDevice CreateDirect3DDeviceFromSharpDXDevice(ID3D11Device* d3dDevice)
immediateContext->CopyResource(stagingResource, bitmap);
if (immediateContext->Map(stagingResource, 0, Map.Read, 0, &mappedSubresource) != 0)
throw new Exception("Failed to map staging texture");

//更新窗口的Size数据
screenCaptureInfo.WindowInfo.Rect = new Rect(0,0,item.Size.Width, item.Size.Height);
var bytesSpan = CaptureTool.GetBytesSpan(mappedSubresource,adapterForMonitor.Item2,ref screenCaptureInfo);

return new ScreenCaptureResult()
Expand Down
38 changes: 25 additions & 13 deletions KitopiaAvalonia/Windows/ScreenCaptureWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Point = Avalonia.Point;
using Rect = PluginCore.Rect;
using Rectangle = SixLabors.ImageSharp.Rectangle;
using Size = Avalonia.Size;

Expand All @@ -47,6 +48,7 @@ public partial class ScreenCaptureWindow : Window
private ScreenCaptureInfo _screenCaptureInfo;
private bool Finish = false;
private List<WindowInfo> _windowInfos;
private WindowInfo _currentWindowInfo;
public ScreenCaptureWindow(ScreenCaptureInfo screenCaptureInfo)
{
InitializeComponent();
Expand Down Expand Up @@ -292,7 +294,7 @@ protected override void OnPointerMoved(PointerEventArgs e)
SelectBox.Width = selectBoxWidth;
SelectBox._dragTransform.X = _startPoint.X;
}

_currentWindowInfo=new WindowInfo();
UpdateSelectBox();
}

Expand All @@ -317,7 +319,9 @@ protected override void OnPointerMoved(PointerEventArgs e)
}
else
{

var windowInfo = firstOrDefault.FirstOrDefault();
_currentWindowInfo=windowInfo;
_startPoint=new Point(windowInfo.Rect.X*screenInfoWidth,windowInfo.Rect.Y*screenInfoHeight);
SelectBox._dragTransform.X = _startPoint.X;
SelectBox._dragTransform.Y = _startPoint.Y;
Expand Down Expand Up @@ -553,7 +557,7 @@ private void SelectBox_OnPointerMoved(object? sender, PointerEventArgs e)
ellipse.Width = round;
ellipse.Height = round;
ellipse.Measure(new Size(round, round));
ellipse.Arrange(new Rect(new Point(0, 0), new Size(round, round)));
ellipse.Arrange(new Avalonia.Rect(new Point(0, 0), new Size(round, round)));
renderTargetBitmap.Render(ellipse);
SelectBox.Cursor.Dispose();
SelectBox.Cursor = new Cursor(renderTargetBitmap,
Expand All @@ -575,7 +579,7 @@ private void SelectBox_OnPointerMoved(object? sender, PointerEventArgs e)
ellipse.Width = round;
ellipse.Height = round;
ellipse.Measure(new Size(round, round));
ellipse.Arrange(new Rect(new Point(0, 0), new Size(round, round)));
ellipse.Arrange(new Avalonia.Rect(new Point(0, 0), new Size(round, round)));
renderTargetBitmap.Render(ellipse);
SelectBox.Cursor.Dispose();
SelectBox.Cursor = new Cursor(renderTargetBitmap,
Expand Down Expand Up @@ -758,11 +762,11 @@ private void UpdateSelectBox()
{
var fullScreenRect = new RectangleGeometry
{
Rect = new Rect(0, 0, Bounds.Width, Bounds.Height)
Rect = new Avalonia.Rect(0, 0, Bounds.Width, Bounds.Height)
};
var selectionRect = new RectangleGeometry
{
Rect = new Rect(new Point(SelectBox._dragTransform.X, SelectBox._dragTransform.Y), SelectBox.DesiredSize)
Rect = new Avalonia.Rect(new Point(SelectBox._dragTransform.X, SelectBox._dragTransform.Y), SelectBox.DesiredSize)
};


Expand Down Expand Up @@ -830,15 +834,23 @@ private void FinnishCapture()
else cropH = (int)selectBoxHeight + (int)dragTransformY;
if (selectMode)
{
selectModeAction.Invoke(new ScreenCaptureInfo()
if (_currentWindowInfo.Hwnd != IntPtr.Zero)
selectModeAction.Invoke(new ScreenCaptureInfo()
{
ScreenCaptureType = ScreenCaptureType.窗口,
WindowInfo = _currentWindowInfo,
});
else
{

X = Math.Max((int)dragTransformX, 0),
Y = Math.Max((int)dragTransformY, 0),
Width = cropW,
Height = cropH,
ScreenInfo = _screenCaptureInfo.ScreenInfo
});
selectModeAction.Invoke(new ScreenCaptureInfo()
{
X = Math.Max((int)dragTransformX, 0),
Y = Math.Max((int)dragTransformY, 0),
Width = cropW,
Height = cropH,
ScreenInfo = _screenCaptureInfo.ScreenInfo
});
}
} else{
foreach (var canvasChild in Canvas.Children)
if (canvasChild is CaptureToolBase draggableResizeableControl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ public string Serialize<T>(T value)
{
return "";
}
return $"WindowInfo.Title={screenCaptureInfo.WindowInfo.Title},ScreenInfo.X={screenCaptureInfo.ScreenInfo.X},ScreenInfo.Y={screenCaptureInfo.ScreenInfo.Y},ScreenInfo.Height={screenCaptureInfo.ScreenInfo.Height},ScreenInfo.Width={screenCaptureInfo.ScreenInfo.Width},{nameof(screenCaptureInfo.X)}={screenCaptureInfo.X},{nameof(screenCaptureInfo.Y)}={screenCaptureInfo.Y},{nameof(screenCaptureInfo.Width)}={screenCaptureInfo.Width},{nameof(screenCaptureInfo.Height)}={screenCaptureInfo.Height}";
return $"ScreenCaptureType={screenCaptureInfo.ScreenCaptureType},WindowInfo.Title={screenCaptureInfo.WindowInfo.Title},ScreenInfo.X={screenCaptureInfo.ScreenInfo.X},ScreenInfo.Y={screenCaptureInfo.ScreenInfo.Y},ScreenInfo.Height={screenCaptureInfo.ScreenInfo.Height},ScreenInfo.Width={screenCaptureInfo.ScreenInfo.Width},{nameof(screenCaptureInfo.X)}={screenCaptureInfo.X},{nameof(screenCaptureInfo.Y)}={screenCaptureInfo.Y},{nameof(screenCaptureInfo.Width)}={screenCaptureInfo.Width},{nameof(screenCaptureInfo.Height)}={screenCaptureInfo.Height}";
}

public object Deserialize(ReadOnlySpan<byte> value)
{
var str = Encoding.UTF8.GetString(value);

var split = str.Split(',');
if (split.Length != 9)
if (split.Length != 10)
{
return null;
}
Expand All @@ -36,6 +36,7 @@ public object Deserialize(ReadOnlySpan<byte> value)

var screenCaptureInfo = new ScreenCaptureInfo
{
ScreenCaptureType = Enum.Parse<ScreenCaptureType>(dic["ScreenCaptureType"]),
ScreenInfo = new ScreenInfo
{
Height = int.Parse(dic["ScreenInfo.Height"]),
Expand Down
22 changes: 21 additions & 1 deletion KitopiaEx/KitopiaEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,35 @@ public void OnEnabled(IServiceProvider serviceProvider)
Kitopia.ToolTipConverters.TryAdd(typeof(ScreenCaptureInfo), info =>
{
var screenCaptureInfo = (ScreenCaptureInfo)info;
if (screenCaptureInfo.ScreenCaptureType == ScreenCaptureType.屏幕)
{
return
$"显示器:{screenCaptureInfo.ScreenInfo.hMonitor},起始坐标:{screenCaptureInfo.X},{screenCaptureInfo.Y}\n大小:{screenCaptureInfo.Width}x{screenCaptureInfo.Height}";
}
if (screenCaptureInfo.ScreenCaptureType == ScreenCaptureType.窗口)
{
return
$"窗口:{screenCaptureInfo.WindowInfo.Title}";
}
return
$"显示器:{screenCaptureInfo.ScreenInfo.hMonitor},起始坐标:{screenCaptureInfo.X},{screenCaptureInfo.Y}\n大小:{screenCaptureInfo.Width}x{screenCaptureInfo.Height}";
});
Kitopia.ToolTipConverters.TryAdd(typeof(ScreenCaptureResult), e =>
{
var screenCaptureResult = (ScreenCaptureResult)e;
var screenCaptureInfo = screenCaptureResult.Info;
if (screenCaptureInfo.ScreenCaptureType == ScreenCaptureType.屏幕)
{
return
$"显示器:{screenCaptureInfo.ScreenInfo.hMonitor},起始坐标:{screenCaptureInfo.X},{screenCaptureInfo.Y}\n大小:{screenCaptureInfo.Width}x{screenCaptureInfo.Height}";
}
if (screenCaptureInfo.ScreenCaptureType == ScreenCaptureType.窗口)
{
return
$"窗口:{screenCaptureInfo.WindowInfo.Title}";
}
return
$"显示器:{screenCaptureInfo.ScreenInfo.hMonitor},起始坐标:{screenCaptureInfo.X},{screenCaptureInfo.Y}\n大小:{screenCaptureInfo.Width}x{screenCaptureInfo.Height}\nByte数据:{(screenCaptureResult.Bytes is null?"不存在":"存在")}\nBitmap数据:{(screenCaptureResult.Source is null?"不存在":"存在")}";
$"起始坐标:{screenCaptureInfo.X},{screenCaptureInfo.Y}\n大小:{screenCaptureInfo.Width}x{screenCaptureInfo.Height}\nByte数据:{(screenCaptureResult.Bytes is null?"不存在":"存在")}\nBitmap数据:{(screenCaptureResult.Source is null?"不存在":"存在")}";
});
Kitopia.JsonConverters.TryAdd(typeof(ScreenCaptureInfo), new ScreenCaptureInfoCustomScenarioValueSerializer());
}
Expand Down
1 change: 1 addition & 0 deletions KitopiaEx/Ocr/OcrResultShowWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Avalonia.Threading;
using Microsoft.Extensions.DependencyInjection;
using PluginCore;
using Rect = Avalonia.Rect;

namespace KitopiaEx.Ocr;

Expand Down
2 changes: 1 addition & 1 deletion PluginCore
Submodule PluginCore updated 1 files
+9 −0 IScreenCapture.cs

0 comments on commit 82f5367

Please sign in to comment.