[gthumb: 109/129] rotate tool: bilinear interpolation, enabled by default
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb: 109/129] rotate tool: bilinear interpolation, enabled by default
- Date: Wed, 27 Apr 2011 20:59:32 +0000 (UTC)
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]