Make the canvas behave in HiDPI mode
Open, NormalPublic

Description

In HiDPI mode, the canvas is scaled up and down and made fuzzy and the wrong zoom level.

https://bugs.kde.org/show_bug.cgi?id=360541

rempt created this task.Apr 16 2016, 1:42 PM
Restricted Application added a subscriber: woltherav. · View Herald TranscriptApr 16 2016, 1:42 PM
rempt reassigned this task from rempt to dkazakov.Sep 1 2016, 9:28 AM
rempt added a comment.Feb 2 2017, 10:27 AM

Date: Thu, 26 May 2016 15:55:17 +0200
From: Frederik Gladhorn <frederik.gladhorn@qt.io>
To: Boudewijn Rempt <boud@kde.org>, Morten Sorvig <morten.sorvig@qt.io>
Subject: Krita and high dpi canvas painting

Hello Boud, hello Morten,

after I chatted with each of you, I thought I'd just get you in touch.
Morten was interested in the scaling issues with regards to the Krita canvas.
Since he did most of the Qt work on High DPI, I'm sure he has ideas how to
make things work smoothly.

I guess it's best if you can give a description of the issues Krita is facing,
I don't want to play man in the middle ;)

Cheers,

Date: Thu, 26 May 2016 18:18:05 +0200 (CEST)
From: Boudewijn Rempt <boud@valdyas.org>
To: Frederik Gladhorn <frederik.gladhorn@qt.io>
Cc: Boudewijn Rempt <boud@kde.org>, Morten Sorvig <morten.sorvig@qt.io>, dimula73@gmail.com
Subject: Re: Krita and high dpi canvas painting

Hi!

Dmitry has digged in a bit deeper, so I've cc'ed him so he can chime in
as well. On OSX there doesn't seem to be a problem, at least not for the
opengl canvas. On Windows, this is the basic issue:

https://bugs.kde.org/show_bug.cgi?id=360541

It's testable with the latest dev builds, by setting the KRITA_HIDPI
environment variable to something -- if it's set, AA_EnableHighDpiScaling
is set.

Users complain that if they enable 150% or 200% display scaling both
the opengl-based and the cpu, qpainter-based canvas are wrong: the
image is also displayed at 150% or 200%.

This is the same image at 100% zoom with scaling:

http://imgur.com/shQ8iyw

And this is without the option set:

http://imgur.com/VqoSg59

Dmitry probably can explain what he thinks Qt is doing better than me,
but basically, we want to render at the real, physical dpi of the screen,
and zoom the image using our high-quality (that's the name, but it's
pretty good) scaling shader.

The qpainter-based canvas is probably less important for us, but from
what I hear from Frederik might actually be easier to fix by just using
the floating point qpainter api, but with opengl we of course cannot
use the qpainter api for the image itself.

(And for the canvas decoration, the opengl qpainter engine needs to be
ported to 3.0 core profile, but we've got a summer of code student
for that!)

On Thu, 26 May 2016, Frederik Gladhorn wrote:

Hello Boud, hello Morten,

after I chatted with each of you, I thought I'd just get you in touch.
Morten was interested in the scaling issues with regards to the Krita canvas.
Since he did most of the Qt work on High DPI, I'm sure he has ideas how to
make things work smoothly.

I guess it's best if you can give a description of the issues Krita is facing,
I don't want to play man in the middle ;)

Cheers,
Frederik

Boudewijn Rempt | http://www.krita.org, http://www.valdyas.org

