Search code examples
delphiscreendelphi-10.3-rioppi

Why PPI reported by Delphi is different from the calculated one?


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;

Solution

  • 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.