简体   繁体   中英

Why does increasing the devicePixelRatio decrease the size of my QImage?

The devicePixelRatio is used to translate the device independent pixels into the actual physical pixels on the device. Therefore taking the example from the Qt Summit 2015 if I had a 300 x 200 QWidget with a devicePixelRatio of 2, it will render a 600 x 400 QWidget to the screen.

I have beneath me some code that I created, however I feel as though the opposite behaviour is happening. When I set self.image.setDevicePixelRatio(2) my QImage actually halves in size, however with a devicePixelRatio of 2, I interpret this as, for each device independent pixel, now occupy 2 pixels on my physical device, therefore doubling the size of my QImage (or atleast doubling when comparing to the before devicePixelRatio value of 1). Could someone explain why it is instead halving the size?

import sys

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QImage, QPainter
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget

DEFAULT_WIDTH = DEFAULT_HEIGHT = 250


class QPainterWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.image = QImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, QImage.Format_RGB32)
        self.image.fill(Qt.green)
        self.image.setDevicePixelRatio(2)
        painter = QPainter(self.image)
        painter.end()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawImage(0, 0, self.image)
        painter.end()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(DEFAULT_WIDTH, DEFAULT_HEIGHT)
        self.setCentralWidget(QPainterWidget())


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

在此处输入图像描述

This is very logical but a bit complicated at the first sight from the library users' perspective. And very complicated considering all implementation details in Qt library. But you do not need to care about most of the details, fortunately.

With widgets it is easy: you specify the logical size and the physical size on screen is derived from the logical size multiplied by the application DPR.

widget_physical_size_on_screen = widget_logical_size * application_dpr

But with images (and also icons and pixmaps) you need to distinguish between the image physical resolution and the physical size on screen. They can differ. And you also need to distinguish between the application DPR and the image DPR . They can also differ. The final physical size on screen is calculated using logical size as an intermediary step.

So with images you first specify the physical resolution in the constructor QImage(width, height) , the parameters width and height represent physical pixels of your image resolution. Then you can specify image DPR (as you did in your code). Then your image logical pixel size is derived from its physical resolution by dividing with the image DPR. And when the image is displayed in application, it is then resized to physical size by multiplying with the application DPR (not the image DPR).

image_logical_size = physical_image_resolution / image_dpr
image_physical_size_on_screen = image_logical_size * application_dpr

Therefore if you display image with physical resolution 600x400 with image DPR 2 (then it has logical size 300x200) in application which has DPR 1, then it will have physical size 300x200 pixels, because in application with DPR 1, one logical pixel translates to one physical pixel on screen. This is exactly what hapenned in your case - increasing DPR to 2 leads to smaller image on screen because your application still has DPR 1. Eg image with resolution 600x400 with image DPR 2 appears as 300x200 on screen.

If you display the same image in application with application DPR 2, then the image will have 600x400 physical pixels. Because in application with DPR 2, one logical pixel translates to 2 physical pixels. Ie your image will have its original size if and only if your application has the same DPR as is the image DPR.

If you display this image in application with DPR 3 (eg if you have really large 8k HighDPI display), then the image will have 900x600 physical pixels. And it will scale up badly (if you get a closer look on your fancy 8k display, the image will be slightly pixelated) because the original image does not have such a big resolution.

Actually setting DPR for images is not strictly needed, you can leave it with default value, which is DPR 1. But then you will need to do some more size recalculations and image resizings yourself. So if you want image with logical pixel size 300x200 then for application with DPR 1 you would need to create image with resolution 300x200, for application with DPR 2 you would need image with resolution 600x400 etc. Otherwise if you display image with physical resolution 300x200 and DPR 1 (ie it has logical size 300x200) in application with DPR 2, the image will be scaled-up to physical size 600x400 but it will be pixelated because the actual physical resolution of the image is still just 300x200.

So keeping images with DPR 1 can actually save some memory in some cases since you will allocate smaller images, only as big as you really need. But the drawback is doing more calculations in code yourself. It may be tempting to do these calculations yourself at the first sight. But I should warn you, in the text above I left out one important detail. In general case there is not a single application-wide DPR. In multiscreen set up the application can occupy more screens, each with different DPR. So it is not the application which specifies the DPR but DPR is specified for each widget individually. And widgets can move from screen to screen if you move your window and hence their DPR can change. And believe, then you do not want to recalculate the size of your images yourself in your code.

But if you are 100 % sure you do not want to support multiscreen mixed DPR scenario, then you can leave your images with DPR 1 (the default) and resize your images yourself. It can save you save memory and processing power in some cases because you can avoid allocating unneccessarily big images and avoid scaling down if the big resoltion is not needed. But I would probably not recommend going this way.

And one last very important note: always test your application in low-DPI scenarion with DPR == 1 and high-DPI scenario with DPR = 2. And ideally test your application also with fractional DPR, ie when DPR is a floating point number such as 1.25 or 1.5. Your application should work well in all these cases. If not, then fix it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM