//Color reducer and cartoonizer - gimp plugin
//to install (on linux) - for user:
//   gimptool-2.0 --install cartoonizer.c
//and for system-wide installation:
//   gimptool-2.0 --install-admin cartoonizer.c
// following might be needed beforehand: 
//   export LDFLAGS="$LDFLAGS -lm"
//wiki: http://code.google.com/p/cartooner-color-reducer/wiki/cartoonerIntroduction
//contact: tiborb95 at gmail dot com,
//any feedback welcomed


#define VERSION 0.8.5
#define VERBOSE FALSE
#define DOCHECKSUMS FALSE
#define RESPONSE_RESET   99
#define RESPONSE_OK      1
#define COLORSDEF 6
#define WEIGHTDEF 2
#define SPTRESHDEF 0
#define SPITDEF 4
#define LINDIFFTRESHDEF 0.08
#define LINHUETRESHDEF 0.04
#define LINREMDEF 0
#define STEPPING 50
#define MAPPERLENGTH 1000000
#define CHECKCOLORS 1
#define LINESONLY 0
#define MAXCOLORS 300
#define GUIMAXCOLORS 50
#define MAXEFFECTIVE 288
//#define MAXDERIVED 300
#define ERODEDEF 20
#define STRICT TRUE
#define LOOSE FALSE
#define ITERATE(x,start,end) for(x=start;x<end;x++)
#define SAMPLEINT 0.005
#define SIMPLESAT 0   //get_saturation()
#define SIMPLE 0
#define QUADR 1
#define WEB 3
#define PENALTYDEF 0
#define SATONLY 1
//used in combos
//pixel wighting combo
#define COUNT 0
#define BYSAT 1
#define BYNEIGH 2
#define BYHUE 3
#define MIX 4
#define WMODEDEF 4  //default
//algorithm (processing) combo
#define FAIR 0
#define ENHANCED 1
#define CAMO 2
#define DITHER 3
#define DITHER31 4
#define ADAPTIVE 5
#define MODEDEF 0  //default
//postprocessing combo
#define NONE 0
#define PASTEL 1
#define DARKPASTEL 2
#define SATURATED 3
#define GOLD 4
#define PALETTE 5

#include <libgimp/gimp.h>
#include <gtk/gtk.h>
#include <stdio.h>
#include <libgimp/gimpui.h>
#include <string.h>
#include <stdlib.h> 
#include <unistd.h> 


typedef struct {
	gfloat r;
	gfloat g;
	gfloat b;
	gfloat wr;
	gfloat wg;
	gfloat wb;
	gfloat step_r[10]; //for line distance
	gfloat step_g[10];
	gfloat step_b[10];
	gint steps;
	guchar R;
	guchar G;
	guchar B;
	gint parent[4];
	gint parents;  // number of parents
	//gfloat sat;
	gint count;   //statistics of occurence in image
} Color;

static Color primaryc[MAXCOLORS];
static Color effectivec[MAXEFFECTIVE];
//static Color derivedc[MAXDERIVED];

//functions
static void query (void);
static void run   (const gchar      *name,
                   gint              nparams,
                   const GimpParam  *param,
                   gint             *nreturn_vals,
                   GimpParam       **return_vals);

static void process (GimpDrawable *source,GimpPreview *preview);
static gboolean gui_window (GimpDrawable *source);
static void response_callback (GtkWidget *widget,  gint response_id);
static void recalculate_sizes(void);
static void do_sampling(void);
static void get_fcolors(void);
static void show_fcolors(gboolean forceprint) ;
static void indexing(void);
static void deindexing(void);
static void denoising(gint firsttresh,gint secondtresh,gint maxiter,gboolean strict);
static void despotting2(void);
static void dolines_changed(void);
static void doerode_changed(void);
static void denoising_changed(void);
static void legend_changed(void);
static void do_lines2(void);
static void exportwrapper (GimpDrawable *source);
static void insert_legend(void);
static void remove_short(gboolean *outlinemask,gboolean checkcolors,gint treshold);
static void temp_change_col(int channel, int color, float change);
static void tmp_color_reject(void);
static void do_weight(gfloat r, gfloat g, gfloat b, gfloat *newr,gfloat *newg, gfloat *newb);
//static void undo_weight(gfloat r, gfloat g, gfloat b, gfloat *newr,gfloat *newg, gfloat *newb);
static void cb_changed( GtkComboBox *combo, gpointer data );
static void colors_warning(gboolean on) ;
static float get_saturation(gfloat r,gfloat g,gfloat b,gint type);
static void find_best2(gfloat wr,gfloat wg,gfloat wb,gint *pos,gfloat *dist,gint mode);
static void cascade_colors(gboolean printinfo,gint forcedmode);
static float get_brightness(gfloat r,gfloat g,gfloat b,gint type);
static gint get_huegroup(gfloat r, gfloat g,gfloat b);

//global variables

typedef struct
{
	gint colors;
	gint spotSzTr;
	gint spotIt;
	gint weight;
	gboolean exportlayer;
	gfloat linDiffTresh;
	gfloat linhuetresh;
	gboolean legend;
	gint rmLinSzTr;
	gboolean doErode;
	gint erodeIt;
	gboolean doDenoise;
	gint mode;
	gint postprMode;
	gint pwMode;
	gboolean forcebw;
	gint channels;
	gboolean dolines;
	gboolean previewbool;
	//gint derivedcolors;
	gint effective;
	gfloat startrange;  //for linedist
	gfloat maxdiff;		//for dithering
	gfloat fixeddiff;   // for un/do_weight
	
} MySharpVals;
static MySharpVals maindata = { COLORSDEF,SPTRESHDEF,SPITDEF,WEIGHTDEF,FALSE,LINDIFFTRESHDEF,LINHUETRESHDEF,
		FALSE,LINREMDEF,FALSE, ERODEDEF,TRUE,MODEDEF,NONE,WMODEDEF,FALSE  };

typedef struct {
	gint channel;
	gint color;
	gfloat value;
} tmpchange;
static tmpchange origcolor={0,0,0};

typedef struct {
	gint startx;
	gint endx;
	gint starty;
	gint endy;
	gint width;
	gint height;
} Sizes;
static Sizes image;
static Sizes warea;
static Sizes parea;

typedef struct {
	gfloat r; // content depends on colorspace
	gfloat g;
	gfloat b;
	gfloat wr; // content depends on colorspace
	gfloat wg;
	gfloat wb;
	gint count;
	gfloat w_count;
	gfloat w_sat;
	gfloat w_neigh;
	gint hue_group;
	gfloat w_hue;
	gfloat final_weight;
	gfloat reg_importance;
} Sample;
static Sample samples[STEPPING*STEPPING];


static gchar *mode1  = "Plain       ";
static gchar *mode2  = "Dither 4*1   ";
static gchar *mode3  = "Camouflage   ";
static gchar *mode4  = "Enhanced     ";
static gchar *mode5  = "Dither 3+1   ";
static gchar *mode8  = "Adaptive     ";
static gchar *wmode1 = "No weighting";
static gchar *wmode2 = "By saturation";
static gchar *wmode3 = "Neighbourhood";
static gchar *wmode4 = "Unique colors";
static gchar *wmode5 = "Combined";
static gchar *post1  = "None";
static gchar *post2  = "Pastel";
static gchar *post3  = "Saturated";
static gchar *post4  = "Gold";
static gchar *post5  = "Dark Pastel";
static gchar *post6  = "Palette";
static gchar *title  = "Color Reducer and Cartoonizer (v. 0.8.5)";
static gchar *linescheckboxtest= "A pixel is turned black if while comparing to any of its 8 adjacent pixels both tresholds below are satisfied. Lines are created on own layer and with alpha channel.";
static gchar *rgbtresttext = "Treshold of 3D RGB distance, actual RGB distance must be bigger to create a line. However the other condition must be met too.";
static gchar *huetresttext = "Treshold for hue, actual color difference must be bigger to create a line. However the other condition must be met too.";
static gfloat		gammed_values[256]; //to be used for fast conversion 
								//from raw RGB to gammed 0-1 values
gboolean process_image = FALSE;
static guchar *rect_img;
static guchar *rect_wrk;
static guchar *rect_out;
static gint *rect_indx;
static gboolean rect_wrk_initialized=FALSE;
static gboolean rect_out_initialized=FALSE;
static gboolean rect_indx_initialized=FALSE;
static gint samplescount;
gint32 image_ID,drawable_ID;

GimpRGB RGBvalues;	
GimpHSV HSVvalues;	

static GtkWidget *spin_count,*spin_weight,*spin_sp_tresh,*spin_sp_it,*spin_erode_it,*spin_lin_diff_tresh,*spin_lin_hue_tresh,
		*spin_lin_rem;
static GtkObject *spin_count_adj,*spin_weight_adj,*spin_sp_tresh_adj,*spin_sp_it_adj,*spin_erode_it_adj
		,*spin_lin_diff_tresh_adj,*spin_lin_hue_tresh_adj,*spin_lin_rem_adj;
static GtkWidget *dolines_button,*legend_button,*denoising_button,*doerode_button,*forcebw_button;
static GtkWidget *infolabel_var, *infolabel_warn;
static GtkWidget *combo,*combo2,*combo3;


GimpPlugInInfo PLUG_IN_INFO = { NULL, NULL, query, run};


MAIN()


static void query (void)
{
  static GimpParamDef args[] =
  {
    { GIMP_PDB_INT32,"run-mode", "Run mode" },
    { GIMP_PDB_IMAGE,"image","Input image"  },
    { GIMP_PDB_DRAWABLE,"drawable","Input drawable" },
	{ GIMP_PDB_INT32,"Colors", "Color count" },
	{ GIMP_PDB_INT32,"Weight", "Color weight" },       	
	{ GIMP_PDB_INT32,"PWeight", "Pixel Weighting mode" },     
	{ GIMP_PDB_INT32,"Algorithm", "Algorithm" },      
 	{ GIMP_PDB_INT32,"BlackWhite", "Force Black and White color" },       
 	{ GIMP_PDB_INT32,"Legend", "Insert Legend" }, 
 	{ GIMP_PDB_INT32,"Denoise", "Denoise" }, 
 	{ GIMP_PDB_INT32,"SpotSize", "Spot size in px" }, 
  	{ GIMP_PDB_INT32,"DIterations", "Despotting iterations" }, 	
   	{ GIMP_PDB_INT32,"Erode", "Erode shapes" }, 	 	
	{ GIMP_PDB_INT32,"EIterations", "Eroding iterations" },  	
	{ GIMP_PDB_INT32,"DoLines", "Do lines instead of image" },     
	{ GIMP_PDB_FLOAT,"BrTresh", "Brightness treshold (for lines)" },      
	{ GIMP_PDB_FLOAT,"HueTresh", "Color treshold (for lines)" }, 	
	{ GIMP_PDB_INT32,"RemShort", "Remove lines shorter then n pixels" },    	
	{ GIMP_PDB_INT32,"Postprocess", "postprocess mode" }  

        	
        	
  };

  gimp_install_procedure (
    "plug-in-cartoonizer",
    "Color Reduced and Cartoonizer",
    "Color Reducer and Cartoonizer v. 0.8.5 Reduce number of colors in image, clean up fragmentation and/or add lines.",
    "Tibor Bamhor",
    "Copyright Tibor Bamhor",
    "2012",
    "_Cartoonizer",
    "RGB*",
    GIMP_PLUGIN,
    G_N_ELEMENTS (args), 0,
    args, NULL);

  gimp_plugin_menu_register ("plug-in-cartoonizer",
                             "<Image>/Filters/Artistic");
}

void static parse_bool(gint value, gint* cur_value,gint position) {
	if (value==-1) return;			
 	if (value>=0 && value <=1) *cur_value=value;
 	else {printf ("  ERROR in parsing non-int arguments: Incorrect argument %1d: %1d (expecting bool - 0/1). Fix the calling command and run anew.\n\n",position, value);exit(1);}
	} 
 	
void static parse_range_int(gint value, gint* cur_value,gint position,gint min, gint max){
	if (value==-1) return;
 	if (value>=min && value <=max) *cur_value=value;
 	else {printf ("  ERROR in parsing non-int arguments: Incorrect argument %1d: %1d (expecting int in range %1d - %1d). Fix the calling command and run anew.\n\n",position, value, min,max);exit(1);}
	}	

void static parse_range_float(gfloat value, gfloat* cur_value,gint position,gfloat min, gfloat max){
	if (value==-1) return;
 	if (value>=min && value <=max) *cur_value=value;
 	else {printf ("  ERROR in parsing non-int arguments:Incorrect argument %1d: %.3f (expecting float in range %.3f - %.3f). Fix the calling command and run anew.\n\n",position, value, min,max);exit(1);}
	}	

static void
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
  static GimpParam  values[1];
  GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  GimpRunMode       run_mode;
  GimpDrawable     *drawable; //<- original layer

	fflush(stdout);

  image_ID = param[1].data.d_image; //<- zistujem imageID
  if (VERBOSE) printf("Image ID is: %d\n", image_ID);  


  /* Setting mandatory output values */
  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type = GIMP_PDB_STATUS;
  values[0].data.d_status = status;

  /* Getting run_mode - we won't display a dialog if
   * we are in NONINTERACTIVE mode
   */
  run_mode = param[0].data.d_int32;

  // getting original drawable
  drawable = gimp_drawable_get (param[2].data.d_drawable);
   
    switch (run_mode) {
    	case GIMP_RUN_INTERACTIVE:
		   if (! gui_window (drawable)) return; //if it fails, plugin terminates
            break;

		case GIMP_RUN_NONINTERACTIVE:
			if (nparams != 19) {
				printf (" NON-INTERACTIVE: wrong number of parameters (%1d, expected: 18)",nparams-1);
				exit(1);}
			if (VERBOSE) printf (" Running non-interactive mode; parameters: %1d\n",nparams);  
 			
  			parse_range_int(param[3].data.d_int32,&maindata.colors,1,0,300); 			
  			parse_range_int(param[4].data.d_int32,&maindata.weight,2,1,20);			
 			parse_range_int(param[5].data.d_int32,&maindata.pwMode,3,0,4);
			parse_range_int(param[6].data.d_int32,&maindata.mode,4,0,5);
			parse_bool(param[7].data.d_int32,&maindata.forcebw,5);
 			parse_bool(param[8].data.d_int32,&maindata.legend,6);
 			parse_bool(param[9].data.d_int32,&maindata.doDenoise,7);
 			parse_range_int(param[10].data.d_int32,&maindata.spotSzTr,8,0,1000);
 			parse_range_int(param[11].data.d_int32,&maindata.spotIt,9,0,100);
 			parse_bool(param[12].data.d_int32,&maindata.doErode,10);
 			parse_range_int(param[13].data.d_int32,&maindata.erodeIt,11,0,30);
 			parse_bool(param[14].data.d_int32,&maindata.dolines,12);
 			parse_range_float(param[15].data.d_float,&maindata.linDiffTresh,13,0,1);
  			parse_range_float(param[16].data.d_float,&maindata.linhuetresh,14,0,1);
 			parse_range_int(param[17].data.d_int32,&maindata.rmLinSzTr,15,0,1000);
 			parse_range_int(param[18].data.d_int32,&maindata.postprMode,16,0,5);				

		
            break;

		//case GIMP_RUN_WITH_LAST_VALS:
            ///*  Get options last values if needed  */
            //gimp_get_data ("plug-in-colorblur", &satvals);
            //break;

		default:
            break;
        }
  //processing
  process (drawable, NULL);
 
  gimp_displays_flush ();
  gimp_drawable_detach (drawable);

	fflush (stdout );

  return;
}
	

void denoising_changed(void){
	maindata.doDenoise=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (denoising_button));
	if (VERBOSE) printf("denoising checkbutton changed %d\n",gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (denoising_button)) );
	}


void dolines_changed(void){
	maindata.dolines=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (dolines_button));
	if (VERBOSE) printf("dolines_changed %d\n",gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (dolines_button)) );
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (dolines_button))){
		gtk_widget_set_sensitive  (spin_lin_diff_tresh,TRUE);
		gtk_widget_set_sensitive  (spin_lin_hue_tresh,TRUE);
		gtk_widget_set_sensitive  (spin_lin_rem,TRUE);
		gtk_combo_box_set_active(GTK_COMBO_BOX( combo2), 0);}
	else {
		gtk_widget_set_sensitive  (spin_lin_diff_tresh,FALSE);
		gtk_widget_set_sensitive  (spin_lin_hue_tresh,FALSE);
		gtk_widget_set_sensitive  (spin_lin_rem,FALSE);}
	}


void doerode_changed(void){
	maindata.doErode=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (doerode_button));
	if (VERBOSE) printf("doerodes_changed %d\n",gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (doerode_button)) );
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (doerode_button))){
		gtk_widget_set_sensitive  (spin_erode_it,TRUE);
		}
	else {
		gtk_widget_set_sensitive  (spin_erode_it,FALSE);}
	}


void forcebw_changed(void){
	maindata.forcebw=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forcebw_button));
	if (VERBOSE) printf("Forced Black+White changed %d\n",gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forcebw_button)) );

	}


static void colors_warning(gboolean on) {
	static char labeltextwarn [70];
	gboolean debug=FALSE;
	
	if (debug) printf ("Setting the warning label: %1s\n",(on)?"ON":"OFF");
	
	if (on) {
	if (debug) printf (" WARNING: Number of subcolors exceeded, results will not be adequate...\n");
	sprintf(labeltextwarn, "WARNING: Too many colors!");
	gtk_widget_set_tooltip_text(infolabel_warn, "You set too many colors for this mode, result will not be adequate");
	gtk_label_set_label(GTK_LABEL(infolabel_warn),labeltextwarn ); }
	else {
	sprintf(labeltextwarn, " ");
	gtk_widget_set_tooltip_text(infolabel_warn, NULL);
	gtk_label_set_label(GTK_LABEL(infolabel_warn),labeltextwarn ); }		
	}

	
void legend_changed(void){
	maindata.legend=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (legend_button));
	if (VERBOSE) printf("legend_changed %d\n",gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (legend_button)) );
	}


