legacy-imlib2/src/modules/loaders/loader_tga.c

582 lines
17 KiB
C

/*
* loader_tga.c - Loader for Truevision Targa images
* for Imlib2
*
* by Dan Maas <dmaas@dcine.com> May 15, 2000
*
* based on TGA specifications available at:
* http://www.wotsit.org/cgi-bin/search.cgi?TGA
*
* header/footer structures courtesy of the GIMP Targa plugin
*/
#include "config.h"
#include "Imlib2_Loader.h"
#define DBG_PFX "LDR-tga"
static const char *const _formats[] = { "tga" };
/* flip an inverted image - see RLE reading below */
static void tgaflip(uint32_t * in, int w, int h, int fliph, int flipv);
/* TGA pixel formats */
#define TGA_TYPE_MAPPED 1
#define TGA_TYPE_COLOR 2
#define TGA_TYPE_GRAY 3
#define TGA_TYPE_MAPPED_RLE 9
#define TGA_TYPE_COLOR_RLE 10
#define TGA_TYPE_GRAY_RLE 11
/* TGA header flags */
#define TGA_DESC_ABITS 0x0f
#define TGA_DESC_HORIZONTAL 0x10
#define TGA_DESC_VERTICAL 0x20
static const char tga_signature[18] = "TRUEVISION-XFILE.";
typedef struct {
unsigned char idLength;
unsigned char colorMapType;
unsigned char imageType;
unsigned char colorMapIndexLo, colorMapIndexHi;
unsigned char colorMapLengthLo, colorMapLengthHi;
unsigned char colorMapSize;
unsigned char xOriginLo, xOriginHi;
unsigned char yOriginLo, yOriginHi;
unsigned char widthLo, widthHi;
unsigned char heightLo, heightHi;
unsigned char bpp;
unsigned char descriptor;
} tga_header;
typedef struct {
unsigned char extensionAreaOffset[4];
unsigned char developerDirectoryOffset[4];
char signature[18];
} tga_footer;
/* Load up a TGA file
*
* As written this function only recognizes the following types of Targas:
* Type 02 - Uncompressed RGB, 24 or 32 bits
* Type 03 - Uncompressed grayscale, 8 bits
* Type 10 - RLE-compressed RGB, 24 or 32 bits
* Type 11 - RLE-compressed grayscale, 8 bits
* There are several other (uncommon) Targa formats which this function can't currently handle
*/
static int
_load(ImlibImage *im, int load_data)
{
int rc;
const unsigned char *fptr;
const tga_header *header;
const tga_footer *footer;
int footer_present;
int rle, bpp, hasa, hasc, fliph, flipv;
unsigned long datasize;
const unsigned char *bufptr, *bufend, *palette;
uint32_t *imdata;
int palcnt = 0, palbpp = 0;
unsigned char a, r, g, b;
unsigned int pix16;
rc = LOAD_FAIL;
if (im->fi->fsize < (int)(sizeof(tga_header)) ||
(uintmax_t) im->fi->fsize > SIZE_MAX)
return rc;
fptr = im->fi->fdata;
header = im->fi->fdata;
if (im->fi->fsize > (int)(sizeof(tga_footer)))
{
footer =
(const tga_footer *)(fptr + im->fi->fsize - sizeof(tga_footer));
/* check the footer to see if we have a v2.0 TGA file */
footer_present = memcmp(footer->signature, tga_signature,
sizeof(footer->signature)) == 0;
}
else
{
footer_present = 0;
}
if ((size_t)im->fi->fsize < sizeof(tga_header) + header->idLength +
(footer_present ? sizeof(tga_footer) : 0))
goto quit;
/* skip over header */
fptr += sizeof(tga_header);
/* skip over alphanumeric ID field */
if (header->idLength)
fptr += header->idLength;
/* now parse the header */
/* this flag indicates right-to-left pixel storage */
fliph = !!(header->descriptor & TGA_DESC_HORIZONTAL);
/* this flag indicates bottom-up pixel storage */
flipv = !(header->descriptor & TGA_DESC_VERTICAL);
rle = 0; /* RLE compressed */
hasc = 0; /* Has color */
switch (header->imageType)
{
default:
goto quit;
case TGA_TYPE_MAPPED:
break;
case TGA_TYPE_COLOR:
hasc = 1;
break;
case TGA_TYPE_GRAY:
break;
case TGA_TYPE_MAPPED_RLE:
rle = 1;
break;
case TGA_TYPE_COLOR_RLE:
hasc = 1;
rle = 1;
break;
case TGA_TYPE_GRAY_RLE:
rle = 1;
break;
}
bpp = header->bpp; /* Bits per pixel */
hasa = 0; /* Has alpha */
switch (bpp)
{
default:
goto quit;
case 32:
if (header->descriptor & TGA_DESC_ABITS)
hasa = 1;
break;
case 24:
break;
case 16:
if (header->descriptor & TGA_DESC_ABITS)
hasa = 1;
break;
case 8:
break;
}
rc = LOAD_BADIMAGE; /* Format accepted */
/* endian-safe loading of 16-bit sizes */
im->w = (header->widthHi << 8) | header->widthLo;
im->h = (header->heightHi << 8) | header->heightLo;
D("Image info: type: %d bpp=%d desc=%04x\n",
header->imageType, header->bpp, header->descriptor);
if (!IMAGE_DIMENSIONS_OK(im->w, im->h))
goto quit;
im->has_alpha = hasa;
if (!load_data)
QUIT_WITH_RC(LOAD_SUCCESS);
/* find out how much data must be read from the file */
/* (this is NOT simply width*height*4, due to compression) */
datasize = im->fi->fsize - sizeof(tga_header) - header->idLength -
(footer_present ? sizeof(tga_footer) : 0);
palette = NULL;
if (header->imageType == TGA_TYPE_MAPPED ||
header->imageType == TGA_TYPE_MAPPED_RLE)
{
if (bpp != 8)
goto quit;
palette = fptr;
palcnt = (header->colorMapLengthHi << 8) | header->colorMapLengthLo;
palbpp = header->colorMapSize / 8; /* bytes per palette entry */
if (palbpp < 3 || palbpp > 4)
goto quit; /* only supporting 24/32bit palettes */
int palbytes = palcnt * palbpp;
fptr += palbytes;
datasize -= palbytes;
}
/* buffer is ready for parsing */
/* bufptr is the next byte to be read from the buffer */
bufptr = fptr;
bufend = bufptr + datasize;
/* Load data */
/* allocate the destination buffer */
if (!__imlib_AllocateData(im))
QUIT_WITH_RC(LOAD_OOM);
/* imdata is the next 32-bit pixel to be filled in */
imdata = im->data;
if (!rle)
{
int x, y;
/* decode uncompressed BGRA data */
for (y = 0; y < im->h; y++) /* for each row */
{
/* point imdata at the beginning of the row */
if (flipv)
/* some TGA's are stored upside-down! */
imdata = im->data + ((im->h - y - 1) * im->w);
else
imdata = im->data + (y * im->w);
for (x = 0; (x < im->w); x++) /* for each pixel in the row */
{
if (bufptr + bpp / 8 > bufend)
goto quit;
switch (bpp)
{
case 32: /* 32-bit BGRA pixels */
b = *bufptr++;
g = *bufptr++;
r = *bufptr++;
a = *bufptr++;
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
case 24: /* 24-bit BGR pixels */
b = *bufptr++;
g = *bufptr++;
r = *bufptr++;
a = 0xff;
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
case 16:
b = *bufptr++;
a = *bufptr++;
if (hasc)
{
pix16 = b | ((unsigned short)a << 8);
r = (pix16 >> 7) & 0xf8;
g = (pix16 >> 2) & 0xf8;
b = (pix16 << 3) & 0xf8;
a = (hasa && !(pix16 & 0x8000)) ? 0x00 : 0xff;
}
else
{
r = g = b;
}
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
case 8: /* 8-bit grayscale or palette */
b = *bufptr++;
a = 0xff;
if (palette)
{
if (b >= palcnt)
goto quit;
r = palette[b * palbpp + 2];
g = palette[b * palbpp + 1];
b = palette[b * palbpp + 0];
}
else
{
r = g = b;
}
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
}
} /* end for (each pixel) */
}
if (fliph)
tgaflip(im->data, im->w, im->h, fliph, 0);
}
else
{
/* decode RLE compressed data */
uint32_t *final_pixel = imdata + im->w * im->h;
/* loop until we've got all the pixels or run out of input */
while ((imdata < final_pixel))
{
int i, count;
unsigned char curbyte;
if ((bufptr + 1 + (bpp / 8)) > bufend)
goto quit;
curbyte = *bufptr++;
count = (curbyte & 0x7F) + 1;
if (curbyte & 0x80) /* RLE packet */
{
switch (bpp)
{
case 32:
b = *bufptr++;
g = *bufptr++;
r = *bufptr++;
a = *bufptr++;
for (i = 0; (i < count) && (imdata < final_pixel); i++)
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
case 24:
b = *bufptr++;
g = *bufptr++;
r = *bufptr++;
a = 0xff;
for (i = 0; (i < count) && (imdata < final_pixel); i++)
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
case 16:
b = *bufptr++;
a = *bufptr++;
if (hasc)
{
pix16 = b | ((unsigned short)a << 8);
r = (pix16 >> 7) & 0xf8;
g = (pix16 >> 2) & 0xf8;
b = (pix16 << 3) & 0xf8;
a = (hasa && !(pix16 & 0x8000)) ? 0x00 : 0xff;
}
else
{
r = g = b;
}
for (i = 0; (i < count) && (imdata < final_pixel); i++)
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
case 8:
b = *bufptr++;
a = 0xff;
if (palette)
{
if (b >= palcnt)
goto quit;
r = palette[b * palbpp + 2];
g = palette[b * palbpp + 1];
b = palette[b * palbpp + 0];
}
else
{
r = g = b;
}
for (i = 0; (i < count) && (imdata < final_pixel); i++)
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
}
} /* end if (RLE packet) */
else /* raw packet */
{
for (i = 0; (i < count) && (imdata < final_pixel); i++)
{
if ((bufptr + bpp / 8) > bufend)
goto quit;
switch (bpp)
{
case 32: /* 32-bit BGRA pixels */
b = *bufptr++;
g = *bufptr++;
r = *bufptr++;
a = *bufptr++;
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
case 24: /* 24-bit BGR pixels */
b = *bufptr++;
g = *bufptr++;
r = *bufptr++;
a = 0xff;
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
case 16:
b = *bufptr++;
a = *bufptr++;
if (hasc)
{
pix16 = b | ((unsigned short)a << 8);
r = (pix16 >> 7) & 0xf8;
g = (pix16 >> 2) & 0xf8;
b = (pix16 << 3) & 0xf8;
a = (hasa && !(pix16 & 0x8000)) ? 0x00 : 0xff;
}
else
{
r = g = b;
}
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
case 8: /* 8-bit grayscale or palette */
b = *bufptr++;
a = 0xff;
if (palette)
{
if (b >= palcnt)
goto quit;
r = palette[b * palbpp + 2];
g = palette[b * palbpp + 1];
b = palette[b * palbpp + 0];
}
else
{
r = g = b;
}
*imdata++ = PIXEL_ARGB(a, r, g, b);
break;
}
}
} /* end if (raw packet) */
} /* end for (each packet) */
if (fliph || flipv)
tgaflip(im->data, im->w, im->h, fliph, flipv);
}
if (im->lc)
__imlib_LoadProgressRows(im, 0, im->h);
rc = LOAD_SUCCESS;
quit:
return rc;
}
/* flip a uint32_t image block in place */
static void
tgaflip(uint32_t *in, int w, int h, int fliph, int flipv)
{
uint32_t tmp;
int x, y, x2, y2, dx, dy, nx, ny;
dx = fliph ? -1 : 1;
dy = flipv ? -1 : 1;
nx = fliph ? w / 2 : w;
ny = flipv && !fliph ? h / 2 : h;
y2 = flipv ? h - 1 : 0;
for (y = 0; y < ny; y++, y2 += dy)
{
x2 = fliph ? w - 1 : 0;
for (x = 0; x < nx; x++, x2 += dx)
{
tmp = in[y * w + x];
in[y * w + x] = in[y2 * w + x2];
in[y2 * w + x2] = tmp;
}
}
}
/*
* Write an uncompressed RGBA 24- or 32-bit targa to disk
* (If anyone wants to write a RLE saver, feel free =)
*/
static int
_save(ImlibImage *im)
{
int rc;
FILE *f = im->fi->fp;
const uint32_t *imdata;
unsigned char *buf, *bufptr;
int y;
tga_header header;
rc = LOAD_BADFILE;
/* assemble the TGA header information */
/* most entries are zero... */
memset(&header, 0x0, sizeof(header));
/* uncompressed RGB Targa identifier */
header.imageType = TGA_TYPE_COLOR;
/* image width, low byte */
header.widthLo = im->w & 0xFF;
/* image width, high byte */
header.widthHi = im->w >> 8;
/* image height, low byte */
header.heightLo = im->h & 0xFF;
/* image height, high byte */
header.heightHi = im->h >> 8;
/* total number of bits per pixel */
header.bpp = im->has_alpha ? 32 : 24;
/* number of extra (alpha) bits per pixel */
header.descriptor = im->has_alpha ? 8 : 0;
/* top-to-bottom storage */
header.descriptor |= TGA_DESC_VERTICAL;
/* allocate a buffer to receive the BGRA-swapped pixel values */
buf = malloc(im->w * im->h * (im->has_alpha ? 4 : 3));
if (!buf)
QUIT_WITH_RC(LOAD_OOM);
/* now we have to read from im->data into buf, swapping RGBA to BGRA */
imdata = im->data;
bufptr = buf;
/* for each row */
for (y = 0; y < im->h; y++)
{
int x;
/* for each pixel in the row */
for (x = 0; x < im->w; x++)
{
uint32_t pixel = *imdata++;
*bufptr++ = PIXEL_B(pixel);
*bufptr++ = PIXEL_G(pixel);
*bufptr++ = PIXEL_R(pixel);
if (im->has_alpha)
*bufptr++ = PIXEL_A(pixel);
} /* end for (each pixel in row) */
/* report progress every row */
if (im->lc && __imlib_LoadProgressRows(im, y, 1))
QUIT_WITH_RC(LOAD_BREAK);
}
/* write the header */
if (fwrite(&header, 1, sizeof(header), f) != sizeof(header))
goto quit;
/* write the image data */
if (fwrite(buf, (im->has_alpha ? 4 : 3), im->w * im->h, f) !=
(size_t)(im->w * im->h))
goto quit;
rc = LOAD_SUCCESS;
quit:
free(buf);
return rc;
}
IMLIB_LOADER(_formats, _load, _save);