www.pudn.com > cximage599c_full.rar > ximamng.cpp


/* 
 * File:	ximamng.cpp 
 * Purpose:	Platform Independent MNG Image Class Loader and Writer 
 * Author:	07/Aug/2001 Davide Pizzolato - www.xdp.it 
 * CxImage version 5.99c 17/Oct/2004 
 */ 
 
#include "ximamng.h" 
 
#if CXIMAGE_SUPPORT_MNG 
 
//////////////////////////////////////////////////////////////////////////////// 
// callbacks for the mng decoder: 
//////////////////////////////////////////////////////////////////////////////// 
 
//////////////////////////////////////////////////////////////////////////////// 
// memory allocation; data must be zeroed 
static mng_ptr 
mymngalloc( mng_uint32 size ) 
{ 
	return (mng_ptr)calloc(1, size); 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
// memory deallocation 
static void mymngfree(mng_ptr p, mng_uint32 size) 
{ 
	free(p); 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
// Stream open/close: 
// since the user is responsible for opening and closing the file, 
// we leave the default implementation open 
static mng_bool mymngopenstream(mng_handle mng)      { return MNG_TRUE; } 
static mng_bool mymngopenstreamwrite(mng_handle mng) { return MNG_TRUE; } 
static mng_bool mymngclosestream(mng_handle mng)     { return MNG_TRUE; } 
 
//////////////////////////////////////////////////////////////////////////////// 
// feed data to the decoder 
static mng_bool mymngreadstream(mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 *bytesread) 
{ 
	mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); 
	// read the requested amount of data from the file 
	*bytesread = mymng->file->Read( buffer, sizeof(BYTE), size); 
	return MNG_TRUE; 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
static mng_bool mymngwritestream (mng_handle mng, mng_ptr pBuf, mng_uint32 iSize, mng_uint32 *iWritten) 
{ 
	mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); 
	// write it 
	*iWritten = mymng->file->Write (pBuf, 1, iSize); 
	return MNG_TRUE; 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
// the header's been read. set up the display stuff 
static mng_bool mymngprocessheader( mng_handle mng, mng_uint32 width, mng_uint32 height ) 
{ 
	// normally the image buffer is allocated here, 
	// but in this module we don't know nothing about 
	// the final environment. 
 
	mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); 
	 
	mymng->width  = width; 
	mymng->height = height; 
	mymng->bpp    = 24; 
	mymng->effwdt = ((((width * mymng->bpp) + 31) >> 5) << 2); 
 
	if (mng->bUseBKGD){ 
		mymng->nBkgndIndex = 0; 
		mymng->nBkgndColor.rgbRed  = mng->iBGred >> 8; 
		mymng->nBkgndColor.rgbGreen =mng->iBGgreen >> 8; 
		mymng->nBkgndColor.rgbBlue = mng->iBGblue >> 8; 
	} 
 
	mymng->image = (BYTE*)malloc(height * mymng->effwdt); 
 
	// tell the mng decoder about our bit-depth choice 
	mng_set_canvasstyle( mng, MNG_CANVAS_BGR8 ); 
	return MNG_TRUE; 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
// return a row pointer for the decoder to fill 
static mng_ptr mymnggetcanvasline( mng_handle mng, mng_uint32 line ) 
{ 
	mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); 
	return (mng_ptr)(mymng->image + (mymng->effwdt * (mymng->height - 1 - line))); 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
// timer 
static mng_uint32 mymnggetticks(mng_handle mng) 
{ 
#ifdef WIN32 
	return (mng_uint32)GetTickCount(); 
#else 
  return 0; 
#endif 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
// Refresh: actual frame need to be updated (Invalidate) 
static mng_bool mymngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h) 
{ 
//	mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); 
	return MNG_TRUE; 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
// interframe delay callback 
static mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs) 
{ 
	mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); 
	mymng->delay = msecs; 	// set the timer for when the decoder wants to be woken 
	return MNG_TRUE; 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
static mng_bool mymngerror(mng_handle mng, mng_int32 code, mng_int8 severity, mng_chunkid chunktype, mng_uint32 chunkseq, mng_int32 extra1, mng_int32 extra2, mng_pchar text) 
{ 
	//throw (const char *)text; 
	return mng_cleanup(&mng); // 
} 
 
//////////////////////////////////////////////////////////////////////////////// 
// CxImage members 
//////////////////////////////////////////////////////////////////////////////// 
CxImageMNG::CxImageMNG(): CxImage(CXIMAGE_FORMAT_MNG) 
{ 
	hmng = NULL; 
	memset(&mnginfo,0,sizeof(mngstuff)); 
	mnginfo.nBkgndIndex = -1; 
	mnginfo.speed = 1.0f; 
} 
//////////////////////////////////////////////////////////////////////////////// 
CxImageMNG::~CxImageMNG() 
{ 
	// cleanup and return 
	if (mnginfo.thread){ //close the animation thread 
		mnginfo.animation_enabled=0; 
		ResumeThread(mnginfo.thread); 
		WaitForSingleObject(mnginfo.thread,500); 
		CloseHandle(mnginfo.thread); 
	} 
	// free objects 
	if (mnginfo.image) free(mnginfo.image); 
	if (hmng) mng_cleanup(&hmng); //be sure it's not needed any more. (active timers ?) 
} 
//////////////////////////////////////////////////////////////////////////////// 
void CxImageMNG::SetCallbacks(mng_handle mng) 
{ 
	// set the callbacks 
	mng_setcb_errorproc(mng, mymngerror); 
	mng_setcb_openstream(mng, mymngopenstream); 
	mng_setcb_closestream(mng, mymngclosestream); 
	mng_setcb_readdata(mng, mymngreadstream); 
	mng_setcb_processheader(mng, mymngprocessheader); 
	mng_setcb_getcanvasline(mng, mymnggetcanvasline); 
	mng_setcb_refresh(mng, mymngrefresh); 
	mng_setcb_gettickcount(mng, mymnggetticks); 
	mng_setcb_settimer(mng, mymngsettimer); 
	mng_setcb_refresh(mng, mymngrefresh); 
} 
//////////////////////////////////////////////////////////////////////////////// 
// can't use the CxImage implementation because it looses mnginfo 
bool CxImageMNG::Load(const char * imageFileName){ 
		FILE* hFile;	//file handle to read the image 
		if ((hFile=fopen(imageFileName,"rb"))==NULL)  return false; 
		bool bOK = Decode(hFile); 
		fclose(hFile); 
		return bOK; 
} 
//////////////////////////////////////////////////////////////////////////////// 
bool CxImageMNG::Decode(CxFile *hFile) 
{ 
	if (hFile == NULL) return false; 
 
	try { 
		// set up the mng decoder for our stream 
		hmng = mng_initialize(&mnginfo, mymngalloc, mymngfree, MNG_NULL); 
		if (hmng == NULL) throw "could not initialize libmng";			 
 
		// set the file we want to play 
		mnginfo.file = hFile; 
 
		// Set the colorprofile, lcms uses this: 
		mng_set_srgb(hmng, MNG_TRUE ); 
		// Set white as background color: 
		WORD Red,Green,Blue; 
		Red = Green = Blue = (255 << 8) + 255; 
		mng_set_bgcolor(hmng, Red, Green, Blue ); 
		// If PNG Background is available, use it: 
		mng_set_usebkgd(hmng, MNG_TRUE ); 
 
		// No need to store chunks: 
		mng_set_storechunks(hmng, MNG_FALSE); 
		// No need to wait: straight reading 
		mng_set_suspensionmode(hmng, MNG_FALSE); 
 
		SetCallbacks(hmng); 
 
		mng_datap pData = (mng_datap)hmng; 
 
		// read in the image 
		info.nNumFrames=0; 
		mng_readdisplay(hmng); 
 
		// read all 
		int retval=MNG_NOERROR; 
		while(pData->bReading){ 
			retval = mng_display_resume(hmng); 
			info.nNumFrames++; 
		} 
 
		// single frame check: 
		if (retval != MNG_NEEDTIMERWAIT){ 
			info.nNumFrames--; 
		} else { 
			mnginfo.animation=1; 
		} 
 
		if (info.nNumFrames<=0) info.nNumFrames=1; 
 
		if (mnginfo.animation_enabled==0){ 
			// select the frame 
			if (info.nFrame>=0 && info.nFramewidth,OffsetH=mymng->height; 
 
	BYTE *tmpbuffer = new BYTE[ (mymng->effwdt+1) * mymng->height]; 
	if( tmpbuffer == 0 ) return; 
 
	// Write DEFI chunk. 
	mng_putchunk_defi( hMNG, 0, 0, 0, MNG_TRUE, OffsetX, OffsetY, MNG_FALSE, 0, 0, 0, 0 ); 
 		  
	// Write Header: 
	mng_putchunk_ihdr( 
		hMNG,  
		OffsetW, OffsetH,  
		MNG_BITDEPTH_8,  
		MNG_COLORTYPE_RGB,  
		MNG_COMPRESSION_DEFLATE,  
		MNG_FILTER_ADAPTIVE,  
		MNG_INTERLACE_NONE  
	); 
 
	// transfer data, add Filterbyte: 
	for( int Row=0; Row No Filter. 
		tmpbuffer[Row*(mymng->effwdt+1)]=0;  
		// Copy the scanline: (reverse order) 
		memcpy(tmpbuffer+Row*(mymng->effwdt+1)+1,  
			mymng->image+((OffsetH-1-(OffsetY+Row))*(mymng->effwdt))+OffsetX,mymng->effwdt); 
		// swap red and blue components 
		RGBtoBGR(tmpbuffer+Row*(mymng->effwdt+1)+1,mymng->effwdt); 
	}  
 
	// Compress data with ZLib (Deflate): 
	BYTE *dstbuffer = new BYTE[(mymng->effwdt+1)*OffsetH]; 
	if( dstbuffer == 0 ) return; 
	DWORD dstbufferSize=(mymng->effwdt+1)*OffsetH; 
 
	// Compress data: 
	if(Z_OK != compress2((Bytef *)dstbuffer,(ULONG *)&dstbufferSize,(const Bytef*)tmpbuffer, 
						(ULONG) (mymng->effwdt+1)*OffsetH,9 )) return; 
 
	// Write Data into MNG File: 
	mng_putchunk_idat( hMNG, dstbufferSize, (mng_ptr*)dstbuffer); 
	mng_putchunk_iend(hMNG); 
 
	// Free the stuff: 
	delete [] tmpbuffer; 
	delete [] dstbuffer; 
} 
//////////////////////////////////////////////////////////////////////////////// 
long CxImageMNG::Resume() 
{ 
	if (MNG_NEEDTIMERWAIT == mng_display_resume(hmng)){ 
		if (info.pImage==NULL) Create(mnginfo.width,mnginfo.height,mnginfo.bpp, CXIMAGE_FORMAT_MNG); 
		if (IsValid()) memcpy(GetBits(), mnginfo.image, mnginfo.effwdt * mnginfo.height); 
	} else { 
		mnginfo.animation_enabled = 0; 
	} 
	return mnginfo.animation_enabled; 
} 
//////////////////////////////////////////////////////////////////////////////// 
void CxImageMNG::SetSpeed(float speed) 
{ 
	if (speed>10.0) mnginfo.speed = 10.0f; 
	else if (speed<0.1) mnginfo.speed = 0.1f; 
	else mnginfo.speed=speed; 
} 
//////////////////////////////////////////////////////////////////////////////// 
#endif // CXIMAGE_SUPPORT_MNG