void  cb_changed( GtkComboBox *combo, gpointer data ) {
    
    //changes blur mode (1- standard,2-selective,3-RGB selective)
    if ( strncmp( gtk_combo_box_get_active_text( combo ), mode1,11) == 0) {
    	maindata.mode=FAIR;}
    if ( strncmp((gtk_combo_box_get_active_text( combo )),mode2,11) == 0) {
    	maindata.mode=DITHER;}
    if ( strncmp((gtk_combo_box_get_active_text( combo )),mode3,11) == 0) {
    	maindata.mode=CAMO;}
    if ( strncmp((gtk_combo_box_get_active_text( combo )),mode4,11) == 0) {
    	maindata.mode=ENHANCED;}
    if ( strncmp((gtk_combo_box_get_active_text( combo )),mode5,11) == 0) {
    	maindata.mode=DITHER31;}
    //if ( strncmp((gtk_combo_box_get_active_text( combo )),mode6,11) == 0) {
    	//maindata.mode=DITHER31S;}
    if ( strncmp((gtk_combo_box_get_active_text( combo )),mode8,11) == 0) {
    	maindata.mode=ADAPTIVE;}

    	
    if (VERBOSE) printf( "cb_changed: Changing type of mask to %d\n",maindata.mode );
}


void  wmode_changed( GtkComboBox *combo3, gpointer data ) {
    
    //changes blur mode (1- standard,2-selective,3-RGB selective)
    if ( strncmp( gtk_combo_box_get_active_text( combo3 ), wmode1,5) == 0) {
    	maindata.pwMode=COUNT;}
    if ( strncmp((gtk_combo_box_get_active_text( combo3 )),wmode2,5) == 0) {
    	maindata.pwMode=BYSAT;}
    if ( strncmp((gtk_combo_box_get_active_text( combo3 )),wmode3,5) == 0) {
    	maindata.pwMode=BYNEIGH;}
    if ( strncmp((gtk_combo_box_get_active_text( combo3 )),wmode4,5) == 0) {
    	maindata.pwMode=BYHUE;}
    if ( strncmp((gtk_combo_box_get_active_text( combo3 )),wmode5,5) == 0) {
    	maindata.pwMode=MIX;}
    	
    if (VERBOSE) printf( "cb_changed: Changing type of mask to %d\n",maindata.mode );
}


void  post_changed( GtkComboBox *combo2, gpointer data ) {
    
    //changes blur mode (1- standard,2-selective,3-RGB selective)
    if ( strncmp( gtk_combo_box_get_active_text( combo2 ), post1,5) == 0) {
    	maindata.postprMode=NONE;}
    if ( strncmp((gtk_combo_box_get_active_text( combo2 )),post2,5) == 0) {
    	maindata.postprMode=PASTEL;
    	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dolines_button  ),FALSE);}
    if ( strncmp((gtk_combo_box_get_active_text( combo2 )),post3,5) == 0) {
    	maindata.postprMode=SATURATED;
    	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dolines_button  ),FALSE);}
    if ( strncmp((gtk_combo_box_get_active_text( combo2 )),post4,4) == 0) {
    	maindata.postprMode=GOLD;
    	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dolines_button  ),FALSE);}
    if ( strncmp((gtk_combo_box_get_active_text( combo2 )),post5,4) == 0) {
    	maindata.postprMode=DARKPASTEL;
    	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dolines_button  ),FALSE);}
    if ( strncmp((gtk_combo_box_get_active_text( combo2 )),post6,8) == 0) {
    	maindata.postprMode=PALETTE;
    	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dolines_button  ),FALSE);}
    if (VERBOSE) printf( "post_changed: Changing postprocessing mode to %d\n",maindata.postprMode );
}

static void get_weightdiff(gfloat rdiff,gfloat gdiff,gfloat bdiff, gfloat *negdiff,gfloat *posdiff){
	gint poscount=0,negcount=0;

	poscount+=rdiff>0;
	negcount+=rdiff<0;
	poscount+=gdiff>0;
	negcount+=gdiff<0;	
	poscount+=bdiff>0;
	negcount+=bdiff<0;

	if (negcount>1) *negdiff=maindata.fixeddiff/2.0;
	else *negdiff=maindata.fixeddiff;
	if (poscount>1) *posdiff=maindata.fixeddiff/2.0;	
	else *posdiff=maindata.fixeddiff;	
	//printf (" calculated diffs: %.4f %.4f\n", *negdiff,*posdiff);	
	
}

static void do_weight(gfloat r, gfloat g, gfloat b, gfloat *newr,gfloat *newg, gfloat *newb) {
	gfloat avg,rdiff,gdiff,bdiff;
	gfloat negdiff,posdiff;
			
	avg=get_brightness(r,g,b,SIMPLE);

	rdiff=r-avg;
	gdiff=g-avg;
	bdiff=b-avg;
	
	get_weightdiff(rdiff,gdiff,bdiff, &negdiff, &posdiff);

	if (rdiff<0) *newr=rdiff*maindata.weight + avg - negdiff;
	else if (rdiff>0) *newr=rdiff*maindata.weight + avg + posdiff;
	else *newr=r;
	if (gdiff<0) *newg=gdiff*maindata.weight + avg - negdiff;
	else if (gdiff>0) *newg=gdiff*maindata.weight + avg + posdiff;
	else *newg=g;
	if (bdiff<0) *newb=bdiff*maindata.weight + avg - negdiff;
	else if (bdiff>0) *newb=bdiff*maindata.weight + avg + posdiff;
	else if (bdiff>0) *newb=bdiff*maindata.weight + avg + posdiff;
	else *newb=b;	
	//printf (" Changing (avg: %.3f)  %.3f %.3f %.3f to %.3f %.3f %.3f, diffs: %.4f %.4f\n",avg, r,g,b,*newr,*newg,*newb,negdiff,posdiff);
}



static void fcolors_to_int(void) {
	gfloat r,g,b;
	gint i;
	
	for (i=0;i<maindata.colors;i=i+1){
		r=pow(primaryc[i].r,2.2) * 255;
		g=pow(primaryc[i].g,2.2) * 255;
		b=pow(primaryc[i].b,2.2) * 255;	
		primaryc[i].R=(int)MAX(MIN(ROUND(r),255),0);
		primaryc[i].G=(int)MAX(MIN(ROUND(g),255),0);
		primaryc[i].B=(int)MAX(MIN(ROUND(b),255),0);}
	}
		
	
static void get_gold_colors(){
	gfloat avg;
	gint i;
	
	for (i=0;i<maindata.colors;i+=1) {
		//undo_weight(derived[i].wr,derived[i].wg,derived[i].wb,&r,&g,&b);	
		avg=(primaryc[i].r+primaryc[i].g+primaryc[i].b)/3.0;
		primaryc[i].r =(1-avg)*0.62+avg*0.89+(primaryc[i].r-avg)*0.2;
		primaryc[i].g =(1-avg)*0.28+avg*0.85+(primaryc[i].g-avg)*0.2;
		primaryc[i].b =(1-avg)*0.0+avg*0.59+(primaryc[i].b-avg)*0.2;
		//if (VERBOSE) printf ("   Changing RGB values for color %1d to %.3f %.3f %.3f\n",i,r,g,b);
	}
}

inline gfloat findmaxfloat(gfloat a,gfloat b,gfloat c){
	return MAX(a,MAX(b,c));}

static void get_saturated_colors() {
	//we presume that rgb values contain final (ungammed) colors and we are going to change them
	gint i;
	gfloat sat,satboost,avg;
	
	//first we need average saturation
	for (i=0;i<maindata.colors;i+=1) {
		//undo_weight(base[i].wr,base[i].wg,base[i].wb,&r,&g,&b);
		avg=(primaryc[i].r+primaryc[i].g+primaryc[i].b)/3.0;
		sat=get_saturation(primaryc[i].r,primaryc[i].g,primaryc[i].b,SIMPLESAT);
		if (sat>0.25 || sat ==0) satboost=1;
		else {
			satboost=(pow((sat/0.25),0.5)*0.25)/sat;}

		//if (VERBOSE) printf ("  Saturation boost for color %1d: %.3f (current sat.: %.3f)\n",i,satboost,sat);
		primaryc[i].r=(primaryc[i].r-avg)*satboost + avg ;
		primaryc[i].g=(primaryc[i].g-avg)*satboost + avg ;
		primaryc[i].b=(primaryc[i].b-avg)*satboost + avg ;
		//if (VERBOSE) printf ("   New RGB values: %.3f %.3f %.3f\n",r,g,b);
		//show_fcolors();
	}}	

static gfloat get_diff(gfloat h_old,gfloat h_new,gfloat sat_old,gfloat sat_new,
	gfloat r_old, gfloat r_new, gfloat g_old, gfloat g_new, gfloat b_old, gfloat b_new,
	gfloat* huediff, gfloat* satdiff, gfloat* rgbdiff){

	gfloat upper_sat,bottom_sat;
	bottom_sat=(sat_old*0.15)/2.0;
	upper_sat=0.18;
	
	*rgbdiff=ABS(r_old-r_new) + ABS(g_old-g_new) + ABS(b_old-b_new);
	//*rgbdiff/=20.0;
	*huediff=MIN( ABS(h_old-h_new), 1-ABS(h_old-h_new) );
	if (*huediff>0.06) *huediff= 0.06 + (*huediff-0.06)*5;
	upper_sat=sat_old*2;
	if ( sat_new> upper_sat) *satdiff=(sat_new-upper_sat)*5;
	else if (sat_new < bottom_sat ) *satdiff=(bottom_sat-sat_new)*5;
	else *satdiff=0;
	return (*huediff+*satdiff+*rgbdiff);
	
}


static void get_cartoon_colors4() {
	gint Gnew,Bnew,Rnew;
	GimpRGB rgbcolor;
	GimpHSV hsvcolor;
	gboolean have_grey_col=FALSE;	
	gint i,j,k, l, cartooncolcount=0;
	gint non_grey_start;
	gfloat diff,mindiff,huediff,satdiff,rgbdiff;
	gint Rstepping=102;
	gint Bstepping=85;
	gint startpointR[2]={0,51};
	gint startpointB[2]={17,0};	
	gint bestcolor,colcount,duplicount;
	gboolean debug=FALSE;
	
	typedef struct {
	gfloat r;
	gfloat g;
	gfloat b;
	gfloat h;
	gfloat sat;
	gfloat br;
	gboolean grey;
	gint paired_to;
	gfloat handicap;
	} localcolor;

	localcolor loccartoon[400];	
	localcolor locprimary[MAXCOLORS];
		
	//GENERATING/COPYING primary colors

	for (i=0;i<maindata.colors;i+=1){
		rgbcolor.r=primaryc[i].r;
		rgbcolor.g=primaryc[i].g;
		rgbcolor.b=primaryc[i].b;

		gimp_rgb_to_hsv (&rgbcolor,&hsvcolor);
		locprimary[i].h=hsvcolor.h;
		locprimary[i].r=primaryc[i].r;
		locprimary[i].g=primaryc[i].g;
		locprimary[i].b=primaryc[i].b;
		locprimary[i].br=get_brightness(primaryc[i].r,primaryc[i].g,primaryc[i].b,WEB);
		locprimary[i].sat=get_saturation(primaryc[i].r,primaryc[i].g,primaryc[i].b,SIMPLESAT);
		locprimary[i].paired_to=-1;
		//locprimary[i].handicap=1;
		if   (hsvcolor.s<=0.03) {
			locprimary[i].grey=TRUE;
			have_grey_col=TRUE;
			locprimary[i].h=-100;
			locprimary[i].br=0.3+locprimary[i].br*0.7; // eliminating dark colors
			}
		else locprimary[i].grey=FALSE;
		//printf ("Primary color %2d: %.3f %.3f %.3f; h: %.3f; sat: %.3f\n",i,locprimary[i].r,locprimary[i].g,locprimary[i].b,locprimary[i].h,locprimary[i].sat);
		}
	//calculating
	
	//MAPPING GREY COLORS
	if (have_grey_col) {
		for (i=2;i<17;i+=1){
			Gnew=i*16-1;     //Gnew used by chance
			if (Gnew<0) Gnew=0;
			loccartoon[cartooncolcount].r=gammed_values[Gnew];
			loccartoon[cartooncolcount].g=gammed_values[Gnew];
			loccartoon[cartooncolcount].b=gammed_values[Gnew];		
			loccartoon[cartooncolcount].h=0;
			loccartoon[cartooncolcount].paired_to=-1;
			loccartoon[cartooncolcount].br=gammed_values[Gnew];
			loccartoon[cartooncolcount].grey=TRUE;
			//printf ( "New cartoon color: %3d %3d %3d; h: %.3f;sat: %.3f  (id: %3d)\n",
			// Gnew,Gnew,Gnew,loccartoon[cartooncolcount].h,loccartoon[cartooncolcount].sat  ,cartooncolcount);	
			cartooncolcount+=1;}	
		for (i=0;i<maindata.colors;i+=1){
			if (locprimary[i].grey ==FALSE) continue;
			//printf ("testing primary color: %1d\n",i);
			mindiff=1000;
			bestcolor=-1;
			for (j=0;j<cartooncolcount;j+=1) {
				if (loccartoon[j].grey == FALSE) continue;
				diff=ABS(locprimary[i].br - loccartoon[j].br);
				if (diff<mindiff) {mindiff=diff;bestcolor=j;}}
					
			if (bestcolor ==-1) {printf (" ERROR -  no (gray) bestcolor was selected...\n"); exit(1);}
			//if bestcolor has been allocated to other primary color
			locprimary[i].paired_to=bestcolor;
			}
		}
	
	//GENERATING NON-GREY COLORS
	non_grey_start=cartooncolcount;
	for (l=0;l<2;l+=1){ // two startpoints
		for (i=0;i<5;i+=1) { for (j=0;j<5;j+=1){
			Rnew=startpointR[l]+Rstepping*i;
			Bnew=startpointB[l]+Bstepping*j;
			if (Bnew>255 ||Rnew>255) continue;
			if (Rnew==Bnew) continue;
			//generating G channel values
			for (k=0;k<17;k+=1){
				Gnew=k*32-1;
				if (Gnew>255) break;
				if (Gnew<0) Gnew=0;
				if (Rnew==Gnew) continue;
				rgbcolor.r=gammed_values[Rnew];
				rgbcolor.g=gammed_values[Gnew];
				rgbcolor.b=gammed_values[Bnew];
				gimp_rgb_to_hsv (&rgbcolor,&hsvcolor);
				loccartoon[cartooncolcount].r=rgbcolor.r;
				loccartoon[cartooncolcount].g=rgbcolor.g;
				loccartoon[cartooncolcount].b=rgbcolor.b;
				loccartoon[cartooncolcount].h=hsvcolor.h;
				loccartoon[cartooncolcount].paired_to=-1;
				loccartoon[cartooncolcount].br=get_brightness(rgbcolor.r,rgbcolor.g,rgbcolor.b,WEB);
				loccartoon[cartooncolcount].sat=get_saturation(rgbcolor.r,rgbcolor.g,rgbcolor.b,SIMPLESAT);
				loccartoon[cartooncolcount].handicap=1;
				if (debug) printf ( "New cartoon color: %3d %3d %3d; h: %.3f;sat: %.3f  (id: %3d)\n",
					Rnew,Gnew,Bnew,loccartoon[cartooncolcount].h,loccartoon[cartooncolcount].sat  ,cartooncolcount);	
				cartooncolcount+=1;			
				}
			}}
		}

	//MAPPING NON-GREY COLORS
	for (k=0;k<30;k+=1) {
		if (debug) printf (" Re/maping nongrey colors (it:%2d)\n",k+1);
		for (i=0;i<maindata.colors;i+=1){
			if (locprimary[i].grey) continue;
			if (k>0 && loccartoon[locprimary[i].paired_to].handicap==1) continue;
			//printf ("Debug - doing primary color :%1d, pointing to: %2d\n", i,locprimary[i].paired_to);
			mindiff=1000;
			bestcolor=-1;
			for (j=non_grey_start;j<cartooncolcount;j+=1) {
				diff=get_diff(locprimary[i].h,loccartoon[j].h,locprimary[i].sat,loccartoon[j].sat,
					locprimary[i].r,loccartoon[j].r, locprimary[i].g,loccartoon[j].g,locprimary[i].b,loccartoon[j].b,
					&huediff,&satdiff,&rgbdiff);
				//if (k>0)
				diff*=loccartoon[j].handicap;
				//printf ("  diff: %.3f\n",diff);
				if (diff<mindiff) {	mindiff=diff;bestcolor=j;}}
					
			if (bestcolor ==-1) {printf ("  ERROR -  no (non-grey) bestcolor was selected(%1d,%1d)...\n",k,i); exit(1);}
			
			//if bestcolor is other than currently assigned cartoon color
			if (locprimary[i].paired_to!=bestcolor) {
				locprimary[i].paired_to=bestcolor;
				if (debug) printf ("   mappping color %2d to %2d (mindiff: %3f)\n", i,bestcolor,mindiff);
				}
			}
			
		//setting handicap for colors that occurs more times
		if (debug) printf ("  Calculating handicap...\n");
		duplicount=0;
		for (i=non_grey_start;i<cartooncolcount;i+=1){
			colcount=0;
			for (j=0;j<maindata.colors;j+=1){
				if (locprimary[j].paired_to==i) colcount+=1;}
			//printf ("  colcount of %3d: %2d\n",i,colcount);
			if (colcount==0)loccartoon[i].handicap=MAX(0.96*loccartoon[i].handicap,1);
			if (colcount>1) loccartoon[i].handicap*=1.15;	
			if (debug && colcount>1) printf ("  DEBUG: color %3d occurs %2d times, new handicap: %.3f\n", 
				i,colcount,loccartoon[i].handicap);	
			if (colcount>1) duplicount+=colcount-1;	
			 }
		if (debug) printf ("   (%2d non-unique colors found....)\n", duplicount);	
		if (duplicount==0) break;	
			
	}	

	for (i=0;i<maindata.colors;i+=1){
		//changing primaryc colors
		//printf ("Changing primaryc %2d: %.2f %.2f %.2f -> %.2f %.2f %.2f\n",
		//	i,primaryc[i].r, primaryc[i].g, primaryc[i].b ,loccartoon[locprimary[i].paired_to].r,
		//	loccartoon[locprimary[i].paired_to].g,loccartoon[locprimary[i].paired_to].b);
		diff=get_diff(locprimary[i].h,loccartoon[locprimary[i].paired_to].h,locprimary[i].sat,loccartoon[locprimary[i].paired_to].sat,
			locprimary[i].r,loccartoon[locprimary[i].paired_to].r, locprimary[i].g,loccartoon[locprimary[i].paired_to].g,locprimary[i].b,loccartoon[locprimary[i].paired_to].b,
			&huediff,&satdiff,&rgbdiff);
		if (debug) printf (" Diffs for color %2d: hue:  %.2f, sat: %.2f, rgb: %.2f; overall: %.2f\n", i, huediff,satdiff,rgbdiff,diff);
		if (debug && diff > 0.5 ) printf ("  Above color was changed from %2d: %.2f %.2f %.2f -> %.2f %.2f %.2f\n",
			i,primaryc[i].r, primaryc[i].g, primaryc[i].b ,loccartoon[locprimary[i].paired_to].r,
			loccartoon[locprimary[i].paired_to].g,loccartoon[locprimary[i].paired_to].b);
		primaryc[i].r=loccartoon[locprimary[i].paired_to].r;
		primaryc[i].g=loccartoon[locprimary[i].paired_to].g;	
		primaryc[i].b=loccartoon[locprimary[i].paired_to].b;
		}


}


