[Text Extractor] Restructuring Screen scale handling (#31646)

* [Text Extractor] Restructuring Screen scale handling

* spell checker

* Update src/modules/PowerOCR/PowerOCR/Helpers/WPFExtensionMethods.cs

Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com>

* Update src/modules/PowerOCR/PowerOCR/Helpers/WPFExtensionMethods.cs

* Adding a workaround to move the window in the desired position

* Restructure coordinate calculations.

---------

Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com>
This commit is contained in:
Laszlo Nemeth
2024-03-22 16:02:30 +01:00
committed by GitHub
parent f6e7635a4e
commit 3dc0ed100c
7 changed files with 73 additions and 62 deletions

View File

@@ -47,51 +47,17 @@ internal sealed class ImageMethods
return destination;
}
internal static ImageSource GetWindowBoundsImage(Window passedWindow)
internal static ImageSource GetWindowBoundsImage(OCROverlay passedWindow)
{
DpiScale dpi = VisualTreeHelper.GetDpi(passedWindow);
int windowWidth = (int)(passedWindow.ActualWidth * dpi.DpiScaleX);
int windowHeight = (int)(passedWindow.ActualHeight * dpi.DpiScaleY);
System.Windows.Point absPosPoint = passedWindow.GetAbsolutePosition();
int thisCorrectedLeft = (int)absPosPoint.X;
int thisCorrectedTop = (int)absPosPoint.Y;
using Bitmap bmp = new(windowWidth, windowHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Rectangle screenRectangle = passedWindow.GetScreenRectangle();
using Bitmap bmp = new(screenRectangle.Width, screenRectangle.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(thisCorrectedLeft, thisCorrectedTop, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
g.CopyFromScreen(screenRectangle.Left, screenRectangle.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
return BitmapToImageSource(bmp);
}
internal static Bitmap GetWindowBoundsBitmap(Window passedWindow)
{
DpiScale dpi = VisualTreeHelper.GetDpi(passedWindow);
int windowWidth = (int)(passedWindow.ActualWidth * dpi.DpiScaleX);
int windowHeight = (int)(passedWindow.ActualHeight * dpi.DpiScaleY);
System.Windows.Point absPosPoint = passedWindow.GetAbsolutePosition();
int thisCorrectedLeft = (int)absPosPoint.X;
int thisCorrectedTop = (int)absPosPoint.Y;
Bitmap bmp = new(
windowWidth,
windowHeight,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(
thisCorrectedLeft,
thisCorrectedTop,
0,
0,
bmp.Size,
CopyPixelOperation.SourceCopy);
return bmp;
}
internal static Bitmap GetRegionAsBitmap(Window passedWindow, Rectangle selectedRegion)
internal static Bitmap GetRegionAsBitmap(OCROverlay passedWindow, Rectangle selectedRegion)
{
Bitmap bmp = new(
selectedRegion.Width,
@@ -99,15 +65,11 @@ internal sealed class ImageMethods
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using Graphics g = Graphics.FromImage(bmp);
System.Windows.Point absPosPoint = passedWindow.GetAbsolutePosition();
int thisCorrectedLeft = (int)absPosPoint.X + selectedRegion.Left;
int thisCorrectedTop = (int)absPosPoint.Y + selectedRegion.Top;
Rectangle screenRectangle = passedWindow.GetScreenRectangle();
g.CopyFromScreen(
thisCorrectedLeft,
thisCorrectedTop,
screenRectangle.Left + selectedRegion.Left,
screenRectangle.Top + selectedRegion.Top,
0,
0,
bmp.Size,
@@ -117,7 +79,7 @@ internal sealed class ImageMethods
return bmp;
}
internal static async Task<string> GetRegionsText(Window? passedWindow, Rectangle selectedRegion, Language? preferredLanguage)
internal static async Task<string> GetRegionsText(OCROverlay? passedWindow, Rectangle selectedRegion, Language? preferredLanguage)
{
if (passedWindow is null)
{
@@ -130,17 +92,15 @@ internal sealed class ImageMethods
return resultText != null ? resultText.Trim() : string.Empty;
}
internal static async Task<string> GetClickedWord(Window passedWindow, System.Windows.Point clickedPoint, Language? preferredLanguage)
internal static async Task<string> GetClickedWord(OCROverlay passedWindow, System.Windows.Point clickedPoint, Language? preferredLanguage)
{
DpiScale dpi = VisualTreeHelper.GetDpi(passedWindow);
Bitmap bmp = new((int)(passedWindow.ActualWidth * dpi.DpiScaleX), (int)(passedWindow.ActualHeight * dpi.DpiScaleY), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Rectangle screenRectangle = passedWindow.GetScreenRectangle();
Bitmap bmp = new((int)screenRectangle.Width, (int)passedWindow.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bmp);
System.Windows.Point absPosPoint = passedWindow.GetAbsolutePosition();
int thisCorrectedLeft = (int)absPosPoint.X;
int thisCorrectedTop = (int)absPosPoint.Y;
g.CopyFromScreen(thisCorrectedLeft, thisCorrectedTop, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
g.CopyFromScreen((int)absPosPoint.X, (int)absPosPoint.Y, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
System.Windows.Point adjustedPoint = new(clickedPoint.X, clickedPoint.Y);

View File

@@ -62,7 +62,7 @@ namespace PowerOCR.Helpers
}
}
public static async Task<string> GetRegionsTextAsTableAsync(Window passedWindow, Rectangle regionScaled, Language? language)
public static async Task<string> GetRegionsTextAsTableAsync(OCROverlay passedWindow, Rectangle regionScaled, Language? language)
{
if (language is null)
{

View File

@@ -37,4 +37,28 @@ public static class WPFExtensionMethods
return new Point(r.X, r.Y);
}
public static DpiScale GetDpi(this System.Windows.Forms.Screen screen)
{
var point = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(point, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, DpiType.Effective, out uint dpiX, out uint dpiY);
return new DpiScale(dpiX / 96.0, dpiY / 96.0);
}
// https://msdn.microsoft.com/library/windows/desktop/dd145062(v=vs.85).aspx
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
// https://msdn.microsoft.com/library/windows/desktop/dn280510(v=vs.85).aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
// https://msdn.microsoft.com/library/windows/desktop/dn280511(v=vs.85).aspx
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
}

View File

@@ -24,8 +24,9 @@ public static class WindowUtilities
Logger.LogInfo($"Adding Overlays for each screen");
foreach (Screen screen in Screen.AllScreens)
{
Logger.LogInfo($"screen {screen}");
OCROverlay overlay = new(screen.Bounds);
DpiScale dpiScale = screen.GetDpi();
Logger.LogInfo($"screen {screen}, dpiScale {dpiScale.DpiScaleX}, {dpiScale.DpiScaleY}");
OCROverlay overlay = new(screen.Bounds, dpiScale);
overlay.Show();
ActivateWindow(overlay);

View File

@@ -7,8 +7,6 @@
xmlns:p="clr-namespace:PowerOCR.Properties"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Title="TextExtractor"
Width="200"
Height="200"
ui:Design.Background="Transparent"
AllowsTransparency="True"
Background="Transparent"

View File

@@ -5,10 +5,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using Common.UI;
using ManagedCommon;
@@ -42,11 +44,21 @@ public partial class OCROverlay : Window
private bool isComboBoxReady;
private const double ActiveOpacity = 0.4;
private readonly UserSettings userSettings = new(new ThrottledActionInvoker());
private System.Drawing.Rectangle screenRectangle;
private DpiScale dpiScale;
public OCROverlay(System.Drawing.Rectangle screenRectangle)
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool bRepaint);
public OCROverlay(System.Drawing.Rectangle screenRectangleParam, DpiScale dpiScaleParam)
{
Left = screenRectangle.Left >= 0 ? screenRectangle.Left : screenRectangle.Left + (screenRectangle.Width / 2);
Top = screenRectangle.Top >= 0 ? screenRectangle.Top : screenRectangle.Top + (screenRectangle.Height / 2);
screenRectangle = screenRectangleParam;
dpiScale = dpiScaleParam;
Left = screenRectangle.Left;
Top = screenRectangle.Top;
Width = screenRectangle.Width / dpiScale.DpiScaleX;
Height = screenRectangle.Height / dpiScale.DpiScaleY;
InitializeComponent();
@@ -106,7 +118,6 @@ public partial class OCROverlay : Window
private void Window_Loaded(object sender, RoutedEventArgs e)
{
WindowState = WindowState.Maximized;
FullWindow.Rect = new Rect(0, 0, Width, Height);
KeyDown += MainWindow_KeyDown;
KeyUp += MainWindow_KeyUp;
@@ -119,6 +130,12 @@ public partial class OCROverlay : Window
#if DEBUG
Topmost = false;
#endif
IntPtr hwnd = new WindowInteropHelper(this).Handle;
// The first move puts it on the correct monitor, which triggers WM_DPICHANGED
// The +1/-1 coerces WPF to update Window.Top/Left/Width/Height in the second move
MoveWindow(hwnd, (int)(screenRectangle.Left + 1), (int)screenRectangle.Top, (int)(screenRectangle.Width - 1), (int)screenRectangle.Height, false);
MoveWindow(hwnd, (int)screenRectangle.Left, (int)screenRectangle.Top, (int)screenRectangle.Width, (int)screenRectangle.Height, true);
}
private void Window_Unloaded(object sender, RoutedEventArgs e)
@@ -476,4 +493,9 @@ public partial class OCROverlay : Window
break;
}
}
public System.Drawing.Rectangle GetScreenRectangle()
{
return screenRectangle;
}
}

View File

@@ -47,6 +47,12 @@
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
PerMonitor
</dpiAwareness>
</windowsSettings>
</application>
</compatibility>