////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
// 
//
// Implementation file
//
//    Copyright (c) 2000 Guenter Geiger geiger@epy.co.at
//    For information on usage and redistribution, and for a DISCLAIMER OF ALL
//    WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
//
/////////////////////////////////////////////////////////

#include "pix_fromsig.h"

#ifdef unix
#include <unistd.h>
#include <strings.h>
#endif

#include "Base/GemCache.h"

static inline int powerOfTwo(int value)
{
    int x = 1;
    while(x < value) x <<= 1;
    return(x);
}

CPPEXTERN_NEW_WITH_TWO_ARGS(pix_fromsig,t_float,A_DEFFLOAT,t_float, A_DEFFLOAT)

/////////////////////////////////////////////////////////
//
// pix_fromsig
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
pix_fromsig :: pix_fromsig(t_float& width, t_float& height)

{
     // make sure that there are some characters
     if (width == 0) width = 256;
     if (height == 0) height = 256;
     
     m_pixBlock.image.xsize =(GLint) width;
     m_pixBlock.image.ysize = (GLint) height;
     m_pixBlock.image.csize = 4;
     m_pixBlock.image.format = GL_RGBA;
     m_pixBlock.image.type = GL_UNSIGNED_BYTE;

     m_red = 1.;
     m_blue = 1.;
     m_green = 1.;
     m_alpha = 0.;
     
     m_mode = 0;
     post("width = %d, height = %d",m_pixBlock.image.xsize,m_pixBlock.image.ysize);
     // our data field is twice as long, because we have some sort of 
     // circular buffer
     m_pixsize = m_pixBlock.image.xsize*m_pixBlock.image.ysize* m_pixBlock.image.csize;
     m_datastart = m_pixBlock.image.data = new unsigned char [m_pixsize*2];
     m_current = m_datastart + m_pixsize;

}

/////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////
pix_fromsig :: ~pix_fromsig()
{
    cleanImage();
}

//
/////////////////////////////////////////////////////////
void pix_fromsig :: render(GemState *state)
{
    // if we don't have an image, just return

//     post(" called %d %d",m_pixBlock.newimage,m_pixBlock.image.ysize);
//     post("in = %d", m_pixBlock.image.data[0]);

    state->image = &m_pixBlock;
}

/////////////////////////////////////////////////////////
// postrender
//
/////////////////////////////////////////////////////////
void pix_fromsig :: postrender(GemState *state)
{
    m_pixBlock.newimage = 0;
    state->image = NULL;
}

/////////////////////////////////////////////////////////
// startRendering
//
/////////////////////////////////////////////////////////
void pix_fromsig :: startRendering()
{
    m_pixBlock.newimage = 1;
}

/////////////////////////////////////////////////////////
// cleanImage
//
/////////////////////////////////////////////////////////
void pix_fromsig :: cleanImage()
{
    // release previous data
     delete [] m_pixBlock.image.data;
     m_pixBlock.image.data = NULL;
	
}


/////////////////////////////////////////////////////////
// static member function
//
/////////////////////////////////////////////////////////


t_int* pix_fromsig ::perform(t_int* w)
{
     pix_fromsig *x = GetMyClass((void*)w[1]);
     t_float* in = (t_float*)(w[2]);
     t_int n = (t_int)(w[3]);
     int i;
     unsigned char* data = x->m_current;
     // Scroll it

     if (data == x->m_datastart) {

	  if (x->m_mode== 0) 
	       memcpy(x->m_datastart + x->m_pixsize, x->m_datastart, x->m_pixsize);
	  data = x->m_datastart + x->m_pixsize;
     }
     
     data-= x->m_pixBlock.image.csize*x->m_pixBlock.image.xsize;

     if (n > x->m_pixBlock.image.xsize) n = x->m_pixBlock.image.xsize;
     n *= x->m_pixBlock.image.csize;
     do {
	  data[n] = (unsigned char) (fabs(*in * x->m_red)*256.0);
	  data[n+1] = (unsigned char) (fabs(*in * x->m_green)*256.0);
	  data[n+2] = (unsigned char) (fabs(*in * x->m_blue)*256.0);
	  data[n+3] = (unsigned char) (fabs(*in++ * x->m_alpha)*256.0);
	  n-=x->m_pixBlock.image.csize;
     } while (n);

     if (x->m_mode == 0) x->m_pixBlock.image.data = data;
     else x->m_pixBlock.image.data = x->m_datastart;
     x->m_current = data;
     x->m_pixBlock.newimage = 1;
	  
     return (w+4);
}


void pix_fromsig :: obj_setupCallback(t_class *classPtr)
{
     class_addmethod(classPtr, nullfn, gensym("signal"), A_NULL);
     class_addmethod(classPtr, (t_method)pix_fromsig::dspMessCallback, 
		     gensym("dsp"), A_NULL);
     class_addmethod(classPtr, (t_method)pix_fromsig::colorMessCallback, 
		     gensym("color"), A_DEFFLOAT,A_DEFFLOAT,A_DEFFLOAT,A_DEFFLOAT);
}


void pix_fromsig :: dspMessCallback(void *data,t_signal** sp)
{
     dsp_add(pix_fromsig::perform, 3, data, sp[0]->s_vec, sp[0]->s_n);
}

void pix_fromsig ::colorMessCallback(void *data, t_float r, t_float g, t_float b,t_float a)
{
     pix_fromsig *x = GetMyClass(data);
     x->m_red = r;
     x->m_green = g;
     x->m_blue = b;
     x->m_alpha = a;
//     post ("rgb: %f %f %f",r,g,b);

}