static void get_pastel_colors(gfloat avgoffset,gfloat avgrange) {
	//we presume that rgb values contain final (ungammed) colors and we are going to change them
	gint i;
	gfloat r,g,b,newr,newb,newg,oldavg,newavg,oldspread, newspread,oldRdiff,oldGdiff,oldBdiff,spreadratio;
	gboolean debug=FALSE;

	
	for (i=0;i<maindata.colors;i+=1) {
		r=primaryc[i].r;
		g=primaryc[i].g;
		b=primaryc[i].b;
		oldavg=get_brightness(r,g,b,SIMPLE);
		oldspread=MAX(r,MAX(g,b))-MIN(r,MIN(g,b));

		newavg=oldavg*avgrange + avgoffset;
		newspread=oldspread*0.10 + 0.20;
		if (oldspread>0) spreadratio=newspread/oldspread;
		else 			 spreadratio=0;
		if(debug) printf ("postprocessing: oldspread: %.3f, new spread: %.3f\n",oldspread,newspread);
				
		oldRdiff=r-oldavg;
		oldGdiff=g-oldavg;
		oldBdiff=b-oldavg;
		newr=newavg+oldRdiff*spreadratio;
		newg=newavg+oldGdiff*spreadratio;
		newb=newavg+oldBdiff*spreadratio;
		if(debug) printf ("  Changing %3.3f, %3.3f %3.3f with avg %.3f to %3.3f, %3.3f %3.3f with avg %.3f\n",r,g,b,oldavg,newr,newg,newb,newavg);


		primaryc[i].r=newr;
		primaryc[i].g=newg;
		primaryc[i].b=newb;
		}
	}
	

void exportwrapper (GimpDrawable *source){ 
	maindata.exportlayer=TRUE;
	process (source,NULL);
	maindata.exportlayer=FALSE;	}


inline gint get_basepos(gint x, gint y,gint ch, gint width){
	return ch*y*width + ch*x;}

	
inline gint get_basepos2(gint x, gint y, gint width){
	return y*width + x;}


static void denoising(gint firsttresh,gint secondtresh,gint maxiter,gboolean strict) {
	gint col,row,i;
	gint nearx[8]={0,1,1,1,0,-1,-1,-1};
	gint neary[8]={1,1,0,-1,-1,-1,0,1};
	gint *occ;    //[maindata.colors];
	gint centralcolor, nearcolor;
	gint sumdiff,iter;
	gint substitute,maxocc;
	gboolean debug=FALSE;
	gint changed;
	gint localtresh;
	
	gboolean *dirty,*newdirty;
	
	dirty=g_new(gboolean,warea.width*warea.height);
	newdirty=g_new(gboolean,warea.width*warea.height);
	for (i=0;i<warea.width*warea.height;i+=1) { dirty[i]=TRUE;}//setting initial values
	
	occ=g_new(gint, maindata.effective);
	
	for (iter=0;iter<maxiter;iter=iter+1){	
		
		if (i<=1) localtresh=firsttresh;
		else      localtresh=secondtresh;

		for (i=0;i<warea.width*warea.height;i+=1) {	newdirty[i]=FALSE;} //resetting newdirty
		
		changed=0;
		
		for (col=1;col<warea.width-1;col=col+1) {for (row=1;row<warea.height-1;row=row+1){
			
			//skip the pixel if not dirty
			if (dirty[get_basepos2(col,row,warea.width)]==FALSE) continue;
			
			for (i=0;i<maindata.effective;i=i+1) occ[i]=0; //resetting occurences
			sumdiff=0;
			
			centralcolor=rect_indx[get_basepos2(col,row,warea.width)];
			for (i=0;i<8;i=i+1) {
				nearcolor = rect_indx[get_basepos2(col+nearx[i],row+neary[i],warea.width)];
				if (centralcolor == nearcolor) continue;
				occ[nearcolor]=occ[nearcolor]+1;
				sumdiff=sumdiff+1;
				}
			
			//if (debug) printf ("sumdiff: %.i\n",sumdiff);
			
			if (sumdiff<localtresh) continue;
			
			if (debug) printf ("sumdiff: %.i\n",sumdiff);
			
			//so we are going to change the pixel
			//search for most frequent colors
			maxocc=0;
			
			//finding most frequent color in near pixels
			for (i=0;i<maindata.effective;i=i+1) {if (occ[i]>maxocc) {maxocc=occ[i]; substitute=i;}}
						
			if (strict && maxocc<localtresh) continue; // used by erode
			
			if (debug) printf ("New color: %1d\n",substitute);
			
			rect_indx[get_basepos2(col,row,warea.width)]=substitute;
			changed=changed+1;
			
			//setting pixels in newdirty as dirthy
			newdirty[get_basepos2(col,row,warea.width)]=TRUE;
			for (i=0;i<8;i=i+1) newdirty[get_basepos2(col+nearx[i],row+neary[i],warea.width)]=TRUE;
			
		}}
		if (VERBOSE) printf (" Denoising: changed pixels: %0d (iteration: %.i)\n",changed,iter+1);
		if (changed<1) break;
		
		//pushing content of new dirty to dirty
		for (i=0;i<warea.width*warea.height;i+=1)  dirty[i]=newdirty[i];
		}
}


static void indexing(){
	//puts base colors into rect_indx
	gint col,row,count,i;
	gfloat r,g,b;
	gint basepos;
	gint pos;
	gfloat dist;
	gboolean debug;
	debug=FALSE;
	gint xpos[4]={0,0,1,1};
	gint ypos[4]={0,1,0,1};
	
	if (maindata.mode==DITHER ||maindata.mode==DITHER31||maindata.mode==ADAPTIVE ) {
		for (col=0;col<warea.width;col=col+2) {for (row=0;row<warea.height;row=row+2){
			r=0;g=0;b=0;count=0;
			for (i=0;i<4;i+=1) {
				if ( (col+xpos[i])>=warea.width || (row+ypos[i])>=warea.height ) continue;
				basepos=get_basepos(col+xpos[i],row+ypos[i],maindata.channels,warea.width);			
				r=r + gammed_values[rect_wrk[basepos  ]];
				g=g + gammed_values[rect_wrk[basepos+1]];
				b=b + gammed_values[rect_wrk[basepos+2]];
				count+=1;}
			r=r/count;
			g=g/count;
			b=b/count;
			do_weight(r,g,b,&r,&g,&b);
			find_best2(r,g,b,&pos,&dist,maindata.mode);
			if (debug && ( pos<0 || pos>=maindata.effective) ) {
				printf (" Out of range index: %1d - range (0 - %1d)\n", pos,maindata.effective);
				exit(1);}
			for (i=0;i<4;i+=1) {
				if ( (col+xpos[i])>=warea.width || (row+ypos[i])>=warea.height ) continue;
				basepos=get_basepos2(col+xpos[i],row+ypos[i],warea.width);	
				rect_indx[basepos]=pos;}
		}}
		}
	
	else {
		for (col=0;col<warea.width;col=col+1) {for (row=0;row<warea.height;row=row+1){
			basepos=get_basepos(col,row,maindata.channels,warea.width);
			//basepos2=get_basepos2(col,row,warea.width);
			r=gammed_values[rect_wrk[basepos  ]];
			g=gammed_values[rect_wrk[basepos+1]];
			b=gammed_values[rect_wrk[basepos+2]];
			do_weight(r,g,b,&r,&g,&b);
			//finding best color from fcolors
			find_best2(r,g,b,&pos,&dist,maindata.mode);
			/*if (debug && col%200==0 && row%200==0) printf (" pixel %.i - converting to fcolor: %.i (=%.3f,%.3f,%.3f)\n",\
			basepos2,pos,base[pos].r,base[pos].g,base[pos].b);*/
			rect_indx[get_basepos2(col,row,warea.width)]=pos;
		}}
	}

}
		
static void deindexing(){
	//rect_indx contain base color for all modes, except for DITHER where it contains derived pos
	gint col,row,eff_color,prim_color;
	gint basepos,basepos2,parentpos=0;
	//static char labeltextprc [70];
	gboolean debug=FALSE;

	if (debug) printf ("Deindexing started...");	


	fcolors_to_int();  //to be sure
	
	for (col=0;col<warea.width;col=col+1) {for (row=0;row<warea.height;row=row+1){
		if (debug) printf (" Col: %1d, row: %1d\n",col,row);
		basepos2=get_basepos2(col,row,warea.width);
		basepos=get_basepos(col,row,maindata.channels,warea.width);
		eff_color=rect_indx[basepos2];
		if (debug && (eff_color>= maindata.effective || eff_color<0) )
			printf ("Effective color index out of range: %.i (0 - %1d)\n",  eff_color,maindata.effective);

		if (debug) printf (" DEBUG2\n");
		
		if (maindata.mode==DITHER||maindata.mode==DITHER31) {
			parentpos=row%2*2 + col%2;}
		else parentpos=0;
		
		prim_color=effectivec[eff_color].parent[parentpos];
		if (debug) printf (" DEBUG3\n");		
		//doing statistics
		primaryc[prim_color].count=primaryc[prim_color].count +1;	
		if (debug) printf (" DEBUG3a\n");			
		if (debug && (prim_color>= maindata.colors || prim_color<0) )
			printf ("Primary color index out of range: %.i (0 - %1d)\n",  prim_color,maindata.effective);
		if (debug) printf (" DEBUG3b\n");			
		rect_out[basepos  ]=primaryc[prim_color].R;
		rect_out[basepos+1]=primaryc[prim_color].G;
		rect_out[basepos+2]=primaryc[prim_color].B;	
		if (debug) printf (" DEBUG4\n");
		//if (debug && col%100==0 && row%100==0) printf (" deindex: Entering %1d: %1d %1d %1d\n",
			//prim_color,rect_out[basepos  ],rect_out[basepos+1],rect_out[basepos+2]);
		if (debug && (rect_out[basepos] >255 || rect_out[basepos]<0)) printf ("Out of 0-255 range)\n");
		if (debug && (rect_out[basepos+1] >255 || rect_out[basepos+1]<0)) printf ("Out of 0-255 range)\n");
		if (debug && (rect_out[basepos+2] >255 || rect_out[basepos+2]<0)) printf ("Out of 0-255 range)\n");	
		if (debug && (parentpos<0 || parentpos>4)) 	 printf ("parentpos out of 0-255 range !\n");		
		if (maindata.channels==4)rect_out[basepos+3]=rect_wrk[basepos+3];
		}}
		
	//statistic:
	//gint sum=0,least=100000000;
	//for (i=0;i<maindata.colors;i+=1) {
		//sum=primaryc[i].count +sum;
		//if (primaryc[i].count<least) least=primaryc[i].count;}
		
	//sprintf(labeltextprc, "Least color freq:  %.2f%%",(gfloat)least*100/sum);
	//gtk_label_set_label(GTK_LABEL(infolabel_prc),labeltextprc ); 
	if (debug) printf ("Deindexing done...");	
	}


static void insert_legend(){
	//inserting legend (samples of colors)
	gint borderwidth,samplesize,legendheight,legendwidth,offset,rowstart,rowend;
	gint tmp;
	gint i,j;
	gint col,row;
	gint basepos;
	gint color;
	gint *sortedcolors,*colorsfreq;
	gboolean debug=FALSE;
	
	offset=image.width*0.01;
	tmp=(image.width*0.8)/maindata.colors;
	if (tmp>image.width*0.04) tmp=image.width*0.04; //limiting the size of legend
	samplesize=tmp*0.7;
	borderwidth=tmp-samplesize;
	legendheight=2*borderwidth+samplesize;
	legendwidth=borderwidth*(1+maindata.colors)+maindata.colors*samplesize;
	rowstart=image.height-legendheight-offset;
	rowend=image.height-offset;
	
	//getting order of colors
	sortedcolors=g_new(gint,maindata.colors);
	colorsfreq  =g_new(gint,maindata.colors);
	
	for (i=0;i<maindata.colors;i+=1) {
		sortedcolors[i]=i;
		colorsfreq[i]=primaryc[i].count;}
		
	for (i=0;i<maindata.colors;i+=1){
		for (j=0;j<maindata.colors-1;j+=1)
			if (colorsfreq[j] < colorsfreq[j+1]) {
				tmp=colorsfreq[j+1];colorsfreq[j+1]=colorsfreq[j];colorsfreq[j]=tmp;
				tmp=sortedcolors[j+1];sortedcolors[j+1]=sortedcolors[j];sortedcolors[j]=tmp;}}
	//printing details
	if (VERBOSE) for (i=0;i<maindata.colors;i+=1) printf ("  Adding color %2d (occ: %8d) to legend\n", sortedcolors[i],colorsfreq[i]);
	
	if (debug) printf (" Samplesize=%.i; borderwidth=%.i,legend size= %.ix%.i\n",samplesize,borderwidth,borderwidth,legendwidth);
	for (col=offset;col<offset+legendwidth;col=col+1) {for (row=rowstart;row<rowend;row=row+1) {
		basepos=get_basepos(col,row,maindata.channels,image.width);
		//bottom strip
		if (row<rowstart+borderwidth || row>rowend-borderwidth) {
			if (debug) printf("  Adding white to %.i x %.i (%.i)\n",col,row,basepos);
			rect_out[basepos  ]=255;
			rect_out[basepos+1]=255;
			rect_out[basepos+2]=255;}	
		else if ((col-offset)%(samplesize+borderwidth)<borderwidth) {
			rect_out[basepos  ]=255;
			rect_out[basepos+1]=255;
			rect_out[basepos+2]=255;}
		else {color=(col-offset)/(samplesize+borderwidth);
			rect_out[basepos  ]=primaryc[sortedcolors[color]].R;
			rect_out[basepos+1]=primaryc[sortedcolors[color]].G;
			rect_out[basepos+2]=primaryc[sortedcolors[color]].B;}
	}}			
				
}


static gfloat get_linedist(gfloat wr,gfloat wg,gfloat wb,gint ecolor){
	//finding distance of sample to virtual line belonging to effective color
	
	gfloat minlindist=1000,curlindist,dist;
	gint i,bestpos;
	static gint debugcount=0;
	gboolean debug=FALSE;
	
	//printf (" function get_linedist broken\n");
	//exit(1);
	
	for (i=0;i<effectivec[ecolor].steps+1;i+=1){
		curlindist=	ABS(wr-effectivec[ecolor].step_r[i]) +
					ABS(wg-effectivec[ecolor].step_g[i]) +
					ABS(wb-effectivec[ecolor].step_b[i]);
		if (curlindist<minlindist) {
			minlindist=curlindist;
			bestpos=i;}
		if (minlindist < curlindist) break;
		}		
	
	dist=	(effectivec[ecolor].step_r[bestpos]-wr)*(effectivec[ecolor].step_r[bestpos]-wr)+
			(effectivec[ecolor].step_g[bestpos]-wg)*(effectivec[ecolor].step_g[bestpos]-wg)+
			(effectivec[ecolor].step_b[bestpos]-wb)*(effectivec[ecolor].step_b[bestpos]-wb)  ;
	if (debug && debugcount%10000==0 && effectivec[ecolor].steps >3)
		printf ("  Closest position on line: %1d/%1d, dist: %.3f\n",
		bestpos,effectivec[ecolor].steps,dist);
	debugcount+=1;
	return dist;
	}



static void find_best2(gfloat wr,gfloat wg,gfloat wb,gint *pos,gfloat *dist,gint mode){
	//return index color (effective color)
	//input - weighted r,g,b
	gint i;
	gfloat disttmp;
	//gboolean debug=FALSE;
	
	*dist=100000;

	for (i=0;i<maindata.effective;i=i+1){
		if (mode == FAIR) 
			disttmp=(wr-effectivec[i].wr)*(wr-effectivec[i].wr) + (wg-effectivec[i].wg)*(wg-effectivec[i].wg) + (wb-effectivec[i].wb)*(wb-effectivec[i].wb);
		else
			disttmp=get_linedist(wr,wg,wb,i);
			
		if (disttmp<*dist) {
			*dist=disttmp;
			*pos=i;}
		}

	//*pos=derivedc[*pos].parent[0];
	*dist=pow(*dist,0.5);
	}

		
static void show_fcolors(gboolean forceprint) {
	gint i;
	gint checksum=0;
	gfloat checksumfl=0;
	//gfloat mindist;
	
	if (VERBOSE==FALSE && DOCHECKSUMS==FALSE && forceprint==FALSE) return;
	
	for (i=0;i<maindata.colors;i=i+1){
		//mindist=find_least_dist(i);
		if (VERBOSE || forceprint) printf (" Color %2d: %.2f,%.2f,%.2f (%.2f,%.2f,%.2f) => %3d,%3d,%3d. Sat.: %.3f Occurence: %8d \n"
			,i,primaryc[i].r, primaryc[i].g,primaryc[i].b,
			primaryc[i].wr, primaryc[i].wg,primaryc[i].wb,
			primaryc[i].R,primaryc[i].G,primaryc[i].B, get_saturation(primaryc[i].r,
			primaryc[i].g,primaryc[i].b,SIMPLESAT),primaryc[i].count );	
		if (DOCHECKSUMS) {
			checksum+=(i+1.0)*3.0*(primaryc[i].R + 2.0*primaryc[i].G+3.0*primaryc[i].B);
			checksumfl+=(i+1.0)*3.0*(primaryc[i].r + 2.0*primaryc[i].g+3.0*primaryc[i].b);
			checksumfl+=(i+4.0)*3.0*(effectivec[i].wr + 2.0*effectivec[i].wg+3.0*effectivec[i].wb);			
			}
		}
	if (DOCHECKSUMS) printf (" show_fcolors checksums: %10.3f %10d\n",checksumfl,checksum);
	}

