diff --git a/AUTHORS b/AUTHORS index 2ab03ba..015556f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,3 +7,4 @@ Eric Dorland Chutt (Isaac Richards) gilbertt (Tom Gilbert) Takis +Dan Maas diff --git a/loaders/Makefile.am b/loaders/Makefile.am index 384b2a7..287f4a5 100644 --- a/loaders/Makefile.am +++ b/loaders/Makefile.am @@ -12,7 +12,7 @@ INCLUDES = -I/usr/X11R6/include -I$(top_srcdir)/libltdl \ -I$(top_srcdir)/loaders pkgdir = $(libdir)/loaders/image -pkg_LTLIBRARIES = png.la jpeg.la gif.la pnm.la argb.la tiff.la bmp.la +pkg_LTLIBRARIES = png.la jpeg.la gif.la pnm.la argb.la tiff.la bmp.la tga.la png_la_SOURCES = loader_png.c png_la_LDFLAGS = -no-undefined -module -avoid-version @@ -41,3 +41,8 @@ tiff_la_LIBADD = -ltiff bmp_la_SOURCES = loader_bmp.c bmp_la_LDFLAGS = -no-undefined -module -avoid-version bmp_la_LIBADD = + +tga_la_SOURCES = loader_tga.c +tga_la_LDFLAGS = -no-undefined -module -avoid-version +tga_la_LIBADD = + diff --git a/loaders/loader_tga.c b/loaders/loader_tga.c new file mode 100644 index 0000000..b2a0d34 --- /dev/null +++ b/loaders/loader_tga.c @@ -0,0 +1,627 @@ +/* + * loader_tga.c - Loader for Truevision Targa images + * for Imlib2 + * + * by Dan Maas 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 + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "common.h" +#include +#include +#include +#include +#include +#include +#include "image.h" +#include "colormod.h" +#include "blend.h" + +char load (ImlibImage *im, ImlibProgressFunction progress, + char progress_granularity, char immediate_load); +char save (ImlibImage *im, ImlibProgressFunction progress, + char progress_granularity); +void formats (ImlibLoader *l); + +/* flip an inverted image - see RLE reading below */ +static DATA32* flip(DATA32* in, int w, int h); + +/* 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 + +#define TGA_SIGNATURE "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 int extensionAreaOffset; + unsigned int developerDirectoryOffset; + char signature[16]; + char dot; + char null; +} tga_footer; + + +/* + * Write an uncompressed RGBA 24- or 32-bit targa to disk + * (If anyone wants to write a RLE saver, feel free =) + */ + +char +save (ImlibImage *im, ImlibProgressFunction progress, + char progress_granularity) +{ + FILE *f; + DATA32 *dataptr; + unsigned char *buf, *bufptr; + int y, pl = 0; + char pper = 0; + + tga_header header; + + if(!im->data) + return 0; + + f = fopen(im->file, "wb"); + if(!f) + return 0; + + + /* 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->flags & F_HAS_ALPHA) ? 32 : 24; + /* number of extra (alpha) bits per pixel */ + header.descriptor = (im->flags & F_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->flags & F_HAS_ALPHA) ? 4 : 3) ); + if(!buf) { + fclose(f); + return 0; + } + + /* now we have to read from im->data into buf, swapping RGBA to BGRA */ + dataptr = im->data; bufptr = buf; + + /* for each row */ + for(y = 0; y < im->h; y++) + { + int x; + unsigned char r, g, b, a; + + /* for each pixel in the row */ + for(x = 0; x < im->w; x++) { + if(im->flags & F_HAS_ALPHA) { + READ_RGBA(dataptr, r, g, b, a); + *bufptr++ = b; + *bufptr++ = g; + *bufptr++ = r; + *bufptr++ = a; + } else { + READ_RGB(dataptr, r, g, b); + *bufptr++ = b; + *bufptr++ = g; + *bufptr++ = r; + } + dataptr++; + } /* end for (each pixel in row) */ + + + /* report progress every row */ + if (progress) + { + char per; + int l; + + per = (char)((100 * y) / im->h); + if (((per - pper) >= progress_granularity) || + (y == (im->h - 1))) + { + l = y - pl; + if(!progress(im, per, 0, (y - l), im->w, l)) + { + if(buf) + free(buf); + fclose(f); + return 2; + } + pper = per; + pl = y; + } + } + } + + /* write the header */ + fwrite(&header, sizeof(header), 1, f); + + /* write the image data */ + fwrite(buf, 1, im->w * im->h * ((im->flags & F_HAS_ALPHA) ? 4 : 3), f); + + if(buf) + free(buf); + fclose(f); + return 1; +} + +/* 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 + */ + +char +load (ImlibImage *im, ImlibProgressFunction progress, + char progress_granularity, char immediate_load) +{ + FILE *fp; + int bpp, vinverted = 0; + int rle = 0, footer_present = 0; + + tga_header header; + tga_footer footer; + + if(im->data) + return 0; + + fp = fopen(im->file, "rb"); + if(!fp) + return 0; + + /* read the footer first */ + fseek (fp, 0L - (sizeof (tga_footer)), SEEK_END); + if (fread (&footer, sizeof (tga_footer), 1, fp) != 1) + { + fclose(fp); + return 0; + } + + /* check the footer to see if we have a v2.0 TGA file */ + if (memcmp(footer.signature, TGA_SIGNATURE, sizeof (footer.signature)) == 0) + footer_present = 1; + + /* now read the header */ + if (fseek (fp, 0, SEEK_SET) || fread (&header, sizeof (header), 1, fp) != 1) + { + fclose(fp); + return 0; + } + + /* skip over alphanumeric ID field */ + if (header.idLength && fseek (fp, header.idLength, SEEK_CUR)) + { + fclose(fp); + return 0; + } + + /* now parse the header */ + + /* this flag indicated bottom-up pixel storage */ + vinverted = header.descriptor ^ TGA_DESC_VERTICAL; + + /* endian-safe loading of 16-bit sizes */ + im->w = (header.widthHi << 8) | header.widthLo; + im->h = (header.heightHi << 8) | header.heightLo; + + switch (header.imageType) + { + case TGA_TYPE_COLOR_RLE: + case TGA_TYPE_GRAY_RLE: + rle = 1; + break; + + case TGA_TYPE_COLOR: + case TGA_TYPE_GRAY: + rle = 0; + break; + + default: + fclose(fp); + return 0; + } + + /* bits per pixel */ + bpp = header.bpp; + + if( ! ((bpp == 32) || (bpp == 24) || (bpp == 8)) ) + { + fclose(fp); + return 0; + } + + + if(!im->format) + { + if (bpp == 32) + SET_FLAG(im->flags, F_HAS_ALPHA); + else + UNSET_FLAG(im->flags, F_HAS_ALPHA); + im->format = strdup("tga"); + } + + /* if we need to actually read the pixel data... */ + if (((!im->data) && (im->loader)) || (immediate_load) || (progress)) + { + unsigned long datasize; + struct stat ss; + unsigned char *buf, *bufptr; + DATA32 *dataptr; + + int y, pl = 0; + char pper = 0; + + /* allocate the destination buffer */ + im->data = malloc(im->w * im->h * sizeof(DATA32)); + if(!im->data) + { + fclose(fp); + return 0; + } + + /* first we read the file data into a buffer for parsing */ + /* then we decode from RAM */ + + /* find out how much data must be read from the file */ + /* (this is NOT simply width*height*4, due to compression) */ + + stat(im->file, &ss); + datasize = ss.st_size - sizeof(tga_header) - header.idLength - + (footer_present ? sizeof(tga_footer) : 0); + + buf = malloc(datasize); + if(!buf) + { + fclose(fp); + return 0; + } + + /* read in the pixel data */ + if( fread(buf, 1, datasize, fp) != datasize) + { + fclose(fp); + return 0; + } + + /* buffer is ready for parsing */ + + /* bufptr is the next byte to be read from the buffer */ + bufptr = buf; + + /* dataptr is the next 32-bit pixel to be filled in */ + dataptr = im->data; + + /* decode uncompressed BGRA data */ + if(!rle) + { + for(y = 0; y < im->h; y++) /* for each row */ + { + int x; + + /* point dataptr at the beginning of the row */ + if(vinverted) + /* some TGA's are stored upside-down! */ + dataptr = im->data + (im->h - (y+1)) * im->w; + else + dataptr = im->data + y * im->w; + + + for(x = 0; x < im->w; x++) /* for each pixel in the row */ + { + switch(bpp) { + + /* 32-bit BGRA pixels */ + case 32: + WRITE_RGBA(dataptr, + *(bufptr + 2), /* R */ + *(bufptr + 1), /* G */ + *(bufptr + 0), /* B */ + *(bufptr + 3) /* A */ + ); + dataptr++; + bufptr += 4; + break; + + /* 24-bit BGR pixels */ + case 24: + WRITE_RGBA(dataptr, + *(bufptr + 2), /* R */ + *(bufptr + 1), /* G */ + *(bufptr + 0), /* B */ + (char) 0xff /* A */ + ); + dataptr++; + bufptr += 3; + break; + + /* 8-bit grayscale */ + case 8: + WRITE_RGBA(dataptr, + *bufptr, /* grayscale */ + *bufptr, + *bufptr, + (char) 0xff + ); + dataptr++; + bufptr += 1; + break; + } + + } /* end for (each pixel) */ + + /* report progress every row */ + if(progress) + { + char per; + int l; + + per = (char)((100*y) / im->h); + if (((per - pper) >= progress_granularity) || + (y == (im->h - 1))) + { + l = y - pl; + if(!progress(im, per, 0, (y - l), im->w, l)) + { + free(buf); + fclose(fp); + return 2; + } + pper = per; + pl = y; + } + } + + } /* end for (each row) */ + + } /* end if (RLE) */ + + /* decode RLE compressed data */ + else + { + unsigned char curbyte, red, green, blue, alpha; + DATA32 *final_pixel = dataptr + im->w * im->h; + + /* loop until we've got all the pixels */ + while(dataptr < final_pixel) + { + int count; + + curbyte = *bufptr++; + count = (curbyte & 0x7F) + 1; + + if(curbyte & 0x80) /* RLE packet */ + { + int i; + + switch(bpp) { + case 32: + blue = *bufptr++; green = *bufptr++; red = *bufptr++; + alpha = *bufptr++; + for(i = 0; i < count; i++) { + WRITE_RGBA(dataptr, red, green, blue, alpha); + dataptr++; + } + break; + + case 24: + blue = *bufptr++; green = *bufptr++; red = *bufptr++; + for(i = 0; i < count; i++) { + WRITE_RGBA(dataptr, red, green, blue, (char) 0xff); + dataptr++; + } + break; + + case 8: + alpha = *bufptr++; + for(i = 0; i < count; i++) { + WRITE_RGBA(dataptr, alpha, alpha, alpha, (char) 0xff); + dataptr++; + } + break; + } + + } /* end if (RLE packet) */ + + else /* raw packet */ + { + int i; + + for(i = 0; i < count; i++) + { + switch(bpp) { + + /* 32-bit BGRA pixels */ + case 32: + WRITE_RGBA(dataptr, + *(bufptr + 2), /* R */ + *(bufptr + 1), /* G */ + *(bufptr + 0), /* B */ + *(bufptr + 3) /* A */ + ); + dataptr++; + bufptr += 4; + break; + + /* 24-bit BGR pixels */ + case 24: + WRITE_RGBA(dataptr, + *(bufptr + 2), /* R */ + *(bufptr + 1), /* G */ + *(bufptr + 0), /* B */ + (char) 0xff /* A */ + ); + dataptr++; + bufptr += 3; + break; + + /* 8-bit grayscale */ + case 8: + WRITE_RGBA(dataptr, + *bufptr, /* pseudo-grayscale */ + *bufptr, + *bufptr, + (char) 0xff + ); + dataptr++; + bufptr += 1; + break; + } + } + } /* end if (raw packet) */ + + /* report progress every packet */ + if(progress) + { + char per; + int l; + + /* compute an approximate y value */ + /* can't be exact since packets don't necessarily */ + /* end at the end of a row */ + y = (dataptr - im->data) / im->w; + + per = (char)((100*y) / im->h); + + if (((per - pper) >= progress_granularity) || + (y == (im->h - 1))) + { + l = y - pl; + if(!progress(im, per, 0, (y - l), im->w, l)) + { + free(buf); + fclose(fp); + return 2; + } + pper = per; + pl = y; + } + } /* end progress report */ + + } /* end for (each packet) */ + + /* must now flip a bottom-up image */ + + /* This is the best of several ugly implementations + * I considered. It's not very good since the image + * will be upside-down throughout the loading process. + * This could be done in-line with the de-RLE code + * above, but that would be messy to code. There's + * probably a better way... */ + + if(vinverted) { + im->data = flip(im->data, im->w, im->h); + if(!im->data) { + fclose(fp); + free(buf); + return 0; + } + } + + } /* end if (image is RLE) */ + + free(buf); + + } /* end if (loading pixel data) */ + + fclose(fp); + return 1; +} + +void +formats (ImlibLoader *l) +{ + char *list_formats[] = + { "tga" }; + + { + int i; + + l->num_formats = (sizeof(list_formats) / sizeof (char *)); + l->formats = malloc(sizeof(char *) * l->num_formats); + for (i = 0; i < l->num_formats; i++) + l->formats[i] = strdup(list_formats[i]); + } +} + +/**********************/ + +/* flip a DATA32 image block vertically + * by allocating a new block, then copying + * the rows in reverse order + */ + +static DATA32* +flip(DATA32* in, int w, int h) +{ + int adv, adv2, i; + DATA32* out; + + out = malloc(w * h * sizeof(DATA32)); + if(!out) + return NULL; + + adv = 0; adv2 = w * h; + + for(i = 0; i < h; i++) { + adv2 -= w; + memmove(out + adv, in + adv2, w * sizeof(DATA32)); + adv += w; + } + + free(in); + return out; +} +