Click here to Skip to main content
15,894,825 members
Articles / Desktop Programming / MFC

WaterMarker

Rate me:
Please Sign up or sign in to vote.
4.75/5 (17 votes)
5 Apr 2006Zlib5 min read 104.1K   3K   64  
An utility to protect yout pictures with a stamp bitmap.
/*
 * 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); //<Arkadiy Olovyannikov>
}

////////////////////////////////////////////////////////////////////////////////
// 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.nFrame<info.nNumFrames){
				for (int n=0;n<info.nFrame;n++) mng_display_resume(hmng);
			} else throw "Error: frame not present in MNG file";
		}

		if (mnginfo.nBkgndIndex != -1){
			info.nBkgndIndex = mnginfo.nBkgndIndex;
			info.nBkgndColor.rgbRed = mnginfo.nBkgndColor.rgbRed;
			info.nBkgndColor.rgbGreen = mnginfo.nBkgndColor.rgbGreen;
			info.nBkgndColor.rgbBlue = mnginfo.nBkgndColor.rgbBlue;
		}

		//store the newly created image
		if (Create(mnginfo.width,mnginfo.height,mnginfo.bpp, CXIMAGE_FORMAT_MNG)){
			memcpy(GetBits(), mnginfo.image, mnginfo.effwdt * mnginfo.height);
		} else throw "CxImageMNG::Decode cannot create image";


	} catch (char *message) {
		strncpy(info.szLastError,message,255);
		return false;
	}
	return true;
}
////////////////////////////////////////////////////////////////////////////////
bool CxImageMNG::Encode(CxFile *hFile)
{
	if (EncodeSafeCheck(hFile)) return false;

	try {
		if (head.biClrUsed != 0) throw "MNG encoder can save only RGB images";
		// set the file we want to play
		mnginfo.file = hFile;
		mnginfo.bpp = head.biBitCount;
		mnginfo.effwdt = info.dwEffWidth;
		mnginfo.height = head.biHeight;
		mnginfo.width =  head.biWidth;

		mnginfo.image = (BYTE*)malloc(head.biSizeImage);
		if (mnginfo.image == NULL) throw "could not allocate memory for MNG";
		memcpy(mnginfo.image,info.pImage, head.biSizeImage);

		// set up the mng decoder for our stream
		hmng = mng_initialize(&mnginfo, mymngalloc, mymngfree, MNG_NULL);
		if (hmng == NULL) throw "could not initialize libmng";			

		mng_setcb_openstream(hmng, mymngopenstreamwrite );
		mng_setcb_closestream(hmng, mymngclosestream);
		mng_setcb_writedata(hmng, mymngwritestream);

		// Write File:
   		mng_create(hmng);
		// Just a single Frame (save a normal PNG):
		WritePNG(hmng, 0, 1 );
		// Now write file:
		mng_write(hmng);

	} catch (char *message) {
		strncpy(info.szLastError,message,255);
		return false;
	}
	return true;
}
////////////////////////////////////////////////////////////////////////////////
// Writes a single PNG datastream
void CxImageMNG::WritePNG( mng_handle hMNG, int Frame, int FrameCount )
{
	mngstuff *mymng = (mngstuff *)mng_get_userdata(hMNG);
	
	int OffsetX=0,OffsetY=0,OffsetW=mymng->width,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<OffsetH; Row++ ){
		// First Byte in each Scanline is Filterbyte: Currently 0 -> 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

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The zlib/libpng License


Written By
Software Developer
France France
KOCH David, 41 years old
Coder (embedded, C/C++, ASM, Erlang)

Comments and Discussions