parent
9e55f36473
commit
f31a31581d
1
AUTHORS
1
AUTHORS
|
@ -7,3 +7,4 @@ Eric Dorland <dorland@lords.com>
|
|||
Chutt (Isaac Richards) <ijr@po.cwru.edu>
|
||||
gilbertt (Tom Gilbert) <gilbertt@btinternet.com>
|
||||
Takis <takis@lumumba.luc.ac.be>
|
||||
Dan Maas <dmaas@dcine.com>
|
||||
|
|
|
@ -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 =
|
||||
|
||||
|
|
|
@ -0,0 +1,627 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
#include <X11/Xutil.h>
|
||||
#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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue