/*--------------------------------------------------------------
	BMP to PNM image filter       bmptopnm  v1.7
		Written by H.Goto , Jul 1996
		Revised by H.Goto , Oct 1996
		Revised by H.Goto , Feb 1997
		Revised by H.Goto , Aug 1998
		Revised by H.Goto , Apr 2000
		Revised by H.Goto , May 2002
		Revised by H.Goto , Jul 2003
		Revised by H.Goto , Dec 2008
--------------------------------------------------------------*/

/*--------------------------------------------------------------------
  Copyright (C) 1996-2008  Hideaki Goto

        All Rights Reserved

  Permission to use, copy, modify, and distribute this software and
  its documentation for any purpose is hereby granted without fee,
  provided that (i) the above copyright notice and this permission
  notice appear in all copies and in supporting documentation, (ii)
  the name of the author, Hideaki Goto, may not be used in any
  advertising or otherwise to promote the sale, use or other
  dealings in this software without prior written authorization
  from the author, (iii) this software may not be used for
  commercial products without prior written permission from the
  author, and (iv) the notice of modification is specified in cases
  where modified copies of this software are distributed.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
  THE AUTHOR WILL NOT BE RESPONSIBLE FOR ANY DAMAGE CAUSED BY THIS
  SOFTWARE.
--------------------------------------------------------------------*/


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

#include	"siplib.h"
#include	"ufilep.h"


static int	bmp_width;
static int	bmp_height;
static int	bmp_depth;
static int	pnm_depth;
static int	bmp_bytesperline;
static uchar	*lbuf = 0;


int bmp_readheader(FILE *fp){
	char	c;
	uint32	off;
	uint32	colors = 0;
	int	i;
	int32	biSize;

	if ( EOF == (c = getc(fp)) )  return(-1);
	if ( c != 'B' )  return(-2);
	if ( EOF == (c = getc(fp)) )  return(-1);
	if ( c != 'M' )  return(-2);
				/* bfSize: file size in bytes */
	if ( 0 > (int32)uf_GetLittleDWORD(fp) )  return(-2);
				/* bfReserved1: = 0 */
	if ( 0 != uf_GetLittleWORD(fp) )  return(-2);
				/* bfReserved2: = 0 */
	if ( 0 != uf_GetLittleWORD(fp) )  return(-2);
				/* bfOffBits: offset to bitmap data */
	if ( 0 > (int32)(off = uf_GetLittleDWORD(fp)) )  return(-2);

				/* biSize: size of BITMAPINFOHEADER in bytes */
				/*   = 40: MS-Windows */
				/*   = 12: OS/2       */
	if ( 0 > (biSize = (int32)uf_GetLittleDWORD(fp)) )  return(-2);

	if ( biSize > 12 ){
				/* biWidth: width of bitmap in pixels */
		if ( 0 > (int32)(bmp_width = (int)uf_GetLittleDWORD(fp)) )  return(-2);
				/* biHeight: height of bitmap in pixels */
		if ( 0 > (int32)(bmp_height = (int)uf_GetLittleDWORD(fp)) )  return(-2);
				/* biPlanes: = 1 */
		if ( 1 != uf_GetLittleWORD(fp) )  return(-2);
				/* biBitCount: bits per pixel */
		bmp_depth = (int)uf_GetLittleWORD(fp);
				/* biCompression: type of compression */
		if ( 0 != uf_GetLittleDWORD(fp) )  return(-2);
				/* biSizeImage: size of image in bytes */
		if ( 0 > (int32)uf_GetLittleDWORD(fp) )  return(-2);
				/* biXPelsPerMeter: horizontal pixels per meter */
		if ( 0 > (int32)uf_GetLittleDWORD(fp) )  return(-2);
				/* biYPelsPerMeter: vertical pixels per meter */
		if ( 0 > (int32)uf_GetLittleDWORD(fp) )  return(-2);
				/* biClrUsed: used colors */
		if ( 0 > (int32)(colors = uf_GetLittleDWORD(fp)) )  return(-2);
				/* biClrImportant: important colors */
		if ( 0 > (int32)uf_GetLittleDWORD(fp) )  return(-2);
	}
	else{
				/* biWidth: width of bitmap in pixels */
		if ( 0 > (int32)(bmp_width = (int)uf_GetLittleWORD(fp)) )  return(-2);
				/* biHeight: height of bitmap in pixels */
		if ( 0 > (int32)(bmp_height = (int)uf_GetLittleWORD(fp)) )  return(-2);
				/* biPlanes: = 1 */
		if ( 1 != uf_GetLittleWORD(fp) )  return(-2);
				/* biBitCount: bits per pixel */
		bmp_depth = (int)uf_GetLittleWORD(fp);
	}

	switch ( bmp_depth ){
	    case 1:
		bmp_bytesperline = ((bmp_width + 31) /8) & ~0x03;
		break;
	    case 8:
		bmp_bytesperline = (bmp_width + 3) & ~0x03;
		break;
	    case 16:
		bmp_bytesperline = (2 * bmp_width + 3) & ~0x03;
		break;
	    case 24:
		bmp_bytesperline = (3 * bmp_width + 3) & ~0x03;
		break;
	    default:
		return(-2);
	}

	if ( 0 != lbuf )  delete []lbuf;
	if ( 0 == (lbuf = new uchar[bmp_bytesperline]) )  return(-3);

				/* Skip color table */
	for ( i=0 ; i < (int)colors * 4 ; i++ ){
		if ( EOF == getc(fp) )  return(-2);
	}
	return(0);
}


