DPI , dot per inch ,即每英寸包含的點數。還有一個概念是 PPI ,即每英寸包含的像素數。一般我們用 DPI 就夠了,對于專業人士處理超高 DPI 的場景,使用 PPI 可能更精确一些。在 Qt 中,隻有 DPI ,是以我們單說它吧。
這個值越大,像素密度越大,小尺寸的螢幕就可以有大分辨率。比如有的 Android 手機, 3.7 吋螢幕就能提供 960x540 的分辨率,而有的手機, 5 吋螢幕卻提供 800x480 的分辨率。這兩種不同螢幕的尺寸和分辨率的手機,5 吋屏看起來會有顆粒感,而 3.7 吋看起來則非常細膩。這就是像素密度帶來的差别。
有的螢幕,橫向的 DPI 和縱向的 DPI 不一樣,即像素點不是正方形,這就更複雜了……
我們在寫應用時,理想的情況是:應當根據 DPI + 螢幕分辨率來設定界面元素的大小。
Mac OS 10.8(10.7是非正式的?)添加了對高DPI的Retina顯示的支援。Qt 4免費獲得這一支援,因為它使用的是CoreGraphics繪制引擎。
Qt 5使用的是光栅繪制引擎并且Qt通過縮放繪圖器變換(transform)實作了高DPI矢量的繪制。HITheme同時為Qt 4和5提供了高DPI的Mac風格。在Qt 5的Fusion風格中,對于高DPI模式的支援也已經修改好了。
OpenGL是一種基于裝置像素的API并且對于高DPI模式也仍然如此。在NSView中有一個flag可以用來開啟或者禁用2x縮放——Qt在所有情況下都可以設定它。Shaders運作在裝置像素中。
Qt Quick 1是建構于QGraphicsView之上的,它是一個QWidget并且通過QPainter獲得對于高DPI的支援。
Qt Quick 2是基于Scene Graph(和OpenGL),已經更新了高DPI的支援。Qt Quick控件(也就是以前的Desktop Component)也已經更新了在高DPI模式下的渲染,其中包括距離場(distance field)文本渲染。(譯者注:關于距離場,可以參考Yoann Lopes – Text Rendering in the QML Scene Graph以及iKDE上的譯文。)
這裡的賣點是應用程式開發人員不需要關心這些,您隻需要在裝置無關像素的空間裡舒适地開發,Qt和/或OS會為您處理那些複雜的事情。但有一個例外,光栅内容(raster content)——需要提供高DPI光栅内容,并且應用程式代碼需要正确處理這些内容。
QPainter代碼絕大多數情況下都和原來一樣。我們來看看繪制漸變(gradient)的代碼:
在高DPI顯示器上,這個漸變在螢幕上的大小還是一樣的,但是被填充了更多的(裝置)像素。
繪制一個像素映射(pixmap)也是類似的:
為了避免在高DPI顯示器上出現縮放失真,像素映射必須包含足夠的像素:兩倍于destinationRect的寬和高。應用程式可以直接提供它們,也可以使用QIcon來管理不同的解析度:
QIcon::pixmap()已經被修改了,可以在高DPI系統中傳回一個更大的像素映射。這種行為的改變會破壞現有的代碼,是以它是由AA_UseHighDpiPixmaps這個應用程式屬性來控制的:
在Qt 5.1中這個屬性預設值是關閉的,但在未來的Qt釋出中它很有可能預設為打開。
Qt的視窗部件有一些極端情況。在理想情況下,它一直使用QIcon,并且在繪制的時候會使用正确的像素映射,但是實際情況是Qt API經常直接生成和使用像素映射。當像素映射的大小被用來計算布局的幾何資訊時,會發生錯誤——如果一個像素映射已經是高分辨率的,那麼在螢幕上它就不應該再占用更多的空間。
通過使用QPixmap::devicePixelRatio(),就能讓200x200的像素映射實際占據100x100的裝置無關像素。由QIcon::pixmap()傳回的像素映射中devicePixelRatio已經設定好了。
例如QLabel就是一個“像素映射消費者”:
然後QLabel會除以devicePixelRatio來獲得布局的大小:
與此類似的幾種情況在Qt中都已經修複,并且應用程式代碼在啟用AA_UseHighDpiPixmaps之前也需要做類似處理。
下面幾個Qt類中都提供了devicePixelRatio()的讀寫函數:
類
注釋
QWindow::devicePixelRatio()
推薦使用的讀寫函數
QScreen::devicePixelRatio()
QGuiApplication::devicePixelRatio()
如果沒有QWindow指針,請使用這個
QImage::[set]devicePixelRatio()
QPixmap::[set]devicePixelRatio()
字型大小還可以和原來一樣,會在高DPI顯示中産生類似的大小(但會有一點小問題)。字型的像素大小是裝置無關的像素大小。您在高DPI顯示中永遠不會得到太小的文本。
OpenGL是在裝置像素空間中工作的。例如,傳遞給glViewport的寬和高應該是裝置像素。QGLWidget::resizeGL()中的寬和高也是裝置像素的。
不管怎樣,QGLWidget::width()實際上就是QWidget::width(),它傳回的是裝置無關像素的值。如果需要,用它來乘以widget->windowHandle()->devicePixelRatio()可以解決很多問題。
Qt Quick 2和Qt Quick控件可以直接使用。因為視窗部件的坐标是裝置無關像素的。Qt Quick也有幾個和光栅相關的極端情況,因為QML的Image元素是通過URL來指定圖像源的,這樣就避免了像素映射的傳遞。
Qt Quick控件
還有一個例外是OpenGL着色器(shader),它運作在裝置像素空間中并且可以看到全分辨率。在通常情況下這沒有什麼問題,我們應該知道的一件重要的事情是,滑鼠坐标是裝置無關像素的,也許需要被轉換成裝置像素。
運作中的着色器效果執行個體
正如我們所看到的,在縮放的情況下,光栅内容看起來會不夠好,是以必須提供高解析度的内容。作為應用程式開發人員,您有兩個選項:(請忽略“什麼都不做”選項)
使用高解析度版本替換現有光栅内容
另外提供一份高解析度内容
第一個選項很簡單,因為每個資源隻有一個版本。可是您也許會發現(或者您的設計師會告訴您)像圖示這樣的資源隻有在它被建立的那個特定解析度下看起來才最好。為了解決這個問題,Qt沿用了“@2x”這種圖像檔案名的方案:
這樣高解析度的内容和原來的一一對應。在需要的時候,“@2x”的版本會被QML的Image元素以及QIcon自動加載。
(對于QIcon請記住使用AA_UseHighDpiPixmaps)
QPA允許我們相對容易的完成跨平台的實作。Qt現在把這一問題分為三層:
應用程式層(應用程式代碼和使用QPA類的Qt代碼)
QPA層(QWindow、QScreen、QBackingStore)
平台插件層(QPlatform*子類)
簡化一下,應用程式層是在裝置無關像素空間中工作的,并不知道裝置像素。平台插件是在裝置像素空間中工作的,并不知道裝置無關像素。QPA層在兩者之間,基于一個由環境變量QT_HIGHDPI_SCALE_FACTOR指定的縮放因子進行轉換。
實際上,這個情況還會更複雜一些,各層之間會有洩露的事情發生,并且在Mac和iOS下還會有一些例外情況。
代碼在github上。最後是XCB下的Qt Creator的截屏:
DPI縮放的Qt Creator
QT_HIGDPI_SCALE_FACTOR=2縮放的Qt Creator
High DPI Revisited
A few weeks ago I talked about high DPI in KDE applications
By settings QT_DEVICE_PIXEL_RATIO=2 Qt will scale all painting to draw things twice the size
By default this will just scale all our p_w_picpaths so it slightly defeats the point of buying a fancy new high resolution screen
Qt can try and be clever and draw high resolution icons and other p_w_picpaths
This is liable to break stuff, so requires each and every app to opt-in
On Monday this week I was loaned a high resolution laptop, and set about trying to make sure everything works perfectly within the KDE world.We can now set this environment variable from a configuration file, and a user interface is in review to allow the user to manually set their scaling factor.
I then set about trying to enable high resolution p_w_picpath support in various applications and trying to fix all that is broken.
This task is two-fold. The first is fixing any bugs that result in simply enabling the high resolution icons. Second is making sure applications that provide their own p_w_picpaths, do so in a way that still look spot on when used on a high resolution monitor.
Here is my screenshot just after installing Kubuntu CI on a high resolution laptop (3800x1800).

