582 lines
17 KiB
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);
|