[gthumb/ext] [adjust colors] added histogram, color levels and reset button



commit e31546c31531b5b9b2779903b1e56177ed034597
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sat Sep 19 20:20:41 2009 +0200

    [adjust colors] added histogram, color levels and reset button

 .../file_tools/data/ui/adjust-colors-options.ui    |  123 ++++-
 .../file_tools/gth-file-tool-adjust-colors.c       |  393 ++++------------
 gthumb/Makefile.am                                 |    2 +
 gthumb/gth-histogram-view.c                        |  501 ++++++++++++++++++++
 gthumb/gth-histogram-view.h                        |   82 ++++
 gthumb/gth-histogram.c                             |  208 ++++++---
 gthumb/gth-histogram.h                             |   53 ++-
 7 files changed, 970 insertions(+), 392 deletions(-)
---
diff --git a/extensions/file_tools/data/ui/adjust-colors-options.ui b/extensions/file_tools/data/ui/adjust-colors-options.ui
index 7160faa..0099a5d 100644
--- a/extensions/file_tools/data/ui/adjust-colors-options.ui
+++ b/extensions/file_tools/data/ui/adjust-colors-options.ui
@@ -16,9 +16,21 @@
             <property name="orientation">vertical</property>
             <property name="spacing">12</property>
             <child>
+              <object class="GtkHBox" id="histogram_hbox">
+                <property name="height_request">60</property>
+                <property name="visible">True</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
               <object class="GtkTable" id="table1">
                 <property name="visible">True</property>
-                <property name="n_rows">6</property>
+                <property name="n_rows">8</property>
                 <property name="n_columns">2</property>
                 <property name="column_spacing">6</property>
                 <property name="row_spacing">6</property>
@@ -127,34 +139,69 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkHBox" id="hue_hbox">
+                  <object class="GtkLabel" id="label6">
                     <property name="visible">True</property>
-                    <child>
-                      <placeholder/>
-                    </child>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Cyan-_Red:</property>
+                    <property name="use_underline">True</property>
                   </object>
                   <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
+                    <property name="top_attach">5</property>
+                    <property name="bottom_attach">6</property>
+                    <property name="x_options">GTK_FILL</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkLabel" id="label5">
+                  <object class="GtkLabel" id="label7">
                     <property name="visible">True</property>
                     <property name="xalign">0</property>
-                    <property name="label" translatable="yes">_Hue:</property>
+                    <property name="label" translatable="yes">_Magenta-Green:</property>
                     <property name="use_underline">True</property>
                   </object>
                   <packing>
+                    <property name="top_attach">6</property>
+                    <property name="bottom_attach">7</property>
+                    <property name="x_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label8">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">_Yellow-Blue:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">7</property>
+                    <property name="bottom_attach">8</property>
+                    <property name="x_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkAlignment" id="alignment1">
+                    <property name="visible">True</property>
+                    <property name="top_padding">12</property>
+                    <property name="bottom_padding">6</property>
+                    <child>
+                      <object class="GtkLabel" id="label5">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Color Levels</property>
+                        <property name="use_underline">True</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                        </attributes>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="right_attach">2</property>
                     <property name="top_attach">4</property>
                     <property name="bottom_attach">5</property>
-                    <property name="x_options">GTK_FILL</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkHBox" id="lightness_hbox">
+                  <object class="GtkHBox" id="cyan_red_hbox">
                     <property name="visible">True</property>
                     <child>
                       <placeholder/>
@@ -168,21 +215,57 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkLabel" id="label6">
+                  <object class="GtkHBox" id="magenta_green_hbox">
                     <property name="visible">True</property>
-                    <property name="xalign">0</property>
-                    <property name="label" translatable="yes">_Lightness:</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">6</property>
+                    <property name="bottom_attach">7</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="yellow_blue_hbox">
+                    <property name="visible">True</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">7</property>
+                    <property name="bottom_attach">8</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox2">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkButton" id="reset_button">
+                    <property name="label" translatable="yes">_Reset</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
                     <property name="use_underline">True</property>
                   </object>
                   <packing>
-                    <property name="top_attach">5</property>
-                    <property name="bottom_attach">6</property>
-                    <property name="x_options">GTK_FILL</property>
+                    <property name="expand">False</property>
+                    <property name="position">0</property>
                   </packing>
                 </child>
               </object>
               <packing>
-                <property name="position">0</property>
+                <property name="position">2</property>
               </packing>
             </child>
           </object>
diff --git a/extensions/file_tools/gth-file-tool-adjust-colors.c b/extensions/file_tools/gth-file-tool-adjust-colors.c
index 45d56a0..1f6f1cd 100644
--- a/extensions/file_tools/gth-file-tool-adjust-colors.c
+++ b/extensions/file_tools/gth-file-tool-adjust-colors.c
@@ -28,216 +28,13 @@
 
 
 #define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
-#define APPLY_DELAY 250
+#define APPLY_DELAY 150
+#define SQR(x) ((x) * (x))
 
 
 static gpointer parent_class = NULL;
 
 
-/* -- gimpcolorspace -- */
-
-
-static void
-gimp_rgb_to_hls_int (gint *red,
-		     gint *green,
-		     gint *blue)
-{
-  gint    r, g, b;
-  gdouble h, l, s;
-  gint    min, max;
-  gint    delta;
-
-  r = *red;
-  g = *green;
-  b = *blue;
-
-  if (r > g)
-    {
-      max = MAX (r, b);
-      min = MIN (g, b);
-    }
-  else
-    {
-      max = MAX (g, b);
-      min = MIN (r, b);
-    }
-
-  l = (max + min) / 2.0;
-
-  if (max == min)
-    {
-      s = 0.0;
-      h = 0.0;
-    }
-  else
-    {
-      delta = (max - min);
-
-      if (l < 128)
-	s = 255 * (gdouble) delta / (gdouble) (max + min);
-      else
-	s = 255 * (gdouble) delta / (gdouble) (511 - max - min);
-
-      if (r == max)
-	h = (g - b) / (gdouble) delta;
-      else if (g == max)
-	h = 2 + (b - r) / (gdouble) delta;
-      else
-	h = 4 + (r - g) / (gdouble) delta;
-
-      h = h * 42.5;
-
-      if (h < 0)
-	h += 255;
-      else if (h > 255)
-	h -= 255;
-    }
-
-  *red   = h;
-  *green = l;
-  *blue  = s;
-}
-
-
-static gint
-gimp_hls_value (gdouble n1,
-		gdouble n2,
-		gdouble hue)
-{
-  gdouble value;
-
-  if (hue > 255)
-    hue -= 255;
-  else if (hue < 0)
-    hue += 255;
-  if (hue < 42.5)
-    value = n1 + (n2 - n1) * (hue / 42.5);
-  else if (hue < 127.5)
-    value = n2;
-  else if (hue < 170)
-    value = n1 + (n2 - n1) * ((170 - hue) / 42.5);
-  else
-    value = n1;
-
-  return (gint) (value * 255);
-}
-
-
-static void
-gimp_hls_to_rgb_int (gint *hue,
-		     gint *lightness,
-		     gint *saturation)
-{
-  gdouble h, l, s;
-  gdouble m1, m2;
-
-  h = *hue;
-  l = *lightness;
-  s = *saturation;
-
-  if (s == 0)
-    {
-      /*  achromatic case  */
-      *hue        = l;
-      *lightness  = l;
-      *saturation = l;
-    }
-  else
-    {
-      if (l < 128)
-	m2 = (l * (255 + s)) / 65025.0;
-      else
-	m2 = (l + s - (l * s) / 255.0) / 255.0;
-
-      m1 = (l / 127.5) - m2;
-
-      /*  chromatic case  */
-      *hue        = gimp_hls_value (m1, m2, h + 85);
-      *lightness  = gimp_hls_value (m1, m2, h);
-      *saturation = gimp_hls_value (m1, m2, h - 85);
-    }
-}
-
-
-/* -- hue, lightness, saturation -- */
-
-
-typedef enum {
-	GIMP_ALL_HUES,
-	GIMP_RED_HUES,
-	GIMP_YELLOW_HUES,
-	GIMP_GREEN_HUES,
-	GIMP_CYAN_HUES,
-	GIMP_BLUE_HUES,
-	GIMP_MAGENTA_HUES
-} GimpHueRange;
-
-
-typedef struct {
-	double hue[7];
-	double lightness[7];
-	double saturation[7];
-	int    hue_transfer[6][256];
-	int    lightness_transfer[6][256];
-	int    saturation_transfer[6][256];
-} HueSaturationData;
-
-
-static void
-hue_saturation_data_init (HueSaturationData *hs)
-{
-	GimpHueRange partition;
-
-	g_return_if_fail (hs != NULL);
-
-	for (partition = GIMP_ALL_HUES; partition <= GIMP_MAGENTA_HUES; partition++) {
-		hs->hue[partition]        = 0.0;
-		hs->lightness[partition]  = 0.0;
-		hs->saturation[partition] = 0.0;
-	}
-}
-
-
-static void
-hue_saturation_calculate_transfers (HueSaturationData *hs)
-{
-	int value;
-	int hue;
-	int i;
-
-	g_return_if_fail (hs != NULL);
-
-	/*  Calculate transfers  */
-	for (hue = 0; hue < 6; hue++)
-		for (i = 0; i < 256; i++) {
-			/* Hue */
-			value = (hs->hue[0] + hs->hue[hue + 1]) * 255.0 / 360.0;
-			if ((i + value) < 0)
-				hs->hue_transfer[hue][i] = 255 + (i + value);
-			else if ((i + value) > 255)
-				hs->hue_transfer[hue][i] = i + value - 255;
-			else
-				hs->hue_transfer[hue][i] = i + value;
-
-			/*  Lightness  */
-			value = (hs->lightness[0] + hs->lightness[hue + 1]) * 127.0 / 100.0;
-			value = CLAMP (value, -255, 255);
-			if (value < 0)
-				hs->lightness_transfer[hue][i] = (unsigned char) ((i * (255 + value)) / 255);
-			else
-				hs->lightness_transfer[hue][i] = (unsigned char) (i + ((255 - i) * value) / 255);
-
-			/*  Saturation  */
-			value = (hs->saturation[0] + hs->saturation[hue + 1]) * 255.0 / 100.0;
-			value = CLAMP (value, -255, 255);
-			hs->saturation_transfer[hue][i] = CLAMP ((i * (255 + value)) / 255, 0, 255);
-		}
-}
-
-
-/* --- */
-
-
 struct _GthFileToolAdjustColorsPrivate {
 	GdkPixbuf     *src_pixbuf;
 	GdkPixbuf     *dest_pixbuf;
@@ -246,26 +43,40 @@ struct _GthFileToolAdjustColorsPrivate {
 	GtkAdjustment *brightness_adj;
 	GtkAdjustment *contrast_adj;
 	GtkAdjustment *saturation_adj;
-	GtkAdjustment *hue_adj;
-	GtkAdjustment *lightness_adj;
+	GtkAdjustment *cyan_red_adj;
+	GtkAdjustment *magenta_green_adj;
+	GtkAdjustment *yellow_blue_adj;
+	GtkWidget     *histogram_view;
+	GthHistogram  *histogram;
 	GthTask       *pixbuf_task;
 	guint          apply_event;
 };
 
 
 typedef struct {
-	GtkWidget         *viewer_page;
-	double             gamma;
-	double             brightness;
-	double             contrast;
-	double             saturation;
-	double             hue;
-	double             lightness;
-	PixbufCache       *cache;
-	HueSaturationData *hs;
+	GtkWidget   *viewer_page;
+	double       gamma;
+	double       brightness;
+	double       contrast;
+	double       saturation;
+	double       color_level[3];
+	PixbufCache *cache;
+	double       midtone_distance[256];
 } AdjustData;
 
 
+static void
+adjust_colors_init (GthPixbufTask *pixop)
+{
+	AdjustData *data = pixop->data;
+	int         i;
+
+	data->cache = pixbuf_cache_new ();
+	for (i = 0; i < 256; i++)
+		data->midtone_distance[i] = 0.667 * (1 - SQR (((double) i - 127.0) / 127.0));
+}
+
+
 static guchar
 interpolate_value (guchar original,
 		   guchar reference,
@@ -295,88 +106,49 @@ gamma_correction (guchar original,
 
 
 static void
-adjust_colors_init (GthPixbufTask *pixop)
+adjust_colors_step (GthPixbufTask *pixop)
 {
 	AdjustData *data = pixop->data;
+	int         channel;
 
-	data->hs = g_new (HueSaturationData, 1);
-	hue_saturation_data_init (data->hs);
-	data->hs->hue[GIMP_ALL_HUES] = data->hue /* -180.0 ==> 180.0 */;
-	data->hs->lightness[GIMP_ALL_HUES] = data->lightness /* -100.0 ==> 100.0 */;
-	data->hs->saturation[GIMP_ALL_HUES] = data->saturation * (- 100.0)  /* -100.0 ==> 100.0 */;
-	hue_saturation_calculate_transfers (data->hs);
-
-	data->cache = pixbuf_cache_new ();
-}
-
+	if (pixop->has_alpha)
+		pixop->dest_pixel[ALPHA_PIX] = pixop->src_pixel[ALPHA_PIX];
 
-static void
-hue_saturation_step (GthPixbufTask *pixop)
-{
-	AdjustData        *data = pixop->data;
-	HueSaturationData *hs = data->hs;
-	int                r, g, b, hue_idx;
-
-	r = pixop->dest_pixel[RED_PIX];
-	g = pixop->dest_pixel[GREEN_PIX];
-	b = pixop->dest_pixel[BLUE_PIX];
-
-	gimp_rgb_to_hls_int (&r, &g, &b);
-
-	if (r < 43)
-		hue_idx = 0;
-	else if (r < 85)
-		hue_idx = 1;
-	else if (r < 128)
-		hue_idx = 2;
-	else if (r < 171)
-		hue_idx = 3;
-	else if (r < 213)
-		hue_idx = 4;
-	else
-		hue_idx = 5;
+	/* gamma correction / brightness / contrast */
 
-	r = hs->hue_transfer[hue_idx][r];
-	g = hs->lightness_transfer[hue_idx][g];
-	b = hs->saturation_transfer[hue_idx][b];
+	for (channel = RED_PIX; channel <= BLUE_PIX; channel++) {
+		guchar v;
 
-	gimp_hls_to_rgb_int (&r, &g, &b);
+		v = pixop->src_pixel[channel];
+		if (! pixbuf_cache_get (data->cache, channel + 1, &v)) {
+			int i;
 
-	pixop->dest_pixel[RED_PIX] = r;
-	pixop->dest_pixel[GREEN_PIX] = g;
-	pixop->dest_pixel[BLUE_PIX] = b;
-}
+			v = gamma_correction (v, data->gamma);
 
+			if (data->brightness > 0)
+				v = interpolate_value (v, 0, data->brightness);
+			else
+				v = interpolate_value (v, 255, - data->brightness);
 
-static void
-adjust_colors_step (GthPixbufTask *pixop)
-{
-	AdjustData *data = pixop->data;
-	int         channel;
+			if (data->contrast < 0)
+				v = interpolate_value (v, 127, tan (data->contrast * G_PI_2) /*data->contrast*/);
+			else
+				v = interpolate_value (v, 127, data->contrast);
 
-	if (pixop->has_alpha)
-		pixop->dest_pixel[ALPHA_PIX] = pixop->src_pixel[ALPHA_PIX];
+			i = v + data->color_level[channel] * data->midtone_distance[v];
+			v = CLAMP(i, 0, 255);
 
-	for (channel = RED_PIX; channel <= BLUE_PIX; channel++) {
-		pixop->dest_pixel[channel] = pixop->src_pixel[channel];
-		if (! pixbuf_cache_get (data->cache, channel + 1, &pixop->dest_pixel[channel])) {
-			pixop->dest_pixel[channel] = gamma_correction (pixop->dest_pixel[channel], data->gamma);
-			pixop->dest_pixel[channel] = interpolate_value (pixop->dest_pixel[channel], 0, data->brightness);
-			pixop->dest_pixel[channel] = interpolate_value (pixop->dest_pixel[channel], 127, data->contrast);
-			pixbuf_cache_set (data->cache, channel + 1, pixop->src_pixel[channel], pixop->dest_pixel[channel]);
+			pixbuf_cache_set (data->cache, channel + 1, pixop->src_pixel[channel], v);
 		}
-	}
 
-#if 1
-	if ((data->saturation != 0.0) || (data->hue != 0.0) || (data->lightness != 0))
-		hue_saturation_step (pixop);
-#endif
+		pixop->dest_pixel[channel] = v;
+	}
 
-#if 0
 	/* saturation */
 
 	if (data->saturation != 0.0) {
 		guchar min, max, lightness;
+		double saturation;
 
 		max = MAX (pixop->dest_pixel[RED_PIX], pixop->dest_pixel[GREEN_PIX]);
 		max = MAX (max, pixop->dest_pixel[BLUE_PIX]);
@@ -384,11 +156,15 @@ adjust_colors_step (GthPixbufTask *pixop)
 		min = MIN (min, pixop->dest_pixel[BLUE_PIX]);
 		lightness = (max + min) / 2;
 
-		pixop->dest_pixel[RED_PIX] = interpolate_value (pixop->dest_pixel[RED_PIX], lightness, data->saturation);
-		pixop->dest_pixel[GREEN_PIX] = interpolate_value (pixop->dest_pixel[GREEN_PIX], lightness, data->saturation);
-		pixop->dest_pixel[BLUE_PIX] = interpolate_value (pixop->dest_pixel[BLUE_PIX], lightness, data->saturation);
+		if (data->saturation < 0)
+			saturation = tan (data->saturation * G_PI_2);
+		else
+			saturation = data->saturation;
+
+		pixop->dest_pixel[RED_PIX] = interpolate_value (pixop->dest_pixel[RED_PIX], lightness, saturation);
+		pixop->dest_pixel[GREEN_PIX] = interpolate_value (pixop->dest_pixel[GREEN_PIX], lightness, saturation);
+		pixop->dest_pixel[BLUE_PIX] = interpolate_value (pixop->dest_pixel[BLUE_PIX], lightness, saturation);
 	}
-#endif
 }
 
 
@@ -400,7 +176,6 @@ adjust_colors_release (GthPixbufTask *pixop,
 
 	g_object_unref (data->viewer_page);
 	pixbuf_cache_free (data->cache);
-	g_free (data->hs);
 	g_free (data);
 }
 
@@ -455,6 +230,20 @@ cancel_button_clicked_cb (GtkButton               *button,
 
 
 static void
+reset_button_clicked_cb (GtkButton               *button,
+			 GthFileToolAdjustColors *self)
+{
+	gtk_adjustment_set_value (self->priv->gamma_adj, 0.0);
+	gtk_adjustment_set_value (self->priv->brightness_adj, 0.0);
+	gtk_adjustment_set_value (self->priv->contrast_adj, 0.0);
+	gtk_adjustment_set_value (self->priv->saturation_adj, 0.0);
+	gtk_adjustment_set_value (self->priv->cyan_red_adj, 0.0);
+	gtk_adjustment_set_value (self->priv->magenta_green_adj, 0.0);
+	gtk_adjustment_set_value (self->priv->yellow_blue_adj, 0.0);
+}
+
+
+static void
 task_completed_cb (GthTask                 *task,
 		   GError                  *error,
 		   GthFileToolAdjustColors *self)
@@ -470,6 +259,7 @@ task_completed_cb (GthTask                 *task,
 		window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
 		viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
 		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page), self->priv->dest_pixbuf, FALSE);
+		gth_histogram_calculate (self->priv->histogram, self->priv->dest_pixbuf);
 	}
 }
 
@@ -498,8 +288,9 @@ apply_cb (gpointer user_data)
 	data->brightness = gtk_adjustment_get_value (self->priv->brightness_adj) / 100.0 * -1.0;
 	data->contrast = gtk_adjustment_get_value (self->priv->contrast_adj) / 100.0 * -1.0;
 	data->saturation = gtk_adjustment_get_value (self->priv->saturation_adj) / 100.0 * -1.0;
-	data->hue = gtk_adjustment_get_value (self->priv->hue_adj);
-	data->lightness = gtk_adjustment_get_value (self->priv->lightness_adj);
+	data->color_level[0] = gtk_adjustment_get_value (self->priv->cyan_red_adj);
+	data->color_level[1] = gtk_adjustment_get_value (self->priv->magenta_green_adj);
+	data->color_level[2] = gtk_adjustment_get_value (self->priv->yellow_blue_adj);
 
 	self->priv->pixbuf_task = gth_pixbuf_task_new (_("Applying changes"),
 						       self->priv->src_pixbuf,
@@ -599,12 +390,17 @@ gth_file_tool_adjust_colors_get_options (GthFileTool *base)
 	options = _gtk_builder_get_widget (self->priv->builder, "options");
 	gtk_widget_show (options);
 
+	self->priv->histogram_view = gth_histogram_view_new (self->priv->histogram);
+	gtk_widget_show (self->priv->histogram_view);
+	gtk_box_pack_start (GTK_BOX (GET_WIDGET ("histogram_hbox")), self->priv->histogram_view, TRUE, TRUE, 0);
+
 	self->priv->brightness_adj = gimp_scale_entry_new (GET_WIDGET ("brightness_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
 	self->priv->contrast_adj = gimp_scale_entry_new (GET_WIDGET ("contrast_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
 	self->priv->gamma_adj = gimp_scale_entry_new (GET_WIDGET ("gamma_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
 	self->priv->saturation_adj = gimp_scale_entry_new (GET_WIDGET ("saturation_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
-	self->priv->hue_adj = gimp_scale_entry_new (GET_WIDGET ("hue_hbox"), 0.0, -180.0, 180.0, 1.0, 10.0, 0);
-	self->priv->lightness_adj = gimp_scale_entry_new (GET_WIDGET ("lightness_hbox"), 0.0, -180.0, 180.0, 1.0, 10.0, 0);
+	self->priv->cyan_red_adj = gimp_scale_entry_new (GET_WIDGET ("cyan_red_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
+	self->priv->magenta_green_adj = gimp_scale_entry_new (GET_WIDGET ("magenta_green_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
+	self->priv->yellow_blue_adj = gimp_scale_entry_new (GET_WIDGET ("yellow_blue_hbox"), 0.0, -100.0, 100.0, 1.0, 10.0, 0);
 
 	g_signal_connect (GET_WIDGET ("ok_button"),
 			  "clicked",
@@ -614,6 +410,10 @@ gth_file_tool_adjust_colors_get_options (GthFileTool *base)
 			  "clicked",
 			  G_CALLBACK (cancel_button_clicked_cb),
 			  self);
+	g_signal_connect (GET_WIDGET ("reset_button"),
+			  "clicked",
+			  G_CALLBACK (reset_button_clicked_cb),
+			  self);
 	g_signal_connect (G_OBJECT (self->priv->brightness_adj),
 			  "value-changed",
 			  G_CALLBACK (value_changed_cb),
@@ -630,15 +430,21 @@ gth_file_tool_adjust_colors_get_options (GthFileTool *base)
 			  "value-changed",
 			  G_CALLBACK (value_changed_cb),
 			  self);
-	g_signal_connect (G_OBJECT (self->priv->hue_adj),
+	g_signal_connect (G_OBJECT (self->priv->cyan_red_adj),
 			  "value-changed",
 			  G_CALLBACK (value_changed_cb),
 			  self);
-	g_signal_connect (G_OBJECT (self->priv->lightness_adj),
+	g_signal_connect (G_OBJECT (self->priv->magenta_green_adj),
+			  "value-changed",
+			  G_CALLBACK (value_changed_cb),
+			  self);
+	g_signal_connect (G_OBJECT (self->priv->yellow_blue_adj),
 			  "value-changed",
 			  G_CALLBACK (value_changed_cb),
 			  self);
 
+	gth_histogram_calculate (self->priv->histogram, self->priv->dest_pixbuf);
+
 	return options;
 }
 
@@ -675,7 +481,9 @@ static void
 gth_file_tool_adjust_colors_instance_init (GthFileToolAdjustColors *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_FILE_TOOL_ADJUST_COLORS, GthFileToolAdjustColorsPrivate);
-	gth_file_tool_construct (GTH_FILE_TOOL (self), GTK_STOCK_EDIT, _("Adjust Colors"), _("Adjust Colors"), TRUE);
+	self->priv->histogram = gth_histogram_new ();
+
+	gth_file_tool_construct (GTH_FILE_TOOL (self), GTK_STOCK_EDIT, _("Adjust Colors"), _("Adjust Colors"), FALSE);
 	gtk_widget_set_tooltip_text (GTK_WIDGET (self), _("Change brightness, contrast, saturation and gamma level of the image"));
 }
 
@@ -693,6 +501,7 @@ gth_file_tool_adjust_colors_finalize (GObject *object)
 	_g_object_unref (self->priv->src_pixbuf);
 	_g_object_unref (self->priv->dest_pixbuf);
 	_g_object_unref (self->priv->builder);
+	_g_object_unref (self->priv->histogram);
 
 	/* Chain up */
 	G_OBJECT_CLASS (parent_class)->finalize (object);
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index 6f3f445..be3ef3d 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -54,6 +54,7 @@ PUBLIC_HEADER_FILES = 					\
 	gth-filter-file.h				\
 	gth-folder-tree.h				\
 	gth-histogram.h					\
+	gth-histogram-view.h				\
 	gth-hook.h					\
 	gth-icon-cache.h				\
 	gth-icon-view.h					\
@@ -161,6 +162,7 @@ gthumb_SOURCES = 					\
 	gth-filter-file.c				\
 	gth-folder-tree.c				\
 	gth-histogram.c					\
+	gth-histogram-view.c				\
 	gth-hook.c					\
 	gth-icon-cache.c				\
 	gth-icon-view.c					\
diff --git a/gthumb/gth-histogram-view.c b/gthumb/gth-histogram-view.c
new file mode 100644
index 0000000..6d2f2f2
--- /dev/null
+++ b/gthumb/gth-histogram-view.c
@@ -0,0 +1,501 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2001-2009 The Free Software Foundation, Inc.
+ *
+ *  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 Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <cairo/cairo.h>
+#include "glib-utils.h"
+#include "gth-histogram-view.h"
+
+
+/* Properties */
+enum {
+        PROP_0,
+        PROP_HISTOGRAM
+};
+
+
+static gpointer gth_histogram_view_parent_class = NULL;
+
+
+struct _GthHistogramViewPrivate {
+	GthHistogram      *histogram;
+	gulong             histogram_changed_event;
+	GthHistogramMode   display_mode;
+	GthHistogramScale  scale_type;
+	int                current_channel;
+	guchar             selection_start;
+	guchar             selection_end;
+};
+
+
+static void
+gth_histogram_set_property (GObject      *object,
+			    guint         property_id,
+			    const GValue *value,
+			    GParamSpec   *pspec)
+{
+	GthHistogramView *self;
+
+        self = GTH_HISTOGRAM_VIEW (object);
+
+	switch (property_id) {
+	case PROP_HISTOGRAM:
+		gth_histogram_view_set_histogram (self, g_value_get_object (value));
+		break;
+	default:
+		break;
+	}
+}
+
+
+static void
+gth_histogram_get_property (GObject    *object,
+		            guint       property_id,
+		            GValue     *value,
+		            GParamSpec *pspec)
+{
+	GthHistogramView *self;
+
+        self = GTH_HISTOGRAM_VIEW (object);
+
+	switch (property_id) {
+	case PROP_HISTOGRAM:
+		g_value_set_object (value, self->priv->histogram);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+		break;
+	}
+}
+
+
+static void
+gth_histogram_view_finalize (GObject *obj)
+{
+	GthHistogramView *self;
+
+	self = GTH_HISTOGRAM_VIEW (obj);
+
+	gth_histogram_view_set_histogram (self, NULL);
+
+	G_OBJECT_CLASS (gth_histogram_view_parent_class)->finalize (obj);
+}
+
+
+static double
+convert_to_scale (GthHistogramScale scale_type,
+		  double            value)
+{
+	switch (scale_type) {
+	case GTH_HISTOGRAM_SCALE_LINEAR:
+		return value;
+	case GTH_HISTOGRAM_SCALE_LOGARITHMIC:
+		return log (value);
+	}
+
+	return 0.0;
+}
+
+
+static void
+gth_histogram_paint_channel (GthHistogramView *self,
+			     cairo_t          *cr,
+			     int               channel,
+			     gboolean          black_mask)
+{
+	GtkWidget *widget = GTK_WIDGET (self);
+	int        w;
+	int        h;
+	double     max;
+	double     step;
+	int        i;
+
+	if (channel > 3)
+		return;
+	if ((self->priv->display_mode == GTH_HISTOGRAM_MODE_ALL_CHANNELS) && (channel == 0))
+		return;
+
+	w = widget->allocation.width;
+	h = widget->allocation.height;
+
+	switch (channel) {
+	case 0:
+	default:
+		cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
+		break;
+	case 1:
+		cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 1.0);
+		break;
+	case 2:
+		cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 1.0);
+		break;
+	case 3:
+		cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 1.0);
+		break;
+	case 4:
+		cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 1.0);
+		break;
+	}
+
+	if (black_mask) {
+		cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
+		cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+	}
+	else if (self->priv->display_mode == GTH_HISTOGRAM_MODE_ALL_CHANNELS)
+		cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+	else
+		cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+	max = gth_histogram_get_channel_max (self->priv->histogram, channel);
+	if (max > 0.0)
+		max = convert_to_scale (self->priv->scale_type, max);
+	else
+		max = 1.0;
+
+	step = w / 256.0;
+	cairo_set_line_width (cr, step);
+	for (i = 0; i < 256; i++) {
+		double value;
+		int    y;
+
+		value = gth_histogram_get_value (self->priv->histogram, channel, i);
+		y = (int) (h * convert_to_scale (self->priv->scale_type, value)) / max;
+
+		cairo_new_path (cr);
+		cairo_move_to (cr, i * step + (step / 2), h - y);
+		cairo_line_to (cr, i * step + (step / 2), h);
+		cairo_close_path (cr);
+		cairo_stroke (cr);
+
+		/*cairo_rectangle (cr, i * step, h - y, 1 + step, h);
+		cairo_fill (cr);*/
+	}
+}
+
+
+static void
+gth_histogram_paint_grid (GthHistogramView *self,
+			  cairo_t          *cr)
+{
+	GtkWidget *widget = GTK_WIDGET (self);
+	int        w;
+	int        h;
+	/*int        i;*/
+
+	w = widget->allocation.width;
+	h = widget->allocation.height;
+
+	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+	gdk_cairo_set_source_color (cr, &widget->style->dark[GTK_WIDGET_STATE (widget)]);
+
+	cairo_rectangle (cr, 0, 0, w, h);
+	cairo_stroke (cr);
+
+	/*cairo_set_line_width (cr, 1.0);
+	for (i = 1; i <= 4; i++) {
+		int x;
+
+		x = (i * 64) * ((float) w / 256);
+
+		cairo_new_path (cr);
+		cairo_move_to (cr, x, 0);
+		cairo_line_to (cr, x, h);
+		cairo_close_path (cr);
+		cairo_stroke (cr);
+	}*/
+}
+
+
+static gboolean
+gth_histogram_view_expose_event (GtkWidget      *widget,
+				 GdkEventExpose *event)
+{
+	GthHistogramView *self;
+	int               w;
+	int               h;
+	cairo_t          *cr;
+
+	self = GTH_HISTOGRAM_VIEW (widget);
+
+	w = widget->allocation.width;
+	h = widget->allocation.height;
+
+	cr = gdk_cairo_create (widget->window);
+
+	gdk_cairo_set_source_color (cr, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+	cairo_rectangle (cr, 0, 0, w, h);
+	cairo_fill (cr);
+
+	cairo_set_line_width (cr, 2.0);
+
+	if ((self->priv->histogram == NULL)
+	    || (self->priv->current_channel > gth_histogram_get_nchannels (self->priv->histogram)))
+	{
+		/* draw an x if no histogram is set */
+		cairo_new_path (cr);
+		cairo_move_to (cr, 0, 0);
+		cairo_line_to (cr, w, h);
+		cairo_close_path (cr);
+		cairo_stroke (cr);
+
+		cairo_new_path (cr);
+		cairo_move_to (cr, w, 0);
+		cairo_line_to (cr, 0, h);
+		cairo_close_path (cr);
+		cairo_stroke (cr);
+	}
+	else {
+		gth_histogram_paint_grid (self, cr);
+		if (self->priv->display_mode == GTH_HISTOGRAM_MODE_ALL_CHANNELS) {
+			int i;
+
+			for (i = 0; i <= gth_histogram_get_nchannels (self->priv->histogram); i++)
+				gth_histogram_paint_channel (self, cr, i, TRUE);
+
+			for (i = 0; i <= gth_histogram_get_nchannels (self->priv->histogram); i++)
+				if (i != self->priv->current_channel)
+					gth_histogram_paint_channel (self, cr, i, FALSE);
+			gth_histogram_paint_channel (self, cr, self->priv->current_channel, FALSE);
+		}
+		else
+			gth_histogram_paint_channel (self, cr, self->priv->current_channel, FALSE);
+	}
+
+	cairo_destroy (cr);
+
+	if (GTK_WIDGET_CLASS (gth_histogram_view_parent_class)->expose_event != NULL)
+		GTK_WIDGET_CLASS (gth_histogram_view_parent_class)->expose_event (widget, event);
+
+	return FALSE;
+}
+
+
+static void
+gth_histogram_view_map (GtkWidget *widget)
+{
+	if (GTK_WIDGET_CLASS (gth_histogram_view_parent_class)->map != NULL)
+		GTK_WIDGET_CLASS (gth_histogram_view_parent_class)->map (widget);
+
+	gdk_window_set_events (widget->window, GDK_BUTTON_PRESS_MASK | gdk_window_get_events (widget->window));
+}
+
+
+static gboolean
+gth_histogram_view_scroll_event (GtkWidget      *widget,
+				 GdkEventScroll *event)
+{
+	GthHistogramView *self;
+
+	self = GTH_HISTOGRAM_VIEW (widget);
+
+	if (self->priv->histogram == NULL)
+		return FALSE;
+
+	if (event->direction == GDK_SCROLL_UP)
+		self->priv->current_channel--;
+	else if (event->direction == GDK_SCROLL_DOWN)
+		self->priv->current_channel++;
+	self->priv->current_channel = CLAMP (self->priv->current_channel, 0, 3);
+	gtk_widget_queue_draw (widget);
+
+	return TRUE;
+}
+
+
+static void
+gth_histogram_view_class_init (GthHistogramViewClass *klass)
+{
+	GObjectClass   *object_class;
+	GtkWidgetClass *widget_class;
+
+	gth_histogram_view_parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GthHistogramViewPrivate));
+
+	object_class = (GObjectClass*) klass;
+	object_class->set_property = gth_histogram_set_property;
+	object_class->get_property = gth_histogram_get_property;
+	object_class->finalize = gth_histogram_view_finalize;
+
+	widget_class = (GtkWidgetClass*) klass;
+	widget_class->expose_event = gth_histogram_view_expose_event;
+	widget_class->map = gth_histogram_view_map;
+	widget_class->scroll_event = gth_histogram_view_scroll_event;
+
+	/* properties */
+
+	g_object_class_install_property (object_class,
+					 PROP_HISTOGRAM,
+					 g_param_spec_object ("histogram",
+							      "Histogram",
+							      "The histogram to display",
+							      GTH_TYPE_HISTOGRAM,
+							      G_PARAM_READWRITE));
+}
+
+
+static void
+gth_histogram_view_instance_init (GthHistogramView *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_HISTOGRAM_VIEW, GthHistogramViewPrivate);
+	self->priv->histogram = NULL;
+	self->priv->current_channel = 0;
+	self->priv->display_mode = GTH_HISTOGRAM_MODE_ONE_CHANNEL /*GTH_HISTOGRAM_MODE_ALL_CHANNELS*/;
+	self->priv->scale_type = GTH_HISTOGRAM_SCALE_LINEAR;
+}
+
+
+GType
+gth_histogram_view_get_type (void) {
+	static GType gth_histogram_view_type_id = 0;
+	if (gth_histogram_view_type_id == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (GthHistogramViewClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gth_histogram_view_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (GthHistogramView),
+			0,
+			(GInstanceInitFunc) gth_histogram_view_instance_init,
+			NULL
+		};
+		gth_histogram_view_type_id = g_type_register_static (GTK_TYPE_DRAWING_AREA, "GthHistogramView", &g_define_type_info, 0);
+	}
+	return gth_histogram_view_type_id;
+}
+
+
+GtkWidget *
+gth_histogram_view_new (GthHistogram *histogram)
+{
+	return (GtkWidget *) g_object_new (GTH_TYPE_HISTOGRAM_VIEW, "histogram", histogram, NULL);
+}
+
+
+static void
+histogram_changed_cb (GthHistogram *histogram,
+		      gpointer      user_data)
+{
+	GthHistogramView *self = user_data;
+
+	gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+
+void
+gth_histogram_view_set_histogram (GthHistogramView *self,
+				  GthHistogram     *histogram)
+{
+	g_return_if_fail (GTH_IS_HISTOGRAM_VIEW (self));
+
+	if (self->priv->histogram == histogram)
+		return;
+
+	if (self->priv->histogram != NULL) {
+		g_signal_handler_disconnect (self->priv->histogram, self->priv->histogram_changed_event);
+		_g_object_unref (self->priv->histogram);
+		self->priv->histogram_changed_event = 0;
+		self->priv->histogram = NULL;
+	}
+
+	if (histogram == NULL)
+		return;
+
+	self->priv->histogram = g_object_ref (histogram);
+	self->priv->histogram_changed_event = g_signal_connect (self->priv->histogram, "changed", G_CALLBACK (histogram_changed_cb), self);
+}
+
+
+GthHistogram *
+gth_histogram_view_get_histogram (GthHistogramView *self)
+{
+	g_return_val_if_fail (GTH_IS_HISTOGRAM_VIEW (self), NULL);
+	return self->priv->histogram;
+}
+
+
+void
+gth_histogram_view_set_current_channel (GthHistogramView *self,
+					int               n_channel)
+{
+	g_return_if_fail (GTH_IS_HISTOGRAM_VIEW (self));
+	self->priv->current_channel = n_channel;
+	gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+
+gint
+gth_histogram_view_get_current_channel (GthHistogramView *self)
+{
+	g_return_val_if_fail (GTH_IS_HISTOGRAM_VIEW (self), 0);
+	return self->priv->current_channel;
+}
+
+
+void
+gth_histogram_view_set_display_mode (GthHistogramView *self,
+				     GthHistogramMode  mode)
+{
+	g_return_if_fail (GTH_IS_HISTOGRAM_VIEW (self));
+	self->priv->display_mode = mode;
+	gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+
+GthHistogramMode
+gth_histogram_view_get_display_mode (GthHistogramView *self)
+{
+	g_return_val_if_fail (GTH_IS_HISTOGRAM_VIEW (self), 0);
+	return self->priv->display_mode;
+}
+
+
+void
+gth_histogram_view_set_scale_type (GthHistogramView  *self,
+				   GthHistogramScale  scale_type)
+{
+	g_return_if_fail (GTH_IS_HISTOGRAM_VIEW (self));
+	self->priv->scale_type = scale_type;
+	gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+
+GthHistogramScale
+gth_histogram_view_get_scale_type (GthHistogramView *self)
+{
+	g_return_val_if_fail (GTH_IS_HISTOGRAM_VIEW (self), 0);
+	return self->priv->scale_type;
+}
+
+
+void
+gth_histogram_view_set_selection (GthHistogramView *self,
+				  guchar            start,
+				  guchar            end)
+{
+	g_return_if_fail (GTH_IS_HISTOGRAM_VIEW (self));
+
+	self->priv->selection_start = start;
+	self->priv->selection_end = end;
+}
diff --git a/gthumb/gth-histogram-view.h b/gthumb/gth-histogram-view.h
new file mode 100644
index 0000000..3b76917
--- /dev/null
+++ b/gthumb/gth-histogram-view.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2001-2009 The Free Software Foundation, Inc.
+ *
+ *  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 Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_HISTOGRAM_VIEW_H
+#define GTH_HISTOGRAM_VIEW_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include "gth-histogram.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_HISTOGRAM_VIEW (gth_histogram_view_get_type ())
+#define GTH_HISTOGRAM_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_HISTOGRAM_VIEW, GthHistogramView))
+#define GTH_HISTOGRAM_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_HISTOGRAM_VIEW, GthHistogramViewClass))
+#define GTH_IS_HISTOGRAM_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_HISTOGRAM_VIEW))
+#define GTH_IS_HISTOGRAM_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_HISTOGRAM_VIEW))
+#define GTH_HISTOGRAM_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_HISTOGRAM_VIEW, GthHistogramViewClass))
+
+typedef struct _GthHistogramView GthHistogramView;
+typedef struct _GthHistogramViewClass GthHistogramViewClass;
+typedef struct _GthHistogramViewPrivate GthHistogramViewPrivate;
+
+typedef enum {
+	GTH_HISTOGRAM_MODE_ONE_CHANNEL,
+	GTH_HISTOGRAM_MODE_ALL_CHANNELS
+} GthHistogramMode;
+
+typedef enum {
+	GTH_HISTOGRAM_SCALE_LINEAR,
+	GTH_HISTOGRAM_SCALE_LOGARITHMIC
+} GthHistogramScale;
+
+struct _GthHistogramView {
+	GtkDrawingArea parent_instance;
+	GthHistogramViewPrivate *priv;
+};
+
+struct _GthHistogramViewClass {
+	GtkDrawingAreaClass parent_class;
+};
+
+GType              gth_histogram_view_get_type              (void);
+GtkWidget *        gth_histogram_view_new                   (GthHistogram     *histogram);
+void               gth_histogram_view_set_histogram         (GthHistogramView *self,
+							     GthHistogram     *histogram);
+GthHistogram *     gth_histogram_view_get_histogram         (GthHistogramView *self);
+void               gth_histogram_view_set_current_channel   (GthHistogramView *self,
+							     int               n_channel);
+int                gth_histogram_view_get_current_channel   (GthHistogramView *self);
+void               gth_histogram_view_set_display_mode      (GthHistogramView *self,
+							     GthHistogramMode  mode);
+GthHistogramMode   gth_histogram_view_get_display_mode      (GthHistogramView *self);
+void               gth_histogram_view_set_scale_type        (GthHistogramView *self,
+							    GthHistogramScale scale);
+GthHistogramScale  gth_histogram_view_get_scale_type        (GthHistogramView *self);
+void               gth_histogram_view_set_selection         (GthHistogramView *self,
+							     guchar            start,
+							     guchar            end);
+
+G_END_DECLS
+
+#endif /* GTH_HISTOGRAM_VIEW_H */
diff --git a/gthumb/gth-histogram.c b/gthumb/gth-histogram.c
index 7617426..c4f3c90 100644
--- a/gthumb/gth-histogram.c
+++ b/gthumb/gth-histogram.c
@@ -3,7 +3,7 @@
 /*
  *  GThumb
  *
- *  Copyright (C) 2001 The Free Software Foundation, Inc.
+ *  Copyright (C) 2001-2009 The Free Software Foundation, Inc.
  *
  *  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
@@ -28,69 +28,149 @@
 #define MAX_N_CHANNELS 4
 
 
-GthHistogram *
-gth_histogram_new (void)
+/* Signals */
+enum {
+        CHANGED,
+        LAST_SIGNAL
+};
+
+
+struct _GthHistogramPrivate {
+	int **values;
+	int  *values_max;
+	int   n_channels;
+};
+
+
+static gpointer parent_class = NULL;
+static guint gth_histogram_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gth_histogram_finalize (GObject *object)
 {
-	GthHistogram *histogram;
-	int              i;
+	GthHistogram *self;
 
-	histogram = g_new0 (GthHistogram, 1);
+	self = GTH_HISTOGRAM (object);
 
-	histogram->values = g_new0 (int *, MAX_N_CHANNELS + 1);
-	for (i = 0; i < MAX_N_CHANNELS + 1; i++)
-		histogram->values[i] = g_new0 (int, 256);
+	g_free (self->priv->values);
+	g_free (self->priv->values_max);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
 
-	histogram->values_max = g_new0 (int, MAX_N_CHANNELS + 1);
 
-	return histogram;
+static void
+gth_histogram_class_init (GthHistogramClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GthHistogramPrivate));
+
+	object_class = (GObjectClass*) klass;
+	object_class->finalize = gth_histogram_finalize;
+
+	/* signals */
+
+	gth_histogram_signals[CHANGED] =
+                g_signal_new ("changed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GthHistogramClass, changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE,
+                              0);
 }
 
 
-void
-gth_histogram_free (GthHistogram *histogram)
+static void
+gth_histogram_init (GthHistogram *self)
 {
 	int i;
 
-	if (histogram == NULL)
-		return;
-
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_HISTOGRAM, GthHistogramPrivate);
+	self->priv->values = g_new0 (int *, MAX_N_CHANNELS + 1);
 	for (i = 0; i < MAX_N_CHANNELS + 1; i++)
-		g_free (histogram->values[i]);
-	g_free (histogram->values);
-	g_free (histogram->values_max);
+		self->priv->values[i] = g_new0 (int, 256);
+	self->priv->values_max = g_new0 (int, MAX_N_CHANNELS + 1);
+}
+
 
-	g_free (histogram);
+GType
+gth_histogram_get_type (void)
+{
+        static GType type = 0;
+
+        if (! type) {
+                GTypeInfo type_info = {
+			sizeof (GthHistogramClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_histogram_class_init,
+			NULL,
+			NULL,
+			sizeof (GthHistogram),
+			0,
+			(GInstanceInitFunc) gth_histogram_init
+		};
+		type = g_type_register_static (G_TYPE_OBJECT,
+					       "GthHistogram",
+					       &type_info,
+					       0);
+	}
+
+        return type;
+}
+
+
+GthHistogram *
+gth_histogram_new (void)
+{
+	return (GthHistogram *) g_object_new (GTH_TYPE_HISTOGRAM, NULL);
 }
 
 
 static void
-histogram_reset_values (GthHistogram *histogram)
+histogram_reset_values (GthHistogram *self)
 {
 	int i;
 
 	for (i = 0; i < MAX_N_CHANNELS + 1; i++) {
-		memset (histogram->values[i], 0, sizeof (int) * 256);
-		histogram->values_max[i] = 0;
+		memset (self->priv->values[i], 0, sizeof (int) * 256);
+		self->priv->values_max[i] = 0;
 	}
 }
 
 
+static void
+gth_histogram_changed (GthHistogram *self)
+{
+	g_signal_emit (self, gth_histogram_signals[CHANGED], 0);
+}
+
+
 void
-gth_histogram_calculate (GthHistogram    *histogram,
+gth_histogram_calculate (GthHistogram    *self,
 			 const GdkPixbuf *pixbuf)
 {
-	int    **values = histogram->values;
-	int     *values_max = histogram->values_max;  
+	int    **values;
+	int     *values_max;
 	int      width, height, has_alpha, n_channels;
 	int      rowstride;
 	guchar  *line, *pixel;
 	int      i, j, max;
 
-	g_return_if_fail (histogram != NULL);
+	g_return_if_fail (GTH_IS_HISTOGRAM (self));
+
+	values = self->priv->values;
+	values_max = self->priv->values_max;
 
 	if (pixbuf == NULL) {
-		histogram->n_channels = 0;
-		histogram_reset_values (histogram);
+		self->priv->n_channels = 0;
+		histogram_reset_values (self);
+		gth_histogram_changed (self);
 		return;
 	}
 
@@ -101,8 +181,8 @@ gth_histogram_calculate (GthHistogram    *histogram,
 	width      = gdk_pixbuf_get_width (pixbuf);
 	height     = gdk_pixbuf_get_height (pixbuf);
 
-	histogram->n_channels = n_channels + 1;
-	histogram_reset_values (histogram);
+	self->priv->n_channels = n_channels + 1;
+	histogram_reset_values (self);
 
 	for (i = 0; i < height; i++) {
 		pixel = line;
@@ -132,87 +212,87 @@ gth_histogram_calculate (GthHistogram    *histogram,
 			pixel += n_channels;
 		}
 	}
+
+	gth_histogram_changed (self);
 }
 
 
 double
-gth_histogram_get_count (GthHistogram *histogram,
-			 int              start,
-			 int              end)
+gth_histogram_get_count (GthHistogram *self,
+			 int           start,
+			 int           end)
 {
 	int    i;
 	double count = 0;
 
-	g_return_val_if_fail (histogram != NULL, 0.0);
+	g_return_val_if_fail (self != NULL, 0.0);
 
 	for (i = start; i <= end; i++)
-		count += histogram->values[0][i];
+		count += self->priv->values[0][i];
 	
 	return count;
 }
 
 
 double
-gth_histogram_get_value (GthHistogram *histogram,
+gth_histogram_get_value (GthHistogram *self,
 			 int           channel,
 			 int           bin)
 {
-	g_return_val_if_fail (histogram != NULL, 0.0);
+	g_return_val_if_fail (self != NULL, 0.0);
 
-	if ((channel < histogram->n_channels) && (bin >= 0) && (bin < 256))
-		return (double) histogram->values[channel][bin];
+	if ((channel < self->priv->n_channels) && (bin >= 0) && (bin < 256))
+		return (double) self->priv->values[channel][bin];
 
 	return 0.0;
 }
 
 
 double
-gth_histogram_get_channel (GthHistogram *histogram,
+gth_histogram_get_channel (GthHistogram *self,
 			   int           channel,
 			   int           bin)
 {
-	g_return_val_if_fail (histogram != NULL, 0.0);
+	g_return_val_if_fail (self != NULL, 0.0);
 
-	if (histogram->n_channels > 3)
-		return gth_histogram_get_value (histogram, channel + 1, bin);
+	if (self->priv->n_channels > 3)
+		return gth_histogram_get_value (self, channel + 1, bin);
 	else
-		return gth_histogram_get_value (histogram, channel, bin);
+		return gth_histogram_get_value (self, channel, bin);
 }
 
 
 double
-gth_histogram_get_max (GthHistogram *histogram,
-		       int           channel)
+gth_histogram_get_channel_max (GthHistogram *self,
+			       int           channel)
 {
-	g_return_val_if_fail (histogram != NULL, 0.0);
+	g_return_val_if_fail (self != NULL, 0.0);
 
-	if (channel < histogram->n_channels)
-		return (double) histogram->values_max[channel];
+	if (channel < self->priv->n_channels)
+		return (double) self->priv->values_max[channel];
 
 	return 0.0;
 }
 
 
-int
-gth_histogram_get_nchannels (GthHistogram *histogram)
+double
+gth_histogram_get_max (GthHistogram *self)
 {
-	g_return_val_if_fail (histogram != NULL, 0.0);
-	return histogram->n_channels - 1;
-}
+	int    i;
+	double max = -1.0;
 
+	g_return_val_if_fail (self != NULL, 0.0);
 
-void
-gth_histogram_set_current_channel (GthHistogram *histogram,
-				   int           channel)
-{
-	g_return_if_fail (histogram != NULL);
-	histogram->cur_channel = channel;
+	for (i = 0; i < self->priv->n_channels; i++)
+		max = MAX (max, (double) self->priv->values_max[i]);
+
+	return max;
 }
 
 
 int
-gth_histogram_get_current_channel (GthHistogram *histogram)
+gth_histogram_get_nchannels (GthHistogram *self)
 {
-	g_return_val_if_fail (histogram != NULL, 0.0);
-	return histogram->cur_channel;
+	g_return_val_if_fail (self != NULL, 0.0);
+	return self->priv->n_channels - 1;
 }
diff --git a/gthumb/gth-histogram.h b/gthumb/gth-histogram.h
index bb76978..a9a0a8e 100644
--- a/gthumb/gth-histogram.h
+++ b/gthumb/gth-histogram.h
@@ -23,35 +23,56 @@
 #ifndef GTH_HISTOGRAM_H
 #define GTH_HISTOGRAM_H
 
+#include <glib-object.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
+G_BEGIN_DECLS
+
+#define GTH_TYPE_HISTOGRAM         (gth_histogram_get_type ())
+#define GTH_HISTOGRAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTH_TYPE_HISTOGRAM, GthHistogram))
+#define GTH_HISTOGRAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTH_TYPE_HISTOGRAM, GthHistogramClass))
+#define GTH_IS_HISTOGRAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTH_TYPE_HISTOGRAM))
+#define GTH_IS_HISTOGRAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTH_TYPE_HISTOGRAM))
+#define GTH_HISTOGRAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GTH_TYPE_HISTOGRAM, GthHistogramClass))
+
+typedef struct _GthHistogram         GthHistogram;
+typedef struct _GthHistogramPrivate  GthHistogramPrivate;
+typedef struct _GthHistogramClass    GthHistogramClass;
+
 #define MAX_N_CHANNELS 4
 
-typedef struct {
-	int   **values;
-	int    *values_max;
-	int     n_channels;
-	int     cur_channel;
-} GthHistogram;
+struct _GthHistogram
+{
+	GObject __parent;
+	GthHistogramPrivate *priv;
+};
+
+struct _GthHistogramClass {
+	GObjectClass __parent_class;
 
+	/*< signals >*/
+
+	void  (*changed)  (GthHistogram *self);
+};
+
+GType          gth_histogram_get_type            (void) G_GNUC_CONST;
 GthHistogram * gth_histogram_new                 (void);
-void           gth_histogram_free                (GthHistogram    *histogram);
-void           gth_histogram_calculate           (GthHistogram    *histogram,
+void           gth_histogram_calculate           (GthHistogram    *self,
 						  const GdkPixbuf *pixbuf);
-double         gth_histogram_get_count           (GthHistogram    *histogram,
+double         gth_histogram_get_count           (GthHistogram    *self,
 						  int              start,
 						  int              end);
-double         gth_histogram_get_value           (GthHistogram    *histogram,
+double         gth_histogram_get_value           (GthHistogram    *self,
 						  int              channel,
 						  int              bin);
-double         gth_histogram_get_channel         (GthHistogram    *histogram,
+double         gth_histogram_get_channel         (GthHistogram    *self,
 						  int              channel,
 						  int              bin);
-double         gth_histogram_get_max             (GthHistogram    *histogram,
+double         gth_histogram_get_channel_max     (GthHistogram    *self,
 						  int              channel);
-int            gth_histogram_get_nchannels       (GthHistogram    *histogram);
-void           gth_histogram_set_current_channel (GthHistogram    *histogram,
-						  int              channel);
-int            gth_histogram_get_current_channel (GthHistogram    *histogram);
+double         gth_histogram_get_max             (GthHistogram    *self);
+int            gth_histogram_get_nchannels       (GthHistogram    *self);
+
+G_END_DECLS
 
 #endif /* GTH_HISTOGRAM_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]