int bmp_readline(FILE *fp,char *buf){
	int	x,x3;
	uchar	c;
	ushort	d;
	if ( 0 == lbuf )  return(-1);
	if ( 1 != fread((void *)lbuf,bmp_bytesperline,1,fp) )  return(-1);
	switch ( bmp_depth ){
	    case 8:
		memcpy((void *)buf,(void *)lbuf,bmp_width);
		break;
	    case 16:
		for ( x=x3=0 ; x < bmp_width ; x++, x3+=3 ){
			d = (ushort)lbuf[2*x+1] << 8 | (ushort)lbuf[2*x];
			c = (uchar)(d >> 7);
			c = (c & 0xf8) | (c >> 5);
			buf[x3]   = c;		/* R */
			c = (uchar)(d >> 2);
			c = (c & 0xfc) | (c >> 5);
			buf[x3+1] = c;		/* G */
			c = (uchar)(d << 3);
			c |= c >> 5;
			buf[x3+2] = c;		/* B */
		}
		break;
	    case 24:
		for ( x=0 ; x < (3 * bmp_width) ; x+=3 ){
			buf[x]   = lbuf[x+2];	/* R */
			buf[x+1] = lbuf[x+1];	/* G */
			buf[x+2] = lbuf[x];	/* B */
		}
		break;
	    default:
		memcpy((void *)buf,(void *)lbuf,(bmp_width +7) /8);
		break;
	}
	return(0);
}


int main(int ac,char **av){
	FILE		*fp = stdin;
	char		*infile = 0;
	int		sw_invert = 0;
	int		sw_verbose = 0;
	PBMFILE		PBMSV;
	int		i,x,y;
	char		*p;
	SIPImage	*image;
	for ( i=1 ; i<ac ; i++ ){
		if ( 0 == strcmp(av[i],"-i") )  sw_invert = -1;
		else if ( 0 == strcmp(av[i],"-v") )  sw_verbose = -1;
		else if ( *av[i] == '-' ){
			fputs("bmptopnm  v1.7 ,  Copyright (C) 1996-2008 Hideaki Goto \n",stderr);
			fputs("usage: bmptopnm [options] [bmpfile] \n",stderr);
			fputs("   Reads a BMP(DIB) image. Produce a portable pixmap as output(stdout). \n",stderr);
			fputs("   Pseudo color image is not supported. \n",stderr);
			fputs("options:  -i : invert binary image \n",stderr);
			fputs("          -v : show processing info. \n",stderr);
			fputs("          -h : show help message \n",stderr);
			return(0);
		}
		else	infile = av[i];
	}

	if ( infile ){
		if ( NULL == (fp = fopen(infile,"rb")) ){
			fputs("File open error.\n",stderr);
			exit(1);
		}
	}
	if ( bmp_readheader(fp) ){
		fputs("File read error.\n",stderr);
		exit(1);
	}
	pnm_depth = bmp_depth;
	if ( 16 == pnm_depth )  pnm_depth = 24;

	if ( sw_verbose ){
		fprintf(stderr,"input image:  %dx%d, %dbit\n", \
			bmp_width,bmp_height,bmp_depth);
		fprintf(stderr,"output image: %dx%d, %dbit\n", \
			bmp_width,bmp_height,pnm_depth);
	}

	if ( 0 == (image = sip_CreateImage(bmp_width, \
			bmp_height, pnm_depth)) ){
		fputs("Memory not enough.\n",stderr);
		exit(1);
	}
	PBMSV.setsize(bmp_width,bmp_height,pnm_depth);
	if ( PBMSV.open("","w") ){
		fputs("Stream open error.\n",stderr);
		exit(1);
	}

	for ( y=0 ; y < bmp_height ; y++ ){
		if ( bmp_readline(fp,sip_getimgptr(image,bmp_height - y -1)) ){
			fputs("File read error.\n",stderr);
			exit(1);
		}
	}
	for ( y=0 ; y < bmp_height ; y++ ){
		if ( sw_invert && bmp_depth == 1){
			p = (char *)sip_getimgptr(image,y);
			for ( x=0 ; x<((bmp_width +7) /8) ; x++ )  p[x] = ~p[x];
		}
		if ( PBMSV.writeline(-1,(void *)sip_getimgptr(image,y)) ){
			fputs("Stream write error.\n",stderr);
			exit(1);
		}
	}

	if ( PBMSV.close() ){
		fputs("Stream close error.\n",stderr);
		exit(1);
	}
	sip_DestroyImage(image);
	if ( ! infile )  fclose(fp);
	return(0);
}


