//=====================================================================
//
// ipicture.c - general picture file operation
//
// NOTE: require ibitmap.h, struct IBITMAPm.h, idcolor.h
// for more information, please see the readme file
//
//=====================================================================

#include "ipicture.h"
#include "ibitmapm.h"
#include "ibmcols.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>



//=====================================================================
//
// PICTURE DATA INTERFACE
//
// IRWCHARD - Media Data Input/Output Interface
// PIXELS   - Pixel Operation for Bitmaps
// FIXUP    - Pixel Format Fixing Up Interface
//
//=====================================================================

//---------------------------------------------------------------------
// Media Data Input/Output Interface
//---------------------------------------------------------------------
int is_getc(IRWCHARD *stream)
{
	int ch;
	ch = (stream->getc)(stream);
	if (ch >= 0) stream->_cnt++;
	return ch;
}

int is_putc(IRWCHARD *stream, int c)
{
	int ch;
	ch = (stream->putc)(stream, c);
	if (ch >= 0) stream->_cnt++;
	return ch;
}

long is_reader(IRWCHARD *stream, void *buffer, long size)
{
	unsigned char *lptr = (unsigned char*)buffer;
	long total = 0;
	int ch;

	for (ch = 0; total < size; total++) {
		ch = is_getc(stream);
		if (ch < 0) break;
		*lptr++ = (unsigned char)ch;
	}
	if (total == 0) return -1;
	return total;
}

long is_writer(IRWCHARD *stream, const void *buffer, long size)
{	
	unsigned char *lptr = (unsigned char*)buffer;
	long total = 0;
	int ch;

	for (ch = 0; total < size; total++) {
		ch = *lptr++; 
		ch = is_putc(stream, ch);
		if (ch < 0) break;
	}
	if (total == 0) return -1;
	return total;
}

int is_igetw(IRWCHARD *stream)
{
	int c1, c2;
	if ((c1 = is_getc(stream)) < 0) return -1;
	if ((c2 = is_getc(stream)) < 0) return -1;
	return c1 | (c2 << 8);
}

int is_iputw(IRWCHARD *stream, int w)
{
	int c1 = (w & 0xFF), c2 = ((w >> 8) & 0xFF);
	if (is_putc(stream, c1) < 0) return -1;
	if (is_putc(stream, c2) < 0) return -1;
	return w;
}

long is_igetl(IRWCHARD *stream)
{
	long c1, c2, c3, c4;
	if ((c1 = (long)is_getc(stream)) < 0) return -1;
	if ((c2 = (long)is_getc(stream)) < 0) return -1;
	if ((c3 = (long)is_getc(stream)) < 0) return -1;
	if ((c4 = (long)is_getc(stream)) < 0) return -1;
	return (c1 | (c2 << 8) | (c3 << 16) | (c4 << 24));
}

long is_iputl(IRWCHARD *stream, long l)
{
	long c1, c2, c3, c4;
	c1 = (l & 0x000000FF) >> 0;
	c2 = (l & 0x0000FF00) >> 8;
	c3 = (l & 0x00FF0000) >> 16;
	c4 = (l & 0xFF000000) >> 24;
	if (is_putc(stream, c1) < 0) return -1;
	if (is_putc(stream, c2) < 0) return -1;
	if (is_putc(stream, c3) < 0) return -1;
	if (is_putc(stream, c4) < 0) return -1;
	return l; 
}

int is_mgetw(IRWCHARD *stream)
{
	int c1, c2;
	if ((c1 = is_getc(stream)) < 0) return -1;
	if ((c2 = is_getc(stream)) < 0) return -1;
	return (c1 << 8) | c2;
}

int is_mputw(IRWCHARD *stream, int w)
{
	int c1 = (w >> 8) & 0xFF, c2 = (w & 0xFF);
	if (is_putc(stream, c1) < 0) return -1;
	if (is_putc(stream, c2) < 0) return -1;
	return w;
}

long is_mgetl(IRWCHARD *stream)
{
	long c1, c2, c3, c4;
	if ((c1 = (long)is_getc(stream)) < 0) return -1;
	if ((c2 = (long)is_getc(stream)) < 0) return -1;
	if ((c3 = (long)is_getc(stream)) < 0) return -1;
	if ((c4 = (long)is_getc(stream)) < 0) return -1;
	return ((c1 << 24) | (c2 << 16) | (c3 << 8) | c4);
}

long is_mputl(IRWCHARD *stream, long l)
{
	long c1, c2, c3, c4;
	c1 = (l & 0xFF000000) >> 24;
	c2 = (l & 0x00FF0000) >> 16;
	c3 = (l & 0x0000FF00) >> 8;
	c4 = (l & 0x000000FF);
	if (is_putc(stream, c1) < 0) return -1;
	if (is_putc(stream, c2) < 0) return -1;
	if (is_putc(stream, c3) < 0) return -1;
	if (is_putc(stream, c4) < 0) return -1;
	return l;
}

long is_getbits(IRWCHARD *stream, int count, int litend)
{
	long data, bitc, bitd, mask, c, i;
	bitc = stream->_bitc;
	bitd = stream->_bitd;
	for (data = 0, i = 0; count > 0; ) {
		if (bitc == 0) {
			bitd = is_getc(stream);
			bitc = 8;
			if (bitd < 0) {
				bitd = 0;
				bitc = 0;
				data = -1;
				break;
			}
		}
		c = (count >= bitc) ? bitc : count;
		if (litend == 0) {
			data <<= c;
			data |= (bitd >> (8 - c)) & 0xff;
			bitd = (bitd << c) & 0xff;
		}	else {
			mask = (1L << c) - 1;
			data |= (bitd & mask) << i;
			bitd >>= c;
			i += c;
		}
		count -= c;
		bitc -= c;
	}
	stream->_bitc = bitc;
	stream->_bitd = bitd;
	return data;
}

long is_putbits(IRWCHARD *stream, long data, int count, int litend)
{
	long bitc, bitd, mask, c;
	bitc = stream->_bitc;
	bitd = stream->_bitd;
	for (; count > 0; ) {
		c = 8 - bitc;
		if (count < c) c = count;
		if (litend == 0) {
			mask = (data >> (count - c)) & 0xff;
			bitd |= (mask << (8 - c)) >> bitc;
		}	else {
			mask = ((1L << c) - 1) & data;
			data >>= c;
			bitd |= mask << bitc;
		}
		bitc += c;
		count -= c;
		if (bitc >= 8) {
			if (is_putc(stream, bitd & 0xff) < 0) {
				return -1;
			}
			bitc = 0;
			bitd = 0;
		}
	}
	stream->_bitc = bitc;
	stream->_bitd = bitd;
	return 0;
}

void is_flushbits(IRWCHARD *stream, int isflushwrite, int litendian)
{
	if (isflushwrite) {
		if (stream->_bitc != 0) {
			is_putbits(stream, 0, 8 - stream->_bitc, litendian);
		}
	}
	stream->_bitc = 0;
	stream->_bitd = 0;
}

long is_igetbits(IRWCHARD *stream, int count)
{
	return is_getbits(stream, count, 1);
}

long is_mgetbits(IRWCHARD *stream, int count)
{
	return is_getbits(stream, count, 0);
}

long is_iputbits(IRWCHARD *stream, long data, int count)
{
	return is_putbits(stream, data, count, 1);
}

long is_mputbits(IRWCHARD *stream, long data, int count)
{
	return is_putbits(stream, data, count, 0);
}

void is_iflushbits(IRWCHARD *stream, int isflushwrite)
{
	is_flushbits(stream, isflushwrite, 1);
}

void is_mflushbits(IRWCHARD *stream, int isflushwrite)
{
	is_flushbits(stream, isflushwrite, 0);
}

void is_seekcur(IRWCHARD *stream, long skip)
{
	for (; skip > 0; skip--) is_getc(stream);
}


