/*****************************************************************************

	Projet	: Pulsar Sample slider AddOn

	Fichier	: slider_addon.cpp
	Partie	: End

	Auteur	: RM
	Date		: 100797
	Format	: tabs==2


*****************************************************************************/

#include "CFilter.h"
#include "GetBitmap.h"
#include <Path.h>

#pragma export on
CFilter * filterInit(uint32 index);
#pragma export reset

//--------------------------------------------------------------------

bool debug=false;

//--------------------------------------------------------------------


//*************************************
class CSliderFilter : public CFilter
//*************************************
{
public:

	CSliderFilter(void) : CFilter()	{ /* nothing */ }
	virtual ~CSliderFilter(void) 			{ /* nothing */ }

	virtual bool load(void);
	virtual bool prepare(void);
	virtual void processFrame8(SFrameInfo &frame);
	virtual void terminate(void);
	virtual void unload();

private:
	thread_id		loader_id;
	sem_id			wait_lock;
	bool				must_quit;

	uchar				*mBits;
	uint32			mBitsLength; 
	BRect 			use_rect;

	static int32 loader_entry(void *data);
	void loader(void);

}; // end of class defs for CSliderFilter


//--------------------------------------------------------------------


//**********************************************
CFilter * filterInit(uint32 index)
//**********************************************
{
	// this add-on only declares ONE filter
	if (index > 0) return NULL;

	CSliderFilter *info = new CSliderFilter;
	if (!info) return NULL;		// memory error, give up

	// returns the instance -- the caller will check that this instance
	// is derived from CFilter but is _not_ a CFilter...
	return info;
} // end of filterInit

//*******************************
bool CSliderFilter::load(void)
//*******************************
{
	sFilter.name = "Img Slider";
	sFilter.author = "R'alf";
	sFilter.info = "Sample code";
	sFilter.majorVersion = 0;
	sFilter.minorVersion = 3;
	sFilter.position = kFilterPositionAny+0.2;
	sFilter.supportedMode = B_COLOR_8_BIT;

	// create semaphores
	wait_lock = create_sem(0, "slider_wait_lock");
	if (wait_lock < B_NO_ERROR) return false;
	must_quit = false;

	use_rect.Set(0,0,0,0);
	mBits = NULL;
	mBitsLength = 0;

	return true;
}  // end of load for CSliderFilter





//***************************************
void CSliderFilter::unload(void)
//***************************************
{
	delete_sem(wait_lock);
}


//***************************************
bool CSliderFilter::prepare(void)
//***************************************
{
	must_quit = false;
	mBitsLength = sPrepare.sxy;
	mBits = new uchar[mBitsLength];
	if (!mBits) return false;
	memset(mBits, 0, mBitsLength);
	
	// launch the thread here
	loader_id = spawn_thread(loader_entry, "slider_loader", B_NORMAL_PRIORITY, (void *)this);
	if(loader_id >= B_NO_ERROR)
	{
		resume_thread(loader_id);
		return true;
	}
	return false;
}



//***************************************
void CSliderFilter::terminate(void)
//***************************************
{
int32 val;

	must_quit = true;
	if (mBits) delete mBits;
	mBits = NULL;
	acquire_sem_etc(wait_lock, 1, B_TIMEOUT, (bigtime_t)0);
	release_sem(wait_lock);
	wait_for_thread(loader_id, &val);
}


//***************************************
int32 CSliderFilter::loader_entry(void *data)
//***************************************
{
CSliderFilter *elthis = (CSliderFilter *)data;
	elthis->loader();
	return 0;
}


