4

I use Delphi 10.3 Rio, and need to know the screen PixelsPerInch ratio to scale my application accordingly.

Calculating with the formula, my screen has 142 ppi. (Real values are: 15.5" diagonal and 1920 x 1080 resolution). But when I read in Delphi the Screen.PixelsPerInch property, I get 134 ! And this value is reportend in PixelsPerInch property of every TForm I create, too. So, why this difference and which is the real ppi ?

AIDA64 reports the real value of 142 ppi... So I think is something wrong with the pixels per inch ratio in Delphi... enter image description here

Edit:

I managed to get the real PPI with this code... but I cannot change this in every Delphi component. So, if I use this value in my components, won't I mess everything up ?

procedure TForm1.FormCreate(Sender: TObject);
var PhiW, PhiH, PixW, PixH: Integer;
    DC: HDC;
    PhiD, PixD, PPI: Real;
begin
 DC:= Form1.Canvas.Handle;
 PhiW:= GetDeviceCaps(DC, HORZSIZE);
 PhiH:= GetDeviceCaps(DC, VERTSIZE);
 PixW:= GetDeviceCaps(DC, HORZRES);
 PixH:= GetDeviceCaps(DC, VERTRES);
 PhiD:= (Sqrt((PhiW*PhiW)+(PhiH*PhiH)))/25.4;
 PixD:= Sqrt((PixW*PixW)+(PixH*PixH));
 PPI:= PixD / PhiD;
 Label1.Caption:= IntToStr(Round(PPI))+' ppi';
end;
Marus Gradinaru
  • 2,824
  • 1
  • 26
  • 55
  • Which version of Delphi are you using? Sounds like a bug you should [report to Embarcadero](https://quality.embarcadero.com), if it hasn't been reported yet. – Remy Lebeau Jan 20 '22 at 15:42
  • @RemyLebeau I use Delphi 10.3 Rio... Do you get the real values in other Delphi version ? – Marus Gradinaru Jan 20 '22 at 15:47
  • @MarcusGradinaru I haven't tried it. – Remy Lebeau Jan 20 '22 at 15:53
  • Your calculation gives me 72 ppi for 528x298 mm displaying 1920x1080 px. Delphi 7's `Screen.PixelsPerInch` reports 96. – AmigoJack Jan 20 '22 at 16:57
  • 3
    I think i solved the mistery... `Screen.PixelsPerInch` does not report the real ppi of the screen, but the standard 96 ppi scaled by your windows scaling setting. I had windows scaling set to 140%. So, 96 x 1.4 = 134 ppi... – Marus Gradinaru Jan 20 '22 at 17:20
  • @MarusGradinaru I'm not surprised: on just 34x19 cm I would not dare to display 1920x1080 px. Just use a lower resolution and go back to 100%. – AmigoJack Jan 20 '22 at 17:41
  • @MarusGradinaru You might as well answer your own question with that. – J... Jan 20 '22 at 20:41
  • I will... But I want to research a little more, because I think that updating `Font.PixelsPerInch` with Windows scaled PPI instead the real PPI is a mistake... but I'm not sure. – Marus Gradinaru Jan 20 '22 at 22:28

1 Answers1

1

Operating system PPI

There is no bug and Delphi returns proper value in PixelsPerInch.

PPI OS will return for the purpose of scaling your application is not the actual PPI value of the actual display device, but virtual pixel density.

For developing applications the PPI value you need is the one that OS gives you, not the actual PPI value of the display device.

Everything you need to know is baseline PPI for the OS and current PPI or the scale factor. Using those numbers you can then calculate number of scaled pixels from some baseline pixel value.

For instance, if your control baseline width is 100 pixels and screen scale is 150% from the baseline PPI then your runtime control size will be 150 pixels.

Different operating systems have different baseline PPI.

OS Baseline PPI
Windows 96 PPI
macOS 72 PPI
Android 160 PPI
iOS 163 PPI

Calculation:

ScaledPx := MulDiv(Px, PixelsPerInch, BaselinePixelsPerInch);

ScaledPx := Px * ScaleFactor;

On Windows:

ScaledPx := MulDiv(Px, PixelsPerInch, 96);

Why you need to use virtual OS PPI and not hardware PPI?

First, different display devices have different hardware pixel density. For instance, full HD monitors with resolution of 1920x1080 pixels will have different hardware PPI depending on whether the device size is 22" or 24". First one will have 100.13 PPI and second one 91.79 PPI.

But when you run Windows values returned for both monitors will be 96 pixels if you use default Windows scale setting (100%). Obviously that value does not match either of those hardware PPI values and is just approximately close.

So displaying 100 pixels square on both monitors will result with different physical dimensions of that square.

Next, user can choose to have different scale setting. Someone might want to use 125% scale which is 120 PPI. If you would use actual hardware PPI in your application, for such user, everything would be too small.

Because virtual PPI can be adjusted, knowing actual hardware PPI is of very little use when writing general purpose applications. For DTP and similar applications, there is sometimes value in being able to see design in 1:1 scale, but in such cases people commonly use monitors which hardware density matches the OS or user must be able to enter his hardware configuration and that value is used to calculate pixel dimensions that need to accurately match physical ones.

While there are OS APIs which will give you actual hardware PPI of the display device, on many devices across platforms you will have hard time getting the correct PPI value for various reasons.

Font PPI

Besides general virtual PPI, there is another PPI value that can be independently customized by user - font PPI. Baseline for font scale is the same as baseline for virtual PPI on different platforms. Applications that fully honor user's settings will show textual data and other related dimensions depending on font PPI rather than general virtual PPI.

Delphi currently does not have built in support for custom font PPI values. In other words, if you set font size to be 10 pt, the actual pixel height of the font will be calculated based on the PixelsPerInch - general PPI.

If the user font scale is set to 100% (which is most commonly used value) then Delphi will correctly scale fonts based on PixelsPerInch value.

Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159
  • Yes, I understand that. The only problem I see now (and you saw it too) is that Delphi does not have support for custom font PPI values. If I choose to scale my forms at a different PPI value, other than OS PPI, the `Font.Size` will be calculated with the OS PPI and a font size of... lets say 14 will not have the same physical height as a 14 font size on a baseline PPI. – Marus Gradinaru Jan 21 '22 at 11:11
  • If you want to handle different scales, you will need to adjust font sizes manually. Additionally, even if Delphi would have support for separate font PPI, it would still be based on the OS provided scale, not some custom value you choose. Unless you have some very specific reason, your application should use values provided by the OS, not what you think is the right scale. – Dalija Prasnikar Jan 21 '22 at 11:17
  • Of course, the right scling PPI is the one returned by the OS. But even is I scale my form at the OS PPI, the `Font.Size` will not be calculated right if its `Font.PixelsPerInch` is not set to the physical screen PPI. For me, a "right font size" means that the same Size has to correspond the same physical height, regardless of the screen PPI. I think thats why it was introduced, so we can have control of the font height size on every display. Otherwise we could have specified the font size directly in pixels... – Marus Gradinaru Jan 21 '22 at 11:36
  • "Font.Size will not be calculated right if its Font.PixelsPerInch is not set to the physical screen PPI" That is not true. Font.Size (pt) is also virtual dimension and it does not follow physical typographic size. In other words on two monitors (22" and 24") for default Windows font size of 9 pt size in pixels will be the same (provided that both monitors have same resolution and scale). If you try to calculate font size based on hardware PPI you will get wrong result - not the ones user wants or needs. – Dalija Prasnikar Jan 21 '22 at 12:06
  • The only exception is if user custom font scale is not 100% and you want to use that custom scale on fonts, but again using it will also not match physical size. – Dalija Prasnikar Jan 21 '22 at 12:08
  • I managed to verify your logic, and it's right. I don't fully understand how it works, but it works. I have 2 laptops with verry different displays, and if leave the `Font.PixelsPerInch` as it is set by the system, and I choose a `Font.Size` of 11 on both computers, the font has the same visual size. – Marus Gradinaru Jan 21 '22 at 13:32
  • It is because for screen displays PPI used in OS is just an approximation and does not match hardware PPI. The "inch" part is not real inch. For printers PPI (DPI) is not an approximation and if you want to print one inch on 300 DPI printer you need to send 300 pixels. – Dalija Prasnikar Jan 21 '22 at 14:10