//---------------------------------------------------------------------
// IBITMAP PIXEL OPERATION
//---------------------------------------------------------------------
#define _is_bmline(bmp, y) ((unsigned char*)((bmp)->line[y]))

unsigned long _is_get3b(const void *p)
{
	unsigned long c1, c2, c3;
	static int flag = 0;
	if (flag++ == 0) _ishift_init();
	c1 = ((unsigned long)((unsigned char*)p)[0]) << _ishift_data_b1;
	c2 = ((unsigned long)((unsigned char*)p)[1]) << _ishift_data_b2;
	c3 = ((unsigned long)((unsigned char*)p)[2]) << _ishift_data_b3;
	return (c1 | c2 | c3);
}

unsigned long _is_put3b(void *p, unsigned long c)
{
	unsigned long c1, c2, c3;
	static int flag = 0;
	if (flag++ == 0) _ishift_init();
	c1 = (c >> _ishift_data_b1) & 0xFF;
	c2 = (c >> _ishift_data_b2) & 0xFF;
	c3 = (c >> _ishift_data_b3) & 0xFF;
	((unsigned char*)p)[0] = (unsigned char)c1;
	((unsigned char*)p)[1] = (unsigned char)c2;
	((unsigned char*)p)[2] = (unsigned char)c3;
	return c;
}

#define _IS_HEADER1() do { \
	if (bmp == NULL) return 0;  \
	if (x<0 || y<0 || x>=(int)bmp->w || y>=(int)bmp->h) \
		return 0; \
}	while (0)

unsigned long _is_get8(const struct IBITMAP *bmp, int x, int y)
{
	_IS_HEADER1();
	return _is_bmline(bmp, y)[x];
}

unsigned long _is_get15(const struct IBITMAP *bmp, int x, int y)
{
	_IS_HEADER1();
	return ((unsigned short*)_is_bmline(bmp, y))[x];
}

unsigned long _is_get16(const struct IBITMAP *bmp, int x, int y)
{
	_IS_HEADER1();
	return ((unsigned short*)_is_bmline(bmp, y))[x];
}

unsigned long _is_get24(const struct IBITMAP *bmp, int x, int y)
{
	_IS_HEADER1();
	return _is_get3b(_is_bmline(bmp, y) + (x) * 3);
}

unsigned long _is_get32(const struct IBITMAP *bmp, int x, int y)
{
	_IS_HEADER1();
	return ((unsigned long*)_is_bmline(bmp, y))[x];
}

unsigned long _is_put8(struct IBITMAP *bmp, int x, int y, unsigned long c)
{
	_IS_HEADER1();
	_is_bmline(bmp, y)[x] = (unsigned char)(c & 0xFF);
	return c;
}

unsigned long _is_put15(struct IBITMAP *bmp, int x, int y, unsigned long c)
{
	_IS_HEADER1();
	((unsigned short*)_is_bmline(bmp, y))[x] = (unsigned short)(c & 0xFFFF);
	return c;
}

unsigned long _is_put16(struct IBITMAP *bmp, int x, int y, unsigned long c)
{
	_IS_HEADER1();
	((unsigned short*)_is_bmline(bmp, y))[x] = (unsigned short)(c & 0xFFFF);
	return c;
}

unsigned long _is_put24(struct IBITMAP *bmp, int x, int y, unsigned long c)
{
	_IS_HEADER1();
	_is_put3b(_is_bmline(bmp, y) + (x) * 3, c);
	return c;
}

unsigned long _is_put32(struct IBITMAP *bmp, int x, int y, unsigned long c)
{
	_IS_HEADER1();
	((unsigned long*)_is_bmline(bmp, y))[x] = c;
	return c;
}

unsigned long _is_getpx(const struct IBITMAP *bmp, int x, int y)
{
	_IS_HEADER1();
	if (bmp->bpp == 8) return _is_get8(bmp, x, y);
	if (bmp->bpp == 15) return _is_get15(bmp, x, y);
	if (bmp->bpp == 16) return _is_get16(bmp, x, y);
	if (bmp->bpp == 24) return _is_get24(bmp, x, y);
	if (bmp->bpp == 32) return _is_get32(bmp, x, y);
	return 0;
}

unsigned long _is_putpx(struct IBITMAP *bmp, int x, int y, unsigned long c)
{
	_IS_HEADER1();
	if (bmp->bpp == 8) return _is_put8(bmp, x, y, c);
	if (bmp->bpp == 15) return _is_put15(bmp, x, y, c);
	if (bmp->bpp == 16) return _is_put16(bmp, x, y, c);
	if (bmp->bpp == 24) return _is_put24(bmp, x, y, c);
	if (bmp->bpp == 32) return _is_put32(bmp, x, y, c);
	return 0;
}

#undef _IS_HEADER1

//---------------------------------------------------------------------
// local definition
//---------------------------------------------------------------------
typedef struct IBITMAP ibitmap_t;
long _is_perrno = 0;

void _is_perrno_set(long v) { _is_perrno = v; }
long _is_perrno_get(void) { return _is_perrno; }

//---------------------------------------------------------------------
// stream operation interface
//---------------------------------------------------------------------
#define IRWCHECK(s, v) do { if ((s)->_code != (v)) return -1; } while(0)
static int iproc_getc_mem(struct IRWCHARD *stream)
{
	IRWCHECK(stream, 0);
	if (stream->_pos < stream->_len)
		return ((unsigned char*)(stream->data))[stream->_pos++];
	return -1;
}

static int iproc_putc_mem(struct IRWCHARD *stream, int c)
{
	unsigned char ch = (unsigned char)c;
	IRWCHECK(stream, 0);
	if (stream->_pos == stream->_len) return -1;
	else if (stream->_pos < stream->_len) {
		((unsigned char*)(stream->data))[stream->_pos++] = ch;
	}
	if (stream->_pos > stream->_len) stream->_len = stream->_pos;
	return c;
}

static int iproc_getc_file(struct IRWCHARD *stream)
{
	IRWCHECK(stream, 1);
	return fgetc((FILE*)(stream->data));
}

static int iproc_putc_file(struct IRWCHARD *stream, int c)
{
	IRWCHECK(stream, 1);
	return fputc(c, (FILE*)(stream->data));
}

int is_init_mem(IRWCHARD *stream, const void *lptr, long size)
{
	stream->_code = 0;
	stream->_pos = 0;
	stream->_len = size;
	stream->_bitc = 0;
	stream->_bitd = 0;
	stream->data = (void*)lptr;
	stream->getc = iproc_getc_mem;
	stream->putc = iproc_putc_mem;
	return 0;
}

int is_open_file(IRWCHARD *stream, const char *filename, const char *rw)
{
	stream->_code = -1;
	stream->data = fopen(filename, rw);
	if (stream->data == NULL) return -1;
	stream->_code = 1;
	stream->_bitc = 0;
	stream->_bitd = 0;
	stream->getc = iproc_getc_file;
	stream->putc = iproc_putc_file;
	return 0;
}

int is_close_file(IRWCHARD *stream)
{
	IRWCHECK(stream, 1);
	fclose((FILE*)(stream->data));
	return 0;
}

//---------------------------------------------------------------------
// ibitmap_fixup - [B,G,R,A] order is used if bpp < 0
//---------------------------------------------------------------------
struct IBITMAP *ibitmap_fixup(const struct IBITMAP *src, int bpp, const IRGB *pal)
{
	struct IBITMAP *bmp;
	unsigned long r, g, b, a, c, i, j, m, p;
	long rgbm = (bpp >= 0)? 0 : 1;

	bpp = (bpp >= 0)? bpp : (-bpp);
	bmp = ibitmap_create(src->w, src->h, bpp);
	m = (src->mask & 1);
	pal = (pal)? pal : _ipaletted;
	if (bmp == NULL) return NULL;
	bmp->mask = rgbm;

