/*
pr0file.c

This frei0r plugin ia an "2D video oscilloscope"
Version 0.1	jun 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 pr0file.c -o pr0file.o
//link: gcc -shared -o pr0file.so pr0file.o

#include <frei0r.h>

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

#include "font2.h"
#include "measure.h"

double PI=3.14159265358979;

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

}

//---------------------------------------------------------------
//d=dim value   0.0=black   1.0=no dimming
void darken_rectangle(float_rgba *s, int w, int h, float x, float y, float wr, float hr, float d)
{
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].r=d*s[w*i+j].r;
		s[w*i+j].g=d*s[w*i+j].g;
		s[w*i+j].b=d*s[w*i+j].b;
		}
}

//--------------------------------------------------------
//uses a 8x16 font from a .xbm image 32 char wide x 3 char high
void draw_char(float_rgba *sl, int w, int h, int x, int y, unsigned char c, float_rgba col)
{
int i,j,z;

if ((c<32)||(c>127)) return;
if (x<0) return;
if ((x+8)>=w) return;
if (y<0) return;
if ((y+16)>=h)return;

z=(c-32)%32+((c-32)/32)*512;	//position in font image

for (i=0;i<16;i++)
	for (j=0;j<8;j++)
		if ((font2_bits[z+32*i]&(1<<j))!=0)
			sl[(i+y)*w+j+x]=col;

}

//-----------------------------------------------------------
void draw_string(float_rgba *sl, int w, int h, int x, int y, char *c, float_rgba col)
{
int i;

i=0;
while (c[i]!=0)
	{
	draw_char(sl,w,h,x+8*i,y,c[i],col);
	i++;
	}
}

//--------------------------------------------------------
//generates format string for 6 chars wide number, right
//justified
//p=0 one decimal place    p=1 three decimal places
//m=1 always show sign
inline static void forstr(float a, int p, int m, char *s)
{
float b;
char *p3=" %5.3f";
char *p3m="%+5.3f";
char *p1=" %5.1f";
char *p1m1=" %+4.1f";
char *p1m2="  %+3.1f";
char *ss;

b=fabsf(a);

if (p==1)
	{
	if (m==0) ss=p3; else ss=p3m;
	}
else
	{
	if (m==0)
		ss=p1;
	else
		{
		if (b<10.0) ss=p1m2;
		if ((b>=10.0)&&(b<100.0)) ss=p1m1;
		ss=p3m;
		}
	}
sprintf(s,"%s",ss);
}

//-------------------------------------------------------------
//draws a simple line (no antialiasing)
//xz,yz=start point
//xk,yk=end point
void draw_line(float_rgba *s, int w, int h, int xz, int yz, int xk, int yk, float_rgba c)
{
int x,y,d,i;

d =  (abs(xk-xz)>abs(yk-yz)) ? abs(xk-xz) : abs(yk-yz);
if (d==0) return;
for (i=0;i<d;i++)
  {
  x=xz+(float)i/d*(xk-xz);
  y=yz+(float)i/d*(yk-yz);
  if((x>=0)&&(x<w)&&(y>=0)&&(y<h))
    s[y*w+x]=c;
  }
}

//-------------------------------------------------------------
//mark position of the profile on the image
void pmarker(float_rgba *s, int w, int h, int xz, int yz, int xk, int yk, int sir, float_rgba c, float m1, float m2)
{
float dx,dy,dd;
float s2,s3,xm,ym;

dx=(float)(xk-xz);
dy=(float)(yk-yz);
dd=sqrtf(dx*dx+dy*dy);
if (dd==0.0) return;
dx=dx/dd;
dy=dy/dd;

s2=1.415;
s3=10.0;

//lower line
draw_line(s, w, h, xz-s2*dy, yz+s2*dx, xk-s2*dy, yk+s2*dx, c);
//upper line
draw_line(s, w, h, xz+s2*dy, yz-s2*dx, xk+s2*dy, yk-s2*dx, c);

//left end mark
draw_line(s, w, h, xz-s3*dy, yz+s3*dx, xz+s3*dy, yz-s3*dx, c);
//right end mark
draw_line(s, w, h, xk+s3*dy, yk-s3*dx, xk-s3*dy, yk+s3*dx, c);

//marker ticks
s2=2.5;
if (m1>0.0)
  {
  xm=xz+dd*dx*m1;
  ym=yz+dd*dy*m1;
  draw_line(s, w, h, xm+s2*dy, ym-s2*dx, xm+s3*dy, ym-s3*dx, c);
  draw_line(s, w, h, xm-s2*dy, ym+s2*dx, xm-s3*dy, ym+s3*dx, c);
  }
if (m2>0.0)
  {
  xm=xz+dd*dx*m2;
  ym=yz+dd*dy*m2;
  draw_line(s, w, h, xm+s2*dy, ym-s2*dx, xm+s3*dy, ym-s3*dx, c);
  draw_line(s, w, h, xm-s2*dy, ym+s2*dx, xm-s3*dy, ym+s3*dx, c);
  }
}

//--------------------------------------------------------
//select one of 8 colors for the crosshair
float_rgba mcolor(int c)
{
float_rgba wh={1.0,1.0,1.0,1.0};
float_rgba ye={1.0,1.0,0.0,1.0};
float_rgba cy={0.0,1.0,1.0,1.0};
float_rgba gr={0.0,1.0,0.0,1.0};
float_rgba mg={1.0,0.0,1.0,1.0};
float_rgba rd={1.0,0.0,0.0,1.0};
float_rgba bl={0.0,0.0,1.0,1.0};
float_rgba bk={0.0,0.0,0.0,1.0};

switch (c)
  {
  case 0: return wh;	//white
  case 1: return ye;	//yellow
  case 2: return cy;	//cyan
  case 3: return gr;	//green
  case 4: return mg;	//magenta
  case 5: return rd;	//red
  case 6: return bl;	//blue
  case 7: return bk;	//black
  default: return bk;	//black
  }
}

//--------------------------------------------------------
//graph p[], p[]+ofs should be between 0.0 and 1.0
void draw_trace(float_rgba *s, int w, int h, int x0, int y0, int vx, int vy, float p[], int n, float ofs, float_rgba c)
{
int i,x,y,xs,ys;

if (n==0) return;
xs=x0; ys=y0+vy*(1.0-p[0]-ofs);
for (i=0;i<n;i++)
  {
  x=x0+(i+1)*vx/n;
  if (x<0) x=0; if (x>=w) x=w-1;
  y=y0+(vy-1)*(1.0-p[i]-ofs)+1;
  if (y<y0) y=y0; if (y>=(y0+vy)) y=y0+vy-1; if (y>=h) y=h-1;
  draw_line(s, w, h, xs, ys, xs, y, c);
  draw_line(s, w, h, xs, y, x, y, c);
  xs=x; ys=y;
  }
}

//-------------------------------------------------------------
//numeric display below the "oscilloscope"
//m=which channel to display (one of seven)
//dit=what data to display (display items flags)
//m1,m2 marker positions as indexes into p.x arrays
//output is written into string *str
void izpis(profdata p, char *str, int m, int u, int m1, int m2, int dit)
{
int i;
char fs[256],frs[16];
float data[8];

for (i=0;i<8;i++) data[i]=0;

switch (m>>24)	//select channel  (r,g,b....)  & copy data
  {
  case 0:	//display nothing
    return;
  case 1:	//display R channel
    data[0]=p.r[m1]; data[1]=p.r[m2]; data[2]=data[1]-data[0];
    data[3]=p.sr.avg; data[4]=p.sr.rms; data[5]=p.sr.min;
    data[6]=p.sr.max;
    break;
  case 2:	//display G channel
    data[0]=p.g[m1]; data[1]=p.g[m2]; data[2]=data[1]-data[0];
    data[3]=p.sg.avg; data[4]=p.sg.rms; data[5]=p.sg.min;
    data[6]=p.sg.max;
    break;
  case 3:	//display B channel
    data[0]=p.b[m1]; data[1]=p.b[m2]; data[2]=data[1]-data[0];
    data[3]=p.sb.avg; data[4]=p.sb.rms; data[5]=p.sb.min;
    data[6]=p.sb.max;
    break;
  case 4:	//display Y channel
    data[0]=p.y[m1]; data[1]=p.y[m2]; data[2]=data[1]-data[0];
    data[3]=p.sy.avg; data[4]=p.sy.rms; data[5]=p.sy.min;
    data[6]=p.sy.max;
    break;
  case 5:	//display Pr channel
    data[0]=p.u[m1]; data[1]=p.u[m2]; data[2]=data[1]-data[0];
    data[3]=p.su.avg; data[4]=p.su.rms; data[5]=p.su.min;
    data[6]=p.su.max;
    break;
  case 6:	//display Pb channel
    data[0]=p.v[m1]; data[1]=p.v[m2]; data[2]=data[1]-data[0];
    data[3]=p.sv.avg; data[4]=p.sv.rms; data[5]=p.sv.min;
    data[6]=p.sv.max;
    break;
  case 7:	//display alpha channel
    data[0]=p.a[m1]; data[1]=p.a[m2]; data[2]=data[1]-data[0];
    data[3]=p.sa.avg; data[4]=p.sa.rms; data[5]=p.sa.min;
    data[6]=p.sa.max;
    break;
  default:
    break;
  }

if (u!=0) for (i=0;i<8;i++) data[i]=data[i]*255.0;

for (i=0;i<256;i++) {fs[i]=0; str[i]=0;}
if ((dit&0x00000001)!=0)	//marker 1 value
  {
  if (m1>0)
    {
    forstr(data[0],1-u,0,frs);
    snprintf(fs,255,"%%s Mk1=%s", frs);
    sprintf(str,fs,str,data[0]);
    }
  else
    sprintf(str,"%s %s",str,"Mk1= -----");
  }
if ((dit&0x00000004)!=0)	//marker 2 value
  {
  if (m2>0)
    {
    forstr(data[1],1-u,0,frs);
    snprintf(fs,255,"%%s Mk2=%s", frs);
    sprintf(str,fs,str,data[1]);
    }
  else
    sprintf(str,"%s %s",str,"Mk2= -----");
  }
if ((dit&0x00000010)!=0)	//difference marker2-marker1
  {
  if ((m2>0)&&(m1>0))
    {
    forstr(data[2],1-u,0,frs);
    snprintf(fs,255,"%%s D=%s", frs);
    sprintf(str,fs,str,data[2]);
    }
  else
    sprintf(str,"%s %s",str,"D= -----");
  }
if ((dit&0x00000020)!=0)	//average of profile
  {
  forstr(data[3],1-u,0,frs);
  snprintf(fs,255,"%%s Avg=%s", frs);
  sprintf(str,fs,str,data[3]);
  }
if ((dit&0x00000040)!=0)	//RMS of profile
  {
  forstr(data[4],1-u,0,frs);
  snprintf(fs,255,"%%s RMS=%s", frs);
  sprintf(str,fs,str,data[4]);
  }
if ((dit&0x00000080)!=0)	//MIN of profile
  {
  forstr(data[5],1-u,0,frs);
  snprintf(fs,255,"%%s Min=%s", frs);
  sprintf(str,fs,str,data[5]);
  }
if ((dit&0x00000100)!=0)	//MAX of profile
  {
  forstr(data[6],1-u,0,frs);
  snprintf(fs,255,"%%s Max=%s", frs);
  sprintf(str,fs,str,data[6]);
  }
}

//--------------------------------------------------------------
//draw info window
//sx,sy=size of probe   (must be odd)
//poz=position of info window	0=left   1=right
//m=measurement channel, trace/numeric display
//u=units    0=0.0-1.0    1=0-255
//as = auto scale
//m1,m2=marker positions
//dit=display items flags
//cc=crosshair color [0...7]
//cm=0 rec 601, cm=1 rec 709
void prof(float_rgba *s, int w, int h, int *poz, int x, int y, float tilt, int len, int sir, int m, int u, int as, int m1, int m2, int dit, int cc, int cm, profdata *p)
{
int x0,y0,vx,vy;
int xz,xk,yz,yk;	//zacetna in koncna tocka
char string[256];
int i,sl;
float_rgba white={1.0,1.0,1.0,1.0};
float_rgba lgray={0.7,0.7,0.7,1.0};
float_rgba gray={0.5,0.5,0.5,1.0};
float_rgba dgray={0.3,0.3,0.3,1.0};
float_rgba red={1.0,0.0,0.0,1.0};
float_rgba dgreen={0.0,0.7,0.0,1.0};
float_rgba lblue={0.3,0.3,1.0,1.0};
float_rgba yellow={0.7,0.7,0.0,1.0};
float_rgba pink={0.8,0.4,0.5,1.0};
float_rgba magenta={0.8,0.0,0.8,1.0};
float_rgba cyan={0.0,0.7,0.8,1.0};

//position and size of info window
if (y<h/2-20) *poz=1;	//bottom
if (y>h/2+20) *poz=0;	//top
x0=h/20;
vx=w*15/16;
vy = h*6/16;
y0 = (*poz==0) ? h/20 : h-h/20-vy;

//end points of profile
xz=x-len/2.0*cosf(tilt);
xk=x+len/2.0*cosf(tilt);
yz=y-len/2.0*sinf(tilt);
yk=y+len/2.0*sinf(tilt);

//measure
meriprof(s, w, h, xz, yz, xk, yk, sir, p);
prof_yuv(p,cm);
prof_stat(p);

//draw crosshair
pmarker(s, w, h, xz, yz, xk, yk, sir, mcolor(cc), (float)m1/p->n, (float)m2/p->n);

//info window background
darken_rectangle(s, w, h, x0, y0, vx, vy, 0.4);

//draw scope
//background
//draw_rectangle(s, w, h, x0+50, y0+5, vx-55, vy-40, black);
//grid
yz=y0+6; yk=y0+vy-36;
for (i=0;i<9;i++)
  {
  xz=x0+49+(i+1)*(vx-55)/10;
  draw_line(s, w, h, xz, yz, xz, yk, dgray);
  }
xz=x0+50;xk=x0+vx-6;
for (i=0;i<3;i++)
  {
  yz=y0+5+(i+1)*(vy-40)/4;
  draw_line(s, w, h, xz, yz, xk, yz, dgray);
  }
//traces
if ((m&0x00000001)!=0)		//R
  draw_trace(s, w, h, x0+50, y0+5, vx-55, vy-40, p->r, p->n, 0.0, red);
if ((m&0x00000002)!=0)		//G
  draw_trace(s, w, h, x0+50, y0+5, vx-55, vy-40, p->g, p->n, 0.0, dgreen);
if ((m&0x00000004)!=0)		//B
  draw_trace(s, w, h, x0+50, y0+5, vx-55, vy-40, p->b, p->n, 0.0, lblue);
if ((m&0x00000008)!=0)		//Y
  draw_trace(s, w, h, x0+50, y0+5, vx-55, vy-40, p->y, p->n, 0.0, lgray);
if ((m&0x00000010)!=0)		//Pr
  draw_trace(s, w, h, x0+50, y0+5, vx-55, vy-40, p->u, p->n, 0.5, magenta);
if ((m&0x00000020)!=0)		//Pb
  draw_trace(s, w, h, x0+50, y0+5, vx-55, vy-40, p->v, p->n, 0.5, cyan);
if ((m&0x00000040)!=0)		//alpha
  draw_trace(s, w, h, x0+50, y0+5, vx-55, vy-40, p->a, p->n, 0.0, gray);
//markers
if ((m1>=0)&&(m1<p->n))
  {
  draw_line(s, w, h, x0+50+(m1+0.5)*(vx-55)/p->n, y0+5, x0+50+(m1+0.5)*(vx-55)/p->n, y0+vy-35, yellow);
  }
if ((m2>=0)&&(m2<p->n))
  {
  draw_line(s, w, h, x0+50+(m2+0.5)*(vx-55)/p->n, y0+5, x0+50+(m2+0.5)*(vx-55)/p->n, y0+vy-35, pink);
  }
//frame
draw_line(s, w, h, x0+49, y0+5, x0+vx-5, y0+5, gray);
draw_line(s, w, h, x0+49, y0+vy-35, x0+vx-5, y0+vy-35, gray);
draw_line(s, w, h, x0+49, y0+5, x0+49, y0+vy-35, gray);
draw_line(s, w, h, x0+vx-5, y0+5, x0+vx-5, y0+vy-35, gray);

//numeric display
izpis(*p,string,m,u,m1,m2,dit);
sl=strlen(string);
if (sl>((vx-55)/8))
  {
  sprintf(string,"<- NOT ENOUGH SPACE ->");
  draw_string(s, w, h, x0+vx/2-88, y0+vy-25, string, white);
  return;
  }
switch (m>>24)	//which channel data under the scope
  {
  case 0:
    break;
  case 1:
    draw_string(s, w, h, x0+20, y0+vy-25, "R", red);
    draw_string(s, w, h, x0+60, y0+vy-25, string, red);
    break;
  case 2:
    draw_string(s, w, h, x0+20, y0+vy-25, "G", dgreen);
    draw_string(s, w, h, x0+60, y0+vy-25, string, dgreen);
    break;
  case 3:
    draw_string(s, w, h, x0+20, y0+vy-25, "B", lblue);
    draw_string(s, w, h, x0+60, y0+vy-25, string, lblue);
    break;
  case 4:
    draw_string(s, w, h, x0+20, y0+vy-25, "Y", lgray);
    draw_string(s, w, h, x0+60, y0+vy-25, string, lgray);
    break;
  case 5:
    draw_string(s, w, h, x0+20, y0+vy-25, "Pr", magenta);
    draw_string(s, w, h, x0+60, y0+vy-25, string, magenta);
    break;
  case 6:
    draw_string(s, w, h, x0+20, y0+vy-25, "Pb", cyan);
    draw_string(s, w, h, x0+60, y0+vy-25, string, cyan);
    break;
  case 7:
    draw_string(s, w, h, x0+20, y0+vy-25, "a", gray);
    draw_string(s, w, h, x0+60, y0+vy-25, string, gray);
    break;
  default:
    break;
  }

}

//-----------------------------------------------------
//converts the internal RGBA float image into
//Frei0r rgba8888 color
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].a) & 0xFF;
	p=(p<<8) + ((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;
	}
}

//-----------------------------------------------------
//converts the Frei0r rgba8888 color image into
//internal float RGBA
void color2floatrgba(const uint32_t* inframe, float_rgba *sl, int w , int h)
{
int i;

for (i=0;i<w*h;i++)
	{
	sl[i].r=((float)(inframe[i] & 0x000000FF))*0.00392157;
	sl[i].g=((float)((inframe[i] & 0x0000FF00)>>8))*0.00392157;
	sl[i].b=((float)((inframe[i] & 0x00FF0000)>>16))*0.00392157;
	sl[i].a=((float)((inframe[i] & 0xFF000000)>>24))*0.00392157;
	}
}

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

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

int x;		//horizontal position
int y;		//vertical position
float tilt;	//tilt of profile
int len;	//length of profile
int chn;	//channel for numeric display
int m1;		//marker 1 position along profile
int m2;		//marker 2 position along profile
int rt;		//show r trace		BOOL
int gt;		//show g trace		BOOL
int bt;		//show b trace		BOOL
int yt;		//show Y' trace		BOOL
int ut;		//show Pr trace		BOOL
int vt;		//show Pb trace		BOOL
int at;		//show alpha trace	BOOL
int davg;	//display average	BOOL
int drms;	//display rms		BOOL
int dmin;	//display minimum	BOOL
int dmax;	//display maximum	BOOL
int un;		//0...255 units		BOOL
int col;	//color, rec 601 or rec 709
int chc;	//crosshair color  [0...7]

int poz;
int mer;	//display channel + trace flags
int dit;	//numeric display items flags

float_rgba *sl;
profdata *p;

} inst;

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

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

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

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

info->name="pr0file";
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=21;
info->explanation="2D video oscilloscope";
}

//--------------------------------------------------
void f0r_get_param_info(f0r_param_info_t* info, int param_index)
{

switch(param_index)
	{
	case 0:
		info->name = "X";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "X position of profile";
		break;
	case 1:
		info->name = "Y";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "Y position of profile";
		break;
	case 2:
		info->name = "Tilt";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "Tilt of profile";
		break;
	case 3:
		info->name = "Length";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "Length of profile";
		break;
	case 4:
		info->name = "Channel";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "Channel to numerically display";
		break;
	case 5:
		info->name = "Marker 1";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "Position of marker 1";
		break;
	case 6:
		info->name = "Marker 2";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "Position of marker 2";
		break;
	case 7:
		info->name = "R trace";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "Show R trace on scope";
		break;
	case 8:
		info->name = "G trace";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "Show G trace on scope";
		break;
	case 9:
		info->name = "B trace";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "Show B trace on scope";
		break;
	case 10:
		info->name = "Y trace";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "Show Y' trace on scope";
		break;
	case 11:
		info->name = "Pr trace";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "Show Pr trace on scope";
		break;
	case 12:
		info->name = "Pb trace";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "Show Pb trace on scope";
		break;
	case 13:
		info->name = "Alpha trace";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "Show Alpha trace on scope";
		break;
	case 14:
		info->name = "Display average";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "e";
		break;
	case 15:
		info->name = "Display RMS";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "";
		break;
	case 16:
		info->name = "Display minimum";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "";
		break;
	case 17:
		info->name = "Display maximum";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "";
		break;
	case 18:
		info->name = "256 scale";
		info->type = F0R_PARAM_BOOL;
		info->explanation = "use 0-255 instead of 0.0-1.0";
		break;
	case 19:
		info->name = "Color";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "rec 601 or rec 709";
		break;
	case 20:
		info->name = "Crosshair color";
		info->type = F0R_PARAM_DOUBLE;
		info->explanation = "Color of the profile marker";
		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->x=width/2;
in->y=height/2;
in->tilt=0.0;
in->len=3*width/4;
in->chn=3;
in->m1=0;
in->m2=0;
in->rt=1;
in->gt=1;
in->bt=1;
in->yt=0;
in->ut=0;
in->vt=0;
in->at=0;
in->davg=1;
in->drms=1;
in->dmin=0;
in->dmax=0;
in->un=0;
in->col=0;
in->chc=0;

in->poz=1;
in->mer=(3<<24)+7;	//Y display + R,G,B traces
in->dit=32+64;		//avg+RMS

in->sl=(float_rgba*)calloc(width*height,sizeof(float_rgba));
in->p=(profdata*)calloc(1,sizeof(profdata));

in->p->n=5;

return (f0r_instance_t)in;
}

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

in=(inst*)instance;

free(in->sl);
free(in->p);
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:		//X
                tmpi=map_value_forward(*((double*)parm), 0.0, p->w);
		if (tmpi != p->x) chg=1;
		p->x=tmpi;
		break;
	case 1:		//Y
                tmpi=map_value_forward(*((double*)parm), 0.0, p->h);
		if (tmpi != p->y) chg=1;
		p->y=tmpi;
		break;
	case 2:		//tilt
                tmpf=map_value_forward(*((double*)parm), -PI/2.0, PI/2.0);
		if (tmpf != p->tilt) chg=1;
		p->tilt=tmpf;
		break;
	case 3:		//length
                tmpi=map_value_forward(*((double*)parm), 20.0, sqrtf(p->w*p->w+p->h*p->h));
		if (tmpi != p->len) chg=1;
		p->len=tmpi;
		break;
	case 4:		//channel
                tmpi=map_value_forward(*((double*)parm), 1.0, 7.9999);
		if (tmpi != p->chn) chg=1;
		p->chn=tmpi;
		break;
	case 5:		//marker 1
                tmpi=map_value_forward(*((double*)parm), -1.0, p->p->n);
		if (tmpi != p->m1) chg=1;
		p->m1=tmpi;
		break;
	case 6:		//marker 2
                tmpi=map_value_forward(*((double*)parm), -1.0, p->p->n);
		if (tmpi != p->m2) chg=1;
		p->m2=tmpi;
		break;
	case 7:		//R trace
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->rt != tmpi) chg=1;
                p->rt=tmpi;
                break;
	case 8:		//G trace
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->gt != tmpi) chg=1;
                p->gt=tmpi;
                break;
	case 9:		//B trace
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->bt != tmpi) chg=1;
                p->bt=tmpi;
                break;
	case 10:	//Y trace
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->yt != tmpi) chg=1;
                p->yt=tmpi;
                break;
	case 11:	//U trace
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->ut != tmpi) chg=1;
                p->ut=tmpi;
                break;
	case 12:	//V trace
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->vt != tmpi) chg=1;
                p->vt=tmpi;
                break;
	case 13:	//alpha trace
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->at != tmpi) chg=1;
                p->at=tmpi;
                break;
	case 14:	//display avg
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->davg != tmpi) chg=1;
                p->davg=tmpi;
                break;
	case 15:	//display RMS
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->drms != tmpi) chg=1;
                p->drms=tmpi;
                break;
	case 16:	//display min
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->dmin != tmpi) chg=1;
                p->dmin=tmpi;
                break;
	case 17:	//display max
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->dmax != tmpi) chg=1;
                p->dmax=tmpi;
                break;
	case 18:	//256 units
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.0); //BOOL!!
                if (p->un != tmpi) chg=1;
                p->un=tmpi;
                break;
	case 19:	//color mode
                tmpi=map_value_forward(*((double*)parm), 0.0, 1.9999);
                if (p->col != tmpi) chg=1;
                p->col=tmpi;
                break;
	case 20:	//Crosshair color
                tmpi=map_value_forward(*((double*)parm), 0.0, 7.9999);
                if (p->chc != tmpi) chg=1;
                p->chc=tmpi;
                break;
	}

if (chg==0) return;

p->mer=p->chn<<24;
p->mer=p->mer+p->rt;
p->mer=p->mer+2*p->gt;
p->mer=p->mer+4*p->bt;
p->mer=p->mer+8*p->yt;
p->mer=p->mer+16*p->ut;
p->mer=p->mer+32*p->vt;
p->mer=p->mer+64*p->at;

p->dit=0;
if (p->m1>=0) p->dit=p->dit+1;
if (p->m2>=0) p->dit=p->dit+4;
if ((p->m1>=0)&&(p->m2>=0)) p->dit=p->dit+16;
p->dit=p->dit+32*p->davg;
p->dit=p->dit+64*p->drms;
p->dit=p->dit+128*p->dmin;
p->dit=p->dit+256*p->dmax;

}

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

p=(inst*)instance;

switch(param_index)
	{
	case 0:
		*((double*)param)=map_value_backward(p->x, 0.0, p->w);
		break;
	case 1:
		*((double*)param)=map_value_backward(p->y, 0.0, p->h);
		break;
	case 2:
		*((double*)param)=map_value_backward(p->tilt, -PI/2.0, PI/2.0);
		break;
	case 3:
		*((double*)param)=map_value_backward(p->len, 20.0, sqrtf(p->w*p->w+p->h*p->h));
		break;
	case 4:
		*((double*)param)=map_value_backward(p->chn, 0.0, 7.9999);
		break;
	case 5:
		*((double*)param)=map_value_backward(p->m1, 0.0, p->p->n);
		break;
	case 6:
		*((double*)param)=map_value_backward(p->m2, 0.0, p->p->n);
		break;
	case 7:
                *((double*)param)=map_value_backward(p->rt, 0.0, 1.0);//BOOL!!
		break;
	case 8:
                *((double*)param)=map_value_backward(p->gt, 0.0, 1.0);//BOOL!!
		break;
	case 9:
                *((double*)param)=map_value_backward(p->bt, 0.0, 1.0);//BOOL!!
		break;
	case 10:
                *((double*)param)=map_value_backward(p->yt, 0.0, 1.0);//BOOL!!
		break;
	case 11:
                *((double*)param)=map_value_backward(p->ut, 0.0, 1.0);//BOOL!!
		break;
	case 12:
                *((double*)param)=map_value_backward(p->vt, 0.0, 1.0);//BOOL!!
		break;
	case 13:
                *((double*)param)=map_value_backward(p->at, 0.0, 1.0);//BOOL!!
		break;
	case 14:
                *((double*)param)=map_value_backward(p->davg, 0.0, 1.0);//BOOL!!
		break;
	case 15:
                *((double*)param)=map_value_backward(p->drms, 0.0, 1.0);//BOOL!!
		break;
	case 16:
                *((double*)param)=map_value_backward(p->dmin, 0.0, 1.0);//BOOL!!
		break;
	case 17:
                *((double*)param)=map_value_backward(p->dmax, 0.0, 1.0);//BOOL!!
		break;
	case 18:
                *((double*)param)=map_value_backward(p->un, 0.0, 1.0);//BOOL!!
		break;
	case 19:
                *((double*)param)=map_value_backward(p->col, 0.0, 1.9999);
		break;
	case 20:
                *((double*)param)=map_value_backward(p->chc, 0.0, 7.9999);
		break;
	}
}

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

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

color2floatrgba(inframe, in->sl, in->w , in->h);

prof(in->sl, in->w, in->h, &in->poz, in->x, in->y, in->tilt, in->len, 1, in->mer, in->un, 0, in->m1, in->m2, in->dit, in->chc, in->col, in->p);

floatrgba2color(in->sl, outframe, in->w , in->h);
}
