/*c0rners.c
four corners geometry engine
* Copyright (C) 2010 Marko Cebokli http://lea.hamradio.si/~s57uuu
* This file is a Frei0r plugin.
*
* 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 -Wall -std=c99 -c -fPIC c0rners.c -o c0rners.o
//link: gcc -shared -lm -o c0rners.so c0rners.o
#include <stdlib.h>
#include <stdio.h>
#include <frei0r.h>
#include <string.h>
#include <math.h>
#include "frei0r/math.h"
#include "interp.h"
//----------------------------------------
//structure for Frei0r instance
typedef struct
{
int h;
int w;
float x1;
float y1;
float x2;
float y2;
float x3;
float y3;
float x4;
float y4;
int stretchON;
float stretchx;
float stretchy;
int intp;
int transb;
float feath;
int op;
interpp interp;
float *map;
unsigned char *amap;
int mapIsDirty;
} inst;
//2D point
typedef struct //tocka v ravnini
{
float x;
float y;
} tocka2;
//2D line
typedef struct //premica v ravnini
{
float a;
float b;
float c;
float sa; //se normalna oblika
float ca;
float p;
} premica2;
// line through two points in the plane (2D)
// calculate a, b, c for the equation of the line: ax + by + c = 0
// ps se sin a, cos a and p for normal form
// return: (return value)
// - 10 if it is not possible to determine p. coincident points
// 0 general p. general case
// + 1 vertical p. x v * a vertical
// + 2 horizontal p. y v * b horizontal
// a line through two points
int premica2d(tocka2 t1, tocka2 t2, premica2 *p)
{
float dx,dy,m;
dx=t2.x-t1.x;
dy=t2.y-t1.y;
if (dx==0.0) //navpicna
{
if (dy==0.0) return -10;
p->a=1.0;
p->b=0.0;
p->c=-t1.x;
m=1.0/p->a; if (p->c>=0) m=-m;
p->sa=m;
p->ca=0.0;
p->p=m*p->c;
return 1;
}
if (dy==0.0) //vodoravna
{
if (dx==0.0) return -10;
p->a=0.0;
p->b=1.0;
p->c=-t1.y;
m=1.0/p->b; if (p->c>=0) m=-m;
p->sa=0.0;
p->ca=m;
p->p=m*p->c;
return 2;
}
//posevna
p->a=1.0/dx;
p->b=-1.0/dy;
p->c=t1.y/dy-t1.x/dx;
m=1.0/sqrtf(p->a*p->a+p->b*p->b); if (p->c>=0) m=-m;
p->sa=m*p->a;
p->ca=m*p->b;
p->p=m*p->c;
return 0;
}
// ------------------------------------------------ -----
// point distance from line (for alpha feather)
// distance between a point and a line
// needed only for alpha feathering
float razd_t_p(tocka2 t, premica2 p)
{
float r;
r = t.x*p.ca + t.y*p.sa + p.p;
return r;
}
// ------------------------------------------------ -----
// intersection of two lines in a plane (2D)
// return:
// 0 if everything is OK
// - 1 if the lines are parallel
// intersection of two lines
int presecisce2(premica2 p1, premica2 p2, tocka2 *t)
{
float d1,d2,d3;
d1=p1.a*p2.b-p2.a*p1.b;
if (d1==0.0) //vzporedni
{
return -1;
}
d2=p1.b*p2.c-p2.b*p1.c;
d3=p1.c*p2.a-p2.c*p1.a;
t->x=d2/d1;
t->y=d3/d1;
return 0;
}
// ------------------------------------------------ ---------------
// generate mapping for a general quadrangle
// wi, hi = input image size
// wo, ho = output image size
// vog [] = the four corners
// str: 0 = no stretch 1 = do stretch
// strx, stry: stretch values ​​[0 ... 1] 0.5 = no stretch
void cetverokotnik4(int wi, int hi, int wo, int ho, tocka2 vog[], int str, float strx, float stry, float *map)
{
double a,b,c,d,e,f,g,h,a2,b2,c2,u,v,aa,bb,de,sde,v1,v2,u1,u2;
tocka2 T;
int x,y;
float kx,ky,k1,k2;
de=0.0;v1=1000.0;v2=1000.0; //da compiler ne jamra
kx=4.0*2.0*fabsl(strx-0.5)+0.00005;
k1=1.0-1.0/(kx+1.0);
ky=4.0*2.0*fabsl(stry-0.5)+0.00005;
k2=1.0-1.0/(ky+1.0);
for (y=0;y<ho;y++)
{
for (x=0;x<wo;x++)
{
T.x=(float)x+0.5;T.y=(float)y+0.5;
//enacba za Xt, prva moznost
a=vog[0].x-T.x; b=vog[1].x-vog[0].x;
c=vog[3].x-vog[0].x;
d=vog[2].x-vog[1].x-(vog[3].x-vog[0].x);
//enacba za Xt, druga moznost
// a=vog[0].x-T.x; b=vog[1].x-vog[0].x;
// c=vog[2].x-vog[0].x;
// d=vog[3].x-vog[2].x-(vog[1].x-vog[0].x);
//enacba za Yt, prva moznost
e=vog[0].y-T.y; f=vog[1].y-vog[0].y;
g=vog[3].y-vog[0].y;
h=vog[2].y-vog[1].y-(vog[3].y-vog[0].y);
//enacba za Yt, druga moznost
// e=vog[0].y-T.y; f=vog[1].y-vog[0].y;
// g=vog[2].y-vog[0].y;
// h=vog[3].y-vog[2].y-(vog[1].y-vog[0].y);
//resitev za v in u
a2=g*d-h*c; b2=e*d-f*c-h*a+g*b; c2=e*b-f*a;
//linearni priblizek uporabim, ce je napaka < 1/10 piksla
// if (fabsl(a2*c2*c2/(b2*b2*b2))< 0.1/wi)
//dodaten pogoj za a2, da ni spranje v konkavnih
if ((fabsl(a2*c2*c2/(b2*b2*b2))< 0.1/wi) && (fabsl(a2)<1.0))
{
v1 = (b2!=0.0) ? -c2/b2 : 1000.0;
v2=1000.0;
}
else
{
de=b2*b2-4.0*a2*c2;
if (de>=0.0)
{
sde=sqrt(de);
v1=(-b2+sde)/2.0/a2;
v2=(-b2-sde)/2.0/a2;
}
else
{
v1=1001.0; //krneki zunaj
v2=1001.0; //krneki zunaj
}
}
aa=b+d*v1; bb=f+h*v1;
if (fabs(aa)>fabs(bb))
u1 = (aa!=0.0) ? -(a+c*v1)/aa : 1000.0;
else
u1 = (bb!=0.0) ? -(e+g*v1)/bb : 1000.0;
aa=b+d*v2; bb=f+h*v2;
if (fabs(aa)>fabs(bb))
u2 = (aa!=0.0) ? -(a+c*v2)/aa : 1000.0;
else
u2 = (bb!=0.0) ? -(e+g*v2)/bb : 1000.0;
if ((u1>0.0)&&(u1<1.0)&&(v1>0.0)&&(v1<1.0))
{
u=u1;
v=v1;
}
else
{
if ((u2>0.0)&&(u2<1.0)&&(v2>0.0)&&(v2<1.0))
{
u=u2;
v=v2;
}
else
{
u=1002.0;
v=1002.0;
}
}
//if requested, apply stretching
if (str!=0)
{
if (strx>0.5)
u=(1.0-1.0/(kx*u+1.0))/k1;
else
u=1.0-(1.0-1.0/(kx*(1.0-u)+1.0))/k1;
if (stry>0.5)
v=(1.0-1.0/(ky*v+1.0))/k2;
else
v=1.0-(1.0-1.0/(ky*(1.0-v)+1.0))/k2;
}
//zdaj samo se vpise izracunana (u,v) v map[]
if ((u>=0.0)&&(u<=1.0)&&(v>=0.0)&&(v<=1.0))
{ //ce smo znotraj orig slike
map[2*(y*wo+x)]=u*(wi-1);
map[2*(y*wo+x)+1]=v*(hi-1);
}
else
{
map[2*(y*wo+x)]=-1;
map[2*(y*wo+x)+1]=-1;
}
}
}
}
//---------------------------------------------------------------
//generate mapping for a triangle
void trikotnik1(int wi, int hi, int wo, int ho, tocka2 vog[], tocka2 R, tocka2 S, premica2 p12, premica2 p23, premica2 p34, premica2 p41, int t12, int t23, int str, float strx, float stry, float *map)
{
int x,y;
tocka2 T,A,B;
premica2 p5,p6;
float u,v;
float kx,ky,k1,k2;
kx=4.0*2.0*fabsl(strx-0.5)+0.00005;
k1=1.0-1.0/(kx+1.0);
ky=4.0*2.0*fabsl(stry-0.5)+0.00005;
k2=1.0-1.0/(ky+1.0);
for (y=0;y<ho;y++)
{
for (x=0;x<wo;x++)
{
T.x=(float)x+0.5;T.y=(float)y+0.5;
premica2d(T,R,&p5);
presecisce2(p5,p12,&A);
if (t12!=-10) //razlicno od cetverokotnika
{ //tocka je med preseciscem in r
if (fabsf(p12.a)>fabsf(p12.b)) //bolj pokonci
u=(A.y-vog[0].y)/(vog[1].y-vog[0].y);
else
u=(A.x-vog[0].x)/(vog[1].x-vog[0].x);
}
else
{
presecisce2(p5,p34,&A);
if (fabsf(p34.a)>fabsf(p34.b)) //bolj pokonci
u=(A.y-vog[3].y)/(vog[2].y-vog[3].y);
else
u=(A.x-vog[3].x)/(vog[2].x-vog[3].x);
}
premica2d(T,S,&p6);
presecisce2(p6,p23,&B);
if (t23!=-10) //razlicno od cetverokotnika
{
if (fabsf(p23.a)>fabsf(p23.b)) //bolj pokonci
v=(B.y-vog[1].y)/(vog[2].y-vog[1].y);
else
v=(B.x-vog[1].x)/(vog[2].x-vog[1].x);
}
else
{
presecisce2(p6,p41,&B);
if (fabsf(p41.a)>fabsf(p41.b)) //bolj pokonci
v=(B.y-vog[0].y)/(vog[3].y-vog[0].y);
else
v=(B.x-vog[0].x)/(vog[3].x-vog[0].x);
}
//if requested, apply stretching
if (str!=0)
{
if (strx>0.5)
u=(1.0-1.0/(kx*u+1.0))/k1;
else
u=1.0-(1.0-1.0/(kx*(1.0-u)+1.0))/k1;
if (stry>0.5)
v=(1.0-1.0/(ky*v+1.0))/k2;
else
v=1.0-(1.0-1.0/(ky*(1.0-v)+1.0))/k2;
}
//zdaj samo se vpise izracunana (u,v) v map[]
if ((u>=0.0)&&(u<=1.0)&&(v>=0.0)&&(v<=1.0))
{ //ce smo znotraj orig slike
map[2*(y*wo+x)]=u*(wi-1);
map[2*(y*wo+x)+1]=v*(hi-1);
}
else
{
map[2*(y*wo+x)]=-1;
map[2*(y*wo+x)+1]=-1;
}
}
}
}
//-------------------------------------------------------
//generates a map of alpha values for transparent background
//with feathered (soft) border
//feath = soft border width 0..max in pixels
//amap = generated alpha map
//map = map generated by geom4c_b()
//nots[] = flags for inner sides
//for now it does not feather caustics on concaves an crossed sides
void make_alphamap(unsigned char *amap, tocka2 vog[], int wo, int ho, float *map, float feath, int nots[])
{
float r12, r23, r34, r41, rmin;
tocka2 t;
int i,j;
premica2 p12,p23,p34,p41;
premica2d(vog[0],vog[1],&p12); // 1-2
premica2d(vog[2],vog[3],&p34); // 3-4
premica2d(vog[3],vog[0],&p41); // 4-1
premica2d(vog[1],vog[2],&p23); // 2-3
for (i=0;i<ho;i++)
for (j=0;j<wo;j++)
{
t.x=(float)i+0.5; t.y=(float)j+0.5;
r12=fabsf(razd_t_p(t,p12));
r23=fabsf(razd_t_p(t,p23));
r34=fabsf(razd_t_p(t,p34));
r41=fabsf(razd_t_p(t,p41));
rmin=1.0E22;
if ((r12<rmin) && (nots[0]!=1)) rmin=r12;
if ((r23<rmin) && (nots[1]!=1)) rmin=r23;
if ((r34<rmin) && (nots[2]!=1)) rmin=r34;
if ((r41<rmin) && (nots[3]!=1)) rmin=r41;
if ((map[2*(i*wo+j)]>=0.0)&&(map[2*(i*wo+j)+1]>=0.0))
{ //inside
if (rmin<=feath) //border area
amap[i*wo+j]=255*(rmin/feath);
else
amap[i*wo+j]=255;
}
else //outside
amap[i*wo+j]=0;
}
}
//-------------------------------------------------------
void apply_alphamap(uint32_t* frame, int w, int h, unsigned char *amap, int operation)
{
int i, length;
uint32_t t;
length = w * h;
switch (operation)
{
case 0: //write on clear
for (i=0;i<length;i++)
{
t=((uint32_t)amap[i])<<24;
frame[i] = (frame[i]&0x00FFFFFF) | t;
}
break;
case 1: //max
for (i=0;i<length;i++)
{
t=((uint32_t)amap[i])<<24;
frame[i] = (frame[i]&0x00FFFFFF) | MAX(frame[i]&0xFF000000, t);
}
break;
case 2: //min
for (i=0;i<length;i++)
{
t=((uint32_t)amap[i])<<24;
frame[i] = (frame[i]&0x00FFFFFF) | MIN(frame[i]&0xFF000000, t);
}
break;
case 3: //add
for (i=0;i<length;i++)
{
t=((uint32_t)amap[i])<<24;
t=((frame[i]&0xFF000000)>>1)+(t>>1);
t = (t>0x7F800000) ? 0xFF000000 : t<<1;
frame[i] = (frame[i]&0x00FFFFFF) | t;
}
break;
case 4: //subtract
for (i=0;i<length;i++)
{
t=((uint32_t)amap[i])<<24;
t= ((frame[i]&0xFF000000)>t) ? (frame[i]&0xFF000000)-t : 0;
frame[i] = (frame[i]&0x00FFFFFF) | t;
}
break;
default:
break;
}
}
// ------------------------------------------------ ---------------
// function for byte fields (char)
// generate map from the four corners
// first checks for different types of degenerate geometrty ...
// wi, hi input image size
// wo, ho output image size
// vog [] the four corners
// nots [] "inner" sides (for alpha feathering)
int geom4c_b(int wi, int hi, int wo, int ho, tocka2 vog[], int str, float strx, float stry, float *map, int nots[])
{
premica2 p12,p23,p34,p41;
tocka2 R,S;
int r41,r23,s12,s34; //tocki R in S
int p1,p2; //paralelnost stranic
int t12,t23,t34,t41; //sovpadanje tock
int tip; //1=degen trik 2=paral 3=splosni 4=twist 5=konkavni
int i;
for (i=0;i<4;i++) //convert indexes to positions (pixel)
{
vog[i].x=vog[i].x+0.5;
vog[i].y=vog[i].y+0.5;
}
tip=3;
t12=premica2d(vog[0],vog[1],&p12); // 1-2
t34=premica2d(vog[2],vog[3],&p34); // 3-4
t41=premica2d(vog[3],vog[0],&p41); // 4-1
t23=premica2d(vog[1],vog[2],&p23); // 2-3
//preveri degeneracijo v crto ali piko
//check for degeneration into a line or point
if ((t12+t34+t41+t23)<-19)
{ //vec kot dve sovpadata
//daj tu fill with background??
return 0;
}
if (((vog[0].x==vog[2].x)&&(vog[0].y==vog[2].y)) || ((vog[1].x==vog[3].x)&&(vog[1].y==vog[3].y)))
{ //sovpadata dve diagonalni tocki
//daj tu fill with background??
return 0;
}
p1=presecisce2(p12,p34,&S); //tocka S
p2=presecisce2(p41,p23,&R); //tocka R
//preveri degeneracijo v trikotnik (sovpadanje nediagonalnih tock)
//check for degeneration into triangle (coincident non-diagonal c.)
if (t12==-10) {R=vog[0];p12=p34;p1=-1;tip=1;}
if (t34==-10) {R=vog[2];p34=p12;p1=-1;tip=1;}
if (t41==-10) {S=vog[0];p41=p23;p2=-1;tip=1;}
if (t23==-10) {S=vog[2];p23=p41;p2=-1;tip=1;}
//preveri vzporednost
//check parallelity
if (p1==-1) //vzporedni 1-2 in 3-4
{
if (fabsf(p12.a)>fabsf(p12.b)) //bolj pokonci
{S.y=1.0E9;S.x=-(p12.b*S.y+p12.c)/p12.a;}
else
{S.x=1.0E9;S.y=-(p12.a*S.x+p12.c)/p12.b;}
}
if (p2==-1) //vzporedni 2-3 in 4-1
{
if (fabsf(p41.a)>fabsf(p41.b)) //bolj pokonci
{R.y=1.0E9;R.x=-(p41.b*R.y+p41.c)/p41.a;}
else
{R.x=1.0E9;R.y=-(p41.a*R.x+p41.c)/p41.b;}
}
//pogleda, ce je priblizno paralelogram
//check if approximately parallelogram
if (((fabsf(R.x)>1000000.0)||(fabsf(R.y)>1000000.0)) &&
((fabsf(S.x)>1000000.0)||(fabsf(S.y)>1000000.0)))
tip=2;
//preveri, ce je prekrizan ali konkaven (R ali S med ogljici)
//check for concave or crossed sides
r41=0;r23=0;s12=0;s34=0;
if (fabsf(p41.a)>fabsf(p41.b)) //bolj pokonci
{if (((R.y-vog[3].y)*(R.y-vog[0].y))<0.0) r41=1;}//R na 4-1
else
{if (((R.x-vog[3].x)*(R.x-vog[0].x))<0.0) r41=1;}//R na 4-1
if (fabsf(p23.a)>fabsf(p23.b)) //bolj pokonci
{if (((R.y-vog[1].y)*(R.y-vog[2].y))<0.0) r23=1;}//R na 2-3
else
{if (((R.x-vog[1].x)*(R.x-vog[2].x))<0.0) r23=1;}//R na 2-3
if (fabsf(p12.a)>fabsf(p12.b)) //bolj pokonci
{if (((S.y-vog[0].y)*(S.y-vog[1].y))<0.0) s12=1;}//S na 1-2
else
{if (((S.x-vog[0].x)*(S.x-vog[1].x))<0.0) s12=1;}//S na 1-2
if (fabsf(p34.a)>fabsf(p34.b)) //bolj pokonci
{if (((S.y-vog[2].y)*(S.y-vog[3].y))<0.0) s34=1;}//S na 3-4
else
{if (((S.x-vog[2].x)*(S.x-vog[3].x))<0.0) s34=1;}//S na 3-4
if (((r41+r23+s12+s34)>0)&&(tip==3))
{
if ((r41*r23+s12*s34)==0) //konkaven
tip=5;
else //prekrizan
tip=4;
}
//prepare nots[] flags
nots[0]=nots[1]=nots[2]=nots[3]=0;
if (tip==4)
{
nots[0] = (s12==0) ? 0 : 1;
nots[1] = (r23==0) ? 0 : 1;
nots[2] = (s34==0) ? 0 : 1;
nots[3] = (r41==0) ? 0 : 1;
}
if (tip==5)
{
nots[2] = (s12==0) ? 0 : 1;
nots[3] = (r23==0) ? 0 : 1;
nots[0] = (s34==0) ? 0 : 1;
nots[1] = (r41==0) ? 0 : 1;
}
//OK, zdaj gremo risat...
switch (tip)
{
case 0: //should never come to here...
break;
case 1: //triangle
trikotnik1(wi, hi, wo, ho, vog, R, S, p12, p23, p34, p41, t12, t23, str, strx, stry, map);
break;
case 2: //paralelogram
//a faster algorithm could be used here...
case 3: //general quadrangle
case 4: //crossed sides
case 5: //concave quadrangle
cetverokotnik4(wi, hi, wo, ho, vog, str, strx, stry, map);
break;
}
return 0;
}
//-------------------------------------------------------
interpp set_intp(inst p)
{
switch (p.intp) //katero interpolacijo bo uporabil
{
// case -1:return interpNNpr_b; //nearest neighbor+print
case 0: return interpNN_b32; //nearest neighbor
case 1: return interpBL_b32; //bilinear
case 2: return interpBC_b32; //bicubic smooth
case 3: return interpBC2_b32; //bicibic sharp
case 4: return interpSP4_b32; //spline 4x4
case 5: return interpSP6_b32; //spline 6x6
case 6: return interpSC16_b32; //lanczos 8x8
default: return NULL;
}
}
//-----------------------------------------------------
//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="c0rners";
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=15;
info->explanation="Four corners geometry engine";
}
//--------------------------------------------------
void f0r_get_param_info(f0r_param_info_t* info, int param_index)
{
switch(param_index)
{
case 0:
info->name = "Corner 1 X";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "X coordinate of corner 1";
break;
case 1:
info->name = "Corner 1 Y";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Y coordinate of corner 1";
break;
case 2:
info->name = "Corner 2 X";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "X coordinate of corner 2";
break;
case 3:
info->name = "Corner 2 Y";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Y coordinate of corner 2";
break;
case 4:
info->name = "Corner 3 X";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "X coordinate of corner 3";
break;
case 5:
info->name = "Corner 3 Y";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Y coordinate of corner 3";
break;
case 6:
info->name = "Corner 4 X";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "X coordinate of corner 4";
break;
case 7:
info->name = "Corner 4 Y";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Y coordinate of corner 4";
break;
case 8:
info->name = "Enable Stretch";
info->type = F0R_PARAM_BOOL;
info->explanation = "Enable stretching";
break;
case 9:
info->name = "Stretch X";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Amount of stretching in X direction";
break;
case 10:
info->name = "Stretch Y";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Amount of stretching in Y direction";
break;
case 11:
info->name = "Interpolator";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Quality of interpolation";
break;
case 12:
info->name = "Transparent Background";
info->type = F0R_PARAM_BOOL;
info->explanation = "Makes background transparent";
break;
case 13:
info->name = "Feather Alpha";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Makes smooth transition into transparent";
break;
case 14:
info->name = "Alpha operation";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "";
}
}
//----------------------------------------------
f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
{
inst *in;
in=(inst*)calloc(1, sizeof(inst));
in->w=width;
in->h=height;
in->x1=0.333333;
in->y1=0.333333;
in->x2=0.666666;
in->y2=0.333333;
in->x3=0.666666;
in->y3=0.666666;
in->x4=0.333333;
in->y4=0.666666;
in->stretchON=0;
in->stretchx=0.5;
in->stretchy=0.5;
in->intp=1;
in->transb=0;
in->feath=1.0;
in->op=0;
in->map=(float*)calloc(1, sizeof(float)*(in->w*in->h*2+2));
in->amap=(unsigned char*)calloc(1, sizeof(char)*(in->w*in->h*2+2));
in->interp=set_intp(*in);
in->mapIsDirty=1;
return (f0r_instance_t)in;
}
//---------------------------------------------------
void f0r_destruct(f0r_instance_t instance)
{
inst *p;
p=(inst*)instance;
free(p->map);
free(p->amap);
free(instance);
}
//-----------------------------------------------------
void f0r_set_param_value(f0r_instance_t instance, f0r_param_t parm, int param_index)
{
inst *p;
double tmpf;
int chg;
p=(inst*)instance;
chg=0;
switch(param_index)
{
case 0: //X coordinate of corner 1
tmpf=*(double*)parm;
if (tmpf!=p->x1) chg=1;
p->x1=tmpf;
break;
case 1: //Y coordinate of corner 1
tmpf=*(double*)parm;
if (tmpf!=p->y1) chg=1;
p->y1=tmpf;
break;
case 2: //X coordinate of corner 2
tmpf=*(double*)parm;
if (tmpf!=p->x2) chg=1;
p->x2=tmpf;
break;
case 3: //Y coordinate of corner 2
tmpf=*(double*)parm;
if (tmpf!=p->y2) chg=1;
p->y2=tmpf;
break;
case 4: //X coordinate of corner 3
tmpf=*(double*)parm;
if (tmpf!=p->x3) chg=1;
p->x3=tmpf;
break;
case 5: //Y coordinate of corner 3
tmpf=*(double*)parm;
if (tmpf!=p->y3) chg=1;
p->y3=tmpf;
break;
case 6: //X coordinate of corner 4
tmpf=*(double*)parm;
if (tmpf!=p->x4) chg=1;
p->x4=tmpf;
break;
case 7: //Y coordinate of corner 4
tmpf=*(double*)parm;
if (tmpf!=p->y4) chg=1;
p->y4=tmpf;
break;
case 8: //Enable stretching
tmpf=map_value_forward(*((double*)parm), 0.0, 1.0);//BOOL!!
if (p->stretchON != tmpf) chg=1;
p->stretchON = tmpf;
break;
case 9: //Stretch X
tmpf=*(double*)parm;
if (tmpf!=p->stretchx) chg=1;
p->stretchx=tmpf;
break;
case 10: //Stretch Y
tmpf=*(double*)parm;
if (tmpf!=p->stretchy) chg=1;
p->stretchy=tmpf;
break;
case 11: //Interpolation
tmpf=map_value_forward(*((double*)parm), 0.0, 6.999);
if (p->intp != tmpf) chg=1;
p->intp=tmpf;
break;
case 12: //Transparent Background
tmpf=map_value_forward(*((double*)parm), 0.0, 1.0);//BOOL!!
// if (p->transb != tmpf) chg=1;
p->transb = tmpf;
break;
case 13: //Feather Alpha
tmpf=map_value_forward(*((double*)parm), 0.0, 100.0);
if (tmpf!=p->feath) chg=1;
p->feath=tmpf;
break;
case 14: //Alpha operation
p->op=map_value_forward(*((double*)parm), 0.0, 4.9999);
break;
}
if (chg!=0)
{
p->interp=set_intp(*p);
p->mapIsDirty = 1;
}
}
//--------------------------------------------------
void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
inst *p;
double tmpf;
p=(inst*)instance;
switch(param_index)
{
case 0: //X coordinate of corner 1
tmpf=(float)p->x1;
*((double*)param)=tmpf;
break;
case 1: //Y coordinate of corner 1
tmpf=(float)p->y1;
*((double*)param)=tmpf;
break;
case 2: //X coordinate of corner 2
tmpf=(float)p->x2;
*((double*)param)=tmpf;
break;
case 3: //Y coordinate of corner 2
tmpf=(float)p->y2;
*((double*)param)=tmpf;
break;
case 4: //X coordinate of corner 3
tmpf=(float)p->x3;
*((double*)param)=tmpf;
break;
case 5: //Y coordinate of corner 3
tmpf=(float)p->y3;
*((double*)param)=tmpf;
break;
case 6: //X coordinate of corner 4
tmpf=(float)p->x4;
*((double*)param)=tmpf;
break;
case 7: //Y coordinate of corner 4
tmpf=(float)p->y4;
*((double*)param)=tmpf;
break;
case 8: //Enable stretching
*((double*)param)=map_value_backward(p->stretchON, 0.0, 1.0); //BOOL!!
break;
case 9: //Stretch X
tmpf=(float)p->stretchx;
*((double*)param)=tmpf;
break;
case 10: //Stretch Y
tmpf=(float)p->stretchy;
*((double*)param)=tmpf;
break;
case 11: //Interpolation
*((double*)param)=map_value_backward(p->intp, 0.0, 6.0); //!!!!!! 6.999 ???? tudi v defish!!!!
break;
case 12: //Transparent Background
*((double*)param)=map_value_backward(p->transb, 0.0, 1.0); //BOOL!!
break;
case 13: //Feather Alpha
*((double*)param)=map_value_backward(p->feath, 0.0, 100.0);
break;
case 14: //Alpha operation
*((double*)param)=map_value_backward(p->op, 0.0, 4.9999);
break;
}
}
#define EPSILON 1e-5f
#define EQUIVALENT_FLOATS(x, y) (fabsf((x) - (y)) < EPSILON)
//-------------------------------------------------
void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)
{
inst *p;
int bkgr;
p=(inst*)instance;
if (EQUIVALENT_FLOATS(p->x1, 0.333333f) &&
EQUIVALENT_FLOATS(p->y1, 0.333333f) &&
EQUIVALENT_FLOATS(p->x2, 0.666666f) &&
EQUIVALENT_FLOATS(p->y2, 0.333333f) &&
EQUIVALENT_FLOATS(p->x3, 0.666666f) &&
EQUIVALENT_FLOATS(p->y3, 0.666666f) &&
EQUIVALENT_FLOATS(p->x4, 0.333333f) &&
EQUIVALENT_FLOATS(p->y4, 0.666666f) &&
(!p->stretchON || (
EQUIVALENT_FLOATS(p->stretchx, 0.5f) &&
EQUIVALENT_FLOATS(p->stretchy, 0.5f))))
{
memcpy(outframe, inframe, p->w * p->h * 4);
return;
}
if (p->mapIsDirty) {
tocka2 vog[4];
int nots[4];
vog[0].x=(p->x1*3-1)*p->w;
vog[0].y=(p->y1*3-1)*p->h;
vog[1].x=(p->x2*3-1)*p->w;
vog[1].y=(p->y2*3-1)*p->h;
vog[2].x=(p->x3*3-1)*p->w;
vog[2].y=(p->y3*3-1)*p->h;
vog[3].x=(p->x4*3-1)*p->w;
vog[3].y=(p->y4*3-1)*p->h;
geom4c_b(p->w, p->h, p->w, p->h, vog, p->stretchON, p->stretchx, p->stretchy, p->map, nots);
make_alphamap(p->amap, vog, p->w, p->h, p->map, p->feath, nots);
p->mapIsDirty = 0;
}
//if (p->transb==0) bkgr=0xFF000000; else bkgr=0;
bkgr=0xFF000000;
remap32(p->w, p->h, p->w, p->h, (unsigned char*) inframe, (unsigned char *) outframe, p->map, bkgr, p->interp);
if (p->transb!=0)
apply_alphamap(outframe, p->w, p->h, p->amap, p->op);
}