DPI Aware Application

Share your advanced PureBasic knowledge/code with the community.
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: DPI Aware Application

Post by Lunasole »

Well after some test here are summary:

1st variant: it scales both font size and whole UI, so perfectly fits to any DPI setting. For now I didn't found any side-effects.
2nd variant: it doesn't scale UI and doesn't allow Windows to scale up fonts. So program UI is always fixed, which means if you made it too small, it will look bad on high DPI; if you made it too large, it will look bad on lower DPIs. Generally it is acceptable solution, it requires only to control used fonts sizes (which is done automatically by code posted previously).
3rd variant: without any scaling. Windows scales font size automatically but UI remains fixed, and your app looks like a crap. Or if no event manifest (DPIAware) set, Windows 8-10 scales whole UI, it becomes blured and looks even worst than crap.

So 1st variant should be perfect one, but it is complicated to finish it (need to add wrapper for every PB function which does something related with gadget/window width, height and probably x/y too. I've added most of such functions already, but there are still enough. However I think I'll continue with that, unless there is no such fix built-in at PB functions itself. Nice If someone like to participate too ^^
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
Roger Hågensen
User
User
Posts: 47
Joined: Wed Mar 25, 2015 1:06 pm
Location: Norway

Re: DPI Aware Application

Post by Roger Hågensen »

I'm hoping down the road that Fred and Freak will add DPI support to all GUI functions (it needs to be combined with a DPI aware manifest as well).


Your 2nd variant fights against the purpose of a high DPI rendering.

As an example think of a game rendered rather than in text/font sizes.


With a high DPI display you'd normally want everything rendered with more details/pixels, this gets rid of the need for antialiasing. With 5K (and larger) monitors you really do not need antialiasing anymore at "normal" viewing distances.


The user may also tell the OS (Windows) that they'd like larger/smaller text, and your application should honor that wish.
You may also want to provide a DPI override/customization option in your program in case the user want your GUI to appear larger or smaller than other apps.
4 music albums under CC BY license available for free (any use, even commercial) at Skuldwyrm.no
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: DPI Aware Application

Post by Thunder93 »

For the moment I don’t need glasses. I have 23″ Monitor, using 150% DPI. Running your examples I can barely see the text.

Like Roger Hågensen said, you should honour peoples choices. If you programming for personal use, do whatever.

Back in 2015 I’ve posted my modified version of Rescator code. Once again, here it is;

Code: Select all

;;;;; DPI-Aware Application ;;;;;
; Posted by Rescator on Sat Jan 02, 2010  -  http://forums.purebasic.com/english/viewtopic.php?f=12&t=40507
; Platforms: Windows Only

;Placed in the Public Domain by Roger Hågensen.
#PB_Compiler_Exe=#True ;This does not exist (yet?)

;http://msdn.microsoft.com/en-us/library/dd464660%28VS.85%29.aspx
Global.f _ScaleDPI_X_ = 1.0, _ScaleDPI_Y_ = 1.0, Font$, FontSize.b = 9
#DefaultDPIX = 96.0  ;Different platforms might have different default DPI, Windows is 96 DPI.
#DefaultDPIY = 96.0
Procedure InitScaleDPI() ;Windows 5.0 or higher needed for minimum functionality of this procedure.
  Protected.i dc, lpx, lpy, IsUser32DLL, *SetProcessDPIAware, *IsProcessDPIAware, DPIAware.l = #False , ncm.NONCLIENTMETRICS, Font;, Font$, FontSize.b = 9
  
  ;This part is Windows 6.x+ only (Vista etc.) and must be done before we use devcaps.
  ;http://msdn.microsoft.com/en-us/library/dd464660%28VS.85%29.aspx#declaring_dpi_awareness
  ;You really should use the DPI aware manifest instead of SetProcessDPIAware() when possible.
  ;On Windows 2000 and XP the manifest has no effect and set dpi aware is not available,
  ;however Devicecaps still returns usefull info that can be used.
  ;Note! If the dpi aware manifest is missing on Vista and Win7 then the OS will lie on devicecaps and will autoscale the entire app window.
  CompilerIf #PB_Compiler_Exe ;Only use this in exes, as DLLs inherit DPI from the calling process.
                              ;If the exe or the calling exe in case of this being a DLLs is allready dpi aware (like through a manifest),
                              ;then we skip using the the set dpi aware function, a DLLs should never use the set function, but it should check if the process id dpi aware
                              ;and apply the proper modifiers where appropriate obviously.
    
    IsUser32DLL = OpenLibrary(#PB_Any, "user32.dll")
    If IsUser32DLL
      *IsProcessDPIAware = GetFunction(IsUser32DLL, "IsProcessDPIAware")
      If *IsProcessDPIAware
        DPIAware = CallFunctionFast(*IsProcessDPIAware)
      EndIf
      If Not DPIAware
        *SetProcessDPIAware = GetFunction(IsUser32DLL, "SetProcessDPIAware")
        If *SetProcessDPIAware
          CallFunctionFast(*SetProcessDPIAware)
        EndIf
      EndIf
    EndIf
  CompilerEndIf
  dc = GetDC_(#Null)
  If dc
    lpx = GetDeviceCaps_(dc, #LOGPIXELSX)
    lpy = GetDeviceCaps_(dc, #LOGPIXELSY)

    If lpx>0
      _ScaleDPI_X_ = lpx/#DefaultDPIX
    EndIf
    If lpy>0
      _ScaleDPI_Y_ = lpy/#DefaultDPIY
    EndIf
    ReleaseDC_(#Null, dc)
  EndIf
  
    ;Get the system font for message boxes etc.
    ;We default to a size of 9, which is also the Vista and Win7 default size.
    ;The OS will automatically (Vista and Win7 at least) scale the font per the current user's DPI setting.
    ncm\cbSize = SizeOf(NONCLIENTMETRICS)
    If SystemParametersInfo_(#SPI_GETNONCLIENTMETRICS, SizeOf(NONCLIENTMETRICS), ncm, #Null)
      Font$ = PeekS(@ncm\lfMessageFont\lfFaceName)
      If OSVersion() < #PB_OS_Windows_Vista : FontSize = 8 : EndIf
      FontSize = IntQ(FontSize*_ScaleDPI_Y_) ;: Debug FontSize
;       scaleFactorY = MulDiv_(lpy, 100, 96) ; Actual Scale factor (g.e. 150%)      
      Font = LoadFont(#PB_Any, Font$, FontSize, #PB_Font_HighQuality)
      If Font
        SetGadgetFont(#PB_Default, FontID(Font))
      EndIf
    EndIf
EndProcedure : InitScaleDPI()
Macro Dx(x) : (x)*_ScaleDPI_X_ : EndMacro
Macro Dy(y) : (y)*_ScaleDPI_Y_ : EndMacro

Macro pDx(x) : Int((x)/_ScaleDPI_X_) : EndMacro
Macro pDy(y) : Int((y)/_ScaleDPI_Y_) : EndMacro
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;-> Procedures for Macros associates 
Procedure _OpenWindow(WindowID.l, x.l, y.l, Width.l, Height.l, Title$, Flags.l, ParentWinID.l)
  ProcedureReturn OpenWindow(WindowID, x, y, Width, Height, Title$, Flags, ParentWinID)
EndProcedure

Procedure _StringGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
  ProcedureReturn StringGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure

Procedure _ButtonGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
  ProcedureReturn ButtonGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure

Procedure _FrameGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
  ProcedureReturn FrameGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure

Procedure _PanelGadget(GadgetID.l, x.l, y.l, Width.l, Height.l)
  ProcedureReturn PanelGadget(GadgetID, x, y, Width, Height)
EndProcedure

Procedure _TrackBarGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Minimum, Maximum, Flags)
  ProcedureReturn TrackBarGadget(GadgetID, x, y, Width, Height, Minimum, Maximum, Flags)
EndProcedure

Procedure _CheckBoxGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
  ProcedureReturn CheckBoxGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure

Procedure _ComboBoxGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Flags.l)
  ProcedureReturn ComboBoxGadget(GadgetID, x, y, Width, Height, Flags)
EndProcedure

Procedure _OptionGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$)
  ProcedureReturn OptionGadget(GadgetID, x, y, Width, Height, Text$)
EndProcedure

Procedure _TextGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
  ProcedureReturn TextGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure

Procedure _ListIconGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Title$, TitleWidth.l, Flags.l)
  ProcedureReturn ListIconGadget(GadgetID, x, y, Width, Height, Title$, TitleWidth, Flags)
EndProcedure

Procedure _ListViewGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Flags.l)
  ProcedureReturn ListViewGadget(GadgetID, x, y, Width, Height, Flags)
EndProcedure

Procedure _EditorGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Flags.l)
  ProcedureReturn EditorGadget(GadgetID, x, y, Width, Height, Flags)
EndProcedure


; Procedure _WindowBound(WindowID.l, MinimumWidth.l, MinimumHeight.l, MaximumWidth.l, MaximumHeight.l)
;   ProcedureReturn WindowBounds(WindowID, MinimumWidth, MinimumHeight, MaximumWidth, MaximumHeight)  
; EndProcedure
  


;-> Macros for Procedures associates
Macro OpenWindow(WindowID, x, y, Width, Height, Title, Flags = #PB_Window_SystemMenu, ParentWinID = 0)
  _OpenWindow(WindowID, Dx(x), Dy(y), Dx(Width), Dy(Height), Title, Flags, ParentWinID)
EndMacro

Macro StringGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
  _StringGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro

Macro ButtonGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
  _ButtonGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro

Macro FrameGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
  _FrameGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro

Macro PanelGadget(GadgetID, x, y, Width, Height)
  _PanelGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height))
EndMacro

Macro TrackBarGadget(GadgetID, x, y, Width, Height, Minimum, Maximum, Flags = 0)
  _TrackBarGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Minimum, Maximum, Flags)
EndMacro

Macro CheckBoxGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
  _CheckBoxGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro

Macro ComboBoxGadget(GadgetID, x, y, Width, Height, Flags = 0)
  _ComboBoxGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Flags)
