/* * This is a plugin for the GIMP. * * Copyright (C) 1996 Xavier Bouchoux * * 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. * */ /* * This plug-in produces keftales images. * * Please send any patches *bugs* or suggestions to me: * Xavier.Bouchoux@ensimag.imag.fr. */ /* Version 1.0 */ #include #include #include #include "libgimp/gimp.h" #ifndef _AIX typedef unsigned char uchar; #endif /* * This structure is used for persistent data. */ #define B_W 0 /* colors setting */ #define USE_FG_BG 1 #define USE_COLORS 2 #define LINEAR 0 /* colorization settings */ #define BILINEAR 1 #define SINUS 2 #define IDEAL 0 /* Perturbation settings */ #define PERTURBED 1 typedef uchar RGB[3]; typedef struct { double xc,yc; double scale,degre; double blend_power; long seed; int perturbation; int colorization; int colors; RGB col1,col2; } Data; static void scale_callback(); static void ok_callback(); static void cancel_callback(); static void text_callback(); static void long_callback(); static void keftale(); static void keftale_grey(); int main_dialog(); static void color_button_callback(); static void toggle_callback(); void color_dialog(); static void color_ok_callback(); static void color_cancel_callback(); double frac(); double linear(); double bilinear(); double sinus(); static int dialog_id, cdialog_id; static int grey_scale, color_open; Data *data; int main(int argc, char **argv) { Image output, input; if (gimp_init(argc, argv)) { output = 0; input = gimp_get_input_image(0); output = gimp_get_output_image(0); if (input && output) data = gimp_get_params(); if (!data){ /* Use arbitary defaults. */ data = malloc(sizeof(Data)); data->xc = 0.4; data->yc = 0.4; data->scale = 19.000; data->seed = 123; data->degre = 4.000; data->blend_power=1; data->perturbation= PERTURBED; data->colorization= BILINEAR; data->colors= USE_COLORS; data->col1[0]=255; data->col1[1]=255; data->col1[1]=255; data->col1[2]=0; data->col2[0]=0; data->col2[1]=0; data->col2[2]=255; } switch (gimp_image_type(input)) { case GIMP_RGB_IMAGE: grey_scale= FALSE; if (main_dialog(data)) { gimp_set_params(sizeof(Data), data); srand(data->seed); gimp_init_progress("Keftales"); keftale(input, output, data); gimp_update_image(output); } break; case GIMP_GRAY_IMAGE: grey_scale= TRUE; if (main_dialog(data)) { gimp_set_params(sizeof(Data), data); srand(data->seed); gimp_init_progress("Keftales"); keftale_grey(input, output, data); gimp_update_image(output); } break; case GIMP_INDEXED_IMAGE: gimp_message("Keftale: cannot operate on indexed color images"); break; default: gimp_message("Keftale: cannot operate on unknown image types"); break; } if (input) gimp_free_image(input); if (output) gimp_free_image(output); gimp_quit(); } return 0; } int main_dialog(Data *data) { int grp_id, row_id, frame_id, radio_id; int text_x_id, text_y_id, scale_id, rand_id, degre_id; int perturb_ideal_id, perturb_perturb_id; int color_settings_id; long scale, degre; int perturb_perturb=0, perturb_ideal=0; char bufx[100], bufy[100], bufrand[100]; scale= data->scale*1000; degre= data->degre*1000; switch (data->perturbation) { case IDEAL: perturb_ideal = 1; break; case PERTURBED: default: perturb_perturb = 1; } dialog_id = gimp_new_dialog("Keftales"); grp_id = gimp_new_row_group(dialog_id, DEFAULT, NORMAL, ""); frame_id = gimp_new_frame (dialog_id, grp_id, "Drawing settings"); row_id = gimp_new_row_group(dialog_id, frame_id, NORMAL, ""); gimp_new_label(dialog_id, row_id, "x center:"); sprintf(bufx,"%0.3f", data->xc); text_x_id = gimp_new_text(dialog_id, row_id, bufx); gimp_add_callback(dialog_id, text_x_id, text_callback, &(data->xc)); gimp_new_label(dialog_id, row_id, "y center:"); sprintf(bufy, "%0.3f", data->yc); text_y_id = gimp_new_text(dialog_id, row_id, bufy); gimp_add_callback(dialog_id, text_y_id, text_callback, &(data->yc)); gimp_new_label(dialog_id, row_id, "scale factor:"); scale_id = gimp_new_scale(dialog_id, row_id, 0, 250000, scale, 3); gimp_add_callback(dialog_id, scale_id, scale_callback, &scale); frame_id = gimp_new_frame (dialog_id, grp_id, "Calculation settings"); row_id = gimp_new_row_group(dialog_id, frame_id, NORMAL, ""); gimp_new_label(dialog_id, row_id, "random seed:"); sprintf(bufrand,"%ld", data->seed); rand_id = gimp_new_text(dialog_id, row_id, bufrand); gimp_add_callback(dialog_id, rand_id, long_callback, &(data->seed)); gimp_new_label(dialog_id, row_id, "Equation power:"); degre_id = gimp_new_scale(dialog_id, row_id, 2000, 4000, degre, 3); gimp_add_callback(dialog_id, degre_id, scale_callback, °re); radio_id= gimp_new_row_group(dialog_id, row_id, RADIO, ""); perturb_ideal_id= gimp_new_radio_button (dialog_id, radio_id, "Ideal coefs"); perturb_perturb_id= gimp_new_radio_button (dialog_id, radio_id, "Distorted"); gimp_change_item (dialog_id, perturb_ideal_id, sizeof(int), &perturb_ideal); gimp_change_item (dialog_id, perturb_perturb_id, sizeof(int), &perturb_perturb); gimp_add_callback(dialog_id, perturb_ideal_id, toggle_callback, &perturb_ideal); gimp_add_callback(dialog_id, perturb_perturb_id, toggle_callback, &perturb_perturb); color_settings_id= gimp_new_push_button( dialog_id, grp_id, "Color settings..."); gimp_add_callback(dialog_id, color_settings_id, color_button_callback, NULL); gimp_add_callback(dialog_id, gimp_ok_item_id(dialog_id), ok_callback, NULL); gimp_add_callback(dialog_id, gimp_cancel_item_id(dialog_id), cancel_callback, NULL); if (gimp_show_dialog(dialog_id)) { data->scale= (double)scale/1000; data->degre= (double)degre/1000; data->perturbation= perturb_ideal? IDEAL : PERTURBED; return TRUE; } else { return FALSE; } } void color_dialog(Data *data) { int col_id,grp_id, row_id, frame_id, radio_id; int color_bw_id, color_fgbg_id, color_col_id, blend_linear_id, blend_bilinear_id, blend_sinus_id; int col1_r_id, col2_r_id, col1_g_id, col2_g_id, col1_b_id, col2_b_id; int blend_power_id; long blend_power; int color_bw=0, color_fgbg=0, color_col=0; int blend_linear=0, blend_bilinear=0, blend_sinus=0; long col1_r, col2_r, col1_g, col2_g, col1_b, col2_b; blend_power= data->blend_power*1000; col1_r= data->col1[0]; col1_g= data->col1[1]; col1_b= data->col1[2]; col2_r= data->col2[0]; col2_g= data->col2[1]; col2_b= data->col2[2]; switch (data->colorization) { case LINEAR: blend_linear=1; break; case BILINEAR: blend_bilinear=1; break; case SINUS: blend_sinus=1; break; default: blend_linear=1; } switch (data->colors) { case B_W: color_bw=1; break; case USE_FG_BG: color_fgbg=1; break; case USE_COLORS: default: color_col=1; } cdialog_id = gimp_new_dialog("Keftales: colors"); if (!grey_scale) { col_id= gimp_new_column_group(cdialog_id, DEFAULT, NORMAL, ""); grp_id= gimp_new_row_group(cdialog_id, col_id, NORMAL, ""); } else { grp_id= gimp_new_row_group(cdialog_id, DEFAULT, NORMAL, ""); } if (!grey_scale) { frame_id = gimp_new_frame (cdialog_id, grp_id, "Colors"); radio_id = gimp_new_row_group(cdialog_id, frame_id, RADIO, ""); color_bw_id= gimp_new_radio_button (cdialog_id, radio_id, "Black & White"); color_fgbg_id=gimp_new_radio_button (cdialog_id, radio_id, "Foreground & Background"); color_col_id= gimp_new_radio_button (cdialog_id, radio_id, "Colors"); gimp_change_item (cdialog_id, color_bw_id, sizeof(int), &color_bw); gimp_change_item (cdialog_id, color_fgbg_id, sizeof(int), &color_fgbg); gimp_change_item (cdialog_id, color_col_id, sizeof(int), &color_col); gimp_add_callback(cdialog_id, color_bw_id, toggle_callback, &color_bw); gimp_add_callback(cdialog_id, color_fgbg_id, toggle_callback, &color_fgbg); gimp_add_callback(cdialog_id, color_col_id, toggle_callback, &color_col); } frame_id = gimp_new_frame (cdialog_id, grp_id, "Blend"); row_id = gimp_new_row_group(cdialog_id, frame_id, NORMAL, ""); radio_id = gimp_new_row_group(cdialog_id, row_id, RADIO, ""); blend_linear_id= gimp_new_radio_button (cdialog_id, radio_id, "Linear"); blend_bilinear_id= gimp_new_radio_button (cdialog_id, radio_id, "Bilinear"); blend_sinus_id= gimp_new_radio_button (cdialog_id, radio_id, "Sinusoidal"); gimp_change_item (cdialog_id, blend_linear_id, sizeof(int), &blend_linear ); gimp_change_item (cdialog_id, blend_bilinear_id, sizeof(int), &blend_bilinear); gimp_change_item (cdialog_id, blend_sinus_id, sizeof(int), &blend_sinus); gimp_add_callback(cdialog_id, blend_linear_id, toggle_callback, &blend_linear); gimp_add_callback(cdialog_id, blend_bilinear_id, toggle_callback, &blend_bilinear); gimp_add_callback(cdialog_id, blend_sinus_id, toggle_callback, &blend_sinus); blend_power_id = gimp_new_scale (cdialog_id, row_id, 0, 20000, blend_power, 3); gimp_add_callback(cdialog_id, blend_power_id, scale_callback, &blend_power); if (!grey_scale) { grp_id= gimp_new_row_group(cdialog_id, col_id, NORMAL, ""); frame_id = gimp_new_frame (cdialog_id, grp_id, "Color 1"); row_id = gimp_new_row_group(cdialog_id, frame_id, NORMAL, ""); col1_r_id= gimp_new_scale (cdialog_id, row_id, 0, 255, col1_r, 0); col1_g_id= gimp_new_scale (cdialog_id, row_id, 0, 255, col1_g, 0); col1_b_id= gimp_new_scale (cdialog_id, row_id, 0, 255, col1_b, 0); gimp_add_callback(cdialog_id, col1_r_id, scale_callback, &col1_r); gimp_add_callback(cdialog_id, col1_g_id, scale_callback, &col1_g); gimp_add_callback(cdialog_id, col1_b_id, scale_callback, &col1_b); frame_id = gimp_new_frame (cdialog_id, grp_id, "Color 2"); row_id = gimp_new_row_group(cdialog_id, frame_id, NORMAL, ""); col2_r_id= gimp_new_scale (cdialog_id, row_id, 0, 255, col2_r, 0); col2_g_id= gimp_new_scale (cdialog_id, row_id, 0, 255, col2_g, 0); col2_b_id= gimp_new_scale (cdialog_id, row_id, 0, 255, col2_b, 0); gimp_add_callback(cdialog_id, col2_r_id, scale_callback, &col2_r); gimp_add_callback(cdialog_id, col2_g_id, scale_callback, &col2_g); gimp_add_callback(cdialog_id, col2_b_id, scale_callback, &col2_b); } gimp_add_callback(cdialog_id, gimp_ok_item_id(cdialog_id), color_ok_callback, NULL); gimp_add_callback(cdialog_id, gimp_cancel_item_id(cdialog_id), color_cancel_callback,NULL); if (gimp_show_dialog(cdialog_id)) { data->blend_power= (double)blend_power/1000; data->col1[0]= col1_r; data->col1[1]= col1_g; data->col1[2]= col1_b; data->col2[0]= col2_r; data->col2[1]= col2_g; data->col2[2]= col2_b; data->colors= color_bw ? B_W : (color_fgbg ? USE_FG_BG : USE_COLORS); data->colorization= blend_linear ? LINEAR : (blend_bilinear ? BILINEAR : SINUS); } } /* * Main procedure */ static void keftale(Image input, Image output, Data *data) /*double scale, double xcentre, double ycentre, double degre) */ { long width, height; int ix1, iy1, ix2, iy2; /* Selected image size. */ long channels, row_stride; uchar *dest, *dest_row; double x,y; double scale=data->scale,degre=data->degre; int i,j; double c41=0,c42=0,c43=0,c44=0,c45=0; double c31=0,c32=0,c33=0,c34=0; double c21=0,c22=0,c23=0; double grey; RGB col1,col2; int diff_coul[3]; double (*blend) (double ); switch (data->colorization) { case BILINEAR: blend = bilinear; break; case SINUS: blend = sinus; break; case LINEAR: default: blend = linear; } switch (data->colors) { case USE_COLORS: col1[0]=data->col1[0]; col1[1]=data->col1[1]; col1[2]=data->col1[2]; col2[0]=data->col2[0]; col2[1]=data->col2[1]; col2[2]=data->col2[2]; break; case B_W: col1[0]=col1[1]=col1[2]=0; col2[0]=col2[1]=col2[2]=255; break; case USE_FG_BG: gimp_foreground_color(&col2[0], &col2[1], &col2[2]); gimp_background_color(&col1[0], &col1[1], &col1[2]); break; } gimp_image_area(input, &ix1, &iy1, &ix2, &iy2); width = gimp_image_width(input); height = gimp_image_height(input); channels = gimp_image_channels(input); row_stride = width * channels; dest_row = gimp_image_data(output); dest_row += row_stride * iy1 + ix1 *channels; c21=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c22=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c23=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; if (degre>2) { if (degre>3.99) { c31=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c32=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c33=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c34=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; } else { c31=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c32=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c33=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c34=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); } } if (degre>3) { if (degre>3.99) { c41=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c42=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c43=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c44=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c45=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; } else { c41=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c42=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c43=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c44=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c45=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); } } if (data->perturbation==IDEAL) { c23=c33=c34=c43=c44=c45=0; } diff_coul[0]=(int)col2[0]-col1[0]; diff_coul[1]=(int)col2[1]-col1[1]; diff_coul[2]=(int)col2[2]-col1[2]; for (j=iy1; jyc; dest = dest_row; for (i=ix1; ixc; grey=((((c41*x+c43*y)+c31)*x+c21+(c33+c44*y)*y)*x+c23*y)*x \ + (((c42*y+c45*x)+c32)*y+c34*x+c22)*y*y; grey=pow(blend(grey),data->blend_power); *dest++= col1[0]+(int)(grey*diff_coul[0]); *dest++= col1[1]+(int)(grey*diff_coul[1]); *dest++= col1[2]+(int)(grey*diff_coul[2]); } dest_row += row_stride; if ((j&15)==0) gimp_do_progress(j-iy1, iy2-iy1); } } static void keftale_grey(Image input, Image output, Data *data) { long width, height; double x,y; double scale=data->scale,degre=data->degre; int i,j; double c41=0,c42=0,c43=0,c44=0,c45=0; double c31=0,c32=0,c33=0,c34=0; double c21=0,c22=0,c23=0; double grey; double (*blend) (double ); int ix1, iy1, ix2, iy2; /* Selected image size. */ long channels, row_stride; uchar *dest,*dest_row; switch (data->colorization) { case BILINEAR: blend = bilinear; break; case SINUS: blend = sinus; break; case LINEAR: default: blend = linear; } gimp_image_area(input, &ix1, &iy1, &ix2, &iy2); width = gimp_image_width(input); height = gimp_image_height(input); channels = gimp_image_channels(input); row_stride = width * channels; dest_row = gimp_image_data(output) + iy1 * row_stride + ix1 * channels; c21=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c22=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c23=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; if (degre>2) { if (degre>3.99) { c31=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c32=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c33=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c34=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; } else { c31=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c32=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c33=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c34=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); } } if (degre > 3) { if (degre>3.99) { c41=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c42=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c43=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c44=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; c45=(2.0*rand()/(RAND_MAX+1.0)-1)*scale; } else { c41=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c42=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c43=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c44=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); c45=(2.0*rand()/(RAND_MAX+1.0)-1)*scale*frac(degre); } } if (data->perturbation==IDEAL) { c23=c33=c34=c43=c44=c45=0; } for (j=iy1; jyc; dest = dest_row; for (i=ix1; ixc; grey=((((c41*x+c43*y)+c31)*x+c21+(c33+c44*y)*y)*x+c23*y)*x \ + (((c42*y+c45*x)+c32)*y+c34*x+c22)*y*y; grey=pow(blend(grey),data->blend_power); *dest++ = (uchar)(grey*255.0); } dest_row += row_stride; if ((j&15) == 0) gimp_do_progress(j-iy1, iy2-iy1); } } double frac( double v ) { return v-(double)((int)v); } double linear (double v) { register double a=v-(int)v; return (a<0?1.0+a:a); } double bilinear(double v) { register double a=v-(int)v; a=(a<0?1.0+a:a); return (a>0.5?2-2*a:2*a); } double sinus(double v) { return 0.5+0.5*sin((v+0.25)*PI*2); } static void scale_callback(int item_ID, void *client_data, void *call_data) { *((long *) client_data) = *((long *) call_data); } static void ok_callback(int item_ID, void *client_data, void *call_data) { if (color_open) { gimp_close_dialog(cdialog_id, 0); color_open= FALSE; } gimp_close_dialog(dialog_id, 1); } static void cancel_callback(int item_ID, void *client_data, void *call_data) { if (color_open) { gimp_close_dialog(cdialog_id, 0); color_open= FALSE; } gimp_close_dialog(dialog_id, 0); } static void long_callback(int item_id, void *client_data, void *call_data) { *((long *) client_data) = atoi((char *)call_data); } /* text_callback */ static void text_callback(int item_id, void *client_data, void *call_data) { *((double *) client_data) = atof(call_data); /* *((long *) client_data) = atoi((char *)call_data);*/ } /* text_callback */ static void color_button_callback(int item_id, void *client_data, void *call_data) { /* gimp_message("Keftale: Boite de dialogue appellée"); */ color_open= TRUE; color_dialog(data); color_open = FALSE; } static void toggle_callback(int item_id, void *client_data, void *call_data) { *((int *) client_data) = *((int *) call_data); } static void color_ok_callback(int item_ID, void *client_data, void *call_data) { gimp_close_dialog(cdialog_id, 1); } static void color_cancel_callback(int item_ID, void *client_data, void *call_data) { gimp_close_dialog(cdialog_id, 0); }