ppm/pgm image formats: fix reading 16bit and limited range
The color values of ppm and pgm images can be either 8 or 16 bits. They can also be scaled to a smaller max value, and they can be expressed either binary or ascii. For some of these permutations, Qt's image handler lacked implementation or would decode the wrong color value. This commit fixes that. Task-number: QTBUG-18262 Task-number: QTBUG-35990 Change-Id: I7cf11c2366244f3a9b31c1a565a81e2658bc6a51 Reviewed-by: Lars Knoll <lars.knoll@qt.io>bb10
parent
593a707ba3
commit
c4c8886a86
|
|
@ -45,6 +45,7 @@
|
|||
#include <qvariant.h>
|
||||
#include <qvector.h>
|
||||
#include <ctype.h>
|
||||
#include <qrgba64.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -120,6 +121,11 @@ static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int&
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline QRgb scale_pbm_color(quint16 mx, quint16 rv, quint16 gv, quint16 bv)
|
||||
{
|
||||
return QRgba64::fromRgba64((rv * 0xffff) / mx, (gv * 0xffff) / mx, (bv * 0xffff) / mx, 0xffff).toArgb32();
|
||||
}
|
||||
|
||||
static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
|
||||
{
|
||||
int nbits, y;
|
||||
|
|
@ -148,9 +154,6 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
|
|||
}
|
||||
raw = type >= '4';
|
||||
|
||||
int maxc = mcc;
|
||||
if (maxc > 255)
|
||||
maxc = 255;
|
||||
if (outImage->size() != QSize(w, h) || outImage->format() != format) {
|
||||
*outImage = QImage(w, h, format);
|
||||
if (outImage->isNull())
|
||||
|
|
@ -175,22 +178,50 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
|
|||
b = buf24;
|
||||
while (p < end) {
|
||||
if (mcc < 256) {
|
||||
*p++ = qRgb(b[0],b[1],b[2]);
|
||||
if (mcc == 255)
|
||||
*p++ = qRgb(b[0],b[1],b[2]);
|
||||
else
|
||||
*p++ = scale_pbm_color(mcc, b[0], b[1], b[2]);
|
||||
b += 3;
|
||||
} else {
|
||||
*p++ = qRgb(((int(b[0]) * 256 + int(b[1]) + 1) * 256) / (mcc + 1) - 1,
|
||||
((int(b[2]) * 256 + int(b[3]) + 1) * 256) / (mcc + 1) - 1,
|
||||
((int(b[4]) * 256 + int(b[5]) + 1) * 256) / (mcc + 1) - 1);
|
||||
quint16 rv = b[0] << 8 | b[1];
|
||||
quint16 gv = b[2] << 8 | b[3];
|
||||
quint16 bv = b[4] << 8 | b[5];
|
||||
if (mcc == 0xffff)
|
||||
*p++ = QRgba64::fromRgba64(rv, gv, bv, 0xffff).toArgb32();
|
||||
else
|
||||
*p++ = scale_pbm_color(mcc, rv, gv, bv);
|
||||
b += 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] buf24;
|
||||
} else if (nbits == 8 && mcc > 255) { // type 5 16bit
|
||||
pbm_bpl = 2*w;
|
||||
uchar *buf16 = new uchar[pbm_bpl];
|
||||
for (y=0; y<h; y++) {
|
||||
if (device->read((char *)buf16, pbm_bpl) != pbm_bpl) {
|
||||
delete[] buf16;
|
||||
return false;
|
||||
}
|
||||
uchar *p = outImage->scanLine(y);
|
||||
uchar *end = p + w;
|
||||
uchar *b = buf16;
|
||||
while (p < end) {
|
||||
*p++ = (b[0] << 8 | b[1]) * 255 / mcc;
|
||||
b += 2;
|
||||
}
|
||||
}
|
||||
delete[] buf16;
|
||||
} else { // type 4,5
|
||||
for (y=0; y<h; y++) {
|
||||
if (device->read((char *)outImage->scanLine(y), pbm_bpl)
|
||||
!= pbm_bpl)
|
||||
uchar *p = outImage->scanLine(y);
|
||||
if (device->read((char *)p, pbm_bpl) != pbm_bpl)
|
||||
return false;
|
||||
if (nbits == 8 && mcc < 255) {
|
||||
for (int i = 0; i < pbm_bpl; i++)
|
||||
p[i] = (p[i] * 255) / mcc;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // read ascii data
|
||||
|
|
@ -227,7 +258,7 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
|
|||
} else { // 32 bits
|
||||
n /= 4;
|
||||
int r, g, b;
|
||||
if (mcc == maxc) {
|
||||
if (mcc == 255) {
|
||||
while (n--) {
|
||||
r = read_pbm_int(device);
|
||||
g = read_pbm_int(device);
|
||||
|
|
@ -237,10 +268,10 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
|
|||
}
|
||||
} else {
|
||||
while (n--) {
|
||||
r = read_pbm_int(device) * maxc / mcc;
|
||||
g = read_pbm_int(device) * maxc / mcc;
|
||||
b = read_pbm_int(device) * maxc / mcc;
|
||||
*((QRgb*)p) = qRgb(r, g, b);
|
||||
r = read_pbm_int(device);
|
||||
g = read_pbm_int(device);
|
||||
b = read_pbm_int(device);
|
||||
*((QRgb*)p) = scale_pbm_color(mcc, r, g, b);
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,9 @@ private slots:
|
|||
void gifImageCount();
|
||||
void gifLoopCount();
|
||||
|
||||
void ppmMaxval_data();
|
||||
void ppmMaxval();
|
||||
|
||||
void readCorruptImage_data();
|
||||
void readCorruptImage();
|
||||
void readCorruptBmp();
|
||||
|
|
@ -956,6 +959,79 @@ void tst_QImageReader::gifLoopCount()
|
|||
}
|
||||
}
|
||||
|
||||
void tst_QImageReader::ppmMaxval_data()
|
||||
{
|
||||
QTest::addColumn<bool>("hasColor");
|
||||
QTest::addColumn<QByteArray>("bytes");
|
||||
|
||||
QTest::newRow("PGM plain 8bit full") << false << QByteArray("P2 3 1 255 255 0 127\n");
|
||||
QTest::newRow("PGM plain 8bit lim.") << false << QByteArray("P2 3 1 50 50 0 25\n");
|
||||
QTest::newRow("PGM plain 16bit full") << false << QByteArray("P2 3 1 65535 65535 0 32767\n");
|
||||
QTest::newRow("PGM plain 16bit lim.") << false << QByteArray("P2 3 1 5000 5000 0 2500\n");
|
||||
QTest::newRow("PGM raw 8bit full") << false << QByteArray("P5 3 1 255 \xff\x00\x7f", 13 + 3);
|
||||
QTest::newRow("PGM raw 8bit lim.") << false << QByteArray("P5 3 1 50 \x32\x00\x19", 13 + 3);
|
||||
QTest::newRow("PGM raw 16bit full") << false << QByteArray("P5 3 1 65535 \xff\xff\x00\x00\x7f\xff", 13 + 3 * 2);
|
||||
QTest::newRow("PGM raw 16bit lim.") << false << QByteArray("P5 3 1 5000 \x13\x88\x00\x00\x09\xc4", 13 + 3 * 2);
|
||||
|
||||
QTest::newRow("PPM plain 8bit full") << true << QByteArray("P3 3 2 255 "
|
||||
"255 255 255 0 0 0 127 127 127 "
|
||||
"255 0 0 0 255 0 0 0 255\n");
|
||||
|
||||
QTest::newRow("PPM plain 8bit lim.") << true << QByteArray("P3 3 2 50 "
|
||||
" 50 50 50 0 0 0 25 25 25 "
|
||||
" 50 0 0 0 50 0 0 0 50\n");
|
||||
|
||||
QTest::newRow("PPM plain 16bit full") << true << QByteArray("P3 3 2 65535 "
|
||||
"65535 65535 65535 0 0 0 32767 32767 32767 "
|
||||
"65535 0 0 0 65535 0 0 0 65535\n");
|
||||
|
||||
QTest::newRow("PPM plain 16bit lim.") << true << QByteArray("P3 3 2 5000 "
|
||||
" 5000 5000 5000 0 0 0 2500 2500 2500 "
|
||||
" 5000 0 0 0 5000 0 0 0 5000\n");
|
||||
|
||||
QTest::newRow("PPM raw 8bit full") << true << QByteArray("P6 3 2 255 "
|
||||
"\xff\xff\xff\x00\x00\x00\x7f\x7f\x7f"
|
||||
"\xff\x00\x00\x00\xff\x00\x00\x00\xff", 13 + 6 * 3);
|
||||
|
||||
QTest::newRow("PPM raw 8bit lim.") << true << QByteArray("P6 3 2 50 "
|
||||
"\x32\x32\x32\x00\x00\x00\x19\x19\x19"
|
||||
"\x32\x00\x00\x00\x32\x00\x00\x00\x32", 13 + 6 * 3);
|
||||
|
||||
QTest::newRow("PPM raw 16bit full") << true << QByteArray("P6 3 2 65535 "
|
||||
"\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x7f\xff\x7f\xff\x7f\xff"
|
||||
"\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff", 13 + 6 * 3 * 2);
|
||||
|
||||
QTest::newRow("PPM raw 16bit lim.") << true << QByteArray("P6 3 2 5000 "
|
||||
"\x13\x88\x13\x88\x13\x88\x00\x00\x00\x00\x00\x00\x09\xc4\x09\xc4\x09\xc4"
|
||||
"\x13\x88\x00\x00\x00\x00\x00\x00\x13\x88\x00\x00\x00\x00\x00\x00\x13\x88", 13 + 6 * 3 * 2);
|
||||
}
|
||||
|
||||
void tst_QImageReader::ppmMaxval()
|
||||
{
|
||||
SKIP_IF_UNSUPPORTED("ppm");
|
||||
|
||||
QFETCH(bool, hasColor);
|
||||
QFETCH(QByteArray, bytes);
|
||||
|
||||
QImage img;
|
||||
img.loadFromData(bytes);
|
||||
QVERIFY(!img.isNull());
|
||||
QCOMPARE(img.width(), 3);
|
||||
QCOMPARE(img.height(), hasColor ? 2 : 1);
|
||||
|
||||
QCOMPARE(img.pixel(0,0), qRgb(0xff, 0xff, 0xff));
|
||||
QCOMPARE(img.pixel(1,0), qRgb(0, 0, 0));
|
||||
QRgb gray = img.pixel(2,0);
|
||||
QVERIFY(qIsGray(gray));
|
||||
QVERIFY(qRed(gray) > 0x70 && qRed(gray) < 0x90 );
|
||||
|
||||
if (hasColor) {
|
||||
QCOMPARE(img.pixel(0,1), qRgb(0xff, 0, 0));
|
||||
QCOMPARE(img.pixel(1,1), qRgb(0, 0xff, 0));
|
||||
QCOMPARE(img.pixel(2,1), qRgb(0, 0, 0xff));
|
||||
}
|
||||
}
|
||||
|
||||
class Server : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
|||
Loading…
Reference in New Issue