/*
* 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();
}
}