问题
When I try to create a ARGB32 QImage from a reinterpret_cast<uchar*>(quint32*)
using the QImage constructor the Image looses its color and alpha channel and the resulting QImage is grayscale!
The grayscale image is displayed as expected, if I was trying to display it in grayscale. So I know the scaling and indexing of ushort data to the quint32 array went well, but what is going wrong?
A Qt forum post suggested to do it the way I am doing it (as far as I can see), but maybe behavior has changed since that version of Qt? (I am Using Qt 5.9)
I realise that the documentation says:
data must be 32-bit aligned, and each scanline of data in the image must also be 32-bit aligned.
But I would expect quint32 to be 32-bit aligned even after reinterpret_cast<uchar*>()
?
Now the details: I am converting the results of a calculation (an array with unsigned short values) to a semi-transparent blue-to-green-to-red image like this:
inline uchar val_to_blue(const double val) {
if (val > 0.5)
return 0;
else if (val < 0.25)
return 255;
else // x={.5,...,.25}:a=255/(.25-.5)=-4*255 & b=-255*0.5/(0.25-0.5)=4/2*255=2*255
return (uchar)(val * -4.0 * 255.0) + 2 * 255;
}
inline uchar val_to_green(const double val) {
if (val > 0.25 && val < 0.75)
return 255;
else if (val < 0.25)// x={0,...,.25}:a=255/(.25-0)=4*255 & b=-255*0/(0.25-0)=0
return (uchar)(val * 4.0 * 255.0);
else // if (val > .75) // x={.75,...,1}:a=255/(.75-.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * -4.0 * 255.0) - 2 * 255;
}
inline uchar val_to_red(const double val) {
if (val < 0.5)
return 0;
if (val > 0.75)
return 255;
else // x={0.5,...,0.75}:a=255/(0.75-0.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * 4.0 * 255.0) - 2 * 255;
}
inline QRgb val_to_rgba_scale(const double val) {
return qRgba( // ax+b={0,...,255} for x={i,...,j}, a=255/(j-i), b= -255i/(j-i)
val_to_blue(val),
val_to_green(val),
val_to_red(val),
(uchar)(val * 81)
);
}
Where val
is a double between 0 and 1 scaled from the ushort data.
Each QRgb
value is stored at the corresponding index of a quint32
array, like this:
if (m_pData[i*m_iWidth + j] >= uppVal)
tmpData[tmpIdx] = 0x45ff0000;
else if (m_pData[i*m_iWidth + j] <= lowVal)
tmpData[tmpIdx] = 0x00000000;
else
tmpData[tmpIdx] = val_to_rgba_scale((m_pData[i*m_iWidth + j] - lowVal) / (double)winWidth);
Where (m_pData[i*m_iWidth + j] - lowVal) / (double)winWidth
is the ushort-to-double scaling method.
This is done in a for loop.
Finally I attempt to construct the image with:
QImage tmpQImage = QImage(reinterpret_cast<unsigned char*>(tmpData), m_iWidth, m_iHeight, QImage::Format_ARGB32);
But this doesn't work as I expect, because tmpQImage.allGray()
returns true when called immediately after!
What am I doing wrong, and what should I do instead to create a ARGB image and keep both the colors and alpha channel?
回答1:
I tried to reproduce your problem but I couldn't.
Either the actual issue of the OP is not part of the presented code, or I accidentally missed a detail when I tried to form an MCVE from the OP.
However, I want to present what I got so far as this may be helpful to fix the OP.
My source testQImageGrayToRGB.cc
:
#include <vector>
#include <QtWidgets>
typedef unsigned char uchar;
namespace AGA {
uchar val_to_blue(const double val) {
if (val > 0.5)
return 0;
else if (val < 0.25)
return 255;
else // x={.5,...,.25}:a=255/(.25-.5)=-4*255 & b=-255*0.5/(0.25-0.5)=4/2*255=2*255
return (uchar)(val * -4.0 * 255.0) + 2 * 255;
}
uchar val_to_green(const double val) {
if (val > 0.25 && val < 0.75)
return 255;
else if (val < 0.25)// x={0,...,.25}:a=255/(.25-0)=4*255 & b=-255*0/(0.25-0)=0
return (uchar)(val * 4.0 * 255.0);
else // if (val > .75) // x={.75,...,1}:a=255/(.75-.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * -4.0 * 255.0) - 2 * 255;
}
uchar val_to_red(const double val) {
if (val < 0.5)
return 0;
if (val > 0.75)
return 255;
else // x={0.5,...,0.75}:a=255/(0.75-0.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * 4.0 * 255.0) - 2 * 255;
}
} // namespace AGA
namespace DS {
uchar val_to_blue(const double val)
{
return val < 0.25 ? 255
: val < 0.5 ? (0.5 - val) * 4 * 255
: 0;
}
uchar val_to_green(const double val)
{
return val < 0.25 ? val * 4 * 255
: val < 0.75 ? 255
: (1.0 - val) * 4 * 255;
}
uchar val_to_red(const double val)
{
return val < 0.5 ? 0
: val < 0.75 ? (val - 0.5) * 4 * 255
: 255;
}
} // namespace DS
std::vector<quint32> buildImageData(
const int w, const int h,
uchar (*pFuncValToR)(double),
uchar (*pFuncValToG)(double),
uchar (*pFuncValToB)(double))
{
// make temp. buffer to build up raw image data
std::vector<quint32> data(w * h);
// fill raw image - make values 0 ... 1 in n steps
const int n = w - 1;
for (int x = 0; x < w; ++x) {
const double v = (double)x / n;
QRgb qRgb = qRgba(pFuncValToR(v), pFuncValToG(v), pFuncValToB(v), 255);
for (int y = 0; y < h; ++y) data[y * w + x] = qRgb;
}
// done
return data;
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
QApplication app(argc, argv);
// build contents
enum { w = 256, h = 32 };
std::vector<quint32> dataAGA = buildImageData(w, h,
&AGA::val_to_red, &AGA::val_to_green, &AGA::val_to_blue);
QImage qImgAGA((const uchar*)dataAGA.data(), w, h, QImage::Format_ARGB32);
std::vector<quint32> dataDS = buildImageData(w, h,
&DS::val_to_red, &DS::val_to_green, &DS::val_to_blue);
QImage qImgDS((const uchar*)dataDS.data(), w, h, QImage::Format_ARGB32);
// build some GUI
QWidget win;
QVBoxLayout qVBox;
QLabel qLblAGA(
QString::fromUtf8("QImage (Functions of Andreas Gravgaard Andersen):"));
qVBox.addWidget(&qLblAGA);
QLabel qLblImgAGA;
qLblImgAGA.setPixmap(QPixmap::fromImage(qImgAGA));
qVBox.addWidget(&qLblImgAGA);
QLabel qLblDS(
QString::fromUtf8("QImage (Functions of Scheff):"));
qVBox.addWidget(&qLblDS);
QLabel qLblImgDS;
qLblImgDS.setPixmap(QPixmap::fromImage(qImgDS));
qVBox.addWidget(&qLblImgDS);
win.setLayout(&qVBox);
win.show();
// exec. application
return app.exec();
}
I compiled and tested it with VS2013, Qt5.6 on Windows 10 (64 bit):
Notes:
The
val_to_
functions made me a little bit suspicious: an expression casted to(uchar)
, then a constant term added (which definitely does not fit into(uchar)
, the result returned asuchar
...
Hmm...
Therefore, I remade them – with a little bit clean-up.
Actually, the visual comparison shows the differences are nearly invisible (with the only exception of the red line in the yellow region).I had no problems to make a
QImage
out of the rawquint32
array (including the cast-to-uchar*
-hack).
Update:
May be, it is not obvious: The sample code is carefully designed to grant that life-time of buffer data (std::vector<quint32> dataAGA
and std::vector<quint32> dataDS
) is longer than the life-time of Qt images (QImage qImgAGA
and QImage qImgDS
). This has been done according to the Qt doc. for QImage::QImage():
The buffer must remain valid throughout the life of the QImage and all copies that have not been modified or otherwise detached from the original buffer. The image does not delete the buffer at destruction. You can provide a function pointer cleanupFunction along with an extra pointer cleanupInfo that will be called when the last copy is destroyed.
Image data may consume a significant amount of memory. Thus, the QImage
implementation tries to prevent unnecessary copies (to safe memory space and time). Instead, the "user" (i.e. application developer) is responsible to ensure proper storage of image data.
来源:https://stackoverflow.com/questions/45524596/creating-argb-qimage-from-uint32-or-uchar-array