	for (j = 0; j < src->h; j++) {
		for (i = 0; i < src->w; i++) {
			c = _is_getpx(src, i, j);
			p = _im_color_rgb(c, src->bpp, m, pal);
			a = (p >> 24) & 0xFF;
			r = (p >> 16) & 0xFF;
			g = (p >>  8) & 0xFF;
			b = (p >>  0) & 0xFF;

			switch (bpp)
			{
			case 8:
				p = (src->bpp == 8)? c : ((r&0xE0)|((g>>3)&0x1C)|(b>>6));
				_is_putpx(bmp, i, j, p);
				break;
			case 15:
				p = (rgbm == 0)? _imcolor15(r, g, b) : _imcolor15(b, g, r);
				_is_putpx(bmp, i, j, p);
				break;
			case 16:
				p = (rgbm == 0)? _imcolor16(r, g, b) : _imcolor16(b, g, r);
				_is_putpx(bmp, i, j, p);
				break;
			case 24:
				p = (rgbm == 0)? _imcolor24(r, g, b) : _imcolor24(b, g, r);
				_is_putpx(bmp, i, j, p);
				break;
			case 32:
				p = (rgbm == 0)? _imcolora32(r, g, b, a) :
					_imcolora32(b, g, r, a);
				_is_putpx(bmp, i, j, p);
				break;
			}
		}
	}
	return bmp;
}


//=====================================================================
//
// bmp - Windows/OS2 IBITMAP Format
//
// decoding program - idcolor.h
// 
//=====================================================================
#define IBI_RGB          0
#define IBI_RLE8         1
#define IBI_RLE4         2
#define IBI_BITFIELDS    3

#define IOS2INFOHEADERSIZE  12
#define IWININFOHEADERSIZE  40


typedef struct _IBITMAPFILEHEADER
{
	unsigned long  bfType;
	unsigned long  bfSize;
	unsigned short bfReserved1;
	unsigned short bfReserved2;
	unsigned long  bfOffBits;
}	IBITMAPFILEHEADER;


typedef struct _IBITMAPINFOHEADER
{
	unsigned long  biWidth;
	unsigned long  biHeight;
	unsigned short biBitCount;
	unsigned long  biCompression;
}	IBITMAPINFOHEADER;


//---------------------------------------------------------------------
// bmp - read_bmfileheader
//---------------------------------------------------------------------
static int read_bmfileheader(IRWCHARD *stream, IBITMAPFILEHEADER *fileheader)
{
	fileheader->bfType = is_igetw(stream);
	fileheader->bfSize= is_igetl(stream);
	fileheader->bfReserved1= is_igetw(stream);
	fileheader->bfReserved2= is_igetw(stream);
	fileheader->bfOffBits= is_igetl(stream);

	if (fileheader->bfType != 0x4D42) return -1;

	return 0;
}

//---------------------------------------------------------------------
// bmp - read_bminfoheader
//---------------------------------------------------------------------
static int read_bminfoheader(IRWCHARD *stream, IBITMAPINFOHEADER *infoheader, 
	int ihsize)
{
	long data[20];

	if (ihsize == IWININFOHEADERSIZE) {
		data[0] = is_igetl(stream);		/* unsigned long  biWidth......... */
		data[1] = is_igetl(stream);		/* unsigned long  biHeight........ */
		data[2] = is_igetw(stream);		/* unsigned short biPlanes........ */
		data[3] = is_igetw(stream);		/* unsigned short biBitCount...... */
		data[4] = is_igetl(stream);		/* unsigned long  biCompression... */
		data[5] = is_igetl(stream);		/* unsigned long  biSizeImage..... */
		data[6] = is_igetl(stream);		/* unsigned long  biXPelsPerMeters */
		data[7] = is_igetl(stream);		/* unsigned long  biYPelsPerMeters */
		data[8] = is_igetl(stream);		/* unsigned long  biClrUsed....... */
		data[9] = is_igetl(stream);		/* unsigned long  biClrImportant.. */

		infoheader->biWidth = data[0];
		infoheader->biHeight = data[1];
		infoheader->biBitCount = (unsigned short)data[3];
		infoheader->biCompression = data[4];
	}	
	else 
	if (ihsize == IOS2INFOHEADERSIZE) {
		data[0] = is_igetw(stream);		/* unsigned short biWidth......... */
		data[1] = is_igetw(stream);		/* unsigned short biHeight........ */
		data[2] = is_igetw(stream);		/* unsigned short biPlanes........ */
		data[3] = is_igetw(stream);		/* unsigned short biBitCount...... */

		infoheader->biWidth = data[0];
		infoheader->biHeight= data[1];
		infoheader->biBitCount = (unsigned short)data[3];
		infoheader->biCompression = 0;
	}
	else {
		return -1;
	}

	return 0;
}

//---------------------------------------------------------------------
// bmp - read_bmicolors
//---------------------------------------------------------------------
static void read_bmicolors(int ncols, IRGB *pal, IRWCHARD *stream, 
	int win_flag)
{
	int i;
	for (i = 0; i < ncols; i++) {
		pal[i].b = is_getc(stream);
		pal[i].g = is_getc(stream);
		pal[i].r = is_getc(stream);
		if (win_flag) is_getc(stream);
	}
}

//---------------------------------------------------------------------
// bmp - read_image
//---------------------------------------------------------------------
static void read_image(IRWCHARD *stream, ibitmap_t *bmp, const 
	IBITMAPINFOHEADER *infoheader)
{
	long line, start, length;
	long i, j, k, a, nbytes;
	unsigned long n;
	unsigned char b[32];
	IRGB c;

	for (start = 0; start < (long)infoheader->biHeight; start++) {
		line = infoheader->biHeight - start - 1;
		length = (long)infoheader->biWidth;

		switch (infoheader->biBitCount) {

		case 1:
			for (i = 0; i < length; i++) {
				j = i % 32;
				if (j == 0) {
					n = is_mgetl(stream);
					for (k = 0; k < 32; k++) {
						b[31 - k] = (char)(n & 1);
						n = n >> 1;
					}
				}
				_is_put8(bmp, i, line, b[j]);
			}
			break;

		case 4:
			for (i = 0; i < length; i++) {
				j = i % 8;
				if (j == 0) {
					n = is_igetl(stream);
					for (k = 0; k < 4; k++) {
						b[k * 2 + 1] = (ICOLORB)(((n & 0xFF) >> 0) & 0x0F);
						b[k * 2 + 0] = (ICOLORB)(((n & 0xFF) >> 4) & 0x0F);
						n = n >> 8;
					}
				}
				_is_put8(bmp, i, line, b[j]);
			}
			break;

		case 8:
			for (i = 0; i < length; i++) {
				_is_put8(bmp, i, line, is_getc(stream));
			}
			break;

		case 24:
			for (nbytes = 0, i = 0; i < length; i++, nbytes += 3) {
				c.b = is_getc(stream);
				c.g = is_getc(stream);
				c.r = is_getc(stream);
				_is_put24(bmp, i, line, _imcolor24(c.r, c.g, c.b));
			}
			nbytes = nbytes % 4;
			for (i = nbytes; nbytes != 0 && i < 4; i++) is_getc(stream);
			break;

		case 32:
			for (i = 0; i < length; i++) {
				c.b = is_getc(stream);
				c.g = is_getc(stream);
				c.r = is_getc(stream);
				a = is_getc(stream);
				if (bmp->bpp == 32) {
					_is_put32(bmp, i, line, _imcolora32(c.r, c.g, c.b, a));
				}	else {
					_is_put24(bmp, i, line, _imcolora32(c.r, c.g, c.b, a));
				}
			}
			break;
		}
	}
}

