Difference between revisions of "High DPI"

From Free Pascal wiki
Jump to navigationJump to search
m (→‎STEP 2 - Scale Forms and Controls: simplified procedure)
(New structure.)
Line 1: Line 1:
 
{{High DPI}}
 
{{High DPI}}
  
== Definition ==
+
== Introduction ==
 
DPI (dot per inch) is the relation between size in pixels and the actual display size. Here dot is an equivalent for pixel in printing terminology. Applications can either use pixel sizes, or take into account that actual display size. In this second cases, sizes are given in points.
 
DPI (dot per inch) is the relation between size in pixels and the actual display size. Here dot is an equivalent for pixel in printing terminology. Applications can either use pixel sizes, or take into account that actual display size. In this second cases, sizes are given in points.
  
Line 8: Line 8:
 
High DPI awareness means that an application takes this DPI setting into account.
 
High DPI awareness means that an application takes this DPI setting into account.
  
== Pixels and points ==
+
'''Pixels and points'''
  
For example 300 DPI means that there are 300 pixels (or dot) per inch. There are 72 points per inch, so :
+
For example '''300 DPI''' means that there are 300 pixels (or dot) per inch. There are 72 points per inch, so :
  
 
300 pixels ↔ 1 inch
 
300 pixels ↔ 1 inch
Line 18: Line 18:
 
4.16 pixels ↔ 1 point
 
4.16 pixels ↔ 1 point
  
 
+
'''Now with 96 DPI :'''
Now with 96 DPI :
 
  
 
72 pixels ↔ 1 inch
 
72 pixels ↔ 1 inch
Line 25: Line 24:
 
1.33 pixel ↔ 1 point
 
1.33 pixel ↔ 1 point
  
 
+
'''Now with 144 DPI :'''
Now with 144 DPI :
 
  
 
144 pixels ↔ 1 inch
 
144 pixels ↔ 1 inch
Line 32: Line 30:
 
2 pixels ↔ 1 point
 
2 pixels ↔ 1 point
  
== Setting High DPI in Windows ==
+
== Example - Fixed Font Sizes (not HighDPI) ==
In Windows 7 go to Control Panel > Appareance and Personalization > Display.
 
 
 
Select Smaller 100% (default), Medium 125% or Larger 150%. If you select 100% (96 DPI) is the default Windows DPI setting, not High DPI.
 
 
 
If you select 125% (120 DPI) the option "Use Windows XP style DPI scaling" is enabled, applications you run under this setting are scaled like at Windows XP.
 
 
 
If you select 150% (144 DPI) the option "Use Windows XP style DPI scaling" is disabled (DPI Virtualization enabled), applications you run under this setting must be High DPI Awareness else they will be scaled by the system like a blurred image.
 
 
 
Also you can set your custom DPI setting in the option "Set custom text size (DPI)" and enable/disable the DPI Virtualization.
 
 
 
== Example ==
 
  
 
Here is a form with an undefined font size (set to zero, which is the default value). Designed in 96 DPI (100%), it looks like this :
 
Here is a form with an undefined font size (set to zero, which is the default value). Designed in 96 DPI (100%), it looks like this :
Line 69: Line 56:
 
As a conclusion, it is possible to avoid inconsistence in the display by fixing font sizes. But we do not take into account that the graphical elements may be smaller according to actual DPI of the screen. With DPI awareness, it is possible to make application behave as if they new the real size of the pixels.
 
As a conclusion, it is possible to avoid inconsistence in the display by fixing font sizes. But we do not take into account that the graphical elements may be smaller according to actual DPI of the screen. With DPI awareness, it is possible to make application behave as if they new the real size of the pixels.
  
== Example (Windows) ==
+
== Setting High DPI under Windows ==
 +
 
 +
'''Windows Vista / Windows 7'''
 +
 
 +
In Windows 7 go to Control Panel > Appareance and Personalization > Display.
 +
 
 +
Select Smaller 100% (default), Medium 125% or Larger 150%. If you select 100% (96 DPI) is the default Windows DPI setting, not High DPI.
 +
 
 +
If you select 125% (120 DPI) the option "Use Windows XP style DPI scaling" is enabled, applications you run under this setting are scaled like at Windows XP.
 +
 
 +
If you select 150% (144 DPI) the option "Use Windows XP style DPI scaling" is disabled (DPI Virtualization enabled), applications you run under this setting must be High DPI Awareness else they will be scaled by the system like a blurred image.
 +
 
 +