static void temp_change_col(int channel, int color, float change){
	if (channel==0) origcolor.value = primaryc[color].r;
	if (channel==1) origcolor.value = primaryc[color].g;
	if (channel==2) origcolor.value = primaryc[color].b;
	origcolor.channel=channel;
	origcolor.color=color;
	if (channel==0) primaryc[color].r +=change;
	if (channel==1) primaryc[color].g +=change;
	if (channel==2) primaryc[color].b +=change;
	}


static void tmp_color_reject(){
	if (origcolor.channel==0) primaryc[origcolor.color].r=origcolor.value;
	if (origcolor.channel==1) primaryc[origcolor.color].g=origcolor.value;
	if (origcolor.channel==2) primaryc[origcolor.color].b=origcolor.value;}	


static float get_brightness(gfloat r,gfloat g,gfloat b,gint type) {
	if (type==SIMPLE) return 0.33*r+0.34*g+0.33*b;
	if (type==WEB) return 0.299*r + 0.587*g + 0.114*b; 
	else printf ("Unknow brightness type\n");
	return 0;}
	
	
static float get_saturation(gfloat r,gfloat g,gfloat b,gint type) {
	gfloat avg;
	if (type==QUADR) { 
		avg=0.241*pow(r,2.0)+0.691*pow(g,2.0)+0.068*pow(b,2.0);
		avg=CLAMP(avg,0,1);
		avg=pow(avg,0.5);}
	else 	avg=get_brightness(r,g,b,SIMPLE);
	return (ABS(avg-r) + ABS(avg-g) + ABS(avg-b))/3.0;}


static gboolean get_dither_colors(){
	gint i,j,k,l,pos;
	gfloat dist_to_avg;
	gfloat avgri,avggi,avgbi;
	gfloat avgrj,avggj,avgbj;
	gfloat avgrk,avggk,avgbk;	
	gboolean debug=FALSE;
	//gfloat r,g,b;
						
	pos=0;
	for (i=0;i<maindata.colors;i+=1) {
		avgri=primaryc[i].r;avggi=primaryc[i].g;avgbi=primaryc[i].b;
		//avgr=avgri;avgg=avggi;avgb=avgbi;
		if (debug) printf (" i (%1d) accepted by default\n",i);
		if (debug) printf (" Starting values/avg: %.3f %.3f %.3f\n",avgri,avgbi,avgbi);		
		for (j=0;j<maindata.colors;j+=1) {
			if (j<i) continue;
			if (maindata.mode==DITHER31 && (!(i==j))) continue;
			dist_to_avg=pow((primaryc[j].r - avgri) * (primaryc[j].r - avgri) +
							(primaryc[j].g - avggi) * (primaryc[j].g - avggi) +
							(primaryc[j].b - avgbi) * (primaryc[j].b - avgbi) , 0.5);
			//printf ("  Calculated dist: %.3f (color %.3f, %.3f %.3f)\n",
				//dist_to_avg,primaryc[j].r,primaryc[j].g,primaryc[j].b);
			if (dist_to_avg> maindata.maxdiff/2) continue;
			avgrj=(avgri+primaryc[j].r)/2.0;avggj=(avggi+primaryc[j].g)/2.0;avgbj=(avgbi+primaryc[j].b)/2.0;
			if (debug) printf ("  New averages: %.3f %.3f %.3f\n",avgrj,avgbj,avgbj);
			if (debug) printf ("  j (%1d) is good\n",j);
					
			for (k=0;k<maindata.colors;k+=1) {
				if (k<j) continue;				
				if (maindata.mode==DITHER31 && (!(i==j))) continue;
				dist_to_avg=pow((primaryc[k].r - avgrj) * (primaryc[k].r - avgrj) +
								(primaryc[k].g - avggj) * (primaryc[k].g - avggj) +
								(primaryc[k].b - avgbj) * (primaryc[k].b - avgbj) , 0.5);
				if (dist_to_avg> maindata.maxdiff/2) continue;
				avgrk=(avgrj*2.0+primaryc[k].r)/3;avggk=(avggj*2.0+primaryc[k].g)/3;avgbk=(avgbj*2.0+primaryc[k].b)/3;
				if (debug) printf ("   New averages: %.3f %.3f %.3f\n",avgrk,avgbk,avgbk);
				if (debug) printf ("   k (%1d) is good\n",k);				
				
				for (l=0;l<maindata.colors;l+=1) {
					if (l<k) continue;
					dist_to_avg=pow((primaryc[l].r - avgrk) * (primaryc[l].r - avgrk) +
									(primaryc[l].g - avggk) * (primaryc[l].g - avggk) +
									(primaryc[l].b - avgbk) * (primaryc[l].b - avgbk) , 0.5);
					if (debug) printf ("    dist_toavg: %.3f (max: %.3f)\n",dist_to_avg,maindata.maxdiff/2);
					if (dist_to_avg> maindata.maxdiff/2) continue;					
					if (debug) printf ("     l*(%1d) is good\n",l);										
					
					effectivec[pos].wr=(primaryc[i].wr + primaryc[j].wr + primaryc[k].wr + primaryc[l].wr)/4.0;	
					effectivec[pos].wg=(primaryc[i].wg + primaryc[j].wg + primaryc[k].wg + primaryc[l].wg)/4.0;	
					effectivec[pos].wb=(primaryc[i].wb + primaryc[j].wb + primaryc[k].wb + primaryc[l].wb)/4.0;	
					effectivec[pos].parent[0]=i;
					effectivec[pos].parent[1]=j;
					effectivec[pos].parent[2]=k;
					effectivec[pos].parent[3]=l;
					effectivec[pos].parents=4;
					pos+=1;
					if (pos>=MAXEFFECTIVE) {maindata.effective=pos;return TRUE;}
					if (debug) printf (" Eff. color %1d accepted - parrents: %1d %1d %1d %1d\n",pos,i,j,k,l);
					}}}}
	if (pos==0) {printf (" ERROR: no derived colors generated\n");exit (1);}
	if (debug) printf ("Got %1d derived colors\n",pos);
	maindata.effective=pos;
	return FALSE;
}

static void populate_linedistdata(gfloat startrange,gint type){
	//For camo end enhanced mode each effective color has up to ten  sustitutes, that has to pretend
	//that we are seeking distance to a line not a single point (effective color)
	gfloat end_wr,end_wg,end_wb,dist,avg,avg_wr=0,avg_wb=0,avg_wg=0;
	gint i,j,steps;
	gboolean debug=FALSE;
	
	//for CAMO we draw virtual line from effective color to average of all effective colors
	if (type==CAMO) {
		for (i=0;i<maindata.effective;i+=1) {
			avg_wr=avg_wr+effectivec[i].wr;
			avg_wg=avg_wg+effectivec[i].wg;
			avg_wb=avg_wb+effectivec[i].wb;}
		avg_wr=avg_wr/maindata.effective;			
		avg_wg=avg_wg/maindata.effective;	
		avg_wb=avg_wb/maindata.effective;
		}	

				
	for (i=0;i<maindata.effective;i+=1) {

		avg=get_brightness(effectivec[i].wr,effectivec[i].wg,effectivec[i].wb,SIMPLE);
		if (type==SATONLY){
			avg_wr=avg;avg_wg=avg;avg_wb=avg;}
		else if (type==CAMO) {
			avg_wr=avg_wr*0.35 + avg*0.65;
			avg_wb=avg_wb*0.35 + avg*0.65;
			avg_wg=avg_wg*0.35 + avg*0.65;}
			
		end_wr=effectivec[i].wr*startrange + avg_wr*(1-startrange);
		end_wg=effectivec[i].wg*startrange + avg_wg*(1-startrange);
		end_wb=effectivec[i].wb*startrange + avg_wb*(1-startrange);
		
		//calculating length of virtual line and dividing into points
		dist= pow( (effectivec[i].wr-end_wr)*(effectivec[i].wr-end_wr) + 
			(effectivec[i].wg-end_wg)*(effectivec[i].wg-end_wg) + 
			(effectivec[i].wb-end_wb)*(effectivec[i].wb-end_wb) ,0.5);
		steps=dist/0.02;
		if (steps>9) steps=9;
		effectivec[i].steps=steps;
		
		if (debug) printf ("Distance for linedistdata: %.3f - steps: %1d (dcolor: %2d)\n",dist,steps,i);
		if (debug) printf (" Range: %.3f %.3f %.3f to %.3f %.3f %.3f\n",effectivec[i].wr,effectivec[i].wg,
			effectivec[i].wb,end_wr,end_wg,end_wb);
		
		effectivec[i].step_r[0]=effectivec[i].wr;
		effectivec[i].step_g[0]=effectivec[i].wg;
		effectivec[i].step_b[0]=effectivec[i].wb;
		for (j=0;j<steps;j+=1){
			effectivec[i].step_r[j+1]=effectivec[i].wr+(end_wr-effectivec[i].wr)*(j+1)/9;
			effectivec[i].step_g[j+1]=effectivec[i].wg+(end_wg-effectivec[i].wg)*(j+1)/9;
			effectivec[i].step_b[j+1]=effectivec[i].wb+(end_wb-effectivec[i].wb)*(j+1)/9;}
		//printing subpoints
			//printf (" Derivedc: %.3f %.3f %.3f\n", derivedc[i].wr,derivedc[i].wg,derivedc[i].wb);
		for (j=0;j<steps+1;j+=1){
			if (debug) printf ("  Subpoint: %.3f %.3f %.3f\n",effectivec[i].step_r[j],
				effectivec[i].step_g[j],effectivec[i].step_b[j]);}
		}
	}


static void cascade_colors(gboolean printinfo,gint forcedmode){
	gint i,k;
	gint oldtype=-1;
	
	if (forcedmode>=0){
		oldtype=maindata.mode;
		maindata.mode=forcedmode;}
	
	//calculating w
	for (i=0;i<maindata.colors;i+=1) {
	do_weight(primaryc[i].r,primaryc[i].g,primaryc[i].b,&primaryc[i].wr,&primaryc[i].wg,&primaryc[i].wb);}
	
	//populating effective colors
	if (maindata.mode==FAIR || maindata.mode==ENHANCED || maindata.mode==CAMO \
	|| maindata.mode==ADAPTIVE ) {
		for (i=0;i<maindata.colors;i+=1) {
			effectivec[i].r=primaryc[i].r;
			effectivec[i].g=primaryc[i].g;
			effectivec[i].b=primaryc[i].b;			
			effectivec[i].wr=primaryc[i].wr;
			effectivec[i].wg=primaryc[i].wg;
			effectivec[i].wb=primaryc[i].wb;
			effectivec[i].parent[0]=i;
			effectivec[i].parents=1;
			}
		maindata.effective=maindata.colors;
		}
	else if (maindata.mode==DITHER || maindata.mode==DITHER31) {		//DITHER
		get_dither_colors();
		}
	else {
		printf ("ERROR: cascade_colors(): Undefined mode: %1d\n",maindata.mode);
		exit(1);} 
		
	
	//for (i=0;i<maindata.effective;i+=1) {
		//derivedc[i].wr=effectivec[i].wr;
		//derivedc[i].wg=effectivec[i].wg;
		//derivedc[i].wb=effectivec[i].wb;
		//derivedc[i].parent[0]=i;
		//derivedc[i].parents=1;
		//}
		//maindata.derivedcolors=maindata.effective;
	
	
	//for line distance mode
	if ( maindata.mode==CAMO) populate_linedistdata(maindata.startrange,CAMO);
	else if ( !(maindata.mode==FAIR)) populate_linedistdata(maindata.startrange,SATONLY);

	
	if (printinfo) {
		for (k=0;k<maindata.effective;k+=1){
		printf ("  Color %1d :   has %1d parents\n",
			k,effectivec[k].parents);
		}}		
		//printf ("   Primary -> Effective -> Derived colors\n");
		//for (i=0;i<maindata.derivedcolors;i+=1){
			//printf ("derived color: %1d\n",i);
			//for(j=0;j<derivedc[i].parents;j+=1){
				//printf (" Parrent %1d :                %1d (effective color)\n",j,derivedc[i].parent[j]);
				//efftmp=derivedc[i].parent[j];
				//////printf ("effectivec parents count: %1d for %1d\n",effectivec[efftmp].parents,efftmp);
				//for (k=0;k<effectivec[efftmp].parents;k+=1){
					//////derivedcolor: i
					//printf ("  Parrent %1d :                %1d (primary color)\n",
						//k,effectivec[efftmp].parent[k]);
					//}
					//}
			

	if (oldtype>=0) maindata.mode=oldtype;

	}	


static void get_startingcolors(void){
	//creting temporary array with samples, sorting it and selecting few samples as a starting colors
	gint origpos[STEPPING*STEPPING];
	gfloat sampleweight[STEPPING*STEPPING];
	gfloat ftmp;
	gint itmp;
	gint i,j;
	gfloat k;
	gint primaryc_count=0,primaryc_count_start=0;
	gboolean tobeadded=FALSE;
	gfloat dist,targetdist;
	gboolean debug=FALSE;
	
	targetdist=0.04*pow(maindata.weight,0.5);
	//calculating region importance
	for (i=0;i<samplescount;i=i+1) samples[i].reg_importance=samples[i].final_weight;
	for (i=0;i<samplescount;i=i+1) {
		for (j=0;j<samplescount;j=j+1) {
			if  (j<=i) continue;
			dist=pow( 	(samples[i].r-samples[j].r)*(samples[i].r-samples[j].r)+
						(samples[i].r-samples[j].g)*(samples[i].r-samples[j].g)+
						(samples[i].r-samples[j].b)*(samples[i].r-samples[j].b) , 0.5);
			if (dist<targetdist) {
				samples[i].reg_importance=samples[i].reg_importance+samples[j].final_weight;
				samples[j].reg_importance=samples[j].reg_importance+samples[i].final_weight;}
	}}
	if (debug) {
		for (i=0;i<samplescount;i=i+1)
			printf ("Sample %1d importance: %3f (orig. weight: %.3f)\n",
				i,samples[i].reg_importance,		samples[i].final_weight);}
			
	for (i=0;i<samplescount;i=i+1){
		origpos[i]=i;
		sampleweight[i]=samples[i].reg_importance;}
	
	//sorting
	for (j=0;j<samplescount;j=j+1){
		for (i=0;i<samplescount-1;i=i+1){
			if (sampleweight[i]<sampleweight[i+1]){
				itmp=origpos[i];origpos[i]=origpos[i+1];origpos[i+1]=itmp;
				ftmp=sampleweight[i];sampleweight[i]=sampleweight[i+1];sampleweight[i+1]=ftmp;}
	}}
	
	//printing first n samples
	if (debug){
		for (i=0;i<200;i+=1){
			printf ("Position: %2d, weight %2.3f\n",origpos[i],sampleweight[i]);}}

	//populating primaryc
	if(maindata.forcebw){
		primaryc[0].r=0;primaryc[0].g=0;primaryc[0].b=0;
		primaryc[1].r=1;primaryc[1].g=1;primaryc[1].b=1;
		primaryc_count_start=2;}
		
	for (k=0.8;k>=0;k=k-0.05){
		primaryc_count=primaryc_count_start;
		for (j=0;j<samplescount;j=j+1){
			if (samples[origpos[j]].reg_importance<k*10) continue;
			//printf ("importance: %.3f\n", samples[origpos[j]].reg_importance);
			tobeadded=TRUE;
			for (i=0;i<primaryc_count;i+=1){
				dist=ABS (primaryc[i].r-samples[origpos[j]].r) + 
					 ABS (primaryc[i].g-samples[origpos[j]].g) + 
					 ABS (primaryc[i].b-samples[origpos[j]].b) ;
				if (dist<k){tobeadded=FALSE;break;}
			}
			if (tobeadded){
				primaryc[primaryc_count].r=samples[origpos[j]].r;
				primaryc[primaryc_count].g=samples[origpos[j]].g;				 
				primaryc[primaryc_count].b=samples[origpos[j]].b;	
				primaryc_count+=1;
				if (debug) printf (" Adding color %1d ₍%.3f, %.3f %.3f), importance: %3f\n",
					j,samples[origpos[j]].r,samples[origpos[j]].g,samples[origpos[j]].b,samples[origpos[j]].reg_importance);
				}
			if (primaryc_count==maindata.colors) break;}
		
		if (debug) {
			printf ("Primary colors achieved for tresh %.3f: %1d/%1d\n", k,primaryc_count,maindata.colors);
			//for (i=0;i<primaryc_count;i+=1){
				//printf (" Primary color %1d: RGB_ %.3f, %.3f %.3f\n",
				//i, primaryc[i].r,primaryc[i].g,primaryc[i].b,);}
			}
		
		//if we got needed colors no more iterating
		if (primaryc_count==maindata.colors) break;
	}
	
	if (primaryc_count<maindata.colors) {
		if (VERBOSE || debug) printf ("Small internal shortcoming - not enough starting colors generated\n");
		for (i=primaryc_count;i<maindata.colors;i+=1){
			primaryc[i].r=0.5;
			primaryc[i].g=0.5;				 
			primaryc[i].b=0.5;}
		}
	
}
										
