/*
alphagrad.c

This frei0r plugin fills alpha channel with a gradient
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.

*/


//compile: gcc -c -fPIC -Wall alphagrad.c -o alphagrad.o
//link: gcc -shared -o alphagrad.so alphagrad.o

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



//----------------------------------------
//struktura za instanco efekta
typedef struct
{
int h;
int w;

float poz,wdt,tilt,min,max;
uint32_t *gr8;
int op;

} inst;

//-----------------------------------------------------
//RGBA8888 little endian
void fill_grad(inst *in)
{
int i,j;
float st,ct,po,wd,d,a;

st=sinf(in->tilt);
ct=cosf(in->tilt);
po=(-in->w/2.0+in->poz*in->w)*1.5;
wd=in->wdt*in->w;

if (in->min==in->max)
	{
	for (i=0;i<in->h*in->w;i++)
		in->gr8[i]=(((uint32_t)(in->min*255.0))<<24)&0xFF000000;
	return;
	}

for (i=0;i<in->h;i++)
	for (j=0;j<in->w;j++)
		{
		d=(i-in->h/2)*ct+(j-in->w/2)*st-po;
		if (fabsf(d)>wd/2.0)
			{
			if (d>0.0)
				a=in->min;
			else
				a=in->max;
			}
		else
			{
			if (d>wd/2.0) d=wd/2.0;
			a = in->min+(wd/2.0-d) / wd*(in->max-in->min);
			}
		a=255.0*a;
		in->gr8[i*in->w+j] = (((uint32_t)a)<<24)&0xFF000000;
		}
}

//-----------------------------------------------------
//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);
}

//***********************************************
// OBVEZNE FREI0R FUNKCIJE

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

//------------------------------------------------
void f0r_deinit()
{
}

//-----------------------------------------------
void f0r_get_plugin_info(f0r_plugin_info_t* info)
{

info->name="alphagrad";
info->author="Marko Cebokli";
info->plugin_type=F0R_PLUGIN_TYPE_FILTER;
info->color_model=F0R_COLOR_MODEL_RGBA8888;
info->frei0r_version=FREI0R_MAJOR_VERSION;
info->major_version=0;
info->minor_version=2;
info->num_params=6;
info->explanation="Fills alpha channel with a gradient";
}

//--------------------------------------------------
void f0r_get_param_info(f0r_param_info_t* info, int param_index)
{
switch(param_index)
	{
	case 0:
		info->name = "Position";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "";
		break;
	case 1:
		info->name = "Transition width";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "";
		break;
	case 2:
		info->name = "Tilt";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "";
		break;
	case 3:
		info->name = "Min";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "";
		break;
	case 4:
		info->name = "Max";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "";
		break;
	case 5:
		info->name = "Operation";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "";
		break;
	}
}

//----------------------------------------------
f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
{
inst *in;

in=calloc(1,sizeof(inst));
in->w=width;
in->h=height;

in->poz=0.5;
in->wdt=0.5;
in->tilt=0.0;
in->min=0.0;
in->max=1.0;
in->op=0;

in->gr8 = (uint32_t*)calloc(in->w*in->h, sizeof(uint32_t));

fill_grad(in);

return (f0r_instance_t)in;
}

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

in=(inst*)instance;

free(in->gr8);
free(instance);
}

//-----------------------------------------------------
void f0r_set_param_value(f0r_instance_t instance, f0r_param_t parm, int param_index)
{
inst *p;
double tmpf;
int tmpi,chg;

p=(inst*)instance;

chg=0;
switch(param_index)
	{
	case 0:
                tmpf=*((double*)parm);
		if (tmpf!=p->poz) chg=1;
		p->poz=tmpf;
		break;
	case 1:
                tmpf=*((double*)parm);
		if (tmpf!=p->wdt) chg=1;
		p->wdt=tmpf;
		break;
	case 2:
                tmpf=map_value_forward(*((double*)parm), -3.15, 3.15);
		if (tmpf!=p->tilt) chg=1;
		p->tilt=tmpf;
		break;
	case 3:
                tmpf=*((double*)parm);
		if (tmpf!=p->min) chg=1;
		p->min=tmpf;
		break;
	case 4:
                tmpf=*((double*)parm);
		if (tmpf!=p->max) chg=1;
		p->max=tmpf;
		break;
	case 5:
                tmpi=map_value_forward(*((double*)parm), 0.0, 4.9999);
                if (p->op != tmpi) chg=1;
                p->op=tmpi;
		break;
	}

if (chg==0) return;

fill_grad(p);
}

//--------------------------------------------------
void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
inst *p;
double tmpf;
int tmpi;

p=(inst*)instance;

switch(param_index)
	{
	case 0:
                *((double*)param)=p->poz;
		break;
	case 1:
                *((double*)param)=p->wdt;
		break;
	case 2:
                *((double*)param)=map_value_backward(p->tilt, -3.15, 3.15);
		break;
	case 3:
                *((double*)param)=p->min;
		break;
	case 4:
                *((double*)param)=p->max;
		break;
	case 5:
                *((double*)param)=map_value_backward(p->op, 0.0, 4.9999);
		break;
	}
}

//-------------------------------------------------
//RGBA8888 little endian
void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)
{
inst *in;
int i,j;
float a,d,st,ct;
float po,wd;
uint32_t t;

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

switch (in->op)
	{
	case 0:		//write on clear
		for (i=0;i<in->h*in->w;i++)
			outframe[i] = (inframe[i]&0x00FFFFFF) | in->gr8[i];
		break;
	case 1:		//max
		for (i=0;i<in->h*in->w;i++)
			{
			t=((inframe[i]&0xFF000000)>in->gr8[i]) ? inframe[i]&0xFF000000 : in->gr8[i];
			outframe[i] = (inframe[i]&0x00FFFFFF) | t;
			}
		break;
	case 2:		//min
		for (i=0;i<in->h*in->w;i++)
			{
			t=((inframe[i]&0xFF000000)<in->gr8[i]) ? inframe[i]&0xFF000000 : in->gr8[i];
			outframe[i] = (inframe[i]&0x00FFFFFF) | t;
			}
		break;
	case 3:		//add
		for (i=0;i<in->h*in->w;i++)
			{
			t=((inframe[i]&0xFF000000)>>1)+(in->gr8[i]>>1);
			t = (t>0x7F800000) ? 0xFF000000 : t<<1;
			outframe[i] = (inframe[i]&0x00FFFFFF) | t;
			}
		break;
	case 4:		//subtract
		for (i=0;i<in->h*in->w;i++)
			{
			t= ((inframe[i]&0xFF000000)>in->gr8[i]) ? (inframe[i]&0xFF000000)-in->gr8[i] : 0;
			outframe[i] = (inframe[i]&0x00FFFFFF) | t;
			}
		break;
	default:
		break;
	}
}

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