We can correct some parts by just boosting the font size, but that doesn't solve the problems of small checkboxes, buttons and other hit areas. This isn't just a superficial problem and it becomes a major usability problem especially as these screens become more widespread.
This second screenshot shows the result with the device pixel ratio set and a weeks worth of fixing in a range of apps. (click for full size)
The most obvious thing is that the sizes are bigger, but more importantly this is happening whilst all icons and scrollbars remain crystal clear at the native resolution for that screen.
A zoomed in section looks like this:
Every Qt5 app can double up with no work at all, but to look right requires some effort.
For some applications supporting high DPI has been easy. It is a single one line in KWrite, and suddenly all icons look spot on with no regressions. For applications such as Dolphin which do a lot more graphical tasks, this has not been so trivial. There are a lot of p_w_picpaths involved, and a lot of complicated code around caching these which conflicts with the high resolution support without some further work.
I am tracking progress on a Kanboard page . Naturally I can't do every application, but I hope that by checking a few I can make sure all of our frameworks have full support making it easy for every other developer.
We also have a problem that Qt4 applications do not support device independent pixels. There are still many applications without a frameworks release even in the upcoming 15.04 applications release. Even in the next applications release in 15.08 August we are still unlikely to see a released PIM stack.Is it a good idea to add an option into our UIs that improves some applications at the cost of consistency? It's not an easy answer.