/*
test_pat_C
This frei0r plugin generates cross sections of color spaces
Version 0.1	aug 2010

Copyright (C) 2010  Marko Cebokli    http://lea.hamradio.si/~s57uuu


 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

/***********************************************************
Test patterns: cross sections of color spaces
*************************************************************/

//compile:	gcc -Wall -c -fPIC test_pat_C.c -o test_pat_C.o

//link: gcc -lm -shared -o test_pat_C.so test_pat_C.o

#include <stdlib.h>
#include <assert.h>
#include <math.h>

#include "frei0r.h"



double PI=3.14159265358979;

typedef struct
	{
	float r;
	float g;
	float b;
	float a;
	} float_rgba;

//--------------------------------------------------------------
void draw_rectangle(float_rgba *s, int w, int h, float x, float y, float wr, float hr, float_rgba c)
{
int i,j;
int zx,kx,zy,ky;

zx=x;  if (zx<0) zx=0;
zy=y;  if (zy<0) zy=0;
kx=x+wr;  if (kx>w) kx=w;
ky=y+hr;  if (ky>h) ky=h;
for (i=zy;i<ky;i++)
	for (j=zx;j<kx;j++)
		s[w*i+j]=c;

}

//-------------------------------------------------------
int inside(float_rgba c)
{
if (c.r<0.0) return 0;
if (c.r>1.0) return 0;
if (c.g<0.0) return 0;
if (c.g>1.0) return 0;
if (c.b<0.0) return 0;
if (c.b>1.0) return 0;
return 1;
}

//-----------------------------------------------------------
//os:   0=RG(B)   1=GB(R)   2=BR(G)
//a:	value on third axis
void risi_presek_rgb(float_rgba *s, int w, int h, float x, float y, float wr, float hr, int os, float a)
{
int i,j;
int zx,kx,zy,ky;
float_rgba c;
float d1,d2;

zx=x;  if (zx<0) zx=0;
zy=y;  if (zy<0) zy=0;
kx=x+wr;  if (kx>w) kx=w;
ky=y+hr;  if (ky>h) ky=h;
switch (os)
	{
	case 0:
		c.b=a;
		d1=1.0/hr;
		d2=1.0/wr;
		c.r=0.0;
		for (i=zy;i<ky;i++)
			{
			c.r=c.r+d1;
			c.g=0.0;
			for (j=zx;j<kx;j++)
				{
				c.g=c.g+d2;
				c.a=1.0;
				s[w*i+j]=c;
				}
			}
		break;
	case 1:
		c.r=a;
		d1=1.0/hr;
		d2=1.0/wr;
		c.g=0.0;
		for (i=zy;i<ky;i++)
			{
			c.g=c.g+d1;
			c.b=0.0;
			for (j=zx;j<kx;j++)
				{
				c.b=c.b+d2;
				c.a=1.0;
				s[w*i+j]=c;
				}
			}
		break;
	case 2:
		c.g=a;
		d1=1.0/hr;
		d2=1.0/wr;
		c.b=0.0;
		for (i=zy;i<ky;i++)
			{
			c.b=c.b+d1;
			c.r=0.0;
			for (j=zx;j<kx;j++)
				{
				c.r=c.r+d2;
				c.a=1.0;
				s[w*i+j]=c;
				}
			}
		break;
	default:
		break;
	}

}

