www.pudn.com > PopFaxPrinter_src_v2.01.zip > bmp2tiff.c


/* $Id: bmp2tiff.c,v 1.16 2005/12/29 00:33:51 bfriesen Exp $ 
 * 
 * Project:  libtiff tools 
 * Purpose:  Convert Windows BMP files in TIFF. 
 * Author:   Andrey Kiselev, dron@remotesensing.org 
 * 
 ****************************************************************************** 
 * Copyright (c) 2004, Andrey Kiselev  
 * 
 * Permission to use, copy, modify, distribute, and sell this software and  
 * its documentation for any purpose is hereby granted without fee, provided 
 * that (i) the above copyright notices and this permission notice appear in 
 * all copies of the software and related documentation, and (ii) the names of 
 * Sam Leffler and Silicon Graphics may not be used in any advertising or 
 * publicity relating to the software without the specific, prior written 
 * permission of Sam Leffler and Silicon Graphics. 
 *  
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,  
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY  
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.   
 *  
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR 
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, 
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF  
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE  
 * OF THIS SOFTWARE. 
 */ 
 
#include "tif_config.h.in" 
#include  
#include  
 
#include  
#include  
#include  
#include  
#include  
#include  
 
#ifdef HAVE_UNISTD_H 
# include  
#endif 
 
#if HAVE_FCNTL_H 
# include  
#endif 
 
#if HAVE_SYS_TYPES_H 
# include  
#endif 
 
#if HAVE_IO_H 
# include  
#endif 
 
#include "tiffio.h" 
 
#ifndef O_BINARY 
# define O_BINARY 0 
#endif 
 
enum BMPType 
{ 
    BMPT_WIN4,      /* BMP used in Windows 3.0/NT 3.51/95 */ 
    BMPT_WIN5,      /* BMP used in Windows NT 4.0/98/Me/2000/XP */ 
    BMPT_OS21,      /* BMP used in OS/2 PM 1.x */ 
    BMPT_OS22       /* BMP used in OS/2 PM 2.x */ 
}; 
 
/* 
 * Bitmap file consists of a BMPFileHeader structure followed by a 
 * BMPInfoHeader structure. An array of BMPColorEntry structures (also called 
 * a colour table) follows the bitmap information header structure. The colour 
 * table is followed by a second array of indexes into the colour table (the 
 * actual bitmap data). Data may be comressed, for 4-bpp and 8-bpp used RLE 
 * compression. 
 * 
 * +---------------------+ 
 * | BMPFileHeader       | 
 * +---------------------+ 
 * | BMPInfoHeader       | 
 * +---------------------+ 
 * | BMPColorEntry array | 
 * +---------------------+ 
 * | Colour-index array  | 
 * +---------------------+ 
 * 
 * All numbers stored in Intel order with least significant byte first. 
 */ 
 
enum BMPComprMethod 
{ 
    BMPC_RGB = 0L,          /* Uncompressed */ 
    BMPC_RLE8 = 1L,         /* RLE for 8 bpp images */ 
    BMPC_RLE4 = 2L,         /* RLE for 4 bpp images */ 
    BMPC_BITFIELDS = 3L,    /* Bitmap is not compressed and the colour table 
			     * consists of three DWORD color masks that specify 
			     * the red, green, and blue components of each 
			     * pixel. This is valid when used with 
			     * 16- and 32-bpp bitmaps. */ 
    BMPC_JPEG = 4L,         /* Indicates that the image is a JPEG image. */ 
    BMPC_PNG = 5L           /* Indicates that the image is a PNG image. */ 
}; 
 
enum BMPLCSType                 /* Type of logical color space. */ 
{ 
    BMPLT_CALIBRATED_RGB = 0,	/* This value indicates that endpoints and 
				 * gamma values are given in the appropriate 
				 * fields. */ 
    BMPLT_DEVICE_RGB = 1, 
    BMPLT_DEVICE_CMYK = 2 
}; 
 
typedef struct 
{ 
    int32   iCIEX; 
    int32   iCIEY; 
    int32   iCIEZ; 
} BMPCIEXYZ; 
 
