/* vigneting.c
 * Make or remove Wigneting plugin for Gimp 2.x
 * Copyright (C) 2006 Luc courtrai
 *
 * structure of program is copie of
 * hpf.c
 * A high pass filter plugin for Gimp 2.x
 * Smooths overall brightness of an image
 * Copyright (C) 2004 David Hodson hodsond@ozemail.com.au
 *
 * tank David
 */


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


#define PLUGIN_NAME "Vigneting"
#define PLUGIN_TITLE "Vigneting"

static void query (void);
static void run   (const char *name, 
		   int n_params, 
		   const GimpParam * param, 
		   int *nreturn_vals, 
		   GimpParam ** return_vals);


// parametre dialogue
typedef struct {
  int radius;
  int brightness;
} ProcParams;

//valeur par defaut
static ProcParams procParams = { 80, 40 };
static gint32 do_it = FALSE;

static void start(gint32 image_id, GimpDrawable * drawable);
static void procDialog();

GimpPlugInInfo  PLUG_IN_INFO =
                {
                  NULL,         /* init_proc */
                  NULL,         /* quit_proc */
                  query,        /* query_proc */
                  run           /* run_proc */
                };

MAIN()

static void query (void)
{
        static GimpParamDef args[] =
        {
                {GIMP_PDB_INT32,     "run_mode",     "Interactive"},
                {GIMP_PDB_IMAGE,     "image",        "Input image"},
                {GIMP_PDB_DRAWABLE,  "drawable",     "Input drawable"},
		{GIMP_PDB_INT32,     "radius",       "Start radius" },
		{GIMP_PDB_INT32,     "brightness",   "Average brightness" },
        };
        
        static GimpParamDef *return_vals = NULL;
        static guint nargs = sizeof(args) / sizeof(args[0]);
        static guint nreturn_vals = 0;

        gimp_install_procedure
        (
	        PLUGIN_NAME,
                "Vignetting",
                "Make or remove wigneting of photo",
                "Luc Courtrai",
                "Luc Courtrai",
                "2006",
/*
				modification samj novembre 2008
				"<Image>/Filters/Light Effets/Vignetting...",	
*/				
                "<Image>/Filters/Light and Shadow/Vignetting...",
                "GRAY*,RGB*",
                GIMP_PLUGIN,
                nargs,
                nreturn_vals,
                args,
                return_vals
        );

}

