[gthumb] added a sharpen tool



commit bad069e43906e244e9ca84900f47d64e06e3f1a0
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Fri Apr 8 10:32:50 2011 +0200

    added a sharpen tool
    
    [new feature]

 extensions/file_tools/Makefile.am                |    6 +-
 extensions/file_tools/data/ui/sharpen-options.ui |  199 ++++++++++++
 extensions/file_tools/gdk-pixbuf-blur.c          |  278 +++++++++++++++++
 extensions/file_tools/gdk-pixbuf-blur.h          |   40 +++
 extensions/file_tools/gth-file-tool-crop.c       |    1 +
 extensions/file_tools/gth-file-tool-sharpen.c    |  349 ++++++++++++++++++++++
 extensions/file_tools/gth-file-tool-sharpen.h    |   53 ++++
 extensions/file_tools/main.c                     |    2 +
 gthumb/pixbuf-utils.c                            |   14 +
 gthumb/pixbuf-utils.h                            |    1 +
 10 files changed, 942 insertions(+), 1 deletions(-)
---
diff --git a/extensions/file_tools/Makefile.am b/extensions/file_tools/Makefile.am
index 7785eff..c09df38 100644
--- a/extensions/file_tools/Makefile.am
+++ b/extensions/file_tools/Makefile.am
@@ -8,8 +8,10 @@ ENUM_TYPES =		\
 	enum-types.c
 
 HEADER_FILES = 				\
+	gdk-pixbuf-blur.h		\
 	gth-file-tool-adjust-colors.h	\
 	gth-file-tool-crop.h		\
+	gth-file-tool-desaturate.h	\
 	gth-file-tool-enhance.h		\
 	gth-file-tool-equalize.h	\
 	gth-file-tool-flip.h		\
@@ -21,6 +23,7 @@ HEADER_FILES = 				\
 	gth-file-tool-rotate-right.h	\
 	gth-file-tool-save.h		\
 	gth-file-tool-save-as.h		\
+	gth-file-tool-sharpen.h		\
 	gth-file-tool-undo.h		\
 	preferences.h
 
@@ -50,10 +53,10 @@ libfile_tools_la_SOURCES = 		\
 	$(HEADER_FILES)			\
 	callbacks.c			\
 	callbacks.h			\
+	gdk-pixbuf-blur.c		\
 	gth-file-tool-adjust-colors.c	\
 	gth-file-tool-crop.c		\
 	gth-file-tool-desaturate.c	\
-	gth-file-tool-desaturate.h	\
 	gth-file-tool-enhance.c		\
 	gth-file-tool-equalize.c	\
 	gth-file-tool-flip.c		\
@@ -65,6 +68,7 @@ libfile_tools_la_SOURCES = 		\
 	gth-file-tool-rotate-right.c	\
 	gth-file-tool-save.c		\
 	gth-file-tool-save-as.c		\
+	gth-file-tool-sharpen.c		\
 	gth-file-tool-undo.c		\
 	main.c
 
