[gthumb: 109/129] rotate tool: bilinear interpolation, enabled by default



commit 9df287a14feb3bf8e4964cae7b95710bbe31f1f3
Author: Stefano Pettini <spettini users sourceforge net>
Date:   Fri Apr 22 21:11:34 2011 +0100

    rotate tool: bilinear interpolation, enabled by default

 extensions/file_tools/data/ui/rotate-options.ui |   18 +++++-
 extensions/file_tools/gdk-pixbuf-rotate.c       |   74 +++++++++++++++++------
 extensions/file_tools/gdk-pixbuf-rotate.h       |    3 +-
 extensions/file_tools/gth-file-tool-rotate.c    |   10 +++-
 4 files changed, 83 insertions(+), 22 deletions(-)
---
diff --git a/extensions/file_tools/data/ui/rotate-options.ui b/extensions/file_tools/data/ui/rotate-options.ui
index 03123e7..5ce5e50 100644
--- a/extensions/file_tools/data/ui/rotate-options.ui
+++ b/extensions/file_tools/data/ui/rotate-options.ui
@@ -73,18 +73,32 @@
                         <property name="visible">True</property>
                         <property name="spacing">6</property>
                         <child>
+                          <object class="GtkCheckButton" id="high_quality">
+                            <property name="label" translatable="yes">_High quality</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="tooltip_text" translatable="yes">Slower but produces better results</property>
+                            <property name="use_underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
                           <object class="GtkCheckButton" id="auto_crop">
                             <property name="label" translatable="yes">A_uto-crop</property>
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="receives_default">False</property>
-                            <property name="tooltip_text" translatable="yes">Whether to crop automatically the image to avoid black areas after rotation</property>
                             <property name="use_underline">True</property>
                             <property name="active">True</property>
                             <property name="draw_indicator">True</property>
                           </object>
                           <packing>
-                            <property name="position">0</property>
+                            <property name="position">1</property>
                           </packing>
                         </child>
                       </object>
diff --git a/extensions/file_tools/gdk-pixbuf-rotate.c b/extensions/file_tools/gdk-pixbuf-rotate.c
index 9dd516c..0b72108 100644
--- a/extensions/file_tools/gdk-pixbuf-rotate.c
+++ b/extensions/file_tools/gdk-pixbuf-rotate.c
@@ -24,13 +24,33 @@
 #include "gdk-pixbuf-rotate.h"
 
 
