Difference between revisions of "High DPI"
Jwdietrich (talk | contribs) |
|||
Line 56: | Line 56: | ||
The conclusion from this is that it is possible to avoid inconsistency 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 an application behave as if it knew the real size of the pixels. | The conclusion from this is that it is possible to avoid inconsistency 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 an application behave as if it knew the real size of the pixels. | ||
− | == Setting High DPI | + | == Setting High DPI == |
+ | |||
+ | === Windows === | ||
'''Windows Vista / Windows 7''' | '''Windows Vista / Windows 7''' | ||
Line 69: | Line 71: | ||
You can also set your custom DPI setting via the option "Set custom text size (DPI)" and enable/disable the DPI Virtualization. | You can also set your custom DPI setting via the option "Set custom text size (DPI)" and enable/disable the DPI Virtualization. | ||
+ | |||
+ | === Linux === | ||
+ | |||
+ | On Linux DPI setting is more complicated and depends on used software and their version. | ||
+ | |||
+ | You can discover your current monitor DPI by command: | ||
+ | <syntaxhighlight lang="bash">xdpyinfo|grep dots </syntaxhighlight> | ||
+ | |||
+ | You can change DPI to new value by command: | ||
+ | <syntaxhighlight lang="bash">xrandr --dpi 144x144</syntaxhighlight> | ||
+ | |||
+ | To preserve setting after reboot you need to add the command as script to /etc/X11/Xsession.d/77set_dpi. | ||
+ | |||
+ | More information: | ||
+ | * [http://askubuntu.com/questions/197828/how-to-find-and-change-the-screen-dpi How to find and change the screen DPI?] | ||
== Example - DPI Aware Application (For Vista +) == | == Example - DPI Aware Application (For Vista +) == |
Revision as of 12:34, 3 December 2014
│
Deutsch (de) │
English (en) │
español (es) │
русский (ru) │
Introduction
DPI (dots 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 the actual display size. In this second case, sizes are given in points.
On Windows 95 and later, 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. This article was designed for Windows 7. For Windows 8 Metro Applications read this http://blogs.msdn.com/b/b8/archive/2012/03/21/scaling-to-different-screens.aspx
Pixels and points
For example 300 DPI means that there are 300 pixels (or dots) 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). It has been designed at 96 DPI (100%), and it looks like this :
Now, at 120 DPI (125%), it becomes :
As you can see, the font gets bigger and so the text is clipped. The window title gets bigger, but the client area of the window remains the same size. Note that these changes in size can occur by using an application with a different Windows theme, or with another operating system.
To avoid this, you must 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, only the value of Font.Height 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, then at 96 DPI (100%), we get this :
Now if the same program is run at 120 DPI (125%), it becomes :
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.
The conclusion from this is that it is possible to avoid inconsistency 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 an application behave as if it knew the real size of the pixels.
Setting High DPI
Windows
Windows Vista / Windows 7
In Windows 7 go to Control Panel > Appearance and Personalization > Display (or just Control Panel > Display in recent updates).
Select Smaller 100% (default), Medium 125% or Larger 150%. If you select 100% (96 DPI) this is the default Windows DPI setting, (High DPI is not the default).
If you select 125% (120 DPI) the option "Use Windows XP style DPI scaling" is enabled. Applications you run under this setting are scaled as if running under Windows XP.
If you select 150% (144 DPI) the option "Use Windows XP style DPI scaling" is disabled (DPI Virtualization is enabled), and applications you run under this setting must be High DPI Awareness to prevent system scaling which will produce a blurred image.
You can also set your custom DPI setting via the option "Set custom text size (DPI)" and enable/disable the DPI Virtualization.
Linux
On Linux DPI setting is more complicated and depends on used software and their version.
You can discover your current monitor DPI by command:
xdpyinfo|grep dots
You can change DPI to new value by command:
xrandr --dpi 144x144
To preserve setting after reboot you need to add the command as script to /etc/X11/Xsession.d/77set_dpi.
More information:
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.
Windows DPI Scaling
This is same app running at 144 DPI (150%) without a manifest, so Windows scales it like a bitmap. The result is a blurred image.
With Manifest
Running at 144 DPI (150%). This time the app includes a manifest but the application contains no code to handle scaling. Items aren't scaled whereas fonts are scaled (Windows does this automatically), so text is clipped.
High DPI
Finally with both a manifest and a coded scaling handler, the app is in High DPI.
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 by going to Options > Project Options > then selecting 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 in the OnCreate event of each form in your project.
First copy the code below and save it to a text file "uscaledpi.pas":
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.
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":
unit form1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
uScaleDPI; // This includes ScaleDPI procedure
The OnCreate event of each form calls the procedure in this way:
procedure TForm1.FormCreate(Sender: TObject);
begin
ScaleDPI(Self,96); // 96 is the DPI you designed the Form1
end;
Scale All Forms
You can resize all forms at once without having to touch each form's OnCreate event. 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:
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.
The result looks like this:
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.
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 behaviour can change in different OSs.
You can edit the ScaleDPI procedure to include code to scale all controls in the way you want.
This is the uscaledpi used in LazPaint. This is very useful for scaling ToolBars and ToolBox.
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 (including Forms). Then test the effect under the different DPI modes, with different 'skinning' themes (if available in your target OS) and different font sizes. This can be very useful technique, and has been employed to solve several Lazarus IDE HighDPI issues.
For example using the default AutoSize and ChildSizing most controls can be automatically sized and positioned. But the spacing must be scaled:
with WinControl.ChildSizing do
begin
HorizontalSpacing := ScaleX(HorizontalSpacing, FromDPI);
LeftRightSpacing := ScaleX(LeftRightSpacing, FromDPI);
TopBottomSpacing := ScaleY(TopBottomSpacing, FromDPI);
VerticalSpacing := ScaleY(VerticalSpacing, FromDPI);
end;
For more information see:
The uscaledpi.pas with this additional code:
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;
with WinControl.ChildSizing do
begin
HorizontalSpacing := ScaleX(HorizontalSpacing, FromDPI);
LeftRightSpacing := ScaleX(LeftRightSpacing, FromDPI);
TopBottomSpacing := ScaleY(TopBottomSpacing, FromDPI);
VerticalSpacing := ScaleY(VerticalSpacing, FromDPI);
end;
for i := 0 to WinControl.ControlCount - 1 do
ScaleDPI(WinControl.Controls[i], FromDPI);
end;
end;
end.
External Links
- High DPI (Windows) MSDN article about High DPI
- Guidelines for scaling Windows Dev Center - User experience guidelines for scaling Metro style apps
- Windows Icon How to create icons that work with High DPI.