typedef struct                  /* This structure contains the x, y, and z */ 
{				/* coordinates of the three colors that */ 
				/* correspond */ 
    BMPCIEXYZ   iCIERed;        /* to the red, green, and blue endpoints for */ 
    BMPCIEXYZ   iCIEGreen;      /* a specified logical color space. */ 
    BMPCIEXYZ	iCIEBlue; 
} BMPCIEXYZTriple; 
 
typedef struct 
{ 
    char	bType[2];       /* Signature "BM" */ 
    uint32	iSize;          /* Size in bytes of the bitmap file. Should 
				 * always be ignored while reading because 
				 * of error in Windows 3.0 SDK's description 
				 * of this field */ 
    uint16	iReserved1;     /* Reserved, set as 0 */ 
    uint16	iReserved2;     /* Reserved, set as 0 */ 
    uint32	iOffBits;       /* Offset of the image from file start in bytes */ 
} BMPFileHeader; 
 
/* File header size in bytes: */ 
const int       BFH_SIZE = 14; 
 
typedef struct 
{ 
    uint32	iSize;          /* Size of BMPInfoHeader structure in bytes. 
				 * Should be used to determine start of the 
				 * colour table */ 
    int32	iWidth;         /* Image width */ 
    int32	iHeight;        /* Image height. If positive, image has bottom 
				 * left origin, if negative --- top left. */ 
    int16	iPlanes;        /* Number of image planes (must be set to 1) */ 
    int16	iBitCount;      /* Number of bits per pixel (1, 4, 8, 16, 24 
				 * or 32). If 0 then the number of bits per 
				 * pixel is specified or is implied by the 
				 * JPEG or PNG format. */ 
    uint32	iCompression;	/* Compression method */ 
    uint32	iSizeImage;     /* Size of uncomressed image in bytes. May 
				 * be 0 for BMPC_RGB bitmaps. If iCompression 
				 * is BI_JPEG or BI_PNG, iSizeImage indicates 
				 * the size of the JPEG or PNG image buffer. */ 
    int32	iXPelsPerMeter; /* X resolution, pixels per meter (0 if not used) */ 
    int32	iYPelsPerMeter; /* Y resolution, pixels per meter (0 if not used) */ 
    uint32	iClrUsed;       /* Size of colour table. If 0, iBitCount should 
				 * be used to calculate this value 
				 * (1< 0) ? info_hdr.iHeight : -info_hdr.iHeight; 
 
	switch (info_hdr.iBitCount) 
        { 
                case 1: 
                case 4: 
                case 8: 
                        nbands = 1; 
			depth = info_hdr.iBitCount; 
                        photometric = PHOTOMETRIC_PALETTE; 
                        /* Allocate memory for colour table and read it. */ 
                        if (info_hdr.iClrUsed) 
                            clr_tbl_size = ((uint32)(1 << depth) < info_hdr.iClrUsed) ? 
				    (uint32) (1 << depth) : info_hdr.iClrUsed; 
                        else 
                            clr_tbl_size = 1 << depth; 
                        clr_tbl = (unsigned char *) 
				_TIFFmalloc(n_clr_elems * clr_tbl_size); 
			if (!clr_tbl) { 
				TIFFError(infilename, 
				"Can't allocate space for color table"); 
				goto bad; 
			} 
 
			lseek(fd, BFH_SIZE + info_hdr.iSize, SEEK_SET); 
                        read(fd, clr_tbl, n_clr_elems * clr_tbl_size); 
 
			red_tbl = (unsigned short*) 
				_TIFFmalloc(1< 0) 
                                offset = file_hdr.iOffBits + (length - row - 1) * size; 
                        else 
                                offset = file_hdr.iOffBits + row * size; 
                        if (lseek(fd, offset, SEEK_SET) == (off_t)-1) { 
				TIFFError(infilename, 
					  "scanline %lu: Seek error", 
					  (unsigned long) row); 
				break; 
                        } 
 
			if (read(fd, scanbuf, size) < 0) { 
				TIFFError(infilename, 
					  "scanline %lu: Read error", 
					  (unsigned long) row); 
				break; 
                        } 
 
                        rearrangePixels(scanbuf, width, info_hdr.iBitCount); 
 
                        if (TIFFWriteScanline(out, scanbuf, row, 0) < 0) { 
				TIFFError(infilename, 
					  "scanline %lu: Write error", 
					  (unsigned long) row); 
				break; 
                        } 
                } 
 
                _TIFFfree(scanbuf); 
 