//-----------------------------------------------------------
//PAZI!!!!  ali so meje -0.5 do 0.5 za Pr in Pb prave?
//os:   0=Y'Pr(Pb)   1=PrPb(Y)   2=PbY'(Pr)
//a:	value on third axis
void risi_presek_yprpb601(float_rgba *s, int w, int h, float x, float y, float wr, float hr, int os, float a)
{
int i,j;
int zx,kx,zy,ky;
float_rgba c;
float d1,d2,yy,pr,pb;

zx=x;  if (zx<0) zx=0;
zy=y;  if (zy<0) zy=0;
kx=x+wr;  if (kx>w) kx=w;
ky=y+hr;  if (ky>h) ky=h;
switch (os)
	{
	case 0:
		pb=a-0.5;
		d1=1.0/hr;
		d2=1.0/wr;
		yy=0.0;
		for (i=zy;i<ky;i++)
			{
			yy=yy+d1;
			pr=-0.5;
			for (j=zx;j<kx;j++)
				{
				pr=pr+d2;
				c.r=pr+yy;
				c.b=pb+yy;
				c.g=(yy-0.3*c.r-0.1*c.b)/0.6;
				c.a=1.0;
				if (inside(c)==1) s[w*i+j]=c;
				}
			}
		break;
	case 1:
		yy=a;
		d1=1.0/hr;
		d2=1.0/wr;
		pr=-0.5;
		for (i=zy;i<ky;i++)
			{
			pr=pr+d1;
			pb=-0.5;
			for (j=zx;j<kx;j++)
				{
				pb=pb+d2;
				c.r=pr+yy;
				c.b=pb+yy;
				c.g=(yy-0.3*c.r-0.1*c.b)/0.6;
				c.a=1.0;
				if (inside(c)==1) s[w*i+j]=c;
				}
			}
		break;
	case 2:
		pr=a-0.5;
		d1=1.0/hr;
		d2=1.0/wr;
		pb=-0.5;
		for (i=zy;i<ky;i++)
			{
			pb=pb+d1;
			yy=0.0;
			for (j=zx;j<kx;j++)
				{
				yy=yy+d2;
				c.r=pr+yy;
				c.b=pb+yy;
				c.g=(yy-0.3*c.r-0.1*c.b)/0.6;
				c.a=1.0;
				if (inside(c)==1) s[w*i+j]=c;
				}
			}
		break;
	default:
		break;
	}

}

//-----------------------------------------------------------
//PAZI!!!!  ali so meje -1.0 do 1.0 za aa in bb prave?
//os:   0=AB(I)   1=BI(A)   2=IA(B)
//a:	value on third axis
void risi_presek_abi(float_rgba *s, int w, int h, float x, float y, float wr, float hr, int os, float a)
{
int i,j;
int zx,kx,zy,ky;
float_rgba c;
float d1,d2,aa,bb,ii,k3,ik3;

zx=x;  if (zx<0) zx=0;
zy=y;  if (zy<0) zy=0;
kx=x+wr;  if (kx>w) kx=w;
ky=y+hr;  if (ky>h) ky=h;
k3=sqrtf(3.0)/2.0;
ik3=0.5/k3;

switch (os)
	{
	case 0:
		ii=a;
		d1=2.0/hr;
		d2=2.0/wr;
		aa=-1.0;
		for (i=zy;i<ky;i++)
			{
			aa=aa+d1;
			bb=-1.0;
			for (j=zx;j<kx;j++)
				{
				bb=bb+d2;
				c.r=(aa+1.5*ii)*0.666666666;
				c.b=ii-0.333333*aa-ik3*bb;
				c.g=bb+k3*c.b;
				c.a=1.0;
				if (inside(c)==1) s[w*i+j]=c;
				}
			}
		break;
	case 1:
		aa=2.0*a-1.0;
		d1=2.0/hr;
		d2=1.0/wr;
		bb=-1.0;
		for (i=zy;i<ky;i++)
			{
			bb=bb+d1;
			ii=0.0;
			for (j=zx;j<kx;j++)
				{
				ii=ii+d2;
				c.r=(aa+1.5*ii)*0.666666666;
				c.b=ii-0.333333*aa-ik3*bb;
				c.g=bb+k3*c.b;
				c.a=1.0;
				if (inside(c)==1) s[w*i+j]=c;
				}
			}
		break;
	case 2:
		bb=2.0*a-1.0;
		d1=1.0/hr;
		d2=2.0/wr;
		ii=0.0;
		for (i=zy;i<ky;i++)
			{
			ii=ii+d1;
			aa=-1.0;
			for (j=zx;j<kx;j++)
				{
				aa=aa+d2;
				c.r=(aa+1.5*ii)*0.666666666;
				c.b=ii-0.333333*aa-ik3*bb;
				c.g=bb+k3*c.b;
				c.a=1.0;
				if (inside(c)==1) s[w*i+j]=c;
				}
			}
		break;
	default:
		break;
	}

}

