Fixed decoding 4-bit RLE encoded BMP files

Also flipped the return value of readRlePixels() to match convention.

Fixes https://github.com/libsdl-org/sdl2-compat/issues/308
This commit is contained in:
Sam Lantinga 2025-02-03 19:34:38 -08:00
parent 8ccf85c59e
commit 07c22da464
1 changed files with 31 additions and 56 deletions

View File

@ -76,16 +76,16 @@ static bool readRlePixels(SDL_Surface *surface, SDL_IOStream *src, int isRle8)
int ofs = 0; int ofs = 0;
Uint8 ch; Uint8 ch;
Uint8 needsPad; Uint8 needsPad;
const int pixels_per_byte = (isRle8 ? 1 : 2);
#define COPY_PIXEL(x) \ #define COPY_PIXEL(x) \
spot = &bits[ofs++]; \ spot = &bits[ofs++]; \
if (spot >= start && spot < end) \ if (spot >= start && spot < end) \
*spot = (x) *spot = (x)
// !!! FIXME: for all these reads, handle error vs eof? handle -2 if non-blocking?
for (;;) { for (;;) {
if (!SDL_ReadU8(src, &ch)) { if (!SDL_ReadU8(src, &ch)) {
return true; return false;
} }
/* /*
| encoded mode starts with a run length, and then a byte | encoded mode starts with a run length, and then a byte
@ -94,26 +94,12 @@ static bool readRlePixels(SDL_Surface *surface, SDL_IOStream *src, int isRle8)
if (ch) { if (ch) {
Uint8 pixel; Uint8 pixel;
if (!SDL_ReadU8(src, &pixel)) { if (!SDL_ReadU8(src, &pixel)) {
return true; return false;
}
if (isRle8) { // 256-color bitmap, compressed
do {
COPY_PIXEL(pixel);
} while (--ch);
} else { // 16-color bitmap, compressed
Uint8 pixel0 = pixel >> 4;
Uint8 pixel1 = pixel & 0x0F;
for (;;) {
COPY_PIXEL(pixel0); // even count, high nibble
if (!--ch) {
break;
}
COPY_PIXEL(pixel1); // odd count, low nibble
if (!--ch) {
break;
}
}
} }
ch /= pixels_per_byte;
do {
COPY_PIXEL(pixel);
} while (--ch);
} else { } else {
/* /*
| A leading zero is an escape; it may signal the end of the bitmap, | A leading zero is an escape; it may signal the end of the bitmap,
@ -121,7 +107,7 @@ static bool readRlePixels(SDL_Surface *surface, SDL_IOStream *src, int isRle8)
| zero tag may be absolute mode or an escape | zero tag may be absolute mode or an escape
*/ */
if (!SDL_ReadU8(src, &ch)) { if (!SDL_ReadU8(src, &ch)) {
return true; return false;
} }
switch (ch) { switch (ch) {
case 0: // end of line case 0: // end of line
@ -129,47 +115,33 @@ static bool readRlePixels(SDL_Surface *surface, SDL_IOStream *src, int isRle8)
bits -= pitch; // go to previous bits -= pitch; // go to previous
break; break;
case 1: // end of bitmap case 1: // end of bitmap
return false; // success! printf("SUCCESS!\n");
return true; // success!
case 2: // delta case 2: // delta
if (!SDL_ReadU8(src, &ch)) { if (!SDL_ReadU8(src, &ch)) {
return true; return false;
} }
ofs += ch; ofs += ch / pixels_per_byte;
if (!SDL_ReadU8(src, &ch)) { if (!SDL_ReadU8(src, &ch)) {
return true; return false;
} }
bits -= (ch * pitch); bits -= ((ch / pixels_per_byte) * pitch);
break; break;
default: // no compression default: // no compression
if (isRle8) { ch /= pixels_per_byte;
needsPad = (ch & 1); needsPad = (ch & 1);
do { do {
Uint8 pixel; Uint8 pixel;
if (!SDL_ReadU8(src, &pixel)) { if (!SDL_ReadU8(src, &pixel)) {
return true; return false;
}
COPY_PIXEL(pixel);
} while (--ch);
} else {
needsPad = (((ch + 1) >> 1) & 1); // (ch+1)>>1: bytes size
for (;;) {
Uint8 pixel;
if (!SDL_ReadU8(src, &pixel)) {
return true;
}
COPY_PIXEL(pixel >> 4);
if (!--ch) {
break;
}
COPY_PIXEL(pixel & 0x0F);
if (!--ch) {
break;
}
} }
} COPY_PIXEL(pixel);
} while (--ch);
// pad at even boundary // pad at even boundary
if (needsPad && !SDL_ReadU8(src, &ch)) { if (needsPad && !SDL_ReadU8(src, &ch)) {
return true; return false;
} }
break; break;
} }
@ -513,10 +485,13 @@ SDL_Surface *SDL_LoadBMP_IO(SDL_IOStream *src, bool closeio)
goto done; goto done;
} }
if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) { if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
was_error = readRlePixels(surface, src, biCompression == BI_RLE8); if (!readRlePixels(surface, src, biCompression == BI_RLE8)) {
if (was_error) {
SDL_SetError("Error reading from datastream"); SDL_SetError("Error reading from datastream");
goto done;
} }
// Success!
was_error = false;
goto done; goto done;
} }
top = (Uint8 *)surface->pixels; top = (Uint8 *)surface->pixels;