[gthumb: 1/129] added a tool to rotate images of any degree
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb: 1/129] added a tool to rotate images of any degree
- Date: Wed, 27 Apr 2011 20:50:27 +0000 (UTC)
commit bfad6e812457a67c62ef17206aa83750e1df3486
Author: Paolo Bacchilega <paobac src gnome org>
Date: Wed Apr 13 11:14:23 2011 +0200
added a tool to rotate images of any degree
First batch of changes to add a tool to rotate images
of any degree.
[new feature, bug #627563]
data/icons/hicolor/16x16/actions/Makefile.am | 1 +
data/icons/hicolor/16x16/actions/tool-rotate.png | Bin 0 -> 703 bytes
data/icons/hicolor/22x22/actions/Makefile.am | 1 +
data/icons/hicolor/22x22/actions/tool-rotate.png | Bin 0 -> 863 bytes
extensions/file_tools/Makefile.am | 6 +
extensions/file_tools/data/Makefile.am | 5 +-
.../data/gthumb_rotate_options.schemas.in | 17 +
extensions/file_tools/data/ui/Makefile.am | 4 +-
extensions/file_tools/data/ui/rotate-options.ui | 479 +++++++++++
extensions/file_tools/gth-file-tool-mirror.c | 2 +-
extensions/file_tools/gth-file-tool-resize.c | 2 +-
extensions/file_tools/gth-file-tool-rotate-right.c | 2 +-
extensions/file_tools/gth-file-tool-rotate.c | 524 ++++++++++++
extensions/file_tools/gth-file-tool-rotate.h | 53 ++
extensions/file_tools/gth-image-rotator.c | 555 ++++++++++++
extensions/file_tools/gth-image-rotator.h | 74 ++
extensions/file_tools/gth-transform-resize.c | 890 ++++++++++++++++++++
extensions/file_tools/gth-transform-resize.h | 60 ++
extensions/file_tools/main.c | 7 +-
extensions/file_tools/preferences.h | 2 +
gthumb/cairo-utils.c | 118 +++-
gthumb/cairo-utils.h | 4 +
gthumb/gtk-utils.c | 3 +-
23 files changed, 2779 insertions(+), 30 deletions(-)
---
diff --git a/data/icons/hicolor/16x16/actions/Makefile.am b/data/icons/hicolor/16x16/actions/Makefile.am
index 8ee5cd0..f7cb81a 100644
--- a/data/icons/hicolor/16x16/actions/Makefile.am
+++ b/data/icons/hicolor/16x16/actions/Makefile.am
@@ -26,6 +26,7 @@ icons_DATA = \
tool-mirror.png \
tool-red-eye.png \
tool-resize.png \
+ tool-rotate.png \
tool-rotate-270.png \
tool-rotate-90.png \
tool-sharpen.png
diff --git a/data/icons/hicolor/16x16/actions/tool-rotate.png b/data/icons/hicolor/16x16/actions/tool-rotate.png
new file mode 100644
index 0000000..60ab412
Binary files /dev/null and b/data/icons/hicolor/16x16/actions/tool-rotate.png differ
diff --git a/data/icons/hicolor/22x22/actions/Makefile.am b/data/icons/hicolor/22x22/actions/Makefile.am
index ee56140..253bb44 100644
--- a/data/icons/hicolor/22x22/actions/Makefile.am
+++ b/data/icons/hicolor/22x22/actions/Makefile.am
@@ -19,6 +19,7 @@ icons_DATA = \
tool-enhance.png \
tool-red-eye.png \
tool-resize.png \
+ tool-rotate.png \
tool-sharpen.png \
zoom-fit-width.png
diff --git a/data/icons/hicolor/22x22/actions/tool-rotate.png b/data/icons/hicolor/22x22/actions/tool-rotate.png
new file mode 100644
index 0000000..38dae1f
Binary files /dev/null and b/data/icons/hicolor/22x22/actions/tool-rotate.png differ
diff --git a/extensions/file_tools/Makefile.am b/extensions/file_tools/Makefile.am
index c09df38..6625eb8 100644
--- a/extensions/file_tools/Makefile.am
+++ b/extensions/file_tools/Makefile.am
@@ -19,12 +19,15 @@ HEADER_FILES = \
gth-file-tool-negative.h \
gth-file-tool-redo.h \
gth-file-tool-resize.h \
+ gth-file-tool-rotate.h \
gth-file-tool-rotate-left.h \
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 \
+ gth-image-rotator.h \
+ gth-transform-resize.h \
preferences.h
enum-types.h: $(HEADER_FILES) $(GLIB_MKENUMS)
@@ -64,12 +67,15 @@ libfile_tools_la_SOURCES = \
gth-file-tool-negative.c \
gth-file-tool-redo.c \
gth-file-tool-resize.c \
+ gth-file-tool-rotate.c \
gth-file-tool-rotate-left.c \
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 \
+ gth-image-rotator.c \
+ gth-transform-resize.c \
main.c
libfile_tools_la_CFLAGS = $(GTHUMB_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb
diff --git a/extensions/file_tools/data/Makefile.am b/extensions/file_tools/data/Makefile.am
index f4f2f1e..8b2a4ff 100644
--- a/extensions/file_tools/data/Makefile.am
+++ b/extensions/file_tools/data/Makefile.am
@@ -1,7 +1,10 @@
SUBDIRS = ui
schemadir = @GCONF_SCHEMA_FILE_DIR@
-schema_in_files = gthumb_crop_options.schemas.in gthumb_resize_options.schemas.in
+schema_in_files = \
+ gthumb_crop_options.schemas.in \
+ gthumb_resize_options.schemas.in \
+ gthumb_rotate_options.schemas.in
schema_DATA = $(schema_in_files:.schemas.in=.schemas)
@INTLTOOL_SCHEMAS_RULE@
diff --git a/extensions/file_tools/data/gthumb_rotate_options.schemas.in b/extensions/file_tools/data/gthumb_rotate_options.schemas.in
new file mode 100644
index 0000000..ed9c164
--- /dev/null
+++ b/extensions/file_tools/data/gthumb_rotate_options.schemas.in
@@ -0,0 +1,17 @@
+<gconfschemafile>
+ <schemalist>
+
+ <schema>
+ <key>/schemas/apps/gthumb/ext/resize/unit</key>
+ <applyto>/apps/gthumb/ext/resize/unit</applyto>
+ <owner>gthumb</owner>
+ <type>string</type>
+ <default>percentage</default>
+ <locale name="C">
+ <short></short>
+ <long></long>
+ </locale>
+ </schema>
+
+ </schemalist>
+</gconfschemafile>
diff --git a/extensions/file_tools/data/ui/Makefile.am b/extensions/file_tools/data/ui/Makefile.am
index 12e0d50..cc7927b 100644
--- a/extensions/file_tools/data/ui/Makefile.am
+++ b/extensions/file_tools/data/ui/Makefile.am
@@ -2,7 +2,9 @@ uidir = $(pkgdatadir)/ui
ui_DATA = \
adjust-colors-options.ui \
crop-options.ui \
- resize-options.ui
+ resize-options.ui \
+ rotate-options.ui \
+ sharpen-options.ui
EXTRA_DIST = $(ui_DATA)
-include $(top_srcdir)/git.mk
diff --git a/extensions/file_tools/data/ui/rotate-options.ui b/extensions/file_tools/data/ui/rotate-options.ui
new file mode 100644
index 0000000..152df48
--- /dev/null
+++ b/extensions/file_tools/data/ui/rotate-options.ui
@@ -0,0 +1,479 @@
+<?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="spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="angle_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label">45°</property>
+ <attributes>
+ <attribute name="size" value="24000"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="angle_box">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="step_checkbutton">
+ <property name="label" translatable="yes">5 degrees</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="small_angle_box">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Angle</property>
+ <property name="use_markup">True</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame4">
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkSpinButton" id="center_x_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">6</property>
+ <property name="adjustment">center_x_adjustment</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="unit_combobox">
+ <property name="visible">True</property>
+ <property name="model">unit_liststore</property>
+ <property name="active">0</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkSpinButton" id="center_y_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">6</property>
+ <property name="adjustment">center_y_adjustment</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Center</property>
+ <property name="use_markup">True</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkComboBox" id="size_combobox">
+ <property name="visible">True</property>
+ <property name="model">size_liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Size</property>
+ <property name="use_markup">True</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame3">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Background:</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="GtkHBox" id="grid_box">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="grid_checkbutton">
+ <property name="label" translatable="yes">_Grid:</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <child>
+ <object class="GtkColorButton" id="background_colorbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="color">#000000000000</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </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>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Options</property>
+ <property name="use_markup">True</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</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-ok</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>
+ <object class="GtkListStore" id="unit_liststore">
+ <columns>
+ <!-- column-name type -->
+ <column type="gint"/>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">0</col>
+ <col id="1" translatable="yes">pixels</col>
+ </row>
+ <row>
+ <col id="0">1</col>
+ <col id="1" translatable="yes">%</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkAdjustment" id="center_x_adjustment">
+ <property name="lower">1</property>
+ <property name="upper">999999</property>
+ <property name="step_increment">1</property>
+ </object>
+ <object class="GtkAdjustment" id="center_y_adjustment">
+ <property name="lower">1</property>
+ <property name="upper">999999</property>
+ <property name="step_increment">1</property>
+ </object>
+ <object class="GtkListStore" id="grid_liststore">
+ <columns>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">None</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Number of lines</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Space between lines</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="size_liststore">
+ <columns>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">Original size</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Bounding box</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Crop corners</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Crop corners keeping ratio</col>
+ </row>
+ </data>
+ </object>
+</interface>
diff --git a/extensions/file_tools/gth-file-tool-mirror.c b/extensions/file_tools/gth-file-tool-mirror.c
index 22f478d..22ad1a1 100644
--- a/extensions/file_tools/gth-file-tool-mirror.c
+++ b/extensions/file_tools/gth-file-tool-mirror.c
@@ -70,7 +70,7 @@ gth_file_tool_mirror_update_sensitivity (GthFileTool *base)
static void
gth_file_tool_mirror_instance_init (GthFileToolMirror *self)
{
- gth_file_tool_construct (GTH_FILE_TOOL (self), "tool-mirror", _("Mirror"), NULL, TRUE);
+ gth_file_tool_construct (GTH_FILE_TOOL (self), "tool-mirror", _("Mirror"), NULL, FALSE);
/*gtk_widget_set_tooltip_text (GTK_WIDGET (self), _("Automatic white balance correction"));*/
}
diff --git a/extensions/file_tools/gth-file-tool-resize.c b/extensions/file_tools/gth-file-tool-resize.c
index 56a2dbb..63f1ca2 100644
--- a/extensions/file_tools/gth-file-tool-resize.c
+++ b/extensions/file_tools/gth-file-tool-resize.c
@@ -546,7 +546,7 @@ static void
gth_file_tool_resize_instance_init (GthFileToolResize *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_FILE_TOOL_RESIZE, GthFileToolResizePrivate);
- gth_file_tool_construct (GTH_FILE_TOOL (self), "tool-resize", _("Resize..."), _("Resize"), TRUE);
+ gth_file_tool_construct (GTH_FILE_TOOL (self), "tool-resize", _("Resize..."), _("Resize"), FALSE);
}
diff --git a/extensions/file_tools/gth-file-tool-rotate-right.c b/extensions/file_tools/gth-file-tool-rotate-right.c
index c81b292..cdf1b4a 100644
--- a/extensions/file_tools/gth-file-tool-rotate-right.c
+++ b/extensions/file_tools/gth-file-tool-rotate-right.c
@@ -70,7 +70,7 @@ gth_file_tool_rotate_right_update_sensitivity (GthFileTool *base)
static void
gth_file_tool_rotate_right_instance_init (GthFileToolRotateRight *self)
{
- gth_file_tool_construct (GTH_FILE_TOOL (self), "tool-rotate-90", _("Rotate Right"), NULL, FALSE);
+ gth_file_tool_construct (GTH_FILE_TOOL (self), "tool-rotate-90", _("Rotate Right"), NULL, TRUE);
/*gtk_widget_set_tooltip_text (GTK_WIDGET (self), _("Automatic white balance correction"));*/
}
diff --git a/extensions/file_tools/gth-file-tool-rotate.c b/extensions/file_tools/gth-file-tool-rotate.c
new file mode 100644
index 0000000..caa57ed
--- /dev/null
+++ b/extensions/file_tools/gth-file-tool-rotate.c
@@ -0,0 +1,524 @@
+/* -*- 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 <math.h>
+#include <gthumb.h>
+#include <extensions/image_viewer/gth-image-viewer-page.h>
+#include "gth-file-tool-rotate.h"
+#include "gth-image-rotator.h"
+#include "preferences.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+#define HIGH_QUALITY_INTERPOLATION GDK_INTERP_HYPER
+#define APPLY_DELAY 50
+#define DEFAULT_GRID 15
+
+
+static gpointer parent_class = NULL;
+
+
+struct _GthFileToolRotatePrivate {
+ GtkBuilder *builder;
+ GtkAdjustment *angle_adj;
+ GtkAdjustment *small_angle_adj;
+ GthImageRotator *rotator;
+ int pixbuf_width;
+ int pixbuf_height;
+ GdkPixbuf *tmp_pixbuf;
+ int new_width;
+ int new_height;
+ GthUnit unit;
+ guint apply_event;
+ double step;
+ gboolean use_grid;
+ GtkAdjustment *grid_adj;
+};
+
+
+static void
+gth_file_tool_rotate_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
+cancel_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GthFileToolRotate *self = user_data;
+
+ if (self->priv->apply_event != 0) {
+ g_source_remove (self->priv->apply_event);
+ self->priv->apply_event = 0;
+ }
+
+ gth_file_tool_hide_options (GTH_FILE_TOOL (self));
+}
+
+
+static void
+ok_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GthFileToolRotate *self = user_data;
+ GdkPixbuf *new_pixbuf;
+
+ new_pixbuf = gth_image_rotator_get_result (self->priv->rotator);
+ if (new_pixbuf != NULL) {
+ GtkWidget *window;
+ GtkWidget *viewer_page;
+
+ 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), new_pixbuf, TRUE);
+ gth_file_tool_hide_options (GTH_FILE_TOOL (self));
+
+ g_object_unref (new_pixbuf);
+ }
+}
+
+
+static void
+center_position_changed_cb (GtkSpinButton *spinbutton,
+ gpointer user_data)
+{
+ /*GthFileToolRotate *self = user_data;
+
+ FIXME */
+}
+
+
+static void
+unit_combobox_changed_cb (GtkComboBox *combobox,
+ gpointer user_data)
+{
+ /* FIXME GthFileToolRotate *self = user_data;
+
+ g_signal_handlers_block_by_data (GET_WIDGET ("resize_width_spinbutton"), self);
+ g_signal_handlers_block_by_data (GET_WIDGET ("resize_height_spinbutton"), self);
+
+ self->priv->unit = gtk_combo_box_get_active (combobox);
+ if (self->priv->unit == GTH_UNIT_PERCENTAGE) {
+ double p;
+
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (GET_WIDGET ("resize_width_spinbutton")), 2);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (GET_WIDGET ("resize_height_spinbutton")), 2);
+
+ p = ((double) self->priv->new_width) / self->priv->pixbuf_width * 100.0;
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("resize_width_spinbutton")), p);
+ p = ((double) self->priv->new_height) / self->priv->pixbuf_height * 100.0;
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("resize_height_spinbutton")), p);
+ }
+ else if (self->priv->unit == GTH_UNIT_PIXELS) {
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (GET_WIDGET ("resize_width_spinbutton")), 0);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (GET_WIDGET ("resize_height_spinbutton")), 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("resize_width_spinbutton")), self->priv->new_width);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("resize_height_spinbutton")), self->priv->new_height);
+ }
+
+ g_signal_handlers_unblock_by_data (GET_WIDGET ("resize_width_spinbutton"), self);
+ g_signal_handlers_unblock_by_data (GET_WIDGET ("resize_height_spinbutton"), self);
+
+ selection_width_value_changed_cb (GTK_SPIN_BUTTON (GET_WIDGET ("resize_width_spinbutton")), self);
+ */
+}
+
+
+static void
+_gth_file_tool_rotate (GthFileToolRotate *self,
+ double angle)
+{
+ char *s;
+
+ gth_image_rotator_set_angle (self->priv->rotator, angle);
+ s = g_strdup_printf ("%2.2f°", angle);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("angle_label")), s);
+
+ g_free (s);
+}
+
+
+static gboolean
+apply_small_angle_cb (gpointer user_data)
+{
+ GthFileToolRotate *self = user_data;
+ double angle;
+
+ self->priv->apply_event = 0;
+
+ angle = gtk_adjustment_get_value (self->priv->angle_adj) + gtk_adjustment_get_value (self->priv->small_angle_adj);
+ _gth_file_tool_rotate (self, angle);
+
+ return FALSE;
+}
+
+
+static void
+small_angle_value_changed_cb (GtkAdjustment *adj,
+ gpointer user_data)
+{
+ GthFileToolRotate *self = user_data;
+
+ if (self->priv->apply_event == 0)
+ self->priv->apply_event = g_timeout_add (APPLY_DELAY, apply_small_angle_cb, self);
+}
+
+
+static void
+angle_value_changed_cb (GtkAdjustment *adj,
+ gpointer user_data)
+{
+ GthFileToolRotate *self = user_data;
+ double angle;
+
+ if (self->priv->apply_event != 0) {
+ g_source_remove (self->priv->apply_event);
+ self->priv->apply_event = 0;
+ }
+
+ if (self->priv->step != 0) {
+ double angle;
+ double rounded_angle;
+
+ angle = gtk_adjustment_get_value (self->priv->angle_adj) / self->priv->step;
+ rounded_angle = round (angle);
+ if (angle != rounded_angle) {
+ angle = rounded_angle * self->priv->step;
+ gtk_adjustment_set_value (self->priv->angle_adj, angle);
+ return;
+ }
+ }
+
+ g_signal_handlers_block_by_func (self->priv->small_angle_adj, small_angle_value_changed_cb, self);
+ gtk_adjustment_set_value (self->priv->small_angle_adj, 0.0);
+ g_signal_handlers_unblock_by_func (self->priv->small_angle_adj, small_angle_value_changed_cb, self);
+
+ angle = gtk_adjustment_get_value (self->priv->angle_adj) + gtk_adjustment_get_value (self->priv->small_angle_adj);
+ _gth_file_tool_rotate (self, angle);
+}
+
+
+static void
+step_checkbutton_toggled_cb (GtkToggleButton *button,
+ gpointer user_data)
+{
+ GthFileToolRotate *self = user_data;
+
+ if (gtk_toggle_button_get_active (button))
+ self->priv->step = 5.0;
+ else
+ self->priv->step = 0.1;
+ gtk_adjustment_set_step_increment (self->priv->angle_adj, self->priv->step);
+ angle_value_changed_cb (NULL, user_data);
+}
+
+
+static void
+grid_value_changed_cb (GtkAdjustment *adj,
+ gpointer user_data)
+{
+ GthFileToolRotate *self = user_data;
+
+ if (self->priv->use_grid)
+ gth_image_rotator_set_grid (self->priv->rotator, TRUE, (int) gtk_adjustment_get_value (self->priv->grid_adj));
+}
+
+
+static void
+grid_checkbutton_toggled_cb (GtkToggleButton *button,
+ gpointer user_data)
+{
+ GthFileToolRotate *self = user_data;
+
+ self->priv->use_grid = gtk_toggle_button_get_active (button);
+ if (self->priv->use_grid)
+ gth_image_rotator_set_grid (self->priv->rotator, TRUE, (int) gtk_adjustment_get_value (self->priv->grid_adj));
+ else
+ gth_image_rotator_set_grid (self->priv->rotator, FALSE, 0);
+}
+
+
+static void
+size_combobox_changed_cb (GtkComboBox *combo_box,
+ gpointer user_data)
+{
+ GthFileToolRotate *self = user_data;
+
+ gth_image_rotator_set_resize (self->priv->rotator, (GthTransformResize) gtk_combo_box_get_active (combo_box));
+}
+
+
+static GtkWidget *
+gth_file_tool_rotate_get_options (GthFileTool *base)
+{
+ GthFileToolRotate *self;
+ GtkWidget *window;
+ GtkWidget *viewer_page;
+ GtkWidget *viewer;
+ GdkPixbuf *src_pixbuf;
+ GtkAllocation allocation;
+ int max_size;
+ int width;
+ int height;
+ GtkWidget *options;
+
+ self = (GthFileToolRotate *) 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;
+
+ viewer = gth_image_viewer_page_get_image_viewer (GTH_IMAGE_VIEWER_PAGE (viewer_page));
+ src_pixbuf = gth_image_viewer_get_current_pixbuf (GTH_IMAGE_VIEWER (viewer));
+ if (src_pixbuf == NULL)
+ return NULL;
+
+ gtk_widget_get_allocation (viewer, &allocation);
+ max_size = MIN (allocation.width, allocation.height);
+
+ self->priv->pixbuf_width = gdk_pixbuf_get_width (src_pixbuf);
+ self->priv->pixbuf_height = gdk_pixbuf_get_height (src_pixbuf);
+ width = self->priv->pixbuf_width;
+ height = self->priv->pixbuf_height;
+ if (scale_keeping_ratio (&width, &height, max_size, max_size, FALSE))
+ self->priv->tmp_pixbuf = _gdk_pixbuf_scale_simple_safe (src_pixbuf, width, height, GDK_INTERP_BILINEAR);
+ else
+ self->priv->tmp_pixbuf = gdk_pixbuf_copy (src_pixbuf);
+
+ self->priv->unit = eel_gconf_get_enum (PREF_ROTATE_UNIT, GTH_TYPE_UNIT, GTH_UNIT_PERCENTAGE);
+ self->priv->builder = _gtk_builder_new_from_file ("rotate-options.ui", "file_tools");
+
+ options = _gtk_builder_get_widget (self->priv->builder, "options");
+ gtk_widget_show (options);
+
+ if (self->priv->unit == GTH_UNIT_PIXELS) {
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (GET_WIDGET ("center_x_spinbutton")), 0);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (GET_WIDGET ("center_y_spinbutton")), 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("center_x_spinbutton")), self->priv->pixbuf_width / 2);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("center_y_spinbutton")), self->priv->pixbuf_height / 2);
+ }
+ else if (self->priv->unit == GTH_UNIT_PERCENTAGE) {
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (GET_WIDGET ("center_x_spinbutton")), 2);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (GET_WIDGET ("center_y_spinbutton")), 2);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("center_x_spinbutton")), 50.0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("center_y_spinbutton")), 50.0);
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("unit_combobox")), self->priv->unit);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("size_combobox")), 0);
+
+ self->priv->angle_adj = gimp_scale_entry_new (GET_WIDGET ("angle_box"),
+ NULL,
+ 0.0, -180.0, 180.0, 5.0, 10.0, 0);
+ self->priv->small_angle_adj = gimp_scale_entry_new (GET_WIDGET ("small_angle_box"),
+ NULL,
+ 0.0, -5.0, 5.0, 0.01, 0.1, 2);
+ self->priv->grid_adj = gimp_scale_entry_new (GET_WIDGET ("grid_box"),
+ NULL,
+ DEFAULT_GRID, 1.0, 50.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 ("center_x_spinbutton"),
+ "value-changed",
+ G_CALLBACK (center_position_changed_cb),
+ self);
+ g_signal_connect (GET_WIDGET ("center_y_spinbutton"),
+ "value-changed",
+ G_CALLBACK (center_position_changed_cb),
+ self);
+ g_signal_connect (GET_WIDGET ("unit_combobox"),
+ "changed",
+ G_CALLBACK (unit_combobox_changed_cb),
+ self);
+ g_signal_connect (G_OBJECT (self->priv->angle_adj),
+ "value-changed",
+ G_CALLBACK (angle_value_changed_cb),
+ self);
+ g_signal_connect (G_OBJECT (self->priv->small_angle_adj),
+ "value-changed",
+ G_CALLBACK (small_angle_value_changed_cb),
+ self);
+ g_signal_connect (GET_WIDGET ("step_checkbutton"),
+ "toggled",
+ G_CALLBACK (step_checkbutton_toggled_cb),
+ self);
+ g_signal_connect (G_OBJECT (self->priv->grid_adj),
+ "value-changed",
+ G_CALLBACK (grid_value_changed_cb),
+ self);
+ g_signal_connect (GET_WIDGET ("grid_checkbutton"),
+ "toggled",
+ G_CALLBACK (grid_checkbutton_toggled_cb),
+ self);
+ g_signal_connect (GET_WIDGET ("size_combobox"),
+ "changed",
+ G_CALLBACK (size_combobox_changed_cb),
+ self);
+
+ self->priv->rotator = (GthImageRotator *) gth_image_rotator_new (GTH_IMAGE_VIEWER (viewer));
+ gth_image_rotator_set_grid (self->priv->rotator, FALSE, DEFAULT_GRID);
+ _gth_file_tool_rotate (self, 0.0);
+
+ /*g_signal_connect (self->priv->rotator,
+ "changed",
+ G_CALLBACK (rotator_changed_cb),
+ self);
+ */
+
+ gth_image_viewer_set_tool (GTH_IMAGE_VIEWER (viewer), (GthImageViewerTool *) self->priv->rotator);
+
+ return options;
+}
+
+
+static void
+gth_file_tool_rotate_destroy_options (GthFileTool *base)
+{
+ GthFileToolRotate *self;
+ GtkWidget *window;
+ GtkWidget *viewer_page;
+ GtkWidget *viewer;
+
+ self = (GthFileToolRotate *) base;
+
+ if (self->priv->apply_event != 0) {
+ g_source_remove (self->priv->apply_event);
+ self->priv->apply_event = 0;
+ }
+
+ if (self->priv->builder != NULL) {
+ int unit;
+
+ /* save the dialog options */
+
+ unit = gtk_combo_box_get_active (GTK_COMBO_BOX (GET_WIDGET ("unit_combobox")));
+ eel_gconf_set_enum (PREF_ROTATE_UNIT, GTH_TYPE_UNIT, unit);
+
+ /* destroy the options data */
+
+ _g_object_unref (self->priv->tmp_pixbuf);
+ _g_object_unref (self->priv->builder);
+ self->priv->tmp_pixbuf = NULL;
+ self->priv->builder = NULL;
+ }
+
+ window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
+ viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
+ viewer = gth_image_viewer_page_get_image_viewer (GTH_IMAGE_VIEWER_PAGE (viewer_page));
+ gth_image_viewer_set_tool (GTH_IMAGE_VIEWER (viewer), NULL);
+}
+
+
+static void
+gth_file_tool_rotate_activate (GthFileTool *base)
+{
+ gth_file_tool_show_options (base);
+}
+
+
+static void
+gth_file_tool_rotate_instance_init (GthFileToolRotate *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_FILE_TOOL_ROTATE, GthFileToolRotatePrivate);
+ self->priv->tmp_pixbuf = NULL;
+ self->priv->step = 5.0;
+ self->priv->use_grid = TRUE;
+ gth_file_tool_construct (GTH_FILE_TOOL (self), "tool-rotate", _("Rotate..."), _("Rotate"), TRUE);
+}
+
+
+static void
+gth_file_tool_rotate_finalize (GObject *object)
+{
+ GthFileToolRotate *self;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTH_IS_FILE_TOOL_ROTATE (object));
+
+ self = (GthFileToolRotate *) object;
+
+ _g_object_unref (self->priv->tmp_pixbuf);
+ _g_object_unref (self->priv->builder);
+
+ /* Chain up */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gth_file_tool_rotate_class_init (GthFileToolRotateClass *class)
+{
+ GObjectClass *gobject_class;
+ GthFileToolClass *file_tool_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (GthFileToolRotatePrivate));
+
+ gobject_class = (GObjectClass*) class;
+ gobject_class->finalize = gth_file_tool_rotate_finalize;
+
+ file_tool_class = (GthFileToolClass *) class;
+ file_tool_class->update_sensitivity = gth_file_tool_rotate_update_sensitivity;
+ file_tool_class->activate = gth_file_tool_rotate_activate;
+ file_tool_class->get_options = gth_file_tool_rotate_get_options;
+ file_tool_class->destroy_options = gth_file_tool_rotate_destroy_options;
+}
+
+
+GType
+gth_file_tool_rotate_get_type (void) {
+ static GType type_id = 0;
+ if (type_id == 0) {
+ static const GTypeInfo g_define_type_info = {
+ sizeof (GthFileToolRotateClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gth_file_tool_rotate_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (GthFileToolRotate),
+ 0,
+ (GInstanceInitFunc) gth_file_tool_rotate_instance_init,
+ NULL
+ };
+ type_id = g_type_register_static (GTH_TYPE_FILE_TOOL, "GthFileToolRotate", &g_define_type_info, 0);
+ }
+ return type_id;
+}
diff --git a/extensions/file_tools/gth-file-tool-rotate.h b/extensions/file_tools/gth-file-tool-rotate.h
new file mode 100644
index 0000000..927e3ee
--- /dev/null
+++ b/extensions/file_tools/gth-file-tool-rotate.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_ROTATE_H
+#define GTH_FILE_TOOL_ROTATE_H
+
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_FILE_TOOL_ROTATE (gth_file_tool_rotate_get_type ())
+#define GTH_FILE_TOOL_ROTATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_FILE_TOOL_ROTATE, GthFileToolRotate))
+#define GTH_FILE_TOOL_ROTATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_FILE_TOOL_ROTATE, GthFileToolRotateClass))
+#define GTH_IS_FILE_TOOL_ROTATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_FILE_TOOL_ROTATE))
+#define GTH_IS_FILE_TOOL_ROTATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_FILE_TOOL_ROTATE))
+#define GTH_FILE_TOOL_ROTATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_FILE_TOOL_ROTATE, GthFileToolRotateClass))
+
+typedef struct _GthFileToolRotate GthFileToolRotate;
+typedef struct _GthFileToolRotateClass GthFileToolRotateClass;
+typedef struct _GthFileToolRotatePrivate GthFileToolRotatePrivate;
+
+struct _GthFileToolRotate {
+ GthFileTool parent_instance;
+ GthFileToolRotatePrivate *priv;
+};
+
+struct _GthFileToolRotateClass {
+ GthFileToolClass parent_class;
+};
+
+GType gth_file_tool_rotate_get_type (void);
+
+G_END_DECLS
+
+#endif /* GTH_FILE_TOOL_ROTATE_H */
diff --git a/extensions/file_tools/gth-image-rotator.c b/extensions/file_tools/gth-image-rotator.c
new file mode 100644
index 0000000..d394b83
--- /dev/null
+++ b/extensions/file_tools/gth-image-rotator.c
@@ -0,0 +1,555 @@
+/* -*- 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 <stdlib.h>
+#include <math.h>
+#include "gth-image-rotator.h"
+
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+
+static guint signals[LAST_SIGNAL] = { 0 };
+static gpointer parent_class = NULL;
+
+
+struct _GthImageRotatorPrivate {
+ GthImageViewer *viewer;
+ int original_width;
+ int original_height;
+ cairo_surface_t *image;
+ GdkRectangle image_area;
+ GdkRectangle clip_area;
+ GdkRectangle inner_area;
+ GdkPoint center;
+ double angle;
+ gboolean paint_image;
+ gboolean paint_grid;
+ int grid_lines;
+ GthTransformResize resize;
+ cairo_matrix_t matrix;
+};
+
+
+static void
+gth_image_rotator_realize (GthImageViewerTool *base)
+{
+ /* GthImageRotator *self = GTH_IMAGE_ROTATOR (base);
+ FIXME */
+}
+
+
+static void
+gth_image_rotator_unrealize (GthImageViewerTool *base)
+{
+ /* GthImageRotator *self = GTH_IMAGE_ROTATOR (base);
+ FIXME */
+}
+
+
+static void
+_gth_image_rotator_update_tranformation_matrix (GthImageRotator *self)
+{
+ int tx, ty;
+ double zoom;
+
+ tx = self->priv->image_area.x + self->priv->center.x;
+ ty = self->priv->image_area.y + self->priv->center.y;
+ zoom = gth_image_viewer_get_zoom (self->priv->viewer);
+
+ cairo_matrix_init_identity (&self->priv->matrix);
+ cairo_matrix_translate (&self->priv->matrix, tx, ty);
+ cairo_matrix_rotate (&self->priv->matrix, self->priv->angle);
+ cairo_matrix_translate (&self->priv->matrix, -tx, -ty);
+
+ gth_transform_resize (&self->priv->matrix,
+ self->priv->resize,
+ &self->priv->image_area,
+ &self->priv->clip_area);
+}
+
+
+static void
+update_image_surface (GthImageRotator *self)
+{
+ GtkAllocation allocation;
+ GdkPixbuf *src_pixbuf;
+ int max_size;
+ int width;
+ int height;
+ double zoom;
+ GdkPixbuf *tmp_pixbuf;
+
+ if (self->priv->image != NULL) {
+ cairo_surface_destroy (self->priv->image);
+ self->priv->image = NULL;
+ }
+
+ src_pixbuf = gth_image_viewer_get_current_pixbuf (GTH_IMAGE_VIEWER (self->priv->viewer));
+ if (src_pixbuf == NULL)
+ return;
+
+ zoom = gth_image_viewer_get_zoom (self->priv->viewer);
+
+ self->priv->original_width = gdk_pixbuf_get_width (src_pixbuf);
+ self->priv->original_height = gdk_pixbuf_get_height (src_pixbuf);
+ width = self->priv->original_width;
+ height = self->priv->original_height;
+ gtk_widget_get_allocation (GTK_WIDGET (self->priv->viewer), &allocation);
+ max_size = MIN (allocation.width, allocation.height) / 1.2;
+ if (scale_keeping_ratio (&width, &height, max_size, max_size, FALSE))
+ tmp_pixbuf = _gdk_pixbuf_scale_simple_safe (src_pixbuf, width, height, GDK_INTERP_BILINEAR);
+ else
+ tmp_pixbuf = gdk_pixbuf_copy (src_pixbuf);
+ self->priv->image = _cairo_image_surface_create_from_pixbuf (tmp_pixbuf);
+ self->priv->image_area.width = width;
+ self->priv->image_area.height = height;
+ self->priv->image_area.x = MAX ((allocation.width - self->priv->image_area.width) / 2, 0);
+ self->priv->image_area.y = MAX ((allocation.height - self->priv->image_area.height) / 2, 0);
+
+ self->priv->center.x = self->priv->image_area.width * 0.5;
+ self->priv->center.y = self->priv->image_area.height * 0.5;
+
+ _gth_image_rotator_update_tranformation_matrix (self);
+
+ g_object_unref (tmp_pixbuf);
+}
+
+
+static void
+gth_image_rotator_size_allocate (GthImageViewerTool *base,
+ GtkAllocation *allocation)
+{
+ update_image_surface (GTH_IMAGE_ROTATOR (base));
+}
+
+
+static void
+gth_image_rotator_map (GthImageViewerTool *base)
+{
+ /* void */
+}
+
+
+static void
+gth_image_rotator_unmap (GthImageViewerTool *base)
+{
+ /* void */
+}
+
+
+static void
+paint_image (GthImageRotator *self,
+ GdkEventExpose *event,
+ cairo_t *cr)
+{
+ cairo_save (cr);
+
+ cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+ cairo_set_source_surface (cr, self->priv->image,
+ self->priv->image_area.x,
+ self->priv->image_area.y);
+ cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
+ cairo_rectangle (cr,
+ self->priv->image_area.x,
+ self->priv->image_area.y,
+ self->priv->image_area.width,
+ self->priv->image_area.height);
+ cairo_fill (cr);
+
+ cairo_restore (cr);
+}
+
+
+static void
+paint_grid (GthImageRotator *self,
+ GdkEventExpose *event,
+ cairo_t *cr)
+{
+ double ux, uy;
+ double delta;
+ int n;
+ double grid_x, grid_y;
+ int i;
+
+ cairo_save (cr);
+
+ ux = 0.5, uy = 0.5;
+ cairo_device_to_user_distance (cr, &ux, &uy);
+ if (ux < uy)
+ ux = uy;
+ cairo_set_line_width (cr, ux);
+ cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
+
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 9, 2)
+ cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+#else
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+#endif
+
+ delta = self->priv->grid_lines /*(double) self->priv->clip_area.width / (self->priv->grid_lines + 1)*/;
+ n = (double) self->priv->clip_area.width / delta + 0.5;
+ grid_x = self->priv->clip_area.x;
+ grid_y = self->priv->clip_area.y;
+ for (i = 0; i <= n; i++) {
+ cairo_move_to (cr, grid_x + 0.5, grid_y + 0.5);
+ cairo_line_to (cr, grid_x + 0.5, grid_y + self->priv->clip_area.height - 0.5);
+ grid_x += delta;
+ }
+
+ delta = self->priv->grid_lines /*(double) self->priv->clip_area.height / (self->priv->grid_lines + 1)*/;
+ n = (double) self->priv->clip_area.height / delta + 0.5;
+ grid_x = self->priv->clip_area.x;
+ grid_y = self->priv->clip_area.y;
+ for (i = 0; i <= n; i++) {
+ cairo_move_to (cr, grid_x + 0.5, grid_y + 0.5);
+ cairo_line_to (cr, grid_x + self->priv->clip_area.width - 0.5, grid_y + 0.5);
+ grid_y += delta;
+ }
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+}
+
+
+static void
+paint_center (GthImageRotator *self,
+ GdkEventExpose *event,
+ cairo_t *cr)
+{
+ cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
+
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 9, 2)
+ cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+#else
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+#endif
+
+ /* rotation center */
+
+ cairo_translate (cr,
+ self->priv->image_area.x + self->priv->center.x + 0.5,
+ self->priv->image_area.y + self->priv->center.y + 0.5);
+ cairo_arc (cr, 0.0, 0.0, 10.0, 0.0, 2 * M_PI);
+
+ cairo_move_to (cr, 0.0, - 10.0);
+ cairo_line_to (cr, 0.0, 10.0);
+
+ cairo_move_to (cr, 0.0 - 10.0, 0.0);
+ cairo_line_to (cr, 0.0 + 10.0, 0.0);
+
+ cairo_stroke (cr);
+}
+
+
+static void
+gth_image_rotator_expose (GthImageViewerTool *base,
+ GdkEventExpose *event,
+ cairo_t *cr)
+{
+ GthImageRotator *self = GTH_IMAGE_ROTATOR (base);
+ GtkStyle *style;
+ GtkAllocation allocation;
+ cairo_matrix_t matrix;
+
+ if (self->priv->image == NULL)
+ return;
+
+ cairo_save (cr);
+
+ cairo_rectangle (cr,
+ event->area.x,
+ event->area.y,
+ event->area.width,
+ event->area.height);
+ cairo_clip (cr);
+
+ /* background */
+
+ style = gtk_widget_get_style (GTK_WIDGET (self->priv->viewer));
+ gtk_widget_get_allocation (GTK_WIDGET (self->priv->viewer), &allocation);
+ gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
+ cairo_rectangle (cr,
+ 0,
+ 0,
+ allocation.width,
+ allocation.height);
+ cairo_fill (cr);
+
+ /* clip box */
+
+ cairo_rectangle (cr,
+ self->priv->clip_area.x,
+ self->priv->clip_area.y,
+ self->priv->clip_area.width,
+ self->priv->clip_area.height);
+ cairo_clip_preserve (cr);
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_fill (cr);
+
+ /* image */
+
+ matrix = self->priv->matrix;
+ cairo_set_matrix (cr, &matrix);
+
+ if (self->priv->paint_image)
+ paint_image (self, event, cr);
+
+ /* grid */
+
+ cairo_matrix_init_identity (&matrix);
+ cairo_set_matrix (cr, &matrix);
+
+ if (self->priv->paint_grid)
+ paint_grid (self, event, cr);
+
+ paint_center (self, event, cr);
+
+ cairo_restore (cr);
+}
+
+
+static gboolean
+gth_image_rotator_button_release (GthImageViewerTool *base,
+ GdkEventButton *event)
+{
+ /* FIXME */
+
+ return FALSE;
+}
+
+
+static gboolean
+gth_image_rotator_button_press (GthImageViewerTool *base,
+ GdkEventButton *event)
+{
+ /* FIXME */
+
+ return FALSE;
+}
+
+
+static gboolean
+gth_image_rotator_motion_notify (GthImageViewerTool *base,
+ GdkEventMotion *event)
+{
+ /* FIXME */
+
+ return FALSE;
+}
+
+
+static void
+gth_image_rotator_image_changed (GthImageViewerTool *base)
+{
+ update_image_surface (GTH_IMAGE_ROTATOR (base));
+}
+
+
+static void
+gth_image_rotator_zoom_changed (GthImageViewerTool *base)
+{
+ update_image_surface (GTH_IMAGE_ROTATOR (base));
+}
+
+
+static void
+gth_image_rotator_instance_init (GthImageRotator *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_IMAGE_ROTATOR, GthImageRotatorPrivate);
+ self->priv->image = NULL;
+ self->priv->paint_image = TRUE;
+ self->priv->paint_grid = FALSE;
+ self->priv->grid_lines = 0;
+ self->priv->resize = GTH_TRANSFORM_RESIZE_CLIP;
+}
+
+
+static void
+gth_image_rotator_finalize (GObject *object)
+{
+ GthImageRotator *self;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTH_IS_IMAGE_ROTATOR (object));
+
+ self = (GthImageRotator *) object;
+ if (self->priv->image != NULL)
+ cairo_surface_destroy (self->priv->image);
+
+ /* Chain up */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gth_image_rotator_class_init (GthImageRotatorClass *class)
+{
+ GObjectClass *gobject_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (GthImageRotatorPrivate));
+
+ gobject_class = (GObjectClass*) class;
+ gobject_class->finalize = gth_image_rotator_finalize;
+
+ signals[CHANGED] = g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GthImageRotatorClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+
+static void
+gth_image_rotator_gth_image_tool_interface_init (GthImageViewerToolIface *iface)
+{
+ iface->realize = gth_image_rotator_realize;
+ iface->unrealize = gth_image_rotator_unrealize;
+ iface->size_allocate = gth_image_rotator_size_allocate;
+ iface->map = gth_image_rotator_map;
+ iface->unmap = gth_image_rotator_unmap;
+ iface->expose = gth_image_rotator_expose;
+ iface->button_press = gth_image_rotator_button_press;
+ iface->button_release = gth_image_rotator_button_release;
+ iface->motion_notify = gth_image_rotator_motion_notify;
+ iface->image_changed = gth_image_rotator_image_changed;
+ iface->zoom_changed = gth_image_rotator_zoom_changed;
+}
+
+
+GType
+gth_image_rotator_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (GthImageRotatorClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gth_image_rotator_class_init,
+ NULL,
+ NULL,
+ sizeof (GthImageRotator),
+ 0,
+ (GInstanceInitFunc) gth_image_rotator_instance_init
+ };
+ static const GInterfaceInfo gth_image_tool_info = {
+ (GInterfaceInitFunc) gth_image_rotator_gth_image_tool_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT,
+ "GthImageRotator",
+ &type_info,
+ 0);
+ g_type_add_interface_static (type, GTH_TYPE_IMAGE_VIEWER_TOOL, >h_image_tool_info);
+ }
+
+ return type;
+}
+
+
+GthImageViewerTool *
+gth_image_rotator_new (GthImageViewer *viewer)
+{
+ GthImageRotator *rotator;
+
+ rotator = g_object_new (GTH_TYPE_IMAGE_ROTATOR, NULL);
+ rotator->priv->viewer = viewer;
+ rotator->priv->angle = 0.0;
+
+ return GTH_IMAGE_VIEWER_TOOL (rotator);
+}
+
+
+void
+gth_image_rotator_set_center (GthImageRotator *self,
+ int x,
+ int y)
+{
+ self->priv->center.x = x;
+ self->priv->center.y = y;
+ _gth_image_rotator_update_tranformation_matrix (self);
+ gtk_widget_queue_draw (GTK_WIDGET (self->priv->viewer));
+
+ g_signal_emit (self, signals[CHANGED], 0);
+}
+
+
+void
+gth_image_rotator_set_angle (GthImageRotator *self,
+ double angle)
+{
+ double radiants;
+
+ radiants = angle * M_PI / 180.0;
+ if (radiants == self->priv->angle)
+ return;
+ self->priv->angle = radiants;
+ _gth_image_rotator_update_tranformation_matrix (self);
+ gtk_widget_queue_draw (GTK_WIDGET (self->priv->viewer));
+
+ g_signal_emit (self, signals[CHANGED], 0);
+}
+
+
+void
+gth_image_rotator_set_grid (GthImageRotator *self,
+ gboolean use_grid,
+ int lines)
+{
+ self->priv->paint_grid = use_grid;
+ self->priv->grid_lines = lines;
+ gtk_widget_queue_draw (GTK_WIDGET (self->priv->viewer));
+
+ g_signal_emit (self, signals[CHANGED], 0);
+}
+
+
+void
+gth_image_rotator_set_resize (GthImageRotator *self,
+ GthTransformResize resize)
+{
+ self->priv->resize = resize;
+ _gth_image_rotator_update_tranformation_matrix (self);
+ gtk_widget_queue_draw (GTK_WIDGET (self->priv->viewer));
+
+ g_signal_emit (self, signals[CHANGED], 0);
+}
+
+
+GdkPixbuf *
+gth_image_rotator_get_result (GthImageRotator *self)
+{
+ return NULL;
+}
diff --git a/extensions/file_tools/gth-image-rotator.h b/extensions/file_tools/gth-image-rotator.h
new file mode 100644
index 0000000..34bd40d
--- /dev/null
+++ b/extensions/file_tools/gth-image-rotator.h
@@ -0,0 +1,74 @@
+/* -*- 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_IMAGE_ROTATOR_H
+#define GTH_IMAGE_ROTATOR_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gthumb.h>
+#include "gth-transform-resize.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_IMAGE_ROTATOR (gth_image_rotator_get_type ())
+#define GTH_IMAGE_ROTATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_IMAGE_ROTATOR, GthImageRotator))
+#define GTH_IMAGE_ROTATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_IMAGE_ROTATOR, GthImageRotatorClass))
+#define GTH_IS_IMAGE_ROTATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_IMAGE_ROTATOR))
+#define GTH_IS_IMAGE_ROTATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_IMAGE_ROTATOR))
+#define GTH_IMAGE_ROTATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_IMAGE_ROTATOR, GthImageRotatorClass))
+
+typedef struct _GthImageRotator GthImageRotator;
+typedef struct _GthImageRotatorClass GthImageRotatorClass;
+typedef struct _GthImageRotatorPrivate GthImageRotatorPrivate;
+
+struct _GthImageRotator
+{
+ GObject __parent;
+ GthImageRotatorPrivate *priv;
+};
+
+struct _GthImageRotatorClass
+{
+ GObjectClass __parent_class;
+
+ /*< signals >*/
+
+ void (* changed) (GthImageRotator *rotator);
+};
+
+GType gth_image_rotator_get_type (void);
+GthImageViewerTool * gth_image_rotator_new (GthImageViewer *viewer);
+void gth_image_rotator_set_center (GthImageRotator *rotator,
+ int x,
+ int y);
+void gth_image_rotator_set_angle (GthImageRotator *rotator,
+ double angle);
+void gth_image_rotator_set_grid (GthImageRotator *self,
+ gboolean use_grid,
+ int lines);
+void gth_image_rotator_set_resize (GthImageRotator *self,
+ GthTransformResize resize);
+GdkPixbuf * gth_image_rotator_get_result (GthImageRotator *self);
+
+G_END_DECLS
+
+#endif /* GTH_IMAGE_ROTATOR_H */
diff --git a/extensions/file_tools/gth-transform-resize.c b/extensions/file_tools/gth-transform-resize.c
new file mode 100644
index 0000000..23d2048
--- /dev/null
+++ b/extensions/file_tools/gth-transform-resize.c
@@ -0,0 +1,890 @@
+/* -*- 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/>.
+ */
+
+/* Based on the file app/core/gimp-transform-resize.h of gimp 2.6
+ *
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
+ *
+ * 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 Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <glib.h>
+#include "gth-transform-resize.h"
+
+
+#define HAVE_ISFINITE 1
+
+
+#if defined (HAVE_FINITE)
+#define FINITE(x) finite(x)
+#elif defined (HAVE_ISFINITE)
+#define FINITE(x) isfinite(x)
+#elif defined (G_OS_WIN32)
+#define FINITE(x) _finite(x)
+#else
+#error "no FINITE() implementation available?!"
+#endif
+
+#define EPSILON 0.00000001
+#define MIN4(a,b,c,d) MIN(MIN((a),(b)),MIN((c),(d)))
+#define MAX4(a,b,c,d) MAX(MAX((a),(b)),MAX((c),(d)))
+
+
+typedef struct
+{
+ gdouble x, y;
+} Point;
+
+typedef struct
+{
+ Point a, b, c, d;
+ gdouble area;
+ gdouble aspect;
+} Rectangle;
+
+
+static void gth_transform_resize_boundary (cairo_matrix_t *inv,
+ GthTransformResize resize,
+ int u1,
+ int v1,
+ int u2,
+ int v2,
+ int *x1,
+ int *y1,
+ int *x2,
+ int *y2);
+static void gimp_transform_resize_adjust (gdouble dx1,
+ gdouble dy1,
+ gdouble dx2,
+ gdouble dy2,
+ gdouble dx3,
+ gdouble dy3,
+ gdouble dx4,
+ gdouble dy4,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2);
+static void gimp_transform_resize_crop (gdouble dx1,
+ gdouble dy1,
+ gdouble dx2,
+ gdouble dy2,
+ gdouble dx3,
+ gdouble dy3,
+ gdouble dx4,
+ gdouble dy4,
+ gdouble aspect,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2);
+
+static void add_rectangle (Point points[4],
+ Rectangle *r,
+ Point a,
+ Point b,
+ Point c,
+ Point d);
+static gboolean intersect (Point a,
+ Point b,
+ Point c,
+ Point d,
+ Point *i);
+static gboolean intersect_x (Point a,
+ Point b,
+ Point c,
+ Point *i);
+static gboolean intersect_y (Point a,
+ Point b,
+ Point c,
+ Point *i);
+static gboolean in_poly (Point points[4],
+ Point p);
+static gboolean point_on_border (Point points[4],
+ Point p);
+
+static void find_two_point_rectangle (Rectangle *r,
+ Point points[4],
+ gint p);
+static void find_three_point_rectangle_corner (Rectangle *r,
+ Point points[4],
+ gint p);
+static void find_three_point_rectangle (Rectangle *r,
+ Point points[4],
+ gint p);
+static void find_three_point_rectangle_triangle (Rectangle *r,
+ Point points[4],
+ gint p);
+static void find_maximum_aspect_rectangle (Rectangle *r,
+ Point points[4],
+ gint p);
+
+
+void
+gth_transform_resize (cairo_matrix_t *matrix,
+ GthTransformResize resize,
+ GdkRectangle *original,
+ GdkRectangle *boundary)
+{
+ int o_x1, o_y1, o_x2, o_y2;
+ int t_x1, t_y1, t_x2, t_y2;
+
+ o_x1 = original->x;
+ o_y1 = original->y;
+ o_x2 = original->x + original->width;
+ o_y2 = original->y + original->height;
+
+ gth_transform_resize_boundary (matrix,
+ resize,
+ o_x1, o_y1, o_x2, o_y2,
+ &t_x1, &t_y1, &t_x2, &t_y2);
+
+ boundary->x = t_x1;
+ boundary->y = t_y1;
+ boundary->width = t_x2 - t_x1;
+ boundary->height = t_y2 - t_y1;
+}
+
+
+static void
+_cairo_matrix_transform_point (cairo_matrix_t *matrix,
+ double x,
+ double y,
+ double *tx,
+ double *ty)
+{
+ *tx = x;
+ *ty = y;
+ cairo_matrix_transform_point (matrix, tx, ty);
+}
+
+
+/*
+ * This function wants to be passed the inverse transformation matrix!!
+ */
+static void
+gth_transform_resize_boundary (cairo_matrix_t *inv,
+ GthTransformResize resize,
+ int u1,
+ int v1,
+ int u2,
+ int v2,
+ int *x1,
+ int *y1,
+ int *x2,
+ int *y2)
+{
+ double dx1, dx2, dx3, dx4;
+ double dy1, dy2, dy3, dy4;
+
+ g_return_if_fail (inv != NULL);
+
+ /* initialize with the original boundary */
+ *x1 = u1;
+ *y1 = v1;
+ *x2 = u2;
+ *y2 = v2;
+
+ /* if clipping then just return the original rectangle */
+ if (resize == GTH_TRANSFORM_RESIZE_CLIP)
+ return;
+
+ _cairo_matrix_transform_point (inv, u1, v1, &dx1, &dy1);
+ _cairo_matrix_transform_point (inv, u2, v1, &dx2, &dy2);
+ _cairo_matrix_transform_point (inv, u1, v2, &dx3, &dy3);
+ _cairo_matrix_transform_point (inv, u2, v2, &dx4, &dy4);
+
+ /* check if the transformation matrix is valid at all */
+ if (! FINITE (dx1) || ! FINITE (dy1) ||
+ ! FINITE (dx2) || ! FINITE (dy2) ||
+ ! FINITE (dx3) || ! FINITE (dy3) ||
+ ! FINITE (dx4) || ! FINITE (dy4))
+ {
+ g_warning ("invalid transform matrix");
+ /* since there is no sensible way to deal with this, just do
+ * the same as with GTH_TRANSFORM_RESIZE_CLIP: return
+ */
+ return;
+ }
+
+ switch (resize) {
+ case GTH_TRANSFORM_RESIZE_BOUNDING_BOX:
+ /* return smallest rectangle (with sides parallel to x- and y-axis)
+ * that surrounds the new points */
+ gimp_transform_resize_adjust (dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4,
+ x1, y1, x2, y2);
+ break;
+
+ case GTH_TRANSFORM_RESIZE_CROP:
+ gimp_transform_resize_crop (dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4,
+ 0.0,
+ x1, y1, x2, y2);
+ break;
+
+ case GTH_TRANSFORM_RESIZE_CROP_KEEP_RATIO:
+ gimp_transform_resize_crop (dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4,
+ ((gdouble) u2 - u1) / (v2 - v1),
+ x1, y1, x2, y2);
+ break;
+
+ default:
+ break;
+ }
+
+ /* ensure that resulting rectangle has at least area 1 */
+ if (*x1 == *x2)
+ (*x2)++;
+
+ if (*y1 == *y2)
+ (*y2)++;
+}
+
+
+/* this calculates the smallest rectangle (with sides parallel to x- and
+ * y-axis) that contains the points d1 to d4
+ */
+static void
+gimp_transform_resize_adjust (gdouble dx1,
+ gdouble dy1,
+ gdouble dx2,
+ gdouble dy2,
+ gdouble dx3,
+ gdouble dy3,
+ gdouble dx4,
+ gdouble dy4,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2)
+{
+ *x1 = (gint) floor (MIN4 (dx1, dx2, dx3, dx4));
+ *y1 = (gint) floor (MIN4 (dy1, dy2, dy3, dy4));
+
+ *x2 = (gint) ceil (MAX4 (dx1, dx2, dx3, dx4));
+ *y2 = (gint) ceil (MAX4 (dy1, dy2, dy3, dy4));
+}
+
+static void
+gimp_transform_resize_crop (gdouble dx1,
+ gdouble dy1,
+ gdouble dx2,
+ gdouble dy2,
+ gdouble dx3,
+ gdouble dy3,
+ gdouble dx4,
+ gdouble dy4,
+ gdouble aspect,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2)
+{
+ Point points[4];
+ Rectangle r;
+ Point t,a;
+ gint i, j;
+ gint min;
+
+ /* fill in the points array */
+ points[0].x = dx1;
+ points[0].y = dy1;
+ points[1].x = dx2;
+ points[1].y = dy2;
+ points[2].x = dx3;
+ points[2].y = dy3;
+ points[3].x = dx4;
+ points[3].y = dy4;
+
+ /* find lowest, rightmost corner of surrounding rectangle */
+ a.x = 0;
+ a.y = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (points[i].x < a.x)
+ a.x = points[i].x;
+
+ if (points[i].y < a.y)
+ a.y = points[i].y;
+ }
+
+ /* and translate all the points to the first quadrant */
+ for (i = 0; i < 4; i++)
+ {
+ points[i].x += (-a.x) * 2;
+ points[i].y += (-a.y) * 2;
+ }
+
+ /* find the convex hull using Jarvis's March as the points are passed
+ * in different orders due to gimp_matrix3_transform_point()
+ */
+ min = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (points[i].y < points[min].y)
+ min = i;
+ }
+
+ t = points[0];
+ points[0] = points[min];
+ points[min] = t;
+
+ for (i = 1; i < 4; i++)
+ {
+ gdouble theta, theta_m = 2.0 * G_PI;
+ gdouble theta_v = 0;
+
+ min = 3;
+
+ for (j = i; j < 4; j++)
+ {
+ gdouble sy = points[j].y - points[i - 1].y;
+ gdouble sx = points[j].x - points[i - 1].x;
+
+ theta = atan2 (sy, sx);
+
+ if ((theta < theta_m) &&
+ ((theta > theta_v) || ((theta == theta_v) && (sx > 0))))
+ {
+ theta_m = theta;
+ min = j;
+ }
+ }
+
+ theta_v = theta_m;
+
+ t = points[i];
+ points[i] = points[min];
+ points[min] = t;
+ }
+
+ /* reverse the order of points */
+ t = points[0];
+ points[0] = points[3];
+ points[3] = t;
+
+ t = points[1];
+ points[1] = points[2];
+ points[2] = t;
+
+ r.a.x = r.a.y = r.b.x = r.b.y = r.c.x = r.c.y = r.d.x = r.d.y = r.area = 0;
+ r.aspect = aspect;
+
+ if (aspect != 0)
+ {
+ for (i = 0; i < 4; i++)
+ find_maximum_aspect_rectangle (&r, points, i);
+ }
+ else
+ {
+ for (i = 0; i < 4; i++)
+ {
+ find_three_point_rectangle (&r, points, i);
+ find_three_point_rectangle_corner (&r, points, i);
+ find_two_point_rectangle (&r, points, i);
+ find_three_point_rectangle_triangle (&r, points, i);
+ }
+ }
+
+ if (r.area == 0)
+ {
+ /* saveguard if something went wrong, adjust and give warning */
+ gimp_transform_resize_adjust (dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4,
+ x1, y1, x2, y2);
+ g_warning ("no rectangle found by algorith, no cropping done");
+ return;
+ }
+ else
+ {
+ /* round and translate the calculated points back */
+ *x1 = floor (r.a.x + 0.5);
+ *y1 = floor (r.a.y + 0.5);
+ *x2 = ceil (r.c.x - 0.5);
+ *y2 = ceil (r.c.y - 0.5);
+
+ *x1 = *x1 - ((-a.x) * 2);
+ *y1 = *y1 - ((-a.y) * 2);
+ *x2 = *x2 - ((-a.x) * 2);
+ *y2 = *y2 - ((-a.y) * 2);
+ return;
+ }
+}
+
+static void
+find_three_point_rectangle (Rectangle *r,
+ Point points[4],
+ gint p)
+{
+ Point a = points[p % 4]; /* 0 1 2 3 */
+ Point b = points[(p + 1) % 4]; /* 1 2 3 0 */
+ Point c = points[(p + 2) % 4]; /* 2 3 0 1 */
+ Point d = points[(p + 3) % 4]; /* 3 0 1 2 */
+ Point i1; /* intersection point */
+ Point i2; /* intersection point */
+ Point i3; /* intersection point */
+
+ if (intersect_x (b, c, a, &i1) &&
+ intersect_y (c, d, i1, &i2) &&
+ intersect_x (d, a, i2, &i3))
+ add_rectangle (points, r, i3, i3, i1, i1);
+
+ if (intersect_y (b, c, a, &i1) &&
+ intersect_x (c, d, i1, &i2) &&
+ intersect_y (d, a, i2, &i3))
+ add_rectangle (points, r, i3, i3, i1, i1);
+
+ if (intersect_x (d, c, a, &i1) &&
+ intersect_y (c, b, i1, &i2) &&
+ intersect_x (b, a, i2, &i3))
+ add_rectangle (points, r, i3, i3, i1, i1);
+
+ if (intersect_y (d, c, a, &i1) &&
+ intersect_x (c, b, i1, &i2) &&
+ intersect_y (b, a, i2, &i3))
+ add_rectangle (points, r, i3, i3, i1, i1);
+}
+
+static void
+find_three_point_rectangle_corner (Rectangle *r,
+ Point points[4],
+ gint p)
+{
+ Point a = points[p % 4]; /* 0 1 2 3 */
+ Point b = points[(p + 1) % 4]; /* 1 2 3 0 */
+ Point c = points[(p + 2) % 4]; /* 2 3 0 2 */
+ Point d = points[(p + 3) % 4]; /* 3 0 2 1 */
+ Point i1; /* intersection point */
+ Point i2; /* intersection point */
+
+ if (intersect_x (b, c, a , &i1) &&
+ intersect_y (c, d, i1, &i2))
+ add_rectangle (points, r, a, a, i1, i2);
+
+ if (intersect_y (b, c, a , &i1) &&
+ intersect_x (c, d, i1, &i2))
+ add_rectangle (points, r, a, a, i1, i2);
+
+ if (intersect_x (c, d, a , &i1) &&
+ intersect_y (b, c, i1, &i2))
+ add_rectangle (points, r, a, a, i1, i2);
+
+ if (intersect_y (c, d, a , &i1) &&
+ intersect_x (b, c, i1, &i2))
+ add_rectangle (points, r, a, a, i1, i2);
+}
+
+static void
+find_two_point_rectangle (Rectangle *r,
+ Point points[4],
+ gint p)
+{
+ Point a = points[ p % 4]; /* 0 1 2 3 */
+ Point b = points[(p + 1) % 4]; /* 1 2 3 0 */
+ Point c = points[(p + 2) % 4]; /* 2 3 0 1 */
+ Point d = points[(p + 3) % 4]; /* 3 0 1 2 */
+ Point i1; /* intersection point */
+ Point i2; /* intersection point */
+ Point mid; /* Mid point */
+
+ add_rectangle (points, r, a, a, c, c);
+ add_rectangle (points, r, b, b, d, d);
+
+ if (intersect_x (c, b, a, &i1) &&
+ intersect_y (c, b, a, &i2))
+ {
+ mid.x = ( i1.x + i2.x ) / 2.0;
+ mid.y = ( i1.y + i2.y ) / 2.0;
+
+ add_rectangle (points, r, a, a, mid, mid);
+ }
+}
+
+static void
+find_three_point_rectangle_triangle (Rectangle *r,
+ Point points[4],
+ gint p)
+{
+ Point a = points[p % 4]; /* 0 1 2 3 */
+ Point b = points[(p + 1) % 4]; /* 1 2 3 0 */
+ Point c = points[(p + 2) % 4]; /* 2 3 0 1 */
+ Point d = points[(p + 3) % 4]; /* 3 0 1 2 */
+ Point i1; /* intersection point */
+ Point i2; /* intersection point */
+ Point mid;
+
+ mid.x = (a.x + b.x) / 2.0;
+ mid.y = (a.y + b.y) / 2.0;
+
+ if (intersect_x (b, c, mid, &i1) &&
+ intersect_y (a, d, mid, &i2))
+ add_rectangle (points, r, mid, mid, i1, i2);
+
+ if (intersect_y (b, c, mid, &i1) &&
+ intersect_x (a, d, mid, &i2))
+ add_rectangle (points, r, mid, mid, i1, i2);
+
+ if (intersect_x (a, d, mid, &i1) &&
+ intersect_y (b, c, mid, &i2))
+ add_rectangle (points, r, mid, mid, i1, i2);
+
+ if (intersect_y (a, d, mid, &i1) &&
+ intersect_x (b, c, mid, &i2))
+ add_rectangle (points, r, mid, mid, i1, i2);
+}
+
+static void
+find_maximum_aspect_rectangle (Rectangle *r,
+ Point points[4],
+ gint p)
+{
+ Point a = points[ p % 4]; /* 0 1 2 3 */
+ Point b = points[(p + 1) % 4]; /* 1 2 3 0 */
+ Point c = points[(p + 2) % 4]; /* 2 3 0 1 */
+ Point d = points[(p + 3) % 4]; /* 3 0 1 2 */
+ Point i1; /* intersection point */
+ Point i2; /* intersection point */
+ Point i3; /* intersection point */
+
+ if (intersect_x (b, c, a, &i1))
+ {
+ i2.x = i1.x + 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (c, d, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ i2.x = i1.x - 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (c, d, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+ }
+
+ if (intersect_y (b, c, a, &i1))
+ {
+ i2.x = i1.x + 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (c, d, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ i2.x = i1.x - 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (c, d, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+ }
+
+ if (intersect_x (c, d, a, &i1))
+ {
+ i2.x = i1.x + 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (b, c, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ i2.x = i1.x - 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (b, c, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+ }
+
+ if (intersect_y (c, d, a, &i1))
+ {
+ i2.x = i1.x + 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (b, c, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ i2.x = i1.x - 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+
+ if (intersect (b, c, i1, i2, &i3))
+ add_rectangle (points, r, i1, i3, i1, i3);
+ }
+}
+
+/* check if point is inside the polygon "points", if point is on border
+ * its still inside.
+ */
+static gboolean
+in_poly (Point points[4],
+ Point p)
+{
+ Point p1, p2;
+ gint counter = 0;
+ gint i;
+
+ p1 = points[0];
+
+ for (i = 1; i <= 4; i++)
+ {
+ p2 = points[i % 4];
+
+ if (p.y > MIN (p1.y, p2.y))
+ {
+ if (p.y <= MAX (p1.y, p2.y))
+ {
+ if (p.x <= MAX (p1.x, p2.x))
+ {
+ if (p1.y != p2.y)
+ {
+ gdouble xinters = ((p.y - p1.y) * (p2.x - p1.x) /
+ (p2.y - p1.y) + p1.x);
+
+ if (p1.x == p2.x || p.x <= xinters)
+ counter++;
+ }
+ }
+ }
+ }
+
+ p1 = p2;
+ }
+
+ /* border check */
+ if (point_on_border (points, p))
+ return TRUE;
+
+ return (counter % 2 != 0);
+}
+
+/* check if the point p lies on the polygon "points"
+ */
+static gboolean
+point_on_border (Point points[4],
+ Point p)
+{
+ gint i;
+
+ for (i = 0; i <= 4; i++)
+ {
+ Point a = points[i % 4];
+ Point b = points[(i + 1) % 4];
+ gdouble a1 = (b.y - a.y);
+ gdouble b1 = (a.x - b.x);
+ gdouble c1 = a1 * a.x + b1 * a.y;
+ gdouble c2 = a1 * p.x + b1 * p.y;
+
+ if (ABS (c1 - c2) < EPSILON &&
+ MIN (a.x, b.x) <= p.x &&
+ MAX (a.x, b.x) >= p.x &&
+ MIN (a.y, b.y) <= p.y &&
+ MAX (a.y, b.y) >= p.y)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* calculate the intersection point of the line a-b with the line c-d
+ * and write it to i, if existing.
+ */
+static gboolean
+intersect (Point a,
+ Point b,
+ Point c,
+ Point d,
+ Point *i)
+{
+ gdouble a1 = (b.y - a.y);
+ gdouble b1 = (a.x - b.x);
+ gdouble c1 = a1 * a.x + b1 * a.y;
+
+ gdouble a2 = (d.y - c.y);
+ gdouble b2 = (c.x - d.x);
+ gdouble c2 = a2 * c.x + b2 * c.y;
+ gdouble det = a1 * b2 - a2 * b1;
+
+ if (det == 0)
+ return FALSE;
+
+ i->x = (b2 * c1 - b1 * c2) / det;
+ i->y = (a1 * c2 - a2 * c1) / det;
+
+ return TRUE;
+}
+
+/* calculate the intersection point of the line a-b with the vertical line
+ * through c and write it to i, if existing.
+ */
+static gboolean
+intersect_x (Point a,
+ Point b,
+ Point c,
+ Point *i)
+{
+ Point d = c;
+ d.y += 1;
+
+ return intersect(a,b,c,d,i);
+}
+
+/* calculate the intersection point of the line a-b with the horizontal line
+ * through c and write it to i, if existing.
+ */
+static gboolean
+intersect_y (Point a,
+ Point b,
+ Point c,
+ Point *i)
+{
+ Point d = c;
+ d.x += 1;
+
+ return intersect(a,b,c,d,i);
+}
+
+/* this takes the smallest ortho-aligned (the sides of the rectangle are
+ * parallel to the x- and y-axis) rectangle fitting around the points a to d,
+ * checks if the whole rectangle is inside the polygon described by points and
+ * writes it to r if the area is bigger than the rectangle already stored in r.
+ */
+static void
+add_rectangle (Point points[4],
+ Rectangle *r,
+ Point a,
+ Point b,
+ Point c,
+ Point d)
+{
+ gdouble width;
+ gdouble height;
+ gdouble minx, maxx;
+ gdouble miny, maxy;
+
+ /* get the orthoaligned (the sides of the rectangle are parallel to the x-
+ * and y-axis) rectangle surrounding the points a to d.
+ */
+ minx = MIN4 (a.x, b.x, c.x, d.x);
+ maxx = MAX4 (a.x, b.x, c.x, d.x);
+ miny = MIN4 (a.y, b.y, c.y, d.y);
+ maxy = MAX4 (a.y, b.y, c.y, d.y);
+
+ a.x = minx;
+ a.y = miny;
+
+ b.x = maxx;
+ b.y = miny;
+
+ c.x = maxx;
+ c.y = maxy;
+
+ d.x = minx;
+ d.y = maxy;
+
+ width = maxx - minx;
+ height = maxy - miny;
+
+ /* check if this rectangle is inside the polygon "points" */
+ if (in_poly (points, a) &&
+ in_poly (points, b) &&
+ in_poly (points, c) &&
+ in_poly (points, d))
+ {
+ gdouble area = width * height;
+
+ /* check if the new rectangle is larger (in terms of area)
+ * than the currently stored rectangle in r, if yes store
+ * new rectangle to r
+ */
+ if (r->area <= area)
+ {
+ r->a.x = a.x;
+ r->a.y = a.y;
+
+ r->b.x = b.x;
+ r->b.y = b.y;
+
+ r->c.x = c.x;
+ r->c.y = c.y;
+
+ r->d.x = d.x;
+ r->d.y = d.y;
+
+ r->area = area;
+ }
+ }
+}
diff --git a/extensions/file_tools/gth-transform-resize.h b/extensions/file_tools/gth-transform-resize.h
new file mode 100644
index 0000000..488aad4
--- /dev/null
+++ b/extensions/file_tools/gth-transform-resize.h
@@ -0,0 +1,60 @@
+/* -*- 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/>.
+ */
+
+/* Based from the file app/core/gimp-transform-resize.h of gimp 2.6
+ *
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
+ *
+ * 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 Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_TRANSFORM_RESIZE_H
+#define GTH_TRANSFORM_RESIZE_H
+
+#include <gdk/gdk.h>
+#include <cairo.h>
+
+typedef enum {
+ GTH_TRANSFORM_RESIZE_CLIP,
+ GTH_TRANSFORM_RESIZE_BOUNDING_BOX,
+ GTH_TRANSFORM_RESIZE_CROP,
+ GTH_TRANSFORM_RESIZE_CROP_KEEP_RATIO
+} GthTransformResize;
+
+void gth_transform_resize (cairo_matrix_t *matrix,
+ GthTransformResize resize,
+ GdkRectangle *original,
+ GdkRectangle *boundary);
+
+#endif /* GTH_TRANSFORM_RESIZE_H */
diff --git a/extensions/file_tools/main.c b/extensions/file_tools/main.c
index 6931e66..e95dd5a 100644
--- a/extensions/file_tools/main.c
+++ b/extensions/file_tools/main.c
@@ -34,6 +34,7 @@
#include "gth-file-tool-negative.h"
#include "gth-file-tool-redo.h"
#include "gth-file-tool-resize.h"
+#include "gth-file-tool-rotate.h"
#include "gth-file-tool-rotate-left.h"
#include "gth-file-tool-rotate-right.h"
#include "gth-file-tool-save.h"
@@ -57,11 +58,11 @@ gthumb_extension_activate (void)
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_DESATURATE);
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_NEGATIVE);
- gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_MIRROR);
- gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_FLIP);
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_ROTATE_RIGHT);
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_ROTATE_LEFT);
-
+ gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_MIRROR);
+ gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_FLIP);
+ gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_ROTATE);
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_RESIZE);
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_CROP);
diff --git a/extensions/file_tools/preferences.h b/extensions/file_tools/preferences.h
index 2d3c836..132c163 100644
--- a/extensions/file_tools/preferences.h
+++ b/extensions/file_tools/preferences.h
@@ -43,6 +43,8 @@ G_BEGIN_DECLS
#define PREF_RESIZE_ASPECT_RATIO_INVERT "/apps/gthumb/ext/resize/aspect_ratio_invert"
#define PREF_RESIZE_HIGH_QUALITY "/apps/gthumb/ext/resize/high_quality"
+#define PREF_ROTATE_UNIT "/apps/gthumb/ext/rotate/unit"
+
G_END_DECLS
#endif /* PREFERENCES_H */
diff --git a/gthumb/cairo-utils.c b/gthumb/cairo-utils.c
index 5b72c29..47ced00 100644
--- a/gthumb/cairo-utils.c
+++ b/gthumb/cairo-utils.c
@@ -46,6 +46,27 @@ _gdk_color_to_cairo_color_255 (GdkColor *g_color,
}
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN /* BGRA */
+#define SET_PIXEL(red, green, blue, alpha) \
+ s_iter[0] = blue; \
+ s_iter[1] = green; \
+ s_iter[2] = red; \
+ s_iter[3] = alpha;
+#elif G_BYTE_ORDER == G_BIG_ENDIAN /* ARGB */
+#define SET_PIXEL(red, green, blue, alpha) \
+ s_iter[0] = alpha; \
+ s_iter[1] = red; \
+ s_iter[2] = green; \
+ s_iter[3] = blue;
+#else /* PDP endianness: RABG */
+#define SET_PIXEL(red, green, blue, alpha) \
+ s_iter[0] = red; \
+ s_iter[1] = alpha; \
+ s_iter[2] = blue; \
+ s_iter[3] = green;
+#endif
+
+
void
_cairo_paint_full_gradient (cairo_surface_t *surface,
GdkColor *h_color1,
@@ -85,8 +106,7 @@ _cairo_paint_full_gradient (cairo_surface_t *surface,
x = (double) (height - h) / height;
for (w = 0; w < width; w++) {
- y = (double) (width - w) / width;
-
+ y = (double) (width - w) / width;
x_y = x * y;
x_1_y = x * (1.0 - y);
y_1_x = y * (1.0 - x);
@@ -96,25 +116,7 @@ _cairo_paint_full_gradient (cairo_surface_t *surface,
green = hcolor1.g * x_y + hcolor2.g * x_1_y + vcolor1.g * y_1_x + vcolor2.g * _1_x_1_y;
blue = hcolor1.b * x_y + hcolor2.b * x_1_y + vcolor1.b * y_1_x + vcolor2.b * _1_x_1_y;
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
- /* BGRA */
- s_iter[0] = blue;
- s_iter[1] = green;
- s_iter[2] = red;
- s_iter[3] = 0xff;
-#elif G_BYTE_ORDER == G_BIG_ENDIAN
- /* ARGB */
- s_iter[0] = 0xff;
- s_iter[1] = red;
- s_iter[2] = green;
- s_iter[3] = blue;
-#else /* PDP endianness */
- /* RABG */
- s_iter[0] = red;
- s_iter[1] = 0xff;
- s_iter[2] = blue;
- s_iter[3] = green;
-#endif
+ SET_PIXEL (red, green, blue, 0xff);
s_iter += 4;
}
@@ -124,6 +126,80 @@ _cairo_paint_full_gradient (cairo_surface_t *surface,
}
+cairo_surface_t *
+_cairo_image_surface_create_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ cairo_surface_t *surface;
+ int width;
+ int height;
+ int p_stride;
+ int p_n_channels;
+ guchar *p_pixels;
+ int s_stride;
+ unsigned char *s_pixels;
+ int h, w;
+
+ g_object_get (G_OBJECT (pixbuf),
+ "width", &width,
+ "height", &height,
+ "rowstride", &p_stride,
+ "n-channels", &p_n_channels,
+ "pixels", &p_pixels,
+ NULL );
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ s_stride = cairo_image_surface_get_stride (surface);
+ s_pixels = cairo_image_surface_get_data (surface);
+
+ if (p_n_channels == 4) {
+ guchar *s_iter;
+ guchar *p_iter;
+ double alpha_factor;
+ guchar red, green, blue, alpha;
+
+ for (h = 0; h < height; h++) {
+ s_iter = s_pixels;
+ p_iter = p_pixels;
+
+ for (w = 0; w < width; w++) {
+ alpha = p_iter[3];
+ alpha_factor = (double) alpha / 255.0;
+ red = (guchar) (alpha_factor * p_iter[0]) ;
+ green = (guchar) (alpha_factor * p_iter[1]);
+ blue = (guchar) (alpha_factor * p_iter[2]);
+
+ SET_PIXEL (red, green, blue, alpha);
+
+ s_iter += 4;
+ p_iter += p_n_channels;
+ }
+
+ s_pixels += s_stride;
+ p_pixels += p_stride;
+ }
+ }
+ else {
+ guchar *s_iter;
+ guchar *p_iter;
+
+ for (h = 0; h < height; h++) {
+ s_iter = s_pixels;
+ p_iter = p_pixels;
+
+ for (w = 0; w < width; w++) {
+ SET_PIXEL (p_iter[0], p_iter[1], p_iter[2], 0xff);
+ s_iter += 4;
+ p_iter += p_n_channels;
+ }
+
+ s_pixels += s_stride;
+ p_pixels += p_stride;
+ }
+ }
+
+ return surface;
+}
+
+
void
_cairo_draw_rounded_box (cairo_t *cr,
double x,
diff --git a/gthumb/cairo-utils.h b/gthumb/cairo-utils.h
index ace63b7..be1d010 100644
--- a/gthumb/cairo-utils.h
+++ b/gthumb/cairo-utils.h
@@ -24,6 +24,7 @@
#include <glib.h>
#include <gdk/gdk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
#include <cairo.h>
typedef struct {
@@ -49,6 +50,9 @@ void _cairo_paint_full_gradient (cairo_surface_t *surface,
GdkColor *h_color2,
GdkColor *v_color1,
GdkColor *v_color2);
+cairo_surface_t *
+ _cairo_image_surface_create_from_pixbuf
+ (GdkPixbuf *pixbuf);
void _cairo_draw_rounded_box (cairo_t *cr,
double x,
double y,
diff --git a/gthumb/gtk-utils.c b/gthumb/gtk-utils.c
index a5e33a2..1ca5412 100644
--- a/gthumb/gtk-utils.c
+++ b/gthumb/gtk-utils.c
@@ -1302,7 +1302,8 @@ gimp_scale_entry_new (GtkWidget *parent_box,
gtk_box_pack_start (GTK_BOX (parent_box), hbox, TRUE, TRUE, 0);
gtk_widget_show_all (hbox);
- gtk_label_set_mnemonic_widget (label, scale);
+ if (label != NULL)
+ gtk_label_set_mnemonic_widget (label, scale);
return (GtkAdjustment *) adj;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]