Discussion:
[Interest] Widget to device pixel alignment under fractional DPI scaling
Alvin Wong
2018-10-17 10:02:13 UTC
Permalink
Dear all,

I have some questions on fractional DPI handling. I know fractional
DPI scaling isn't officially supported on Windows (waiting for
[QTBUG-53022][1] to be picked up again), but from what I hear KDE does
support actual fractional DPI scaling. Anyway, it is possible to test
fractional DPI on Windows with these environment variables (this
forces Qt to treat the first monitor as 1.5x scaling and the second
monitor as 1x), and does work kind of fine when it matches native
settings and using the Fusion style:

QT_AUTO_SCREEN_SCALE_FACTOR=0
QT_SCREEN_SCALE_FACTORS=1.5;1

I am trying to help get Krita to render the canvas in 1:1 device pixel
mapping. At this point it's certain that it can be made to work under
integer scaling (2x) but now I have some concerns on getting it to
work under fractional scaling (1.5x for example):

1. Does Qt align widgets positions to device pixels? In my
understanding, widget positions are specified in device-independent
pixels (I'll call it DIP for short), which can fall in between whole
device pixels. E.g. if a widget is at (31, 31) DIP, it would be placed
at (46.5, 46.5) px, which is at half pixels. Theoretically, painting a
0.6667 DIP line should result in a pixel-perfect 1px line, but will
this be broken due to the widget position not aligning to device
pixels?
2. Does Qt align widgets dimensions to device pixels? Like above, if a
widget has a width of 123 DIP, it would really be 184.5px. In this
case, and assuming that the left edge lies on a full device pixel,
what would happen to the right edge of the widget? And what if the
left edge is on half of a device pixel? Also, if an application wants
to create a pixel-perfect QBitmap (of devicePixelRatio=1, since the
aim is to paint in device pixels not DIP,) to be painted onto the
widget in full, what should its dimensions be?
3. On QOpenGLWidget: It creates the FBO in device pixels in
`QOpenGLWidgetPrivate::recreateFbo`, but with the code `const QSize
deviceSize = q->size() * q->devicePixelRatioF();` the FBO size is
implicitly truncated to integer. In
`QOpenGLWidgetPrivate::invokeUserPaint`, the OpenGL viewport
dimensions are also truncated to integer. Is there a possibility of
rounding errors, especially when the QOpenGLWidget is not using its
own native window?

The lowest Qt version we target is 5.9.x LTS if it makes any differences.

Best Regards,
Alvin

[1]: https://bugreports.qt.io/browse/QTBUG-53022
Alvin Wong
2018-10-29 10:15:54 UTC
Permalink
Hi Dmitry,

If this is the case, shouldn't it be better to change the behaviour of
Krita to match that of Qt, i.e. use qFloor for the conversion instead
of qCeil? The code in Qt would have been doing it this way for quite a
while, and asking Qt to change this behaviour could possibly break
other existing Qt applications.

Best Regards,
Alvin
Hi, Alvin!
I didn't test Krita much on fractional scaling, but I haven't seen any
special code in Qt for this alignment. We have a thing like that is
Krita to support Instant Preview, but it has nothing related to GUI.
Post by Alvin Wong
3. On QOpenGLWidget: It creates the FBO in device pixels in
`QOpenGLWidgetPrivate::recreateFbo`, but with the code `const QSize
deviceSize = q->size() * q->devicePixelRatioF();` the FBO size is
implicitly truncated to integer.
Yes, it looks like truncation is possible here. First, Qt calculates the
desired size of the widget in DI-pixels, and then allocates a
framebuffer of a corresponding size. When we do a thing like that in
Krita, we usually use qCeil() for the final size. But in Qt's code,
there seem to be no such thing.
I guess the best approach would be if you provided us with a patch that
we can apply to a local build of Qt in Krita. And after testing by the
users it would be much easier to push it upstream to Qt :)
Post by Alvin Wong
Is there a possibility of
rounding errors, especially when the QOpenGLWidget is not using its
own native window?
I guess there should be some rounding errors, yes. Usually, such errors
look like semi-transparent or write line at the border of the canvas.
--
Dmitry Kazakov
Loading...