diff --git a/extensions/file_tools/data/ui/sharpen-options.ui b/extensions/file_tools/data/ui/sharpen-options.ui
new file mode 100644
index 0000000..3f0b67c
--- /dev/null
+++ b/extensions/file_tools/data/ui/sharpen-options.ui
@@ -0,0 +1,199 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkAlignment" id="options">
+    <property name="visible">True</property>
+    <property name="top_padding">6</property>
+    <child>
+      <object class="GtkVBox" id="vbox2">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="border_width">12</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkHBox" id="preview_hbox">
+                <property name="height_request">160</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">3</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">6</property>
+                <property name="row_spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="amount_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Amount:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="threshold_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Threshold:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="x_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="amount_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">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="threshold_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">2</property>
+                    <property name="bottom_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="radius_hbox">
+                    <property name="visible">True</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="radius_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Radius:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="x_options">GTK_FILL</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="expand">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHSeparator" id="hseparator1">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="padding">6</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHButtonBox" id="hbuttonbox1">
+            <property name="visible">True</property>
+            <property name="spacing">12</property>
+            <property name="layout_style">center</property>
+            <child>
+              <object class="GtkButton" id="ok_button">
+                <property name="label">gtk-apply</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="padding">6</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/extensions/file_tools/gdk-pixbuf-blur.c b/extensions/file_tools/gdk-pixbuf-blur.c
new file mode 100644
index 0000000..e13611e
--- /dev/null
+++ b/extensions/file_tools/gdk-pixbuf-blur.c
@@ -0,0 +1,278 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2011 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include <gthumb.h>
+#include "gdk-pixbuf-blur.h"
+
+
+
+static GdkPixbuf *
+_gdk_pixbuf_gaussian_blur (GdkPixbuf *src,
+		           int        radius)
+{
+	return NULL;
+}
+
+
+static void
+box_blur (GdkPixbuf *src,
+	  GdkPixbuf *dest,
+	  int        radius,
+	  guchar    *div_kernel_size)
+{
+	int     width, height, rowstride, n_channels;
+	guchar *p_src, *p_dest, *c1, *c2;
+	int     x, y, i, i1, i2, width_minus_1, height_minus_1, radius_plus_1;
+	int     r, g, b, a;
+	guchar *p_dest_row, *p_dest_col;
+
+	width = gdk_pixbuf_get_width (src);
+	height = gdk_pixbuf_get_height (src);
+	rowstride = gdk_pixbuf_get_rowstride (src);
+	n_channels = gdk_pixbuf_get_n_channels (src);
+	radius_plus_1 = radius + 1;
+
+	/* horizontal blur */
+
+	p_src = gdk_pixbuf_get_pixels (src);
+	p_dest = gdk_pixbuf_get_pixels (dest);
+	width_minus_1 = width - 1;
+	for (y = 0; y < height; y++) {
+
+		/* calc the initial sums of the kernel */
+
+		r = g = b = a = 0;
+
+		for (i = -radius; i <= radius; i++) {
+			c1 = p_src + (CLAMP (i, 0, width_minus_1) * n_channels);
+			r += c1[RED_PIX];
+			g += c1[GREEN_PIX];
+			b += c1[BLUE_PIX];
+			/*if (n_channels == 4)
+				a += c1[ALPHA_PIX];*/
+		}
+
+		p_dest_row = p_dest;
+		for (x = 0; x < width; x++) {
+			/* set as the mean of the kernel */
+
+			p_dest_row[RED_PIX] = div_kernel_size[r];
+			p_dest_row[GREEN_PIX] = div_kernel_size[g];
+			p_dest_row[BLUE_PIX] = div_kernel_size[b];
+			/*if (n_channels == 4)
+				p_dest_row[ALPHA_PIX] = div_kernel_size[a];*/
+			p_dest_row += n_channels;
+
+			/* the pixel to add to the kernel */
+
+			i1 = x + radius_plus_1;
+			if (i1 > width_minus_1)
+				i1 = width_minus_1;
+			c1 = p_src + (i1 * n_channels);
+
+			/* the pixel to remove from the kernel */
+
+			i2 = x - radius;
+			if (i2 < 0)
+				i2 = 0;
+			c2 = p_src + (i2 * n_channels);
+
+			/* calc the new sums of the kernel */
+
+			r += c1[RED_PIX] - c2[RED_PIX];
+			g += c1[GREEN_PIX] - c2[GREEN_PIX];
+			b += c1[BLUE_PIX] - c2[BLUE_PIX];
+			/*if (n_channels == 4)
+				a += c1[ALPHA_PIX] - c2[ALPHA_PIX];*/
+		}
+
+		p_src += rowstride;
+		p_dest += rowstride;
+	}
+
+	/* vertical blur */
+
+	p_src = gdk_pixbuf_get_pixels (dest);
+	p_dest = gdk_pixbuf_get_pixels (src);
+	height_minus_1 = height - 1;
+	for (x = 0; x < width; x++) {
+
+		/* calc the initial sums of the kernel */
+
+		r = g = b = a = 0;
+
+		for (i = -radius; i <= radius; i++) {
+			c1 = p_src + (CLAMP (i, 0, height_minus_1) * rowstride);
+			r += c1[RED_PIX];
+			g += c1[GREEN_PIX];
+			b += c1[BLUE_PIX];
+			/*if (n_channels == 4)
+				a += c1[ALPHA_PIX];*/
+		}
+
+		p_dest_col = p_dest;
+		for (y = 0; y < height; y++) {
+			/* set as the mean of the kernel */
+
+			p_dest_col[RED_PIX] = div_kernel_size[r];
+			p_dest_col[GREEN_PIX] = div_kernel_size[g];
+			p_dest_col[BLUE_PIX] = div_kernel_size[b];
+			/*if (n_channels == 4)
+				p_dest_row[ALPHA_PIX] = div_kernel_size[a];*/
+			p_dest_col += rowstride;
+
+			/* the pixel to add to the kernel */
+
+			i1 = y + radius_plus_1;
+			if (i1 > height_minus_1)
+				i1 = height_minus_1;
+			c1 = p_src + (i1 * rowstride);
+
+			/* the pixel to remove from the kernel */
+
+			i2 = y - radius;
+			if (i2 < 0)
+				i2 = 0;
+			c2 = p_src + (i2 * rowstride);
+
+			/* calc the new sums of the kernel */
+
+			r += c1[RED_PIX] - c2[RED_PIX];
+			g += c1[GREEN_PIX] - c2[GREEN_PIX];
+			b += c1[BLUE_PIX] - c2[BLUE_PIX];
+			/*if (n_channels == 4)
+				a += c1[ALPHA_PIX] - c2[ALPHA_PIX];*/
+		}
+
+		p_src += n_channels;
+		p_dest += n_channels;
+	}
+}
+
+
+static GdkPixbuf *
+_gdk_pixbuf_box_blur (GdkPixbuf *src,
+		      int        radius,
+		      int        iterations)
+{
+	GdkPixbuf *dest;
+	GdkPixbuf *tmp;
+	gint64     kernel_size;
+	guchar    *div_kernel_size;
+	int        i;
+
+	dest = gdk_pixbuf_copy (src);
+	tmp = _gdk_pixbuf_new_compatible (src);
+
+	kernel_size = 2 * radius + 1;
+	div_kernel_size = g_new (guchar, 256 * kernel_size);
+	for (i = 0; i < 256 * kernel_size; i++)
+		div_kernel_size[i] = (guchar) (i / kernel_size);
+
+	while (iterations-- > 0)
+		box_blur (dest, tmp, radius, div_kernel_size);
+
+	g_object_unref (tmp);
+
+	return dest;
+}
+
+
+GdkPixbuf *
+_gdk_pixbuf_blur (GdkPixbuf *src,
+		  int        radius)
+{
+	if (radius <= 10)
+		return _gdk_pixbuf_box_blur (src, radius, 3);
+	else
+		return _gdk_pixbuf_gaussian_blur (src, radius);
+}
+
+
+#define interpolate_value(original, reference, distance) (CLAMP (((distance) * (reference)) + ((1.0 - (distance)) * (original)), 0, 255))
+
+
+GdkPixbuf *
+_gdk_pixbuf_sharpen (GdkPixbuf *src,
+	             int        radius,
+	             double     amount,
+	             guchar     threshold)
+{
+	GdkPixbuf *dest;
+	int        width, height, rowstride, n_channels;
+	int        x, y;
+	guchar    *p_src, *p_dest;
+	guchar    *p_src_row, *p_dest_row;
+	guchar     r1, g1, b1;
+	guchar     r2, g2, b2;
+
+	dest = _gdk_pixbuf_blur (src, radius);
+	width = gdk_pixbuf_get_width (src);
+	height = gdk_pixbuf_get_height (src);
+	rowstride = gdk_pixbuf_get_rowstride (src);
+	n_channels = gdk_pixbuf_get_n_channels (src);
+
+	p_src = gdk_pixbuf_get_pixels (src);
+	p_dest = gdk_pixbuf_get_pixels (dest);
+
+	for (y = 0; y < height; y++) {
+		p_src_row = p_src;
+		p_dest_row = p_dest;
+
+		for (x = 0; x < width; x++) {
+			r1 = p_src_row[RED_PIX];
+			g1 = p_src_row[GREEN_PIX];
+			b1 = p_src_row[BLUE_PIX];
+			/* if (n_channels == 4)
+				a1 = p_src_row[ALPHA_PIX]; */
+
+			r2 = p_dest_row[RED_PIX];
+			g2 = p_dest_row[GREEN_PIX];
+			b2 = p_dest_row[BLUE_PIX];
+			/* if (n_channels == 4)
+				a2 = p_dest_row[ALPHA_PIX]; */
+
+			if (ABS (r1 - r2) >= threshold)
+				r1 = interpolate_value (r1, r2, amount);
+			if (ABS (g1 - g2) >= threshold)
+				g1 = interpolate_value (g1, g2, amount);
+			if (ABS (b1 - b2) >= threshold)
+				b1 = interpolate_value (b1, b2, amount);
+
+			p_dest_row[RED_PIX] = r1;
+			p_dest_row[GREEN_PIX] = g1;
+			p_dest_row[BLUE_PIX] = b1;
+			/* if (n_channels == 4)
+				p_dest_row[ALPHA_PIX] = p_src_row[ALPHA_PIX]; */
+
+			p_src_row += n_channels;
+			p_dest_row += n_channels;
+		}
+
+		p_src += rowstride;
+		p_dest += rowstride;
+	}
+
+	return dest;
+}
diff --git a/extensions/file_tools/gdk-pixbuf-blur.h b/extensions/file_tools/gdk-pixbuf-blur.h
new file mode 100644
index 0000000..88e8d8f
--- /dev/null
+++ b/extensions/file_tools/gdk-pixbuf-blur.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2011 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDK_PIXBUF_BLUR_H
+#define GDK_PIXBUF_BLUR_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+GdkPixbuf * _gdk_pixbuf_blur    (GdkPixbuf *src,
+			         int        radius);
+GdkPixbuf * _gdk_pixbuf_sharpen (GdkPixbuf *src,
+	         	 	 int        radius,
+	         	 	 double     amount,
+	         	 	 guchar     threshold);
+
+G_END_DECLS
+
+#endif /* GDK_PIXBUF_BLUR_H */
diff --git a/extensions/file_tools/gth-file-tool-crop.c b/extensions/file_tools/gth-file-tool-crop.c
index 5614b96..5bdbe53 100644
--- a/extensions/file_tools/gth-file-tool-crop.c
+++ b/extensions/file_tools/gth-file-tool-crop.c
@@ -529,6 +529,7 @@ gth_file_tool_crop_get_options (GthFileTool *base)
 			  self);
 
 	gth_image_viewer_set_tool (GTH_IMAGE_VIEWER (viewer), (GthImageViewerTool *) self->priv->selector);
