From 3fa27956f338a47b3dc1cc2d60f397d36a2a0e6a Mon Sep 17 00:00:00 2001 From: Per Hedbor <ph@opera.com> Date: Tue, 6 Apr 1999 02:37:29 +0200 Subject: [PATCH] Added TGA support (both encode and decode) Rev: src/modules/Image/encodings/Makefile.in:1.18 Rev: src/modules/Image/encodings/tga.c:1.1 --- .gitattributes | 1 + src/modules/Image/encodings/Makefile.in | 4 +- src/modules/Image/encodings/tga.c | 969 ++++++++++++++++++++++++ 3 files changed, 972 insertions(+), 2 deletions(-) create mode 100644 src/modules/Image/encodings/tga.c diff --git a/.gitattributes b/.gitattributes index 4f36f569fe..11b65eb44f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -184,6 +184,7 @@ testfont binary /src/modules/Image/encodings/gif_lzw.h foreign_ident /src/modules/Image/encodings/png.c foreign_ident /src/modules/Image/encodings/pnm.c foreign_ident +/src/modules/Image/encodings/tga.c foreign_ident /src/modules/Image/encodings/x.c foreign_ident /src/modules/Image/encodings/xwd.c foreign_ident /src/modules/Image/font.c foreign_ident diff --git a/src/modules/Image/encodings/Makefile.in b/src/modules/Image/encodings/Makefile.in index 8d5dd21582..e5770eb404 100644 --- a/src/modules/Image/encodings/Makefile.in +++ b/src/modules/Image/encodings/Makefile.in @@ -1,7 +1,7 @@ -# $Id: Makefile.in,v 1.17 1999/03/22 09:33:42 mirar Exp $ +# $Id: Makefile.in,v 1.18 1999/04/06 00:37:28 per Exp $ SRCDIR=@srcdir@ VPATH=@srcdir@:@srcdir@/../../..:../../.. -OBJS = gif.o gif_lzw.o pnm.o x.o xwd.o png.o any.o bmp.o +OBJS = gif.o gif_lzw.o pnm.o x.o xwd.o png.o any.o bmp.o tga.o @SET_MAKE@ diff --git a/src/modules/Image/encodings/tga.c b/src/modules/Image/encodings/tga.c new file mode 100644 index 0000000000..7aa97f752e --- /dev/null +++ b/src/modules/Image/encodings/tga.c @@ -0,0 +1,969 @@ + +/* + * $Id: tga.c,v 1.1 1999/04/06 00:37:29 per Exp $ + * + * TGA codec for pike. Based on the tga plugin for gimp. + * + * The information below is from the original TGA module. + * + * + * + * Id: tga.c,v 1.6 1999/01/15 17:34:47 unammx Exp + * 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 +**! +*/ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "interpret.h" +#include "svalue.h" +#include "pike_macros.h" +#include "object.h" +#include "program.h" +#include "array.h" +#include "error.h" +#include "constants.h" +#include "mapping.h" +#include "stralloc.h" +#include "multiset.h" +#include "pike_types.h" +#include "rusage.h" +#include "operators.h" +#include "fsort.h" +#include "callback.h" +#include "gc.h" +#include "backend.h" +#include "main.h" +#include "pike_memory.h" +#include "threads.h" +#include "time_stuff.h" +#include "version.h" +#include "encode.h" +#include "module_support.h" +#include "module.h" +#include "opcodes.h" +#include "cyclic.h" +#include "signal_handler.h" +#include "security.h" + + +#include "image.h" +#include "colortable.h" + +#ifndef MIN +# define MIN(X,Y) ((X)<(Y)?(X):(Y)) +#endif + +#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 +{ + unsigned int len; + 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; + struct tga_footer footer; + struct buffer buffer; + char *data_pointer; + int left_in_buffer; + INT32 image_ID = -1; + + buffer.str = str->str; + buffer.len = str->len; + + if(buffer.len < ((sizeof(struct tga_footer)+sizeof(struct tga_header)))) + error("Data (%d bytes) is too short\n", buffer.len); + +/* footer = *(struct tga_footer *)(buffer.str + */ +/* (buffer.len-sizeof(struct tga_footer))); */ +/* write(2, footer.signature, 16 ); */ + + 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; + + if(buffer.len < 10) + error("Not enough data in buffer to decode a TGA image\n"); + + return ReadImage (&buffer, &hdr); +} + +static int std_fread (unsigned char *buf, + int datasize, int nelems, struct buffer *fp) +{ + int amnt = MIN((nelems*datasize),((int)fp->len)); + MEMCPY(buf, fp->str, amnt); + fp->len -= amnt; + fp->str += amnt; + return amnt / datasize; +} + +static int std_fwrite (unsigned char *buf, + int datasize, int nelems, struct buffer *fp) +{ + int amnt = MIN((nelems*datasize),(int)fp->len); + MEMCPY(fp->str, buf, amnt); + 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. */ + +static int rle_fread (guchar *buf, int datasize, int nelems, struct buffer *fp) +{ + /* If we want to call this function more than once per image, change the + variables below to be static.. */ + guchar *statebuf = 0; + int statelen = 0; + int laststate = 0; + + /* end static variables.. */ + int j, k; + int buflen, count, bytes; + guchar *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 = std_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 (std_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 (std_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; +} + + +/* 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 + loading performance than whole-stream images. +*/ + +/* RunLength Encode a bufferful of file. */ +static int rle_fwrite (guchar *buf, int datasize, int nelems, + struct buffer *fp) +{ + /* Now runlength-encode the whole buffer. */ + int count, j, buflen; + 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. */ + if (std_fputc ((count - 1) | RLE_PACKETSIZE, fp) == EOF || + std_fwrite (begin, datasize, 1, fp) != 1) + 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. */ + if (std_fputc (count - 1, fp) == EOF || + std_fwrite (begin, datasize, count, fp) != count) + 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; +} + +static struct image_alpha ReadImage(struct buffer *fp, struct tga_header *hdr) +{ + int width, height, bpp, abpp, pbpp; + int i, j, k; + int pelbytes=0, npels, pels, read_so_far=0, rle=0; + unsigned char *cmap=NULL, *data; + int itype; + + int (*myfread)(unsigned char *, int, int, struct buffer *); + + /* 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) + pbpp = MIN (bpp / 3, 8) * 3; + 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; + } + + 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. */ + error ("TGA: index sizes other than 8 bits are unimplemented\n"); + 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 = RGB; + break; + + default: + error ("TGA: unrecognized image type %d\n", hdr->imageType); + } + + if ((abpp && abpp != 8) || + (itype == RGB && pbpp != 24) || + ((itype == GRAY || itype == INDEXED) && pbpp != 8)) + /* FIXME: We haven't implemented bit-packed fields yet. */ + error ("TGA: channel sizes other than 8 bits are unimplemented. bpp=%d;app=%d\n",pbpp,abpp); + + /* Check that we have a color map only when we need it. */ + if (itype == INDEXED) + { + if (hdr->colorMapType != 1) + error ("TGA: indexed image has invalid color map type %d\n", + hdr->colorMapType); + } + else if (hdr->colorMapType != 0) + { + error ("TGA: non-indexed image has invalid color map type %d\n", + hdr->colorMapType); + } + + if (hdr->colorMapType == 1) + { + /* We need to read in the colormap. */ + int index, length, colors; + int tmp; + + index = (hdr->colorMapIndexHi << 8) | hdr->colorMapIndexLo; + length = (hdr->colorMapLengthHi << 8) | hdr->colorMapLengthLo; + + if (length == 0) + error ("TGA: invalid color map length %d\n", length); + + pelbytes = ROUNDUP_DIVIDE (hdr->colorMapSize, 8); + colors = length + index; + cmap = 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 (std_fread (cmap + (index * pelbytes), pelbytes, length, fp) != length) + { + free(cmap); + error ("TGA: error reading colormap\n"); + } + + /* Now pretend as if we only have 8 bpp. */ + abpp = 0; + pbpp = 8; + } + + + /* Calculate TGA bytes per pixel. */ + bpp = ROUNDUP_DIVIDE (pbpp + abpp, 8); + + /* Allocate the data. */ + data = (guchar *) malloc (width * height * bpp); + + if (rle) + myfread = rle_fread; + else + myfread = std_fread; + + npels = width * height; + + /* Suck in the data. */ +/* do */ +/* { */ + pels = (*myfread) (data+(read_so_far*bpp), bpp, npels, fp); + read_so_far += pels; + npels -= pels; +/* if(pels <= 0) */ +/* { */ +/* pels = read_so_far; */ +/* break; */ +/* } */ +/* } while(npels); */ + if(npels) + MEMSET( data+(read_so_far*bpp), 0, npels*bpp ); + + /* Now convert the data to two image objects. */ + { + int x, y; + struct image_alpha i; + push_int( width ); + push_int( height ); + i.io = clone_object( image_program, 2 ); + i.img = (struct image*)get_storage(i.io,image_program); + push_int( width ); + push_int( height ); + push_int( 255 ); + push_int( 255 ); + push_int( 255 ); + i.ao = clone_object( image_program, 5 ); + i.alpha = (struct image*)get_storage(i.ao,image_program); + + { + rgb_group *id = i.img->img; + rgb_group *ad = i.alpha->img; + unsigned char *sd = data; + 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) + { + ad->r = ad->g = (ad++)->b = cmap[cmapind]; + } + sd++; + } + break; + case GRAY: + for(y = 0; y<height; y++) + for(x = 0; x<width; x++) + { + id->r = id->g = (id++)->b = *(sd++); + if(abpp) + { + ad->r = ad->g = (ad++)->b = *(sd++); + } + } + break; + case RGB: + for(y = 0; y<height; y++) + for(x = 0; x<width; x++) + { + id->b = *(sd++); + id->g = *(sd++); + (id++)->r = *(sd++); + if(abpp) { + ad->r = ad->g = (ad++)->b = *(sd++); + } + } + } + } + free (data); + if(cmap) free (cmap); + if(horzrev) + { + apply( i.io, "mirrorx", 0 ); + free_object(i.io); + i.io = sp[-1].u.object; + sp--; + apply( i.ao, "mirrorx", 0 ); + free_object(i.ao); + i.ao = sp[-1].u.object; + sp--; + } + if(vertrev) + { + apply( i.io, "mirrory", 0 ); + free_object(i.io); + i.io = sp[-1].u.object; + sp--; + apply( i.ao, "mirrory", 0 ); + free_object(i.ao); + i.ao = sp[-1].u.object; + sp--; + } + 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; + int i, j, k; + int pelbytes, bsize; + int transparent, status; + struct tga_header hdr; + int (*myfwrite)(unsigned char *, int, int, struct buffer *); + + unsigned char *data; + + if(alpha && + (alpha->xsize != img->xsize || + alpha->ysize != img->ysize )) + error("Alpha and image objects are not equally sized.\n"); + + 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; + + /* Choose the imageType based on alpha precense and compression options. */ + + 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. */ + hdr.idLength = strlen (SAVE_ID_STRING); + + buf.len = width*height*(alpha?4:3)+strlen(SAVE_ID_STRING)+sizeof(hdr)+65535; + buf.str = xalloc(buf.len); + obuf.len = buf.len; + obuf.str = buf.str; + + /* Just write the header. */ + if (std_fwrite((void *)&hdr, sizeof (hdr), 1, fp) != 1) + { + free(obuf.str); + error("Internal error: Out of space in buffer.\n"); + } + if (std_fwrite (SAVE_ID_STRING, hdr.idLength, 1, fp) != 1) + { + free(obuf.str); + error("Internal error: Out of space in buffer.\n"); + } + + /* 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++; + p = data = malloc( width*height*4 ); + datalen = width*height*4; + if(!data) + { + free(obuf.str); + error("Out of memory while encoding image\n"); + } + for(y=0; y<height; y++) + for(x=0; x<width; x++) + { + *(p++) = is->b; + *(p++) = is->g; + *(p++) = (is++)->r; + *(p++) = ((int)as->r+(int)as->g*2+(as++)->b)/4; + } + } else { + p = data = malloc( width*height*3 ); + datalen = width*height*3; + if(!data) + { + free(obuf.str); + error("Out of memory while encoding image\n"); + } + for(y=0; y<height; y++) + for(x=0; x<width; x++) + { + *(p++) = is->b; + *(p++) = is->g; + *(p++) = (is++)->r; + } + } + if ((*myfwrite) (data, pixsize,datalen/pixsize, fp) != datalen/pixsize) + { + free(data); + free(obuf.str); + error("Internal error: Out of space in buffer.\n"); + } + free(data); + } + obuf.len -= buf.len; + return obuf; +} + + + + + + + + + + + +/* Pike wrappers. */ +/* +**! method object _decode(string data) +**! Decodes a TGA image to a mapping. +**! The mapping follows this format: +**! ([ "image":img_object, "alpha":alpha_channel ]) +**! +**! note +**! Throws upon error in data. +*/ +void image_tga__decode( INT32 args ) +{ + struct pike_string *data; + struct image_alpha i; + get_all_args( "Image.TGA._decode", args, "%S", &data ); + i = load_image( data ); + + pop_n_elems(args); + + push_constant_text( "alpha" ); + push_object( i.ao ); + push_constant_text( "image" ); + push_object( i.io ); + f_aggregate_mapping( 4 ); +} + +/* +**! method object decode(string data) +**! Decodes a PNG image. +**! +**! note +**! Throws upon error in data. +*/ +void image_tga_decode( INT32 args ) +{ + struct pike_string *data; + struct image_alpha i; + get_all_args( "Image.TGA.decode", args, "%S", &data ); + 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) +**! Encodes a PNG image. +**! +**! The <tt>options</tt> argument may be a mapping +**! containing zero or more encoding options: +**! +**! <pre> +**! normal options: +**! "alpha":image object +**! Use this image as alpha channel +**! (Note: TGA alpha channel is grey. +**! The values are calculated by (r+2g+b)/4.) +**! +**! "raw":1 +**! Do not RLE encode the image +**! +**! </pre> +**! +**! note +**! Please read some about PNG files. +*/ + +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) + error("Image.TGA.encode: too few arguments\n"); + + if (sp[-args].type!=T_OBJECT || + !(img=(struct image*) + get_storage(sp[-args].u.object,image_program))) + error("Image.TGA.encode: illegal argument 1\n"); + + if (!img->img) + error("Image.TGA.encode: no image\n"); + + if (args>1) + { + if (sp[1-args].type!=T_MAPPING) + error("Image.TGA.encode: illegal argument 2\n"); + + push_svalue(sp+1-args); + ref_push_string(param_alpha); + f_index(2); + if (!(sp[-1].type==T_INT + && sp[-1].subtype==NUMBER_UNDEFINED)) + if (sp[-1].type!=T_OBJECT || + !(alpha=(struct image*) + get_storage(sp[-1].u.object,image_program))) + error("Image.TGA.encode: option (arg 2) \"alpha\" has illegal type\n"); + pop_stack(); + + if (alpha && + (alpha->xsize!=img->xsize || + alpha->ysize!=img->ysize)) + error("Image.TGA.encode option (arg 2) \"alpha\"; images differ in size\n"); + if (alpha && !alpha->img) + error("Image.TGA.encode option (arg 2) \"alpha\"; no image\n"); + + push_svalue(sp+1-args); + ref_push_string(param_raw); + f_index(2); + rle = !sp[-1].u.integer; + 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( ) +{ + start_new_program(); + add_function( "_decode", image_tga__decode, + "function(string:mapping(string:object))", 0); + add_function( "decode", image_tga_decode, + "function(string:object)", 0); + add_function( "encode", image_tga_encode, + "function(object,mapping|void:string)", 0); + image_encoding_tga_program=end_program(); + + push_object(clone_object(image_encoding_tga_program,0)); + { + struct pike_string *s=make_shared_string("TGA"); + add_constant(s,sp-1,0); + free_string(s); + } + param_alpha=make_shared_string("alpha"); + param_raw=make_shared_string("raw"); +} + +void exit_image_tga(void) +{ + if(image_encoding_tga_program) + { + free_program(image_encoding_tga_program); + image_encoding_tga_program=0; + free_string(param_alpha); + free_string(param_raw); + } +} -- GitLab