static void get_fcolors(){
	gint i,m,n,o,p;
	//gfloat value;
	gfloat distminglobal,disttmp,distsum;
	gboolean debug;
	gint pos;//,primaryc_pos; //defines color in fcolors
	debug=FALSE;
	gfloat tweakstep,localstep,olddistminglobal,efficiency;
	gint wait_break=0;
	static char labeltextvar [70];
	//gfloat startmin,startup,avg; //range where fcolors will be positioned
	gfloat oldcolorspread, newcolorspread;  // only for defining saturation !!!
	gfloat checksumfl;
	gint checksum;
	
	get_startingcolors();
	fcolors_to_int();
	//show_fcolors();  // temporarily
	cascade_colors(FALSE,-1);

	
	show_fcolors(FALSE);

		
	//calculating distance
	distsum=0;	
	for (m=0;m<samplescount;m=m+1){
		find_best2(samples[m].wr,samples[m].wg,samples[m].wb,&pos,&disttmp,maindata.mode);
		disttmp=disttmp*samples[m].final_weight;
		distsum=distsum+disttmp;
		//if (m<500) printf ("  sample %4d(%.3f %.3f %.3f) aligns to effective color: %2d (%.3f,%.3f, %.3f). Dist: %2.3f/%2.3f\n",
		//	m,samples[m].wr,samples[m].wg,samples[m].wb,pos,effectivec[pos].wr,effectivec[pos].wg,effectivec[pos].wb,disttmp,distsum);
		}
	//printf (" Distsum: %.3f\n",distsum);
	//return;

	distminglobal=distsum;
	
	//changing colors
	tweakstep=0.03; //0.15
	//Doing 50 iterations, as maximum
	for (i=0;i<70;i=i+1) {//was 70
		if (debug) printf ("iteration: %1d, tweakstep: %.3f\n",i,tweakstep);
		olddistminglobal=distminglobal;
		//changing individual colors
		//n color pos,o -channgel,p +-tweak
		for (n=0;n<maindata.colors;n=n+1) { for (o=0;o<3;o=o+1) { for (p=0;p<2;p=p+1) {
			if (p==0) localstep=tweakstep;
			else localstep=-tweakstep;
			
			//for force B/W do not change 1st and 2nd color
			if (maindata.forcebw && (n==0 || n==1)) continue;
			
			if (maindata.postprMode ==PASTEL && i>5)
				oldcolorspread=get_saturation(primaryc[n].r,primaryc[n].g,primaryc[n].b,SIMPLESAT);
			
			temp_change_col(o,n,localstep); //temporarily changing one color
			cascade_colors(FALSE,-1);
			
			// checking distance of colors
			if (maindata.postprMode == PASTEL && i>5) {
				newcolorspread=get_saturation(primaryc[n].r,primaryc[n].g,primaryc[n].b,SIMPLESAT);
				if ( newcolorspread < 0.04 && newcolorspread < oldcolorspread){
					tmp_color_reject();
					cascade_colors(FALSE,-1);
					continue;}	}		

			if (primaryc[n].r<0 || primaryc[n].g<0 || primaryc[n].b<0 || primaryc[n].r>1 || primaryc[n].g>1 || primaryc[n].b>1) {
				tmp_color_reject();
				cascade_colors(FALSE,-1);}
			
			if (debug) printf ("  Changing color %1d, channel %1d, modification: %.3f:\n",n,o,localstep);
			//if (debug) show_fcolors();

			//calculating new dist
			// iterating over samples to get current difference
			distsum=0;
			for (m=0;m<samplescount;m=m+1){
				find_best2(samples[m].wr,samples[m].wg,samples[m].wb,&pos,&disttmp,maindata.mode);
				//printf (" disttmp: %.3f\n", disttmp);
				
				disttmp=disttmp*samples[m].final_weight;
				distsum=distsum+disttmp;
				//if (debug) printf ("Result dist: %.3f\n",disttmp);
				}
			if (debug) printf ("  Result for this fcolors: %.3f vs current global dist: %.3f\n",distsum,distminglobal);		

			if (distsum<distminglobal){ 	//modifying distminglobal and preserving changes
				if (debug) printf ("  good change...\n");
				distminglobal=distsum;
				
				}
			else {						 	//if not returning to previous state
				if (debug) printf ("  not good change (reverting value)...\n");
				tmp_color_reject();
				cascade_colors(FALSE,-1);
				//fcolors_from_w_for(base, n);
				}}}}
				
		//all variations are tested
		
		if (DOCHECKSUMS) {
			checksum=0;checksumfl=0;
 			for (n=0;n<maindata.colors;n=n+1) {
	 			checksum+=(n+1)*3*(primaryc[n].R + 2*primaryc[n].G+3*primaryc[n].B);
				checksumfl+=(n+1)*3*(primaryc[n].r + 2*primaryc[n].g+3*primaryc[n].b);}
			printf ("  Checksums in get_fcolor (it:%2d): %10.3f %10d. Chieved distmin: %2.3f\n",i,checksumfl,checksum,distminglobal);}
	 
	 
		efficiency=1.0-distminglobal/olddistminglobal;		
		if (VERBOSE) printf (" Iteration  %2d, variance reduced by %2.1f%%, tweakstep: %.3f: %.2f -> %.2f\n",i,efficiency*100,tweakstep,olddistminglobal,distminglobal);

		if (efficiency <0.003 && tweakstep<0.0011) {
			wait_break=wait_break+1;
			if (wait_break>2){
				if (VERBOSE) printf ("  Gains are too low, breaking the loop...\n");
				break;}		
		}
		
		if (efficiency<0.01) tweakstep=tweakstep-0.008;
		if (tweakstep<0.001) tweakstep=0.001;
		
		olddistminglobal=distminglobal; 
	}

	//show_fcolors();

	//for debug
	//cascade_colors(TRUE,-1);
	
	if (VERBOSE && (maindata.mode==DITHER || maindata.mode==DITHER31))
		printf ("  %1d effective colors generated\n", maindata.effective);
	
	sprintf(labeltextvar, "Final variance:    %.2f",distminglobal);
	if (maindata.previewbool) gtk_label_set_label(GTK_LABEL(infolabel_var),labeltextvar ); 
	
	
}

static gfloat get_weight(gint x,gint y){
	gint nearx[8]={0,1,1,1,0,-1,-1,-1};
	gint neary[8]={1,1,0,-1,-1,-1,0,1};
	gint prc,i,j,localx,localy,basepos;//one percent based on image sizes
	gfloat r,g,b,r_local,g_local,b_local,maxdist,dist,sumcount,sumdist,count;
	
	prc=(image.height+image.width)/200;
	//printf (" prc: %.d\n",prc);

	basepos=get_basepos(x,y,maindata.channels,image.width);
	r=gammed_values[rect_img[basepos  ]];
	g=gammed_values[rect_img[basepos+1]];
	b=gammed_values[rect_img[basepos+2]];
	//printf ("RGB: %.3f, %.3f %.3f\n",r,g,b);
			
	sumdist=0;sumcount=0;
	for (i=0;i<8;i+=1) {
		maxdist=0;count=0;
		for (j=1;j<4;j+=1) {
			localx=x+nearx[i]*j*2*prc;
			localy=y+neary[i]*j*2*prc;
			if (localx<0|| localy<0 || localx>=image.width || localy >=image.height) continue;
			//printf (" near pixel: %.d %.d for central pixel: %.d x %d\n",localx,localy,x,y);
			basepos=get_basepos(localx,localy,maindata.channels,image.width);
			r_local=gammed_values[rect_img[basepos  ]];
			g_local=gammed_values[rect_img[basepos+1]];
			b_local=gammed_values[rect_img[basepos+2]];
			//printf ("RGB_local: %.3f, %.3f %.3f\n",r_local,g_local,b_local);
			dist=ABS(r_local-r)+ABS(g_local-g)+ABS(b_local-b);
			//printf (" dist: %.3f\n",dist);
			if (dist>maxdist) maxdist=dist;
			count+=1;
		}
		if (count>1) {sumdist=sumdist+dist; sumcount+=1;}
		}
	
	return sumdist/sumcount;
}

static void postdithering(){
	//will iterate over image check one pixel above and previous pixel (those
	//will contain unsatisfied values and calculate goals for current pixel, find nearest color
	//change rect_indx and put residual in current pixels
	gfloat *wr_res_rect,*wg_res_rect,*wb_res_rect;
	gint row,col,basepos,basepos2;	
	gfloat r,g,b,wr,wg,wb; //actual colors of current pixel
	gint pos;
	gfloat wr_res,wg_res,wb_res,dist;
	gboolean debug=FALSE;

	wr_res_rect=g_new(gfloat,warea.width*warea.height);
	wg_res_rect=g_new(gfloat,warea.width*warea.height);
	wb_res_rect=g_new(gfloat,warea.width*warea.height);	
	
		
	cascade_colors(FALSE,-1); //workaround	
								
	for (col=0;col<warea.width;col+=1) {
		for (row=0;row<warea.height;row+=1) {
			if (debug && row%100==0 && col%100==0) printf ("  Row: %1d, col: %1d\n",row,col);
			basepos=get_basepos(col,row,maindata.channels,warea.width);
			basepos2=get_basepos2(col,row,warea.width);
			
			//getting targets
			r=gammed_values[rect_wrk[basepos  ]];
			g=gammed_values[rect_wrk[basepos+1]];
			b=gammed_values[rect_wrk[basepos+2]];	
			do_weight(r,g,b,&wr,&wg,&wb);
			
			//adding previous residuals
			wr_res=wr;
			wg_res=wg;
			wb_res=wb;
			if (row>0) {
				wr_res=wr_res + wr_res_rect[basepos2-warea.width];
				wg_res=wg_res + wg_res_rect[basepos2-warea.width];
				wb_res=wb_res + wb_res_rect[basepos2-warea.width];	}
			if (col>0) {
				wr_res=wr_res + wr_res_rect[basepos2-1];
				wg_res=wg_res + wg_res_rect[basepos2-1];
				wb_res=wb_res + wb_res_rect[basepos2-1];	}
			find_best2(wr_res,wg_res,wb_res,&pos,&dist,FAIR);
			rect_indx[basepos2]=pos;
			//putting new values into residuals rect
			wr_res=wr_res-primaryc[effectivec[pos].parent[0]].wr;
			wg_res=wg_res-primaryc[effectivec[pos].parent[0]].wg;
			wb_res=wb_res-primaryc[effectivec[pos].parent[0]].wb;
			//if (wr_res>maindata.weight || wr_res<-maindata.weight)
			wr_res=CLAMP(wr_res,-maindata.weight*0.5,maindata.weight*0.5);
			wg_res=CLAMP(wg_res,-maindata.weight*0.5,maindata.weight*0.5);
			wb_res=CLAMP(wb_res,-maindata.weight*0.5,maindata.weight*0.5);
			wr_res_rect[basepos2]=wr_res/2.0;
			wg_res_rect[basepos2]=wg_res/2.0;
			wb_res_rect[basepos2]=wb_res/2.0;}}						
	
	
	g_free(wr_res_rect);g_free(wg_res_rect);g_free(wb_res_rect);
	
	if (debug) printf (" Ending adaptive dithering\n");
	
	}

static gint get_huegroup(gfloat r, gfloat g,gfloat b){
	gint huegroup;
	GimpRGB rgbcolor;
	GimpHSV hsvcolor;

	rgbcolor.r=r;
	rgbcolor.g=g;
	rgbcolor.b=b;
	gimp_rgb_to_hsv (&rgbcolor,&hsvcolor);
	if (hsvcolor.s<0.1) huegroup=0; //0 - grey color
	else if (hsvcolor.h>344/360.0) huegroup=1; //red
	else if (hsvcolor.h>260/360.0) huegroup=2; //violet
	else if (hsvcolor.h>218/360.0) huegroup=3; //blue
	else if (hsvcolor.h>168/360.0) huegroup=4; //blue-green
	else if (hsvcolor.h> 78/360.0) huegroup=5; //green
	else if (hsvcolor.h> 42/360.0) huegroup=6; //yellow
	else huegroup=1; //red
	
	//printf (" Calculated huegroup: %1d\n", huegroup);
	
	return huegroup;}	






static void do_sampling(){
	gint i,j,k; //iterators
	gint col,row;
	gint basepos;
	gfloat r,g,b,wr,wg,wb;
	gboolean debug;
	gboolean new_sample;
	gfloat maxweight,minweight;
	//gfloat totalweight=0;
	gfloat total1,total2,total3,total4;
	debug=FALSE;
	gint huegroupsums[7]={0,0,0,0,0,0,0}; // sum for each huegroup
	
	//default values
	for (i=0;i<STEPPING;i=i+1) samples[i].count=1;
	for (i=0;i<STEPPING;i=i+1) samples[i].w_sat=-1;
	
	samplescount=0;   //for positioning in samples
	maxweight=0;minweight=100; //starting values
	for (i=0;i<STEPPING;i=i+1) {for(j=0;j<STEPPING;j=j+1){
		col=image.width/(STEPPING+1)*(i+0.5);
		row=image.height/(STEPPING+1)*(j+0.5);	
		
		if (debug  && k%25==0) printf ("Doing pixel: %i x %i\n",col,row);
		basepos=get_basepos(col,row,maindata.channels,image.width);
		//if (debug) printf ("%i, %i, %i\n",rect_img[basepos  ],rect_img[basepos+1],rect_img[basepos +2]);
		r=gammed_values[rect_img[basepos  ]];
		g=gammed_values[rect_img[basepos+1]];
		b=gammed_values[rect_img[basepos+2]];
		
		//r=ROUND(r/SAMPLEINT)*SAMPLEINT;
		//g=ROUND(g/SAMPLEINT)*SAMPLEINT;
		//b=ROUND(b/SAMPLEINT)*SAMPLEINT;

		do_weight(r,g,b,&wr,&wg,&wb);
				
		new_sample=TRUE;
		//testing previous colors
		for (k=0;k<samplescount;k+=1){
			if (wr==samples[k].wr && wg==samples[k].wg && wb==samples[k].wb) {
				new_sample=FALSE;
				samples[k].count+=1;
				//if (maindata.pwMode==COUNT || maindata.pwMode==MIX ) samples[k].count+=1;
				//if (maindata.pwMode==BYNEIGH || maindata.pwMode==MIX ) 
					//samples[k].w_neigh=samples[k].w_neigh+get_weight(col,row);
				continue;}}
		
		if (new_sample) {
			samples[samplescount].r=r;
			samples[samplescount].g=g;		
			samples[samplescount].b=b;
			samples[samplescount].wr=wr;
			samples[samplescount].wg=wg;		
			samples[samplescount].wb=wb;
			//populating weight
			if (maindata.pwMode==BYSAT || maindata.pwMode==MIX)
				samples[samplescount].w_sat=get_saturation(r,g,b,SIMPLESAT);
			if (maindata.pwMode==BYNEIGH || maindata.pwMode==MIX)
				samples[samplescount].w_neigh=get_weight(col,row);
			if (maindata.pwMode==BYHUE || maindata.pwMode==MIX)
				samples[samplescount].hue_group=get_huegroup(r,g,b);
			//samples[samplescount].uniq=1;
			if (debug &&samples[samplescount].w_sat<0 && (maindata.pwMode==BYHUE || maindata.pwMode==MIX)) printf (" Negative sat found: %.3f; rgb: %.3f %.3f %.3f \n", 
				samples[samplescount].w_sat,r,g,b);
			samples[samplescount].count=1;
			samplescount+=1;}

		if (debug && k%25==0) printf (" Put into samples: %.3f,  %.3f, %.3f, sat: %.3f\n",samples[k].wr,samples[k].wg,samples[k].wb,samples[k].w_sat);
		k=k+1;
	}}
	
	if (maindata.pwMode==BYHUE || maindata.pwMode==MIX){
		//populating w_hue
		for (i=0;i<samplescount;i+=1) {	
			huegroupsums[samples[i].hue_group]+=1;
			if (debug && i%25==0) printf (" increasing count of huegroup %1d to %4d \n",samples[i].hue_group,huegroupsums[samples[i].hue_group] ); }
		//setting hue_weight
		for (i=0;i<samplescount;i+=1) {	
			if (huegroupsums[samples[i].hue_group]==0) samples[i].w_hue=0;
			else samples[i].w_hue=1.0/huegroupsums[samples[i].hue_group];
			if (debug && i%25==0) printf (" w_hue set as %.3f, huegroup %1d, count %3d\n", 
				samples[i].w_hue,samples[i].hue_group,huegroupsums[samples[i].hue_group]);}	
			
		//for debug
		if (debug) {
			printf( " Distribution of colors: \n");
			for (i=0;i<7;i+=1) printf ("  Color: %1d - occurences %4d\n", i, huegroupsums[i]);
			}
		}

	
	//normalizing samples to value 400
	total1=0,total2=0,total3=0,total4=0;
	for (i=0;i<samplescount;i+=1) { 
		total1=total1+samples[i].count;
		total2=total2+samples[i].w_sat  * samples[i].count;		
		total3=total3+samples[i].w_hue  * samples[i].count;		
		total4=total4+samples[i].w_neigh* samples[i].count;}
	
	//printf (" TOTAL3: %.3f\n", total3);
		
	//if (debug) printf ("Totalweight of sampled pixels: %.3f\n",totalweight);
	for (i=0;i<samplescount;i+=1) { 
		samples[i].w_count =(gfloat)samples[i].count*400.0/total1;
		//printf (" resetting saturation from %.3f (/ %.3f)f\n",	samples[i].sat,total2);		
		samples[i].w_sat   =(gfloat)samples[i].w_sat*400.0/total2     * samples[i].count;	
		//printf ("                               to %.3f\n",	samples[i].sat);
		samples[i].w_hue  =(gfloat)samples[i].w_hue*400.0/total3  * samples[i].count;		
		samples[i].w_neigh=(gfloat)samples[i].w_neigh*400.0/total4* samples[i].count;}

	//if (debug) {
		//for (i=0;i<samplescount;i+=1) {
			//if (i%25==0) printf (" Weights for sample %4d: %.3f %.3f %.3f %.3f\n",
			//i,samples[i].count,samples[i].sat,samples[i].w_hue,samples[i].w_neigh);}}
	
	//calculating final weight
	for (i=0;i<samplescount;i+=1) { 
		if (maindata.pwMode==COUNT) samples[i].final_weight=samples[i].w_count;
		else if (maindata.pwMode==BYSAT) samples[i].final_weight=samples[i].w_sat;
		else if (maindata.pwMode==BYHUE) samples[i].final_weight=samples[i].w_hue;
		else if (maindata.pwMode==BYNEIGH) samples[i].final_weight=samples[i].w_neigh;
		else samples[i].final_weight=0.25*samples[i].count + 0.25*samples[i].w_sat +
			0.25*samples[i].w_neigh + 0.25*samples[i].w_hue;
		if (samples[i].final_weight<minweight) minweight=samples[i].final_weight;
		if (samples[i].final_weight>maxweight) maxweight=samples[i].final_weight;
		if (debug && i%25==0) printf (" Weights for sample %4d: Total: %.3f, partial: %.3f %.3f %.3f %.3f\n",
			i,samples[i].final_weight, samples[i].w_count,samples[i].w_sat,samples[i].w_hue,samples[i].w_neigh);
		}
		
	if (VERBOSE) {
		if (minweight>0) printf ("  Min/max weights of sample pixels: %.3f / %.3f (diff: %.1fx)\n",minweight,maxweight,maxweight/minweight);
		else printf ("  Min/max weights of sample pixels: %.3f / %.3f\n",minweight,maxweight);}
	

	if (VERBOSE){
		 printf ("  Colors with high importance (if any):\n");
		for (k=0;k<samplescount;k+=1) {
			samples[k].final_weight=(2+samples[k].final_weight)/3;
			if (samples[k].final_weight>4 ) printf ("  Color %2d: %.3f, %.3f %.3f, weight: %.3f\n",k,samples[k].wr
			,samples[k].wg,samples[k].wb,samples[k].final_weight);}}
		
	if (VERBOSE) printf (" Count of unique sample colors: %1d\n",samplescount);
	
	if (DOCHECKSUMS) {
		gfloat checksum=0;
		gfloat checksumfl=0;
		for (k=0;k<samplescount;k+=1) {
			checksum+=(k-1)*3*(samples[k].r+2*samples[k].b+3*samples[k].g);
			checksumfl+=(k-1)*(samples[k].final_weight);}
		printf ("Checksum for taken samples: %8.3f / weight: %8.2f\n",checksum,checksumfl);}

	}