Also you can set your custom DPI setting in the option "Set custom text size (DPI)" and enable/disable the DPI Virtualization.
 +
 
 +
== Example - DPI Aware Application (For Vista +) ==
  
 
[http://sourceforge.net/projects/cpicksniff2/ CPickSniff] is an application to capture screen colors. We will use it as an example to see how High DPI works in Windows.
 
[http://sourceforge.net/projects/cpicksniff2/ CPickSniff] is an application to capture screen colors. We will use it as an example to see how High DPI works in Windows.
  
=== Default DPI ===
+
'''Default DPI'''
  
 
This is the app running at 96 DPI (100%). It's the default mode, when scaling isn't necessary.
 
This is the app running at 96 DPI (100%). It's the default mode, when scaling isn't necessary.
Line 79: Line 80:
 
[[Image:cpicksniff_defaultdpi.png]]
 
[[Image:cpicksniff_defaultdpi.png]]
  
=== Windows DPI Scaling ===
+
'''Windows DPI Scaling'''
  
 
This is same app running at 144 DPI (150%) without manifest, so Windows scales it like a bitmap. The result is a blurred image.
 
This is same app running at 144 DPI (150%) without manifest, so Windows scales it like a bitmap. The result is a blurred image.
Line 85: Line 86:
 
[[Image:cpicksniff_blured.png]]
 
[[Image:cpicksniff_blured.png]]
  
=== With Manifest ===
+
'''With Manifest'''
  
 
Running at 144 DPI (150%). This time the app includes manifest but the application contains no code to handle scaling. Items aren't scaled whereas font are scaled (Windows does it automatically), so text is clipped.
 
Running at 144 DPI (150%). This time the app includes manifest but the application contains no code to handle scaling. Items aren't scaled whereas font are scaled (Windows does it automatically), so text is clipped.
Line 91: Line 92:
 
[[Image:cpicksniff_nohighdpi.png]]
 
[[Image:cpicksniff_nohighdpi.png]]
  
=== High DPI ===
+
'''High DPI'''
  
 
Finally with manifest and scaling handler, the app is in High DPI.
 
Finally with manifest and scaling handler, the app is in High DPI.
  
 
[[Image:cpicksniff_highdpi.png]]
 
[[Image:cpicksniff_highdpi.png]]
 
== High DPI in Lazarus ==
 
 
With Lazarus we have to follow 2 steps in order to make an High DPI Awareness application for Windows 7.
 
  
 
=== STEP 1 - Declare High DPI Awareness ===
 
=== STEP 1 - Declare High DPI Awareness ===
Line 185: Line 182:
 
end;</delphi>
 
end;</delphi>
  
=== Scale All Forms ===
+
'''Scale All Forms'''
  
You can change the step 2 to scale all forms.
+
You can resize all forms at once without having to go to the event on create.
  
 
In order to do this open your project source (typically the ''Project1''.lpr file) and add uScaleDPI in the uses clause.
 
In order to do this open your project source (typically the ''Project1''.lpr file) and add uScaleDPI in the uses clause.
Line 229: Line 226:
 
end.</delphi>
 
end.</delphi>
  
=== Advanced ===
+
'''Advanced'''
  
 
Some controls have more properties or different property names like TToolBar buttons (ButtonHeight / ButtonWidth instead Width / Height). Also if you use fixed font sizes the behavior can change in different os's.
 
Some controls have more properties or different property names like TToolBar buttons (ButtonHeight / ButtonWidth instead Width / Height). Also if you use fixed font sizes the behavior can change in different os's.
Line 239: Line 236:
 
But this is not the final High DPI unit, for example you can use under Windows different LCL widgets, like Qt and this can change the final result.
 
But this is not the final High DPI unit, for example you can use under Windows different LCL widgets, like Qt and this can change the final result.
  
<delphi>unit uscaledpi;
+
'''Link:''' [http://lazpaint.svn.sourceforge.net/viewvc/lazpaint/lazpaint/uscaledpi.pas?view=log uscaledpi.pas in LazPaint]
  
{$mode objfpc}{$H+}
+
== Example - Using AutoSize ==
  
interface
+
You can enable the 'AutoSize' option for each control you have. Then test it under the different DPI modes, with different 'skinning' themes (if available in your target OS) and different font sizes. It can be very usefull, some Lazarus HighDPI issues was solved using this technique.
 
 
uses
 
  Forms, Graphics, Controls, ComCtrls;
 
 
 
procedure HighDPI(FromDPI: Integer);
 
procedure ScaleDPI(Control: TControl; FromDPI: Integer);
 
procedure ScaleImageList(ImgList: TImageList; FromDPI: Integer);
 
 
 
implementation
 
 
 
uses BGRABitmap, BGRABitmapTypes;
 
 
 
procedure HighDPI(FromDPI: Integer);
 
var
 
  i: Integer;
 
begin
 
  for i:=0 to Screen.FormCount-1 do begin
 
    ScaleDPI(Screen.Forms[i],FromDPI);
 
  end;
 
end;
 
 
 
procedure ScaleImageList(ImgList: TImageList; FromDPI: Integer);
 
var
 
  TempBmp: TBitmap;
 
  TempBGRA: array of TBGRABitmap;
 
  NewWidth,NewHeight: integer;
 
  i: Integer;
 
 
 
begin
 
  if Screen.PixelsPerInch = FromDPI then exit;
 
 
 
  NewWidth := ScaleX(ImgList.Width,FromDPI);
 
  NewHeight := ScaleY(ImgList.Height,FromDPI);
 
 
 
  setlength(TempBGRA, ImgList.Count);
 
  TempBmp := TBitmap.Create;
 
  for i := 0 to ImgList.Count-1 do
 
  begin
 
    ImgList.GetBitmap(i,TempBmp);
 
    TempBGRA[i] := TBGRABitmap.Create(TempBmp);
 
    TempBGRA[i].ResampleFilter := rfBestQuality;
 
    if (TempBGRA[i].width=0) or (TempBGRA[i].height=0) then continue;
 
    while (TempBGRA[i].Width < NewWidth) or (TempBGRA[i].Height < NewHeight) do
 
      BGRAReplace(TempBGRA[i], TempBGRA[i].FilterSmartZoom3(moLowSmooth));
 
    BGRAReplace(TempBGRA[i], TempBGRA[i].Resample(NewWidth,NewHeight));
 
  end;
 
  TempBmp.Free;
 
 
 
  ImgList.Clear;
 
  ImgList.Width:= NewWidth;
 
  ImgList.Height:= NewHeight;
 
 
 
  for i := 0 to high(TempBGRA) do
 
  begin
 
    ImgList.Add(TempBGRA[i].Bitmap,nil);
 
    TempBGRA[i].Free;
 
  end;
 
end;
 
 
 
procedure ScaleDPI(Control: TControl; FromDPI: Integer);
 
var
 
  n: Integer;
 
  WinControl: TWinControl;
 
  ToolBarControl: TToolBar;
 
begin
 
  if Screen.PixelsPerInch = FromDPI then exit;
 
 
 
  with Control do begin
 
    Left:=ScaleX(Left,FromDPI);
 
    Top:=ScaleY(Top,FromDPI);
 
    Width:=ScaleX(Width,FromDPI);
 
    Height:=ScaleY(Height,FromDPI);
 
    {$IFDEF LCL Qt}
 
      Font.Size := 0;
 
    {$ELSE}
 
      Font.Height := ScaleY(Font.GetTextHeight('Hg'),FromDPI);
 
    {$ENDIF}
 
  end;
 
 
 
  if Control is TToolBar then begin
 
    ToolBarControl:=TToolBar(Control);
 
    with ToolBarControl do begin
 
      ButtonWidth:=ScaleX(ButtonWidth,FromDPI);
 
      ButtonHeight:=ScaleY(ButtonHeight,FromDPI);
 
    end;
 
  end;
 
 
 
  if Control is TWinControl then begin
 
    WinControl:=TWinControl(Control);
 
    if WinControl.ControlCount > 0 then begin
 
      for n:=0 to WinControl.ControlCount-1 do begin
 
        if WinControl.Controls[n] is TControl then begin
 
          ScaleDPI(WinControl.Controls[n],FromDPI);
 
        end;
 
      end;
 
    end;
 
  end;
 
end;
 
 
 
end.    </delphi>
 
 
 
== Conclusion ==
 
 
 
Making your application aware of DPI settings and themes includes two steps :
 
* Set a fixed font size at design time (Usefull for cross-platform applications, if your target is only Windows isn't neccesary)
 
* Scale components at run time
 
* Improve the ScaleDPI code to your needs
 
 
 
'''NOTE:'''
 
 
 
See Discussion page for more information: [[Talk:High_DPI]].
 
 
 
I'm NOT using ScaleBy & ScaleControls:
 
 
 
<delphi>Self.ScaleBy(Screen.PixelsPerInch,96);</delphi>
 
 
 
This scales the font, in Windows isn't neccesary because the OS changes font. May be usefull if you use fixed font size.
 
 
 
<delphi>Self.ScaleControls(Screen.PixelsPerInch,96);</delphi>
 
 
 
This doesn't scales form width / height. May be usefull if you scale those by yourself.
 
  
 
== External Links ==
 
== External Links ==
Line 370: Line 246:
 
*[http://msdn.microsoft.com/en-us/library/dd464646(v=VS.85).aspx High DPI (Windows)] MSDN article about High DPI
 
*[http://msdn.microsoft.com/en-us/library/dd464646(v=VS.85).aspx High DPI (Windows)] MSDN article about High DPI
 
*[[Windows Icon]] How to create icons that work with High DPI.
 
*[[Windows Icon]] How to create icons that work with High DPI.
 +
 
[[Category:Tutorials]]
 
[[Category:Tutorials]]

Revision as of 20:52, 21 September 2011

Deutsch (de) English (en) español (es) русский (ru)

Introduction

DPI (dot per inch) is the relation between size in pixels and the actual display size. Here dot is an equivalent for pixel in printing terminology. Applications can either use pixel sizes, or take into account that actual display size. In this second cases, sizes are given in points.

On Windows Vista and further, it is possible to change the DPI ratio to make elements bigger. High DPI means any custom DPI setting with more than 96 DPI (the default setting) *.

High DPI awareness means that an application takes this DPI setting into account.

Pixels and points

For example 300 DPI means that there are 300 pixels (or dot) per inch. There are 72 points per inch, so :

300 pixels ↔ 1 inch

300/72 pixels ↔ 1 point

4.16 pixels ↔ 1 point

Now with 96 DPI :

72 pixels ↔ 1 inch

1.33 pixel ↔ 1 point

Now with 144 DPI :

144 pixels ↔ 1 inch

2 pixels ↔ 1 point

Example - Fixed Font Sizes (not HighDPI)

Here is a form with an undefined font size (set to zero, which is the default value). Designed in 96 DPI (100%), it looks like this :

Testdpi100.png

Now, with 120 DPI (125%), it becomes :

Testdpi125.png

As you can see, the font gets bigger and clipped, the window title gets bigger, but the client area of the window keeps the same size. Note that these change of size can occur by using an application with different windows theme, or with another operating system.

To avoid this, set the font size to a non-zero value. Note that Font.Size is expressed in points and Font.Height is expressed in pixels. In fact, the value of Font.Height only is stored, and Font.Size changes according to current DPI value. So if we set the font size, it will be fixed to a certain size in pixels.

If we try again with a fixed font size of 9 points in 96 DPI (100%), we get this :

Testdpi100fixedM12P9.png

Now if the same program is run in 120 DPI (125%), it becomes :

Testdpi125fixedM12P9.png

The result is the almost the same. The title bar is bigger, but the client area and the font size is the same. Note that in fact, the size in points of the font has changed.

As a conclusion, it is possible to avoid inconsistence in the display by fixing font sizes. But we do not take into account that the graphical elements may be smaller according to actual DPI of the screen. With DPI awareness, it is possible to make application behave as if they new the real size of the pixels.

Setting High DPI under Windows

Windows Vista / Windows 7

In Windows 7 go to Control Panel > Appareance and Personalization > Display.

Select Smaller 100% (default), Medium 125% or Larger 150%. If you select 100% (96 DPI) is the default Windows DPI setting, not High DPI.

If you select 125% (120 DPI) the option "Use Windows XP style DPI scaling" is enabled, applications you run under this setting are scaled like at Windows XP.

If you select 150% (144 DPI) the option "Use Windows XP style DPI scaling" is disabled (DPI Virtualization enabled), applications you run under this setting must be High DPI Awareness else they will be scaled by the system like a blurred image.

Also you can set your custom DPI setting in the option "Set custom text size (DPI)" and enable/disable the DPI Virtualization.

Example - DPI Aware Application (For Vista +)

CPickSniff is an application to capture screen colors. We will use it as an example to see how High DPI works in Windows.

Default DPI

This is the app running at 96 DPI (100%). It's the default mode, when scaling isn't necessary.

cpicksniff defaultdpi.png

Windows DPI Scaling

This is same app running at 144 DPI (150%) without manifest, so Windows scales it like a bitmap. The result is a blurred image.

cpicksniff blured.png

With Manifest

Running at 144 DPI (150%). This time the app includes manifest but the application contains no code to handle scaling. Items aren't scaled whereas font are scaled (Windows does it automatically), so text is clipped.

cpicksniff nohighdpi.png

High DPI

Finally with manifest and scaling handler, the app is in High DPI.

cpicksniff highdpi.png

STEP 1 - Declare High DPI Awareness

To do this we need a manifest file that includes the declaration, with Lazarus 0.9.30 we can do this going to Options > Project Options > then select the options "Use Manifest to Enable Themes (Windows)" and "Dpi Aware application (for Vista +)".

STEP 2 - Scale Forms and Controls

To do this we can call ScaleDPI procedure OnCreate event of each form in your project.

First copy the below code and save to a text file "uscaledpi.pas":

<delphi>unit uscaledpi;

{$mode objfpc}{$H+}

interface

uses

 Forms, Graphics, Controls;

procedure HighDPI(FromDPI: integer); procedure ScaleDPI(Control: TControl; FromDPI: integer);

implementation

procedure HighDPI(FromDPI: integer); var

 i: integer;

begin

 if Screen.PixelsPerInch = FromDPI then
   exit;
 for i := 0 to Screen.FormCount - 1 do
   ScaleDPI(Screen.Forms[i], FromDPI);

end;

procedure ScaleDPI(Control: TControl; FromDPI: integer); var

 i: integer;
 WinControl: TWinControl;

begin

 if Screen.PixelsPerInch = FromDPI then
   exit;
 with Control do
 begin
   Left := ScaleX(Left, FromDPI);
   Top := ScaleY(Top, FromDPI);
   Width := ScaleX(Width, FromDPI);
   Height := ScaleY(Height, FromDPI);
 end;
 if Control is TWinControl then
 begin
   WinControl := TWinControl(Control);
   if WinControl.ControlCount = 0 then
     exit;
   for i := 0 to WinControl.ControlCount - 1 do
     ScaleDPI(WinControl.Controls[i], FromDPI);
 end;

end;

end.</delphi>

Copy the "uscaledpi.pas" file to the main folder of your project:

 MyProject\uscaledpi.pas
 

In the "uses" section of your project you need to add "uScaleDPI":

<delphi>unit form1;

{$mode objfpc}{$H+}

interface

uses

 Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
 uScaleDPI; // This includes ScaleDPI procedure </delphi>

The OnCreate event of each form calls the procedure in this way:

<delphi>procedure TForm1.FormCreate(Sender: TObject); begin

 ScaleDPI(Self,96); // 96 is the DPI you designed the Form1  

end;</delphi>

Scale All Forms

You can resize all forms at once without having to go to the event on create.

In order to do this open your project source (typically the Project1.lpr file) and add uScaleDPI in the uses clause.

Then call the procedure HighDPI below the code that initializes the forms:

<delphi>begin

 RequireDerivedFormResource := True;
 Application.Initialize;
 Application.CreateForm(TForm1, Form1);
 Application.CreateForm(TForm2, Form2);
 Application.CreateForm(TForm3, Form3);
 HighDPI(96);  // 96 is the DPI you designed the Form1, Form2 & Form3
 Application.Run;

end.</delphi>

The result looks like this:

<delphi>program Project1;

{$mode objfpc}{$H+}

uses

 {$IFDEF UNIX}{$IFDEF UseCThreads}
 cthreads,
 {$ENDIF}{$ENDIF}
 Interfaces, Forms,
 Unit1, Unit2, Unit3,
 uScaleDPI;

{$R *.res}

begin

 RequireDerivedFormResource := True;
 Application.Initialize;
 Application.CreateForm(TForm1, Form1);
 Application.CreateForm(TForm2, Form2);
 Application.CreateForm(TForm3, Form3);
 HighDPI(96);
 Application.Run;

end.</delphi>

Advanced

Some controls have more properties or different property names like TToolBar buttons (ButtonHeight / ButtonWidth instead Width / Height). Also if you use fixed font sizes the behavior can change in different os's.

You can edit the ScaleDPI procedure to include a code to scale all controls in the way you want.

This is the uscaledpi used in LazPaint. This is very usefull to scale ToolBars and ToolBox. Also LazPaint uses fixed fonts to be consistent in different os's.

But this is not the final High DPI unit, for example you can use under Windows different LCL widgets, like Qt and this can change the final result.

Link: uscaledpi.pas in LazPaint

Example - Using AutoSize

You can enable the 'AutoSize' option for each control you have. Then test it under the different DPI modes, with different 'skinning' themes (if available in your target OS) and different font sizes. It can be very usefull, some Lazarus HighDPI issues was solved using this technique.

External Links