Blame src/gd_tga.c

Packit Service df60bb
/**
Packit Service df60bb
 * File: TGA Input
Packit Service df60bb
 *
Packit Service df60bb
 * Read TGA images.
Packit Service df60bb
 */
Packit Service df60bb
Packit Service df60bb
#ifdef HAVE_CONFIG_H
Packit Service df60bb
#include "config.h"
Packit Service df60bb
#endif /* HAVE_CONFIG_H */
Packit Service df60bb
Packit Service df60bb
#include <stdio.h>
Packit Service df60bb
#include <stddef.h>
Packit Service df60bb
#include <stdlib.h>
Packit Service df60bb
#include <string.h>
Packit Service df60bb
Packit Service df60bb
#include "gd_tga.h"
Packit Service df60bb
#include "gd.h"
Packit Service df60bb
#include "gd_errors.h"
Packit Service df60bb
#include "gdhelpers.h"
Packit Service df60bb
Packit Service df60bb
/*
Packit Service df60bb
	Function: gdImageCreateFromTga
Packit Service df60bb
Packit Service df60bb
	Creates a gdImage from a TGA file
Packit Service df60bb
Packit Service df60bb
	Parameters:
Packit Service df60bb
Packit Service df60bb
		infile - Pointer to TGA binary file
Packit Service df60bb
 */
Packit Service df60bb
BGD_DECLARE(gdImagePtr) gdImageCreateFromTga(FILE *fp)
Packit Service df60bb
{
Packit Service df60bb
	gdImagePtr image;
Packit Service df60bb
	gdIOCtx* in = gdNewFileCtx(fp);
Packit Service df60bb
	if (in == NULL) return NULL;
Packit Service df60bb
	image = gdImageCreateFromTgaCtx(in);
Packit Service df60bb
	in->gd_free( in );
Packit Service df60bb
	return image;
Packit Service df60bb
}
Packit Service df60bb
Packit Service df60bb
/*
Packit Service df60bb
	Function: gdImageCreateFromTgaPtr
Packit Service df60bb
*/
Packit Service df60bb
BGD_DECLARE(gdImagePtr) gdImageCreateFromTgaPtr(int size, void *data)
Packit Service df60bb
{
Packit Service df60bb
	gdImagePtr im;
Packit Service df60bb
	gdIOCtx *in = gdNewDynamicCtxEx (size, data, 0);
Packit Service df60bb
	if (in == NULL) return NULL;
Packit Service df60bb
	im = gdImageCreateFromTgaCtx(in);
Packit Service df60bb
	in->gd_free(in);
Packit Service df60bb
	return im;
Packit Service df60bb
}
Packit Service df60bb
Packit Service df60bb
Packit Service df60bb
/*
Packit Service df60bb
	Function: gdImageCreateFromTgaCtx
Packit Service df60bb
Packit Service df60bb
	Creates a gdImage from a gdIOCtx referencing a TGA binary file.
Packit Service df60bb
Packit Service df60bb
	Parameters:
Packit Service df60bb
		ctx - Pointer to a gdIOCtx structure
Packit Service df60bb
 */