//---------------------------------------------------------------------
// bmp - read_bitfields_image
//---------------------------------------------------------------------
static void read_bitfields_image(IRWCHARD *stream, ibitmap_t *bmp, 
	IBITMAPINFOHEADER *infoheader)
{
	long k, i;
	long bpp;
	int bytes_per_pixel;
	int red, grn, blu;
	unsigned long buffer;

	bpp = bmp->bpp;
	bytes_per_pixel = (bpp + 7) / 8;

	for (i = 0; i < (long)infoheader->biHeight; i++) {
		for (k = 0; k < (long)infoheader->biWidth; k++) {

			buffer = 0;
			is_reader(stream, &buffer, bytes_per_pixel);
			if (bytes_per_pixel == 1) buffer = is_getc(stream);
			else if (bytes_per_pixel == 2) buffer = is_igetw(stream);
			else if (bytes_per_pixel == 3) {
			}	else buffer = (unsigned long)is_igetl(stream);

			if (bpp == 15) {
				red = (buffer >> 10) & 0x1f;
				grn = (buffer >> 5) & 0x1f;
				blu = (buffer) & 0x1f;
				buffer =	(red << _ishift_rgb_r_15) |
							(grn << _ishift_rgb_g_15) |
							(blu << _ishift_rgb_b_15);
			}
			else if (bpp == 16) {
				red = (buffer >> 11) & 0x1f;
				grn = (buffer >> 5) & 0x3f;
				blu = (buffer) & 0x1f;
				buffer =	(red << _ishift_rgb_r_16) |
							(grn << _ishift_rgb_g_16) |
							(blu << _ishift_rgb_b_16);
			}
			else {
				red = (buffer >> 16) & 0xff;
				grn = (buffer >> 8) & 0xff;
				blu = (buffer) & 0xff;
				buffer =	(red << _ishift_rgb_r_32) |
							(grn << _ishift_rgb_g_32) |
							(blu << _ishift_rgb_b_32);
			}

			if (bytes_per_pixel == 1) _is_put8(bmp, k, i, buffer);
			else if (bytes_per_pixel == 2) _is_put16(bmp, k, i, buffer);
			else if (bytes_per_pixel == 3) _is_put24(bmp, k, i, buffer);
			else _is_put32(bmp, i, k, buffer);
		}
	}
}

//---------------------------------------------------------------------
// bmp - read_rle_image
//---------------------------------------------------------------------
static void read_rle_image(IRWCHARD *stream, ibitmap_t *bmp, 
	IBITMAPINFOHEADER *infoheader)
{
	unsigned char count, val, val0;
	unsigned char b[8];
	long line, j, k, pos, bpp;
	long eolflag, eopicflag;

	line = infoheader->biHeight - 1;
	bpp = (infoheader->biCompression == IBI_RLE8)? 8 : 4;
	val = 0;

	for (eopicflag = 0; eopicflag == 0; ) {
		for (eolflag = 0, pos = 0; eolflag == 0 && eopicflag == 0; ) {
			count = is_getc(stream);
			val0 = is_getc(stream);
			if (count > 0) {
				b[1] = val0 & 15;
				b[0] = (val0 >> 4) & 15;
				for (j = 0; j < count; j++, pos++) {
					val = (bpp == 8)? val0 : b[j % 2];
					_is_put8(bmp, pos, line, val);
				}
			}	
			else {
				if (val == 0) eolflag = 1;
				else if (val == 1) eopicflag = 1;
				else if (val == 2) {
					pos += is_getc(stream);
					line -= is_getc(stream);
				}	else {
					for (j = 0; j < val; j++, pos++) {
						if (bpp == 8) val = is_getc(stream);
						else {
							if ((j % 4) == 0) {
								val0 = is_igetw(stream);
								for (k = 0; k < 2; k++) {
									b[2 * k + 1] = val0 & 15;
									val0 = val0 >> 4;
									b[2 * k] = val0 & 15;
									val0 = val0 >> 4;
								}
							}
							val = b[j % 4];
						}
						_is_put8(bmp, pos, line, val); 
					}
					if (j % 2 == 1 && bpp == 8) val0 = is_getc(stream);
				}
			}
			if (pos >= (long)infoheader->biWidth) eolflag = 1;
		}
		if (--line < 0) eopicflag = 1;
	}
}

//---------------------------------------------------------------------
// bmp - iload_bmp_stream
//---------------------------------------------------------------------
struct IBITMAP *iload_bmp_stream(IRWCHARD *stream, IRGB *pal)
{
	IBITMAPFILEHEADER fileheader;
	IBITMAPINFOHEADER infoheader;
	struct IBITMAP *bmp;
	long want_palette = 1;
	long ncol;
	unsigned long biSize;
	int bpp, dest_depth;
	IRGB tmppal[256];

	assert(stream);

	_is_perrno_set(0);
	_ishift_init();

	if (!pal) {
		want_palette = 0;
		pal = tmppal;
	}

	if (read_bmfileheader(stream, &fileheader) != 0) {
		_is_perrno_set(1);
		return NULL;
	}

	biSize = is_igetl(stream);
	
	if (read_bminfoheader(stream, &infoheader, biSize) != 0) {
		_is_perrno_set(2);
		return NULL;
	}

	if (biSize == IWININFOHEADERSIZE) {
		ncol = (fileheader.bfOffBits - 54) / 4;
		if (infoheader.biCompression != IBI_BITFIELDS)
			read_bmicolors(ncol, pal, stream, 1);
	}
	else if (biSize == IOS2INFOHEADERSIZE) {
		ncol = (fileheader.bfOffBits - 26) / 3;
		if (infoheader.biCompression != IBI_BITFIELDS)
			read_bmicolors(ncol, pal, stream, 0);
	}	else {
		_is_perrno_set(3);
		return NULL;
	}

	if (infoheader.biBitCount == 24) bpp = 24;
	else if (infoheader.biBitCount == 16) bpp = 16;
	else if (infoheader.biBitCount == 32) bpp = 32;
	else bpp = 8;

	if (infoheader.biCompression == IBI_BITFIELDS) {
		unsigned long redMask = is_igetl(stream);
		unsigned long grnMask = is_igetl(stream);
		unsigned long bluMask = is_igetl(stream);
		(void)grnMask;
		if ((bluMask == 0x001f) && (redMask == 0x7C00)) bpp = 15;
		else if ((bluMask == 0x001f) && (redMask == 0xF800)) bpp = 16;
		else if ((bluMask == 0x0000FF) && (redMask == 0xFF0000)) bpp = 32;
		else {
			_is_perrno_set(4);
			return NULL;
		}
	}

	bmp = ibitmap_create(infoheader.biWidth, infoheader.biHeight, bpp);
	dest_depth = bpp;

	if (!bmp) {
		_is_perrno_set(5);
		return NULL;
	}

	switch (infoheader.biCompression) 
	{
	case IBI_RGB:
		read_image(stream, bmp, &infoheader);
		break;
	case IBI_RLE8:
	case IBI_RLE4:
		read_rle_image(stream, bmp, &infoheader);
		break;
	case IBI_BITFIELDS:
		read_bitfields_image(stream, bmp, &infoheader);
		break;
	default:
		ibitmap_release(bmp);
		bmp = NULL;
		_is_perrno_set(6);
		break;
	}

	if (dest_depth != bpp) {
		if ((bpp != 8) && (!want_palette)) pal = NULL;
	}

	/* construct a fake palette if 8-bit mode is not involved */
	if ((bpp != 8) && (dest_depth != 8) && want_palette) {
		_igenerate_332_palette(pal);
	}

	return bmp;
}