//-----------------------------------------------------------
//os:   0=HC(I)   1=CI(H)   2=IH(C)
//a:	value on third axis
void risi_presek_hci(float_rgba *s, int w, int h, float x, float y, float wr, float hr, int os, float a)
{
int i,j;
int zx,kx,zy,ky;
float_rgba c;
float d1,d2,aa,bb,ii,k3,ik3,hh,cc;

zx=x;  if (zx<0) zx=0;
zy=y;  if (zy<0) zy=0;
kx=x+wr;  if (kx>w) kx=w;
ky=y+hr;  if (ky>h) ky=h;
k3=sqrtf(3.0)/2.0;
ik3=0.5/k3;

switch (os)
	{
	case 0:
		ii=a;
		d1=2.0*PI/hr;
		d2=1.0/wr;
		hh=0.0;
		for (i=zy;i<ky;i++)
			{
			hh=hh+d1;
			cc=0.0;
			for (j=zx;j<kx;j++)
				{
				cc=cc+d2;
				aa=cc*cos(hh);
				bb=cc*sin(hh);
				c.r=(aa+1.5*ii)*0.666666666;
				c.b=ii-0.333333*aa-ik3*bb;
				c.g=bb+k3*c.b;
				c.a=1.0;
				if (inside(c)==1) s[w*i+j]=c;
				}
			}
		break;
	case 1:
		hh=a*2.0*PI;
		d1=1.0/hr;
		d2=1.0/wr;
		cc=0.0;
		for (i=zy;i<ky;i++)
			{
			cc=cc+d1;
			ii=0.0;
			for (j=zx;j<kx;j++)
				{
				ii=ii+d2;
				aa=cc*cos(hh);
				bb=cc*sin(hh);
				c.r=(aa+1.5*ii)*0.666666666;
				c.b=ii-0.333333*aa-ik3*bb;
				c.g=bb+k3*c.b;
				c.a=1.0;
				if (inside(c)==1) s[w*i+j]=c;
				}
			}
		break;
	case 2:
		cc=a;
		d1=1.0/hr;
		d2=2.0*PI/wr;
		ii=0.0;
		for (i=zy;i<ky;i++)
			{
			ii=ii+d1;
			hh=0.0;
			for (j=zx;j<kx;j++)
				{
				hh=hh+d2;
				aa=cc*cos(hh);
				bb=cc*sin(hh);
				c.r=(aa+1.5*ii)*0.666666666;
				c.b=ii-0.333333*aa-ik3*bb;
				c.g=bb+k3*c.b;
				c.a=1.0;
				if (inside(c)==1) s[w*i+j]=c;
				}
			}
		break;
	default:
		break;
	}

}

//-----------------------------------------------------
//converts the internal RGB float image into
//Frei0r rgba8888 color
//sets alpha to opaque
void floatrgba2color(float_rgba *sl, uint32_t* outframe, int w , int h)
{
int i;
uint32_t p;

for (i=0;i<w*h;i++)
	{
	p=(uint32_t)(255.0*sl[i].b) & 0xFF;
	p=(p<<8) + ((uint32_t)(255.0*sl[i].g) & 0xFF);
	p=(p<<8) + ((uint32_t)(255.0*sl[i].r) & 0xFF);
	outframe[i] = p + 0xFF000000;   //no transparency
	}

}

//-----------------------------------------------------
//stretch [0...1] to parameter range [min...max] linear
float map_value_forward(double v, float min, float max)
{
return min+(max-min)*v;
}

