/* This code is ripped from Autotrace-0.29. Small modifications by pts. */ /* input-tga.ci reads tga files */ #ifdef __GNUC__ #ifndef __clang__ #pragma implementation #endif #endif #include #include #include /* #include */ #include "at_bitmap.h" /* #include "message.h" */ /* #include "xstd.h" */ /* #include "input-tga.h" */ /* BUGFIX by pts: bmp -> tga */ /* TODO: - Handle loading images that aren't 8 bits per channel. */ /* Round up a division to the nearest integer. */ #define ROUNDUP_DIVIDE(n,d) (((n) + (d - 1)) / (d)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define INDEXED 1 #define INDEXEDA 2 #define GRAY 3 #define TGA_RGB 5 #define INDEXED_IMAGE 1 #define INDEXEDA_IMAGE 2 #define GRAY_IMAGE 3 #define GRAYA_IMAGE 4 #define TGA_RGB_IMAGE 5 #define TGA_RGBA_IMAGE 6 typedef struct _TgaSaveVals { int rle; } TgaSaveVals; typedef struct _TgaSaveInterface { int run; } TgaSaveInterface; struct tga_header { unsigned char idLength; unsigned char colorMapType; /* The image type. */ #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 unsigned char imageType; /* Color Map Specification. */ /* We need to separately specify high and low bytes to avoid endianness and alignment problems. */ unsigned char colorMapIndexLo, colorMapIndexHi; unsigned char colorMapLengthLo, colorMapLengthHi; unsigned char colorMapSize; /* Image Specification. */ unsigned char xOriginLo, xOriginHi; unsigned char yOriginLo, yOriginHi; unsigned char widthLo, widthHi; unsigned char heightLo, heightHi; unsigned char bpp; /* Image descriptor. 3-0: attribute bpp 4: left-to-right ordering 5: top-to-bottom ordering 7-6: zero */ #define TGA_DESC_ABITS 0x0f #define TGA_DESC_HORIZONTAL 0x10 #define TGA_DESC_VERTICAL 0x20 unsigned char descriptor; }; #if 0 static struct { unsigned int extensionAreaOffset; unsigned int developerDirectoryOffset; #define TGA_SIGNATURE "TRUEVISION-XFILE" char signature[16]; char dot; char null; } tga_footer; #endif static bitmap_type ReadImage (FILE *fp, struct tga_header *hdr); #if PTS_SAM2P /**** pts ****/ bitmap_type tga_load_image (FILE* filename) #else bitmap_type tga_load_image (at_string filename) #endif { FILE *fp; struct tga_header hdr; bitmap_type image; image.bitmap = NULL; #if PTS_SAM2P /**** pts ****/ fp=filename; #else fp = fopen (filename, "rb"); if (!fp) FATAL1 ("TGA: can't open \"%s\"\n", filename); #endif memset(&hdr, '\0', sizeof(hdr)); #if 0 /* Check the footer. */ if (fseek (fp, 0L - (sizeof (tga_footer)), SEEK_END) || fread (&tga_footer, sizeof (tga_footer), 1, fp) != 1) #if PTS_SAM2P FATALP("TGA: Cannot read footer"); #else FATAL1 ("TGA: Cannot read footer from \"%s\"\n", filename); #endif /* Check the signature. */ if (fseek (fp, 0, SEEK_SET) || #else if (0 || #endif fread (&hdr, sizeof (hdr), 1, fp) != 1) #if PTS_SAM2P FATALP("TGA: Cannot read header"); #else FATAL1 ("TGA: Cannot read header from \"%s\"\n", filename); #endif /* Skip the image ID field. */ { #if 0 if (hdr.idLength && fseek (fp, hdr.idLength, SEEK_CUR)) #else char buf[256]; if (hdr.idLength!=fread(buf, 1, hdr.idLength, fp)) #endif #if PTS_SAM2P FATALP("TGA: Cannot skip ID field"); #else FATAL1 ("TGA: Cannot skip ID field in \"%s\"\n", filename); #endif } image = ReadImage (fp, &hdr); /* fclose (fp); */ return image; } static int std_fread (unsigned char *buf, int datasize, int nelems, FILE *fp) { return fread (buf, datasize, nelems, fp); } #define RLE_PACKETSIZE 0x80 /* Decode a bufferful of file. */ static int rle_fread (unsigned char *buf, int datasize, int nelems, FILE *fp) { static unsigned char *statebuf = 0; static int statelen = 0; static int laststate = 0; int j, k; int buflen, count, bytes; unsigned char *p; /* Scale the buffer length. */ buflen = nelems * datasize; j = 0; while (j < buflen) { if (laststate < statelen) { /* Copy bytes from our previously decoded buffer. */ bytes = MIN (buflen - j, statelen - laststate); memcpy (buf + j, statebuf + laststate, bytes); j += bytes; laststate += bytes; /* If we used up all of our state bytes, then reset them. */ if (laststate >= statelen) { laststate = 0; statelen = 0; } /* If we filled the buffer, then exit the loop. */ if (j >= buflen) break; } /* Decode the next packet. */ count = fgetc (fp); if (count == EOF) { return j / datasize; } /* Scale the byte length to the size of the data. */ bytes = ((count & ~RLE_PACKETSIZE) + 1) * datasize; if (j + bytes <= buflen) { /* We can copy directly into the image buffer. */ p = buf + j; } else { /* Allocate the state buffer if we haven't already. */ if (!statebuf) statebuf = (unsigned char *) malloc (RLE_PACKETSIZE * datasize); p = statebuf; } if (count & RLE_PACKETSIZE) { /* Fill the buffer with the next value. */ if (fread (p, datasize, 1, fp) != 1) { return j / datasize; } /* Optimized case for single-byte encoded data. */ if (datasize == 1) memset (p + 1, *p, bytes - 1); else for (k = datasize; k < bytes; k += datasize) memcpy (p + k, p, datasize); } else { /* Read in the buffer. */ if (fread (p, bytes, 1, fp) != 1) { return j / datasize; } } /* We may need to copy bytes from the state buffer. */ if (p == statebuf) statelen = bytes; else j += bytes; } return nelems; } static bitmap_type ReadImage (FILE *fp, struct tga_header *hdr) { bitmap_type image; unsigned char *buffer; unsigned char *alphas; int width, height, bpp, abpp, pbpp, nalphas; int j, k; int pelbytes, wbytes, bsize, npels, pels; int rle, badread; int itype=0; unsigned char *cmap = NULL; int (*myfread)(unsigned char *, int, int, FILE *); /* Find out whether the image is horizontally or vertically reversed. */ char horzrev = (char) (hdr->descriptor & TGA_DESC_HORIZONTAL); char vertrev = (char) (!(hdr->descriptor & TGA_DESC_VERTICAL)); image.bitmap = NULL; /* Reassemble the multi-byte values correctly, regardless of host endianness. */ width = (hdr->widthHi << 8) | hdr->widthLo; height = (hdr->heightHi << 8) | hdr->heightLo; bpp = hdr->bpp; abpp = hdr->descriptor & TGA_DESC_ABITS; if (hdr->imageType == TGA_TYPE_COLOR || hdr->imageType == TGA_TYPE_COLOR_RLE) pbpp = MIN (bpp / 3, 8) * 3; else if (abpp < bpp) pbpp = bpp - abpp; else pbpp = bpp; if (abpp + pbpp > bpp) { WARNINGP3 ("TGA: ",pbpp," bit image, ",abpp," bit alpha is greater than ",bpp," total bits per pixel"); /* Assume that alpha bits were set incorrectly. */ abpp = bpp - pbpp; WARNINGP1 ("TGA: reducing to * bit alpha: ", abpp); } else if (abpp + pbpp < bpp) { WARNINGP3 ("TGA: ",pbpp," bit image, ",abpp," bit alpha is less than ",bpp," total bits per pixel"); /* Again, assume that alpha bits were set incorrectly. */ abpp = bpp - pbpp; WARNINGP1 ("TGA: increasing to * bit alpha: ", abpp); } rle = 0; switch (hdr->imageType) { case TGA_TYPE_MAPPED_RLE: rle = 1; case TGA_TYPE_MAPPED: itype = INDEXED; /* Find the size of palette elements. */ pbpp = MIN (hdr->colorMapSize / 3, 8) * 3; if (pbpp < hdr->colorMapSize) abpp = hdr->colorMapSize - pbpp; else abpp = 0; if (bpp != 8) /* We can only cope with 8-bit indices. */ FATALP ("TGA: index sizes other than 8 bits are unimplemented"); break; case TGA_TYPE_GRAY_RLE: rle = 1; case TGA_TYPE_GRAY: itype = GRAY; break; case TGA_TYPE_COLOR_RLE: rle = 1; case TGA_TYPE_COLOR: itype = TGA_RGB; break; default: FATALP1 ("TGA: unrecognized image type ", (unsigned)hdr->imageType); } if ((abpp && abpp != 8) || ((itype == TGA_RGB || itype == INDEXED) && pbpp != 24) || (itype == GRAY && pbpp != 8)) /* FIXME: We haven't implemented bit-packed fields yet. */ FATALP ("TGA: channel sizes other than 8 bits are unimplemented"); /* Check that we have a color map only when we need it. */ if (itype == INDEXED) { if (hdr->colorMapType != 1) FATALP1 ("TGA: indexed image has invalid color map type ", (unsigned)hdr->colorMapType); } else if (hdr->colorMapType != 0) FATALP1 ("TGA: non-indexed image has invalid color map type ", (unsigned)hdr->colorMapType); alphas = 0; nalphas = 0; if (hdr->colorMapType == 1) { /* We need to read in the colormap. */ int index, colors; unsigned int length; index = (hdr->colorMapIndexHi << 8) | hdr->colorMapIndexLo; length = (hdr->colorMapLengthHi << 8) | hdr->colorMapLengthLo; if (length == 0) FATALP1 ("TGA: invalid color map length ", length); pelbytes = ROUNDUP_DIVIDE (hdr->colorMapSize, 8); colors = length + index; cmap = (unsigned char *) malloc (colors * pelbytes); /* Zero the entries up to the beginning of the map. */ memset (cmap, 0, index * pelbytes); /* Read in the rest of the colormap. */ if (fread (cmap + (index * pelbytes), pelbytes, length, fp) != length) FATALP1 ("TGA: error reading colormap; ftell == ", ftell (fp)); /* If we have an alpha channel, then create a mapping to the alpha values. */ if (pelbytes > 3) alphas = (unsigned char *) malloc (colors); k = 0; for (j = 0; j < colors * pelbytes; j += pelbytes) { /* Swap from BGR to TGA_RGB. */ unsigned char tmp = cmap[j]; cmap[k ++] = cmap[j + 2]; cmap[k ++] = cmap[j + 1]; cmap[k ++] = tmp; /* Take the alpha values out of the colormap. */ if (alphas) alphas[nalphas ++] = cmap[j + 3]; } /* If the last color was transparent, then omit it from the mapping. */ if (nalphas && alphas[nalphas - 1] == 0) colors --; /* Now pretend as if we only have 8 bpp. */ abpp = 0; pbpp = 8; pelbytes = 1; } else pelbytes = 3; image.bitmap = (unsigned char *) malloc (width * height * 3 * sizeof(unsigned char)); BITMAP_WIDTH (image) = (unsigned short) width; BITMAP_HEIGHT (image) = (unsigned short) height; BITMAP_PLANES (image) = (unsigned short) 3; /* Calculate TGA bytes per pixel. */ bpp = ROUNDUP_DIVIDE (pbpp + abpp, 8); /* Maybe we need to reverse the data. */ buffer = NULL; if (horzrev || vertrev) buffer = (unsigned char *) malloc (width * height * pelbytes * sizeof (unsigned char)); if (rle) myfread = rle_fread; else myfread = std_fread; wbytes = width * pelbytes; badread = 0; npels = width * height; bsize = wbytes * height; /* Suck in the data one height at a time. */ if (badread) pels = 0; else pels = (*myfread) (image.bitmap, bpp, npels, fp); if (pels != npels) { if (!badread) { /* Probably premature end of file. */ WARNINGP1 ("TGA: error reading; ftell == ", ftell (fp)); badread = 1; } /* Fill the rest of this tile with zeros. */ memset (image.bitmap + (pels * bpp), 0, ((npels - pels) * bpp)); } /* If we have indexed alphas, then set them. */ if (nalphas) { /* Start at the end of the buffer, and work backwards. */ k = (npels - 1) * bpp; for (j = bsize - pelbytes; j >= 0; j -= pelbytes) { /* Find the alpha for this index. */ image.bitmap[j + 1] = alphas[image.bitmap[k]]; image.bitmap[j] = image.bitmap[k --]; } } if (itype == GRAY) for (j = bsize/3 - 1; j >= 0; j -= 1) { /* Find the alpha for this index. */ image.bitmap[3*j] = image.bitmap[j]; image.bitmap[3*j+1] = image.bitmap[j]; image.bitmap[3*j+2] = image.bitmap[j]; } if (pelbytes >= 3) { /* Rearrange the colors from BGR to TGA_RGB. */ for (j = 0; j < bsize; j += pelbytes) { unsigned char tmp = image.bitmap[j]; image.bitmap[j] = image.bitmap[j + 2]; image.bitmap[j + 2] = tmp; } } if (horzrev || vertrev) { unsigned char *tmp; if (vertrev) { /* We need to mirror only vertically. */ for (j = 0; j < bsize; j += wbytes) memcpy (buffer + j, image.bitmap + bsize - (j + wbytes), wbytes); } else if (horzrev) { /* We need to mirror only horizontally. */ for (j = 0; j < bsize; j += wbytes) for (k = 0; k < wbytes; k += pelbytes) memcpy (buffer + k + j, image.bitmap + (j + wbytes) - (k + pelbytes), pelbytes); } else { /* Completely reverse the pixels in the buffer. */ for (j = 0; j < bsize; j += pelbytes) memcpy (buffer + j, image.bitmap + bsize - (j + pelbytes), pelbytes); } /* Swap the buffers because we modified them. */ tmp = buffer; buffer = image.bitmap; image.bitmap = tmp; } if (fgetc (fp) != EOF) WARNINGP ("TGA: too much input data, ignoring extra..."); free (buffer); if (hdr->colorMapType == 1) { unsigned char *temp, *temp2, *temp3; unsigned char index; int xpos, ypos; temp2 = temp = image.bitmap; image.bitmap = temp3 = (unsigned char *) malloc (width * height * 3 * sizeof (unsigned char)); for (ypos = 0; ypos < height; ypos++) { for (xpos = 0; xpos < width; xpos++) { index = *temp2++; *temp3++ = cmap[3*index+0]; *temp3++ = cmap[3*index+1]; *temp3++ = cmap[3*index+2]; } } free (temp); free (cmap); } if (alphas) free (alphas); return image; } /* read_image */