Fix several issues decoding TGA files

This commit is contained in:
David Capello 2019-03-25 23:48:21 -03:00
parent 9a75d01efe
commit 9e7e2767c8

View File

@ -59,12 +59,10 @@ FileFormat* CreateTgaFormat()
return new TgaFormat; return new TgaFormat;
} }
/* rle_tga_read: static void rle_tga_read8(FILE* f, uint8_t* address, int w, int type)
* Helper for reading 256 color RLE data from TGA files.
*/
static void rle_tga_read(unsigned char *address, int w, int type, FILE *f)
{ {
unsigned char value; uint8_t* end = address + (w * (type == 1 ? 1: 2));
uint8_t value;
int count, g; int count, g;
int c = 0; int c = 0;
@ -75,11 +73,14 @@ static void rle_tga_read(unsigned char *address, int w, int type, FILE *f)
c += count; c += count;
value = fgetc(f); value = fgetc(f);
while (count--) { while (count--) {
if (type == 1) if (type == 1) {
*(address++) = value; if (address+1 < end)
*(address++) = value;
}
else { else {
*((uint16_t*)address) = value; if (address+2 < end)
address += sizeof(uint16_t); *((uint16_t*)address) = graya(value, 255);
address += 2;
} }
} }
} }
@ -87,24 +88,25 @@ static void rle_tga_read(unsigned char *address, int w, int type, FILE *f)
count++; count++;
c += count; c += count;
if (type == 1) { if (type == 1) {
fread(address, 1, count, f); if (address+count < end)
fread(address, 1, count, f);
address += count; address += count;
} }
else { else {
for (g=0; g<count; g++) { for (g=0; g<count; g++) {
*((uint16_t*)address) = fgetc(f); int c = fgetc(f);
address += sizeof(uint16_t); if (address+2 < end)
*((uint16_t*)address) = graya(c, 255);
address += 2;
} }
} }
} }
} while (c < w); } while (c < w);
} }
/* rle_tga_read32: static void rle_tga_read32(FILE* f, uint32_t* address, int w, bool withAlpha)
* Helper for reading 32 bit RLE data from TGA files.
*/
static void rle_tga_read32(uint32_t* address, int w, FILE *f)
{ {
uint32_t* end = address + w;
unsigned char value[4]; unsigned char value[4];
int count; int count;
int c = 0; int c = 0;
@ -115,25 +117,28 @@ static void rle_tga_read32(uint32_t* address, int w, FILE *f)
count = (count & 0x7F) + 1; count = (count & 0x7F) + 1;
c += count; c += count;
fread(value, 1, 4, f); fread(value, 1, 4, f);
while (count--) while (count--) {
*(address++) = rgba(value[2], value[1], value[0], value[3]); if (address+1 < end)
*(address++) = rgba(value[2], value[1], value[0],
withAlpha ? value[3]: 255);
}
} }
else { else {
count++; count++;
c += count; c += count;
while (count--) { while (count--) {
fread(value, 1, 4, f); fread(value, 1, 4, f);
*(address++) = rgba(value[2], value[1], value[0], value[3]); if (address+1 < end)
*(address++) = rgba(value[2], value[1], value[0],
withAlpha ? value[3]: 255);
} }
} }
} while (c < w); } while (c < w);
} }
/* rle_tga_read24: static void rle_tga_read24(FILE* f, uint32_t* address, int w)
* Helper for reading 24 bit RLE data from TGA files.
*/
static void rle_tga_read24(uint32_t* address, int w, FILE *f)
{ {
uint32_t* end = address + w;
unsigned char value[4]; unsigned char value[4];
int count; int count;
int c = 0; int c = 0;
@ -144,25 +149,26 @@ static void rle_tga_read24(uint32_t* address, int w, FILE *f)
count = (count & 0x7F) + 1; count = (count & 0x7F) + 1;
c += count; c += count;
fread(value, 1, 3, f); fread(value, 1, 3, f);
while (count--) while (count--) {
*(address++) = rgba(value[2], value[1], value[0], 255); if (address+1 < end)
*(address++) = rgba(value[2], value[1], value[0], 255);
}
} }
else { else {
count++; count++;
c += count; c += count;
while (count--) { while (count--) {
fread(value, 1, 3, f); fread(value, 1, 3, f);
*(address++) = rgba(value[2], value[1], value[0], 255); if (address+1 < end)
*(address++) = rgba(value[2], value[1], value[0], 255);
} }
} }
} while (c < w); } while (c < w);
} }
/* rle_tga_read16:
* Helper for reading 16 bit RLE data from TGA files.
*/
static void rle_tga_read16(uint32_t* address, int w, FILE *f) static void rle_tga_read16(uint32_t* address, int w, FILE *f)
{ {
uint32_t* end = address + w;
unsigned int value; unsigned int value;
uint32_t color; uint32_t color;
int count; int count;
@ -174,22 +180,26 @@ static void rle_tga_read16(uint32_t* address, int w, FILE *f)
count = (count & 0x7F) + 1; count = (count & 0x7F) + 1;
c += count; c += count;
value = fgetw(f); value = fgetw(f);
color = rgba(scale_5bits_to_8bits(((value >> 10) & 0x1F)), color = rgba(scale_5bits_to_8bits((value >> 10) & 0x1F),
scale_5bits_to_8bits(((value >> 5) & 0x1F)), scale_5bits_to_8bits((value >> 5) & 0x1F),
scale_5bits_to_8bits((value & 0x1F)), 255); scale_5bits_to_8bits(value & 0x1F), 255);
while (count--) while (count--) {
*(address++) = color; if (address+1 < end)
*(address++) = color;
}
} }
else { else {
count++; count++;
c += count; c += count;
while (count--) { while (count--) {
value = fgetw(f); value = fgetw(f);
color = rgba(scale_5bits_to_8bits(((value >> 10) & 0x1F)), color = rgba(scale_5bits_to_8bits((value >> 10) & 0x1F),
scale_5bits_to_8bits(((value >> 5) & 0x1F)), scale_5bits_to_8bits((value >> 5) & 0x1F),
scale_5bits_to_8bits((value & 0x1F)), 255); scale_5bits_to_8bits(value & 0x1F), 255);
*(address++) = color;
if (address+1 < end)
*(address++) = color;
} }
} }
} while (c < w); } while (c < w);
@ -232,9 +242,9 @@ bool TgaFormat::onLoad(FileOp* fop)
case 16: case 16:
c = fgetw(f); c = fgetw(f);
image_palette[i][0] = (c & 0x1F) << 3; image_palette[i][0] = scale_5bits_to_8bits(c & 0x1F);
image_palette[i][1] = ((c >> 5) & 0x1F) << 3; image_palette[i][1] = scale_5bits_to_8bits((c >> 5) & 0x1F);
image_palette[i][2] = ((c >> 10) & 0x1F) << 3; image_palette[i][2] = scale_5bits_to_8bits((c >> 10) & 0x1F);
break; break;
case 24: case 24:
@ -265,6 +275,7 @@ bool TgaFormat::onLoad(FileOp* fop)
image_type &= 7; image_type &= 7;
PixelFormat pixelFormat; PixelFormat pixelFormat;
bool withAlpha = false;
switch (image_type) { switch (image_type) {
@ -291,7 +302,8 @@ bool TgaFormat::onLoad(FileOp* fop)
(bpp != 24) && (bpp != 32))) { (bpp != 24) && (bpp != 32))) {
return false; return false;
} }
if ((descriptor_bits & 0xf) == 8) withAlpha = ((descriptor_bits & 0xf) == 8);
if (withAlpha)
fop->sequenceSetHasAlpha(true); fop->sequenceSetHasAlpha(true);
pixelFormat = IMAGE_RGB; pixelFormat = IMAGE_RGB;
break; break;
@ -325,7 +337,7 @@ bool TgaFormat::onLoad(FileOp* fop)
case 1: case 1:
case 3: case 3:
if (compressed) if (compressed)
rle_tga_read(image->getPixelAddress(0, yc), image_width, image_type, f); rle_tga_read8(f, image->getPixelAddress(0, yc), image_width, image_type);
else if (image_type == 1) else if (image_type == 1)
fread(image->getPixelAddress(0, yc), 1, image_width, f); fread(image->getPixelAddress(0, yc), 1, image_width, f);
else { else {
@ -337,18 +349,21 @@ bool TgaFormat::onLoad(FileOp* fop)
case 2: case 2:
if (bpp == 32) { if (bpp == 32) {
if (compressed) { if (compressed) {
rle_tga_read32((uint32_t*)image->getPixelAddress(0, yc), image_width, f); rle_tga_read32(f, (uint32_t*)image->getPixelAddress(0, yc), image_width,
withAlpha);
} }
else { else {
for (x=0; x<image_width; x++) { for (x=0; x<image_width; x++) {
fread(rgb, 1, 4, f); fread(rgb, 1, 4, f);
put_pixel_fast<RgbTraits>(image, x, yc, rgba(rgb[2], rgb[1], rgb[0], rgb[3])); put_pixel_fast<RgbTraits>(image, x, yc,
rgba(rgb[2], rgb[1], rgb[0],
withAlpha ? rgb[3]: 255));
} }
} }
} }
else if (bpp == 24) { else if (bpp == 24) {
if (compressed) { if (compressed) {
rle_tga_read24((uint32_t*)image->getPixelAddress(0, yc), image_width, f); rle_tga_read24(f, (uint32_t*)image->getPixelAddress(0, yc), image_width);
} }
else { else {
for (x=0; x<image_width; x++) { for (x=0; x<image_width; x++) {
@ -364,9 +379,10 @@ bool TgaFormat::onLoad(FileOp* fop)
else { else {
for (x=0; x<image_width; x++) { for (x=0; x<image_width; x++) {
c = fgetw(f); c = fgetw(f);
put_pixel_fast<RgbTraits>(image, x, yc, rgba(((c >> 10) & 0x1F), put_pixel_fast<RgbTraits>(
((c >> 5) & 0x1F), image, x, yc, rgba(scale_5bits_to_8bits((c >> 10) & 0x1F),
(c & 0x1F), 255)); scale_5bits_to_8bits((c >> 5) & 0x1F),
scale_5bits_to_8bits(c & 0x1F), 255));
} }
} }
} }
@ -419,7 +435,8 @@ bool TgaFormat::onSave(FileOp* fop)
fputc(depth, f); /* bits per pixel */ fputc(depth, f); /* bits per pixel */
/* descriptor (bottom to top, 8-bit alpha) */ /* descriptor (bottom to top, 8-bit alpha) */
fputc(image->pixelFormat() == IMAGE_RGB && !fop->document()->sprite()->isOpaque()? 8: 0, f); fputc(image->pixelFormat() == IMAGE_RGB &&
!fop->document()->sprite()->isOpaque() ? 8: 0, f);
if (need_pal) { if (need_pal) {
for (y=0; y<256; y++) { for (y=0; y<256; y++) {