Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi High DPI switch between own scaling and Windows scaling

Tags:

dpi

delphi

hdpi

Some of my customers want to be able to scale my application manually (when Windows dpi is set to 96), so I had to implement scaling. Unfortunately these customers cannot go with setting Windows DPI to an other value and let WIndows scale my app because some very important applications they use do not behave well at all on resolutions <> 96 DPI.

I managed to make my Delphi 10.1 application scale quite well even on 200% but the higher the factor becomes the more some proportions become "not so nice looking". Many 3rd party components need special treatment for scaling and even then do not scale 100% accurate. Although applications scaled by windows look a little blurry on high resolutions, all proportions are 100% accurate and imho the app looks much more professional.

So I asked myself whether it is possible to create a setting that allows to tell Windows to do the scaling as a default and only scale on my own if the customer wants a scaling that is different from the current Windows scaling. This setting is hosted in the Windows manifest of the executable that is read on start of the application. Is there a way to change it at runtime (early startup of the application)? Creating two executables with different manifests is surely no good solution.

Thanks for any help

like image 380
MichaSchumann Avatar asked Nov 19 '16 10:11

MichaSchumann


People also ask

How do you override high DPI scaling behavior?

Right-click the application, select Properties, select the Compatibility tab, and then select the Disable display scaling on high DPI settings check box.

What does the override high DPI scaling setting actually do?

DPI-scaling overrides This forces the process to run in per-monitor DPI awareness mode. This setting was previously referred to as “Disable display scaling on high-DPI settings.”

What happens if I disable display scaling on high DPI settings?

This option was previously known as “Disable display scaling on high DPI settings”, and it does the same thing. System: Windows will use its normal behavior. Applications that don't respect system DPI settings will be “bitmap stretched” to appear larger so they're more easily readable, but will often appear blurry.

Does Windows scaling reduce image quality?

Yes ,scaling down reduces the quality of the image as most of the original pixels are no longer available (scaling is done by Down sampling) .

What does enable high DPI scaling mean?

High DPI displays have increased pixel density, compared to standard DPI displays. Pixel density is measured in Dots per Inch (DPI) or Pixels per Inch (PPI), and is determined by the number of display pixels and their size.


1 Answers

Thanks to Sertac Akyuz I found a solution to my problem. In the Initialization part of the unit containing the scaling code I can switch between DPI-Awareness and Non-DPI-Awareness. It is important not to have this setting in the application manifest, which can be achieved by supplying a custom manifest like this (use control decoration and run with rights of current user):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
 <dependency>
   <dependentAssembly>
     <assemblyIdentity
       type="win32"
       name="Microsoft.Windows.Common-Controls"
       version="6.0.0.0"
       publicKeyToken="6595b64144ccf1df"
       language="*"
       processorArchitecture="*"/>
   </dependentAssembly>
 </dependency>
 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
   <security>
     <requestedPrivileges>
       <requestedExecutionLevel
         level="asInvoker"
         uiAccess="false"/>
       </requestedPrivileges>
   </security>
 </trustInfo>
</assembly>

This is the actual code switching depending on a registry key:

// Set DPI Awareness depending on a registry setting
with TRegIniFile.create('SOFTWARE\' + SRegName) do
begin
  setting := readInteger('SETTINGS', 'scale', 0);
  Free;
end;
handle := LoadLibrary('shcore.dll');
if handle <> 0 then
begin
  setProcessDPIAwareness := GetProcAddress(handle, 'SetProcessDpiAwareness');
  if Assigned(setProcessDPIAwareness) then
  begin
    if setting < 2 then
      // setting <2 means no scaling vs Windows
      setProcessDPIAwareness(0)
    else
      // setting 2: 120%, 3: 140% vs. Windows
      // The actual used scaling factor multiplies by windows DPI/96
      setProcessDPIAwareness(1);
  end;
  FreeLibrary(handle);
  // Get windows scaling as Screen.PixelsPerInch was read before swiching DPI awareness
  // Our scaling routines now work with WinDPI instead of Screen.PixelsPerInch
  WinDPI:= Screen.MonitorFromWindow(application.handle).PixelsPerInch;
end;

The last line of this snippet retrieves the current DPI for the current monitor as screen.pixelsperinch seems to be initialized before and does always return 96 as for a non dpi aware application. I use the value of winDPI in all subsequent scaling calculations and it works perfect.

like image 66
MichaSchumann Avatar answered Sep 19 '22 16:09

MichaSchumann