[ALL of mess

Date: Fri, 27 May 2016 13:13:25 +0000
From: Morten Sorvig <Morten.Sorvig@qt.io>
To: Boudewijn Rempt <boud@valdyas.org>
Cc: Frederik Gladhorn <Frederik.Gladhorn@qt.io>, Boudewijn Rempt <boud@kde.org>,

"dimula73@gmail.com" <dimula73@gmail.com>

Subject: Re: Krita and high dpi canvas painting

Hello!

On 26 May 2016, at 18:18, Boudewijn Rempt <boud@valdyas.org> wrote:

Hi!

Dmitry has digged in a bit deeper, so I've cc'ed him so he can chime in
as well. On OSX there doesn't seem to be a problem, at least not for the
opengl canvas. On Windows, this is the basic issue:

https://bugs.kde.org/show_bug.cgi?id=360541

I’m also a bit surprised that there is a difference between Windows and
OS X in this regard, so this is something to look further into.

It's testable with the latest dev builds, by setting the KRITA_HIDPI
environment variable to something -- if it's set, AA_EnableHighDpiScaling
is set.

I’ll download a dev build and test.

Dmitry probably can explain what he thinks Qt is doing better than me,
but basically, we want to render at the real, physical dpi of the screen,
and zoom the image using our high-quality (that's the name, but it's
pretty good) scaling shader.

The good news here is that OpenGL works in the physical display pixel
coordinate system, so you should be able to run the shader like you want
to.

Inputs/coordinates from the GUI side needs to be scaled for calls like
glViewport(). See for example hellowindow.cpp from qtbase/examples/opengl/hellowindow.

The qpainter-based canvas is probably less important for us, but from
what I hear from Frederik might actually be easier to fix by just using
the floating point qpainter api, but with opengl we of course cannot
use the qpainter api for the image itself.

QPainter input coordinates are in the logical (scaled) coordinate system. It
will rasterize at the full physical resolution. But if you want to provide
inputs at the display resolution then floating point is the way to go.

(And for the canvas decoration, the opengl qpainter engine needs to be
ported to 3.0 core profile, but we've got a summer of code student
for that!)

Nice!

Morten

Date: Tue, 31 May 2016 12:12:20 +0000
From: Morten Sorvig <Morten.Sorvig@qt.io>
To: Boudewijn Rempt <boud@valdyas.org>
Cc: Frederik Gladhorn <Frederik.Gladhorn@qt.io>, "dimula73@gmail.com" <dimula73@gmail.com>
Subject: Re: Krita and high dpi canvas painting

On 27 May 2016, at 15:23, Boudewijn Rempt <boud@valdyas.org> wrote:

On Fri, 27 May 2016, Morten Sorvig wrote:

I’ll download a dev build and test.

I made new ones only last night:

http://files.kde.org/krita/3/windows/devbuilds/krita-3.0-RC-1-master-7785651-x64.zip

Hello again,

After testing a bit on OS X and Windows I don’t really see a difference between the platforms. Note that I don’t know if
I’m testing the OpenGL canvas or not. Is there a setting for it?

What I did was manually adjusting the the scale factor by setting the QT_SCALE_FACTOR environment variable. I also tested
with KRITA_HIDPI, there should be no difference to the application.

What I see is that increasing the scale factor decreases the visual area available for Krita. (everything else being
equal - in real use the increase in scale factor will have a corresponding increase in display pixel density, giving
constant available visual area).

This default behavior is good for the UI but might not be what’s best for the canvas. Here’s a summary of the options:

  1. Constant visual size: Moving the application window from a 1x to a 2x display shows the same amount of canvas content.

Content is rendered in finer detail at zoom < 100%.

  1. Full physical pixel usage: Moving the application window from a 1x to a 2x monitor shows more content, effectively

using the full physical pixel density to display as much as possible of the canvas at a given zoom level.

Implementing option 2 takes some extra effort: The geometry you get from the QWidget API will be in logical pixels, which
you then scale by QPaintDevice::devicePixelRatioF() or QWindow::devicePixelRatio() to get geometry in physical pixels.
(at least for the OpenGL canvans)

This is something we could look further into, although I don’t know how much help I can be with he Krita code base.

Morten

Date: Wed, 1 Jun 2016 16:30:17 +0300
From: Dmitry Kazakov <dimula73@gmail.com>
To: Boudewijn Rempt <boud@valdyas.org>
Cc: Morten Sorvig <Morten.Sorvig@qt.io>, Frederik Gladhorn <Frederik.Gladhorn@qt.io>
Subject: Re: Krita and high dpi canvas painting
Parts/Attachments:

1 Shown    60 lines  Text
2   OK    ~56 lines  Text

Hello!

I might be a bit out of the context now. The last time I checked how openGL
works I fond the following structure:

  1. For most of the widgets the application writes to the local widget's (or

window's) framebuffer.

  1. Then the framebuffer is painted on the final application window using

the some Qt's internal algorithm.

  1. As far as I understood, this internal widget's framebuffer is painted on

screen using the scaling transformation, which is basically the global Qt's
scaling factor.

To implement the correct zooming for Krita I need to know only one answer:

Does Qt scales the framebuffer of the QOpenGLWidget? And if yes, how to
disable it for a specific widget?

According to Boud's screenshots, the scaling happens. That is not what we
want, because the user's image becomes blurry. We use special shaders to
render the image sharply on any small display and all this work is
destroyed by further upscaling it :(

So basically, I just need a way to disable this scaling for a QOpenGLWIdget.

Date: Thu, 2 Jun 2016 11:51:09 +0000
From: Morten Sorvig <Morten.Sorvig@qt.io>
To: Dmitry Kazakov <dimula73@gmail.com>
Cc: Boudewijn Rempt <boud@valdyas.org>, Frederik Gladhorn <Frederik.Gladhorn@qt.io>
Subject: Re: Krita and high dpi canvas painting

On 01 Jun 2016, at 15:30, Dmitry Kazakov <dimula73@gmail.com> wrote:

To implement the correct zooming for Krita I need to know only one answer:

Does Qt scales the framebuffer of the QOpenGLWidget? And if yes, how to disable it for a specific widget?

Hi,

The short answer is that Qt does not (or is not supposed to) scale the OpenGL framebuffer. The fragment shaders will run
for each display pixel, and there is no upscaling of the result.

Here’s a screenshot at ‘ridiculous’ scale (4x using QT_SCALE_FACTOR):

http://imgur.com/PuhMHXA

This should make it clear which parts of the UI are rendered at full resolution (text, canvas, some icons) and not (color
selector, some other icons).

So this was a “works for me” answer :) I you are seeing blurry canvas output on some platform/config then let’s look at
that.

Morten

ALPINE 2.00   MESSAGE TEXT [A]                      <Mail> saved-messages                      Msg 2,672 of 2,998 ALL

Date: Thu, 2 Jun 2016 12:32:10 +0000
From: Morten Sorvig <Morten.Sorvig@qt.io>
To: Boudewijn Rempt <boud@valdyas.org>
Cc: Frederik Gladhorn <Frederik.Gladhorn@qt.io>, "dimula73@gmail.com" <dimula73@gmail.com>
Subject: Re: Krita and high dpi canvas painting

On 31 May 2016, at 14:50, Boudewijn Rempt <boud@valdyas.org> wrote:

On Tue, 31 May 2016, Morten Sorvig wrote:

  1. Constant visual size: Moving the application window from a 1x to a 2x display shows the same amount of canvas

content. Content is rendered in finer detail at zoom < 100%.

  1. Full physical pixel usage: Moving the application window from a 1x to a 2x monitor shows more content, effectively

using the full physical pixel density to display as much as possible of the canvas at a given zoom level.

Implementing option 2 takes some extra effort: The geometry you get from the QWidget API will be in logical pixels,

which you then scale by QPaintDevice::devicePixelRatioF() or QWindow::devicePixelRatio() to get geometry in physical
pixels. (at least for the OpenGL canvans)

I'm so confused now... Thinking again, I guess it's 2) we need, but we'll need to calculate the scaling factor so the