/* -------------------------------------------------------------------- */ 
/*  Read compressed image data.                                         */ 
/* -------------------------------------------------------------------- */ 
 
        } else if ( info_hdr.iCompression == BMPC_RLE8 
		    || info_hdr.iCompression == BMPC_RLE4 ) { 
		uint32		i, j, k, runlength; 
		uint32		compr_size, uncompr_size; 
		unsigned char   *comprbuf; 
		unsigned char   *uncomprbuf; 
 
		compr_size = file_hdr.iSize - file_hdr.iOffBits; 
		uncompr_size = width * length; 
		comprbuf = (unsigned char *) _TIFFmalloc( compr_size ); 
		if (!comprbuf) { 
			TIFFError(infilename, 
			"Can't allocate space for compressed scanline buffer"); 
			goto bad3; 
		} 
		uncomprbuf = (unsigned char *) _TIFFmalloc( uncompr_size ); 
		if (!uncomprbuf) { 
			TIFFError(infilename, 
		"Can't allocate space for uncompressed scanline buffer"); 
			goto bad3; 
		} 
 
		lseek(fd, file_hdr.iOffBits, SEEK_SET); 
		read(fd, comprbuf, compr_size); 
		i = 0; 
		j = 0; 
		if (info_hdr.iBitCount == 8) {		    /* RLE8 */ 
		    while( j < uncompr_size && i < compr_size ) { 
			if ( comprbuf[i] ) { 
			    runlength = comprbuf[i++]; 
			    while( runlength > 0 
				   && j < uncompr_size 
				   && i < compr_size ) { 
				uncomprbuf[j++] = comprbuf[i]; 
				runlength--; 
			    } 
			    i++; 
			} else { 
			    i++; 
			    if ( comprbuf[i] == 0 )         /* Next scanline */ 
				i++; 
			    else if ( comprbuf[i] == 1 )    /* End of image */ 
				break; 
			    else if ( comprbuf[i] == 2 ) {  /* Move to... */ 
				i++; 
				if ( i < compr_size - 1 ) { 
				    j += comprbuf[i] + comprbuf[i+1] * width; 
				    i += 2; 
				} 
				else 
				    break; 
			    } else {                         /* Absolute mode */ 
				runlength = comprbuf[i++]; 
				for ( k = 0; k < runlength && j < uncompr_size && i < compr_size; k++ ) 
				    uncomprbuf[j++] = comprbuf[i++]; 
				if ( k & 0x01 ) 
				    i++; 
			    } 
			} 
		    } 
		} 
		else {					    /* RLE4 */ 
		    while( j < uncompr_size && i < compr_size ) { 
			if ( comprbuf[i] ) { 
			    runlength = comprbuf[i++]; 
			    while( runlength > 0 && j < uncompr_size && i < compr_size ) { 
				if ( runlength & 0x01 ) 
				    uncomprbuf[j++] = (comprbuf[i] & 0xF0) >> 4; 
				else 
				    uncomprbuf[j++] = comprbuf[i] & 0x0F; 
				runlength--; 
			    } 
			    i++; 
			} else { 
			    i++; 
			    if ( comprbuf[i] == 0 )         /* Next scanline */ 
				i++; 
			    else if ( comprbuf[i] == 1 )    /* End of image */ 
				break; 
			    else if ( comprbuf[i] == 2 ) {  /* Move to... */ 
				i++; 
				if ( i < compr_size - 1 ) { 
				    j += comprbuf[i] + comprbuf[i+1] * width; 
				    i += 2; 
				} 
				else 
				    break; 
			    } else {                        /* Absolute mode */ 
				runlength = comprbuf[i++]; 
				for ( k = 0; k < runlength && j < uncompr_size && i < compr_size; k++) { 
				    if ( k & 0x01 ) 
					uncomprbuf[j++] = comprbuf[i++] & 0x0F; 
				    else 
					uncomprbuf[j++] = (comprbuf[i] & 0xF0) >> 4; 
				} 
				if ( k & 0x01 ) 
				    i++; 
			    } 
			} 
		    } 
		} 
 
		_TIFFfree(comprbuf); 
 
		for (row = 0; row < length; row++) { 
                        if (TIFFWriteScanline(out, uncomprbuf + (length - row - 1) * width, row, 0) < 0) { 
				TIFFError(infilename, 
					  "scanline %lu: Write error.\n", 
					  (unsigned long) row); 
                        } 
		} 
 
		_TIFFfree(uncomprbuf); 
	} 
 
