Blame src/gd_bmp.c

Packit ed3af9
/*
Packit ed3af9
	gd_bmp.c
Packit ed3af9
Packit ed3af9
	Bitmap format support for libgd
Packit ed3af9
Packit ed3af9
	* Written 2007, Scott MacVicar
Packit ed3af9
	---------------------------------------------------------------------------
Packit ed3af9
Packit ed3af9
	Todo:
Packit ed3af9
Packit ed3af9
	Bitfield encoding
Packit ed3af9
Packit ed3af9
	----------------------------------------------------------------------------
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
/**
Packit ed3af9
 * File: BMP IO
Packit ed3af9
 *
Packit ed3af9
 * Read and write BMP images.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
/* $Id$ */
Packit ed3af9
#ifdef HAVE_CONFIG_H
Packit ed3af9
#include "config.h"
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
#include <stdio.h>
Packit ed3af9
#include <math.h>
Packit ed3af9
#include <string.h>
Packit ed3af9
#include <stdlib.h>
Packit ed3af9
#include "gd.h"
Packit ed3af9
#include "gdhelpers.h"
Packit ed3af9
#include "bmp.h"
Packit ed3af9
Packit ed3af9
static int compress_row(unsigned char *uncompressed_row, int length);
Packit ed3af9
static int build_rle_packet(unsigned char *row, int packet_type, int length, unsigned char *data);
Packit ed3af9
Packit ed3af9
static int bmp_read_header(gdIOCtxPtr infile, bmp_hdr_t *hdr);
Packit ed3af9
static int bmp_read_info(gdIOCtxPtr infile, bmp_info_t *info);
Packit ed3af9
static int bmp_read_windows_v3_info(gdIOCtxPtr infile, bmp_info_t *info);
Packit ed3af9
static int bmp_read_os2_v1_info(gdIOCtxPtr infile, bmp_info_t *info);
Packit ed3af9
static int bmp_read_os2_v2_info(gdIOCtxPtr infile, bmp_info_t *info);
Packit ed3af9
Packit ed3af9
static int bmp_read_direct(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
Packit ed3af9
static int bmp_read_1bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
Packit ed3af9
static int bmp_read_4bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
Packit ed3af9
static int bmp_read_8bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
Packit ed3af9
static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info);
Packit ed3af9
Packit ed3af9
#define BMP_DEBUG(s)
Packit ed3af9
Packit ed3af9
static int gdBMPPutWord(gdIOCtx *out, int w)
Packit ed3af9
{
Packit ed3af9
	/* Byte order is little-endian */
Packit ed3af9
	gdPutC(w & 0xFF, out);
Packit ed3af9
	gdPutC((w >> 8) & 0xFF, out);
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int gdBMPPutInt(gdIOCtx *out, int w)
Packit ed3af9
{
Packit ed3af9
	/* Byte order is little-endian */
Packit ed3af9
	gdPutC(w & 0xFF, out);
Packit ed3af9
	gdPutC((w >> 8) & 0xFF, out);
Packit ed3af9
	gdPutC((w >> 16) & 0xFF, out);
Packit ed3af9
	gdPutC((w >> 24) & 0xFF, out);
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
	Function: gdImageBmpPtr
Packit ed3af9
Packit ed3af9
	Outputs the given image as BMP data, but using a <gdIOCtx> instead
Packit ed3af9
	of a file. See <gdImageBmp>.
Packit ed3af9
Packit ed3af9
	Parameters:
Packit ed3af9
		im			- the image to save.
Packit ed3af9
		size 		- Output: size in bytes of the result.
Packit ed3af9
		compression - whether to apply RLE or not.
Packit ed3af9
Packit ed3af9
	Returns:
Packit ed3af9
Packit ed3af9
		A pointer to memory containing the image data or NULL on error.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void *) gdImageBmpPtr(gdImagePtr im, int *size, int compression)
Packit ed3af9
{
Packit ed3af9
	void *rv;
Packit ed3af9
	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
Packit ed3af9
	if (out == NULL) return NULL;
Packit ed3af9
	gdImageBmpCtx(im, out, compression);
Packit ed3af9
	rv = gdDPExtractData(out, size);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
	return rv;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
	Function: gdImageBmp
Packit ed3af9
Packit ed3af9
    <gdImageBmp> outputs the specified image to the specified file in
Packit ed3af9
    BMP format. The file must be open for writing. Under MSDOS and all
Packit ed3af9
    versions of Windows, it is important to use "wb" as opposed to
Packit ed3af9
    simply "w" as the mode when opening the file, and under Unix there
Packit ed3af9
    is no penalty for doing so. <gdImageBmp> does not close the file;
Packit ed3af9
    your code must do so.
Packit ed3af9
Packit ed3af9
    In addition, <gdImageBmp> allows to specify whether RLE compression
Packit ed3af9
    should be applied.
Packit ed3af9
Packit ed3af9
	Variants:
Packit ed3af9
Packit ed3af9
		<gdImageBmpCtx> write via a <gdIOCtx> instead of a file handle.
Packit ed3af9
Packit ed3af9
		<gdImageBmpPtr> store the image file to memory.
Packit ed3af9
Packit ed3af9
	Parameters:
Packit ed3af9
Packit ed3af9
		im			- the image to save.
Packit ed3af9
		outFile		- the output FILE* object.
Packit ed3af9
		compression - whether to apply RLE or not.
Packit ed3af9
Packit ed3af9
	Returns:
Packit ed3af9
		nothing
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImageBmp(gdImagePtr im, FILE *outFile, int compression)
Packit ed3af9
{
Packit ed3af9
	gdIOCtx *out = gdNewFileCtx(outFile);
Packit ed3af9
	if (out == NULL) return;
Packit ed3af9
	gdImageBmpCtx(im, out, compression);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
	Function: gdImageBmpCtx
Packit ed3af9
Packit ed3af9
	Outputs the given image as BMP data, but using a <gdIOCtx> instead
Packit ed3af9
	of a file. See <gdImageBmp>.
Packit ed3af9
Packit ed3af9
	Parameters:
Packit ed3af9
		im			- the image to save.
Packit ed3af9
		out 		- the <gdIOCtx> to write to.
Packit ed3af9
		compression - whether to apply RLE or not.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
Packit ed3af9
{
Packit ed3af9
	int bitmap_size = 0, info_size, total_size, padding;
Packit ed3af9
	int i, row, xpos, pixel;
Packit ed3af9
	int error = 0;
Packit ed3af9
	unsigned char *uncompressed_row = NULL, *uncompressed_row_start = NULL;
Packit ed3af9
	FILE *tmpfile_for_compression = NULL;
Packit ed3af9
	gdIOCtxPtr out_original = NULL;
Packit ed3af9
Packit ed3af9
	/* No compression if its true colour or we don't support seek */
Packit ed3af9
	if (im->trueColor) {
Packit ed3af9
		compression = 0;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (compression && !out->seek) {
Packit ed3af9
		/* Try to create a temp file where we can seek */
Packit ed3af9
		if ((tmpfile_for_compression = tmpfile()) == NULL) {
Packit ed3af9
			compression = 0;
Packit ed3af9
		} else {
Packit ed3af9
			out_original = out;
Packit ed3af9
			if ((out = (gdIOCtxPtr)gdNewFileCtx(tmpfile_for_compression)) == NULL) {
Packit ed3af9
				out = out_original;
Packit ed3af9
				out_original = NULL;
Packit ed3af9
				compression = 0;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	bitmap_size = ((im->sx * (im->trueColor ? 24 : 8)) / 8) * im->sy;
Packit ed3af9
Packit ed3af9
	/* 40 byte Windows v3 header */
Packit ed3af9
	info_size = BMP_WINDOWS_V3;
Packit ed3af9
Packit ed3af9
	/* data for the palette */
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		info_size += im->colorsTotal * 4;
Packit ed3af9
		if (compression) {
Packit ed3af9
			bitmap_size = 0;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* bitmap header + info header + data */
Packit ed3af9
	total_size = 14 + info_size + bitmap_size;
Packit ed3af9
Packit ed3af9
	/* write bmp header info */
Packit ed3af9
	gdPutBuf("BM", 2, out);
Packit ed3af9
	gdBMPPutInt(out, total_size);
Packit ed3af9
	gdBMPPutWord(out, 0);
Packit ed3af9
	gdBMPPutWord(out, 0);
Packit ed3af9
	gdBMPPutInt(out, 14 + info_size);
Packit ed3af9
Packit ed3af9
	/* write Windows v3 headers */
Packit ed3af9
	gdBMPPutInt(out, BMP_WINDOWS_V3); /* header size */
Packit ed3af9
	gdBMPPutInt(out, im->sx); /* width */
Packit ed3af9
	gdBMPPutInt(out, im->sy); /* height */
Packit ed3af9
	gdBMPPutWord(out, 1); /* colour planes */
Packit ed3af9
	gdBMPPutWord(out, (im->trueColor ? 24 : 8)); /* bit count */
Packit ed3af9
	gdBMPPutInt(out, (compression ? BMP_BI_RLE8 : BMP_BI_RGB)); /* compression */
Packit ed3af9
	gdBMPPutInt(out, bitmap_size); /* image size */
Packit ed3af9
	gdBMPPutInt(out, 0); /* H resolution */
Packit ed3af9
	gdBMPPutInt(out, 0); /* V ressolution */
Packit ed3af9
	gdBMPPutInt(out, im->colorsTotal); /* colours used */
Packit ed3af9
	gdBMPPutInt(out, 0); /* important colours */
Packit ed3af9
Packit ed3af9
	/* The line must be divisible by 4, else its padded with NULLs */
Packit ed3af9
	padding = ((int)(im->trueColor ? 3 : 1) * im->sx) % 4;
Packit ed3af9
	if (padding) {
Packit ed3af9
		padding = 4 - padding;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* 8-bit colours */
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		for(i = 0; i< im->colorsTotal; ++i) {
Packit ed3af9
			Putchar(gdImageBlue(im, i), out);
Packit ed3af9
			Putchar(gdImageGreen(im, i), out);
Packit ed3af9
			Putchar(gdImageRed(im, i), out);
Packit ed3af9
			Putchar(0, out);
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		if (compression) {
Packit ed3af9
			/* Can potentially change this to X + ((X / 128) * 3) */
Packit ed3af9
			uncompressed_row = uncompressed_row_start = (unsigned char *) gdCalloc(gdImageSX(im) * 2, sizeof(char));
Packit ed3af9
			if (!uncompressed_row) {
Packit ed3af9
				/* malloc failed */
Packit ed3af9
				goto cleanup;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		for (row = (im->sy - 1); row >= 0; row--) {
Packit ed3af9
			if (compression) {
Packit ed3af9
				memset (uncompressed_row, 0, gdImageSX(im));
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			for (xpos = 0; xpos < im->sx; xpos++) {
Packit ed3af9
				if (compression) {
Packit ed3af9
					*uncompressed_row++ = (unsigned char)gdImageGetPixel(im, xpos, row);
Packit ed3af9
				} else {
Packit ed3af9
					Putchar(gdImageGetPixel(im, xpos, row), out);
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			if (!compression) {
Packit ed3af9
				/* Add padding to make sure we have n mod 4 == 0 bytes per row */
Packit ed3af9
				for (xpos = padding; xpos > 0; --xpos) {
Packit ed3af9
					Putchar('\0', out);
Packit ed3af9
				}
Packit ed3af9
			} else {
Packit ed3af9
				int compressed_size = 0;
Packit ed3af9
				uncompressed_row = uncompressed_row_start;
Packit ed3af9
				if ((compressed_size = compress_row(uncompressed_row, gdImageSX(im))) < 0) {
Packit ed3af9
					error = 1;
Packit ed3af9
					break;
Packit ed3af9
				}
Packit ed3af9
				bitmap_size += compressed_size;
Packit ed3af9
Packit ed3af9
Packit ed3af9
				gdPutBuf(uncompressed_row, compressed_size, out);
Packit ed3af9
				Putchar(BMP_RLE_COMMAND, out);
Packit ed3af9
				Putchar(BMP_RLE_ENDOFLINE, out);
Packit ed3af9
				bitmap_size += 2;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		if (compression && uncompressed_row) {
Packit ed3af9
			gdFree(uncompressed_row);
Packit ed3af9
			if (error != 0) {
Packit ed3af9
				goto cleanup;
Packit ed3af9
			}
Packit ed3af9
			/* Update filesize based on new values and set compression flag */
Packit ed3af9
			Putchar(BMP_RLE_COMMAND, out);
Packit ed3af9
			Putchar(BMP_RLE_ENDOFBITMAP, out);
Packit ed3af9
			bitmap_size += 2;
Packit ed3af9
Packit ed3af9
			/* Write new total bitmap size */
Packit ed3af9
			gdSeek(out, 2);
Packit ed3af9
			gdBMPPutInt(out, total_size + bitmap_size);
Packit ed3af9
Packit ed3af9
			/* Total length of image data */
Packit ed3af9
			gdSeek(out, 34);
Packit ed3af9
			gdBMPPutInt(out, bitmap_size);
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
	} else {
Packit ed3af9
		for (row = (im->sy - 1); row >= 0; row--) {
Packit ed3af9
			for (xpos = 0; xpos < im->sx; xpos++) {
Packit ed3af9
				pixel = gdImageGetPixel(im, xpos, row);
Packit ed3af9
Packit ed3af9
				Putchar(gdTrueColorGetBlue(pixel), out);
Packit ed3af9
				Putchar(gdTrueColorGetGreen(pixel), out);
Packit ed3af9
				Putchar(gdTrueColorGetRed(pixel), out);
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			/* Add padding to make sure we have n mod 4 == 0 bytes per row */
Packit ed3af9
			for (xpos = padding; xpos > 0; --xpos) {
Packit ed3af9
				Putchar('\0', out);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
Packit ed3af9
	/* If we needed a tmpfile for compression copy it over to out_original */
Packit ed3af9
	if (tmpfile_for_compression) {
Packit ed3af9
		unsigned char* copy_buffer = NULL;
Packit ed3af9
		int buffer_size = 0;
Packit ed3af9
Packit ed3af9
		gdSeek(out, 0);
Packit ed3af9
		copy_buffer = (unsigned char *) gdMalloc(1024 * sizeof(unsigned char));
Packit ed3af9
		if (copy_buffer == NULL) {
Packit ed3af9
			goto cleanup;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		while ((buffer_size = gdGetBuf(copy_buffer, 1024, out)) != EOF) {
Packit ed3af9
			if (buffer_size == 0) {
Packit ed3af9
				break;
Packit ed3af9
			}
Packit ed3af9
			gdPutBuf(copy_buffer , buffer_size, out_original);
Packit ed3af9
		}
Packit ed3af9
		gdFree(copy_buffer);
Packit ed3af9
Packit ed3af9
		/* Replace the temp with the original which now has data */
Packit ed3af9
		out->gd_free(out);
Packit ed3af9
		out = out_original;
Packit ed3af9
		out_original = NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
cleanup:
Packit ed3af9
	if (tmpfile_for_compression) {
Packit ed3af9
#ifdef _WIN32
Packit ed3af9
		_rmtmp();
Packit ed3af9
#else
Packit ed3af9
		fclose(tmpfile_for_compression);
Packit ed3af9
#endif
Packit ed3af9
		tmpfile_for_compression = NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (out_original) {
Packit ed3af9
		out_original->gd_free(out_original);
Packit ed3af9
	}
Packit ed3af9
	return;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int compress_row(unsigned char *row, int length)
Packit ed3af9
{
Packit ed3af9
	int rle_type = 0;
Packit ed3af9
	int compressed_length = 0;
Packit ed3af9
	int pixel = 0, compressed_run = 0, rle_compression = 0;
Packit ed3af9
	unsigned char *uncompressed_row = NULL, *uncompressed_rowp = NULL, *uncompressed_start = NULL;
Packit ed3af9
Packit ed3af9
	uncompressed_row = (unsigned char *) gdMalloc(length);
Packit ed3af9
	if (!uncompressed_row) {
Packit ed3af9
		return -1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	memcpy(uncompressed_row, row, length);
Packit ed3af9
	uncompressed_start = uncompressed_rowp = uncompressed_row;
Packit ed3af9
Packit ed3af9
	for (pixel = 0; pixel < length; pixel++) {
Packit ed3af9
		if (compressed_run == 0) {
Packit ed3af9
			uncompressed_row = uncompressed_rowp;
Packit ed3af9
			compressed_run++;
Packit ed3af9
			uncompressed_rowp++;
Packit ed3af9
			rle_type = BMP_RLE_TYPE_RAW;
Packit ed3af9
			continue;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		if (compressed_run == 1) {
Packit ed3af9
			/* Compare next byte */
Packit ed3af9
			if (memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) {
Packit ed3af9
				rle_type = BMP_RLE_TYPE_RLE;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		if (rle_type == BMP_RLE_TYPE_RLE) {
Packit ed3af9
			if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) != 0) {
Packit ed3af9
				/* more than what we can store in a single run or run is over due to non match, force write */
Packit ed3af9
				rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row);
Packit ed3af9
				row += rle_compression;
Packit ed3af9
				compressed_length += rle_compression;
Packit ed3af9
				compressed_run = 0;
Packit ed3af9
				pixel--;
Packit ed3af9
			} else {
Packit ed3af9
				compressed_run++;
Packit ed3af9
				uncompressed_rowp++;
Packit ed3af9
			}
Packit ed3af9
		} else {
Packit ed3af9
			if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) {
Packit ed3af9
				/* more than what we can store in a single run or run is over due to match, force write */
Packit ed3af9
				rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row);
Packit ed3af9
				row += rle_compression;
Packit ed3af9
				compressed_length += rle_compression;
Packit ed3af9
				compressed_run = 0;
Packit ed3af9
				pixel--;
Packit ed3af9
			} else {
Packit ed3af9
				/* add this pixel to the row */
Packit ed3af9
				compressed_run++;
Packit ed3af9
				uncompressed_rowp++;
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (compressed_run) {
Packit ed3af9
		compressed_length += build_rle_packet(row, rle_type, compressed_run, uncompressed_row);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	gdFree(uncompressed_start);
Packit ed3af9
Packit ed3af9
	return compressed_length;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int build_rle_packet(unsigned char *row, int packet_type, int length, unsigned char *data)
Packit ed3af9
{
Packit ed3af9
	int compressed_size = 0;
Packit ed3af9
	if (length < 1 || length > 128) {
Packit ed3af9
		return 0;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Bitmap specific cases is that we can't have uncompressed rows of length 1 or 2 */
Packit ed3af9
	if (packet_type == BMP_RLE_TYPE_RAW && length < 3) {
Packit ed3af9
		int i = 0;
Packit ed3af9
		for (i = 0; i < length; i++) {
Packit ed3af9
			compressed_size += 2;
Packit ed3af9
			memset(row, 1, 1);
Packit ed3af9
			row++;
Packit ed3af9
Packit ed3af9
			memcpy(row, data++, 1);
Packit ed3af9
			row++;
Packit ed3af9
		}
Packit ed3af9
	} else if (packet_type == BMP_RLE_TYPE_RLE) {
Packit ed3af9
		compressed_size = 2;
Packit ed3af9
		memset(row, length, 1);
Packit ed3af9
		row++;
Packit ed3af9
Packit ed3af9
		memcpy(row, data, 1);
Packit ed3af9
		row++;
Packit ed3af9
	} else {
Packit ed3af9
		compressed_size = 2 + length;
Packit ed3af9
		memset(row, BMP_RLE_COMMAND, 1);
Packit ed3af9
		row++;
Packit ed3af9
Packit ed3af9
		memset(row, length, 1);
Packit ed3af9
		row++;
Packit ed3af9
Packit ed3af9
		memcpy(row, data, length);
Packit ed3af9
		row += length;
Packit ed3af9
Packit ed3af9
		/* Must be an even number for an uncompressed run */
Packit ed3af9
		if (length % 2) {
Packit ed3af9
			memset(row, 0, 1);
Packit ed3af9
			row++;
Packit ed3af9
			compressed_size++;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	return compressed_size;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
	Function: gdImageCreateFromBmp
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromBmp(FILE * inFile)
Packit ed3af9
{
Packit ed3af9
	gdImagePtr im = 0;
Packit ed3af9
	gdIOCtx *in = gdNewFileCtx(inFile);
Packit ed3af9
	if (in == NULL) return NULL;
Packit ed3af9
	im = gdImageCreateFromBmpCtx(in);
Packit ed3af9
	in->gd_free(in);
Packit ed3af9
	return im;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
	Function: gdImageCreateFromBmpPtr
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromBmpPtr(int size, void *data)
Packit ed3af9
{
Packit ed3af9
	gdImagePtr im;
Packit ed3af9
	gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
Packit ed3af9
	if (in == NULL) return NULL;
Packit ed3af9
	im = gdImageCreateFromBmpCtx(in);
Packit ed3af9
	in->gd_free(in);
Packit ed3af9
	return im;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
	Function: gdImageCreateFromBmpCtx
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromBmpCtx(gdIOCtxPtr infile)
Packit ed3af9
{
Packit ed3af9
	bmp_hdr_t *hdr;
Packit ed3af9
	bmp_info_t *info;
Packit ed3af9
	gdImagePtr im = NULL;
Packit ed3af9
	int error = 0;
Packit ed3af9
Packit ed3af9
	if (!(hdr= (bmp_hdr_t *)gdCalloc(1, sizeof(bmp_hdr_t)))) {
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (bmp_read_header(infile, hdr)) {
Packit ed3af9
		gdFree(hdr);
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (hdr->magic != 0x4d42) {
Packit ed3af9
		gdFree(hdr);
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (!(info = (bmp_info_t *)gdCalloc(1, sizeof(bmp_info_t)))) {
Packit ed3af9
		gdFree(hdr);
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (bmp_read_info(infile, info)) {
Packit ed3af9
		gdFree(hdr);
Packit ed3af9
		gdFree(info);
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	BMP_DEBUG(printf("Numcolours: %d\n", info->numcolors));
Packit ed3af9
	BMP_DEBUG(printf("Width: %d\n", info->width));
Packit ed3af9
	BMP_DEBUG(printf("Height: %d\n", info->height));
Packit ed3af9
	BMP_DEBUG(printf("Planes: %d\n", info->numplanes));
Packit ed3af9
	BMP_DEBUG(printf("Depth: %d\n", info->depth));
Packit ed3af9
	BMP_DEBUG(printf("Offset: %d\n", hdr->off));
Packit ed3af9
Packit ed3af9
	if (info->depth >= 16) {
Packit ed3af9
		im = gdImageCreateTrueColor(info->width, info->height);
Packit ed3af9
	} else {
Packit ed3af9
		im = gdImageCreate(info->width, info->height);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (!im) {
Packit ed3af9
		gdFree(hdr);
Packit ed3af9
		gdFree(info);
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	switch (info->depth) {
Packit ed3af9
	case 1:
Packit ed3af9
		BMP_DEBUG(printf("1-bit image\n"));
Packit ed3af9
		error = bmp_read_1bit(im, infile, info, hdr);
Packit ed3af9
		break;
Packit ed3af9
	case 4:
Packit ed3af9
		BMP_DEBUG(printf("4-bit image\n"));
Packit ed3af9
		error = bmp_read_4bit(im, infile, info, hdr);
Packit ed3af9
		break;
Packit ed3af9
	case 8:
Packit ed3af9
		BMP_DEBUG(printf("8-bit image\n"));
Packit ed3af9
		error = bmp_read_8bit(im, infile, info, hdr);
Packit ed3af9
		break;
Packit ed3af9
	case 16:
Packit ed3af9
	case 24:
Packit ed3af9
	case 32:
Packit ed3af9
		BMP_DEBUG(printf("Direct BMP image\n"));
Packit ed3af9
		error = bmp_read_direct(im, infile, info, hdr);
Packit ed3af9
		break;
Packit ed3af9
	default:
Packit ed3af9
		BMP_DEBUG(printf("Unknown bit count\n"));
Packit ed3af9
		error = 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	gdFree(hdr);
Packit ed3af9
	gdFree(info);
Packit ed3af9
Packit ed3af9
	if (error) {
Packit ed3af9
		gdImageDestroy(im);
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	return im;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_header(gdIOCtx *infile, bmp_hdr_t *hdr)
Packit ed3af9
{
Packit ed3af9
	if(
Packit ed3af9
	    !gdGetWordLSB(&hdr->magic, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&hdr->size, infile) ||
Packit ed3af9
	    !gdGetWordLSB(&hdr->reserved1, infile) ||
Packit ed3af9
	    !gdGetWordLSB(&hdr->reserved2 , infile) ||
Packit ed3af9
	    !gdGetIntLSB(&hdr->off , infile)
Packit ed3af9
	) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_info(gdIOCtx *infile, bmp_info_t *info)
Packit ed3af9
{
Packit ed3af9
	/* read BMP length so we can work out the version */
Packit ed3af9
	if (!gdGetIntLSB(&info->len, infile)) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	switch (info->len) {
Packit ed3af9
		/* For now treat Windows v4 + v5 as v3 */
Packit ed3af9
	case BMP_WINDOWS_V3:
Packit ed3af9
	case BMP_WINDOWS_V4:
Packit ed3af9
	case BMP_WINDOWS_V5:
Packit ed3af9
		BMP_DEBUG(printf("Reading Windows Header\n"));
Packit ed3af9
		if (bmp_read_windows_v3_info(infile, info)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
	case BMP_OS2_V1:
Packit ed3af9
		if (bmp_read_os2_v1_info(infile, info)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
	case BMP_OS2_V2:
Packit ed3af9
		if (bmp_read_os2_v2_info(infile, info)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
	default:
Packit ed3af9
		BMP_DEBUG(printf("Unhandled bitmap\n"));
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_windows_v3_info(gdIOCtxPtr infile, bmp_info_t *info)
Packit ed3af9
{
Packit ed3af9
	if (
Packit ed3af9
	    !gdGetIntLSB(&info->width, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->height, infile) ||
Packit ed3af9
	    !gdGetWordLSB(&info->numplanes, infile) ||
Packit ed3af9
	    !gdGetWordLSB(&info->depth, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->enctype, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->size, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->hres, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->vres, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->numcolors, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->mincolors, infile)
Packit ed3af9
	) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (info->height < 0) {
Packit ed3af9
		info->topdown = 1;
Packit ed3af9
		info->height = -info->height;
Packit ed3af9
	} else {
Packit ed3af9
		info->topdown = 0;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	info->type = BMP_PALETTE_4;
Packit ed3af9
Packit ed3af9
	if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 ||
Packit ed3af9
	        info->depth <= 0  || info->numcolors < 0 || info->mincolors < 0) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_os2_v1_info(gdIOCtxPtr infile, bmp_info_t *info)
Packit ed3af9
{
Packit ed3af9
	if (
Packit ed3af9
	    !gdGetWordLSB((signed short int *)&info->width, infile) ||
Packit ed3af9
	    !gdGetWordLSB((signed short int *)&info->height, infile) ||
Packit ed3af9
	    !gdGetWordLSB(&info->numplanes, infile) ||
Packit ed3af9
	    !gdGetWordLSB(&info->depth, infile)
Packit ed3af9
	) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* OS2 v1 doesn't support topdown */
Packit ed3af9
	info->topdown = 0;
Packit ed3af9
Packit ed3af9
	info->numcolors = 1 << info->depth;
Packit ed3af9
	info->type = BMP_PALETTE_3;
Packit ed3af9
Packit ed3af9
	if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 ||
Packit ed3af9
	        info->depth <= 0 || info->numcolors < 0) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_os2_v2_info(gdIOCtxPtr infile, bmp_info_t *info)
Packit ed3af9
{
Packit ed3af9
	char useless_bytes[24];
Packit ed3af9
	if (
Packit ed3af9
	    !gdGetIntLSB(&info->width, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->height, infile) ||
Packit ed3af9
	    !gdGetWordLSB(&info->numplanes, infile) ||
Packit ed3af9
	    !gdGetWordLSB(&info->depth, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->enctype, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->size, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->hres, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->vres, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->numcolors, infile) ||
Packit ed3af9
	    !gdGetIntLSB(&info->mincolors, infile)
Packit ed3af9
	) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Lets seek the next 24 pointless bytes, we don't care too much about it */
Packit ed3af9
	if (!gdGetBuf(useless_bytes, 24, infile)) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (info->height < 0) {
Packit ed3af9
		info->topdown = 1;
Packit ed3af9
		info->height = -info->height;
Packit ed3af9
	} else {
Packit ed3af9
		info->topdown = 0;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	info->type = BMP_PALETTE_4;
Packit ed3af9
Packit ed3af9
	if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 ||
Packit ed3af9
	        info->depth <= 0  || info->numcolors < 0 || info->mincolors < 0) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_direct(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
Packit ed3af9
{
Packit ed3af9
	int ypos = 0, xpos = 0, row = 0;
Packit ed3af9
	int padding = 0, alpha = 0, red = 0, green = 0, blue = 0;
Packit ed3af9
	signed short int data = 0;
Packit ed3af9
Packit ed3af9
	switch(info->enctype) {
Packit ed3af9
	case BMP_BI_RGB:
Packit ed3af9
		/* no-op */
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case BMP_BI_BITFIELDS:
Packit ed3af9
		if (info->depth == 24) {
Packit ed3af9
			BMP_DEBUG(printf("Bitfield compression isn't supported for 24-bit\n"));
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		BMP_DEBUG(printf("Currently no bitfield support\n"));
Packit ed3af9
		return 1;
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case BMP_BI_RLE8:
Packit ed3af9
		if (info->depth != 8) {
Packit ed3af9
			BMP_DEBUG(printf("RLE is only valid for 8-bit images\n"));
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
	case BMP_BI_RLE4:
Packit ed3af9
		if (info->depth != 4) {
Packit ed3af9
			BMP_DEBUG(printf("RLE is only valid for 4-bit images\n"));
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
	case BMP_BI_JPEG:
Packit ed3af9
	case BMP_BI_PNG:
Packit ed3af9
	default:
Packit ed3af9
		BMP_DEBUG(printf("Unsupported BMP compression format\n"));
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* There is a chance the data isn't until later, would be wierd but it is possible */
Packit ed3af9
	if (gdTell(infile) != header->off) {
Packit ed3af9
		/* Should make sure we don't seek past the file size */
Packit ed3af9
		if (!gdSeek(infile, header->off)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* The line must be divisible by 4, else its padded with NULLs */
Packit ed3af9
	padding = ((int)(info->depth / 8) * info->width) % 4;
Packit ed3af9
	if (padding) {
Packit ed3af9
		padding = 4 - padding;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
Packit ed3af9
	for (ypos = 0; ypos < info->height; ++ypos) {
Packit ed3af9
		if (info->topdown) {
Packit ed3af9
			row = ypos;
Packit ed3af9
		} else {
Packit ed3af9
			row = info->height - ypos - 1;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		for (xpos = 0; xpos < info->width; xpos++) {
Packit ed3af9
			if (info->depth == 16) {
Packit ed3af9
				if (!gdGetWordLSB(&data, infile)) {
Packit ed3af9
					return 1;
Packit ed3af9
				}
Packit ed3af9
				BMP_DEBUG(printf("Data: %X\n", data));
Packit ed3af9
				red = ((data & 0x7C00) >> 10) << 3;
Packit ed3af9
				green = ((data & 0x3E0) >> 5) << 3;
Packit ed3af9
				blue = (data & 0x1F) << 3;
Packit ed3af9
				BMP_DEBUG(printf("R: %d, G: %d, B: %d\n", red, green, blue));
Packit ed3af9
			} else if (info->depth == 24) {
Packit ed3af9
				if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || !gdGetByte(&red, infile)) {
Packit ed3af9
					return 1;
Packit ed3af9
				}
Packit ed3af9
			} else {
Packit ed3af9
				if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || !gdGetByte(&red, infile) || !gdGetByte(&alpha, infile)) {
Packit ed3af9
					return 1;
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
			/*alpha = gdAlphaMax - (alpha >> 1);*/
Packit ed3af9
			gdImageSetPixel(im, xpos, row, gdTrueColor(red, green, blue));
Packit ed3af9
		}
Packit ed3af9
		for (xpos = padding; xpos > 0; --xpos) {
Packit ed3af9
			if (!gdGetByte(&red, infile)) {
Packit ed3af9
				return 1;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_palette(gdImagePtr im, gdIOCtxPtr infile, int count, int read_four)
Packit ed3af9
{
Packit ed3af9
	int i;
Packit ed3af9
	int r, g, b, z;
Packit ed3af9
Packit ed3af9
	for (i = 0; i < count; i++) {
Packit ed3af9
		if (
Packit ed3af9
		    !gdGetByte(&b, infile) ||
Packit ed3af9
		    !gdGetByte(&g, infile) ||
Packit ed3af9
		    !gdGetByte(&r, infile) ||
Packit ed3af9
		    (read_four && !gdGetByte(&z, infile))
Packit ed3af9
		) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		im->red[i] = r;
Packit ed3af9
		im->green[i] = g;
Packit ed3af9
		im->blue[i] = b;
Packit ed3af9
		im->open[i] = 1;
Packit ed3af9
	}
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_1bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
Packit ed3af9
{
Packit ed3af9
	int ypos = 0, xpos = 0, row = 0, index = 0;
Packit ed3af9
	int padding = 0, current_byte = 0, bit = 0;
Packit ed3af9
Packit ed3af9
	if (info->enctype != BMP_BI_RGB) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (!info->numcolors) {
Packit ed3af9
		info->numcolors = 2;
Packit ed3af9
	} else if (info->numcolors < 0 || info->numcolors > 2) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	im->colorsTotal = info->numcolors;
Packit ed3af9
Packit ed3af9
	/* There is a chance the data isn't until later, would be wierd but it is possible */
Packit ed3af9
	if (gdTell(infile) != header->off) {
Packit ed3af9
		/* Should make sure we don't seek past the file size */
Packit ed3af9
		if (!gdSeek(infile, header->off)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* The line must be divisible by 4, else its padded with NULLs */
Packit ed3af9
	padding = ((int)ceil(0.1 * info->width)) % 4;
Packit ed3af9
	if (padding) {
Packit ed3af9
		padding = 4 - padding;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	for (ypos = 0; ypos < info->height; ++ypos) {
Packit ed3af9
		if (info->topdown) {
Packit ed3af9
			row = ypos;
Packit ed3af9
		} else {
Packit ed3af9
			row = info->height - ypos - 1;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		for (xpos = 0; xpos < info->width; xpos += 8) {
Packit ed3af9
			/* Bitmaps are always aligned in bytes so we'll never overflow */
Packit ed3af9
			if (!gdGetByte(&current_byte, infile)) {
Packit ed3af9
				return 1;
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			for (bit = 0; bit < 8; bit++) {
Packit ed3af9
				index = ((current_byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
Packit ed3af9
				if (im->open[index]) {
Packit ed3af9
					im->open[index] = 0;
Packit ed3af9
				}
Packit ed3af9
				gdImageSetPixel(im, xpos + bit, row, index);
Packit ed3af9
				/* No need to read anything extra */
Packit ed3af9
				if ((xpos + bit) >= info->width) {
Packit ed3af9
					break;
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		for (xpos = padding; xpos > 0; --xpos) {
Packit ed3af9
			if (!gdGetByte(&index, infile)) {
Packit ed3af9
				return 1;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_4bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
Packit ed3af9
{
Packit ed3af9
	int ypos = 0, xpos = 0, row = 0, index = 0;
Packit ed3af9
	int padding = 0, current_byte = 0;
Packit ed3af9
Packit ed3af9
	if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE4) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (!info->numcolors) {
Packit ed3af9
		info->numcolors = 16;
Packit ed3af9
	} else if (info->numcolors < 0 || info->numcolors > 16) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	im->colorsTotal = info->numcolors;
Packit ed3af9
Packit ed3af9
	/* There is a chance the data isn't until later, would be wierd but it is possible */
Packit ed3af9
	if (gdTell(infile) != header->off) {
Packit ed3af9
		/* Should make sure we don't seek past the file size */
Packit ed3af9
		if (!gdSeek(infile, header->off)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* The line must be divisible by 4, else its padded with NULLs */
Packit ed3af9
	padding = ((int)ceil(0.5 * info->width)) % 4;
Packit ed3af9
	if (padding) {
Packit ed3af9
		padding = 4 - padding;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	switch (info->enctype) {
Packit ed3af9
	case BMP_BI_RGB:
Packit ed3af9
		for (ypos = 0; ypos < info->height; ++ypos) {
Packit ed3af9
			if (info->topdown) {
Packit ed3af9
				row = ypos;
Packit ed3af9
			} else {
Packit ed3af9
				row = info->height - ypos - 1;
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			for (xpos = 0; xpos < info->width; xpos += 2) {
Packit ed3af9
				if (!gdGetByte(&current_byte, infile)) {
Packit ed3af9
					return 1;
Packit ed3af9
				}
Packit ed3af9
Packit ed3af9
				index = (current_byte >> 4) & 0x0f;
Packit ed3af9
				if (im->open[index]) {
Packit ed3af9
					im->open[index] = 0;
Packit ed3af9
				}
Packit ed3af9
				gdImageSetPixel(im, xpos, row, index);
Packit ed3af9
Packit ed3af9
				/* This condition may get called often, potential optimsations */
Packit ed3af9
				if (xpos >= info->width) {
Packit ed3af9
					break;
Packit ed3af9
				}
Packit ed3af9
Packit ed3af9
				index = current_byte & 0x0f;
Packit ed3af9
				if (im->open[index]) {
Packit ed3af9
					im->open[index] = 0;
Packit ed3af9
				}
Packit ed3af9
				gdImageSetPixel(im, xpos + 1, row, index);
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			for (xpos = padding; xpos > 0; --xpos) {
Packit ed3af9
				if (!gdGetByte(&index, infile)) {
Packit ed3af9
					return 1;
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case BMP_BI_RLE4:
Packit ed3af9
		if (bmp_read_rle(im, infile, info)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	default:
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_8bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
Packit ed3af9
{
Packit ed3af9
	int ypos = 0, xpos = 0, row = 0, index = 0;
Packit ed3af9
	int padding = 0;
Packit ed3af9
Packit ed3af9
	if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE8) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (!info->numcolors) {
Packit ed3af9
		info->numcolors = 256;
Packit ed3af9
	} else if (info->numcolors < 0 || info->numcolors > 256) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	im->colorsTotal = info->numcolors;
Packit ed3af9
Packit ed3af9
	/* There is a chance the data isn't until later, would be wierd but it is possible */
Packit ed3af9
	if (gdTell(infile) != header->off) {
Packit ed3af9
		/* Should make sure we don't seek past the file size */
Packit ed3af9
		if (!gdSeek(infile, header->off)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* The line must be divisible by 4, else its padded with NULLs */
Packit ed3af9
	padding = (1 * info->width) % 4;
Packit ed3af9
	if (padding) {
Packit ed3af9
		padding = 4 - padding;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	switch (info->enctype) {
Packit ed3af9
	case BMP_BI_RGB:
Packit ed3af9
		for (ypos = 0; ypos < info->height; ++ypos) {
Packit ed3af9
			if (info->topdown) {
Packit ed3af9
				row = ypos;
Packit ed3af9
			} else {
Packit ed3af9
				row = info->height - ypos - 1;
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			for (xpos = 0; xpos < info->width; ++xpos) {
Packit ed3af9
				if (!gdGetByte(&index, infile)) {
Packit ed3af9
					return 1;
Packit ed3af9
				}
Packit ed3af9
Packit ed3af9
				if (im->open[index]) {
Packit ed3af9
					im->open[index] = 0;
Packit ed3af9
				}
Packit ed3af9
				gdImageSetPixel(im, xpos, row, index);
Packit ed3af9
			}
Packit ed3af9
			/* Could create a new variable, but it isn't really worth it */
Packit ed3af9
			for (xpos = padding; xpos > 0; --xpos) {
Packit ed3af9
				if (!gdGetByte(&index, infile)) {
Packit ed3af9
					return 1;
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case BMP_BI_RLE8:
Packit ed3af9
		if (bmp_read_rle(im, infile, info)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	default:
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info)
Packit ed3af9
{
Packit ed3af9
	int ypos = 0, xpos = 0, row = 0, index = 0;
Packit ed3af9
	int rle_length = 0, rle_data = 0;
Packit ed3af9
	int padding = 0;
Packit ed3af9
	int i = 0, j = 0;
Packit ed3af9
	int pixels_per_byte = 8 / info->depth;
Packit ed3af9
Packit ed3af9
	for (ypos = 0; ypos < info->height && xpos <= info->width;) {
Packit ed3af9
		if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) {
Packit ed3af9
			return 1;
Packit ed3af9
		}
Packit ed3af9
		row = info->height - ypos - 1;
Packit ed3af9
Packit ed3af9
		if (rle_length != BMP_RLE_COMMAND) {
Packit ed3af9
			if (im->open[rle_data]) {
Packit ed3af9
				im->open[rle_data] = 0;
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			for (i = 0; (i < rle_length) && (xpos < info->width);) {
Packit ed3af9
				for (j = 1; (j <= pixels_per_byte) && (xpos < info->width) && (i < rle_length); j++, xpos++, i++) {
Packit ed3af9
					index = (rle_data & (((1 << info->depth) - 1) << (8 - (j * info->depth)))) >> (8 - (j * info->depth));
Packit ed3af9
					if (im->open[index]) {
Packit ed3af9
						im->open[index] = 0;
Packit ed3af9
					}
Packit ed3af9
					gdImageSetPixel(im, xpos, row, index);
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		} else if (rle_length == BMP_RLE_COMMAND && rle_data > 2) {
Packit ed3af9
			/* Uncompressed RLE needs to be even */
Packit ed3af9
			padding = 0;
Packit ed3af9
			for (i = 0; (i < rle_data) && (xpos < info->width); i += pixels_per_byte) {
Packit ed3af9
				int max_pixels = pixels_per_byte;
Packit ed3af9
Packit ed3af9
				if (!gdGetByte(&index, infile)) {
Packit ed3af9
					return 1;
Packit ed3af9
				}
Packit ed3af9
				padding++;
Packit ed3af9
Packit ed3af9
				if (rle_data - i < max_pixels) {
Packit ed3af9
					max_pixels = rle_data - i;
Packit ed3af9
				}
Packit ed3af9
Packit ed3af9
				for (j = 1; (j <= max_pixels)  && (xpos < info->width); j++, xpos++) {
Packit ed3af9
					int temp = (index >> (8 - (j * info->depth))) & ((1 << info->depth) - 1);
Packit ed3af9
					if (im->open[temp]) {
Packit ed3af9
						im->open[temp] = 0;
Packit ed3af9
					}
Packit ed3af9
					gdImageSetPixel(im, xpos, row, temp);
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			/* Make sure the bytes read are even */
Packit ed3af9
			if (padding % 2 && !gdGetByte(&index, infile)) {
Packit ed3af9
				return 1;
Packit ed3af9
			}
Packit ed3af9
		} else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFLINE) {
Packit ed3af9
			/* Next Line */
Packit ed3af9
			xpos = 0;
Packit ed3af9
			ypos++;
Packit ed3af9
		} else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_DELTA) {
Packit ed3af9
			/* Delta Record, used for bmp files that contain other data*/
Packit ed3af9
			if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) {
Packit ed3af9
				return 1;
Packit ed3af9
			}
Packit ed3af9
			xpos += rle_length;
Packit ed3af9
			ypos += rle_data;
Packit ed3af9
		} else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFBITMAP) {
Packit ed3af9
			/* End of bitmap */
Packit ed3af9
			break;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	return 0;
Packit ed3af9
}