+	gth_image_viewer_set_fit_mode (GTH_IMAGE_VIEWER (viewer), GTH_FIT_SIZE_IF_LARGER);
 	ratio_combobox_changed_cb (NULL, self);
 
 	if (! gth_image_selector_set_selection_width (self->priv->selector, self->priv->pixbuf_width * 2 / 3) || ! gth_image_selector_get_use_ratio (self->priv->selector))
diff --git a/extensions/file_tools/gth-file-tool-sharpen.c b/extensions/file_tools/gth-file-tool-sharpen.c
new file mode 100644
index 0000000..bebf0a1
--- /dev/null
+++ b/extensions/file_tools/gth-file-tool-sharpen.c
@@ -0,0 +1,349 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2011 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <gthumb.h>
+#include <extensions/image_viewer/gth-image-viewer-page.h>
+#include "gth-file-tool-sharpen.h"
+#include "gdk-pixbuf-blur.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+#define APPLY_DELAY 150
+#define DEFAULT_RADIUS 2.0
+#define DEFAULT_AMOUNT 50.0
+#define DEFAULT_THRESHOLD 1.0
+
+
+static gpointer parent_class = NULL;
+
+
+struct _GthFileToolSharpenPrivate {
+	GdkPixbuf     *src_pixbuf;
+	GdkPixbuf     *dest_pixbuf;
+	GtkBuilder    *builder;
+	GtkAdjustment *radius_adj;
+	GtkAdjustment *amount_adj;
+	GtkAdjustment *threshold_adj;
+	GthTask       *pixbuf_task;
+	guint          apply_event;
+};
+
+
+static void
+gth_file_tool_sharpen_update_sensitivity (GthFileTool *base)
+{
+	GtkWidget *window;
+	GtkWidget *viewer_page;
+
+	window = gth_file_tool_get_window (base);
+	viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
+	if (! GTH_IS_IMAGE_VIEWER_PAGE (viewer_page))
+		gtk_widget_set_sensitive (GTK_WIDGET (base), FALSE);
+	else
+		gtk_widget_set_sensitive (GTK_WIDGET (base), TRUE);
+}
+
+
+static void
+task_completed_cb (GthTask  *task,
+		   GError   *error,
+		   gpointer  user_data)
+{
+	GthFileTool *base = user_data;
+
+	if (error == NULL) {
+		GthPixbufTask *pixbuf_task;
+		GtkWidget     *viewer_page;
+
+		pixbuf_task = GTH_PIXBUF_TASK (task);
+		viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (gth_file_tool_get_window (base)));
+		gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page), pixbuf_task->dest, TRUE);
+	}
+}
+
+
+typedef struct {
+	int    radius;
+	double amount;
+	int    threshold;
+} SharpenData;
+
+
+static void
+sharpen_step (GthPixbufTask *pixbuf_task)
+{
+	SharpenData *sharpen_data = pixbuf_task->data;
+
+	pixbuf_task->dest = _gdk_pixbuf_sharpen (pixbuf_task->src,
+						 sharpen_data->radius,
+						 sharpen_data->amount,
+						 sharpen_data->threshold);
+}
+
+
+static void
+ok_button_clicked_cb (GtkButton          *button,
+		      GthFileToolSharpen *self)
+{
+	SharpenData *sharpen_data;
+	GthTask     *task;
+
+	sharpen_data = g_new (SharpenData, 1);
+	sharpen_data->radius = gtk_adjustment_get_value (self->priv->radius_adj);
+	sharpen_data->amount = - gtk_adjustment_get_value (self->priv->amount_adj) / 100.0;
+	sharpen_data->threshold = gtk_adjustment_get_value (self->priv->threshold_adj);
+	task = gth_pixbuf_task_new (_("Sharpening image"),
+				    TRUE,
+				    NULL,
+				    sharpen_step,
+				    NULL,
+				    sharpen_data,
+				    g_free);
+	gth_pixbuf_task_set_source (GTH_PIXBUF_TASK (task), self->priv->src_pixbuf);
+	g_signal_connect (task, "completed", G_CALLBACK (task_completed_cb), self);
+	gth_browser_exec_task (GTH_BROWSER (gth_file_tool_get_window (GTH_FILE_TOOL (self))), task, FALSE);
+
+	g_object_unref (task);
+
+	gth_file_tool_hide_options (GTH_FILE_TOOL (self));
+}
+
+
+static void
+cancel_button_clicked_cb (GtkButton          *button,
+			  GthFileToolSharpen *self)
+{
+	GtkWidget *window;
+	GtkWidget *viewer_page;
+
+	if (self->priv->apply_event != 0) {
+		g_source_remove (self->priv->apply_event);
+		self->priv->apply_event = 0;
+	}
+
+	window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
+	viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
+	gth_image_viewer_page_reset (GTH_IMAGE_VIEWER_PAGE (viewer_page));
+
+	gth_file_tool_hide_options (GTH_FILE_TOOL (self));
+}
+
+
+static void
+reset_button_clicked_cb (GtkButton          *button,
+			 GthFileToolSharpen *self)
+{
+	gtk_adjustment_set_value (self->priv->radius_adj, DEFAULT_RADIUS);
+	gtk_adjustment_set_value (self->priv->amount_adj, DEFAULT_AMOUNT);
+	gtk_adjustment_set_value (self->priv->threshold_adj, DEFAULT_THRESHOLD);
+}
+
+
+static void
+value_changed_cb (GtkAdjustment      *adj,
+		  GthFileToolSharpen *self)
+{
+	if (self->priv->apply_event != 0) {
+		g_source_remove (self->priv->apply_event);
+		self->priv->apply_event = 0;
+	}
+	/* FIXME
+	self->priv->apply_event = g_timeout_add (APPLY_DELAY, apply_cb, self);
+	*/
+}
+
+
+static GtkWidget *
+gth_file_tool_sharpen_get_options (GthFileTool *base)
+{
+	GthFileToolSharpen *self;
+	GtkWidget          *window;
+	GtkWidget          *viewer_page;
+	GtkWidget          *viewer;
+	GtkWidget          *options;
+
+	self = (GthFileToolSharpen *) base;
+
+	window = gth_file_tool_get_window (base);
+	viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
+	if (! GTH_IS_IMAGE_VIEWER_PAGE (viewer_page))
+		return NULL;
+
+	_g_object_unref (self->priv->src_pixbuf);
+	_g_object_unref (self->priv->dest_pixbuf);
+
+	viewer = gth_image_viewer_page_get_image_viewer (GTH_IMAGE_VIEWER_PAGE (viewer_page));
+	self->priv->src_pixbuf = gth_image_viewer_get_current_pixbuf (GTH_IMAGE_VIEWER (viewer));
+	if (self->priv->src_pixbuf == NULL)
+		return NULL;
+
+	self->priv->src_pixbuf = g_object_ref (self->priv->src_pixbuf);
+	self->priv->dest_pixbuf = NULL;
+
+	self->priv->builder = _gtk_builder_new_from_file ("sharpen-options.ui", "file_tools");
+	options = _gtk_builder_get_widget (self->priv->builder, "options");
+	gtk_widget_show (options);
+
+	/* FIXME: add a preview here
+	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->radius_adj = gimp_scale_entry_new (GET_WIDGET ("radius_hbox"),
+						       GTK_LABEL (GET_WIDGET ("radius_label")),
+						       DEFAULT_RADIUS, 0.0, 10.0, 0.1, 1.0, 1);
+	self->priv->amount_adj = gimp_scale_entry_new (GET_WIDGET ("amount_hbox"),
+						       GTK_LABEL (GET_WIDGET ("amount_label")),
+						       DEFAULT_AMOUNT, 0.0, 200.0, 1.0, 10.0, 0);
+	self->priv->threshold_adj = gimp_scale_entry_new (GET_WIDGET ("threshold_hbox"),
+							  GTK_LABEL (GET_WIDGET ("threshold_label")),
+							  DEFAULT_THRESHOLD, 1.0, 255.0, 1.0, 10.0, 0);
+
+	g_signal_connect (GET_WIDGET ("ok_button"),
+			  "clicked",
+			  G_CALLBACK (ok_button_clicked_cb),
+			  self);
+	g_signal_connect (GET_WIDGET ("cancel_button"),
+			  "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->radius_adj),
+			  "value-changed",
+			  G_CALLBACK (value_changed_cb),
+			  self);
+	g_signal_connect (G_OBJECT (self->priv->amount_adj),
+			  "value-changed",
+			  G_CALLBACK (value_changed_cb),
+			  self);
+	g_signal_connect (G_OBJECT (self->priv->threshold_adj),
+			  "value-changed",
+			  G_CALLBACK (value_changed_cb),
+			  self);
+
+	return options;
+}
+
+
+static void
+gth_file_tool_sharpen_destroy_options (GthFileTool *base)
+{
+	GthFileToolSharpen *self;
+
+	self = (GthFileToolSharpen *) base;
+
+	if (self->priv->apply_event != 0) {
+		g_source_remove (self->priv->apply_event);
+		self->priv->apply_event = 0;
+	}
+
+	_g_object_unref (self->priv->src_pixbuf);
+	_g_object_unref (self->priv->dest_pixbuf);
+	_g_object_unref (self->priv->builder);
+	self->priv->src_pixbuf = NULL;
+	self->priv->dest_pixbuf = NULL;
+	self->priv->builder = NULL;
+}
+
+
+static void
+gth_file_tool_sharpen_activate (GthFileTool *base)
+{
+	gth_file_tool_show_options (base);
+}
+
+
+static void
+gth_file_tool_sharpen_instance_init (GthFileToolSharpen *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_FILE_TOOL_SHARPEN, GthFileToolSharpenPrivate);
+	self->priv->src_pixbuf = NULL;
+	self->priv->dest_pixbuf = NULL;
+	self->priv->builder = NULL;
+
+	gth_file_tool_construct (GTH_FILE_TOOL (self), "tool-sharpen", _("Sharpen..."), _("Sharpen"), FALSE);
+}
+
+
+static void
+gth_file_tool_sharpen_finalize (GObject *object)
+{
+	GthFileToolSharpen *self;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GTH_IS_FILE_TOOL_SHARPEN (object));
+
+	self = (GthFileToolSharpen *) object;
+
+	_g_object_unref (self->priv->src_pixbuf);
+	_g_object_unref (self->priv->dest_pixbuf);
+	_g_object_unref (self->priv->builder);
+
+	/* Chain up */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gth_file_tool_sharpen_class_init (GthFileToolSharpenClass *klass)
+{
+	GObjectClass     *gobject_class;
+	GthFileToolClass *file_tool_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GthFileToolSharpenPrivate));
+
+	gobject_class = (GObjectClass*) klass;
+	gobject_class->finalize = gth_file_tool_sharpen_finalize;
+
+	file_tool_class = (GthFileToolClass *) klass;
+	file_tool_class->update_sensitivity = gth_file_tool_sharpen_update_sensitivity;
+	file_tool_class->activate = gth_file_tool_sharpen_activate;
+	file_tool_class->get_options = gth_file_tool_sharpen_get_options;
+	file_tool_class->destroy_options = gth_file_tool_sharpen_destroy_options;
+}
+
+
+GType
+gth_file_tool_sharpen_get_type (void) {
+	static GType type_id = 0;
+	if (type_id == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (GthFileToolSharpenClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gth_file_tool_sharpen_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (GthFileToolSharpen),
+			0,
+			(GInstanceInitFunc) gth_file_tool_sharpen_instance_init,
+			NULL
+		};
+		type_id = g_type_register_static (GTH_TYPE_FILE_TOOL, "GthFileToolSharpen", &g_define_type_info, 0);
+	}
+	return type_id;
+}
diff --git a/extensions/file_tools/gth-file-tool-sharpen.h b/extensions/file_tools/gth-file-tool-sharpen.h
new file mode 100644
index 0000000..c7a149e
--- /dev/null
+++ b/extensions/file_tools/gth-file-tool-sharpen.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2011 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_FILE_TOOL_SHARPEN_H
+#define GTH_FILE_TOOL_SHARPEN_H
+
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_FILE_TOOL_SHARPEN (gth_file_tool_sharpen_get_type ())
+#define GTH_FILE_TOOL_SHARPEN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_FILE_TOOL_SHARPEN, GthFileToolSharpen))
+#define GTH_FILE_TOOL_SHARPEN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_FILE_TOOL_SHARPEN, GthFileToolSharpenClass))
+#define GTH_IS_FILE_TOOL_SHARPEN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_FILE_TOOL_SHARPEN))
+#define GTH_IS_FILE_TOOL_SHARPEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_FILE_TOOL_SHARPEN))
+#define GTH_FILE_TOOL_SHARPEN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_FILE_TOOL_SHARPEN, GthFileToolSharpenClass))
+
+typedef struct _GthFileToolSharpen GthFileToolSharpen;
+typedef struct _GthFileToolSharpenClass GthFileToolSharpenClass;
+typedef struct _GthFileToolSharpenPrivate GthFileToolSharpenPrivate;
+
+struct _GthFileToolSharpen {
+	GthFileTool parent_instance;
+	GthFileToolSharpenPrivate *priv;
+};
+
+struct _GthFileToolSharpenClass {
+	GthFileToolClass parent_class;
+};
+
+GType  gth_file_tool_sharpen_get_type  (void);
+
+G_END_DECLS
+
+#endif /* GTH_FILE_TOOL_SHARPEN_H */
diff --git a/extensions/file_tools/main.c b/extensions/file_tools/main.c
index 3ba9c03..6931e66 100644
--- a/extensions/file_tools/main.c
+++ b/extensions/file_tools/main.c
@@ -38,6 +38,7 @@
 #include "gth-file-tool-rotate-right.h"
 #include "gth-file-tool-save.h"
 #include "gth-file-tool-save-as.h"
+#include "gth-file-tool-sharpen.h"
 #include "gth-file-tool-undo.h"
 
 
@@ -51,6 +52,7 @@ gthumb_extension_activate (void)
 
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_ENHANCE);
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_ADJUST_COLORS);
+	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_SHARPEN);
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_EQUALIZE);
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_DESATURATE);
 	gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_NEGATIVE);
diff --git a/gthumb/pixbuf-utils.c b/gthumb/pixbuf-utils.c
index cfcbe28..e53b3b2 100644
--- a/gthumb/pixbuf-utils.c
+++ b/gthumb/pixbuf-utils.c
@@ -44,6 +44,20 @@ _gdk_pixbuf_new_void (int width,
 }
 
 
+GdkPixbuf *
+_gdk_pixbuf_new_compatible (GdkPixbuf *src)
+{
+	if (src == NULL)
+		return NULL;
+
+	return gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
+			       gdk_pixbuf_get_has_alpha (src),
+			       gdk_pixbuf_get_bits_per_sample (src),
+			       gdk_pixbuf_get_width (src),
+			       gdk_pixbuf_get_height (src));
+}
+
+
 /* Taken from http://www.gtkforums.com/about5204.html
  * Author: tadeboro */
 GdkPixbuf *
diff --git a/gthumb/pixbuf-utils.h b/gthumb/pixbuf-utils.h
index 5f3b5b2..116fd35 100644
--- a/gthumb/pixbuf-utils.h
+++ b/gthumb/pixbuf-utils.h
@@ -32,6 +32,7 @@ G_BEGIN_DECLS
 
 GdkPixbuf * _gdk_pixbuf_new_void               (int              width,
 					        int              height);
+GdkPixbuf *  _gdk_pixbuf_new_compatible        (GdkPixbuf       *src);
 GdkPixbuf * _gdk_pixbuf_new_from_cairo_surface (cairo_t         *cr);
 GdkPixbuf * _gdk_pixbuf_scale_simple_safe      (const GdkPixbuf *src,
 					        int              dest_width,



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