//-----------------------------------------------------
//collapse from parameter range [min...max] to [0...1] linear
double map_value_backward(float v, float min, float max)
{
return (v-min)/(max-min);
}

//-----------------------------------------------------
//stretch [0...1] to parameter range [min...max] logarithmic
//min and max must be positive!
float map_value_forward_log(double v, float min, float max)
{
float sr,k;

sr=sqrtf(min*max);
k=2.0*log(max/sr);
return sr*expf(k*(v-0.5));
}

//-----------------------------------------------------
//collapse from parameter range [min...max] to [0...1] logarithmic
//min and max must be positive!
double map_value_backward_log(float v, float min, float max)
{
float sr,k;

sr=sqrtf(min*max);
k=2.0*log(max/sr);
return logf(v/sr)/k+0.5;
}

//**************************************************
//obligatory frei0r stuff follows

//------------------------------------------------
//this structure holds an instance of the test_pat_C plugin
typedef struct
{
  unsigned int w;
  unsigned int h;

  int spc;
  int cs;
  float thav;
  int fs;

  float_rgba *sl;

} tp_inst_t;

//----------------------------------------------------
int f0r_init()
{
  return 1;
}

//--------------------------------------------------
void f0r_deinit()
{ /* no initialization required */ }

//--------------------------------------------------
void f0r_get_plugin_info(f0r_plugin_info_t* tp_info)
{
  tp_info->name           = "test_pat_C";
  tp_info->author         = "Marko Cebokli";
  tp_info->plugin_type    = F0R_PLUGIN_TYPE_SOURCE;
//  tp_info->plugin_type    = F0R_PLUGIN_TYPE_FILTER;
  tp_info->color_model    = F0R_COLOR_MODEL_RGBA8888;
  tp_info->frei0r_version = FREI0R_MAJOR_VERSION;
  tp_info->major_version  = 0;
  tp_info->minor_version  = 1;
  tp_info->num_params     = 4;
  tp_info->explanation    = "Generates cross sections of color spaces";
}

//--------------------------------------------------
void f0r_get_param_info(f0r_param_info_t* info, int param_index)
{
  switch (param_index)
    {
    case 0:
      info->name        = "Color space";
      info->type        = F0R_PARAM_DOUBLE;
      info->explanation = "";
      break;
    case 1:
      info->name	="Cross section";
      info->type	= F0R_PARAM_DOUBLE;
      info->explanation = "";
      break;
    case 2:
      info->name	= "Third axis value";
      info->type	= F0R_PARAM_DOUBLE;
      info->explanation = "";
      break;
    case 3:
      info->name	= "Fullscreen";
      info->type	= F0R_PARAM_BOOL;
      info->explanation = "";
      break;
    }
}

//--------------------------------------------------
f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
{
  int x0,y0,velx,vely;
  float_rgba c;
  tp_inst_t* inst = calloc(1, sizeof(*inst));
  inst->w  = width; 
  inst->h = height;

  inst->spc=0;
  inst->cs=0;
  inst->thav=0.5;
  inst->fs=0;

  inst->sl=(float_rgba*)calloc(width*height,sizeof(float_rgba));

  x0=(inst->w-3*inst->h/4)/2;
  y0=inst->h/8;
  velx=3*inst->h/4;
  vely=3*inst->h/4;
  c.r=0.5;c.g=0.5;c.b=0.5;c.a=1.0;	//gray background
  draw_rectangle(inst->sl, inst->w, inst->h, 0.0, 0.0, (float)inst->w, (float)inst->h, c);
  c.r=0.4;c.g=0.4;c.b=0.4;c.a=1.0;	//darker gray background
  draw_rectangle(inst->sl, inst->w, inst->h, x0, y0,  velx, vely, c);
  risi_presek_rgb(inst->sl, inst->w, inst->h, x0, y0,  velx, vely, inst->cs, inst->thav);

  return (f0r_instance_t)inst;
}

//--------------------------------------------------
void f0r_destruct(f0r_instance_t instance)
{
  tp_inst_t* inst = (tp_inst_t*)instance;

  free(inst->sl);
  free(inst);
}