static void run   (const char *name, 
		   int n_params, 
		   const GimpParam * param, 
		   int *nreturn_vals, 
		   GimpParam ** return_vals)
{
        static GimpParam values[1];
        GimpRunMode run_mode;
        GimpPDBStatusType status = GIMP_PDB_SUCCESS;
        
        GimpDrawable *drawable;
        gint32 image_id;

        *nreturn_vals = 1;
        *return_vals  = values;

        run_mode = param[0].data.d_int32;
        image_id = param[1].data.d_image;
        drawable = gimp_drawable_get (param[2].data.d_drawable);

        switch (run_mode) {
        case GIMP_RUN_INTERACTIVE:
            gimp_get_data(PLUGIN_NAME, &procParams);
            procDialog();
            if (!do_it) {
	        return;
            }
            break;

        case GIMP_RUN_NONINTERACTIVE:
            if (n_params != 5)
	        status = GIMP_PDB_CALLING_ERROR;
            if (status == GIMP_PDB_SUCCESS) {
	        procParams.radius = param[3].data.d_int32;
	        procParams.brightness = param[4].data.d_int32;
	    }
            break;

        case GIMP_RUN_WITH_LAST_VALS:
            gimp_get_data(PLUGIN_NAME, &procParams);
            break;
    
        default:
            break;
        }

	start (image_id, drawable);

        if (run_mode != GIMP_RUN_NONINTERACTIVE)
            gimp_displays_flush();
        if (run_mode == GIMP_RUN_INTERACTIVE)
            gimp_set_data(PLUGIN_NAME, &procParams, sizeof(ProcParams));

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



void start(gint32 image_id, GimpDrawable* drawable)
{


  float progress;
  float progressStep;
  int progressChunk;

  gint32 x1, y1, x2, y2;
  GimpPixelRgn srcRegion, dstRegion;
  guchar* pr;

  int x, y;



  gint32 centreX= drawable->width /2;
  gint32 centreY= drawable->height /2;


  gint32 distanceMax=
	  sqrt(
          (drawable->width - centreX)*
	  (drawable->width - centreX) +
          ( drawable->height - centreY)*
	     (drawable->height - centreY));

  float  start=procParams.radius/100.;
  int    force=(procParams.brightness*255)/100;


  gimp_drawable_mask_bounds(drawable->drawable_id, &x1, &y1, &x2, &y2);


  /* Initialize progress */
  progress = 0;
  progressStep = 1.0 / ((x2 - x1) * (y2 - y1));
  progressChunk = 0;
  gimp_progress_init(PLUGIN_TITLE);

  gimp_pixel_rgn_init(&srcRegion, drawable,
    x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);

  gimp_pixel_rgn_init(&dstRegion, drawable,
    x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);


  pr = gimp_pixel_rgns_register (2, &srcRegion, &dstRegion);



  for ( ; pr != 0; pr = gimp_pixel_rgns_process(pr)) {
    guchar* src = srcRegion.data;
    //   guchar* blur = blurRegion.data;
    guchar* dst = dstRegion.data;

   


    for (y = 0; y < srcRegion.h; y++) {
      guchar* s = src;
      // guchar* b = blur;
      guchar* d = dst;

      for (x = 0; x < srcRegion.w; x++) {

        //calcul de la distance par rapport au centre
 
        gint32 distance=
	  sqrt(
          ((srcRegion.x + x) - centreX)*
	  ((srcRegion.x + x) - centreX) +
           ((srcRegion.y + y) - centreY)*
	   ((srcRegion.y + y) - centreY));


             
	if (distance <  distanceMax* start) {
          memcpy(d,s,srcRegion.bpp);
	}else{
	  int newR, newG, newB;
          float coef = (distance -(distanceMax *start)) /
                           (float)(distanceMax *(1-start));
          switch (srcRegion.bpp) {
	  case 1 : // NB
	    newR =  s[0]- force*coef;
	    d[0] = (newR < 0) ? 0 : (newR > 255) ? 255 : newR;
	    break;
	  case 2 :
	    newR =  s[0]- force*coef;
	    d[0] = (newR < 0) ? 0 : (newR > 255) ? 255 : newR;
	    d[1] = s[1];
	    break;
	  case 3:
	    newR =  s[0]- force*coef;
	    d[0] = (newR < 0) ? 0 : (newR > 255) ? 255 : newR;
	    newG =  s[1]- force*coef;
	    d[1] = (newG < 0) ? 0 : (newG > 255) ? 255 : newG;
	    newB =  s[2]- force*coef;
	    d[2] = (newB < 0) ? 0 : (newB > 255) ? 255 : newB;
	    break;
	  case 4: 
	    newR =  s[0]- force*coef;
	    d[0] = (newR < 0) ? 0 : (newR > 255) ? 255 : newR;
	    newG =  s[1]- force*coef;
	    d[1] = (newG < 0) ? 0 : (newG > 255) ? 255 : newG;
	    newB =  s[2]- force*coef;
	    d[2] = (newB < 0) ? 0 : (newB > 255) ? 255 : newB;
	    d[3]=s[3];  
	  }


	}
        s += srcRegion.bpp;

        d += dstRegion.bpp;

      }

      src += srcRegion.rowstride;
      dst += dstRegion.rowstride;

    }
    progress += progressStep * srcRegion.w * srcRegion.h;
    ++progressChunk;
    if (progressChunk == 16) {
      gimp_progress_update(progress);
      progressChunk = 0;
    }

  }


  gimp_drawable_flush(drawable);
  gimp_drawable_merge_shadow(drawable->drawable_id, TRUE);
  gimp_drawable_update(drawable->drawable_id, x1, y1, x2 - x1, y2 - y1);
  gimp_displays_flush();
  gimp_drawable_detach(drawable);
}

static void
adjustmentChanged(GtkAdjustment *adjust, gpointer data) {
  int* param = (int*)data;
  *param = adjust->value;
}

static GtkAdjustment*
addControl(GtkWidget* vbox, const char* name, int* data, int min, int max) {

  GtkWidget* label;
  GtkWidget* hbox;
  GtkWidget* hbox2;
  GtkWidget* slider;
  GtkWidget* spinner;
  GtkAdjustment* adjustment;
  gint width;

  label = gtk_label_new(name);

  adjustment = GTK_ADJUSTMENT(gtk_adjustment_new(*data, min, max, 1, 1, 0));

  slider = gtk_hscale_new(adjustment);
  gtk_scale_set_digits(GTK_SCALE(slider), 0);
  gtk_scale_set_draw_value(GTK_SCALE(slider), FALSE);

  spinner = gtk_spin_button_new(adjustment, 0.0, 0);
  /* Need sensible width for value display */
  /* Add a small amount to accommodate the arrows */
  /* Of course, this should be done automagically */
  width = gdk_string_width(gtk_style_get_font(gtk_widget_get_style(spinner)), "-100.0") + 25;
  gtk_widget_set_usize(GTK_WIDGET(spinner), width, 0);

  hbox = gtk_hbox_new(TRUE, 2);
  hbox2 = gtk_hbox_new(TRUE, 2);
  gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(hbox2), spinner, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), slider, TRUE, TRUE, 0);

  gtk_signal_connect(GTK_OBJECT(adjustment), "value_changed",
		      GTK_SIGNAL_FUNC(adjustmentChanged),
		      (gpointer)data);

  gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, FALSE, 0);

  return adjustment;
}

static void
procDialog()
{
  GtkWidget* dialog;
  GtkWidget* vbox;

  int usesPreview = FALSE;
  GtkDialogFlags flags = (GtkDialogFlags)0;

  gimp_ui_init(PLUGIN_NAME, usesPreview);

  dialog = gimp_dialog_new(
    PLUGIN_TITLE,
    PLUGIN_NAME,
    0, flags, 0, 0,
    GTK_STOCK_QUIT, 0,
    GTK_STOCK_OK, 1,
    NULL);

  vbox = GTK_DIALOG(dialog)->vbox;

  addControl(vbox, "Radius start %", &procParams.radius, 0, 100);
  addControl(vbox, "Darkness", &procParams.brightness, -100, 100);

  gtk_widget_show_all(dialog);

  if (gimp_dialog_run(GIMP_DIALOG(dialog))) {
    do_it = TRUE;
  }
}