bad3: 
	if (blue_tbl) 
		_TIFFfree(blue_tbl); 
bad2: 
	if (green_tbl) 
		_TIFFfree(green_tbl); 
bad1: 
	if (red_tbl) 
		_TIFFfree(red_tbl); 
bad: 
        close(fd); 
 
	if (out) 
		TIFFClose(out); 
        return 0; 
} 
 
/* 
 * Image data in BMP file stored in BGR (or ABGR) format. We should rearrange 
 * pixels to RGB (RGBA) format. 
 */ 
static void 
rearrangePixels(char *buf, uint32 width, uint32 bit_count) 
{ 
	char tmp; 
	uint32 i; 
 
        switch(bit_count) { 
		case 16:    /* FIXME: need a sample file */ 
                        break; 
                case 24: 
			for (i = 0; i < width; i++, buf += 3) { 
				tmp = *buf; 
				*buf = *(buf + 2); 
				*(buf + 2) = tmp; 
			} 
                        break; 
                case 32: 
			{ 
				char	*buf1 = buf; 
 
				for (i = 0; i < width; i++, buf += 4) { 
					tmp = *buf; 
					*buf1++ = *(buf + 2); 
					*buf1++ = *(buf + 1); 
					*buf1++ = tmp; 
				} 
			} 
                        break; 
                default: 
                        break; 
        } 
} 
 
static int 
processCompressOptions(char* opt) 
{ 
	if (strcmp(opt, "none") == 0) 
		compression = COMPRESSION_NONE; 
	else if (strcmp(opt, "packbits") == 0) 
		compression = COMPRESSION_PACKBITS; 
	else if (strncmp(opt, "jpeg", 4) == 0) { 
		char* cp = strchr(opt, ':'); 
		if (cp && isdigit((int)cp[1])) 
			quality = atoi(cp+1); 
		if (cp && strchr(cp, 'r')) 
			jpegcolormode = JPEGCOLORMODE_RAW; 
		compression = COMPRESSION_JPEG; 
	} else if (strncmp(opt, "lzw", 3) == 0) { 
		char* cp = strchr(opt, ':'); 
		if (cp) 
			predictor = atoi(cp+1); 
		compression = COMPRESSION_LZW; 
	} else if (strncmp(opt, "zip", 3) == 0) { 
		char* cp = strchr(opt, ':'); 
		if (cp) 
			predictor = atoi(cp+1); 
		compression = COMPRESSION_DEFLATE; 
	} else 
		return (0); 
	return (1); 
} 
 
static char* stuff[] = { 
"bmp2tiff --- convert Windows BMP files to TIFF", 
"usage: bmp2tiff [options] input.bmp output.tif", 
"where options are:", 
" -r #		make each strip have no more than # rows", 
"", 
" -c lzw[:opts]	compress output with Lempel-Ziv & Welch encoding", 
" -c zip[:opts]	compress output with deflate encoding", 
" -c jpeg[:opts]compress output with JPEG encoding", 
" -c packbits	compress output with packbits encoding", 
" -c none	use no compression algorithm on output", 
"", 
"JPEG options:", 
" #		set compression quality level (0-100, default 75)", 
" r		output color image as RGB rather than YCbCr", 
"For example, -c jpeg:r:50 to get JPEG-encoded RGB data with 50% comp. quality", 
"", 
"LZW and deflate options:", 
" #		set predictor value", 
"For example, -c lzw:2 to get LZW-encoded data with horizontal differencing", 
" -o out.tif	write output to out.tif", 
" -h		this help message", 
NULL 
}; 
 
static void 
usage(void) 
{ 
	char buf[BUFSIZ]; 
	int i; 
 
	setbuf(stderr, buf); 
	for (i = 0; stuff[i] != NULL; i++) 
		fprintf(stderr, "%s\n", stuff[i]); 
	exit(-1); 
} 
 
/* vim: set ts=8 sts=8 sw=8 noet: */