/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "../lib/version.h"
#include "appenv.h"
#include "actionarea.h"
#include "expose_image.h"
#include "canvas.h"
#include "drawable.h"
#include "general.h"
#include "gimage_mask.h"
#include "gdisplay.h"
#include "image_map.h"
#include "image_render.h"
#include "interface.h"
#include "pixelarea.h"
#include "pixelrow.h"

#define TEXT_WIDTH 45
#define TEXT_HEIGHT 25
#define SLIDER_WIDTH 200
#define SLIDER_HEIGHT 35

#define GAMMA_SLIDER 0x1
#define EXPOSE_SLIDER 0x2
#define GAMMA_TEXT   0x4
#define EXPOSE_TEXT  0x8
#define ALL          0xF

typedef struct ExposeImage ExposeImage;

struct ExposeImage
{
  int x, y;    /*  coords for last mouse click  */
};

typedef struct ExposeImageDialog ExposeImageDialog;

struct ExposeImageDialog
{
  GtkWidget   *shell;
  GtkWidget   *gimage_name;
  GtkWidget   *gamma_text;
  GtkWidget   *expose_text;
  GtkAdjustment  *gamma_data;
  GtkAdjustment  *expose_data;

  CanvasDrawable *drawable;
  ImageMap     image_map;

  double       gamma;
  double       expose;

  gint         preview;
  gint         forceexpose;
};

/* gamma and expose transfer luts */
static PixelRow gamma_lut;
static PixelRow expose_lut;

/*  gamma expose action functions  */

static void   expose_image_button_press   (Tool *, GdkEventButton *, gpointer);
static void   expose_image_button_release (Tool *, GdkEventButton *, gpointer);
static void   expose_image_motion         (Tool *, GdkEventMotion *, gpointer);
static void   expose_image_cursor_update  (Tool *, GdkEventMotion *, gpointer);
static void   expose_image_control        (Tool *, int, gpointer);

static ExposeImageDialog *  expose_image_new_dialog  (void);
static void   expose_image_update                  (ExposeImageDialog *, int);
static void   expose_image_preview                 (ExposeImageDialog *);
static void   expose_image_ok_callback             (GtkWidget *, gpointer);
static void   expose_image_cancel_callback         (GtkWidget *, gpointer);
static gint   expose_image_delete_callback         (GtkWidget *, GdkEvent *, gpointer);
static void   expose_image_preview_update          (GtkWidget *, gpointer);
static void   expose_image_force_update          (GtkWidget *, gpointer);
static void   expose_image_gamma_scale_update (GtkAdjustment *, gpointer);
static void   expose_image_expose_scale_update   (GtkAdjustment *, gpointer);
static void   expose_image_gamma_text_update  (GtkWidget *, gpointer);
static void   expose_image_expose_text_update    (GtkWidget *, gpointer);
static gint   expose_image_gamma_text_check (char *, ExposeImageDialog *);
static gint   expose_image_expose_text_check (char *, ExposeImageDialog *);

static void *expose_image_options = NULL;
static ExposeImageDialog *expose_image_dialog = NULL;

static Argument * expose_image_invoker  (Argument *);

static double gamma_func (double, double);
static double expose_func (double, double);


/* data type function pointers */
typedef void (*ExposeImageInitTransfersFunc)(void *);
static ExposeImageInitTransfersFunc expose_image_init_transfers;
typedef void (*ExposeImageFunc)(PixelArea *, PixelArea *, void *);
static ExposeImageFunc expose_image;


static void
expose_image_button_press (Tool           *tool,
				  GdkEventButton *bevent,
				  gpointer        gdisp_ptr)
{
  GDisplay *gdisp;

  gdisp = gdisp_ptr;
  tool->drawable = gimage_active_drawable (gdisp->gimage);
  drawable_update (tool->drawable, 0, 0, drawable_width (tool->drawable), drawable_height (tool->drawable));
}

static void
expose_image_button_release (Tool           *tool,
				    GdkEventButton *bevent,
				    gpointer        gdisp_ptr)
{
}

static void
expose_image_motion (Tool           *tool,
			    GdkEventMotion *mevent,
			    gpointer        gdisp_ptr)
{
}

static void
expose_image_cursor_update (Tool           *tool,
				   GdkEventMotion *mevent,
				   gpointer        gdisp_ptr)
{
  GDisplay *gdisp;

  gdisp = (GDisplay *) gdisp_ptr;
  gdisplay_install_tool_cursor (gdisp, GDK_TOP_LEFT_ARROW);
}

static void
expose_image_control (Tool     *tool,
			     int       action,
			     gpointer  gdisp_ptr)
{
  switch (action)
    {
    case PAUSE :
      break;
    case RESUME :
      break;
    case HALT :
      if (expose_image_dialog)
        {
          active_tool->preserve = TRUE;
          image_map_abort (expose_image_dialog->image_map);
          active_tool->preserve = FALSE;
          expose_image_dialog->image_map = NULL;
          expose_image_cancel_callback (NULL, (gpointer) expose_image_dialog);
        }
      break;
    }
}

Tool *
tools_new_expose_image ()
{
  Tool * tool;
  ExposeImage * private;

  /*  The tool options  */
  if (!expose_image_options)
    expose_image_options = tools_register_no_options (EXPOSE_IMAGE,
							     "expose-image Options");

  tool = (Tool *) g_malloc_zero (sizeof (Tool));
  private = (ExposeImage *) g_malloc_zero (sizeof (ExposeImage));

  tool->type = EXPOSE_IMAGE;
  tool->state = INACTIVE;
  tool->scroll_lock = 1;  /*  Disallow scrolling  */
  tool->auto_snap_to = TRUE;
  tool->private = (void *) private;
  tool->button_press_func = expose_image_button_press;
  tool->button_release_func = expose_image_button_release;
  tool->motion_func = expose_image_motion;
  tool->arrow_keys_func = standard_arrow_keys_func;
  tool->cursor_update_func = expose_image_cursor_update;
  tool->control_func = expose_image_control;
  tool->preserve = FALSE;
  tool->gdisp_ptr = NULL;
  tool->drawable = NULL;

  return tool;
}

void
tools_free_expose_image (Tool *tool)
{
  ExposeImage * bc;

  bc = (ExposeImage *) tool->private;

  /*  Close the color select dialog  */
  if (expose_image_dialog)
    expose_image_cancel_callback (NULL, (gpointer) expose_image_dialog);

  g_free (bc);
}

void
expose_image_initialize (void *gdisp_ptr)
{
  GDisplay *gdisp;
  CanvasDrawable *drawable;

  gdisp = (GDisplay *) gdisp_ptr;

  if (drawable_indexed (gimage_active_drawable (gdisp->gimage)))
    {
      g_message ("expose-image does not operate on indexed drawables.");
      return;
    }
  /*  The expose-image dialog  */
  if (!expose_image_dialog)
    expose_image_dialog = expose_image_new_dialog ();
  else
    if (!GTK_WIDGET_VISIBLE (expose_image_dialog->shell))
      gtk_widget_show (expose_image_dialog->shell);

  drawable = gimage_active_drawable (gdisp->gimage);
  
  /*  Initialize dialog fields  */
  expose_image_dialog->image_map = NULL;
  expose_image_dialog->gamma = 1.0;
  expose_image_dialog->expose = 0.0;

  expose_image_dialog->drawable = drawable;
  expose_image_dialog->image_map = image_map_create (gdisp_ptr,
							    expose_image_dialog->drawable);

  expose_image_update (expose_image_dialog, ALL);
}


/********************************/
/*  gamma expose dialog  */
/********************************/

/*  the action area structure  */
static ActionAreaItem action_items[] =
{
  { "OK", expose_image_ok_callback, NULL, NULL },
  { "Cancel", expose_image_cancel_callback, NULL, NULL }
};

static ExposeImageDialog *
expose_image_new_dialog ()
{
  ExposeImageDialog *bcd;
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *table;
  GtkWidget *label;
  GtkWidget *slider;
  GtkWidget *toggle;
  GtkWidget *forceexpose;
  GtkObject *data;

  bcd = g_malloc_zero (sizeof (ExposeImageDialog));
  bcd->preview = TRUE;
  bcd->forceexpose = TRUE;

  /*  The shell and main vbox  */
  bcd->shell = gtk_dialog_new ();
  gtk_window_set_wmclass (GTK_WINDOW (bcd->shell), "expose_image", PROGRAM_NAME);
  gtk_window_set_title (GTK_WINDOW (bcd->shell), "expose-image");
  
  /* handle wm close signal */
  gtk_signal_connect (GTK_OBJECT (bcd->shell), "delete_event",
		      GTK_SIGNAL_FUNC (expose_image_delete_callback),
		      bcd);

  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_border_width (GTK_CONTAINER (vbox), 2);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (bcd->shell)->vbox), vbox, TRUE, TRUE, 0);

  /*  The table containing sliders  */
  table = gtk_table_new (2, 3, FALSE);
  gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);

  /*  Create the gamma scale widget  */
  label = gtk_label_new ("gamma");
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
		    GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 2, 2);

  data = gtk_adjustment_new (2.2, -10, 10, .001, .01, .01);
  bcd->gamma_data = GTK_ADJUSTMENT (data);
  slider = gtk_hscale_new (GTK_ADJUSTMENT (data));
  gtk_widget_set_usize (slider, SLIDER_WIDTH, SLIDER_HEIGHT);
  gtk_scale_set_digits (GTK_SCALE (slider), 3);
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
  gtk_table_attach (GTK_TABLE (table), slider, 1, 2, 0, 1,
		    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
		    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
		    2, 2);
  gtk_signal_connect (GTK_OBJECT (data), "value_changed",
		      (GtkSignalFunc) expose_image_gamma_scale_update,
		      bcd);

  bcd->gamma_text = gtk_entry_new ();
  gtk_widget_set_usize (bcd->gamma_text, TEXT_WIDTH, TEXT_HEIGHT);
  gtk_table_attach (GTK_TABLE (table), bcd->gamma_text, 2, 3, 0, 1,
		    GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 2, 2);
  gtk_signal_connect (GTK_OBJECT (bcd->gamma_text), "changed",
		      (GtkSignalFunc) expose_image_gamma_text_update,
		      bcd);

  gtk_widget_show (label);
  gtk_widget_show (bcd->gamma_text);
  gtk_widget_show (slider);


  /*  Create the expose scale widget  */
  label = gtk_label_new ("expose");
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
		    GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 2, 2);

  data = gtk_adjustment_new (0, -10, 10, .01, .01, .01);
  bcd->expose_data = GTK_ADJUSTMENT (data);
  slider = gtk_hscale_new (GTK_ADJUSTMENT (data));
  gtk_widget_set_usize (slider, SLIDER_WIDTH, SLIDER_HEIGHT);
  gtk_scale_set_digits (GTK_SCALE (slider), 3);
  gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
  gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
  gtk_table_attach (GTK_TABLE (table), slider, 1, 2, 1, 2,
		    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
		    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
		    2, 2);
  gtk_signal_connect (GTK_OBJECT (data), "value_changed",
		      (GtkSignalFunc) expose_image_expose_scale_update,
		      bcd);

  bcd->expose_text = gtk_entry_new ();
  gtk_widget_set_usize (bcd->expose_text, TEXT_WIDTH, TEXT_HEIGHT);
  gtk_table_attach (GTK_TABLE (table), bcd->expose_text, 2, 3, 1, 2,
		    GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 2, 2);
  gtk_signal_connect (GTK_OBJECT (bcd->expose_text), "changed",
		      (GtkSignalFunc) expose_image_expose_text_update,
		      bcd);

  gtk_widget_show (label);
  gtk_widget_show (bcd->expose_text);
  gtk_widget_show (slider);


  /*  Horizontal box for preview and preserve luminosity toggle buttons  */
  hbox = gtk_hbox_new (TRUE, 2);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

  /*  The preview toggle  */
  toggle = gtk_check_button_new_with_label ("Preview");
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), bcd->preview);
  gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      (GtkSignalFunc) expose_image_preview_update,
		      bcd);

  gtk_widget_show (label);
  gtk_widget_show (toggle);
  gtk_widget_show (hbox);

  /*  The expose toggle  */
  forceexpose = gtk_check_button_new_with_label ("ForceExpose");
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), bcd->forceexpose);
  gtk_box_pack_start (GTK_BOX (hbox), forceexpose, TRUE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (forceexpose), "toggled",
		      (GtkSignalFunc) expose_image_force_update,
		      bcd);

  //gtk_widget_show (label);
  gtk_widget_show (forceexpose);


  /*  The action area  */
  action_items[0].user_data = bcd;
  action_items[1].user_data = bcd;
  build_action_area (GTK_DIALOG (bcd->shell), action_items, 2, 0);

  gtk_widget_show (table);
  gtk_widget_show (vbox);
  gtk_widget_show (bcd->shell);

  return bcd;
}

static void
expose_image_update (ExposeImageDialog *bcd,
			    int                       update)
{
  char text[12];

  if (update & GAMMA_SLIDER)
    {
      bcd->gamma_data->value = bcd->gamma;
      gtk_signal_emit_by_name (GTK_OBJECT (bcd->gamma_data), "value_changed");
    }
  if (update & EXPOSE_SLIDER)
    {
      bcd->expose_data->value = bcd->expose;
      gtk_signal_emit_by_name (GTK_OBJECT (bcd->expose_data), "value_changed");
    }
  if (update & GAMMA_TEXT)
    {
      sprintf (text, "%0.3f", bcd->gamma);
      gtk_entry_set_text (GTK_ENTRY (bcd->gamma_text), text);
    }
  if (update & EXPOSE_TEXT)
    {
      sprintf (text, "%0.3f", bcd->expose);
      gtk_entry_set_text (GTK_ENTRY (bcd->expose_text), text);
    }
  image_render_set_gamma(bcd->gamma);
  image_render_set_expose(bcd->expose);
  d_printf("expose_image_update %f %f \n", image_render_get_gamma(), image_render_get_expose());
  drawable_update (expose_image_dialog->drawable, 0, 0, drawable_width (expose_image_dialog->drawable), drawable_height (expose_image_dialog->drawable));
}

static void
expose_image_preview (ExposeImageDialog *bcd)
{
//  if (!bcd->image_map)
//    g_message ("expose_image_preview(): No image map");
  active_tool->preserve = TRUE;
  image_render_set_gamma(bcd->gamma);
  image_render_set_expose(bcd->expose);
  d_printf("expose_image_preview %f %f \n", image_render_get_gamma(), image_render_get_expose());
  active_tool->preserve = FALSE;

  drawable_update (expose_image_dialog->drawable, 0, 0, drawable_width (expose_image_dialog->drawable), drawable_height (expose_image_dialog->drawable));
}

static void
expose_image_ok_callback (GtkWidget *widget,
				 gpointer   client_data)
{
  ExposeImageDialog *bcd;

  bcd = (ExposeImageDialog *) client_data;

  if (GTK_WIDGET_VISIBLE (bcd->shell))
    gtk_widget_hide (bcd->shell);

  bcd->image_map = NULL;
}

static gint
expose_image_delete_callback (GtkWidget *w,
				     GdkEvent *e,
				     gpointer d)
{
  expose_image_cancel_callback (w, d);

  return TRUE;
}

static void
expose_image_cancel_callback (GtkWidget *widget,
				     gpointer   client_data)
{
  ExposeImageDialog *bcd;

  bcd = (ExposeImageDialog *) client_data;
  if (GTK_WIDGET_VISIBLE (bcd->shell))
    gtk_widget_hide (bcd->shell);

  if (bcd->image_map)
    {
      active_tool->preserve = TRUE;
      image_map_abort (bcd->image_map);
      active_tool->preserve = FALSE;
      gdisplays_dirty ();
      gdisplays_flush ();
    }

  bcd->image_map = NULL;
}

static void
expose_image_preview_update (GtkWidget *w,
				    gpointer   data)
{
  GDisplay *gdisp;
  ExposeImageDialog *bcd;

  bcd = (ExposeImageDialog *) data;

  if (GTK_TOGGLE_BUTTON (w)->active)
    {
      bcd->preview = TRUE;
      expose_image_preview (bcd);
    }
  else
    bcd->preview = FALSE;

  gdisplays_dirty ();
  gdisplays_flush ();
}

static void
expose_image_force_update (GtkWidget *w,
				    gpointer   data)
{
  ExposeImageDialog *bcd;

  bcd = (ExposeImageDialog *) data;

  if (GTK_TOGGLE_BUTTON (w)->active)
    {
      bcd->forceexpose = TRUE;
      expose_image_preview (bcd);
      image_render_set_force_expose(bcd->forceexpose);
    }
  else{
    bcd->forceexpose = FALSE;
    image_render_set_force_expose(bcd->forceexpose);
  }
  gdisplays_dirty ();
  gdisplays_flush ();
}


static void
expose_image_gamma_scale_update (GtkAdjustment *adjustment,
					     gpointer       data)
{
  ExposeImageDialog *bcd;

  bcd = (ExposeImageDialog *) data;

  if (bcd->gamma != adjustment->value)
    {
      bcd->gamma = adjustment->value;
      expose_image_update (bcd, GAMMA_TEXT);


//      if (bcd->preview)
	expose_image_preview (bcd);
    } 
  gdisplays_dirty ();
  gdisplays_flush ();
}

static void
expose_image_expose_scale_update (GtkAdjustment *adjustment,
					   gpointer       data)
{
  ExposeImageDialog *bcd;

  bcd = (ExposeImageDialog *) data;

  if (bcd->expose != adjustment->value)
    {
      bcd->expose = adjustment->value;
      expose_image_update (bcd, EXPOSE_TEXT);

//      if (bcd->preview)
	expose_image_preview (bcd);
    }
}

static void
expose_image_gamma_text_update (GtkWidget *w,
					    gpointer   data)
{
  ExposeImageDialog *bcd = (ExposeImageDialog *) data;
  char *str = gtk_entry_get_text (GTK_ENTRY (w));
  
  if (expose_image_gamma_text_check (str, bcd))
  {
      expose_image_update (bcd, GAMMA_SLIDER);

//      if (bcd->preview)
	expose_image_preview (bcd);
  }
}

static gint 
expose_image_gamma_text_check (
				char *str, 
				ExposeImageDialog *bcd
				)
{
  gfloat value;

  value = BOUNDS (atof (str), -20, 20);
  if (bcd->gamma != value)
  {
      bcd->gamma = value;
      return TRUE;
  }
  return FALSE;
}

static void
expose_image_expose_text_update (GtkWidget *w,
					  gpointer   data)
{
  ExposeImageDialog *bcd = (ExposeImageDialog *) data;
  char *str = gtk_entry_get_text (GTK_ENTRY (w));

  if (expose_image_expose_text_check (str, bcd))
    {
      expose_image_update (bcd, EXPOSE_SLIDER);

      if (bcd->preview)
	expose_image_preview (bcd);
    }
}

static gint 
expose_image_expose_text_check  (
				char *str, 
				ExposeImageDialog *bcd
				)
{
  gfloat value;

  value = BOUNDS (atof (str), -20, 20);
  if (bcd->expose != value)
  {
      bcd->expose = value;
      return TRUE;
  }
  return FALSE;
}


/*  The expose_image procedure definition  */
ProcArg expose_image_args[] =
{
  { PDB_IMAGE,
    "image",
    "the image"
  },
  { PDB_DRAWABLE,
    "drawable",
    "the drawable"
  },
  { PDB_INT32,
    "gamma",
    "gamma adjustment: (-20 <= gamma <= 20)"
  },
  { PDB_INT32,
    "exposure",
    "exposure adjustment: (-20 <= exposure <= 20)"
  }
};

ProcRecord expose_image_proc =
{
  "gimp_expose_image",
  "Modify gamma/exposure in the specified drawable",
  "This procedures allows the gamma and exposure of the specified drawable to be modified.  Both 'gamma' and 'exposure' parameters are defined between -20 and 20.",
  "Alan Davidson",
  "Alan Davidson",
  "2001",
  PDB_INTERNAL,

  /*  Input arguments  */
  4,
  expose_image_args,

  /*  Output arguments  */
  0,
  NULL,

  /*  Exec method  */
  { { expose_image_invoker } },
};


static Argument *
expose_image_invoker (Argument *args)
{
  PixelArea src_area, dest_area;
  int success = TRUE;
  int int_value;
  ExposeImageDialog bcd;
  GImage *gimage;
  int gamma;
  int expose;
  int x1, y1, x2, y2;
  void *pr;
  CanvasDrawable *drawable;

  drawable  = NULL;
  gamma     = 1.0;
  expose    = 0;

  /*  the gimage  */
  if (success)
    {
      int_value = args[0].value.pdb_int;
      if (! (gimage = gimage_get_ID (int_value)))
        success = FALSE;
    }
  /*  the drawable  */
  if (success)
    {
      int_value = args[1].value.pdb_int;
      drawable = drawable_get_ID (int_value);
      if (drawable == NULL || gimage != drawable_gimage (drawable))
        success = FALSE;
    }
  /*  make sure the drawable is not indexed color  */
  if (success)
    success = ! drawable_indexed (drawable);

  /*  gamma  */
  if (success)
    {
      int_value = args[2].value.pdb_int;
      if (int_value < -127 || int_value > 127)
        success = FALSE;
      else
        gamma = int_value;
    }
  /*  expose  */
  if (success)
    {
      int_value = args[3].value.pdb_int;
      if (int_value < -127 || int_value > 127)
        success = FALSE;
      else
        expose = int_value;
    }

#if 0    
  /*  arrange to modify the gamma/expose  */
  if (success)
    {
      bcd.gamma = gamma;
      bcd.expose = expose;
        
      pixelrow_init (&gamma_lut, tag_null(), NULL , 0); 
      pixelrow_init (&expose_lut, tag_null(), NULL, 0); 
      (*expose_image_init_transfers)(&bcd);
      
      /*  The application should occur only within selection bounds  */
      drawable_mask_bounds (drawable, &x1, &y1, &x2, &y2);
         
      pixelarea_init (&src_area, drawable_data (drawable),
			x1, y1, (x2 - x1), (y2 - y1), FALSE);
      pixelarea_init (&dest_area, drawable_shadow (drawable), 
			x1, y1, (x2 - x1), (y2 - y1), TRUE);

      for (pr = pixelarea_register (2, &src_area, &dest_area); 
		pr != NULL; 
		pr = pixelarea_process (pr))
        (*expose_image) (&src_area, &dest_area, (void *) &bcd);

      drawable_merge_shadow (drawable, TRUE);
      drawable_update (drawable, x1, y1, (x2 - x1), (y2 - y1));
    }

#endif  
      drawable_mask_bounds (drawable, &x1, &y1, &x2, &y2);
      drawable_merge_shadow (drawable, TRUE);
      drawable_update (drawable, x1, y1, (x2 - x1), (y2 - y1));
  return procedural_db_return_args (&expose_image_proc, success);
}