//---------------------------------------------------------------------
// bmp - isave_bmp_stream
//---------------------------------------------------------------------
int isave_bmp_stream(IRWCHARD *stream, struct IBITMAP *bmp, const IRGB *pal)
{
	unsigned long c, p, m;
	long i, j;
	IRGB tmppal[256];
	int bfSize;
	int biSizeImage;
	int depth;
	int bpp;
	int filler;

	assert(bmp);
	assert(stream);

	depth = bmp->bpp;
	bpp = (depth == 8) ? 8 : 24;
	filler = 3 - ((bmp->w * (bpp / 8) - 1) & 3);
	m = (bmp->mask & 1);

	if (!pal) {
		memcpy(tmppal, _ipaletted, 256 * sizeof(IRGB));
		pal = tmppal;
	}

	if (bpp == 8) {
		biSizeImage = (bmp->w + filler) * bmp->h;
		bfSize = (54 + 256 * 4 + biSizeImage);
	}	else {
		biSizeImage = (bmp->w * 3 + filler) * bmp->h;
		bfSize = 54 + biSizeImage;
	}

	_is_perrno_set(0);

	/* file_header */
	is_iputw(stream, 0x4D42);              /* bfType ("BM") */
	is_iputl(stream, bfSize);              /* bfSize */
	is_iputw(stream, 0);                   /* bfReserved1 */
	is_iputw(stream, 0);                   /* bfReserved2 */

	if (bpp == 8) is_iputl(stream, 54 + 256 * 4); 
	else is_iputl(stream, 54); 

	/* info_header */
	is_iputl(stream, 40);                  /* biSize */
	is_iputl(stream, (long)bmp->w);  /* biWidth */
	is_iputl(stream, (long)bmp->h); /* biHeight */
	is_iputw(stream, 1);                   /* biPlanes */
	is_iputw(stream, bpp);                 /* biBitCount */
	is_iputl(stream, 0);                   /* biCompression */
	is_iputl(stream, biSizeImage);         /* biSizeImage */
	is_iputl(stream, 0xB12);               /* (0xB12 = 72 dpi) */
	is_iputl(stream, 0xB12);               /* biYPelsPerMeter */

	if (bpp == 8) {
		is_iputl(stream, 256);              /* biClrUsed */
		is_iputl(stream, 256);              /* biClrImportant */
		for (i = 0; i < 256; i++) {
			is_putc(stream, pal[i].b);
			is_putc(stream, pal[i].g);
			is_putc(stream, pal[i].r);
			is_putc(stream, 0);
		}
	}	else {
		is_iputl(stream, 0);                /* biClrUsed */
		is_iputl(stream, 0);                /* biClrImportant */
	}

	/* image data */
	for (j = (long)bmp->h - 1; j >= 0; j--) {
		for (i = 0; i < (long)bmp->w; i++) {
			c = _is_getpx(bmp, i, j);
			p = _im_color_rgb(c, bmp->bpp, m, pal);
			if (bpp == 8) {
				is_putc(stream, (long)c);
			}	else {
				is_putc(stream, (int)((p >>  0) & 0xFF));
				is_putc(stream, (int)((p >>  8) & 0xFF));
				is_putc(stream, (int)((p >> 16) & 0xFF));
			}
		}
		for (i = 0; i < (long)filler; i++) is_putc(stream, 0);
	}

	return 0;
}

//---------------------------------------------------------------------
// bmp - iload_bmp_file
//---------------------------------------------------------------------
struct IBITMAP *iload_bmp_file(const char *filename, IRGB *pal, long pos)
{
	IRWCHARD stream;
	struct IBITMAP *bmp;
	if (is_open_file(&stream, filename, "rb")) {
		_is_perrno_set(-1);
		return NULL;
	}
	fseek((FILE*)(stream.data), pos, SEEK_SET);
	bmp = iload_bmp_stream(&stream, pal);
	is_close_file(&stream);
	return bmp;
}

//---------------------------------------------------------------------
// bmp - iload_bmp_mem
//---------------------------------------------------------------------
struct IBITMAP *iload_bmp_mem(const void *memory, IRGB *pal)
{
	IRWCHARD stream;
	is_init_mem(&stream, memory, 0x7FFFFFFF);
	return iload_bmp_stream(&stream, pal);
}

//---------------------------------------------------------------------
// bmp - isave_bmp_file
//---------------------------------------------------------------------
int isave_bmp_file(const char *file, struct IBITMAP *bmp, const IRGB *pal)
{
	IRWCHARD stream;
	int retval;

	if (is_open_file(&stream, file, "wb")) return -1;
	retval = isave_bmp_stream(&stream, bmp, pal);
	is_close_file(&stream);

	return retval;
}


//=====================================================================
//
// tga - Tagged Graphics (Truevision Inc.) Format
//
// decoding program - idcolor.h
// 
//=====================================================================


//---------------------------------------------------------------------
// tga - single_tga_readn
//---------------------------------------------------------------------
static ICOLORD single_tga_readn(IRWCHARD *stream, int bpp)
{
	ICOLORD r, g, b, a, c;

	if (bpp == 8) return is_getc(stream);
	if (bpp == 15) {
		c = is_igetw(stream);
		r = _iscale_rgb_5[(c >> 10) & 0x1F];
		g = _iscale_rgb_5[(c >>  5) & 0x1F];
		b = _iscale_rgb_5[(c >>  0) & 0x1F];
		return _imcolor15(r, g, b);
	}
	if (bpp == 16) {
		c = is_igetw(stream);
		r = _iscale_rgb_5[(c >> 11) & 0x1F];
		g = _iscale_rgb_6[(c >>  5) & 0x3F];
		b = _iscale_rgb_5[(c >>  0) & 0x1F];
		return _imcolor16(r, g, b);
	}
	if (bpp != 24 && bpp != 32) {
		return _imcolora32(0xff,0xff,0xff,0xff);
	}

	b = is_getc(stream);
	g = is_getc(stream);
	r = is_getc(stream);
	a = (bpp == 32)? is_getc(stream) : 0;
	c = (bpp == 32)? _imcolora32(r, g, b, a) : _imcolor24(r, g, b);

	return c;
}

//---------------------------------------------------------------------
// tga - raw_tga_readn
//---------------------------------------------------------------------
static void *raw_tga_readn(void *b, int w, int bpp, IRWCHARD *stream)
{
	unsigned char *lptr = (unsigned char*)b;
	unsigned long c, n;

	n = (bpp + 7) >> 3;
	for (; w > 0; w--, lptr += n) {
		c = single_tga_readn(stream, bpp);
		if (n == 1) _im_put1b(lptr, c);
		else if (n == 2) _im_put2b(lptr, c);
		else if (n == 3) _im_put3b(lptr, c);
		else if (n == 4) _im_put4b(lptr, c);
	}

	return (void*)lptr;
}

//---------------------------------------------------------------------
// tga - iload_tga_stream
//---------------------------------------------------------------------
struct IBITMAP *iload_tga_stream(IRWCHARD *stream, IRGB *pal)
{
	unsigned char image_id[256], image_palette[256][3];
	unsigned char id_length, palette_type, image_type, palette_entry_size;
	unsigned char bpp, descriptor_bits;
	unsigned long c, i, y, yc, n, x;
	unsigned short palette_colors;
	unsigned short image_width, image_height;
	unsigned char *lptr;
	struct IBITMAP *bmp;
	int dest_depth, compressed, want_palette, count;
	IRGB tmppal[256];
	
	assert(stream);

	_is_perrno_set(0);
	_ishift_init();

	want_palette = 1;
	if (!pal) {
		want_palette = 0;
		pal = tmppal;
	}

	id_length = is_getc(stream);
	palette_type = is_getc(stream);
	image_type = is_getc(stream);
	palette_colors  = is_igetw(stream);
	palette_entry_size = is_getc(stream);
	image_width = is_igetw(stream);
	image_height = is_igetw(stream);
	bpp = is_getc(stream);
	descriptor_bits = is_getc(stream);

	is_reader(stream, &image_id, id_length);

