[gthumb/ext] [adjust colors] added histogram, color levels and reset button
- From: Paolo Bacchilega <paobac src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gthumb/ext] [adjust colors] added histogram, color levels and reset button
- Date: Sat, 19 Sep 2009 19:49:16 +0000 (UTC)
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]