Details
-
Bug
-
Resolution: Unresolved
-
P3: Somewhat important
-
None
-
5.12.2
-
None
Description
When using QImage.scanLine to access a QImage scanline by scanline, there's no way to cast that result to QRgb* to access it in a byte-order-independent manner like the C++ docs says:
Warning: If you are accessing 32-bpp image data, cast the returned pointer to QRgb* (QRgb has a 32-bit size) and use it to read/write the pixel value. You cannot use the uchar* pointer directly, because the pixel format depends on the byte order on the underlying platform. Use qRed(), qGreen(), qBlue(), and qAlpha() to access the pixels.
Would be good if there was some way to get a view of the bytes as an array of ints instead, that can be used with qRed(..), qGreen(..) et.c.
Bonus of course if it's still fast, since that's one of the points of using scanLine in the first place
To clarify, consider this C++ snippet:
#include <QImage> #include <QColor> #include <QDebug> int main(int, char *[]) { QImage image(2, 2, QImage::Format_RGB32); image.setPixel(0, 0, QColor(Qt::red).rgb()); image.setPixel(1, 0, QColor(Qt::green).rgb()); image.setPixel(0, 1, QColor(Qt::blue).rgb()); image.setPixel(1, 1, QColor(Qt::darkBlue).rgb()); for (int y = 0; y < image.height(); ++y) { const auto scanLine = (QRgb *)image.scanLine(y); for (int x = 0; x < image.width(); ++x) { const auto pixel = scanLine[x]; qDebug() << "pixel at" << x << y << "is" << "r =" << qRed(pixel) << "g =" << qGreen(pixel) << "b =" << qBlue(pixel); } } return 0; }
And prints:
pixel at 0 0 is r = 255 g = 0 b = 0 pixel at 1 0 is r = 0 g = 255 b = 0 pixel at 0 1 is r = 0 g = 0 b = 255 pixel at 1 1 is r = 0 g = 0 b = 128
Here's the "same" in Python:
from PySide2.QtCore import Qt from PySide2.QtGui import QImage, qRed, qGreen, qBlue, QColor image = QImage(2, 2, QImage.Format_RGB32) image.setPixel(0, 0, QColor(Qt.red).rgb()) image.setPixel(1, 0, QColor(Qt.green).rgb()) image.setPixel(0, 1, QColor(Qt.blue).rgb()) image.setPixel(1, 1, QColor(Qt.darkBlue).rgb()) for y in range(image.height()): scanLine = image.scanLine(y) for x in range(image.width()): pixel = scanLine[x] print('pixel at {},{} is r = {}, g = {}, b = {}'.format( x, y, qRed(pixel), qGreen(pixel), qBlue(pixel)))
And it prints.
pixel at 0,0 is r = 0, g = 0, b = 0 pixel at 1,0 is r = 0, g = 0, b = 0 pixel at 0,1 is r = 0, g = 0, b = 255 pixel at 1,1 is r = 0, g = 0, b = 0
This was just to illustrate the point. The result is obviously wrong, because the scan line has not been cast to an array of ints, it's still an array of bytes. One can access the individual bytes of course and take out the r,g,b components that way, but it's not portable as the byte-order is platform dependent.