EndMacro

Macro OptionGadget(GadgetID, x, y, Width, Height, Text)
  _OptionGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text)
EndMacro

Macro TextGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
  _TextGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro

Macro ListIconGadget(GadgetID, x, y, Width, Height, Title, TitleWidth, Flags = 0)
  _ListIconGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Title, Dx(TitleWidth), Flags)
EndMacro

Macro ListViewGadget(GadgetID, x, y, Width, Height, Flags = 0)
  _ListViewGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Flags)
EndMacro

Macro EditorGadget(GadgetID, x, y, Width, Height, Flags)
  _EditorGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Flags)
EndMacro


; Macro WindowBound(WindowID, MinimumWidth, MinimumHeight, MaximumWidth, MaximumHeight)
;   _WindowBound(WindowID, Dx(MinimumWidth), Dy(MinimumHeight), Dx(MaximumWidth), Dy(MaximumHeight))
; EndMacro




CompilerIf #PB_Compiler_IsMainFile
  ;The Gadget example from PureBasic manual.
  #WindowWidth  = 390
  #WindowHeight = 350
  
  If OpenWindow(0, 100, 200, #WindowWidth, #WindowHeight, "PureBasic - Gadget Demonstration", #PB_Window_MinimizeGadget)
    Debug WindowWidth(0)
    Debug pDx(WindowWidth(0))
    
    Debug WindowHeight(0)
    Debug pDx(WindowHeight(0))
    
    Top = 10
    GadgetHeight = 24
    
    FrameGadget(#PB_Any, 10, Top, 370, 290, "Player...") : Top+25
    
    StringGadget(0, 20, Top, 200, GadgetHeight, "")
    ButtonGadget(1, 223, Top,  72, GadgetHeight, "Play")
    ButtonGadget(2, 295, Top,  72, GadgetHeight, "Stop")  : Top+35
    DisableGadget(2,1)
    
    GadgetToolTip(1,"Play the current song")
    
    PanelGadget(3, 20, Top, #WindowWidth-50, #WindowHeight-Top-55)
    AddGadgetItem(3, 0, "MP3 PlayList")
    ListViewGadget(4, 6, 10, 230, 148)
    
    For k=0 To 30
      AddGadgetItem(4, -1, "Music Song n° "+Str(k))
    Next
    
    ButtonGadget(5,  250, 10, 80, GadgetHeight, "Add")
    ButtonGadget(6,  250, 38, 80, GadgetHeight, "Remove")
    ButtonGadget(7,  250, 66, 80, GadgetHeight, "Select")
    GadgetToolTip(7, "Select the current song")
    
    TrackBarGadget(17, 10, pDx(GadgetHeight(4))+10, 310, 30, 0, 100)
    
    AddGadgetItem(3, 1, "Options")
    Top = 10
    CheckBoxGadget(10, 10, Top, 250, GadgetHeight, "Enable low-pass filter") : Top+30
    CheckBoxGadget(11, 10, Top, 250, GadgetHeight, "Enable visual plug-in")  : Top+30
    ComboBoxGadget(12, 10, Top, 250, 30) : Top+30
    AddGadgetItem(12, -1, "FireWorks")
    AddGadgetItem(12, -1, "OpenGL spectrum")
    AddGadgetItem(12, -1, "Bump bass")
    SetGadgetState(12,0)
    DisableGadget(12,1)
    
    OptionGadget(13, 10, Top, 81, GadgetHeight, "640*480") : Top+20
    OptionGadget(14, 10, Top, 81, GadgetHeight, "800*600") : Top+20
    OptionGadget(15, 10, Top, 81, GadgetHeight, "1024*768")
    SetGadgetState(13, 1)
    
    ButtonGadget(16, 150, Top, 80, GadgetHeight, "Info")
    CloseGadgetList()
    
    TextGadget  (9, 10, #WindowHeight-30, 250, 24, "PureBasic - Gadget demonstration")
    ButtonGadget(8, #WindowWidth-100, #WindowHeight-36, 80, 24, "Quit")
    
    SetGadgetState(3, 0)
    
    Repeat
      EventID = WaitWindowEvent()
      
      If EventID = #PB_Event_Gadget
        
        Select EventGadget()
          Case 0
            If EventType() = #PB_EventType_ReturnKey
              MessageRequester("Info", "Return key pressed", 0)
              SetActiveGadget(0)
            EndIf
            
          Case 1 ; Play
            DisableGadget(2,0)  ; Enable the 'Stop' gadget
            DisableGadget(1,1)  ; Disable the 'Play' Gadget
            
          Case 2 ; Stop
            DisableGadget(1,0)  ; Enable the 'Play' gadget
            DisableGadget(2,1)  ; Disable the 'Stop' Gadget
            
          Case 4
            If EventType() = 2
              SetGadgetText(0, GetGadgetText(4)) ; Get the current item from the ListView..
            EndIf
            
          Case 5 ; Add
            AddGadgetItem(4, -1, "New Item Added...")
            
          Case 6 ; Remove
            RemoveGadgetItem(4, GetGadgetState(4)) ; Remove the current element of the ListView
            
          Case 7 ; Select
            SetGadgetText(0, GetGadgetText(4)) ; Get the current item from the ListView..
            
          Case 8 ; Quit...
            EventID = #PB_Event_CloseWindow
            
          Case 11 ; Enable PlugIn..
            DisableGadget(12, 1-GetGadgetState(11))
            
          Case 16
            If GetGadgetState(13) : Result$ = GetGadgetText(13) : EndIf
            If GetGadgetState(14) : Result$ = GetGadgetText(14) : EndIf
            If GetGadgetState(15) : Result$ = GetGadgetText(15) : EndIf
            
            MessageRequester("Info", "Selected screen mode: "+Result$, 0)
            
          Case 17
            SetGadgetText(0, Str(GetGadgetState(17)))
            
        EndSelect
        
      EndIf
      
    Until EventID = #PB_Event_CloseWindow  
  EndIf
  End
CompilerEndIf
100% DPI
Image

150% DPI
Image
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: DPI Aware Application

Post by Lunasole »

There is fatal flaw in first example in situations like following.

Code: Select all

; imagine you are toggling UI height to hide part of window or show it

If Not OptionWindowIsLarge
	; here you resizing gui height to absolute value
	; asume this value is actual for 100% scaling (96 DPI)
	; ResizeWindow is replaced by macro, so it scales 290 up and result is OK
	ResizeWindow(WndMain, #PB_Ignore, #PB_Ignore, #PB_Ignore, 290) 
Else 
	; and here you resizing gui height to relative value
	; this value received from some gadget Y coordinate
	; the result is shit, because ResizeWindow is replaced by macro
	;	and scales up all passed params
	; and gadget Y was already scaled on gadget creation
	; you need to pass here Y coordinate without applied macro to it for result to be OK
	ResizeWindow(WndMain, #PB_Ignore, #PB_Ignore, #PB_Ignore, GadgetY(SomeGadget)) 
EndIf
The only solution of this seems to be usage of scaling macros manually everytime (or append extra switches to a wrappers).
This is painful and error-full way, leading you to dirty and illogical code and not perfect result (there is scaling problem caused by non-integral values rounding, @Roger Hågensen mentioned it in other thread too. this problem cannot be resolved while calculating pixels [not twips, etc], and will not let you to make pixel-perfect fancy compact guis with controls are placed side to side, etc. btw past year I've encountered same problem while doing smooth scaling in 2D game).


Thus second variants wins :) At least for now. I hope it will not show similar "fatal flaws".

To Roger Hågensen: the purpose of fixed UI is that you setting DPI for it at design stage. So you have statistics by average user display sizes, use this info when designing your program and that should give enough honour to what people like to see. And of course it can be designed specially for 5k displays as well.

That's how apps were made before and I thing this is right way, unless there are no acceptable way to design true DPI-aware GUIs without wasting undue efforts and getting huge code-related problems from it.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: DPI Aware Application

Post by Lunasole »

Thus second variants wins :) At least for now. I hope it will not show similar "fatal flaws".
Side-effects I've noticed already:
- window title font is not remaining fixed
- same with window buttons, scrollbars and probably several other controls (which font is not controled by application, or which images have different versions for different DPIs in windows theme file).

So the program doesn't looks fine anyway with 125% / 150% scaling (and will look much worst with larger).
Here is example for 150%.
http://s000.tinyupload.com/index.php?fi ... 1890217672

However, that seems to be most acceptable solution for now ^^
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
Roger Hågensen
User
User
Posts: 47
Joined: Wed Mar 25, 2015 1:06 pm
Location: Norway

Re: DPI Aware Application

Post by Roger Hågensen »

Lunasole wrote:...(there is scaling problem caused by non-integral values rounding, @Roger Hågensen mentioned it in other thread too. this problem cannot be resolved while calculating pixels [not twips, etc], and will not let you to make pixel-perfect fancy compact guis with controls are placed side to side, etc. btw past year I've encountered same problem while doing smooth scaling in 2D game).
Lunasole wrote:To Roger Hågensen: the purpose of fixed UI is that you setting DPI for it at design stage. So you have statistics by average user display sizes, use this info when designing your program and that should give enough honour to what people like to see. And of course it can be designed specially for 5k displays as well.
Games and such (particularly fullscreen ones) have the luxury of scaling. Due to the many different monitor/screen (and window sizes) they always are designed for scalability, and air for pixel rounding is included. Some games over the years (more recently?) have begun to provide user options in the game menus where you can scale the HUD/UI of the game to smaller or larger.
I'm considering doing that for my current and future projects, so along with proper HiDPI support I'll add a scaling option too. This would be a sort of per program DPI setting, defaulting to the system/monitor DPI but letting the user tweak it. I can even add a toggle option "Reset to default if system settings change" as well.

As to pixel rounding issues. You normally have some air between buttons and other interactive elements to avoid the user accidentally clicking the wrong thing. Artwork and decoration can be handled by simply using alpha transparency, and if artwork has to be pixel perfect joined together then simply join then before drawing the artwork on the GUI.
Lunasole wrote:That's how apps were made before and I thing this is right way, unless there are no acceptable way to design true DPI-aware GUIs without wasting undue efforts and getting huge code-related problems from it.
Pixel rounding errors will always have to be designed for/around by the coder/designer. The rest PureBasic could (and should?) handle "behind the scenes".
Lunasole wrote:Side-effects I've noticed already:
- window title font is not remaining fixed
- same with window buttons, scrollbars and probably several other controls (which font is not controled by application, or which images have different versions for different DPIs in windows theme file).
A user may choose a different font size independent of the DPI. Different languages have different length sentences (which brings a whole other headache intro design).
Some programs use a mix of custom and system buttons and other elements.
Most Windows system GUI stuff "should" be fully Windows 8+ DPI compliant, but there are still a lot of stuff in Windows 10 even that does not handle higher DPI properly.

As higher resolution monitors appear the DPI will keep crawling upwards. Mobiles have passed the "300 DPI is enough" magic line quite some time ago. For a long time monitors was 72PPI (Mac) or 96PPI (Windows). The web now is largely 96PPI and all modern browsers assumes this and will transform this into the OS/display DPI setting, but they also has a scale slider that users can change.

A ~20inch 5K monitor at around arms length is around 300 DPI to your eyes. Larger monitors are usually placed further away. I can't recall what a 8K monitor hung on the other wall in a typical living room would be (with the couch in the middle of the room) but I'm guessing 8K might be a tad overkill if you want to just hit that 300 DPI.
Beyond 300 DPI relatively speaking you are not gaining much in detail. At 300 DPI (or PPI) you no longer need anti-aliasing as the average person can no longer distinguish individual pixels, so "crawling lines" is no longer an issue.

VR is the other way, a screen for VR is closer than arms length, closer than you'd hold a mobile phone (where the 300 DPI thing originated from, thanks Steve Jobs :) initially) so you need a higher pixel density on the display (and hence a higher DPI slash PPI. VR may need 8K since the display is so close to your face/eyes.

(K is not really in the mainstream market yet. I can't recall seeing 7K nor even 6K. I have seen 5K mentioned here and there. 4K is now the current mainstream HiDPI for monitors.
One advantaged of 5K+ though is that you could lean really close to a screen to see more detail, but you can damage your eyesight in the long run by doing that (what is the point of HiDPI then?), a zoom is much more useful instead.
Other issues are lack of insanely high res content, and how much GPU power you need to render things at a acceptable minimum frame rate (60FPS?) beyond 2K that the average (majority) can afford.

But I'm getting sidetracked. Back to topic. higher DPI is here to stay. Windows 8 (or just 8.1?) and later allows different DPI settings per monitor which I consider groundbreaking, but also damn annoying as a programmer.
You have to design for the possibility that the user drags a window from a low DPI to a high DPI monitor, but once you got the code in you recycle that for all your projects.
4 music albums under CC BY license available for free (any use, even commercial) at Skuldwyrm.no
Post Reply