static void recalculate_sizes(){
	if (maindata.previewbool) {
		warea.startx=parea.startx-10;
		if (warea.startx<0) warea.startx=0;
		warea.starty=parea.starty-10;
		if (warea.starty<0) warea.starty=0;
		
		parea.endx=parea.startx+parea.width;
		parea.endy=parea.starty+parea.height;
		warea.endx=parea.endx+10;
		if (warea.endx>image.endx) warea.endx=image.endx;
		warea.endy=parea.endy+10;
		if (warea.endy>image.endy) warea.endy=image.endy;}
	else {
		warea.startx=image.startx;
		warea.starty=image.starty;
		warea.endx=image.endx;
		warea.endy=image.endy;}
	warea.width=warea.endx-warea.startx;
	warea.height=warea.endy-warea.starty;	
	image.width=image.endx-image.startx;
	image.height=image.endy-image.starty;
	}	

static void despotting2(){
	//first we will get maps with pixels that have to be removed
	//then we will heal those pixels (spots)
	gint i,j,col,row,basepos,near_basepos, highest, result, sum,changed;
	gboolean *mask;
	gint *newvalues;
	gint *col_occ;
	gint relpos_x[8]={-1,-1, 0, 1, 1, 1, 0,-1};
	gint relpos_y[8]={ 0, 1, 1, 1, 0,-1,-1,-1};	
	gboolean debug=FALSE;
		
	mask= g_new(gboolean,warea.width*warea.height);
	newvalues= g_new(gint,warea.width*warea.height);
	for (i=0;i<warea.width*warea.height;i=i+1) mask[i]=TRUE;
	for (i=0;i<warea.width*warea.height;i=i+1) newvalues[i]=-1;	
	col_occ=g_new(gint,maindata.effective);
	
	//this will creage 'holes' into mask
	remove_short(&mask[0],CHECKCOLORS,maindata.spotSzTr);
	
	//iterating over holes in mask and calculating new values
	for (j=0;j<50;j=j+1) {
		if (debug) printf (" Despotting iteration: %1d\n",j);
		for (row=0;row<warea.height;row=row+1) { 
			//if (row%100==0) printf (" row: %1d\n",row);
			for (col=0;col<warea.width;col=col+1) {	
				
				basepos=get_basepos2(col,row,warea.width);
				if (mask[basepos]==TRUE) continue;
				for (i=0;i<maindata.effective;i=i+1) {col_occ[i]=0;}
				
				for (i=0;i<8;i=i+1) {
					if (col+relpos_x[i] <0 ||col+relpos_x[i]>=warea.width || row+relpos_y[i]<0 || row+relpos_y[i]>= warea.height) continue;
					near_basepos=get_basepos2(col+relpos_x[i],row+relpos_y[i],warea.width);
					if (mask[near_basepos]==FALSE) continue;
					col_occ[rect_indx[near_basepos]]=col_occ[rect_indx[near_basepos]]+1;}
				
				//now we have col_occ populated
				sum=0;
				for (i=0;i<maindata.effective;i=i+1) sum=sum+col_occ[i];
				if (sum<3) continue;
				
				highest=0;result=-1;
				for (i=0;i<maindata.effective;i=i+1) if (highest<col_occ[i]) {highest=col_occ[i];result=i;}
				//printf ("resulting color: %1d, occ: %1d\n",result,highest);
				
				newvalues[basepos]=result;}}
		
		//putting values into rect_indx
		changed=0;
		for (i=0;i<warea.width*warea.height;i=i+1) {
			//if (i%1000==0) 
			//printf ( "doing pixel: %1d\n",i);
			if (mask[i]==FALSE && newvalues[i]>-1){
				rect_indx[i]=newvalues[i];
				mask[i]=TRUE;
				changed=changed+1;}}
		if (VERBOSE) printf ("  Changed pixels: %7d (despotting)\n", changed);
		if (changed==0) break;

	}
	g_free(mask);
	g_free(newvalues);
	g_free(col_occ);
	
	
}
	
static void  remove_short(gboolean* outlinemask,gboolean checkcolors,gint treshold){
	//creating array with numbers of lines, so each pixel will be part of one line
	gint *lines_id;
	gint col,row,i,j,k,basepos,nearbasepos,changed;
	gint next_id=0;
	gint relpos_x[4]={-1,-1,0,1};
	gint relpos_y[4]={0,-1,-1,-1};	
	gint *mapper;  //for lines that are in fact other lines
	gint *id_occ;
	gboolean debug=FALSE;
	gint spotstats[3]={0,0,0};//total, below tresh,max
	
	lines_id = g_new(int,warea.width*warea.height);
	mapper = g_new(int,MAPPERLENGTH);
	id_occ = g_new(int,MAPPERLENGTH);	
	
	//inserting values to demap
	for (i=0;i<MAPPERLENGTH;i=i+1) mapper[i]=i;
	
	//pupulating with -1
	for (i=0; i<warea.width*warea.height;i=i+1) lines_id[i]=-1;
	if (debug) printf ( " Populating with -1 done\n");


	//iterating over outlinemask
	for (k=0;k<5;k=k+1) {
		//printf ("x- lines_id: %1d\n",lines_id[0]);
		if (debug) printf (" Short lines/spots identification - iteration %1d\n",k);
		for (row=0;row<warea.height;row=row+1) { 
			for (col=0;col<warea.width;col=col+1) {
				//if (col==0 && row ==0 && debug ) printf ("  position %1d x %1d\n",col,row);
				
				basepos=get_basepos2(col,row,warea.width);
				
				// pixel is not a part of outline
				if (outlinemask[basepos]==FALSE) { //Conditional jump or move depends on uninitialised value(s)

					//if (debug) printf ("    not in outlinemask (%.i)\n",basepos);
					continue;}
				
				for (i=0;i<4;i=i+1){
					if (col+relpos_x[i] <0 ||col+relpos_x[i]>=warea.width || row+relpos_y[i]<0) continue;
	
					nearbasepos=get_basepos2(col+relpos_x[i],row+relpos_y[i],warea.width);
					//if (debug) printf ("  Testing near pixel pixel %0d, (%1d x %1d)\n", i,col+relpos_x[i],row+relpos_y[i]);				
					
					if (checkcolors && (!(rect_indx[basepos] == rect_indx[nearbasepos]) )) continue; //for spots 
					
					if (outlinemask[nearbasepos ] == FALSE) continue;
					
					//two basic options are possible, central pixel has id set or do not have it set
					if (lines_id[basepos]==-1) {
						lines_id[basepos]=lines_id[nearbasepos];
						//if (debug) printf ("  adjacent pixel to ID %1d\n",lines_id[nearbasepos]);
						}
					else if (lines_id[basepos] == lines_id[nearbasepos]); //nothing needed to do
					else if (!(mapper[lines_id[basepos]]==lines_id[basepos])); //nothing can be done
					else { 
						mapper[lines_id[basepos]]=lines_id[nearbasepos];
						//if (debug) printf ("  mapping to %1d\n",lines_id[nearbasepos]);
						}
					
				}
				//if the pixel is still without value this will be new color
				if (lines_id[basepos]==-1) {
					lines_id[basepos]=next_id;
					next_id=next_id+1;
					if (next_id>=MAPPERLENGTH) {printf ("next_id exhausted: %1d\n",next_id); exit (1);}	}
					
			}}	  //end of iteration over pixels		
	

		for (j=0;j<100;j=j+1) {
			changed=0;
			for (i=0;i<next_id;i=i+1) {
				//if (i==mapper[i]) continue;
				if (mapper[i]==mapper[mapper[i]]) continue;
				mapper[i]=mapper[mapper[i]];
				changed=changed+1;}
			if (debug) printf ("   Demapping (iter. %1d): changed values: %1d\n", j, changed);
			if (changed==0) break;
		}

	//remapping id of lines in image
	for (j=0;j<warea.width*warea.height;j=j+1) {if (lines_id[j]>-1) lines_id[j]=mapper[lines_id[j]];};

	//resetting values in demap
	for (i=0;i<MAPPERLENGTH;i=i+1) mapper[i]=i;
		//printf ("b- lines_id: %1d\n",lines_id[0]);	

	}

	if (debug) printf (" Mapping done..\n");

	//getting lines count
	for (i=0;i<next_id;i=i+1) id_occ[i]=0;
	for (j=0;j<warea.height*warea.width;j=j+1){
		if (lines_id[j]==-1) continue;
		id_occ[lines_id[j]]=id_occ[lines_id[j]]+1;}

	//printing length of lines
	if (debug) {
		for (i=0;i<next_id;i=i+1){
			spotstats[0]=spotstats[0]+1;
			if (id_occ[i]<treshold) spotstats[1]=spotstats[1]+1;
			if (id_occ[i]>spotstats[2]) spotstats[2]=id_occ[i];
			}
	if (VERBOSE) printf ("  Spots: %1d, below treshold: %1d. Biggest one: %1d\n", spotstats[0],spotstats[1],spotstats[2]);
	}
		
	//finally removing long lines
	for (j=0;j<warea.width*warea.height;j=j+1) {
		if (id_occ[lines_id[j]]<treshold) outlinemask[j]=FALSE;}
	
	g_free(lines_id);
	g_free(mapper);	
	g_free(id_occ);	
			
	}


static void do_lines2(){
	gfloat diff;
	gint i,j,k;
	gboolean *isgreytable;
	gfloat *huetable;
	GimpRGB rgbcolor;
	GimpHSV hsvcolor;
	gboolean debug=FALSE;
	gboolean isline;
	gint nearx[8]={0,1,1,1,0,-1,-1,-1};
	gint neary[8]={1,1,0,-1,-1,-1,0,1};
	gint basepos;
	gboolean *outlinemask;
	gint statsum1=0,statsum2=0;

	if (debug) printf("   starting do_lines()\n");
	outlinemask = g_new (gboolean, warea.width * warea.height);
	huetable    = g_new (gfloat,maindata.effective);	
	isgreytable = g_new (gboolean,maindata.effective);

	gboolean linestab[maindata.effective][maindata.effective]; //defines if a comb of colors creates a outline

	//populating hue&isgrey tables
	for (i=0;i<maindata.effective;i+=1) {
		//undo_weight(effectivec[i].wr,effectivec[i].wg,effectivec[i].wb,&effectivec[i].r,&effectivec[i].g,&effectivec[i].b);
		//fix above!!!
		
		rgbcolor.r=effectivec[i].r;
		rgbcolor.g=effectivec[i].g;
		rgbcolor.b=effectivec[i].b;
		gimp_rgb_to_hsv (&rgbcolor,&hsvcolor);
		huetable[i]=hsvcolor.h;
		if (hsvcolor.s<0.02) isgreytable[i]=TRUE;
		else isgreytable[i]=FALSE;
		if (debug) printf (" Populating hue&isgrey tables with: %.3f and %1d (%2d/%2d)\n", huetable[i],isgreytable[i],i,maindata.effective);
	}	

	//populating linestable
	for (i=0;i<maindata.effective;i=i+1) { for (j=0;j<maindata.effective;j=j+1) {
		if (i==j) {linestab[i][j]=FALSE;continue;}
		diff = ((effectivec[i].r-effectivec[j].r)*(effectivec[i].r-effectivec[j].r)) + 
			((effectivec[i].g-effectivec[j].g)*(effectivec[i].g-effectivec[j].g)) + 
			((effectivec[i].b-effectivec[j].b)*(effectivec[i].b-effectivec[j].b));
		//printf ("  Raw diff: %.3f\n", diff);
		diff=pow(diff,1/2.0);
		//printf (" Comparing: %.3f %.3f %.3f to %.3f %.3f %.3f \n",
		//effectivec[i].r,effectivec[i].g,effectivec[i].b,effectivec[j].r,effectivec[j].g,effectivec[j].b);
		if (debug) printf (" Current diff: %.3f\n", diff);
		if (diff>maindata.linDiffTresh) { linestab[i][j]=TRUE;statsum1+=1;}
		else linestab[i][j]=FALSE;
		if (linestab[i][j]==TRUE && isgreytable[i]== FALSE && isgreytable[j]== FALSE && diff < (maindata.linhuetresh+0.35)){
			if ( ABS(huetable[i]-huetable[j]) < maindata.linhuetresh ){
				if (debug) printf (" Dismissing a line here (%.3f vs. %.3f)\n",huetable[i],huetable[j] );
				statsum2+=1;
				linestab[i][j]=FALSE;}}
		if (debug) printf ("Building linestab[%1d][%1d]: %1d\n",i,j,linestab[i][j]);
	}}				
		
				
		if (debug) printf("   linestab done\n");
	
	//iterating over image to create OUTLINEMASK
	for (i=1;i<warea.width-1;i=i+1) { for (j=1;j<warea.height-1;j=j+1) {
		isline=FALSE;
		for (k=0;k<8;k=k+1) {
			if (linestab[rect_indx[get_basepos2(i,j,warea.width)]][rect_indx[get_basepos2(i+nearx[k],j+neary[k],warea.width)]]) {
				isline=TRUE;
				break;}}
		if (isline) outlinemask[get_basepos2(i,j,warea.width)]=TRUE;
		else  outlinemask[get_basepos2(i,j,warea.width)]=FALSE;
	}}
	if (debug) printf("   outline mask done\n");	


	// REMOVING LONG lines
	if (maindata.rmLinSzTr>0) remove_short(&outlinemask[0],LINESONLY,maindata.rmLinSzTr );

	if (debug) printf ("  end of removing short lines section\n");

	//putting values into rect_out
	for (i=0;i<warea.width;i=i+1) { for (j=0;j<warea.height;j=j+1) {
		//printf ("i: %01d, j: %01d\n",i,j);
		basepos=get_basepos(i,j,maindata.channels,warea.width);
		if (outlinemask[get_basepos2(i,j,warea.width)]) {
			//printf ("  %01d x %01d: line is here\n",i,j);
			rect_out[basepos]=0;
			rect_out[basepos+1]=0;
			rect_out[basepos+2]=0;
			rect_out[basepos+3]=0;
			if (maindata.channels == 4 ) rect_out[basepos+3]=255;	}
		else {
			rect_out[basepos]=255;
			rect_out[basepos+1]=255;
			rect_out[basepos+2]=255;
			if (maindata.channels == 4 ) rect_out[basepos+3]=0;	
			}
	}}
	if (debug) printf("   values in rect_out\n");
	g_free(outlinemask);g_free(isgreytable);g_free(huetable);
	
	if (VERBOSE) printf (" Lines creation, %2d combination good, %2d combination removed due to hue similarity\n",statsum1,statsum2);
	}


static void get_limits(){
	if (maindata.mode ==DITHER ) {
		maindata.maxdiff=(gfloat)(2-maindata.colors*0.175);
		if (maindata.maxdiff<0.6) maindata.maxdiff=0.6;}
	else if ( maindata.mode ==DITHER31){
		maindata.maxdiff=(gfloat)(2.2-maindata.colors*0.2);
		if (maindata.maxdiff<0.6) maindata.maxdiff=0.6;}	
	else
		maindata.maxdiff=1;
	
	if (maindata.mode==ENHANCED) maindata.startrange=0.5;
	else if (maindata.mode==CAMO) maindata.startrange=0.3;
	else if (maindata.mode==ADAPTIVE) maindata.startrange=0.6;
	else if (maindata.mode==DITHER || maindata.mode==DITHER31 ){
 		if (maindata.colors>=7) maindata.startrange=1;
 		else maindata.startrange=(1-(gfloat)(7-maindata.colors)/10);}
 	else maindata.startrange=1;
}
 	
 		
void export_to_layer()
{
	GimpDrawable *new_layer;
	gint layer_ID,basepos_nl,basepos_ro;
	gint x,y;
	//gfloat br; //brightness
	GimpPixelRgn rgn_out_nl;
	guchar *rect_out_nl;
	
	if (VERBOSE) printf("== Exporting to a layer..\n");
	
	//creating and attaching new layer
	layer_ID = gimp_layer_new (image_ID, "Cartoon", image.width, image.height, GIMP_RGBA_IMAGE, 100.0,  GIMP_NORMAL_MODE);
	new_layer = gimp_drawable_get (layer_ID);
	gimp_image_add_layer (image_ID,layer_ID,-1);
	

	rect_out_nl      = g_new (guchar, image.width * image.height * 4);	
	gimp_pixel_rgn_init (&rgn_out_nl, new_layer, 0, 0, image.width, image.height, FALSE, TRUE);

	//populating rgn_out with data
	ITERATE(x,0,image.width) {ITERATE (y,0,image.height) {
		basepos_nl=get_basepos(x,y,4,image.width);
		basepos_ro=get_basepos(x,y,maindata.channels,image.width);
		rect_out_nl[basepos_nl    ] = rect_out[basepos_ro    ];
		rect_out_nl[basepos_nl + 1] = rect_out[basepos_ro +1 ];
		rect_out_nl[basepos_nl + 2] = rect_out[basepos_ro +2 ];	
		if (maindata.dolines && (!(maindata.mode==ADAPTIVE )))
			if (rect_out_nl[basepos_nl    ] ==0 ) rect_out_nl[basepos_nl + 3] = 255;
			else rect_out_nl[basepos_nl + 3] = 0;
		else if ( maindata.channels ==4)
			rect_out_nl[basepos_nl + 3] = rect_out[basepos_ro +3 ];	
		else
			rect_out_nl[basepos_nl + 3] =255;
		}}
   
	//nahratie udajov do rgn_out
    gimp_pixel_rgn_set_rect (&rgn_out_nl, rect_out_nl,0, 0,image.width,image.height);
	//printf ("   h\n");
	g_free (rect_out_nl);
	
	gimp_drawable_flush (new_layer);
	gimp_drawable_merge_shadow (new_layer->drawable_id, TRUE);
	gimp_drawable_update (new_layer->drawable_id,0, 0, image.width,image.height);
	
	gimp_progress_end();
}		


static void process (GimpDrawable *source,GimpPreview *preview) {
	gint i;
	GimpPixelRgn rgn_img; //whole image for samples
	GimpPixelRgn rgn_wrk; //portion (or whole) image for processing
	GimpPixelRgn rgn_out; //output
	
	if ( preview) colors_warning(FALSE) ;
	
	for (i=0;i<256;i++) { //to speed up conversion from raw RGB to gammed values
		gammed_values[i]=pow((gfloat)i/255,1/2.2);}
	for (i=0;i<maindata.colors;i+=1) primaryc[i].count=0;	
	
	get_limits();
	
	maindata.fixeddiff=(maindata.weight-1)/100.0;
		
	if (VERBOSE) {
		printf ("===  Running cartoonizer's core  ====\n");
		printf (" Number of colors: %1d\n",maindata.colors);
		printf (" Color weight: %1d (fdiff: %.3f), pixel weighting mode: %1d\n",maindata.weight,maindata.fixeddiff,maindata.pwMode);
		printf (" Mode (Algorithm): %1d...\n",maindata.mode);
		printf (" Force B&W: %1d, adding legend: %1d\n",maindata.forcebw,maindata.legend);
		if (maindata.doDenoise>0) printf (" Denoising...\n");	
		else printf (" (Not denoising...)\n");
		if (maindata.spotSzTr>0) printf (" Removing spots smaller then: %1d (iter: %1d)\n",maindata.spotSzTr,maindata.spotIt);
								else printf ( " (Not removing spots...)\n");
		if (maindata.doErode>0) printf ("  Eroding shapes: %1d (iter: %1d)\n",maindata.doErode,maindata.erodeIt);
								else printf ( " (Not smoothing shapes...)\n");								
										
		if (maindata.dolines) printf (" Doing lines: RGB 3D distance tresh: %.3f, exemption because of hue tresh: %.3f, removing shorter then: %1d\n",
			maindata.linDiffTresh,maindata.linhuetresh,maindata.rmLinSzTr);		
				else printf ( " (Not doing lines...)\n");	
	
		printf (" Postprocessing mode: %1d\n",maindata.postprMode);
		printf (" Internal limits: startrange: %.3f, maxdiff: %.3f\n",maindata.startrange,maindata.maxdiff);
		}
	//colorscount=maindata.colors;  // default
			
	// getting coordinates of what will be processed
	if ( preview ){
          gimp_preview_get_position (preview, &parea.startx, &parea.starty);
          gimp_preview_get_size (preview, &parea.width, &parea.height);
         maindata.previewbool=TRUE;
			}
	else {
    	maindata.previewbool=FALSE;	}

	if ( (! maindata.previewbool) ) {
				gimp_progress_init ("Cartoonizing...");
				gimp_progress_update (0.01);}
    
    if (VERBOSE) printf (" Is this preview? %01d\n",maindata.previewbool);
    	
    gimp_drawable_mask_bounds (source->drawable_id,&image.startx, &image.starty,&image.endx, &image.endy); 

	maindata.channels = gimp_drawable_bpp (source->drawable_id);//getting number of channels
	if (VERBOSE) printf (" Dimensions retrieved ...\n");
	
	//quit processing if preview sizes are 1
	if (VERBOSE && maindata.previewbool && parea.width<=1) {
		printf (" Small preview got, quitting...\n");
		return;
	}
	
	//recalculating above size
	recalculate_sizes();
	if (VERBOSE) printf ("  Processing section: %1dx%1d to %1dx%1d, sizes: %1dx%1d, Image sizes: %1dx%1d\n",warea.startx,warea.starty,warea.endx,warea.endy,warea.width,warea.height,image.width,image.height);
		
	//initializing pixelrgn for input and output section
	gimp_pixel_rgn_init (&rgn_img , source, image.startx,image.starty,image.width,image.height, FALSE, FALSE); 
	//loading all data from image
	rect_img  =   g_new (guchar, maindata.channels  * image.width * image.height); 
	gimp_pixel_rgn_get_rect (&rgn_img,rect_img,image.startx,image.starty,image.width,image.height);	
	if (VERBOSE) printf ("== Received entire image to be used for sampling ...\n");

	do_sampling();
	if (VERBOSE) printf ("== Sampling done  ..\n");
	if ( maindata.previewbool == FALSE ) gimp_progress_update (0.05);

	//if (VERBOSE) printf ("point 1\n");	
	get_fcolors(); 
	if ( maindata.previewbool == FALSE ) gimp_progress_update (0.3);
	if (VERBOSE) printf ("== Fcolors calculated ...\n");
	if (maindata.previewbool && (maindata.effective>=MAXEFFECTIVE-2 )) colors_warning(TRUE);

	//getting rectangle to be processed
	gimp_pixel_rgn_init (&rgn_wrk , source, warea.startx,warea.starty,warea.width,warea.height, FALSE, FALSE); 
	//loading all data from image
	if (rect_wrk_initialized==FALSE){
		rect_wrk = g_new (guchar, maindata.channels * image.width * image.height); 
		rect_wrk_initialized=TRUE;}	
	gimp_pixel_rgn_get_rect (&rgn_wrk,rect_wrk,warea.startx,warea.starty,warea.width,warea.height);	
	if (VERBOSE) printf ("== Working part of image retrieved ...\n");

	//converting rect_wrk to indexed image
	if (rect_indx_initialized==FALSE){
		rect_indx =g_new (int, image.width * image.height); 
		rect_indx_initialized=TRUE;}

	//allocating space for rect_out
	if (rect_out_initialized==FALSE){
		rect_out =g_new (guchar,maindata.channels *  image.width * image.height); 
		rect_out_initialized=TRUE;}

	//skipping part of workflow
	if (maindata.mode==ADAPTIVE ) goto end_of_middle;	

	// indexing
	indexing();
	if ( maindata.previewbool == FALSE ) gimp_progress_update (0.4);
	if (VERBOSE) printf ("== Image indexed\n");
	
	// elimination of small noise
	if (maindata.doDenoise) {
		denoising(7,8,100,LOOSE);
		if (VERBOSE) printf ("== Denoising done ...\n");}
	if ( maindata.previewbool == FALSE ) gimp_progress_update (0.55);
			
	// elimination of spots
	if (maindata.spotSzTr>0 && maindata.spotIt>0) {
		for (i=0;i<maindata.spotIt;i=i+1) {
			despotting2();
			if (VERBOSE) printf ("== Despotting #%d done ...\n",i+1);}}
	if ( maindata.previewbool == FALSE ) gimp_progress_update (0.65);
	
	// erode shapes
	if (maindata.doErode && maindata.erodeIt > 0) {
		denoising(5,5,maindata.erodeIt,STRICT);
		if (VERBOSE) printf ("== Eroding done done ...\n");
	}
	if ( maindata.previewbool == FALSE ) gimp_progress_update (0.6);		
	
	//doing lines or ...
	if (maindata.dolines){
		do_lines2();
		if ( maindata.previewbool == FALSE ) gimp_progress_update (0.8);
		if (VERBOSE) printf ("== Outlining done\n");}	
	else {
		// ...doing adaptive (post-)dithering...
		end_of_middle:
		if (maindata.mode==ADAPTIVE ) postdithering();
		if ( maindata.previewbool == FALSE ) gimp_progress_update (0.8);
		// ... and posprocesing if needed
		if (maindata.postprMode==PASTEL) 		get_pastel_colors(0.7,0.25);
		if (maindata.postprMode==DARKPASTEL) 	get_pastel_colors(0.4,0.55);
		if (maindata.postprMode==GOLD)   		get_gold_colors();
		if (maindata.postprMode==SATURATED)		get_saturated_colors();
		if (maindata.postprMode==PALETTE)		get_cartoon_colors4(); 
		
				
		fcolors_to_int();   //selfexplanatory
		if (VERBOSE) printf ("== Final RGB values calculated\n");
		
		deindexing();   // changing index to RGB
		if (VERBOSE) printf ("== Image deindexed\n");	
		
		if(maindata.legend && maindata.previewbool==FALSE){//if legend is choosed and not preview
			insert_legend();
			if (VERBOSE) printf ("== Legend added\n");	}
		}
	if ( maindata.previewbool == FALSE ) gimp_progress_update (0.90);

	fcolors_to_int();
	show_fcolors(FALSE);

	if (maindata.exportlayer) {
		export_to_layer();
		return;}

	//putting data back to rgn_out
	gimp_pixel_rgn_init (&rgn_out, source, warea.startx,warea.starty,warea.width,warea.height, preview == NULL, TRUE);
	gimp_pixel_rgn_set_rect (&rgn_out, rect_out,warea.startx,warea.starty,warea.width,warea.height);
		
   //update (graphical) of preview/image
    if (VERBOSE) printf ("== Sending results back to gimp (preview: %1d)\n",maindata.previewbool);  
	if (maindata.previewbool ) {
    	gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview),&rgn_out);}
    else {
		gimp_drawable_flush (source);
		gimp_drawable_merge_shadow (source->drawable_id, TRUE);
		gimp_drawable_update (source->drawable_id,image.startx,image.starty,image.width,image.height);}
	gimp_progress_end();
	g_free(rect_img);
}


