#include "php.h"
#include "php_imlib.h"
#define php_filter_init php_colormatrix_LTX_php_filter_init
#define php_filter_deinit php_colormatrix_LTX_php_filter_deinit
#define php_filter_exec php_colormatrix_LTX_php_filter_exec
#define php_filter_getfiltercount php_colormatrix_LTX_php_filter_getfiltercount
#define php_filter_getfilternames php_colormatrix_LTX_php_filter_getfilternames
#define php_filter_getextinfo php_colormatrix_LTX_php_filter_getextinfo
#define php_filter_getparams php_colormatrix_LTX_php_filter_getparams
#define RLUM (0.3086)
#define GLUM (0.6094)
#define BLUM (0.0820)
/*
* identmat -
* make an identity matrix
*/
static void _php_colormatrix_identmat(double *matrix)
{
*matrix++ = 1.0; /* row 1 */
*matrix++ = 0.0;
*matrix++ = 0.0;
*matrix++ = 0.0;
*matrix++ = 0.0; /* row 2 */
*matrix++ = 1.0;
*matrix++ = 0.0;
*matrix++ = 0.0;
*matrix++ = 0.0; /* row 3 */
*matrix++ = 0.0;
*matrix++ = 1.0;
*matrix++ = 0.0;
*matrix++ = 0.0; /* row 4 */
*matrix++ = 0.0;
*matrix++ = 0.0;
*matrix++ = 1.0;
}
/*
* matrixmult -
* multiply two matricies
*/
static void _php_colormatrix_matrixmult(double a[16],double b[16],double c[16])
{
int x, y;
double tmp[16];
for(y=0; y<4 ; y++)
{
for(x=0 ; x<4 ; x++)
{
tmp[(y<<2)+x] = b[(y<<2)+0] * a[(0<<2)+x]
+ b[(y<<2)+1] * a[(1<<2)+x]
+ b[(y<<2)+2] * a[(2<<2)+x]
+ b[(y<<2)+3] * a[(3<<2)+x];
}
}
memcpy(c,tmp,sizeof(double)*16);
}
/*
* xformpnt -
* transform a 3D point using a matrix
*/
static void _php_colormatrix_xformpnt(double matrix[16],double x,double y,double z,double *tx,double *ty,double *tz)
{
*tx = x*matrix[0] + y*matrix[4] + z*matrix[8] + matrix[12];
*ty = x*matrix[1] + y*matrix[5] + z*matrix[9] + matrix[13];
*tz = x*matrix[2] + y*matrix[6] + z*matrix[10] + matrix[14];
}
/*
* xrotate -
* rotate about the x (red) axis
*/
static void _php_colormatrix_xrotatemat(double mat[16],double rs,double rc)
{
double mmat[16];
mmat[0] = 1.0;
mmat[1] = 0.0;
mmat[2] = 0.0;
mmat[3] = 0.0;
mmat[4] = 0.0;
mmat[5] = rc;
mmat[6] = rs;
mmat[7] = 0.0;
mmat[8] = 0.0;
mmat[9] = -rs;
mmat[10] = rc;
mmat[11] = 0.0;
mmat[12] = 0.0;
mmat[13] = 0.0;
mmat[14] = 0.0;
mmat[15] = 1.0;
_php_colormatrix_matrixmult(mmat,mat,mat);
}
/*
* yrotate -
* rotate about the y (green) axis
*/
static void _php_colormatrix_yrotatemat(double mat[16],double rs,double rc)
{
double mmat[16];
mmat[0] = rc;
mmat[1] = 0.0;
mmat[2] = -rs;
mmat[3] = 0.0;
mmat[4] = 0.0;
mmat[5] = 1.0;
mmat[6] = 0.0;
mmat[7] = 0.0;
mmat[8] = rs;
mmat[9] = 0.0;
mmat[10] = rc;
mmat[11] = 0.0;
mmat[12] = 0.0;
mmat[13] = 0.0;
mmat[14] = 0.0;
mmat[15] = 1.0;
_php_colormatrix_matrixmult(mmat,mat,mat);
}
/*
* zrotate -
* rotate about the z (blue) axis
*/
static void _php_colormatrix_zrotatemat(double mat[16],double rs,double rc)
{
double mmat[16];
mmat[0] = rc;
mmat[1] = rs;
mmat[2] = 0.0;
mmat[3] = 0.0;
mmat[4] = -rs;
mmat[5] = rc;
mmat[6] = 0.0;
mmat[7] = 0.0;
mmat[8] = 0.0;
mmat[9] = 0.0;
mmat[10] = 1.0;
mmat[11] = 0.0;
mmat[12] = 0.0;
mmat[13] = 0.0;
mmat[14] = 0.0;
mmat[15] = 1.0;
_php_colormatrix_matrixmult(mmat,mat,mat);
}
/*
* zshear -
* shear z using x and y.
*/
static void _php_colormatrix_zshearmat(double mat[16],double dx,double dy)
{
double mmat[16];
mmat[0] = 1.0;
mmat[1] = 0.0;
mmat[2] = dx;
mmat[3] = 0.0;
mmat[4] = 0.0;
mmat[5] = 1.0;
mmat[6] = dy;
mmat[7] = 0.0;
mmat[8] = 0.0;
mmat[9] = 0.0;
mmat[10] = 1.0;
mmat[11] = 0.0;
mmat[12] = 0.0;
mmat[13] = 0.0;
mmat[14] = 0.0;
mmat[15] = 1.0;
_php_colormatrix_matrixmult(mmat,mat,mat);
}
/*
* huerotatemat -
* rotate the hue, while maintaining luminance.
*/
static void _php_colormatrix_huerotatemat(double mat[16],double rot)
{
double mmat[16];
double mag;
double lx, ly, lz;
double xrs, xrc;
double yrs, yrc;
double zrs, zrc;
double zsx, zsy;
_php_colormatrix_identmat(mmat);
/* rotate the grey vector into positive Z */
mag = sqrt(2.0);
xrs = 1.0/mag;
xrc = 1.0/mag;
_php_colormatrix_xrotatemat(mmat,xrs,xrc);
mag = sqrt(3.0);
yrs = -1.0/mag;
yrc = sqrt(2.0)/mag;
_php_colormatrix_yrotatemat(mmat,yrs,yrc);
/* shear the space to make the luminance plane horizontal */
_php_colormatrix_xformpnt(mmat,RLUM,GLUM,BLUM,&lx,&ly,&lz);
zsx = lx/lz;
zsy = ly/lz;
_php_colormatrix_zshearmat(mmat,zsx,zsy);
/* rotate the hue */
zrs = sin(rot*M_PI/180.0);
zrc = cos(rot*M_PI/180.0);
_php_colormatrix_zrotatemat(mmat,zrs,zrc);
/* unshear the space to put the luminance plane back */
_php_colormatrix_zshearmat(mmat,-zsx,-zsy);
/* rotate the grey vector back into place */
_php_colormatrix_yrotatemat(mmat,-yrs,yrc);
_php_colormatrix_xrotatemat(mmat,-xrs,xrc);
_php_colormatrix_matrixmult(mmat,mat,mat);
}
/*
* saturatemat -
* make a saturation marix
*/
static void _php_colormatrix_saturatemat(double mat[16], double sat)
{
mat[0] = (1.0-sat)*RLUM + sat;
mat[1] = (1.0-sat)*RLUM;
mat[2] = (1.0-sat)*RLUM;
mat[3] = 0.0;
mat[4] = (1.0-sat)*GLUM;
mat[5] = (1.0-sat)*GLUM + sat;
mat[6] = (1.0-sat)*GLUM;
mat[7] = 0.0;
mat[8] = (1.0-sat)*BLUM;
mat[9] = (1.0-sat)*BLUM;
mat[10] = (1.0-sat)*BLUM + sat;
mat[11] = 0.0;
mat[12] = 0.0;
mat[13] = 0.0;
mat[14] = 0.0;
mat[15] = 1.0;
}
static void _php_colormatrix_usermat(double mat[16], zval** data)
{
HashTable* mat_ht;
HashPosition pos;
convert_to_array_ex(data);
mat_ht=(*data)->value.ht; // FIXME: should really use HASH_OF macro.
// Filter API change needed to supply TSRMLS to filters
zend_hash_internal_pointer_reset_ex(mat_ht,&pos);
while (pos)
{
char *str_index;
ulong num_index;
int retval;
zval **value;
retval=zend_hash_get_current_key_ex(mat_ht,&str_index,NULL,&num_index,0,&pos);
if (retval==HASH_KEY_IS_LONG)
{
if ((num_index>=0)&&(num_index<16))
{
zend_hash_get_current_data_ex(mat_ht,&value,&pos);
convert_to_double_ex(value);
mat[num_index]=Z_DVAL_PP(value);
}
}
zend_hash_move_forward_ex(mat_ht,&pos);
}
}
static int
_php_colormatrix_exec(Imlib_Image im, char* filter, HashTable* params)
{
HashPosition pos;
double mat[16];
int x=0,y=0,w,h,imgw,imgh;
_php_colormatrix_identmat(mat);
imlib_context_set_image( im );
w = imgw = imlib_image_get_width();
h = imgh = imlib_image_get_height();
zend_hash_internal_pointer_reset_ex(params,&pos);
while (pos)
{
char *str_index;
ulong num_index;
int retval;
retval=zend_hash_get_current_key_ex(params,&str_index,NULL,&num_index,0,&pos);
if (retval==HASH_KEY_IS_STRING)
{
zval **data;
zend_hash_get_current_data_ex(params,&data,&pos);
if (!strcmp(str_index,"x")) { convert_to_long_ex(data); x=Z_LVAL_PP(data); };
if (!strcmp(str_index,"y")) { convert_to_long_ex(data); y=Z_LVAL_PP(data); };
if (!strcmp(str_index,"w")) { convert_to_long_ex(data); w=Z_LVAL_PP(data); };
if (!strcmp(str_index,"h")) { convert_to_long_ex(data); h=Z_LVAL_PP(data); };
if (!strcmp(filter,"cm_user"))
{
if (!strcmp(str_index,"matrix")) _php_colormatrix_usermat(mat,data);
}
if (!strcmp(filter,"cm_saturation"))
{
if (!strcmp(str_index,"sat")) { convert_to_double_ex(data); _php_colormatrix_saturatemat(mat,Z_DVAL_PP(data)); };
}
if (!strcmp(filter,"cm_huerot"))
{
if (!strcmp(str_index,"angle")) { convert_to_double_ex(data); _php_colormatrix_huerotatemat(mat,Z_DVAL_PP(data)); };
}
}
zend_hash_move_forward_ex(params,&pos);
}
if (x<0) x=0;
if (y<0) y=0;
if (w<0) w=0;
if (h<0) h=0;
if (x>=imgw) x=imgw-1;
if (y>=imgh) y=imgh-1;
if ((x+w)>imgw) w=imgw-x;
if ((y+h)>imgh) h=imgh-y;
{
DATA32 *img_ptr,*old_ptr;
int i,j;
old_ptr = img_ptr = imlib_image_get_data();
img_ptr+=x+y*imgw;
for (j=y;j<(y+h);j++)
{
for (i=x;i<(x+w);i++)
{
int r,g,b,nr,ng,nb;
DATA32 argb;
argb=*img_ptr;
r=(argb&0x00ff0000)>>16;
g=(argb&0x0000ff00)>>8;
b=(argb&0x000000ff);
nr = r*mat[0] + g*mat[4] + b*mat[8] + mat[12];
ng = r*mat[1] + g*mat[5] + b*mat[9] + mat[13];
nb = r*mat[2] + g*mat[6] + b*mat[10] + mat[14];
if (nr<0) nr=0;
if (nr>255) nr=255;
if (ng<0) ng=0;
if (ng>255) ng=255;
if (nb<0) nb=0;
if (nb>255) nb=255;
*img_ptr=(argb&0xff000000) | (nr<<16) | (ng<<8) | nb;
img_ptr++;
}
img_ptr+=imgw-w;
}
imlib_image_put_back_data(old_ptr);
}
return SUCCESS;
}
int
php_filter_init()
{
return SUCCESS;
}
void
php_filter_deinit()
{
return;
}
int
php_filter_exec(Imlib_Image im, char *filter, HashTable *params)
{
if (!strcmp(filter, "cm_user") || !strcmp(filter,"cm_saturation") || !strcmp(filter,"cm_huerot"))
{
return _php_colormatrix_exec(im,filter,params);
}
return FAILURE;
}
int
php_filter_getfiltercount()
{
return 3;
}
void
php_filter_getfilternames(char** filternames)
{
filternames[0]="cm_user";
filternames[1]="cm_saturation";
filternames[2]="cm_huerot";
}
void
php_filter_getextinfo(char** info)
{
info[0]="Color Matrix";
info[1]="Color processing using 4x4 matrices. Based on an article by Paul Haeberli.\n"
"You can use built-in saturation and hue rotate filters (both filters maintain luminance), or supply your own matrix as a 16-element array. RGB values are computed as follows:\n"
"r' = r*matrix[0] + g*matrix[4] + b*matrix[8] + matrix[12]\n"
"g' = r*matrix[1] + g*matrix[5] + b*matrix[9] + matrix[13]\n"
"b' = r*matrix[2] + g*matrix[6] + b*matrix[10] + matrix[14]";
info[2]="Piotr Pawlow (pp@siedziba.pl)";
}
char*
php_filter_getparams(char* filter)
{
if (!strcmp(filter, "cm_user")) return "int x=0, int y=0, int w=image_width, int h=image_height, array matrix=(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1)";
if (!strcmp(filter, "cm_saturation")) return "int x=0, int y=0, int w=image_width, int h=image_height, double sat=1";
if (!strcmp(filter, "cm_huerot")) return "int x=0, int y=0, int w=image_width, int h=image_height, double angle=0";
return NULL;
}