	if (palette_type == 1) {
		for (i = 0; i < palette_colors; i++) {
			if (palette_entry_size == 16) {
				c = is_igetw(stream);
				image_palette[i][0] = (ICOLORB)(((c >>  0) & 0x1F) << 3);
				image_palette[i][1] = (ICOLORB)(((c >>  5) & 0x1F) << 3);
				image_palette[i][2] = (ICOLORB)(((c >> 10) & 0x1F) << 3);
			}
			else if (palette_entry_size >= 24) {
				image_palette[i][0] = is_getc(stream);
				image_palette[i][1] = is_getc(stream);
				image_palette[i][2] = is_getc(stream);
				if (palette_entry_size == 32) is_getc(stream);

			}
		}
	}
	else if (palette_type != 0) {
		_is_perrno_set(1);
		return NULL;
	}

	/* 
	 * (image_type & 7): 1/colormapped, 2/truecolor, 3/grayscale
	 * (image_type & 8): 0/uncompressed, 8/compressed
	 */
	compressed = (image_type & 8);
	image_type = (image_type & 7);

	if ((image_type < 1) || (image_type > 3)) {
		_is_perrno_set(2);
		return NULL;
	}
	dest_depth = bpp;

	if (image_type == 1) {				/* paletted image */
		if ((palette_type != 1) || (bpp != 8)) {
			_is_perrno_set(11);
			return NULL;
		}
		for(i = 0; i < palette_colors; i++) {
			pal[i].r = image_palette[i][2] >> 2;
			pal[i].g = image_palette[i][1] >> 2;
			pal[i].b = image_palette[i][0] >> 2;
		}
	}
	else if (image_type == 2) {			/* truecolor image */
		if (palette_type != 0) {
			_is_perrno_set(21);
			return NULL;
		}
		if (bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32) {
			_is_perrno_set(22);
			return NULL;
		}
		if (bpp == 16) bpp = 15;
	}
	else if (image_type == 3) {			/* grayscale image */
		if ((palette_type != 0) || (bpp != 8)) {
			_is_perrno_set(31);
			return NULL;
		}
		for (i = 0; i < 256; i++) {
			pal[i].r = (unsigned char)(i >> 2);
			pal[i].g = (unsigned char)(i >> 2);
			pal[i].b = (unsigned char)(i >> 2);
		}
	}

	n = (bpp + 7) >> 3;
	bmp = ibitmap_create(image_width, image_height, bpp);
	if (!bmp) {
		_is_perrno_set(50);
		return NULL;
	}
	for (y = 0; y < bmp->h; y++) {
		memset(bmp->line[y], 0, bmp->pitch);
	}

	for (y = image_height; y; y--) {
		yc = (descriptor_bits & 0x20) ? image_height - y : y - 1;
		lptr = (unsigned char*)(bmp->line[yc]);
		if (compressed == 0) {
			raw_tga_readn(lptr, image_width, bpp, stream);
		}	else {
			for (x = 0; x < image_width; x += count) {
				count = is_getc(stream);
				if (count & 0x80) {
					count = (count & 0x7F) + 1;
					c = single_tga_readn(stream, bpp);
					for (i = 0; i < (unsigned long)count; i++, lptr += n) {
						if (n == 1) _im_put1b(lptr, c);
						else if (n == 2) _im_put2b(lptr, c);
						else if (n == 3) _im_put3b(lptr, c);
						else if (n == 4) _im_put4b(lptr, c);
					}
				}	else {
					count = count + 1;
					raw_tga_readn(lptr, count, bpp, stream);
					lptr = lptr + n * count;
				}
			}
		}
	}

	if (_is_perrno_get()) {
		ibitmap_release(bmp);
		return NULL;
	}

	if (dest_depth != bpp) {
		if ((bpp != 8) && (!want_palette)) pal = NULL;
	}

	/* construct a fake palette if 8-bit mode is not involved */
	if ((bpp != 8) && (dest_depth != 8) && want_palette) {
		_igenerate_332_palette(pal);
	}
      
	return bmp;
}

//---------------------------------------------------------------------
// tga - isave_tga_stream
//---------------------------------------------------------------------
int isave_tga_stream(IRWCHARD *stream, struct IBITMAP *bmp, const IRGB *pal)
{
	unsigned char image_palette[256][3];
	unsigned long c, a, r, g, b, p;
	long x, y, depth, m, n;
	IRGB tmppal[256];

	assert(bmp);
	assert(stream);

	if (!pal) {
		memcpy(tmppal, _ipaletted, 256 * sizeof(IRGB));
		pal = tmppal;
	}

	depth = bmp->bpp;

	if (depth == 15) depth = 16;
	_is_perrno_set(0);

	is_putc(stream, 0);                       /* id length (no id saved) */
	is_putc(stream, (depth == 8) ? 1 : 0);       /* palette type */
	is_putc(stream, (depth == 8) ? 1 : 2);       /* image type */
	is_iputw(stream, 0);                         /* first colour */
	is_iputw(stream, (depth == 8) ? 256 : 0);    /* number of colours */
	is_putc(stream, (depth == 8) ? 24 : 0);      /* palette entry size */
	is_iputw(stream, 0);                         /* left */
	is_iputw(stream, 0);                         /* top */
	is_iputw(stream, (int)bmp->w);         /* width */
	is_iputw(stream, (int)bmp->h);        /* height */
	is_putc(stream, depth);                      /* bits per pixel */
	is_putc(stream, (depth == 32) ? 8 : 0);      /* descriptor 
											(bottom to top, 8-bit alpha) */

	if (depth == 8) {
		for (y = 0; y < 256; y++) {
			image_palette[y][2] = pal[y].r;
			image_palette[y][1] = pal[y].g;
			image_palette[y][0] = pal[y].b;
			is_putc(stream, image_palette[y][0]);
			is_putc(stream, image_palette[y][1]);
			is_putc(stream, image_palette[y][2]);
		}
	}

	n = (bmp->bpp + 7) / 8;
	m = (bmp->mask & 1)? 1 : 0;

	for (y = (long)bmp->h - 1; y >= 0; y--) {
		for (x = 0; x < (long)bmp->w; x++) {
			c = _is_getpx(bmp, x, y);
			p = _im_color_rgb(c, bmp->bpp, m, pal);
			a = (p >> 24) & 0xFF;
			r = (p >> 16) & 0xFF;
			g = (p >>  8) & 0xFF;
			b = (p >>  0) & 0xFF;
			if (n == 1) is_putc(stream, c);
			else if (n == 2) {
				c = ((r << 7) & 0x7C00) | ((g << 2) & 0x3E0);
				is_iputw(stream, c | ((b >> 3) & 0x1F));
			}
			else if (n == 3 || n == 4) {
				is_putc(stream, b);
				is_putc(stream, g);
				is_putc(stream, r);
				if (n == 4) is_putc(stream, a);
			}
		}
	}

	return 0;
}

//---------------------------------------------------------------------
// tga - iload_tga_file
//---------------------------------------------------------------------
struct IBITMAP *iload_tga_file(const char *filename, IRGB *pal, long pos)
{
	IRWCHARD stream;
	struct IBITMAP *bmp;
	if (is_open_file(&stream, filename, "rb")) {
		_is_perrno_set(-1);
		return NULL;
	}
	fseek((FILE*)(stream.data), pos, SEEK_SET);
	bmp = iload_tga_stream(&stream, pal);
	is_close_file(&stream);
	return bmp;
}

//---------------------------------------------------------------------
// tga - iload_tga_mem
//---------------------------------------------------------------------
struct IBITMAP *iload_tga_mem(const void *memory, IRGB *pal)
{
	IRWCHARD stream;
	is_init_mem(&stream, memory, 0x7FFFFFFF);
	return iload_tga_stream(&stream, pal);
}

//---------------------------------------------------------------------
// tga - isave_tga_file
//---------------------------------------------------------------------
int isave_tga_file(const char *file, struct IBITMAP *bmp, const IRGB *pal)
{
	IRWCHARD stream;
	int retval;

	if (is_open_file(&stream, file, "wb")) return -1;
	retval = isave_tga_stream(&stream, bmp, pal);
	is_close_file(&stream);

	return retval;
}


//=====================================================================
// ORIGINAL OPERATION
//=====================================================================
static unsigned char pal[256 * 4];