Packit Service df60bb
BGD_DECLARE(gdImagePtr) gdImageCreateFromTgaCtx(gdIOCtx* ctx)
Packit Service df60bb
{
Packit Service df60bb
	int bitmap_caret = 0;
Packit Service df60bb
	oTga *tga = NULL;
Packit Service df60bb
	/*	int pixel_block_size = 0;
Packit Service df60bb
		int image_block_size = 0; */
Packit Service df60bb
	volatile gdImagePtr image = NULL;
Packit Service df60bb
	int x = 0;
Packit Service df60bb
	int y = 0;
Packit Service df60bb
Packit Service df60bb
	tga = (oTga *) gdMalloc(sizeof(oTga));
Packit Service df60bb
	if (!tga) {
Packit Service df60bb
		return NULL;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	tga->bitmap = NULL;
Packit Service df60bb
	tga->ident = NULL;
Packit Service df60bb
Packit Service df60bb
	if (read_header_tga(ctx, tga) < 0) {
Packit Service df60bb
		free_tga(tga);
Packit Service df60bb
		return NULL;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	/*TODO: Will this be used?
Packit Service df60bb
		pixel_block_size = tga->bits / 8;
Packit Service df60bb
		image_block_size = (tga->width * tga->height) * pixel_block_size;
Packit Service df60bb
	*/
Packit Service df60bb
Packit Service df60bb
	if (read_image_tga(ctx, tga) < 0) {
Packit Service df60bb
		free_tga(tga);
Packit Service df60bb
		return NULL;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	image = gdImageCreateTrueColor((int)tga->width, (int)tga->height );
Packit Service df60bb
Packit Service df60bb
	if (image == 0) {
Packit Service df60bb
		free_tga( tga );
Packit Service df60bb
		return NULL;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	/*!	\brief Populate GD image object
Packit Service df60bb
	 *  Copy the pixel data from our tga bitmap buffer into the GD image
Packit Service df60bb
	 *  Disable blending and save the alpha channel per default
Packit Service df60bb
	 */
Packit Service df60bb
	if (tga->alphabits) {
Packit Service df60bb
		gdImageAlphaBlending(image, 0);
Packit Service df60bb
		gdImageSaveAlpha(image, 1);
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	/* TODO: use alphabits as soon as we support 24bit and other alpha bps (ie != 8bits) */
Packit Service df60bb
	for (y = 0; y < tga->height; y++) {
Packit Service df60bb
		register int *tpix = image->tpixels[y];
Packit Service df60bb
		for ( x = 0; x < tga->width; x++, tpix++) {
Packit Service df60bb
			if (tga->bits == TGA_BPP_24) {
Packit Service df60bb
				*tpix = gdTrueColor(tga->bitmap[bitmap_caret + 2], tga->bitmap[bitmap_caret + 1], tga->bitmap[bitmap_caret]);
Packit Service df60bb
				bitmap_caret += 3;
Packit Service df60bb
			} else if (tga->bits == TGA_BPP_32 && tga->alphabits) {
Packit Service df60bb
				register int a = tga->bitmap[bitmap_caret + 3];
Packit Service df60bb
Packit Service df60bb
				*tpix = gdTrueColorAlpha(tga->bitmap[bitmap_caret + 2], tga->bitmap[bitmap_caret + 1], tga->bitmap[bitmap_caret], gdAlphaMax - (a >> 1));
Packit Service df60bb
				bitmap_caret += 4;
Packit Service df60bb
			}
Packit Service df60bb
		}
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	if (tga->flipv && tga->fliph) {
Packit Service df60bb
		gdImageFlipBoth(image);
Packit Service df60bb
	} else if (tga->flipv) {
Packit Service df60bb
		gdImageFlipVertical(image);
Packit Service df60bb
	} else if (tga->fliph) {
Packit Service df60bb
		gdImageFlipHorizontal(image);
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	free_tga(tga);
Packit Service df60bb
Packit Service df60bb
	return image;
Packit Service df60bb
}
Packit Service df60bb
Packit Service df60bb
/*!	\brief Reads a TGA header.
Packit Service df60bb
 *	Reads the header block from a binary TGA file populating the referenced TGA structure.
Packit Service df60bb
 *	\param ctx Pointer to TGA binary file
Packit Service df60bb
 *	\param tga Pointer to TGA structure
Packit Service df60bb
 *	\return int 1 on sucess, -1 on failure
Packit Service df60bb
 */
Packit Service df60bb
int read_header_tga(gdIOCtx *ctx, oTga *tga)
Packit Service df60bb
{
Packit Service df60bb
Packit Service df60bb
	unsigned char header[18];
Packit Service df60bb
Packit Service df60bb
	if (gdGetBuf(header, sizeof(header), ctx) < 18) {
Packit Service df60bb
		gd_error("fail to read header");
Packit Service df60bb
		return -1;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	tga->identsize = header[0];
Packit Service df60bb
	tga->colormaptype = header[1];
Packit Service df60bb
	tga->imagetype = header[2];
Packit Service df60bb
	tga->colormapstart = header[3] + (header[4] << 8);
Packit Service df60bb
	tga->colormaplength = header[5] + (header[6] << 8);
Packit Service df60bb
	tga->colormapbits = header[7];
Packit Service df60bb
	tga->xstart = header[8] + (header[9] << 8);
Packit Service df60bb
	tga->ystart = header[10] + (header[11] << 8);
Packit Service df60bb
	tga->width = header[12] + (header[13] << 8);
Packit Service df60bb
	tga->height = header[14] + (header[15] << 8);
Packit Service df60bb
	tga->bits = header[16];
Packit Service df60bb
	tga->alphabits = header[17] & 0x0f;
Packit Service df60bb
	tga->fliph = (header[17] & 0x10) ? 1 : 0;
Packit Service df60bb
	tga->flipv = (header[17] & 0x20) ? 0 : 1;
Packit Service df60bb
Packit Service df60bb
#if DEBUG
Packit Service df60bb
	printf("format bps: %i\n", tga->bits);
Packit Service df60bb
	printf("flip h/v: %i / %i\n", tga->fliph, tga->flipv);
Packit Service df60bb
	printf("alpha: %i\n", tga->alphabits);
Packit Service df60bb
	printf("wxh: %i %i\n", tga->width, tga->height);
Packit Service df60bb
#endif
Packit Service df60bb
Packit Service df60bb
	if (!((tga->bits == TGA_BPP_24 && tga->alphabits == 0)
Packit Service df60bb
		|| (tga->bits == TGA_BPP_32 && tga->alphabits == 8)))
Packit Service df60bb
	{
Packit Service df60bb
		gd_error_ex(GD_WARNING, "gd-tga: %u bits per pixel with %u alpha bits not supported\n",
Packit Service df60bb
			tga->bits, tga->alphabits);
Packit Service df60bb
		return -1;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	tga->ident = NULL;
Packit Service df60bb
Packit Service df60bb
	if (tga->identsize > 0) {
Packit Service df60bb
		tga->ident = (char *) gdMalloc(tga->identsize * sizeof(char));
Packit Service df60bb
		if(tga->ident == NULL) {
Packit Service df60bb
			return -1;
Packit Service df60bb
		}
Packit Service df60bb
Packit Service df60bb
		gdGetBuf(tga->ident, tga->identsize, ctx);
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	return 1;
Packit Service df60bb
}
Packit Service df60bb
Packit Service df60bb
/*!	\brief Reads a TGA image data into buffer.
Packit Service df60bb
 *	Reads the image data block from a binary TGA file populating the referenced TGA structure.
Packit Service df60bb
 *	\param ctx Pointer to TGA binary file
Packit Service df60bb
 *	\param tga Pointer to TGA structure
Packit Service df60bb
 *	\return int 0 on sucess, -1 on failure
Packit Service df60bb
 */
Packit Service df60bb
int read_image_tga( gdIOCtx *ctx, oTga *tga )
Packit Service df60bb
{
Packit Service df60bb
	int pixel_block_size = (tga->bits / 8);
Packit Service df60bb
	int image_block_size = (tga->width * tga->height) * pixel_block_size;
Packit Service df60bb
	int* decompression_buffer = NULL;
Packit Service df60bb
	unsigned char* conversion_buffer = NULL;
Packit Service df60bb
	int buffer_caret = 0;
Packit Service df60bb
	int bitmap_caret = 0;
Packit Service df60bb
	int i = 0;
Packit Service df60bb
	int encoded_pixels;
Packit Service df60bb
	int rle_size;
Packit Service df60bb
Packit Service df60bb
	if(overflow2(tga->width, tga->height)) {
Packit Service df60bb
		return -1;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	if(overflow2(tga->width * tga->height, pixel_block_size)) {
Packit Service df60bb
		return -1;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	if(overflow2(image_block_size, sizeof(int))) {
Packit Service df60bb
		return -1;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	/*! \todo Add more image type support.
Packit Service df60bb
	 */
Packit Service df60bb
	if (tga->imagetype != TGA_TYPE_RGB && tga->imagetype != TGA_TYPE_RGB_RLE)
Packit Service df60bb
		return -1;
Packit Service df60bb
Packit Service df60bb
	/*!	\brief Allocate memmory for image block
Packit Service df60bb
	 *  Allocate a chunk of memory for the image block to be passed into.
Packit Service df60bb
	 */
Packit Service df60bb
	tga->bitmap = (int *) gdMalloc(image_block_size * sizeof(int));
Packit Service df60bb
	if (tga->bitmap == NULL)
Packit Service df60bb
		return -1;
Packit Service df60bb
Packit Service df60bb
	switch (tga->imagetype) {
Packit Service df60bb
	case TGA_TYPE_RGB:
Packit Service df60bb
		/*! \brief Read in uncompressed RGB TGA
Packit Service df60bb
		 *  Chunk load the pixel data from an uncompressed RGB type TGA.
Packit Service df60bb
		 */
Packit Service df60bb
		conversion_buffer = (unsigned char *) gdMalloc(image_block_size * sizeof(unsigned char));
Packit Service df60bb
		if (conversion_buffer == NULL) {
Packit Service df60bb
			return -1;
Packit Service df60bb
		}
Packit Service df60bb
Packit Service df60bb
		if (gdGetBuf(conversion_buffer, image_block_size, ctx) != image_block_size) {
Packit Service df60bb
			gd_error("gd-tga: premature end of image data\n");
Packit Service df60bb
			gdFree(conversion_buffer);
Packit Service df60bb
			return -1;
Packit Service df60bb
		}
Packit Service df60bb
Packit Service df60bb
		while (buffer_caret < image_block_size) {
Packit Service df60bb
			tga->bitmap[buffer_caret] = (int) conversion_buffer[buffer_caret];
Packit Service df60bb
			buffer_caret++;
Packit Service df60bb
		}
Packit Service df60bb
Packit Service df60bb
		gdFree(conversion_buffer);
Packit Service df60bb
		break;
Packit Service df60bb
Packit Service df60bb
	case TGA_TYPE_RGB_RLE:
Packit Service df60bb
		/*! \brief Read in RLE compressed RGB TGA
Packit Service df60bb
		 *  Chunk load the pixel data from an RLE compressed RGB type TGA.
Packit Service df60bb
		 */
Packit Service df60bb
		decompression_buffer = (int*) gdMalloc(image_block_size * sizeof(int));
Packit Service df60bb
		if (decompression_buffer == NULL) {
Packit Service df60bb
			return -1;
Packit Service df60bb
		}
Packit Service df60bb
		conversion_buffer = (unsigned char *) gdMalloc(image_block_size * sizeof(unsigned char));
Packit Service df60bb
		if (conversion_buffer == NULL) {
Packit Service df60bb
			gd_error("gd-tga: premature end of image data\n");
Packit Service df60bb
			gdFree( decompression_buffer );
Packit Service df60bb
			return -1;
Packit Service df60bb
		}
Packit Service df60bb
Packit Service df60bb
		rle_size = gdGetBuf(conversion_buffer, image_block_size, ctx);
Packit Service df60bb
		if (rle_size <= 0) {
Packit Service df60bb
			gdFree(conversion_buffer);
Packit Service df60bb
			gdFree(decompression_buffer);
Packit Service df60bb
			return -1;
Packit Service df60bb
		}
Packit Service df60bb
Packit Service df60bb
		buffer_caret = 0;
Packit Service df60bb
Packit Service df60bb
		while( buffer_caret < rle_size) {
Packit Service df60bb
			decompression_buffer[buffer_caret] = (int)conversion_buffer[buffer_caret];
Packit Service df60bb
			buffer_caret++;
Packit Service df60bb
		}
Packit Service df60bb
Packit Service df60bb
		buffer_caret = 0;
Packit Service df60bb
Packit Service df60bb
		while( bitmap_caret < image_block_size ) {
Packit Service df60bb
Packit Service df60bb
			if (buffer_caret + pixel_block_size > rle_size) {
Packit Service df60bb
				gdFree( decompression_buffer );
Packit Service df60bb
				gdFree( conversion_buffer );
Packit Service df60bb
				return -1;
Packit Service df60bb
			}
Packit Service df60bb
Packit Service df60bb
			if ((decompression_buffer[buffer_caret] & TGA_RLE_FLAG) == TGA_RLE_FLAG) {
Packit Service df60bb
				encoded_pixels = ( ( decompression_buffer[ buffer_caret ] & ~TGA_RLE_FLAG ) + 1 );
Packit Service df60bb
				buffer_caret++;
Packit Service df60bb
Packit Service df60bb
				if ((bitmap_caret + (encoded_pixels * pixel_block_size)) > image_block_size
Packit Service df60bb
						|| buffer_caret + pixel_block_size > rle_size) {
Packit Service df60bb
					gdFree( decompression_buffer );
Packit Service df60bb
					gdFree( conversion_buffer );
Packit Service df60bb
					return -1;
Packit Service df60bb
				}
Packit Service df60bb
Packit Service df60bb
				for (i = 0; i < encoded_pixels; i++) {
Packit Service df60bb
					memcpy(tga->bitmap + bitmap_caret, decompression_buffer + buffer_caret, pixel_block_size * sizeof(int));
Packit Service df60bb
					bitmap_caret += pixel_block_size;
Packit Service df60bb
				}
Packit Service df60bb
				buffer_caret += pixel_block_size;
Packit Service df60bb
Packit Service df60bb
			} else {
Packit Service df60bb
				encoded_pixels = decompression_buffer[ buffer_caret ] + 1;
Packit Service df60bb
				buffer_caret++;
Packit Service df60bb
Packit Service df60bb
				if ((bitmap_caret + (encoded_pixels * pixel_block_size)) > image_block_size
Packit Service df60bb
						|| buffer_caret + (encoded_pixels * pixel_block_size) > rle_size) {
Packit Service df60bb
					gdFree( decompression_buffer );
Packit Service df60bb
					gdFree( conversion_buffer );
Packit Service df60bb
					return -1;
Packit Service df60bb
				}
Packit Service df60bb
Packit Service df60bb
				memcpy(tga->bitmap + bitmap_caret, decompression_buffer + buffer_caret, encoded_pixels * pixel_block_size * sizeof(int));
Packit Service df60bb
				bitmap_caret += (encoded_pixels * pixel_block_size);
Packit Service df60bb
				buffer_caret += (encoded_pixels * pixel_block_size);
Packit Service df60bb
			}
Packit Service df60bb
		}
Packit Service df60bb
		gdFree( decompression_buffer );
Packit Service df60bb
		gdFree( conversion_buffer );
Packit Service df60bb
		break;
Packit Service df60bb
	}
Packit Service df60bb
Packit Service df60bb
	return 1;
Packit Service df60bb
}
Packit Service df60bb
Packit Service df60bb
/*!	\brief Cleans up a TGA structure.
Packit Service df60bb
 *	Dereferences the bitmap referenced in a TGA structure, then the structure itself
Packit Service df60bb
 *	\param tga Pointer to TGA structure
Packit Service df60bb
 */
Packit Service df60bb
void free_tga(oTga * tga)
Packit Service df60bb
{
Packit Service df60bb
	if (tga) {
Packit Service df60bb
		if (tga->ident)
Packit Service df60bb
			gdFree(tga->ident);
Packit Service df60bb
		if (tga->bitmap)
Packit Service df60bb
			gdFree(tga->bitmap);
Packit Service df60bb
		gdFree(tga);
Packit Service df60bb
	}
Packit Service df60bb
}