www.pudn.com > CXIMAGE_SRC.ZIP > ximatif.cpp


/* 
 * File:	ximatif.cpp 
 * Purpose:	Platform Independent TIFF Image Class Loader and Writer 
 * 07/Aug/2001  
 * CxImage version 5.00 23/Aug/2002 
 */ 
 
#include "ximatif.h" 
 
#if CXIMAGE_SUPPORT_TIF 
 
#include "../tiff/tiffio.h" 
 
#define CVT(x)			(((x) * 255L) / ((1L<<16)-1)) 
#define	SCALE(x)		(((x)*((1L<<16)-1))/255) 
#define CalculateLine(width,bitdepth)	(((width * bitdepth) + 7) / 8) 
#define CalculatePitch(line)	(line + 3 & ~3) 
 
extern "C" TIFF* TIFFOpenEx(CxFile* stream, const char* mode); 
 
//////////////////////////////////////////////////////////////////////////////// 
bool CxImageTIF::Decode(CxFile * hFile) 
{ 
	//Comment this line if you need more information on errors 
	// TIFFSetErrorHandler(NULL);	// 
 
	//Open file and fill the TIFF structure 
	// m_tif = TIFFOpen(imageFileName,"rb"); 
	TIFF* m_tif = TIFFOpenEx(hFile, "rb"); 
 
	uint32 height=0; 
	uint32 width=0; 
	uint16 bitspersample=1; 
	uint16 samplesperpixel=1; 
	uint32 rowsperstrip=(DWORD)-1; 
	uint16 photometric=0; 
	uint16 compression=1; 
	uint16 orientation=ORIENTATION_TOPLEFT; // 
	uint32 x, y; 
	float resolution, offset; 
	BOOL isRGB; 
	BYTE *bits;		//pointer to source data 
	BYTE *bits2;	//pointer to destination data 
 
  try{ 
	//check if it's a tiff file 
	if (!m_tif) 
		throw "Error encountered while opening TIFF file"; 
 
	info.nNumFrames=0; 
	while(TIFFSetDirectory(m_tif,(uint16)info.nNumFrames)) info.nNumFrames++; 
 
	if (!TIFFSetDirectory(m_tif, (uint16)info.nFrame)) 
		throw "Error: page not present in TIFF file";			 
 
	//get image info 
	TIFFGetField(m_tif, TIFFTAG_COMPRESSION, &compression); 
//	if (compression == COMPRESSION_LZW) 
//		throw "LZW compression is no longer supported due to Unisys patent enforcement";			 
 
	TIFFGetField(m_tif, TIFFTAG_IMAGEWIDTH, &width); 
	TIFFGetField(m_tif, TIFFTAG_IMAGELENGTH, &height); 
	TIFFGetField(m_tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); 
	TIFFGetField(m_tif, TIFFTAG_BITSPERSAMPLE, &bitspersample); 
	TIFFGetField(m_tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);    
	TIFFGetField(m_tif, TIFFTAG_PHOTOMETRIC, &photometric); 
	TIFFGetField(m_tif, TIFFTAG_ORIENTATION, &orientation); 
 
	TIFFGetField(m_tif, TIFFTAG_XRESOLUTION, &resolution); 
	SetXDPI((long)resolution); 
	TIFFGetField(m_tif, TIFFTAG_YRESOLUTION, &resolution); 
	SetYDPI((long)resolution); 
	TIFFGetField(m_tif, TIFFTAG_XPOSITION, &offset); 
	info.xOffset = (long)offset; 
	TIFFGetField(m_tif, TIFFTAG_YPOSITION, &offset); 
	info.yOffset = (long)offset; 
 
	head.biWidth  = width; 
	head.biHeight = height; 
	head.biClrUsed=0; 
	info.nBkgndIndex =-1; 
 
	if (rowsperstrip>height){ 
		rowsperstrip=height; 
		TIFFSetField(m_tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip); 
	} 
 
	isRGB = (bitspersample >= 8) && 
		(photometric == PHOTOMETRIC_RGB) || 
		(photometric == PHOTOMETRIC_YCBCR) || 
		(photometric == PHOTOMETRIC_SEPARATED) || 
		(photometric == PHOTOMETRIC_LOGL) || 
		(photometric == PHOTOMETRIC_LOGLUV); 
 
	if (isRGB){ 
		head.biBitCount=24; 
	}else{ 
		if ((photometric==PHOTOMETRIC_MINISBLACK)||(photometric==PHOTOMETRIC_MINISWHITE)){ 
			if	(bitspersample == 1){ 
				head.biBitCount=1;		//B&W image 
				head.biClrUsed =2; 
			} else if (bitspersample == 4) { 
				head.biBitCount=4;		//16 colors gray scale 
				head.biClrUsed =16; 
			} else { 
				head.biBitCount=8;		//gray scale 
				head.biClrUsed =256; 
			} 
		} else if (bitspersample == 4) { 
			head.biBitCount=4;			// 16 colors 
			head.biClrUsed=16; 
		} else { 
			head.biBitCount=8;			//256 colors 
			head.biClrUsed=256; 
		} 
	} 
 
	if (info.nEscape) throw "Cancelled"; //  - cancel decoding 
 
	Create(head.biWidth,head.biHeight,head.biBitCount,CXIMAGE_FORMAT_TIF);	//image creation 
#if CXIMAGE_SUPPORT_ALPHA 
	if (samplesperpixel==4) AlphaCreate();	//add alpha support for 32bpp tiffs 
	if (samplesperpixel==2 && bitspersample==8) AlphaCreate();	//add alpha support for 8bpp + alpha 
#endif //CXIMAGE_SUPPORT_ALPHA 
 
	if (isRGB) { 
		// Read the whole image into one big RGBA buffer using 
		// the traditional TIFFReadRGBAImage() API that we trust. 
		uint32* raster;		// retrieve RGBA image 
		uint32 *row; 
 
		raster = (uint32*)_TIFFmalloc(width * height * sizeof (uint32)); 
		if (raster == NULL) throw "No space for raster buffer"; 
			 
		// Read the image in one chunk into an RGBA array 
		if(!TIFFReadRGBAImage(m_tif, width, height, raster, 1)) { 
				_TIFFfree(raster); 
				throw "Corrupted TIFF file!"; 
		} 
 
		// read the raster lines and save them in the DIB 
		// with RGB mode, we have to change the order of the 3 samples RGB 
		row = &raster[0]; 
		bits2 = info.pImage; 
		for (y = 0; y < height; y++) { 
 
			if (info.nEscape){ //  - cancel decoding 
				_TIFFfree(raster); 
				throw "Cancelled"; 
			} 
 
			bits = bits2; 
			for (x = 0; x < width; x++) { 
				*bits++ = (BYTE)TIFFGetB(row[x]); 
				*bits++ = (BYTE)TIFFGetG(row[x]); 
				*bits++ = (BYTE)TIFFGetR(row[x]); 
#if CXIMAGE_SUPPORT_ALPHA 
				if (samplesperpixel==4) AlphaSet(x,y,(BYTE)TIFFGetA(row[x])); 
#endif //CXIMAGE_SUPPORT_ALPHA 
			} 
			row += width; 
			bits2 += info.dwEffWidth; 
		} 
		_TIFFfree(raster); 
	} else { 
		RGBQUAD *pal; 
		pal=(RGBQUAD*)calloc(256,sizeof(RGBQUAD)); 
		if (pal==NULL) throw "Unable to allocate TIFF palette"; 
 
		// set up the colormap based on photometric	 
		switch(photometric) { 
			case PHOTOMETRIC_MINISBLACK:	// bitmap and greyscale image types 
			case PHOTOMETRIC_MINISWHITE: 
				if (bitspersample == 1) {	// Monochrome image 
					if (photometric == PHOTOMETRIC_MINISBLACK) { 
						pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; 
					} else { 
						pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255; 
					} 
				} else {		// need to build the scale for greyscale images 
					if (photometric == PHOTOMETRIC_MINISBLACK) { 
						for (UINT i=0; i 0) { 
					if (red[n] >= 256 || green[n] >= 256 || blue[n] >= 256) { 
						Palette16Bits=TRUE; 
						break; 
					} 
				} 
 
				// load the palette in the DIB 
				for (int i = (1 << bitspersample) - 1; i >= 0; i--) { 
					if (Palette16Bits) { 
						pal[i].rgbRed =(BYTE) CVT(red[i]); 
						pal[i].rgbGreen = (BYTE) CVT(green[i]); 
						pal[i].rgbBlue = (BYTE) CVT(blue[i]);            
					} else { 
						pal[i].rgbRed = (BYTE) red[i]; 
						pal[i].rgbGreen = (BYTE) green[i]; 
						pal[i].rgbBlue = (BYTE) blue[i];         
					} 
				} 
				break; 
		} 
		SetPalette(pal,head.biClrUsed);	//palette assign 
		free(pal); 
 
		// read the tiff lines and save them in the DIB 
		uint32 nrow; 
		uint32 ys; 
		int line = CalculateLine(width, bitspersample * samplesperpixel); 
		long bitsize= TIFFStripSize(m_tif); 
		//verify bitsize: could be wrong if StripByteCounts is missing. 
		if (bitsize>(long)(head.biSizeImage)) bitsize=head.biSizeImage; 
		bits = (BYTE*)malloc(bitsize); 
 
		for (ys = 0; ys < height; ys += rowsperstrip) { 
 
			if (info.nEscape){ //  - cancel decoding 
				free(bits); 
				throw "Cancelled"; 
			} 
 
			nrow = (ys + rowsperstrip > height ? height - ys : rowsperstrip); 
 
			if (TIFFReadEncodedStrip(m_tif, TIFFComputeStrip(m_tif, ys, 0), bits, nrow * line) == -1) { 
				free(bits); 
				throw "Corrupted TIFF file!"; 
			} 
			for (y = 0; y < nrow; y++) { 
				long offset=(nrow-y-1)*line; 
				if (bitspersample==16) for (DWORD xi=0;xi=(int)width){ 
							yi--; 
							xi=0; 
						} 
					} 
				} 
			} 
			/*if (head.biClrUsed==2){ 
				for (y = 0; y < nrow; y++) { for (x = 0; x < width; x++) { 
					SetPixelIndex(x,y+ys,(bits[y*line+(x>>3)]>>(7-x%8))&0x01); 
			}}}*/ 
		} 
		free(bits); 
 
		switch(orientation){ 
		case ORIENTATION_TOPRIGHT: /* row 0 top, col 0 rhs */ 
			Mirror(); 
			break; 
		case ORIENTATION_BOTRIGHT: /* row 0 bottom, col 0 rhs */ 
			Flip(); 
			Mirror(); 
			break; 
		case ORIENTATION_BOTLEFT: /* row 0 bottom, col 0 lhs */ 
			Flip(); 
			break; 
		case ORIENTATION_LEFTTOP: /* row 0 lhs, col 0 top */ 
			RotateRight(); 
			Mirror(); 
			break; 
		case ORIENTATION_RIGHTTOP: /* row 0 rhs, col 0 top */ 
			RotateLeft(); 
			break; 
		case ORIENTATION_RIGHTBOT: /* row 0 rhs, col 0 bottom */ 
			RotateLeft(); 
			Mirror(); 
			break; 
		case ORIENTATION_LEFTBOT: /* row 0 lhs, col 0 bottom */ 
			RotateRight(); 
			break; 
		} 
 
	} 
  } catch (char *message) { 
	  strncpy(info.szLastError,message,255); 
	  if (m_tif) TIFFClose(m_tif); 
	  return FALSE; 
  } 
	TIFFClose(m_tif); 
	return TRUE; 
} 
//////////////////////////////////////////////////////////////////////////////// 
bool CxImageTIF::Encode(CxFile * hFile, bool bAppend) 
{ 
  try{ 
	if (hFile==NULL) throw "invalid file pointer"; 
 
	if (m_tif2==NULL) m_tif2=TIFFOpenEx(hFile, "w+b"); 
	if (m_tif2==NULL) throw "initialization fail"; 
 
	if (bAppend || m_pages) m_multipage=true; 
	m_pages++; 
 
	if (!EncodeBody(m_tif2,m_multipage,m_pages,m_pages)) throw "Error saving TIFF file"; 
	if (bAppend) { 
		if (!TIFFWriteDirectory(m_tif2)) throw "Error saving TIFF directory"; 
	} 
  } catch (char *message) { 
	  strncpy(info.szLastError,message,255); 
	  if (m_tif2){ 
		  TIFFClose(m_tif2); 
		  m_tif2=NULL; 
		  m_multipage=false; 
		  m_pages=0; 
	  } 
	  return false; 
  } 
	if (!bAppend){ 
		TIFFClose(m_tif2); 
		m_tif2=NULL; 
		m_multipage=false; 
		m_pages=0; 
	} 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
// Thanks to Abe  
bool CxImageTIF::Encode(CxFile * hFile, CxImage ** pImages, int pagecount) 
{ 
	TIFF *m_tif=NULL; 
  try{ 
	if (hFile==NULL) throw "invalid file pointer"; 
	if (pImages==NULL || pagecount==0) throw "multipage TIFF, no images!"; 
 
	m_tif=TIFFOpenEx(hFile, "w+b"); 
	if (m_tif==NULL) throw "initialization fail"; 
 
	CxImageTIF ghost; 
	for (int i=1; i<=pagecount; i++){ 
		if (pImages[i-1]==NULL) throw "Bad image pointer"; 
		ghost.Ghost(pImages[i-1]); 
		if (!ghost.EncodeBody(m_tif,true,i,pagecount)) throw "Error saving TIFF file"; 
		//if (!((CxImageTIF*)pImages[i-1])->EncodeBody(m_tif,true,i,pagecount)) throw "Error saving TIFF file"; 
		if (!TIFFWriteDirectory(m_tif)) throw "Error saving TIFF directory"; 
	} 
  } catch (char *message) { 
	  strncpy(info.szLastError,message,255); 
	  if (m_tif) TIFFClose(m_tif); 
	  return false; 
  } 
	TIFFClose(m_tif); 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
bool CxImageTIF::EncodeBody(TIFF *m_tif, bool multipage, int page, int pagecount) 
{ 
	uint32 height=head.biHeight; 
	uint32 width=head.biWidth; 
	uint16 bitcount=head.biBitCount; 
	uint16 bitspersample; 
	uint16 samplesperpixel; 
	uint16 photometric=0; 
	uint16 compression; 
//	uint16 pitch; 
//	int line; 
	uint32 x, y; 
 
	samplesperpixel = ((bitcount == 24) || (bitcount == 32)) ? (BYTE)3 : (BYTE)1; 
#if CXIMAGE_SUPPORT_ALPHA 
	if (bitcount==24 && HasAlpha()) { bitcount=32; samplesperpixel=4; } 
#endif //CXIMAGE_SUPPORT_ALPHA 
 
	bitspersample = bitcount / samplesperpixel; 
 
	//set the PHOTOMETRIC tag 
	RGBQUAD *rgb = GetPalette(); 
	switch (bitcount) { 
		case 1: 
			if (CompareColors(&rgb[0],&rgb[1])<0) { 
				/*  some viewers do not handle PHOTOMETRIC_MINISBLACK: 
				 * let's transform the image in PHOTOMETRIC_MINISWHITE 
				 */ 
				//invert the colors 
				RGBQUAD tempRGB=GetPaletteColor(0); 
				SetPaletteIndex(0,GetPaletteColor(1)); 
				SetPaletteIndex(1,tempRGB); 
				//invert the pixels 
				BYTE *iSrc=info.pImage; 
				for (unsigned long i=0;irgbRed != x)||(rgb->rgbRed != rgb->rgbGreen)||(rgb->rgbRed != rgb->rgbBlue)){ 
					photometric = PHOTOMETRIC_PALETTE; 
					break; 
				} 
				rgb++; 
			} 
			break; 
		case 24: 
		case 32: 
			photometric = PHOTOMETRIC_RGB;			 
			break; 
	} 
 
#if CXIMAGE_SUPPORT_ALPHA 
	if (HasAlpha() && bitcount==8) samplesperpixel=2; //8bpp + alpha layer 
#endif //CXIMAGE_SUPPORT_ALPHA 
 
//	line = CalculateLine(width, bitspersample * samplesperpixel); 
//	pitch = (uint16)CalculatePitch(line); 
 
	//prepare the palette struct 
	RGBQUAD pal[256]; 
	if (GetPalette()){ 
		BYTE b; 
		memcpy(pal,GetPalette(),GetPaletteSize()); 
		for(WORD a=0;a gives better compression 
 
	// handle metrics 
	TIFFSetField(m_tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); 
	TIFFSetField(m_tif, TIFFTAG_XRESOLUTION, (float)info.xDPI); 
	TIFFSetField(m_tif, TIFFTAG_YRESOLUTION, (float)info.yDPI); 
//	TIFFSetField(m_tif, TIFFTAG_XPOSITION, (float)info.xOffset); 
//	TIFFSetField(m_tif, TIFFTAG_YPOSITION, (float)info.yOffset); 
 
	// multi-paging - Thanks to Abe  
	if (multipage) 
	{ 
		char page_number[20]; 
		sprintf(page_number, "Page %d", page); 
 
		TIFFSetField(m_tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE); 
		TIFFSetField(m_tif, TIFFTAG_PAGENUMBER, page,pagecount); 
		TIFFSetField(m_tif, TIFFTAG_PAGENAME, page_number); 
	} else { 
		TIFFSetField(m_tif, TIFFTAG_SUBFILETYPE, 0); 
	} 
 
	// palettes (image colormaps are automatically scaled to 16-bits) 
	if (photometric == PHOTOMETRIC_PALETTE) { 
		uint16 *r, *g, *b; 
		r = (uint16 *) _TIFFmalloc(sizeof(uint16) * 3 * 256); 
		g = r + 256; 
		b = g + 256; 
 
		for (int i = 255; i >= 0; i--) { 
			b[i] = (uint16)SCALE((uint16)pal[i].rgbRed); 
			g[i] = (uint16)SCALE((uint16)pal[i].rgbGreen); 
			r[i] = (uint16)SCALE((uint16)pal[i].rgbBlue); 
		} 
 
		TIFFSetField(m_tif, TIFFTAG_COLORMAP, r, g, b); 
		_TIFFfree(r); 
	} 
 
	// compression 
	switch(bitcount) { 
		case 1 : 
			compression = COMPRESSION_CCITTFAX4; //COMPRESSION_CCITTFAX3; 
			break; 
		case 8 : 
			compression = COMPRESSION_PACKBITS; 
			break; 
		case 24 : 
		case 32 : 
			compression = COMPRESSION_JPEG;	//COMPRESSION_PACKBITS; 
			break; 
		default : 
			compression = COMPRESSION_NONE; 
			break; 
	} 
	TIFFSetField(m_tif, TIFFTAG_COMPRESSION, compression); 
 
	// read the DIB lines from bottom to top 
	// and save them in the TIF 
	// -------------------------------------	 
	BYTE *bits; 
	switch(bitcount) {				 
		case 1 : 
		case 4 : 
		case 8 : 
		{ 
			if (samplesperpixel==1){ 
				for (y = 0; y < height; y++) { 
					bits= info.pImage + (height - y - 1)*info.dwEffWidth; 
					TIFFWriteScanline(m_tif,bits, y, 0); 
				} 
			} 
#if CXIMAGE_SUPPORT_ALPHA 
			else { //8bpp + alpha layer 
				bits = (BYTE*)malloc(2*width); 
				for (y = 0; y < height; y++) { 
					for (x=0;x