//***************************************
void CSliderFilter::loader(void)
//***************************************
{
BDirectory imgdir(sLoad.dirImg);
BBitmap *b8;
status_t err;

	while(!must_quit)
	{
		imgdir.Rewind();
		for(;!must_quit;)
		{
			BEntry entry;

			err = acquire_sem_etc(wait_lock, 1, B_TIMEOUT, (bigtime_t)0.5e6);
			if (must_quit) break;

			err = imgdir.GetNextEntry(&entry);
			if (err < B_NO_ERROR) break;
			
			BPath path;
			err = entry.GetPath(&path);
			if (err < B_NO_ERROR) continue;
			
			if(1)
			{
				BBitmap *bitmap;
				bitmap = GetBitmap(path.Path());
				if (!bitmap) continue;
				if (must_quit)
				{
					delete bitmap;
					break;
				}

				b8 = makeBitmap8Bit(bitmap);
				delete bitmap;
				if (!b8) continue;
				if(must_quit)
				{
					delete b8;
					break;
				}
			}

			BRect r;
			r = b8->Bounds();
			int32 sx,sy,rsx,rsy,xoffset,yoffset;
			rsx = sx = r.Width()+1;
			rsy = sy = r.Height()+1;
			int32 bpr=sx;

			if (sx > 640) sx = 640;
			sx = sx & 0xFFF8;		// only keeps a 8-bytes aligned size
			if (sy > 480) sy = 480;
			xoffset = (rsx-sx)/2;	// take center of image
			yoffset = (rsy-sy)/2;
			uchar *bits;
			bits = (uchar *)b8->Bits();
			bits += xoffset + yoffset*bpr;
			
			uchar *dest = mBits;
			int32 destbpr = sPrepare.bpr;
			
			int32 px=0, py=0;
			if (sx < 640)		// modulo 0 doesn't work :-) ... :-/
				px = (rand() % (640-sx)) & 0xFFF8;	// align on 8 boundary for double-copy
			if (sy < 480)
				py = rand() % (480-sy);
			dest+=py*destbpr + px;

			//printf("bits %p (%d), dest %p (%d), sx %d (%d), sy %d (%d), px %d, py %d, xoff %d, yoff %d\n",
			//		bits, bpr, dest, destbpr, sx, rsx, sy, rsy, px, py, xoffset, yoffset);
			if(1)
			{
				// old copy that works for non-aligned data
				for(long y=sy;y>0; y--)
				{
					memcpy(dest, bits, sx);
					bits+=bpr;
					dest+=destbpr;
				}
			}
			/*else
			{
				memset(dest, (px+py+sx+sy)&0xff, sy*destbpr);
			}*/
			use_rect.Set(px,py,px+sx,py+sy);

			//delete bitmap;
			if (b8) delete b8;

			if (must_quit) break;
			err = acquire_sem_etc(wait_lock, 1, B_TIMEOUT, (bigtime_t)3.5e6);
		}
	}
}


// --



//***************************************
void CSliderFilter::processFrame8(SFrameInfo &frame)
//***************************************
{
	// the assumption here is that :
	// px is a multiple of 8,
	// sx is a multiple of 8,
	// base is double aligned
	// thus double copies can be used

uchar *src;
uchar *base;
long px,py,sx,sy;

	src = mBits;
	if (!src) return;

	px=use_rect.left;
	py=use_rect.top;
	sx=use_rect.Width();
	sy=use_rect.Height();
	if (sx<=0) return;

	base = frame.screen;
	int32 bpr = sPrepare.bpr;
	base += px+py*bpr;
	src += px+py*bpr;
	double *s = (double *)(src-8);		// pour utiliser stfdu des le depart
	double *d = (double *)(base-8);	// pour utiliser lfdzu des le depart
	uint32 offset=(bpr-sx)/8;

	int32 sx2 = sx/8;
	for(; sy>0; sy--)
	{
		for(sx=sx2; sx>=4; sx-=4)
		{
			*(++d) = *(++s);
			*(++d) = *(++s);
			*(++d) = *(++s);
			*(++d) = *(++s);
			}
			if (sx > 0)
			{
				switch(sx)
				{
					case 3:	*(++d) = *(++s);
					case 2:	*(++d) = *(++s);
					case 1:	*(++d) = *(++s);
				}
			}
			d+=offset;
			s+=offset;
	}

}  // end of processFrame


// eoc