image is always shown at the right resolution: if the 1x and 2x monitors have the same physical size, we should show the
same amount of image.

So when moving between 1x and 2x monitors of equal physical size the scale factor and physical pixel density changes in
sync, and we show the same amount of image.

The reason for the Windows bug reports may be that when changing the Windows scaling you are (obviously) not changing the
pixel density of the monitor at the same time. So the behavior where higher scaling percentages show less content is
expected, because:

  • a 3840x2160 monitor at 100% is large and should show more content.
  • a 3840x2160 monitor at 250% is smaller and should show less content.

(This is assuming the monitor scaling is correctly configured for its physical size)

If this makes sense then the good news may be that no code changes are needed.

Morten

woltherav triaged this task as Normal priority.Feb 28 2017, 11:28 AM
rempt added a comment.Sep 27 2017, 8:18 AM

I've applied all patches and made screenshots before and after, at fill page and 50% with and without opengl.

I found these issues with HiDPI support:

  1. Various Qt bugs. Regarding Windows, some bugs are fixed for 5.9.2, some for 5.9.3, so I will be updating ext_qt for Windows to 5.9.2 once it's released, and possibly backport some patches from the 5.9 branch.
  2. Qt platform plugins round the scale factor to integer, which cause overly huge UI with *.5 system dpi scaling. This is tracked on https://bugreports.qt.io/browse/QTBUG-53022, but unfortunately it will likely not be fixed on Qt 5.9.x. I can possibly patch Qt to round down the scale factor instead as a temporary workaround...
  3. Krita takes the integral coordinates for mouse events which are snapped to the logical pixel space, instead of the floating point coordinates that can accurately reflect the device pixel space under HiDPI. Seems like this would take a lot of work to fix.
