pnmimage: Support reading of .bmp files with RLE8 compression

This commit is contained in:
rdb 2023-10-11 15:53:57 +02:00
parent d5263b597b
commit 79a60688ce
3 changed files with 82 additions and 47 deletions

View File

@ -92,19 +92,7 @@ BMPlenrgbtable(int classv, unsigned long bitcount)
pm_error(er_internal, "BMPlenrgbtable"); pm_error(er_internal, "BMPlenrgbtable");
return 0; return 0;
} }
switch (classv) lenrgb = (classv == C_OS2) ? 3 : 4;
{
case C_WIN:
lenrgb = 4;
break;
case C_OS2:
lenrgb = 3;
break;
default:
pm_error(er_internal, "BMPlenrgbtable");
return 0;
}
return (1 << bitcount) * lenrgb; return (1 << bitcount) * lenrgb;
} }

View File

@ -55,6 +55,7 @@ public:
unsigned long offBits; unsigned long offBits;
unsigned short cBitCount; unsigned short cBitCount;
unsigned short cCompression;
int indexed; int indexed;
int classv; int classv;

View File

@ -177,6 +177,7 @@ BMPreadinfoheader(
unsigned long *pcx, unsigned long *pcx,
unsigned long *pcy, unsigned long *pcy,
unsigned short *pcBitCount, unsigned short *pcBitCount,
unsigned short *pcCompression,
int *pclassv) int *pclassv)
{ {
unsigned long cbFix; unsigned long cbFix;
@ -185,6 +186,7 @@ BMPreadinfoheader(
unsigned long cx = 0; unsigned long cx = 0;
unsigned long cy = 0; unsigned long cy = 0;
unsigned short cBitCount = 0; unsigned short cBitCount = 0;
unsigned long cCompression = 0;
int classv = 0; int classv = 0;
cbFix = GetLong(fp); cbFix = GetLong(fp);
@ -229,7 +231,9 @@ BMPreadinfoheader(
* for the required total. * for the required total.
*/ */
if (classv != C_OS2) { if (classv != C_OS2) {
for (int i = 0; i < (int)cbFix - 16; i += 4) { cCompression = GetLong(fp);
for (int i = 0; i < (int)cbFix - 20; i += 4) {
GetLong(fp); GetLong(fp);
} }
} }
@ -273,11 +277,13 @@ BMPreadinfoheader(
pm_message("cy: %d", cy); pm_message("cy: %d", cy);
pm_message("cPlanes: %d", cPlanes); pm_message("cPlanes: %d", cPlanes);
pm_message("cBitCount: %d", cBitCount); pm_message("cBitCount: %d", cBitCount);
pm_message("cCompression: %d", cCompression);
#endif #endif
*pcx = cx; *pcx = cx;
*pcy = cy; *pcy = cy;
*pcBitCount = cBitCount; *pcBitCount = cBitCount;
*pcCompression = cCompression;
*pclassv = classv; *pclassv = classv;
*ppos += cbFix; *ppos += cbFix;
@ -401,45 +407,84 @@ BMPreadbits(xel *array, xelval *alpha_array,
unsigned long cx, unsigned long cx,
unsigned long cy, unsigned long cy,
unsigned short cBitCount, unsigned short cBitCount,
int /* classv */, unsigned long cCompression,
int indexed, int indexed,
pixval *R, pixval *R,
pixval *G, pixval *G,
pixval *B) pixval *B)
{ {
long y; long y;
readto(fp, ppos, offBits); readto(fp, ppos, offBits);
if(cBitCount > 24 && cBitCount != 32) if (cBitCount > 24 && cBitCount != 32) {
{ pm_error("%s: cannot handle cBitCount: %d", ifname, cBitCount);
pm_error("%s: cannot handle cBitCount: %d" }
,ifname
,cBitCount); if (cCompression == 1) {
// RLE8 compression
xel *row = array + (cy - 1) * cx;
xel *p = row;
unsigned long nbyte = 0;
while (true) {
int first = GetByte(fp);
int second = GetByte(fp);
nbyte += 2;
if (first != 0) {
// Repeated index.
for (int i = 0; i < first; ++i) {
PPM_ASSIGN(*p, R[second], G[second], B[second]);
++p;
} }
}
/* else if (second == 0) {
* The picture is stored bottom line first, top line last // End of line.
*/ row -= cx;
p = row;
for (y = (long)cy - 1; y >= 0; y--) }
{ else if (second == 1) {
int rc; // End of image.
rc = BMPreadrow(fp, ppos, array + y*cx, alpha_array + y*cx, cx, cBitCount, indexed, R, G, B); break;
if(rc == -1) }
{ else if (second == 2) {
pm_error("%s: couldn't read row %d" // Delta.
,ifname int xoffset = GetByte(fp);
,y); int yoffset = GetByte(fp);
} nbyte += 2;
if(rc%4) row -= cx * yoffset;
{ p += xoffset - cx * yoffset;
pm_error("%s: row had bad number of bytes: %d" }
,ifname else {
,rc); // Absolute run.
} for (int i = 0; i < second; ++i) {
int v = GetByte(fp);
++nbyte;
PPM_ASSIGN(*p, R[v], G[v], B[v]);
++p;
} }
nbyte += second;
if (second % 2) {
// Pad to 16-bit boundary.
GetByte(fp);
++nbyte;
}
}
}
*ppos += nbyte;
}
else {
// The picture is stored bottom line first, top line last
for (y = (long)cy - 1; y >= 0; y--) {
int rc = BMPreadrow(fp, ppos, array + y*cx, alpha_array + y*cx, cx, cBitCount, indexed, R, G, B);
if (rc == -1) {
pm_error("%s: couldn't read row %d", ifname, y);
}
if (rc % 4) {
pm_error("%s: row had bad number of bytes: %d", ifname, rc);
}
}
}
} }
/** /**
@ -474,7 +519,7 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
pos = 0; pos = 0;
BMPreadfileheader(file, &pos, &offBits); BMPreadfileheader(file, &pos, &offBits);
BMPreadinfoheader(file, &pos, &cx, &cy, &cBitCount, &classv); BMPreadinfoheader(file, &pos, &cx, &cy, &cBitCount, &cCompression, &classv);
if (offBits != BMPoffbits(classv, cBitCount)) { if (offBits != BMPoffbits(classv, cBitCount)) {
pnmimage_bmp_cat.warning() pnmimage_bmp_cat.warning()
@ -523,9 +568,10 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
int PNMFileTypeBMP::Reader:: int PNMFileTypeBMP::Reader::
read_data(xel *array, xelval *alpha_array) { read_data(xel *array, xelval *alpha_array) {
BMPreadbits(array, alpha_array, _file, &pos, offBits, _x_size, _y_size, BMPreadbits(array, alpha_array, _file, &pos, offBits, _x_size, _y_size,
cBitCount, classv, indexed, R, G, B); cBitCount, cCompression, indexed, R, G, B);
if (pos != BMPlenfile(classv, cBitCount, _x_size, _y_size)) { if (cCompression != 1 &&
pos != BMPlenfile(classv, cBitCount, _x_size, _y_size)) {
pnmimage_bmp_cat.warning() pnmimage_bmp_cat.warning()
<< "Read " << pos << " bytes, expected to read " << "Read " << pos << " bytes, expected to read "
<< BMPlenfile(classv, cBitCount, _x_size, _y_size) << " bytes\n"; << BMPlenfile(classv, cBitCount, _x_size, _y_size) << " bytes\n";