/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * * For more information see: http://getgreenshot.org/ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using GreenshotPlugin.Core.Enums; using GreenshotPlugin.UnmanagedHelpers; using System; using System.Drawing; using System.Runtime.InteropServices; using GreenshotPlugin.UnmanagedHelpers.Enums; using GreenshotPlugin.UnmanagedHelpers.Structs; namespace GreenshotPlugin.Core { /// /// This handles DPI changes see /// Writing DPI-Aware Desktop and Win32 Applications /// public static class DpiHelper { /// /// This is the default DPI for the screen /// public const uint DefaultScreenDpi = 96; /// /// Retrieve the current DPI for the UI element which is related to this DpiHandler /// public static uint Dpi { get; private set; } = WindowsVersion.IsWindows10OrLater ? GetDpiForSystem() : DefaultScreenDpi; /// /// Calculate a DPI scale factor /// /// uint /// double public static float DpiScaleFactor(uint dpi) { if (dpi == 0) { dpi = Dpi; } return (float)dpi / DefaultScreenDpi; } /// /// Scale the supplied number according to the supplied dpi /// /// double with e.g. the width 16 for 16x16 images /// current dpi, normal is 96. /// A function which can modify the scale factor /// double with the scaled number public static float ScaleWithDpi(float someNumber, uint dpi, Func scaleModifier = null) { var dpiScaleFactor = DpiScaleFactor(dpi); if (scaleModifier != null) { dpiScaleFactor = scaleModifier(dpiScaleFactor); } return dpiScaleFactor * someNumber; } /// /// Scale the supplied Size according to the supplied dpi /// /// Size to resize /// current dpi, normal is 96. /// A function which can modify the scale factor /// NativeSize scaled public static Size ScaleWithDpi(Size size, uint dpi, Func scaleModifier = null) { var dpiScaleFactor = DpiScaleFactor(dpi); if (scaleModifier != null) { dpiScaleFactor = scaleModifier(dpiScaleFactor); } return new Size((int)(dpiScaleFactor * size.Width), (int)(dpiScaleFactor * size.Height)); } /// /// Scale the supplied NativeSize to the current dpi /// /// NativeSize to scale /// A function which can modify the scale factor /// NativeSize scaled public static Size ScaleWithCurrentDpi(Size size, Func scaleModifier = null) { return ScaleWithDpi(size, Dpi, scaleModifier); } /// /// Return the DPI for the screen which the location is located on /// /// POINT /// uint public static uint GetDpi(POINT location) { RECT rect = new RECT(location.X, location.Y, 1,1); IntPtr hMonitor = User32.MonitorFromRect(ref rect, User32.MONITOR_DEFAULTTONEAREST); var result = GetDpiForMonitor(hMonitor, MonitorDpiType.EffectiveDpi, out var dpiX, out var dpiY); if (result.Succeeded()) { return dpiX; } return DefaultScreenDpi; } /// /// Retrieve the DPI value for the supplied window handle /// /// IntPtr /// dpi value public static uint GetDpi(IntPtr hWnd) { if (!User32.IsWindow(hWnd)) { return DefaultScreenDpi; } // Use the easiest method, but this only works for Windows 10 if (WindowsVersion.IsWindows10OrLater) { return GetDpiForWindow(hWnd); } // Use the second easiest method, but this only works for Windows 8.1 or later if (WindowsVersion.IsWindows81OrLater) { var hMonitor = User32.MonitorFromWindow(hWnd, MonitorFrom.DefaultToNearest); // ReSharper disable once UnusedVariable var result = GetDpiForMonitor(hMonitor, MonitorDpiType.EffectiveDpi, out var dpiX, out var dpiY); if (result.Succeeded()) { return dpiX; } } // Fallback to the global DPI settings using var hdc = SafeWindowDcHandle.FromWindow(hWnd); if (hdc == null) { return DefaultScreenDpi; } return (uint)GDI32.GetDeviceCaps(hdc, DeviceCaps.LOGPIXELSX); } /// /// See more at GetDpiForWindow function /// Returns the dots per inch (dpi) value for the associated window. /// /// IntPtr /// uint with dpi [DllImport("User32.dll")] private static extern uint GetDpiForWindow(IntPtr hWnd); /// /// See /// GetDpiForMonitor function /// Queries the dots per inch (dpi) of a display. /// /// IntPtr /// MonitorDpiType /// out int for the horizontal dpi /// out int for the vertical dpi /// true if all okay [DllImport("shcore.dll", SetLastError = true)] private static extern HResult GetDpiForMonitor(IntPtr hMonitor, MonitorDpiType dpiType, out uint dpiX, out uint dpiY); /// /// See GetDpiForSystem function /// Returns the system DPI. /// /// uint with the system DPI [DllImport("User32.dll")] private static extern uint GetDpiForSystem(); } }