//--------------------------------------------------
void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
  tp_inst_t* inst = (tp_inst_t*)instance;
  f0r_param_double* p = (f0r_param_double*) param;
  int chg,tmpi,x0,y0,velx,vely;
  float tmpf;
  float_rgba c;

  chg=0;
  switch (param_index)
    {
    case 0:	//color space
      tmpf=*((double*)p);
      if (tmpf>=1.0)
        tmpi=(int)tmpf;
      else
        tmpi = map_value_forward(tmpf, 0.0, 3.9999);
      if ((tmpi<0)||(tmpi>3.0)) break;
      if (inst->spc != tmpi) chg=1;
      inst->spc = tmpi;
      break;
    case 1:	//cross section
      tmpf=*((double*)p);
      if (tmpf>=1.0)
        tmpi=(int)tmpf;
      else
        tmpi = map_value_forward(tmpf, 0.0, 2.9999);
      if ((tmpi<0)||(tmpi>2.0)) break;
      if (inst->cs != tmpi) chg=1;
      inst->cs = tmpi;
      break;
    case 2:	//third axis value
      tmpf = map_value_forward(*((double*)p), 0.0, 1.0);
      if (inst->thav != tmpf) chg=1;
      inst->thav = tmpf;
      break;
    case 3:	//fullscreen   (BOOL)
      tmpi = map_value_forward(*((double*)p), 0.0, 1.0);
      if (inst->fs != tmpi) chg=1;
      inst->fs = tmpi;
      break;
    }

  if (chg==0) return;

  if (inst->fs==0)
    {
    x0=(inst->w-3*inst->h/4)/2;
    y0=inst->h/8;
    velx=3*inst->h/4;
    vely=3*inst->h/4;
    }
  else
    {
    x0=0;
    y0=0;
    velx=inst->w;
    vely=inst->h;
    }

    c.r=0.5;c.g=0.5;c.b=0.5;c.a=1.0;	//gray background
    draw_rectangle(inst->sl, inst->w, inst->h, 0.0, 0.0, (float)inst->w, (float)inst->h, c);
    c.r=0.4;c.g=0.4;c.b=0.4;c.a=1.0;	//darker gray background
    draw_rectangle(inst->sl, inst->w, inst->h, x0, y0,  velx, vely, c);

switch (inst->spc)
	{
	case 0:
		risi_presek_rgb(inst->sl, inst->w, inst->h, x0, y0,  velx, vely, inst->cs, inst->thav);
		break;
	case 1:
		risi_presek_yprpb601(inst->sl, inst->w, inst->h,  x0, y0,  velx, vely, inst->cs, inst->thav);
		break;
	case 2:
		risi_presek_abi(inst->sl, inst->w, inst->h,  x0, y0,  velx, vely, inst->cs, inst->thav);
		break;
	case 3:
		risi_presek_hci(inst->sl, inst->w, inst->h,  x0, y0,  velx, vely, inst->cs, inst->thav);
		break;
	default:
		break;
	}

}

//-------------------------------------------------
void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
  tp_inst_t* inst = (tp_inst_t*)instance;

  f0r_param_double* p = (f0r_param_double*) param;

  switch (param_index)
    {
    case 0:	//color space
      *p = map_value_backward(inst->spc, 0.0, 3.9999);
      break;
    case 1:	//cross section
      *p = map_value_backward(inst->cs, 0.0, 2.9999);
      break;
    case 2:	//third axis value
      *p = map_value_backward(inst->thav, 0.0, 1.0);
      break;
    case 3:	//fullscreen (BOOL)
      *p = map_value_backward_log(inst->fs, 0.0, 1.0);
      break;
    }
}

//---------------------------------------------------
void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)
{

  assert(instance);
  tp_inst_t* inst = (tp_inst_t*)instance;

  floatrgba2color(inst->sl, outframe, inst->w , inst->h);

}