void ipic_read_pal(void *_pal)
{
	memcpy(_pal, pal, 256 * 4);
}

void ipic_write_pal(const void *_pal)
{
	memcpy(pal, _pal, 256 * 4);
}

struct IBITMAP *ipic_convert(const struct IBITMAP *src, int bpp, void *data)
{
	if (src == NULL) return NULL;
	return 	ibitmap_fixup(src, bpp, (IRGB*)data);
}

struct IBITMAP *ipic_load(const char *file, long pos)
{
	unsigned char header[8];
	FILE *fp;
	fp = fopen(file, "rb");
	if (!fp) return NULL;
	fseek(fp, pos, SEEK_SET);
	fread(header, 1, 8, fp);
	fclose(fp);
	if (header[0] == 0x42 && header[1] == 0x4d) 
		return ipic_load_bmp(file, pos);
	if (memcpy(header, "GIF", 3) == 0)
		return ipic_load_gif(file, pos);
	return ipic_load_tga(file, pos);
}

struct IBITMAP *ipic_load_bmp(const char *file, long pos)
{
	IRWCHARD stream;
	struct IBITMAP *bmp;
	IRGB *pal = _ipaletted;

	if (is_open_file(&stream, file, "rb")) {
		_is_perrno_set(-1);
		return NULL;
	}
	fseek((FILE*)(stream.data), pos, SEEK_SET);
	bmp = iload_bmp_stream(&stream, pal);
	is_close_file(&stream);

	return bmp;
}

struct IBITMAP *ipic_load_tga(const char *file, long pos)
{
	IRWCHARD stream;
	struct IBITMAP *bmp;
	IRGB *pal = _ipaletted;

	if (is_open_file(&stream, file, "rb")) {
		_is_perrno_set(-1);
		return NULL;
	}
	fseek((FILE*)(stream.data), pos, SEEK_SET);
	bmp = iload_tga_stream(&stream, pal);
	is_close_file(&stream);

	return bmp;
}

struct IBITMAP *ipic_load_gif(const char *file, long pos)
{
	IRWCHARD stream;
	struct IBITMAP *bmp;
	IRGB *pal = _ipaletted;

	if (is_open_file(&stream, file, "rb")) {
		_is_perrno_set(-1);
		return NULL;
	}
	fseek((FILE*)(stream.data), pos, SEEK_SET);
	bmp = iload_gif_stream(&stream, pal);
	is_close_file(&stream);

	return bmp;
}

//=====================================================================
//
// GIF Operation Interface
// Since lzw's patent has expired, we can decode/encode gif freely
//
//=====================================================================

//---------------------------------------------------------------------
// _igif_clear_table - clear lzw table
//---------------------------------------------------------------------
static void _igif_clear_table(IGIFDESC *gif)
{
	gif->empty_string = gif->cc + 2;
	gif->curr_bit_size = gif->bit_size + 1;
	gif->bit_overflow = 0;
}

//---------------------------------------------------------------------
// _igif_get_code - get input code with current bit size
//---------------------------------------------------------------------
static void _igif_get_code(IGIFDESC *gif)
{
	if (gif->bit_pos + gif->curr_bit_size > 8) {
		if (gif->data_pos >= gif->data_len) {
			gif->data_len = is_getc(gif->stream);
			gif->data_pos = 0; 
		}
		gif->entire = (is_getc(gif->stream) << 8) + gif->entire;
		gif->data_pos++;
	}
	if (gif->bit_pos + gif->curr_bit_size > 16) {
		if (gif->data_pos >= gif->data_len) {
			gif->data_len = is_getc(gif->stream); 
			gif->data_pos = 0;
		}
		gif->entire = (is_getc(gif->stream) << 16) + gif->entire;
		gif->data_pos++;
	}
	gif->code = (gif->entire >> gif->bit_pos) & ((1 << 
		gif->curr_bit_size) - 1);
	if (gif->bit_pos + gif->curr_bit_size > 8) 
		gif->entire >>= 8;
	if (gif->bit_pos + gif->curr_bit_size > 16)
		gif->entire >>= 8;
	gif->bit_pos = (gif->bit_pos + gif->curr_bit_size) % 8;
	if (gif->bit_pos == 0) {
		if (gif->data_pos >= gif->data_len) {
			gif->data_len = is_getc(gif->stream);
			gif->data_pos = 0;
		}
		gif->entire = is_getc(gif->stream);
		gif->data_pos++;
	}
}

//---------------------------------------------------------------------
// _igif_get_string - get string into gif->string
//---------------------------------------------------------------------
static void _igif_get_string(IGIFDESC *gif, int num)
{
	unsigned char *string;
	int i;
	string = gif->string;
	if (num < gif->cc) {
		gif->string_length = 1;
		string[0] = gif->newc[num];
	}	else {
		i = gif->str[num].length;
		gif->string_length = i;
		for (; i > 0; ) {
			i--;
			string[i] = gif->newc[num];
			num = gif->str[num].base;
		}
	}
}

//---------------------------------------------------------------------
// _igif_output_string - output string to bitmap
//---------------------------------------------------------------------
static void _igif_output_string(IGIFDESC *gif)
{
	unsigned char *string, **line;
	int imgx, imgy, x, y, len, last;
	if (gif->error || gif->bitmap == NULL) return;
	x = gif->x;
	y = gif->y;
	imgx = gif->image_x;
	imgy = gif->image_y;
	line = (unsigned char**)gif->bitmap->line;
	string = gif->string;
	last = gif->string_length;
	for (; last > 0; ) {
		len = gif->image_w + imgx - x;
		len = len > last ? last : len;
		memcpy(line[y] + x, string, len);
		x += len;
		string += len;
		last -= len;
		if (x >= imgx + gif->image_w) {
			x = imgx;
			y += gif->interlace;
			if (gif->interlace) {
				if (y >= imgy + gif->image_h) {
					if (gif->interlace == 8 && (y - imgy) % 8 == 0) {
						gif->interlace = 8;
						y = imgy + 4;
					}	else
					if (gif->interlace == 8 && (y - imgy) % 8 == 4) {
						gif->interlace = 4;
						y = imgy + 2;
					}	else
					if (gif->interlace == 4) {
						gif->interlace = 2;
						y = imgy + 1;
					}
				}
			}
		}
	}
	gif->x = x;
	gif->y = y;
}

//---------------------------------------------------------------------
// ipic_gif_open - open gif and load header
//---------------------------------------------------------------------
int ipic_gif_open(IGIFDESC *gif, IRWCHARD *stream, IRGB *pal, int customer)
{
	long c, i;

	assert(gif && stream);
	memset(gif, 0, sizeof(IGIFDESC));

	gif->stream = stream;
	_ishift_init();

	c = is_mgetw(stream) << 8;
	c = c | is_getc(stream);
	gif->state = -1;
	if (c != 0x474946) {
		return -1;
	}

	is_seekcur(stream, 3);

	gif->width = is_igetw(stream);
	gif->height = is_igetw(stream);
	gif->delay = 0;
	gif->frame = -1;

	gif->pal = pal;
	gif->bpp = 0;

	c = is_getc(stream);
	if (c & 128) 
		gif->bpp = (c & 7) + 1;

	gif->background = (unsigned char)is_getc(stream);
	gif->aspect = (unsigned char)is_getc(stream);

	if (gif->bpp && pal) {
		for (i = 0; i < (1 << gif->bpp); i++) {
			pal[i].r = is_getc(stream);
			pal[i].g = is_getc(stream);
			pal[i].b = is_getc(stream);
		}
	}	
	else if (gif->bpp) {
		is_seekcur(stream, (1 << gif->bpp) * 3);
	}

	if (customer == 0) {
		gif->bitmap = ibitmap_create(gif->width, gif->height, 8);
		if (gif->bitmap == NULL) {
			return -2;
		}
		ibitmap_fill(gif->bitmap, 0, 0, gif->width, gif->height,
			gif->background, 0);
	}	

	gif->state = 1;
	gif->error = 0;
	gif->transparent = -1;
	gif->customer_bmp = customer;

	return 0;
}

