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)也是类似的:
QRect destinationRect = ...QPixmap pixmap = ...painter.drawPixmap(destinationRect, pixmap);
为了避免在高DPI显示器上出现缩放失真,像素映射必须包含足够的像素:两倍于destinationRect的宽和高。应用程序可以直接提供它们,也可以使用QIcon来管理不同的解析度:
QRect destinationRect = ...QIcon icon = ...painter.drawPixmap(destinationRect, icon.pixmap(destinationRect.size()));
QIcon::pixmap()已经被修改了,可以在高DPI系统中返回一个更大的像素映射。这种行为的改变会破坏现有的代码,所以它是由AA_UseHighDpiPixmaps这个应用程序属性来控制的:
qApp->setAttribute(Qt::AA_UseHighDpiPixmaps);
在Qt 5.1中这个属性默认值是关闭的,但在未来的Qt发布中它很有可能默认为打开。
Qt的窗口部件有一些极端情况。在理想情况下,它一直使用QIcon,并且在绘制的时候会使用正确的像素映射,但是实际情况是Qt API经常直接生成和使用像素映射。当像素映射的大小被用来计算布局的几何信息时,会发生错误——如果一个像素映射已经是高分辨率的,那么在屏幕上它就不应该再占用更多的空间。
通过使用QPixmap::devicePixelRatio(),就能让200x200的像素映射实际占据100x100的设备无关像素。由QIcon::pixmap()返回的像素映射中devicePixelRatio已经设置好了。
例如QLabel就是一个“像素映射消费者”:
QPixmap pixmap2x = ...pixmap2x.setDevicePixelRatio(2.0); QLabel *label = ...label->setPixmap(pixmap2x);
然后QLabel会除以devicePixelRatio来获得布局的大小:
QSize layoutSize = pixmap.size() / pixmap.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”这种图像文件名的方案:
foo.pngfoo@2x.png
这样高解析度的内容和原来的一一对应。在需要的时候,“@2x”的版本会被QML的Image元素以及QIcon自动加载。
Image { source = “foo.png” } QIcon icon(“foo.png”)
(对于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.
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。