/* * 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 "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 void tgaflip(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->real_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) { int fd; void *seg, *filedata; struct stat ss; int bpp, vinverted = 0; int rle = 0, footer_present = 0; tga_header *header; tga_footer *footer; if (im->data) return 0; fd = open(im->real_file, O_RDONLY); if (fd < 0) return 0; if (fstat(fd, &ss) < 0) { close(fd); return 0; } if (ss.st_size < sizeof(tga_header) + sizeof(tga_footer)) { close(fd); return 0; } seg = mmap(0, ss.st_size, PROT_READ, MAP_SHARED, fd, 0); if (seg == MAP_FAILED) { close(fd); return 0; } filedata = seg; header = (tga_header *) filedata; footer = (tga_footer *) ((char *)filedata + ss.st_size - sizeof(tga_footer)); /* 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; if (!footer_present) { } /* skip over header */ filedata = (char *)filedata + sizeof(tga_header); /* skip over alphanumeric ID field */ if (header->idLength) filedata = (char *)filedata + header->idLength; /* now parse the header */ /* this flag indicated bottom-up pixel storage */ vinverted = !(header->descriptor & TGA_DESC_VERTICAL); 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: munmap(seg, ss.st_size); close(fd); return 0; } /* bits per pixel */ bpp = header->bpp; if (!((bpp == 32) || (bpp == 24) || (bpp == 8))) { munmap(seg, ss.st_size); close(fd); return 0; } /* endian-safe loading of 16-bit sizes */ im->w = (header->widthHi << 8) | header->widthLo; im->h = (header->heightHi << 8) | header->heightLo; if ((im->w < 1) || (im->h < 1) || (im->w > 8192) || (im->h > 8192)) { munmap(seg, ss.st_size); close(fd); 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; unsigned char *bufptr, *bufend; DATA32 *dataptr; int y; /* allocate the destination buffer */ im->data = malloc(im->w * im->h * sizeof(DATA32)); if (!im->data) { im->w = 0; munmap(seg, ss.st_size); close(fd); 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) */ datasize = ss.st_size - sizeof(tga_header) - header->idLength - (footer_present ? sizeof(tga_footer) : 0); /* buffer is ready for parsing */ /* bufptr is the next byte to be read from the buffer */ bufptr = filedata; bufend = filedata + datasize; /* 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, /* grayscale */ *bufptr, *bufptr, *bufptr, (char)0xff); dataptr++; bufptr += 1; break; } } /* end for (each pixel) */ } if (progress) { progress(im, 100, 0, 0, im->w, im->h); } /* 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 or run out of input */ while ((dataptr < final_pixel) && ((bufptr + 1 + (bpp / 8)) < bufend)) { 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) && (dataptr < final_pixel); i++) { WRITE_RGBA(dataptr, red, green, blue, alpha); dataptr++; } break; case 24: blue = *bufptr++; green = *bufptr++; red = *bufptr++; for (i = 0; (i < count) && (dataptr < final_pixel); i++) { WRITE_RGBA(dataptr, red, green, blue, (char)0xff); dataptr++; } break; case 8: alpha = *bufptr++; for (i = 0; (i < count) && (dataptr < final_pixel); 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) && (dataptr < final_pixel); 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) */ } /* end for (each packet) */ /* must now flip a bottom-up image */ if (vinverted) tgaflip(im->data, im->w, im->h); if (progress) { progress(im, 100, 0, 0, im->w, im->h); } /* end for (each row) */ } /* end if (image is RLE) */ } /* end if (loading pixel data) */ munmap(seg, ss.st_size); close(fd); 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 in place */ static void tgaflip (DATA32 * in, int w, int h) { DATA32 *adv, *adv2; int x, y; adv = in; adv2 = in + (w * (h - 1)); for (y = 0; y < (h / 2); y++) { DATA32 tmp; for (x = 0; x < w; x++) { tmp = adv[x]; adv[x] = adv2[x]; adv2[x] = tmp; } adv2 -= w; adv += w; } }