rempt added a comment.Sep 28 2017, 2:38 PM
  1. sounds scary indeed.

From D7982:

I propose the following roadmap/requirements for the correct HiDPI fix:

  1. KisZoomController's zoom is relative to the hardware pixles. It corresponds to what the user sees in GUI.
  2. As a consequence, KoDPI should measure DPI relative to hardware pixels as well (needs checking all the uses!)
  3. KoCanvasControllerWidget and the hierarchy of scrolling/mirroring classes use logical pixels for its work (questionable, but they interact with widgets a lot).
  4. KisCoordinatesConverter's "widget coordinate system" represents physical coordinates
  5. We add one more coordinates system to KisCoordinatesConverter: "widget logical", which represents logical coordinates.
  6. KisOpenGLCanvas2 uses physical coordinates for rendering
  7. All the tools accept input events and render in physical coordinates (questionable?) and use the new converter's methods for the conversion (that ensures easy display migration)
  8. I don't know what to do with KisQPainterCanvas. I guess, we cannot technically make it render in physical coordinates, so we should just decide how it should handle zoom.
  1. I don't know what to do with KisQPainterCanvas. I guess, we cannot technically make it render in physical coordinates, so we should just decide how it should handle zoom.

This is not true, it is totally possible to render the image in device pixel space, I did it in D7973.

