e576bb2002-10-11Martin Nilsson /* || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. */
3fa2791999-04-06Per Hedbor  /*
d7535b1999-04-06Per Hedbor  * Targa codec for pike. Based on the tga plugin for gimp.
3fa2791999-04-06Per Hedbor  * * The information below is from the original TGA module. * * * TrueVision Targa loading and saving file filter for the Gimp. * Targa code Copyright (C) 1997 Raphael FRANCOIS and Gordon Matzigkeit * * The Targa reading and writing code was written from scratch by * Raphael FRANCOIS <fraph@ibm.net> and Gordon Matzigkeit * <gord@gnu.ai.mit.edu> based on the TrueVision TGA File Format * Specification, Version 2.0: * * <URL:ftp://ftp.truevision.com/pub/TGA.File.Format.Spec/> * * It does not contain any code written for other TGA file loaders. * Not even the RLE handling. ;) * */ /* **! module Image **! submodule TGA **!
29edef2014-07-31Per Hedbor **! TrueVision TGA image decoding and encoding support. **! **! Truevision TGA, often referred to as TARGA, is a raster graphics **! file format created by Truevision Inc. It was the native format of **! TARGA and VISTA boards, which were the first graphic cards for **! IBM-compatible PCs to support Highcolor/truecolor display. **! **! @note **! TGA images were commonly used for screenshots in games, although **! since about 2010 most games have started to migrate to PNG images.
3fa2791999-04-06Per Hedbor */
07228a1999-06-19Fredrik Hübinette (Hubbe) #include "global.h"
3fa2791999-04-06Per Hedbor #include "interpret.h" #include "svalue.h" #include "pike_macros.h" #include "object.h" #include "program.h"
b2d3e42000-12-01Fredrik Hübinette (Hubbe) #include "pike_error.h"
3fa2791999-04-06Per Hedbor #include "mapping.h" #include "stralloc.h" #include "operators.h" #include "pike_memory.h" #include "threads.h" #include "module_support.h"
4832b71999-05-11Mirar (Pontus Hagland) #include "builtin_functions.h"
6a932b2014-08-18Martin Nilsson #include "pike_types.h"
3fa2791999-04-06Per Hedbor  #include "image.h" #include "colortable.h" #define ROUNDUP_DIVIDE(n,d) (((n) + (d - 1)) / (d)) extern struct program *image_colortable_program; extern struct program *image_program; typedef unsigned char guint8; typedef unsigned char gchar; typedef unsigned char guchar; typedef char gint8; typedef unsigned INT32 guint32; typedef INT32 gint32; enum { RGB, GRAY, INDEXED, }; struct tga_header { guint8 idLength; guint8 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 guint8 imageType; /* Color Map Specification. */ /* We need to separately specify high and low bytes to avoid endianness and alignment problems. */ guint8 colorMapIndexLo, colorMapIndexHi; guint8 colorMapLengthLo, colorMapLengthHi; guint8 colorMapSize; /* Image Specification. */ guint8 xOriginLo, xOriginHi; guint8 yOriginLo, yOriginHi; guint8 widthLo, widthHi; guint8 heightLo, heightHi; guint8 bpp; /* Image descriptor. 3-0: alpha 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 guint8 descriptor; }; struct tga_footer { guint32 extensionAreaOffset; guint32 developerDirectoryOffset; #define TGA_SIGNATURE "TRUEVISION-XFILE" gchar signature[16]; gchar dot; gchar null; }; struct buffer {
3bd87a2000-08-03Henrik Grubbström (Grubba)  size_t len;
3fa2791999-04-06Per Hedbor  char *str; }; struct image_alpha { struct image *img; struct object *io; struct image *alpha; struct object *ao; }; static struct image_alpha ReadImage (struct buffer *, struct tga_header *); static struct image_alpha load_image(struct pike_string *str) { struct tga_header hdr;
340c562001-06-13Henrik Grubbström (Grubba)  /* struct tga_footer footer; */
3fa2791999-04-06Per Hedbor  struct buffer buffer; INT32 image_ID = -1; buffer.str = str->str; buffer.len = str->len; if(buffer.len < ((sizeof(struct tga_footer)+sizeof(struct tga_header))))
cc7cf42015-10-14Martin Nilsson  Pike_error("Data (%ld bytes) is too short\n", (long)buffer.len);
3fa2791999-04-06Per Hedbor 
c1c0d02000-03-10Per Hedbor 
59fc9e2014-09-03Martin Nilsson /* memcpy(&footer, (buffer.str+(buffer.len-sizeof(struct tga_footer))), */
c1c0d02000-03-10Per Hedbor /* sizeof( struct tga_footer) ); */
3fa2791999-04-06Per Hedbor  hdr = *((struct tga_header *)buffer.str); buffer.len -= sizeof(struct tga_header); buffer.str += sizeof(struct tga_header); buffer.str += hdr.idLength; buffer.len -= hdr.idLength;
c1c0d02000-03-10Per Hedbor  if( (hdr.bpp != 8) && (hdr.bpp != 16) && (hdr.bpp != 24) && (hdr.bpp != 32) )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Unsupported TGA file (bpp==%d)\n", hdr.bpp);
c1c0d02000-03-10Per Hedbor  if( hdr.imageType > 11 )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Unsupported TGA image type\n");
c1c0d02000-03-10Per Hedbor  if(buffer.len < 3)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Not enough data in buffer to decode a TGA image\n");
c2ed772000-03-09Per Hedbor 
3fa2791999-04-06Per Hedbor  return ReadImage (&buffer, &hdr); }
903b772000-08-09Henrik Grubbström (Grubba) static ptrdiff_t std_fread (unsigned char *buf, size_t datasize, size_t nelems, struct buffer *fp)
3fa2791999-04-06Per Hedbor {
f8e05d2002-10-25Martin Nilsson  size_t amnt = MINIMUM((nelems*datasize), fp->len);
59fc9e2014-09-03Martin Nilsson  memcpy(buf, fp->str, amnt);
3fa2791999-04-06Per Hedbor  fp->len -= amnt; fp->str += amnt; return amnt / datasize; }
903b772000-08-09Henrik Grubbström (Grubba) static ptrdiff_t std_fwrite (unsigned char *buf, size_t datasize, size_t nelems, struct buffer *fp)
3fa2791999-04-06Per Hedbor {
f8e05d2002-10-25Martin Nilsson  size_t amnt = MINIMUM((nelems*datasize), fp->len);
59fc9e2014-09-03Martin Nilsson  memcpy(fp->str, buf, amnt);
3fa2791999-04-06Per Hedbor  fp->len -= amnt; fp->str += amnt; return amnt / datasize; } static int std_fgetc( struct buffer *fp ) { if(fp->len >= 1) { fp->len--; return (int)*((unsigned char *)fp->str++); } return EOF; } static int std_fputc( int c, struct buffer *fp ) { if(fp->len >= 1) { fp->len--; *fp->str++=c; return 1; } return EOF; } #define RLE_PACKETSIZE 0x80 /* Decode a bufferful of file. */
52f9042000-08-13Henrik Grubbström (Grubba) static ptrdiff_t rle_fread (guchar *buf, size_t datasize, size_t nelems, struct buffer *fp)
3fa2791999-04-06Per Hedbor { /* If we want to call this function more than once per image, change the variables below to be static.. */ guchar *statebuf = 0;
903b772000-08-09Henrik Grubbström (Grubba)  ptrdiff_t statelen = 0; ptrdiff_t laststate = 0;
3fa2791999-04-06Per Hedbor  /* end static variables.. */
903b772000-08-09Henrik Grubbström (Grubba)  ptrdiff_t j, k; ptrdiff_t buflen, count, bytes;
3fa2791999-04-06Per Hedbor  guchar *p; /* Scale the buffer length. */ buflen = nelems * datasize; j = 0; while (j < buflen) { if (laststate < statelen) { /* Copy bytes from our previously decoded buffer. */
f8e05d2002-10-25Martin Nilsson  bytes = MINIMUM (buflen - j, statelen - laststate);
59fc9e2014-09-03Martin Nilsson  memcpy (buf + j, statebuf + laststate, bytes);
3fa2791999-04-06Per Hedbor  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 = std_fgetc (fp); if (count == EOF) {
12a36b2008-01-16Henrik Grubbström (Grubba)  if (statebuf) free(statebuf);
3fa2791999-04-06Per Hedbor  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; }
c2ed772000-03-09Per Hedbor  else
3fa2791999-04-06Per Hedbor  { /* Allocate the state buffer if we haven't already. */ if (!statebuf)
dc8d022014-04-27Martin Nilsson  statebuf = malloc (RLE_PACKETSIZE * datasize);
3fa2791999-04-06Per Hedbor  p = statebuf; } if (count & RLE_PACKETSIZE) { /* Fill the buffer with the next value. */ if (std_fread (p, datasize, 1, fp) != 1) {
12a36b2008-01-16Henrik Grubbström (Grubba)  if (statebuf) free(statebuf);
3fa2791999-04-06Per Hedbor  return j / datasize; } /* Optimized case for single-byte encoded data. */ if (datasize == 1)
21b12a2014-09-03Martin Nilsson  memset (p + 1, *p, bytes - 1);
3fa2791999-04-06Per Hedbor  else for (k = datasize; k < bytes; k += datasize)
59fc9e2014-09-03Martin Nilsson  memcpy (p + k, p, datasize);
3fa2791999-04-06Per Hedbor  } else { /* Read in the buffer. */
12a36b2008-01-16Henrik Grubbström (Grubba)  if (std_fread (p, bytes, 1, fp) != 1) { free(statebuf);
3fa2791999-04-06Per Hedbor  return j / datasize;
12a36b2008-01-16Henrik Grubbström (Grubba)  }
3fa2791999-04-06Per Hedbor  } /* We may need to copy bytes from the state buffer. */ if (p == statebuf) statelen = bytes; else j += bytes; }
12a36b2008-01-16Henrik Grubbström (Grubba)  if (statebuf) free(statebuf);
3fa2791999-04-06Per Hedbor  return nelems; } /* This function is stateless, which means that we always finish packets on buffer boundaries. As a beneficial side-effect, rle_fread never has to allocate a state buffer when it loads our files, provided it is called using the same buffer lengths! So, we get better compression than line-by-line encoders, and better
c2ed772000-03-09Per Hedbor  loading performance than whole-stream images.
3fa2791999-04-06Per Hedbor */ /* RunLength Encode a bufferful of file. */
77547b2000-08-11Henrik Grubbström (Grubba) static ptrdiff_t rle_fwrite (guchar *buf, size_t datasize, size_t nelems, struct buffer *fp)
3fa2791999-04-06Per Hedbor { /* Now runlength-encode the whole buffer. */
903b772000-08-09Henrik Grubbström (Grubba)  ptrdiff_t count, j, buflen;
3fa2791999-04-06Per Hedbor  guchar *begin; /* Scale the buffer length. */ buflen = datasize * nelems; begin = buf; j = datasize; while (j < buflen) { /* BUF[J] is our lookahead element, BEGIN is the beginning of this run, and COUNT is the number of elements in this run. */ if (memcmp (buf + j, begin, datasize) == 0) { /* We have a run of identical characters. */ count = 1; do { j += datasize; count ++; } while (j < buflen && count < RLE_PACKETSIZE && memcmp (buf + j, begin, datasize) == 0); /* J now either points to the beginning of the next run, or close to the end of the buffer. */ /* Write out the run. */
cc7cf42015-10-14Martin Nilsson  if (std_fputc((int)(count - 1)|RLE_PACKETSIZE, fp) == EOF ||
77547b2000-08-11Henrik Grubbström (Grubba)  std_fwrite(begin, datasize, 1, fp) != 1)
3fa2791999-04-06Per Hedbor  return 0; } else { /* We have a run of raw characters. */ count = 0; do { j += datasize; count ++; } while (j < buflen && count < RLE_PACKETSIZE && memcmp (buf + j - datasize, buf + j, datasize) != 0); /* Back up to the previous character. */ j -= datasize; /* J now either points to the beginning of the next run, or at the end of the buffer. */ /* Write out the raw packet. */
cc7cf42015-10-14Martin Nilsson  if (std_fputc((int)(count - 1), fp) == EOF ||
77547b2000-08-11Henrik Grubbström (Grubba)  std_fwrite(begin, datasize, count, fp) != count)
3fa2791999-04-06Per Hedbor  return 0; } /* Set the beginning of the next run and the next lookahead. */ begin = buf + j; j += datasize; } /* If we didn't encode all the elements, write one last packet. */ if (begin < buf + buflen) { if (std_fputc (0, fp) == EOF || std_fwrite (begin, datasize, 1, fp) != 1) return 0; } return nelems; }
c2ed772000-03-09Per Hedbor static unsigned short extract_le_short( unsigned char **data ) { (*data)+=2; return (*data)[-2] | ((*data)[-1]<<8); } static unsigned char c5to8bit( unsigned char v ) { return (v << 3) + (v >> 2); }
3fa2791999-04-06Per Hedbor static struct image_alpha ReadImage(struct buffer *fp, struct tga_header *hdr) {
c2ed772000-03-09Per Hedbor  int width, height, bpp, abpp, pbpp, bypp;
340c562001-06-13Henrik Grubbström (Grubba)  int pelbytes=0, rle=0;
3fa2791999-04-06Per Hedbor  unsigned char *cmap=NULL, *data;
8db2c31999-05-23Mirar (Pontus Hagland)  int itype=0;
c2ed772000-03-09Per Hedbor  int really_no_alpha = 0;
340c562001-06-13Henrik Grubbström (Grubba)  ptrdiff_t pels, npels, read_so_far = 0;
ebe0c52000-08-10Henrik Grubbström (Grubba)  ptrdiff_t (*myfread)(unsigned char *, size_t, size_t, struct buffer *);
3fa2791999-04-06Per Hedbor  /* Find out whether the image is horizontally or vertically reversed. The GIMP likes things left-to-right, top-to-bottom. */ int horzrev = hdr->descriptor & TGA_DESC_HORIZONTAL; int vertrev = !(hdr->descriptor & TGA_DESC_VERTICAL); /* 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)
f8e05d2002-10-25Martin Nilsson  pbpp = MINIMUM (bpp / 3, 8) * 3;
3fa2791999-04-06Per Hedbor  else if (abpp < bpp) pbpp = bpp - abpp; else pbpp = bpp; if (abpp + pbpp > bpp) { /* Assume that alpha bits were set incorrectly. */ abpp = bpp - pbpp; } else if (abpp + pbpp < bpp) { /* Again, assume that alpha bits were set incorrectly. */ abpp = bpp - pbpp;
c2ed772000-03-09Per Hedbor  really_no_alpha = 1;
3fa2791999-04-06Per Hedbor  }
294d792004-08-11Per Hedbor  if((double)width * (double)height * bpp > (double)INT_MAX ) Pike_error("Too large image (width * height * bpp overflows)\n");
3fa2791999-04-06Per Hedbor  switch (hdr->imageType) { case TGA_TYPE_MAPPED_RLE: rle = 1;
5f50842018-02-12Marcus Comstedt  /* FALLTHRU */
1f6bbb2013-02-06Henrik Grubbström (Grubba) 
3fa2791999-04-06Per Hedbor  case TGA_TYPE_MAPPED: itype = INDEXED; /* Find the size of palette elements. */
f8e05d2002-10-25Martin Nilsson  pbpp = MINIMUM (hdr->colorMapSize / 3, 8) * 3;
3fa2791999-04-06Per Hedbor  if (pbpp < hdr->colorMapSize) abpp = hdr->colorMapSize - pbpp; else abpp = 0; if (bpp != 8) /* We can only cope with 8-bit indices. */
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error ("TGA: index sizes other than 8 bits are unimplemented\n");
3fa2791999-04-06Per Hedbor  break; case TGA_TYPE_GRAY_RLE: rle = 1;
5f50842018-02-12Marcus Comstedt  /* FALLTHRU */
1f6bbb2013-02-06Henrik Grubbström (Grubba) 
3fa2791999-04-06Per Hedbor  case TGA_TYPE_GRAY: itype = GRAY; break; case TGA_TYPE_COLOR_RLE: rle = 1;
5f50842018-02-12Marcus Comstedt  /* FALLTHRU */
1f6bbb2013-02-06Henrik Grubbström (Grubba) 
3fa2791999-04-06Per Hedbor  case TGA_TYPE_COLOR: itype = RGB; break; default:
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error ("TGA: unrecognized image type %d\n", hdr->imageType);
3fa2791999-04-06Per Hedbor  } /* Check that we have a color map only when we need it. */ if (itype == INDEXED) { if (hdr->colorMapType != 1)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error ("TGA: indexed image has invalid color map type %d\n",
3fa2791999-04-06Per Hedbor  hdr->colorMapType); } else if (hdr->colorMapType != 0) {
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error ("TGA: non-indexed image has invalid color map type %d\n",
3fa2791999-04-06Per Hedbor  hdr->colorMapType); } if (hdr->colorMapType == 1) { /* We need to read in the colormap. */ int index, length, colors; index = (hdr->colorMapIndexHi << 8) | hdr->colorMapIndexLo; length = (hdr->colorMapLengthHi << 8) | hdr->colorMapLengthLo; if (length == 0)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error ("TGA: invalid color map length %d\n", length);
3fa2791999-04-06Per Hedbor  pelbytes = ROUNDUP_DIVIDE (hdr->colorMapSize, 8); colors = length + index;
aacc742010-10-01Martin Nilsson  cmap = xcalloc (colors * pelbytes, 1);
3fa2791999-04-06Per Hedbor  /* Read in the rest of the colormap. */ if (std_fread (cmap + (index * pelbytes), pelbytes, length, fp) != length) { free(cmap);
a4a1722000-12-05Per Hedbor  Pike_error ("TGA: error reading colormap\n");
3fa2791999-04-06Per Hedbor  } /* Now pretend as if we only have 8 bpp. */ abpp = 0; pbpp = 8; } /* Allocate the data. */
dc8d022014-04-27Martin Nilsson  data = malloc (ROUNDUP_DIVIDE((width * height * bpp), 8));
c1c0d02000-03-10Per Hedbor  if(!data)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("TGA: malloc failed\n");
3fa2791999-04-06Per Hedbor  if (rle) myfread = rle_fread; else myfread = std_fread;
c1c0d02000-03-10Per Hedbor  npels = width*height; bypp = ROUNDUP_DIVIDE(bpp,8);
3fa2791999-04-06Per Hedbor  /* Suck in the data. */
903b772000-08-09Henrik Grubbström (Grubba)  pels = myfread(data+read_so_far,bypp,npels,fp);
c1c0d02000-03-10Per Hedbor  read_so_far += pels;
3fa2791999-04-06Per Hedbor  npels -= pels;
c1c0d02000-03-10Per Hedbor  if(npels)
21b12a2014-09-03Martin Nilsson  memset( data+(read_so_far*bypp), 0, npels*bypp );
3fa2791999-04-06Per Hedbor  /* Now convert the data to two image objects. */ { int x, y; struct image_alpha i;
c2ed772000-03-09Per Hedbor  unsigned char *sd = data; rgb_group *id; rgb_group *ad;
3fa2791999-04-06Per Hedbor  push_int( width );
c2ed772000-03-09Per Hedbor  push_int( height );
3fa2791999-04-06Per Hedbor  i.io = clone_object( image_program, 2 );
13b5ed2014-05-26Per Hedbor  i.img = get_storage(i.io,image_program);
3fa2791999-04-06Per Hedbor  push_int( width );
c2ed772000-03-09Per Hedbor  push_int( height );
3fa2791999-04-06Per Hedbor  push_int( 255 ); push_int( 255 ); push_int( 255 ); i.ao = clone_object( image_program, 5 );
13b5ed2014-05-26Per Hedbor  i.alpha = get_storage(i.ao,image_program);
3fa2791999-04-06Per Hedbor 
c2ed772000-03-09Per Hedbor  id = i.img->img; ad = i.alpha->img;
c1c0d02000-03-10Per Hedbor  if( bpp == 16 && pbpp == 15 && itype == RGB )
c2ed772000-03-09Per Hedbor  {
c1c0d02000-03-10Per Hedbor  for( y = 0; y<height; y++ ) for(x = 0; x<width; x++) { unsigned short pixel = extract_le_short(&sd);
f0e3832000-08-31Henrik Grubbström (Grubba)  id->b = c5to8bit((unsigned char)(pixel & 31)); id->g = c5to8bit((unsigned char)((pixel & 922)>>5)); id->r = c5to8bit((unsigned char)((pixel & 31744)>>10));
c1c0d02000-03-10Per Hedbor  id++; }
c2ed772000-03-09Per Hedbor  } else /* 8 bits / channel. Simple and fast implementation. */
3fa2791999-04-06Per Hedbor  { switch( itype ) { case INDEXED: for(y = 0; y<height; y++) for(x = 0; x<width; x++) { int cmapind = (*sd)*pelbytes; id->b = cmap[cmapind++]; id->g = cmap[cmapind++]; (id++)->r = cmap[cmapind++]; if(pelbytes>3) {
09b5b42000-03-26Fredrik Hübinette (Hubbe)  ad->r = ad->g = ad->b = cmap[cmapind]; ad++;
3fa2791999-04-06Per Hedbor  } sd++; } break; case GRAY: for(y = 0; y<height; y++) for(x = 0; x<width; x++) {
09b5b42000-03-26Fredrik Hübinette (Hubbe)  id->r = id->g = id->b = *(sd++); id++;
3fa2791999-04-06Per Hedbor  if(abpp) {
09b5b42000-03-26Fredrik Hübinette (Hubbe)  ad->r = ad->g = ad->b = *(sd++); ad++;
3fa2791999-04-06Per Hedbor  } } break; case RGB: for(y = 0; y<height; y++) for(x = 0; x<width; x++) { id->b = *(sd++); id->g = *(sd++); (id++)->r = *(sd++);
c1c0d02000-03-10Per Hedbor  if(abpp) {
c2ed772000-03-09Per Hedbor  if( !really_no_alpha )
09b5b42000-03-26Fredrik Hübinette (Hubbe)  { ad->r = ad->g = ad->b = *sd;
bd22aa2000-03-26Fredrik Hübinette (Hubbe)  ad++;
09b5b42000-03-26Fredrik Hübinette (Hubbe)  }
c2ed772000-03-09Per Hedbor  sd++;
3fa2791999-04-06Per Hedbor  } } } } free (data); if(cmap) free (cmap); if(horzrev) { apply( i.io, "mirrorx", 0 );
c2ed772000-03-09Per Hedbor  free_object(i.io);
5923b12000-07-06Fredrik Hübinette (Hubbe)  i.io = Pike_sp[-1].u.object; Pike_sp--;
3fa2791999-04-06Per Hedbor  apply( i.ao, "mirrorx", 0 );
c2ed772000-03-09Per Hedbor  free_object(i.ao);
5923b12000-07-06Fredrik Hübinette (Hubbe)  i.ao = Pike_sp[-1].u.object; Pike_sp--;
3fa2791999-04-06Per Hedbor  } if(vertrev) { apply( i.io, "mirrory", 0 );
c2ed772000-03-09Per Hedbor  free_object(i.io);
5923b12000-07-06Fredrik Hübinette (Hubbe)  i.io = Pike_sp[-1].u.object; Pike_sp--;
3fa2791999-04-06Per Hedbor  apply( i.ao, "mirrory", 0 );
c2ed772000-03-09Per Hedbor  free_object(i.ao);
5923b12000-07-06Fredrik Hübinette (Hubbe)  i.ao = Pike_sp[-1].u.object; Pike_sp--;
3fa2791999-04-06Per Hedbor  } return i; } } #define SAVE_ID_STRING "Pike image library TGA" static struct buffer save_tga(struct image *img, struct image *alpha, int rle_encode) { int width, height; struct buffer buf; struct buffer obuf; struct buffer *fp = &buf; struct tga_header hdr;
7d60af2004-05-19Martin Nilsson  ONERROR err;
77547b2000-08-11Henrik Grubbström (Grubba)  ptrdiff_t (*myfwrite)(unsigned char *, size_t, size_t,
903b772000-08-09Henrik Grubbström (Grubba)  struct buffer *);
3fa2791999-04-06Per Hedbor 
c2ed772000-03-09Per Hedbor  if(alpha &&
3fa2791999-04-06Per Hedbor  (alpha->xsize != img->xsize || alpha->ysize != img->ysize ))
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Alpha and image objects are not equally sized.\n");
3fa2791999-04-06Per Hedbor  width = img->xsize; height = img->ysize; memset (&hdr, 0, sizeof (hdr)); /* We like our images top-to-bottom, thank you! */ hdr.descriptor |= TGA_DESC_VERTICAL;
cd3d092008-01-27Henrik Grubbström (Grubba)  /* Choose the imageType based on alpha presence and compression options. */
3fa2791999-04-06Per Hedbor  hdr.bpp = 24; hdr.imageType = TGA_TYPE_COLOR; if(alpha) { hdr.bpp += 8; hdr.descriptor |= 8; } if (rle_encode) { /* Here we take advantage of the fact that the RLE image type codes are exactly 8 greater than the non-RLE. */ hdr.imageType += 8; myfwrite = rle_fwrite; } else myfwrite = std_fwrite; hdr.widthLo = (width & 0xff); hdr.widthHi = (width >> 8); hdr.heightLo = (height & 0xff); hdr.heightHi = (height >> 8); /* Mark our save ID. */
cc7cf42015-10-14Martin Nilsson  hdr.idLength = (unsigned INT32)strlen(SAVE_ID_STRING);
3fa2791999-04-06Per Hedbor  buf.len = width*height*(alpha?4:3)+strlen(SAVE_ID_STRING)+sizeof(hdr)+65535; buf.str = xalloc(buf.len);
cd3d092008-01-27Henrik Grubbström (Grubba)  obuf = buf;
7d60af2004-05-19Martin Nilsson  SET_ONERROR(err, free, obuf.str);
3fa2791999-04-06Per Hedbor  /* Just write the header. */ if (std_fwrite((void *)&hdr, sizeof (hdr), 1, fp) != 1)
7d60af2004-05-19Martin Nilsson  Pike_error(msg_out_of_mem);
01a9572000-02-03Henrik Grubbström (Grubba)  if (std_fwrite ((void *)SAVE_ID_STRING, hdr.idLength, 1, fp) != 1)
7d60af2004-05-19Martin Nilsson  Pike_error(msg_out_of_mem);
3fa2791999-04-06Per Hedbor  /* Allocate a new set of pixels. */ /* Write out the pixel data. */ { char *data, *p; int datalen; int pixsize=3; int x, y; rgb_group *is = img->img; if(alpha) { rgb_group *as = alpha->img; pixsize++;
7d60af2004-05-19Martin Nilsson  p = data = xalloc( width*height*4 );
3fa2791999-04-06Per Hedbor  datalen = width*height*4; for(y=0; y<height; y++) for(x=0; x<width; x++) { *(p++) = is->b; *(p++) = is->g; *(p++) = (is++)->r;
09b5b42000-03-26Fredrik Hübinette (Hubbe)  *(p++) = ((int)as->r+(int)as->g*2+as->b)/4; as++;
3fa2791999-04-06Per Hedbor  } } else {
7d60af2004-05-19Martin Nilsson  p = data = xalloc( width*height*3 );
3fa2791999-04-06Per Hedbor  datalen = width*height*3; for(y=0; y<height; y++) for(x=0; x<width; x++) { *(p++) = is->b; *(p++) = is->g; *(p++) = (is++)->r; } }
903b772000-08-09Henrik Grubbström (Grubba)  if (myfwrite((void *)data, pixsize,datalen/pixsize, fp) !=
01a9572000-02-03Henrik Grubbström (Grubba)  datalen/pixsize)
3fa2791999-04-06Per Hedbor  { free(data);
7d60af2004-05-19Martin Nilsson  Pike_error(msg_out_of_mem);
3fa2791999-04-06Per Hedbor  } free(data); }
7d60af2004-05-19Martin Nilsson  UNSET_ONERROR(err);
cd3d092008-01-27Henrik Grubbström (Grubba) 
59d7122007-12-15Henrik Grubbström (Grubba)  obuf.len -= fp->len;
3fa2791999-04-06Per Hedbor  return obuf; }
d7535b1999-04-06Per Hedbor /* Pike functions. */
3fa2791999-04-06Per Hedbor /* **! method object _decode(string data)
d7535b1999-04-06Per Hedbor **! Decodes a Targa image to a mapping.
c2ed772000-03-09Per Hedbor **! The mapping follows this format:
3fa2791999-04-06Per Hedbor **! ([ "image":img_object, "alpha":alpha_channel ]) **! **! note
a4a1722000-12-05Per Hedbor **! Throws upon error in data.
3fa2791999-04-06Per Hedbor */ void image_tga__decode( INT32 args ) { struct pike_string *data; struct image_alpha i;
391ac52018-08-05Martin Nilsson  get_all_args( NULL, args, "%S", &data );
3fa2791999-04-06Per Hedbor  i = load_image( data ); pop_n_elems(args);
c2ed772000-03-09Per Hedbor 
5e9fc02015-08-18Per Hedbor  push_static_text( "alpha" );
3fa2791999-04-06Per Hedbor  push_object( i.ao );
5e9fc02015-08-18Per Hedbor  push_static_text( "image" );
3fa2791999-04-06Per Hedbor  push_object( i.io );
8db2c31999-05-23Mirar (Pontus Hagland) 
6a932b2014-08-18Martin Nilsson  ref_push_string( literal_type_string );
5e9fc02015-08-18Per Hedbor  push_static_text( "image/x-targa" );
8db2c31999-05-23Mirar (Pontus Hagland) 
5e9fc02015-08-18Per Hedbor  push_static_text( "xsize" );
8db2c31999-05-23Mirar (Pontus Hagland)  push_int( i.img->xsize );
5e9fc02015-08-18Per Hedbor  push_static_text( "ysize" );
8db2c31999-05-23Mirar (Pontus Hagland)  push_int( i.img->ysize ); f_aggregate_mapping( 10 );
3fa2791999-04-06Per Hedbor } /* **! method object decode(string data)
c2ed772000-03-09Per Hedbor **! Decodes a Targa image.
3fa2791999-04-06Per Hedbor **! **! note
a4a1722000-12-05Per Hedbor **! Throws upon error in data.
3fa2791999-04-06Per Hedbor */ void image_tga_decode( INT32 args ) { struct pike_string *data; struct image_alpha i;
391ac52018-08-05Martin Nilsson  get_all_args( NULL, args, "%S", &data );
3fa2791999-04-06Per Hedbor  i = load_image(data); pop_n_elems(args); free_object( i.ao ); push_object( i.io ); } /* **! method string encode(object image) **! method string encode(object image, mapping options)
c2ed772000-03-09Per Hedbor **! Encodes a Targa image.
3fa2791999-04-06Per Hedbor **! **! The <tt>options</tt> argument may be a mapping **! containing zero or more encoding options: **! **! <pre> **! normal options: **! "alpha":image object
c2ed772000-03-09Per Hedbor **! Use this image as alpha channel
d7535b1999-04-06Per Hedbor **! (Note: Targa alpha channel is grey.
3fa2791999-04-06Per Hedbor **! The values are calculated by (r+2g+b)/4.) **! **! "raw":1 **! Do not RLE encode the image **! **! </pre> **! */ static struct pike_string *param_raw; static struct pike_string *param_alpha; void image_tga_encode( INT32 args ) { struct image *img = NULL; struct image *alpha = NULL; struct buffer buf; int rle = 1; if (!args)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Image.TGA.encode: too few arguments\n");
c2ed772000-03-09Per Hedbor 
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(Pike_sp[-args]) != PIKE_T_OBJECT ||
13b5ed2014-05-26Per Hedbor  !(img=get_storage(Pike_sp[-args].u.object,image_program)))
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Image.TGA.encode: illegal argument 1\n");
c2ed772000-03-09Per Hedbor 
3fa2791999-04-06Per Hedbor  if (!img->img)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Image.TGA.encode: no image\n");
3fa2791999-04-06Per Hedbor  if (args>1) {
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(Pike_sp[1-args]) != PIKE_T_MAPPING)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Image.TGA.encode: illegal argument 2\n");
c2ed772000-03-09Per Hedbor 
5923b12000-07-06Fredrik Hübinette (Hubbe)  push_svalue(Pike_sp+1-args);
3fa2791999-04-06Per Hedbor  ref_push_string(param_alpha); f_index(2);
017b572011-10-28Henrik Grubbström (Grubba)  if (!(TYPEOF(Pike_sp[-1]) == PIKE_T_INT && SUBTYPEOF(Pike_sp[-1]) == NUMBER_UNDEFINED)) if (TYPEOF(Pike_sp[-1]) != PIKE_T_OBJECT ||
13b5ed2014-05-26Per Hedbor  !(alpha=get_storage(Pike_sp[-1].u.object,image_program)))
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Image.TGA.encode: option (arg 2) \"alpha\" has illegal type\n");
3fa2791999-04-06Per Hedbor  pop_stack(); if (alpha && (alpha->xsize!=img->xsize || alpha->ysize!=img->ysize))
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Image.TGA.encode option (arg 2) \"alpha\"; images differ in size\n");
3fa2791999-04-06Per Hedbor  if (alpha && !alpha->img)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Image.TGA.encode option (arg 2) \"alpha\"; no image\n");
3fa2791999-04-06Per Hedbor 
5923b12000-07-06Fredrik Hübinette (Hubbe)  push_svalue(Pike_sp+1-args);
c2ed772000-03-09Per Hedbor  ref_push_string(param_raw);
3fa2791999-04-06Per Hedbor  f_index(2);
5923b12000-07-06Fredrik Hübinette (Hubbe)  rle = !Pike_sp[-1].u.integer;
3fa2791999-04-06Per Hedbor  pop_stack(); } buf = save_tga( img, alpha, rle ); pop_n_elems(args); push_string( make_shared_binary_string( buf.str, buf.len )); free(buf.str); } static struct program *image_encoding_tga_program=NULL; void init_image_tga( ) {
226ec82005-01-23Martin Nilsson  ADD_FUNCTION( "_decode", image_tga__decode, tFunc(tStr,tMap(tStr,tObj)), 0); ADD_FUNCTION( "decode", image_tga_decode, tFunc(tStr,tObj), 0); ADD_FUNCTION( "encode", image_tga_encode, tFunc(tObj tOr(tVoid,tMapping),tStr), 0);
8db2c31999-05-23Mirar (Pontus Hagland) 
3fa2791999-04-06Per Hedbor  param_alpha=make_shared_string("alpha"); param_raw=make_shared_string("raw"); } void exit_image_tga(void) {
8db2c31999-05-23Mirar (Pontus Hagland)  free_string(param_alpha); free_string(param_raw);
3fa2791999-04-06Per Hedbor }