-#define ROUND(x) (int) floor ((x) + 0.5)
+#define ROUND(x) ((int) floor ((x) + 0.5))
+
+#define INTERPOLATE(v00, v10, v01, v11, fx, fy) ((v00) + ((v10) - (v00)) * (fx) + ((v01) - (v00)) * (fy) + ((v00) - (v10) - (v01) + (v11)) * (fx) * (fy))
+
+#define GET_VALUES(r, g, b, x, y) \
+			if (x >= 0 && x < src_width && y >= 0 && y < src_height) { \
+				p_src2 = p_src + src_rowstride * y + n_channels * x; \
+				r = p_src2[RED_PIX]; \
+				g = p_src2[GREEN_PIX]; \
+				b = p_src2[BLUE_PIX]; \
+			} \
+			else { \
+				r = R0; \
+				g = G0; \
+				b = B0; \
+			}
 
 
 static GdkPixbuf*
 rotate (GdkPixbuf *src_pixbuf,
-	double     angle)
+	double     angle,
+	gint       high_quality)
 {
+	const guchar R0 = 0;
+	const guchar G0 = 0;
+	const guchar B0 = 0;
+	
 	GdkPixbuf *new_pixbuf;
 
 	double     angle_rad;
@@ -39,13 +59,19 @@ rotate (GdkPixbuf *src_pixbuf,
 	int        new_width, new_height;
 	int        src_rowstride, new_rowstride;
 	int        n_channels;
-	double     x, y;
 	int        xi, yi;
+	double     x, y;
 	double     x2, y2;
-	int        x2i, y2i;
+	int        x2min, y2min;
+	int        x2max, y2max;
+	double     fx, fy;
 	guchar    *p_src, *p_new;
 	guchar    *p_src2, *p_new2;
 	
+	guchar     r00, r01, r10, r11;
+	guchar     g00, g01, g10, g11;
+	guchar     b00, b01, b10, b11;
+
 	angle_rad = angle / 180.0 * 3.1415926535;
 	
 	cos_angle = cos (angle_rad);
@@ -75,30 +101,41 @@ rotate (GdkPixbuf *src_pixbuf,
 	
 		p_new2 = p_new;
 		
+		y = yi - (new_height - 1) / 2.0;
+		
 		for (xi = 0; xi < new_width; xi++) {
 		
 			x = xi - (new_width  - 1) / 2.0;
-			y = yi - (new_height - 1) / 2.0;
 			
 			x2 = cos_angle * x - sin_angle * y + (src_width  - 1) / 2.0;
 			y2 = sin_angle * x + cos_angle * y + (src_height - 1) / 2.0;
 			
-			// TODO: interpolate
-			x2i = ROUND (x2);
-			y2i = ROUND (y2);
+			if (high_quality) {
+			
+				// Bilinear interpolation
 			
-			if (x2i >= 0 && x2i < src_width && y2i >= 0 && y2i < src_height) {
+				x2min = (int) floor (x2);
+				y2min = (int) floor (y2);
 			
-				p_src2 = p_src + src_rowstride * y2i + n_channels * x2i;
+				x2max = (int) ceil (x2);
+				y2max = (int) ceil (y2);
 			
-				p_new2[RED_PIX]   = p_src2[RED_PIX];
-				p_new2[GREEN_PIX] = p_src2[GREEN_PIX];
-				p_new2[BLUE_PIX]  = p_src2[BLUE_PIX];
+				fx = x2 - x2min;
+				fy = y2 - y2min;
+			
+				GET_VALUES (r00, g00, b00, x2min, y2min);
+				GET_VALUES (r01, g01, b01, x2max, y2min);
+				GET_VALUES (r10, g10, b10, x2min, y2max);
+				GET_VALUES (r11, g11, b11, x2max, y2max);
+			
+				p_new2[RED_PIX]   = CLAMP (INTERPOLATE (r00, r01, r10, r11, fx, fy), 0, 255);
+				p_new2[GREEN_PIX] = CLAMP (INTERPOLATE (g00, g01, g10, g11, fx, fy), 0, 255);
+				p_new2[BLUE_PIX]  = CLAMP (INTERPOLATE (b00, b01, b10, b11, fx, fy), 0, 255);
 			}
 			else {
-				p_new2[RED_PIX]   = 0;
-				p_new2[GREEN_PIX] = 0;
-				p_new2[BLUE_PIX]  = 0;
+				// Nearest neighbor
+			
+				GET_VALUES (p_new2[RED_PIX], p_new2[GREEN_PIX], p_new2[BLUE_PIX], ROUND (x2), ROUND (y2));
 			}
 			
 			p_new2 += n_channels;
@@ -113,7 +150,8 @@ rotate (GdkPixbuf *src_pixbuf,
 
 GdkPixbuf*
 _gdk_pixbuf_rotate (GdkPixbuf *src_pixbuf,
-		    double     angle)
+		    double     angle,
+		    gint       high_quality)
 {
 	GdkPixbuf *new_pixbuf;
 	
@@ -128,7 +166,7 @@ _gdk_pixbuf_rotate (GdkPixbuf *src_pixbuf,
 		new_pixbuf = gdk_pixbuf_rotate_simple (src_pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
 	}
 	else {
-		new_pixbuf = rotate (src_pixbuf, -angle);
+		new_pixbuf = rotate (src_pixbuf, -angle, high_quality);
 	}
 
 	return new_pixbuf;
diff --git a/extensions/file_tools/gdk-pixbuf-rotate.h b/extensions/file_tools/gdk-pixbuf-rotate.h
index e0cc9c6..4198dde 100644
--- a/extensions/file_tools/gdk-pixbuf-rotate.h
+++ b/extensions/file_tools/gdk-pixbuf-rotate.h
@@ -29,7 +29,8 @@
 G_BEGIN_DECLS
 
 GdkPixbuf* _gdk_pixbuf_rotate (GdkPixbuf *src_pixbuf,
-			       double     angle);
+			       double     angle,
+			       gint       high_quality);
 
 G_END_DECLS
 
diff --git a/extensions/file_tools/gth-file-tool-rotate.c b/extensions/file_tools/gth-file-tool-rotate.c
index 1405500..31385a7 100644
--- a/extensions/file_tools/gth-file-tool-rotate.c
+++ b/extensions/file_tools/gth-file-tool-rotate.c
@@ -43,6 +43,7 @@ struct _GthFileToolRotatePrivate {
 	int               screen_width;
 	int               screen_height;
 	GtkWidget        *rotation_angle;
+	GtkWidget        *high_quality;
 	GtkWidget        *auto_crop;
 	guint             apply_event;
 };
@@ -107,6 +108,7 @@ apply_cb (gpointer user_data)
 	GtkWidget         *window;
 	GtkWidget         *viewer_page;
 	double             rotation_angle;
+	gint               high_quality;
 	gint               auto_crop;
 
 	if (self->priv->apply_event != 0) {
@@ -118,10 +120,11 @@ apply_cb (gpointer user_data)
 	viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
 
 	rotation_angle = gtk_range_get_value (GTK_RANGE (self->priv->rotation_angle));
+	high_quality = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->high_quality));
 	auto_crop = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->auto_crop));
 	
 	_g_object_unref (self->priv->dest_pixbuf);
-	self->priv->dest_pixbuf = _gdk_pixbuf_rotate (self->priv->src_pixbuf, rotation_angle);
+	self->priv->dest_pixbuf = _gdk_pixbuf_rotate (self->priv->src_pixbuf, rotation_angle, high_quality);
 
 	gth_image_viewer_page_set_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page), self->priv->dest_pixbuf, FALSE);
 	
@@ -177,6 +180,7 @@ gth_file_tool_rotate_get_options (GthFileTool *base)
 	options = _gtk_builder_get_widget (self->priv->builder, "options");
 	gtk_widget_show (options);
 	self->priv->rotation_angle = _gtk_builder_get_widget (self->priv->builder, "rotation_angle");
+	self->priv->high_quality = _gtk_builder_get_widget (self->priv->builder, "high_quality");
 	self->priv->auto_crop = _gtk_builder_get_widget (self->priv->builder, "auto_crop");
 
 	g_signal_connect (GET_WIDGET ("apply_button"),
@@ -191,6 +195,10 @@ gth_file_tool_rotate_get_options (GthFileTool *base)
 			  "value-changed",
 			  G_CALLBACK (value_changed_cb),
 			  self);
+	g_signal_connect (G_OBJECT (self->priv->high_quality),
+			  "toggled",
+			  G_CALLBACK (value_changed_cb),
+			  self);
 	g_signal_connect (G_OBJECT (self->priv->auto_crop),
 			  "toggled",
 			  G_CALLBACK (value_changed_cb),



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