Earlier on irc, I suggested having the zoom manager`store the internal zoom level with respect to logical pixel space and have the UI (e.g. zoom slider) handle zoom in device pixel space and scaling it based on devicePixelRatio as an easy way make the zoom level work in device pixel space, but @dkazakov stated that the zoom manager must store the zoom level in device pixel space, and that the fix I had in mind would be more difficult to implement than his.

We had a (not really productive) discussion about this without a conclusion. But I am not familiar with the Krita codebase enough so I think I'll stop here and let someone else handle this task.

rempt added a comment.May 21 2018, 9:09 AM

http://doc.qt.io/qt-5/highdpi.html

Coordinate systems in Krita

  • Image coordinates: actual pixels in the image
  • Document coordinates: points -- 72 points to an inch (tools, shapes use document coordinates)
  • Flake/View coordinate system: document scaled to "view" pixels at current zoom level -- what a view pixel is, is not defined at this point
  • Widget coordinate system: flake pixels transformed with mirroring, scrolling, rotation
  • QPainter canvas has a viewport coordinate system
  • What is a display/view pixel**
  • Actual display pixel, device pixel
  • Device-independent pixel, scaled by the display scale factor
  • What happens now **
  • Windows scaling is set to 2
  • Krita zoom is set to 50%
  • The image is drawn as if zoom is 100%
  • At 100% zoom, the image is drawn as if zoom is 200%
  • The framebuffer object is in device pixels
  • We should **

Use the canvas widget's devicePixelRatio when calculating the size we draw at. Something like:
QTransform imageToDevicePixelTransform(qreal devicePixelRation = 1.0) const;

Two versions of zoom:

Virtual zoom: zoom that is shown to the user
Physical zoom: zoom used for filtering

RE: our conversation at Akademy

In theory /all/ one needs to do for GL high DPI support is:

  • make the GL window twice the size
  • set the viewport twice the size

That's all handled automatically inside the QOpenGLWidget.
Then you continue doing everything in normalised co-ordinates, and it all just works.

However, when we do low level textures. That always needs to be in device pixels.

These seem to be:
The highQ downscaler chooses the downscale method based on the viewport scale.
We want to be considering the viewport in device pixels, so that uniform needs multiplying by the DPR.

KisTextureTile::update updates mipmaps at certain levels, not always the base layer.
I assume the base layer corresponds to the canvas size, and the current LOD is chosen based on zoom level, if so this LOD needs to be based on devicePixels not virtual pixels.
I tried following that, but it goes back into the srcImage and it gets confusing.

dkazakov added a comment.EditedAug 21 2018, 9:56 AM

Hi, @davidedmundson

In theory /all/ one needs to do for GL high DPI support is:

  • make the GL window twice the size
  • set the viewport twice the size

The problem is that by default, when automatic GUI scaling is enabled, Qt creates an intermediate framebuffer object for your QOpenGLWidget and then scales it up while rendering. So these steps will not work out-of-box. We need to somehow disable this scaling.

That's all handled automatically inside the QOpenGLWidget.
Then you continue doing everything in normalised co-ordinates, and it all just works.

No, it doesn't work. I tested it multiple times. As soon as devicePixelRatio() becomes non-identity, Qt starts to scale up the framebuffer of the widget.

However, when we do low level textures. That always needs to be in device pixels.
The highQ downscaler...
KisTextureTile::update updates mipmaps...

I can fix it easily. But it will not stop Qt from scaling the final image by the devicePixelRation() factor. That is the main problem I don't know how to solve.

PS:
I was testing on Windows, btw. That is the only HiDPI capable device I have :)

davidedmundson added a comment.EditedAug 21 2018, 10:05 PM

Qt creates an intermediate framebuffer object for your QOpenGLWidget and then scales it up while rendering.

Where are you reading that?

QOpenGLWidget.cpp::recreateFBI

const QSize deviceSize = q->size() * q->devicePixelRatioF();
fbo = new QOpenGLFramebufferObject(deviceSize, format);

This is creating the framebuffer in device pixels, that's exactly what we want.

Bumping this, because I just saw this comment about QOpenGLWidget by Boud on Reddit today.

Qt creates an intermediate framebuffer object for your QOpenGLWidget and then scales it up while rendering

It definitely doesn't!
I can't see it happening on a test and the code in QOpenGLWidget indicates the exact opposite - that it's rendering at the native resolution.

It could mean you were looking at an older Qt to me, or maybe there's something funky in the windows QPA that I don't have - but I really don't see it.


I'll sort out the highQ downscaler, it's a oneliner.
If you can give me some pointers on where to track the LOD in KisTextureTile::update we can hopefully move this forward.

rempt added a comment.Oct 4 2018, 8:52 AM

Dmitry, you did figure out once where this happened -- can you locate that again and add a link to the exact bit of source code to this task?

Okya, I'll check it once again. But as far as I remember the scaling is activated when we enable "Scale GUI" option in Krita settings...

Hi, @davidedmundson!

I have just tried to reproduce the problem again, and I do still reproduce it with Qt 5.9.1. I will try to search for exact lines in Qt's code, but I'm not sure I will manage to finish it today. Steps to reproduce:

  1. Set GUI scaling in windows to 150-200%
  2. Start Krita
  3. Go to Settings->General->Enable HiDPI support (it basically activates GUI scaling). Without this option, openGL canvas behaves exactly like you explain, that is, renders inRestart hardware pixels. But after activating this option, scaling starts.
  4. Restart Krita
  5. Create 500x200 image
  6. Activate "Use same aspect as pixels" box at the bottom-right corner of the window
  7. Press '1' to set 100% zoom
  8. BUG: See that the image has actually size of 1000x400 physical pixels. And Krita does not pass any extra scaling to openGL. It paints just in logical pixels.

About you snippet from QOpenGLWidget.cpp::recreateFBI: I might be wrong, but as far as I remember, in GUI-scaling mode q->devicePixelRatioF() is 1.0, that is, it is not passed to the application. All the scaling trickery happens somewhere in the internals of Qt. I will try to check this hypothesis now, though.

Right, I think we just have some miscommunication rather than a code difference.

Aside from the now-super-awkwardly named "Use same aspect as pixels" *. That's /exactly/ how I would expect things to act.

That's the entire premise of high DPI support. You set a window to be 500x200, you get 1000x400 physical pixels.

The killer part that makes it not "scaling" up is that we don't render at 500x200 and resize it.
We get a backing store of 1000x400 and should be rendering everything to match. (i.e if you have 50% zoom, we'd internally be rendering everything as though it's 100% zoom) so you get everything the same physical size a 1x screen, but with more detail.

*It is still possible to set make things match, but it'd be up to krita code to set the correct size in logical pixels. i.e 250x100.

The killer part that makes it not "scaling" up is that we don't render at 500x200 and resize it.

I'm not sure that it really happens this way. I would need to add qDebug's to Qt's code to confirm that. Last time I checked that I got an impression that it just scales things up when rendering the framebuffer into the toplevel widget.

*It is still possible to set make things match, but it'd be up to krita code to set the correct size in logical pixels. i.e 250x100.

You mean to get physical pixels we should render on 50% zoom? That would be doable, I guess, if we'd confirm the previous point. But I feel a bit skeptical about it.

Hi, @davidedmundson!

I have just tried to reproduce the problem again, and I do still reproduce it with Qt 5.9.1. I will try to search for exact lines in Qt's code, but I'm not sure I will manage to finish it today. Steps to reproduce:

  1. Set GUI scaling in windows to 150-200%
  2. Start Krita
  3. Go to Settings->General->Enable HiDPI support (it basically activates GUI scaling). Without this option, openGL canvas behaves exactly like you explain, that is, renders inRestart hardware pixels. But after activating this option, scaling starts.
  4. Restart Krita
  5. Create 500x200 image
  6. Activate "Use same aspect as pixels" box at the bottom-right corner of the window
  7. Press '1' to set 100% zoom
  8. BUG: See that the image has actually size of 1000x400 physical pixels. And Krita does not pass any extra scaling to openGL. It paints just in logical pixels.

Yes, and this bug is one of the few things that Krita should be fixing. We've been talking about this for ages.

About you snippet from QOpenGLWidget.cpp::recreateFBI: I might be wrong, but as far as I remember, in GUI-scaling mode q->devicePixelRatioF() is 1.0, that is, it is not passed to the application. All the scaling trickery happens somewhere in the internals of Qt. I will try to check this hypothesis now, though.

devicePixelRatioF() will definitely be 2.0 in this case. If it's not, then there's a bug in Qt that somehow I've never encountered when I dived into this issue many months ago.

I'm not sure that it really happens this way.

Feel free to check.

This fixes one of the two issues that will make it less blurry https://phabricator.kde.org/P263

Feel free to check.

It might be you are right, the FBO is created of doubled size. Though I don't really understand how it works yet...

Okay, it looks like Qt actually creates the double-sized frame buffer, but we still see it as a single-sized (and reset the viewport). We should just set the viewport to this doubled size.

Here is a draft patch that partly fixes rendering quality (but breaks position and cursor coordinates conversion). The new "virtual" coordinate system should be explained to KisCoordinatesConverter and integrated to other coordinate systems we have. Because at zoom 100% the user must see pixel-to-pixel rendering of the image, and not what we have now.

I'm not sure that it really happens this way.

Feel free to check.

This fixes one of the two issues that will make it less blurry https://phabricator.kde.org/P263

If all you want is to fix the blurriness without fixing the actual issue, I had a series of patches to do that (D7973, D7982, D7984).

Okay, it looks like Qt actually creates the double-sized frame buffer, but we still see it as a single-sized (and reset the viewport). We should just set the viewport to this doubled size.

Here is a draft patch that partly fixes rendering quality (but breaks position and cursor coordinates conversion). The new "virtual" coordinate system should be explained to KisCoordinatesConverter and integrated to other coordinate systems we have. Because at zoom 100% the user must see pixel-to-pixel rendering of the image, and not what we have now.

You should prefer the floating point version devicePixelRatioF() over the integer version, since it can become non-integral when Qt finally supports it in the future.

If all you want is to fix the blurriness without fixing the actual issue, I had a series of patches to do that (D7973, D7982, D7984).

That roughly looks like what I would have expected. It's embracing the high DPI system, rather than trying to fight it.
Something that I would not remotely refer to as "not fixing the actual issue".

@dkazakov Thanks for the partial fix. I've done some testing on Windows with krita-nightly-x64-v4.1.3.1-239-ge64348e693, so here's my observations/findings:

Working:

  • Both OpenGL and ANGLE works with High DPI with integer scaling
  • Per-monitor integer DPI is handled correctly
  • Per-monitor DPI is handled correctly sans the font size issues caused by the rounding by Qt
  • QPainter canvas is blurry. To fix it I believe you can use an approach similiar to D7973, by creating the projection in device pixel size (i.e. like the FBO in the OpenGL canvas) and drawing to the QPainter with the correct arguments taking into account the devicePixelRatio.

Following assumes scale factor (or what Qt rounded up/down to) is at 200% and operating at 100% canvas zoom level:

  • Using the mouse and a stylus, one can't paint every other pixel with 1px pixel brush by single-clicking. (Related to T7130)
  • Straight lines with 1px pixel brush snaps to a 2px grid. (Also related to T7130)
  • The rectangular selection tool also snaps to a 2px grid. The selection outline might also be inaccurate.
  • If you draw two 1px lines in different colour then use the colour picker, It somehow picks an intermediate colour.
  • Brush outline of 1px pixel brush is 2px in size (though this might not be a regression).

(There might be more. This is not an exhaustive list.)

A tip on testing: You can fire up the magnifier by pressing Win+[plus] (or Win+=). Make sure to open the Magnifier settings to disable smooth scaling. This will make it easier to observe the pixels.

One more issue: The fix seems to have used the integer version of devicePixelRatio somewhere instead of the floating point devicePixelRatioF. This is from my observation forcing a 1.5x scaling on Windows. I haven't checked the code. Even though Qt now rounds the scale factor to nearest integer on Windows, we should assume that real fractional scaling support will be added in the future. In fact the then-in-progress task https://bugreports.qt.io/browse/QTBUG-53022 is exactly for this. (It might even be already supported on KDE, but I can't confirm.) So please fix it to use the floating point devicePixelRatio.

Here is how I tested it:

Testing environment:

  • Windows 10 ver. 1803
  • First monitor is 2560x1440, scaling at 150%
  • Second monitor is 1920x1080, scaling at 100%

Running Krita - In a cmd command prompt, execute:

set QT_AUTO_SCREEN_SCALE_FACTOR=0
set QT_SCREEN_SCALE_FACTORS=1.5;1
krita.exe

Now Krita is running at actual scale factor without the rounding applied by Qt. You can see that 100% canvas is actually rendered at 150% (since 1.5 is truncated to 1).

(The UI actually renders pretty alright - at least with the default theme - so I don't really get why Qt chooses to round the scale factor with no easy override...)

I've verified the value of devicePixelRatioF to be correct using GammaRay (see attached images).

Krita on first monitor at 150% scaling:

Krita on second monitor at 100% scaling:

alvinhochun reopened this task as Open.Oct 17 2018, 8:41 AM

@dkazakov I see that you've changed the code to use devicePixelRatioF. I can see that the canvas now attempts to render at 100% device pixel size.

However, the rendered output at 150% display scaling and 100% canvas scale is not pixel-perfect. The render also changes depending on the panning offset

The effect is best observed with these images (and perhaps in wraparound mode):

It's probably caused by rounding errors, and it might be affected by both the Qt layout engine and QOpenGLWidget's FBO creation.. I need to check whether Qt aligns layout stuff to device pixels (looks like the Fusion theme does this to some extent) before making more suggestions. (That means asking on the Qt mailing list... I don't have the time to dig into Qt's code now.)


Given that the QPainter canvas is still not working as expected on at least integer scaling, I'll reopen this task for now.

In my opinion, issues with input handling and fractional scaling should be subtasks to this task. This task shouldn't be closed until the subtasks are cleared.

This comment was removed by alvinhochun.