Создание ARGB QImage из массива uint32 или uchar

Когда я пытаюсь создать ARGB32 QImage из reinterpret_cast<uchar*>(quint32*) используя конструктор QImage, изображение теряет свой цвет и альфа-канал, и в результате получается QImage в градациях серого!

Изображение в градациях серого отображается так, как ожидалось, если я пытался отобразить его в градациях серого. Итак, я знаю, что масштабирование и индексирование коротких данных в массиве quint32 прошло хорошо, но что не так?

Сообщение на форуме Qt предложил сделать это так, как я это делаю (насколько я вижу), но, возможно, поведение изменилось с той версии Qt? (Я использую Qt 5.9)

Я понимаю, что в документации сказано:

данные должны быть выровнены на 32 бита, и каждая строка данных в изображении
также должен быть выровнен 32-разрядным.

Но я ожидаю, что quint32 будет выровнен 32-битным даже после reinterpret_cast<uchar*>()?

Теперь подробности:
Я преобразовываю результаты вычисления (массив с короткими значениями без знака) в полупрозрачное изображение сине-зеленого-красного, как это:

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)
);
}

куда val двойное значение между 0 и 1, масштабированное по данным ushort.
каждый QRgb значение хранится в соответствующем индексе quint32 массив, как это:

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);

куда (m_pData[i*m_iWidth + j] - lowVal) / (double)winWidthэто метод масштабирования от короткого до двойного.
Это делается в цикле for.

в заключение Я пытаюсь построить изображение с:

QImage tmpQImage = QImage(reinterpret_cast<unsigned char*>(tmpData), m_iWidth, m_iHeight, QImage::Format_ARGB32);

Но это не работает, как я ожидаю, потому что tmpQImage.allGray() возвращает true при вызове сразу после!

Что я делаю не так, и что я должен сделать вместо этого, чтобы создать изображение ARGB и сохранить и цвета, и альфа-канал?

0

Решение

Я пытался воспроизвести вашу проблему, но не смог.

Либо фактическая проблема OP не является частью представленного кода, либо я случайно пропустил детали, когда попытался сформировать MCVE из ОП.

Тем не менее, я хочу представить, что я получил, насколько это может быть полезно для исправления ОП.

Мой источник 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();
}

Я скомпилировал и протестировал его с VS2013, Qt5.6 на Windows 10 (64 бит):

Снимок testQImageGrayToRGB

Заметки:

  1. val_to_ функции сделали меня немного подозрительным: выражение, приведенное к (uchar)затем добавлен постоянный член (который определенно не вписывается в (uchar)результат возвращается как uchar
    Хм …
    Поэтому я их переделал — с небольшой уборкой.
    Фактически, визуальное сравнение показывает, что различия почти не видны (за исключением красной линии в желтой области).

  2. У меня не было проблем, чтобы сделать QImage из сырого quint32 массив (включая приведение кuchar*-hack).


Обновить:

Может быть, это не очевидно: пример кода тщательно спроектирован для предоставления времени жизни буферных данных (std::vector<quint32> dataAGA а также std::vector<quint32> dataDS) дольше, чем время жизни изображений Qt (QImage qImgAGA а также QImage qImgDS). Это было сделано в соответствии с Qt doc. за QImage::QImage():

Буфер должен оставаться действительным в течение всего срока действия QImage и все копии, которые не были изменены или иным образом отделены от исходного буфера. Изображение не удаляет буфер при уничтожении. Вы можете предоставить указатель на функцию cleanupFunction вместе с дополнительным указателем cleanupInfo это будет вызвано, когда последняя копия будет уничтожена.

Данные изображения могут занимать значительное количество памяти. Таким образом QImage Реализация пытается предотвратить ненужные копии (чтобы сохранить пространство памяти и время). Вместо этого «пользователь» (то есть разработчик приложения) отвечает за обеспечение надлежащего хранения данных изображения.

2

Другие решения

Других решений пока нет …