static gboolean gui_window (GimpDrawable *source)
{
		GtkWidget *dialog;
		GtkWidget *main_hbox,*vbox2,*vbox1;
		GtkWidget *table; 
		GtkWidget *l_colors,*l_count,*l_sat,*l_weighting, *l_dolines, *l_spot,*info_exp,*l_alg,
				*l_doerode,*l_postprocess,*l_forcebw;
		GtkWidget *l_spotsize, *l_spotiter,*l_linebrstresh,*l_lineshuetresh,*l_legend, *l_linremove,*infolabel,*l_denoise,*l_erode;
		GtkWidget *exportbutton;
		GtkWidget *preview;
		
		
		gimp_ui_init ("Cartoonizer", FALSE);
		
		
		gimp_dialogs_show_help_button (FALSE);
		dialog = gimp_dialog_new (title, "gimp plugin",
                            NULL, 0,
                            gimp_standard_help_func, "plug-in-cartoonizer",
                            GIMP_STOCK_RESET, RESPONSE_RESET,
                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                            GTK_STOCK_OK,     RESPONSE_OK,

                            NULL);

	main_hbox = gtk_hbox_new (FALSE, 10);
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_hbox);
	gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 15);
	gtk_widget_show (main_hbox);
	
	//1.VBOXs
	vbox1 = gtk_vbox_new (FALSE, 5);
	gtk_box_pack_start (GTK_BOX (main_hbox), vbox1, TRUE, TRUE, 0);
	gtk_container_set_border_width (GTK_CONTAINER (vbox1), 6);
	gtk_widget_show (vbox1);
  
  
	vbox2 = gtk_vbox_new (FALSE, 5);
	gtk_box_pack_start (GTK_BOX (main_hbox), vbox2, FALSE,FALSE, 0);
	gtk_container_set_border_width (GTK_CONTAINER (vbox2), 6);
	gtk_widget_show (vbox2);
	
	preview = gimp_drawable_preview_new (source, NULL);
	gtk_box_pack_start (GTK_BOX (vbox1), preview, TRUE, TRUE, 0);
	gtk_widget_set_tooltip_text(preview, "Preview is AUTOEXPANDING. Technical comment: \
 when calculating this preview, only visible area + 10 px in all directions are considered. \
 So especially 'spot removal' and 'short lines removal' functionality might lead to slightly \
 different results when processing entire image afterwards.");
	gtk_widget_show (preview);
	
	infolabel_warn=gtk_label_new("");
	gtk_box_pack_start (GTK_BOX (vbox1), infolabel_warn, FALSE, FALSE, 0);
	gtk_misc_set_alignment (GTK_MISC (infolabel_warn), 0, 0.5);
	gtk_widget_show (infolabel_warn);	
	
	infolabel_var=gtk_label_new("");
	gtk_widget_set_tooltip_text(infolabel_var, "This indicates how different the final image is to source image (not relevant for Adaptive Dithering).");
	gtk_box_pack_start (GTK_BOX (vbox1), infolabel_var, FALSE, FALSE, 0);
	gtk_misc_set_alignment (GTK_MISC (infolabel_var), 0, 0.5);
	gtk_widget_show (infolabel_var);
	
	info_exp = gtk_expander_new  ("<span foreground=\"#505050\">For more info:</span>" );
	gtk_expander_set_use_markup (GTK_EXPANDER(info_exp), TRUE);
	gtk_box_pack_end (GTK_BOX (vbox1), info_exp, FALSE, FALSE, 30);
	gtk_widget_set_tooltip_text( info_exp, "Visit plugin's homepage (link in expander) to learn more about the plugin, check for new versions or to give any feedback.");
	gtk_widget_show (info_exp);
  	
	 	infolabel=gtk_label_new("Visit wiki and downloads section on homepage: http://code.google.com/p/cartooner-color-reducer/");
	 	gtk_label_set_line_wrap (GTK_LABEL(infolabel), TRUE);
	 	gtk_label_set_use_markup (GTK_LABEL(infolabel), TRUE);
	 	gtk_widget_set_size_request(infolabel,230,50);
	 	gtk_container_add (GTK_CONTAINER (info_exp), infolabel);
		//gtk_widget_set_tooltip_text(infolabel, "....");
		gtk_misc_set_alignment (GTK_MISC (infolabel), 0, 0.5);
		gtk_widget_show (infolabel);
  
	table=gtk_table_new(19,2,FALSE);
	gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
	gtk_table_set_col_spacings (GTK_TABLE(table),8);
	gtk_table_set_row_spacings (GTK_TABLE(table),1);
	gtk_table_set_row_spacing  (GTK_TABLE(table),10,16);
	gtk_table_set_row_spacing  (GTK_TABLE(table),12,8);
	gtk_table_set_row_spacing  (GTK_TABLE(table),16,20);
	gtk_table_set_row_spacing  (GTK_TABLE(table),17,20);
	gtk_widget_show (table);
    
	l_colors = gtk_label_new ("<b>Color reduction:</b>");
	//gtk_widget_set_tooltip_text(l_colors, "Target number of colors.");
	gtk_label_set_use_markup (GTK_LABEL(l_colors), TRUE);
	gtk_widget_set_size_request(l_colors,0,35);
	gtk_table_attach_defaults (GTK_TABLE(table),l_colors,0,2,0,1);
	gtk_misc_set_alignment (GTK_MISC (l_colors), 0, 0.7);
	gtk_widget_show (l_colors);

	l_count = gtk_label_new ("Number of colors:");
	gtk_widget_set_tooltip_text(l_count, "Target number of colors.");
	gtk_label_set_use_markup (GTK_LABEL(l_count), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_count,0,1,1,2);
	gtk_misc_set_alignment (GTK_MISC (l_count), 0, 0.5);
	gtk_widget_show (l_count);
	

	l_sat = gtk_label_new ("Color weight:");
	gtk_widget_set_tooltip_text(l_sat, "Something like internal saturation of image - used to increase weight of color against brightness. \
(1 - no change)");
	gtk_label_set_use_markup (GTK_LABEL(l_sat), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_sat,0,1,2,3);
	gtk_misc_set_alignment (GTK_MISC (l_sat), 0, 0.5);
	gtk_widget_show (l_sat);

	l_weighting = gtk_label_new ("Color/pixel weighting:");
	gtk_widget_set_tooltip_text(l_weighting, "Pixels and their colors can have various weight when computing final colors.");
	gtk_label_set_use_markup (GTK_LABEL(l_weighting), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_weighting,0,1,3,4);
	gtk_misc_set_alignment (GTK_MISC (l_weighting), 0, 0.5);
	gtk_widget_show (l_weighting);


	l_alg = gtk_label_new ("Processing algorithm:");
	//gtk_widget_set_tooltip_text(l_alg, "");
	gtk_label_set_use_markup (GTK_LABEL(l_alg), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_alg,0,1,4,5);
	gtk_misc_set_alignment (GTK_MISC (l_alg), 0, 0.5);
	gtk_widget_show (l_alg);

	l_forcebw = gtk_label_new ("Force Black+White:");
	gtk_widget_set_tooltip_text(l_forcebw, "Check this to force white and black color into final colors. \
(However this does not quarantee that black and white colors will be used in final image alltogether).");
	//gtk_widget_set_size_request(l_legend,0,35);
	gtk_table_attach_defaults (GTK_TABLE(table),l_forcebw,0,1,5,6);
	gtk_misc_set_alignment (GTK_MISC (l_forcebw), 0, 0.5);
	gtk_widget_show (l_forcebw);
	
	l_legend = gtk_label_new ("Insert legend:");
	gtk_widget_set_tooltip_text(l_legend, "Insert legend (samples of final colors) to the bottom of image. Not shown in preview.");
	gtk_table_attach_defaults (GTK_TABLE(table),l_legend,0,1,6,7);
	gtk_misc_set_alignment (GTK_MISC (l_legend), 0, 0.5);
	gtk_widget_show (l_legend);

	l_denoise = gtk_label_new ("<b>Denoise:</b>");
	gtk_widget_set_tooltip_text(l_denoise, "Get rid of standalone pixels = change color of pixels if all neighbours are of different color.");
	gtk_label_set_use_markup (GTK_LABEL(l_denoise), TRUE);
	gtk_widget_set_size_request(l_denoise,0,35);
	gtk_table_attach_defaults (GTK_TABLE(table),l_denoise,0,1,7,8);
	gtk_misc_set_alignment (GTK_MISC (l_denoise), 0, 0.5);
	gtk_widget_show (l_denoise);
 
	l_spot = gtk_label_new ("<b>Spot removal:</b>");
	gtk_widget_set_tooltip_text(l_spot, "Identify and get rid of discrete areas of the same color.");
	gtk_widget_set_size_request(l_spot,0,35);
	gtk_label_set_use_markup (GTK_LABEL(l_spot), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_spot,0,2,8,9);
	gtk_misc_set_alignment (GTK_MISC (l_spot), 0, 0.7);
	gtk_widget_show (l_spot);
 
	l_spotsize  = gtk_label_new ("Spot size (px):");
	gtk_widget_set_tooltip_text(l_spotsize, "Size of a discrete spot (in pixels) to be rid of... Default is 0 = disabled");
	gtk_label_set_use_markup (GTK_LABEL(l_spotsize), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_spotsize,0,1,9,10);
	gtk_misc_set_alignment (GTK_MISC (l_spotsize), 0, 0.5);
	gtk_widget_show (l_spotsize);

	l_spotiter  = gtk_label_new ("Iterations:");
	gtk_widget_set_tooltip_text(l_spotiter, "Count of passes...");
	gtk_label_set_use_markup (GTK_LABEL(l_spotiter), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_spotiter,0,1,10,11);
	gtk_misc_set_alignment (GTK_MISC (l_spotiter), 0, 0.5);
	gtk_widget_show (l_spotiter);

	l_doerode = gtk_label_new ("<b>Erode shapes:</b>");
	gtk_widget_set_tooltip_text(l_doerode, "Get rid of sharp outreaching sections, it is strongly recommend to have spot removal (spotsize>0) enabled.");
	gtk_label_set_use_markup (GTK_LABEL(l_doerode), TRUE);
	//gtk_widget_set_size_request(l_doerode,0,35);
	gtk_table_attach_defaults (GTK_TABLE(table),l_doerode,0,1,11,12);
	gtk_misc_set_alignment (GTK_MISC (l_doerode), 0, 0.5);
	gtk_widget_show (l_doerode);
	
	l_erode = gtk_label_new ("Iterations limit:");
	gtk_widget_set_tooltip_text(l_erode, "Maximum number of iterations over image, eroding might be stopped sooner if no further pixels can be modified.");
	gtk_label_set_use_markup (GTK_LABEL(l_erode), TRUE);
	//gtk_widget_set_size_request(l_denoise,0,35);
	gtk_table_attach_defaults (GTK_TABLE(table),l_erode,0,1,12,13);
	gtk_misc_set_alignment (GTK_MISC (l_erode), 0, 0.5);
	gtk_widget_show (l_erode);
	

	l_dolines = gtk_label_new ("<b>Generate lines:</b>");
	gtk_widget_set_tooltip_text(l_dolines, linescheckboxtest);
	gtk_label_set_use_markup (GTK_LABEL(l_dolines), TRUE);
	gtk_widget_set_size_request(l_dolines,0,35);
	gtk_table_attach_defaults (GTK_TABLE(table),l_dolines,0,1,13,14);
	gtk_misc_set_alignment (GTK_MISC (l_dolines), 0, 0.6);
	gtk_widget_show (l_dolines);

	l_linebrstresh = gtk_label_new ("Difference tresh.:");
	gtk_widget_set_tooltip_text(l_linebrstresh, rgbtresttext);
	//gtk_label_set_use_markup (GTK_LABEL(l_linebrstresh), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_linebrstresh,0,1,14,15);
	gtk_misc_set_alignment (GTK_MISC (l_linebrstresh), 0, 0.5);
	gtk_widget_show (l_linebrstresh);

	l_lineshuetresh = gtk_label_new ("Hue treshold:");
	gtk_widget_set_tooltip_text(l_lineshuetresh, huetresttext);
	//gtk_label_set_use_markup (GTK_LABEL(l_linebrstresh), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_lineshuetresh,0,1,15,16);
	gtk_misc_set_alignment (GTK_MISC (l_lineshuetresh), 0, 0.5);
	gtk_widget_show (l_lineshuetresh);

	l_linremove = gtk_label_new ("Remove short:");
	gtk_widget_set_tooltip_text(l_linremove, "Lines with lesser size (in pixels) will be removed.");
	//gtk_label_set_use_markup (GTK_LABEL(l_linebrstresh), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_linremove,0,1,16,17);
	gtk_misc_set_alignment (GTK_MISC (l_linremove), 0, 0.5);
	gtk_widget_show (l_linremove);
	
	l_postprocess = gtk_label_new ("<b>Postprocessing:</b>");
	gtk_label_set_use_markup (GTK_LABEL(l_postprocess), TRUE);
	gtk_widget_set_tooltip_text(l_postprocess, "Final modification of calculated colors.");
	//gtk_label_set_use_markup (GTK_LABEL(l_linebrstresh), TRUE);
	gtk_table_attach_defaults (GTK_TABLE(table),l_postprocess,0,1,17,18);
	gtk_misc_set_alignment (GTK_MISC (l_postprocess), 0, 0.5);
	gtk_widget_show (l_postprocess);


	//NUMBER BOXes
	
	spin_count_adj = gtk_adjustment_new (maindata.colors, 2,GUIMAXCOLORS, 1, 1, 0);
	spin_count = gtk_spin_button_new (GTK_ADJUSTMENT (spin_count_adj), 1, 0);
	gtk_widget_show (spin_count);
	gtk_table_attach_defaults (GTK_TABLE(table),spin_count,1,2,1,2);
	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_count), TRUE);
	gtk_widget_show (spin_count);

	spin_weight_adj = gtk_adjustment_new (WEIGHTDEF, 1,15, 1, 1, 0);
	spin_weight = gtk_spin_button_new (GTK_ADJUSTMENT (spin_weight_adj), 1, 0);
	gtk_widget_set_tooltip_text(spin_weight, "With higher number you can sacrifice \
brightness range for benefit of colors.");
	gtk_widget_show (spin_weight);
	gtk_table_attach_defaults (GTK_TABLE(table),spin_weight,1,2,2,3);
	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_weight), TRUE);
	gtk_widget_show (spin_weight);

	combo3 = gtk_combo_box_new_text();
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo3),wmode1 );
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo3),wmode2);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo3),wmode3);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo3),wmode4);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo3),wmode5);
	gtk_table_attach_defaults (GTK_TABLE(table),combo3,1,2,3,4);
	gtk_widget_set_size_request(combo3,70,-1);
	gtk_combo_box_set_active(GTK_COMBO_BOX( combo3), WMODEDEF);
	gtk_widget_show (combo3);

	combo = gtk_combo_box_new_text();
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo),mode1);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo),mode4);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo),mode3);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo),mode2);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo),mode5);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo),mode8);
	gtk_table_attach_defaults (GTK_TABLE(table),combo,1,2,4,5);
	gtk_widget_set_size_request(combo,120,-1);
	gtk_combo_box_set_active(GTK_COMBO_BOX( combo), MODEDEF);
	gtk_widget_show (combo);

	forcebw_button= gtk_check_button_new();
	gtk_table_attach_defaults (GTK_TABLE(table),forcebw_button,1,2,5,6);
	gtk_widget_set_size_request(forcebw_button,0,25);
	gtk_widget_show (forcebw_button);

	legend_button= gtk_check_button_new();
	gtk_table_attach_defaults (GTK_TABLE(table),legend_button,1,2,6,7);
	gtk_widget_set_size_request(legend_button,0,25);
	gtk_widget_show (legend_button);

	denoising_button= gtk_check_button_new();
	gtk_table_attach_defaults (GTK_TABLE(table),denoising_button,1,2,7,8);
	gtk_widget_set_tooltip_text(denoising_button, "Leave it checked unless you are sure you need raw indexed image. Very usefull for futher operations listed below.");
	gtk_widget_set_size_request(denoising_button,0,40);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (denoising_button   ),TRUE);
	gtk_widget_show (denoising_button);
	  
 	spin_sp_tresh_adj = gtk_adjustment_new (maindata.spotSzTr, 0,500, 1, 1, 0);
	spin_sp_tresh = gtk_spin_button_new (GTK_ADJUSTMENT (spin_sp_tresh_adj), 1, 0);
	gtk_widget_set_tooltip_text(spin_sp_tresh, "0=disabled, good value in range 20 - 100.");
	gtk_widget_show (spin_sp_tresh);
	gtk_table_attach_defaults (GTK_TABLE(table),spin_sp_tresh,1,2,9,10);
	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_sp_tresh), TRUE);
	gtk_widget_show (spin_sp_tresh);

	spin_sp_it_adj = gtk_adjustment_new (maindata.spotIt, 0,5, 1, 1, 0);
	spin_sp_it = gtk_spin_button_new (GTK_ADJUSTMENT (spin_sp_it_adj), 1, 0);
	gtk_widget_set_tooltip_text(spin_sp_it, "0=disabled, 2 is usually enough, if you notice artifacts increase the number.");
	gtk_widget_show (spin_sp_it);
	gtk_table_attach_defaults (GTK_TABLE(table),spin_sp_it,1,2,10,11);
	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_sp_it), TRUE);
	gtk_widget_show (spin_sp_it);

	spin_erode_it_adj = gtk_adjustment_new (ERODEDEF, 0,200, 1, 1, 0);
	spin_erode_it = gtk_spin_button_new (GTK_ADJUSTMENT (spin_erode_it_adj), 1, 0);
	gtk_widget_set_tooltip_text(spin_erode_it, "Either leave it on default or try very low numbers to soften the effect");
	gtk_widget_show (spin_erode_it);
	gtk_table_attach_defaults (GTK_TABLE(table),spin_erode_it,1,2,12,13);
	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_erode_it), TRUE);
	gtk_widget_set_sensitive  (spin_erode_it,FALSE);
	gtk_widget_show (spin_erode_it);

	spin_lin_diff_tresh_adj = gtk_adjustment_new (LINDIFFTRESHDEF, 0,1, 0.01, 0.01, 0);
	spin_lin_diff_tresh = gtk_spin_button_new (GTK_ADJUSTMENT (spin_lin_diff_tresh_adj), 0.1,2);
	gtk_widget_set_tooltip_text(spin_lin_diff_tresh, rgbtresttext);
	gtk_widget_show (spin_lin_diff_tresh);
	gtk_table_attach_defaults (GTK_TABLE(table),spin_lin_diff_tresh,1,2,14,15);
	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_lin_diff_tresh), TRUE);
	gtk_widget_set_sensitive  (spin_lin_diff_tresh,FALSE);
	gtk_widget_show (spin_lin_diff_tresh);

	spin_lin_hue_tresh_adj = gtk_adjustment_new (LINHUETRESHDEF, 0,0.5, 0.01, 0.01, 0);
	spin_lin_hue_tresh = gtk_spin_button_new (GTK_ADJUSTMENT (spin_lin_hue_tresh_adj), 0.1,2);
	gtk_widget_set_tooltip_text(spin_lin_hue_tresh, huetresttext);
	gtk_widget_show (spin_lin_hue_tresh);
	gtk_table_attach_defaults (GTK_TABLE(table),spin_lin_hue_tresh,1,2,15,16);
	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_lin_hue_tresh), TRUE);
	gtk_widget_set_sensitive  (spin_lin_hue_tresh,FALSE);
	gtk_widget_show (spin_lin_hue_tresh);

	spin_lin_rem_adj = gtk_adjustment_new (LINREMDEF, 0,1000, 10, 10, 0);
	spin_lin_rem = gtk_spin_button_new (GTK_ADJUSTMENT (spin_lin_rem_adj), 1,0);
	gtk_widget_set_tooltip_text(spin_lin_rem, "0=disabled (no pixels blacked by above setting will be erased)");
	gtk_widget_show (spin_lin_rem);
	gtk_table_attach_defaults (GTK_TABLE(table),spin_lin_rem,1,2,16,17);
	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_lin_rem), TRUE);
	gtk_widget_set_sensitive  (spin_lin_rem,FALSE);
	gtk_widget_show (spin_lin_rem);

	doerode_button= gtk_check_button_new();
	gtk_table_attach_defaults (GTK_TABLE(table),doerode_button,1,2,11,12);
	gtk_widget_set_size_request(doerode_button,0,40);
	gtk_widget_show (doerode_button);

	dolines_button= gtk_check_button_new();
	gtk_table_attach_defaults (GTK_TABLE(table),dolines_button,1,2,13,14);
	gtk_widget_set_tooltip_text(dolines_button, linescheckboxtest);
	gtk_widget_set_size_request(dolines_button,0,40);
	gtk_widget_show (dolines_button);

	combo2 = gtk_combo_box_new_text();
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo2),post1 );
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo2),post2);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo2),post5);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo2),post3);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo2),post4);
	gtk_combo_box_append_text( GTK_COMBO_BOX( combo2),post6);
	gtk_table_attach_defaults (GTK_TABLE(table),combo2,1,2,17,18);
	//gtk_widget_set_tooltip_text(combo2, "Take this postprocessing options as experimental for now.");
	gtk_widget_set_size_request(combo2,70,-1);
	gtk_combo_box_set_active(GTK_COMBO_BOX( combo2), 0);
	gtk_widget_show (combo2);
	
	exportbutton = gtk_button_new_with_label("Export as new layer");
	gtk_widget_set_tooltip_text(exportbutton, "Plugin window will stay alive with all settings preserved. During export/calculation entire plugin's window is frozen.");
	gtk_table_attach_defaults (GTK_TABLE(table),exportbutton,0,2,18,19);
	gtk_widget_show (exportbutton);

	//sending signal to update preview
	g_signal_connect_swapped (preview			, "invalidated"	,  G_CALLBACK (process),source);
	g_signal_connect_swapped (spin_count_adj	, "value_changed", G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (spin_weight_adj	, "value_changed", G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (spin_sp_tresh_adj , "value_changed", G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (spin_sp_it_adj	, "value_changed", G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (spin_lin_diff_tresh_adj, "value_changed", G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (spin_lin_hue_tresh_adj, "value_changed", G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (spin_lin_rem_adj	, "value_changed", G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (spin_erode_it_adj	, "value_changed", G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (G_OBJECT( dolines_button), "toggled",G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (G_OBJECT( doerode_button), "toggled",G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (G_OBJECT( denoising_button), "toggled",G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (G_OBJECT( forcebw_button), "toggled",G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (G_OBJECT(combo )	, "changed"		, G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (G_OBJECT(combo2)	, "changed"		, G_CALLBACK (gimp_preview_invalidate), preview);
	g_signal_connect_swapped (G_OBJECT(combo3)	, "changed"		, G_CALLBACK (gimp_preview_invalidate), preview);
			
	process (source, GIMP_PREVIEW (preview));
	
	//receiving values from sliders and modifying sharpals
	g_signal_connect (spin_count_adj	, "value_changed"	, G_CALLBACK (gimp_int_adjustment_update), &maindata.colors);
	g_signal_connect (spin_weight_adj	, "value_changed"	, G_CALLBACK (gimp_int_adjustment_update), &maindata.weight);
	g_signal_connect (spin_sp_tresh_adj	, "value_changed"	, G_CALLBACK (gimp_int_adjustment_update), &maindata.spotSzTr);	
	g_signal_connect (spin_sp_it_adj	, "value_changed"	, G_CALLBACK (gimp_int_adjustment_update), &maindata.spotIt);
	g_signal_connect (spin_lin_diff_tresh_adj, "value_changed", G_CALLBACK (gimp_float_adjustment_update), &maindata.linDiffTresh);
	g_signal_connect (spin_lin_hue_tresh_adj,"value_changed", G_CALLBACK (gimp_float_adjustment_update), &maindata.linhuetresh);
	g_signal_connect (spin_lin_rem_adj	, "value_changed"	, G_CALLBACK (gimp_int_adjustment_update), &maindata.rmLinSzTr);
	g_signal_connect (spin_erode_it_adj	, "value_changed"	, G_CALLBACK (gimp_int_adjustment_update), &maindata.erodeIt);
	g_signal_connect (G_OBJECT(dolines_button), "toggled"	, G_CALLBACK( dolines_changed ), NULL );
	g_signal_connect (G_OBJECT(doerode_button), "toggled"	, G_CALLBACK( doerode_changed ), NULL );	
	g_signal_connect (G_OBJECT(denoising_button), "toggled"	, G_CALLBACK( denoising_changed ), NULL );
	g_signal_connect (G_OBJECT(legend_button), "toggled"	, G_CALLBACK( legend_changed ), NULL );	
	g_signal_connect (G_OBJECT(forcebw_button), "toggled"	, G_CALLBACK( forcebw_changed ), NULL );	
	g_signal_connect_swapped(G_OBJECT(exportbutton),"clicked",G_CALLBACK( exportwrapper ), source );
	g_signal_connect( G_OBJECT( combo )	, "changed"  		, G_CALLBACK( cb_changed ), NULL );
	g_signal_connect( G_OBJECT( combo2)	, "changed"  		, G_CALLBACK( post_changed ), NULL );	
	g_signal_connect( G_OBJECT( combo3)	, "changed"  		, G_CALLBACK( wmode_changed ), NULL );
			
	gtk_widget_show (dialog);
	
	//receiving signals from gimp buttons + "x" (close window button)
	g_signal_connect (dialog, "response",
                    G_CALLBACK (response_callback),
                    NULL);

  gtk_main ();

  if (VERBOSE) printf ("  End of (cartoonizer's) GUI... \n");
  return process_image; //TRUE if image should be processed
}


static void response_callback (GtkWidget *widget,  gint response_id) {
	
  switch (response_id) 	 {
	case RESPONSE_RESET:
	if (VERBOSE)  printf (" Plugin window returned: Resetting... \n");
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_count),COLORSDEF);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_weight),WEIGHTDEF);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_sp_tresh),SPTRESHDEF);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_sp_it),SPITDEF);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_erode_it),ERODEDEF);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_lin_diff_tresh),LINDIFFTRESHDEF);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_lin_hue_tresh),LINHUETRESHDEF);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_lin_rem),LINREMDEF);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dolines_button  ),FALSE);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (doerode_button  ),FALSE);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (denoising_button),TRUE );
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (legend_button   ),FALSE);
	gtk_combo_box_set_active(GTK_COMBO_BOX( combo), MODEDEF);
	gtk_combo_box_set_active(GTK_COMBO_BOX( combo2), 0);
	gtk_combo_box_set_active(GTK_COMBO_BOX( combo3), WMODEDEF);
			
	break;

    case RESPONSE_OK:  //quitting plugin window and applying change
      if (VERBOSE) printf (" Plugin window returned: OK... \n");
      process_image=TRUE; 
      gtk_widget_destroy (widget);
      gtk_main_quit ();
      break;

    default: // if other response - terminate plugin window
      if (VERBOSE) printf (" Plugin window terminated... \n");
      gtk_widget_destroy (widget);
      gtk_main_quit ();
      break;
    };
	
}