//---------------------------------------------------------------------
// ipic_gif_close - close gif and free bitmap
//---------------------------------------------------------------------
int ipic_gif_close(IGIFDESC *gif)
{
	if (gif->customer_bmp == 0) {
		ibitmap_release(gif->bitmap);
		gif->bitmap = NULL;
	}
	gif->state = 0;
	return 0;
}

//---------------------------------------------------------------------
// ipic_gif_read_image_desc - read image desc
//---------------------------------------------------------------------
static int ipic_gif_read_image_desc(IGIFDESC *gif)
{
	unsigned char *string;
	IRWCHARD *stream;
	int depth, old, i;
	int retval = 0;

	if (gif->state != 1) return -1;
	stream = gif->stream;
	string = gif->string;
	
	gif->image_x = is_igetw(stream);
	gif->image_y = is_igetw(stream);
	gif->image_w = is_igetw(stream);
	gif->image_h = is_igetw(stream);

	if (gif->image_x < 0 || gif->image_y < 0 ||
		gif->image_x + gif->image_w > gif->width ||
		gif->image_y + gif->image_h > gif->height)
		gif->error = 1;

	i = is_getc(stream);
	if (i & 64) gif->interlace = 8;
	else gif->interlace = 1;

	if (i & 128) {
		depth = (i & 7) + 1;
		if (gif->pal) {
			for (i = 0; i < (1 << depth); i++) {
				gif->pal[i].r = is_getc(stream);
				gif->pal[i].g = is_getc(stream);
				gif->pal[i].b = is_getc(stream);
			}
		}	else {
			is_seekcur(stream, (1 << depth) * 3);
		}
	}

	gif->bit_size = is_getc(stream);
	gif->cc = 1 << gif->bit_size;

	for (i = 0; i < gif->cc; i++) {
		gif->str[i].base = -1;
		gif->str[i].length = 1;
		gif->newc[i] = (unsigned char)i;
	}

	gif->bit_pos = 0;
	gif->data_len = is_getc(stream);
	gif->data_pos = 0;
	gif->entire = is_getc(stream);
	gif->data_pos++;
	gif->string_length = 0;
	gif->x = gif->image_x;
	gif->y = gif->image_y;

	_igif_clear_table(gif);
	_igif_get_code(gif);
	if (gif->code == gif->cc) 
		_igif_get_code(gif);

	_igif_get_string(gif, gif->code);
	_igif_output_string(gif);
	old = gif->code;

	for (; ; ) {
		_igif_get_code(gif);
		if (gif->code == gif->cc + 1) {
			break;
		}
		if (gif->entire < 0) {
			retval = -1;
			gif->error = 2;
			break;
		}
		if (gif->code == gif->cc) {
			_igif_clear_table(gif);
			_igif_get_code(gif);
			_igif_get_string(gif, gif->code);
			_igif_output_string(gif);
			old = gif->code;
		}	else
		if (gif->code < gif->empty_string) {
			_igif_get_string(gif, gif->code);
			_igif_output_string(gif);
			if (gif->bit_overflow == 0) {
				i = gif->empty_string;
				gif->str[i].base = (short)old;
				gif->str[i].length = gif->str[old].length + 1;
				gif->newc[i] = string[0];
				gif->empty_string++;
				if (gif->empty_string == (1 << gif->curr_bit_size))
					gif->curr_bit_size++;
				if (gif->curr_bit_size == 13) {
					gif->curr_bit_size = 12;
					gif->bit_overflow = 1;
				}
			}
			old = gif->code;
		}	else {
			_igif_get_string(gif, old);
			string[gif->str[old].length] = string[0];
			gif->string_length++;
			if (gif->bit_overflow == 0) {
				i = gif->empty_string;
				gif->str[i].base = (short)old;
				gif->str[i].length = gif->str[old].length + 1;
				gif->newc[i] = string[0];
				gif->empty_string++;
				if (gif->empty_string == (1 << gif->curr_bit_size)) 
					gif->curr_bit_size++;
				if (gif->curr_bit_size == 13) {
					gif->curr_bit_size = 12;
					gif->bit_overflow = 1;
				}
			}
			_igif_output_string(gif);
			old = gif->code;
		}
	}
	gif->frame++;

	return retval;
}

//---------------------------------------------------------------------
// ipic_gif_read_extension - read extension
//---------------------------------------------------------------------
int ipic_gif_read_extension(IGIFDESC *gif)
{
	IRWCHARD *stream;
	int control, c;

	stream = gif->stream;
	control = is_getc(stream);
	gif->delay = 0;
	gif->transparent = -1;
	gif->method = 0;

	if (control == 0xf9) {
		c = is_getc(stream); 
		if (c != 4) return -1;

		gif->method = is_getc(stream);
		gif->delay = is_igetw(stream);
		
		gif->transparent = is_getc(stream);
		gif->bitmap->mask = (unsigned char)gif->transparent;

		if ((gif->method & 1) == 0)
			gif->transparent = -1;

		c = (gif->method >> 2) & 7;

		if (c == 2) {
			ibitmap_fill(gif->bitmap, 0, 0, gif->width, gif->height,
				gif->background, 0);
		}
	} 

	for (; ; ) {
		c = is_getc(stream);
		if (c <= 0) break;
		is_seekcur(stream, c);
	}
	
	return 0;
}

//---------------------------------------------------------------------
// ipic_gif_read_frame - read single frame
//---------------------------------------------------------------------
int ipic_gif_read_frame(IGIFDESC *gif)
{
	int retval, type;
	if (gif->state != 1) return -1;
	for (retval = 0; ; ) {
		type = is_getc(gif->stream);
		if (type < 0 || type == 0x3b || gif->error) {
			retval = -2;
			break;
		}
		if (type == 0) continue;
		if (type == 0x21) {
			ipic_gif_read_extension(gif);
		}	else
		if (type == 0x2c) {
			ipic_gif_read_image_desc(gif);
			retval = gif->delay;
			break;
		}	else {
			printf("gif unknow %d\n", type);
			retval = -3;
			gif->error = 5;
			break;
		}
	}
	return retval;
}

//---------------------------------------------------------------------
// load gif picture from stream
//---------------------------------------------------------------------
struct IBITMAP *iload_gif_stream(IRWCHARD *stream, IRGB *pal)
{
	IGIFDESC *gif;
	IBITMAP *bmp;
	gif = (IGIFDESC*)malloc(sizeof(IGIFDESC));
	if (gif == NULL) return NULL;
	if (ipic_gif_open(gif, stream, pal, 0) != 0) {
		free(gif);
		return NULL;
	}
	for (; ; ) {
		if (ipic_gif_read_frame(gif) < 0) break;
	}
	bmp = gif->bitmap;
	free(gif);
	return bmp;
}

//---------------------------------------------------------------------
// gif - iload_gif_file
//---------------------------------------------------------------------
struct IBITMAP *iload_gif_file(const char *filename, IRGB *pal, long pos)
{
	IRWCHARD stream;
	struct IBITMAP *bmp;
	if (is_open_file(&stream, filename, "rb")) {
		_is_perrno_set(-1);
		return NULL;
	}
	fseek((FILE*)(stream.data), pos, SEEK_SET);
	bmp = iload_gif_stream(&stream, pal);
	is_close_file(&stream);
	return bmp;
}

//---------------------------------------------------------------------
// gif - iload_gif_mem
//---------------------------------------------------------------------
struct IBITMAP *iload_gif_mem(const void *memory, IRGB *pal)
{
	IRWCHARD stream;
	is_init_mem(&stream, memory, 0x7FFFFFFF);
	return iload_gif_stream(&stream, pal);
}

