[gnome-photos/wip/rishi/edit-mode: 23/28] Add PhotosToolCrop
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/edit-mode: 23/28] Add PhotosToolCrop
- Date: Tue, 10 Nov 2015 09:48:17 +0000 (UTC)
commit 7bb584c35a220da40e52006681166ed595704388
Author: Debarshi Ray <debarshir gnome org>
Date: Fri Jun 12 01:35:43 2015 +0200
Add PhotosToolCrop
Most of the code is based on the corresponding tool in Shotwell.
po/POTFILES.in | 1 +
src/Makefile.am | 2 +
src/photos-icons.h | 1 +
src/photos-tool-crop.c | 1136 ++++++++++++++++++++++++++++++++++++++++++++++++
src/photos-tool-crop.h | 45 ++
src/photos-utils.c | 94 ++++-
src/photos-utils.h | 24 +
7 files changed, 1302 insertions(+), 1 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9b33725..cc670c5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -31,6 +31,7 @@ src/photos-search-type-manager.c
src/photos-selection-toolbar.c
src/photos-source-manager.c
src/photos-spinner-box.c
+src/photos-tool-crop.c
src/photos-tool-enhance.c
src/photos-tool-filters.c
src/photos-tracker-controller.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 225f87f..4e08294 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -190,6 +190,8 @@ gnome_photos_SOURCES = \
photos-spinner-box.h \
photos-tool.c \
photos-tool.h \
+ photos-tool-crop.c \
+ photos-tool-crop.h \
photos-tool-enhance.c \
photos-tool-enhance.h \
photos-tool-filters.c \
diff --git a/src/photos-icons.h b/src/photos-icons.h
index 5dc471a..b02a793 100644
--- a/src/photos-icons.h
+++ b/src/photos-icons.h
@@ -40,6 +40,7 @@ G_BEGIN_DECLS
#define PHOTOS_ICON_GO_NEXT_SYMBOLIC "go-next-symbolic"
#define PHOTOS_ICON_GO_PREVIOUS_SYMBOLIC "go-previous-symbolic"
+#define PHOTOS_ICON_IMAGE_CROP_SYMBOLIC "image-crop-symbolic"
#define PHOTOS_ICON_IMAGE_FILTER_SYMBOLIC "image-filter-symbolic"
#define PHOTOS_ICON_IMAGE_SHARPEN_SYMBOLIC "image-sharpen-symbolic"
diff --git a/src/photos-tool-crop.c b/src/photos-tool-crop.c
new file mode 100644
index 0000000..2771104
--- /dev/null
+++ b/src/photos-tool-crop.c
@@ -0,0 +1,1136 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2015 Red Hat, Inc.
+ * Copyright © 2011, 2012, 2013, 2014, 2015 Yorba Foundation
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/* Based on code from:
+ * + Shotwell
+ */
+
+
+#include "config.h"
+
+#include <math.h>
+
+#include <cairo.h>
+#include <gdk/gdk.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gegl-gtk-view.h"
+#include "photos-icons.h"
+#include "photos-tool.h"
+#include "photos-tool-crop.h"
+#include "photos-utils.h"
+
+
+typedef enum
+{
+ PHOTOS_TOOL_CROP_LOCATION_NONE,
+ PHOTOS_TOOL_CROP_LOCATION_BOTTOM_LEFT,
+ PHOTOS_TOOL_CROP_LOCATION_BOTTOM_RIGHT,
+ PHOTOS_TOOL_CROP_LOCATION_BOTTOM_SIDE,
+ PHOTOS_TOOL_CROP_LOCATION_CENTER,
+ PHOTOS_TOOL_CROP_LOCATION_LEFT_SIDE,
+ PHOTOS_TOOL_CROP_LOCATION_RIGHT_SIDE,
+ PHOTOS_TOOL_CROP_LOCATION_TOP_LEFT,
+ PHOTOS_TOOL_CROP_LOCATION_TOP_RIGHT,
+ PHOTOS_TOOL_CROP_LOCATION_TOP_SIDE
+} PhotosToolCropLocation;
+
+struct _PhotosToolCrop
+{
+ PhotosTool parent_instance;
+ GAction *crop;
+ GeglRectangle bbox_scaled;
+ GeglRectangle bbox_source;
+ GtkListStore *model;
+ GtkWidget *combo_box;
+ GtkWidget *grid;
+ GtkWidget *reset_button;
+ GtkWidget *view;
+ PhotosToolCropLocation location;
+ cairo_surface_t *surface;
+ gboolean activated;
+ gboolean grabbed;
+ gboolean reset;
+ gdouble crop_aspect_ratio;
+ gdouble crop_height;
+ gdouble crop_width;
+ gdouble crop_x;
+ gdouble crop_y;
+ gdouble event_x_last;
+ gdouble event_y_last;
+ gulong size_allocate_id;
+};
+
+struct _PhotosToolCropClass
+{
+ PhotosToolClass parent_class;
+};
+
+
+G_DEFINE_TYPE_WITH_CODE (PhotosToolCrop, photos_tool_crop, PHOTOS_TYPE_TOOL,
+ photos_utils_ensure_extension_points ();
+ g_io_extension_point_implement (PHOTOS_TOOL_EXTENSION_POINT_NAME,
+ g_define_type_id,
+ "crop",
+ 100));
+
+
+typedef enum
+{
+ PHOTOS_TOOL_CROP_ASPECT_RATIO_ANY,
+ PHOTOS_TOOL_CROP_ASPECT_RATIO_BASIS,
+ PHOTOS_TOOL_CROP_ASPECT_RATIO_ORIGINAL,
+ PHOTOS_TOOL_CROP_ASPECT_RATIO_SCREEN
+} PhotosToolCropAspectRatioType;
+
+typedef struct _PhotosToolCropConstraint PhotosToolCropConstraint;
+
+struct _PhotosToolCropConstraint
+{
+ PhotosToolCropAspectRatioType aspect_ratio_type;
+ const gchar *name;
+ guint basis_height;
+ guint basis_width;
+};
+
+enum
+{
+ CONSTRAINT_COLUMN_ASPECT_RATIO = 0,
+ CONSTRAINT_COLUMN_NAME = 1,
+ CONSTRAINT_COLUMN_BASIS_HEIGHT = 2,
+ CONSTRAINT_COLUMN_BASIS_WIDTH = 3
+};
+
+static PhotosToolCropConstraint CONSTRAINTS[] =
+{
+ { PHOTOS_TOOL_CROP_ASPECT_RATIO_ANY, N_("Free"), 0, 0 },
+ { PHOTOS_TOOL_CROP_ASPECT_RATIO_ORIGINAL, N_("Original Size"), 0, 0 },
+ { PHOTOS_TOOL_CROP_ASPECT_RATIO_SCREEN, N_("Screen"), 0, 0 },
+ { PHOTOS_TOOL_CROP_ASPECT_RATIO_BASIS, N_("Golden Cut"), 100000, 161803},
+ { PHOTOS_TOOL_CROP_ASPECT_RATIO_BASIS, N_("Square"), 1, 1 },
+ { PHOTOS_TOOL_CROP_ASPECT_RATIO_BASIS, N_("A3 (297 × 420 mm)"), 297, 420 },
+ { PHOTOS_TOOL_CROP_ASPECT_RATIO_BASIS, N_("A4 (210 × 297 mm)"), 297, 210 }
+};
+
+static const gdouble CROP_MIN_SIZE = 16.0;
+static const gdouble HANDLE_OFFSET = 3.0;
+static const gdouble HANDLE_RADIUS = 8.0;
+
+
+static gdouble
+photos_tool_crop_calculate_aspect_ratio (PhotosToolCrop *self, guint constraint)
+{
+ gdouble ret_val = 1.0;
+
+ switch (CONSTRAINTS[constraint].aspect_ratio_type)
+ {
+ case PHOTOS_TOOL_CROP_ASPECT_RATIO_ANY:
+ ret_val = -1.0;
+ break;
+
+ case PHOTOS_TOOL_CROP_ASPECT_RATIO_BASIS:
+ ret_val = (gdouble) CONSTRAINTS[constraint].basis_width / CONSTRAINTS[constraint].basis_height;
+ break;
+
+ case PHOTOS_TOOL_CROP_ASPECT_RATIO_ORIGINAL:
+ ret_val = (gdouble) self->bbox_source.width / self->bbox_source.height;
+ break;
+
+ case PHOTOS_TOOL_CROP_ASPECT_RATIO_SCREEN:
+ {
+ GdkScreen *screen;
+ gint height;
+ gint width;
+
+ screen = gdk_screen_get_default ();
+ height = gdk_screen_get_height (screen);
+ width = gdk_screen_get_width (screen);
+ ret_val = (gdouble) width / height;
+ break;
+ }
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return ret_val;
+}
+
+
+static guint
+photos_tool_crop_find_constraint (PhotosToolCrop *self, gdouble aspect_ratio)
+{
+ guint i;
+ guint ret_val = 0; /* ANY */
+
+ for (i = 0; i < G_N_ELEMENTS (CONSTRAINTS); i++)
+ {
+ gdouble constraint_aspect_ratio;
+
+ constraint_aspect_ratio = photos_tool_crop_calculate_aspect_ratio (self, i);
+ if (photos_utils_equal_double (aspect_ratio, constraint_aspect_ratio))
+ {
+ ret_val = i;
+ break;
+ }
+ }
+
+ return ret_val;
+}
+
+
+static void
+photos_tool_crop_redraw_damaged_area (PhotosToolCrop *self)
+{
+ cairo_rectangle_int_t area;
+ cairo_region_t *region;
+ gdouble damage_offset = HANDLE_OFFSET + HANDLE_RADIUS;
+ gdouble x;
+ gdouble y;
+
+ x = (gdouble) gegl_gtk_view_get_x (GEGL_GTK_VIEW (self->view));
+ x = -x + self->crop_x - damage_offset;
+
+ y = (gdouble) gegl_gtk_view_get_y (GEGL_GTK_VIEW (self->view));
+ y = -y + self->crop_y - damage_offset;
+
+ area.height = (gint) (self->crop_height + 2 * damage_offset + 0.5) + 2;
+ area.width = (gint) (self->crop_width + 2 * damage_offset + 0.5) + 2;
+ area.x = (gint) (x + 0.5) - 1;
+ area.y = (gint) (y + 0.5) - 1;
+
+ region = cairo_region_create_rectangle (&area);
+ gtk_widget_queue_draw_region (self->view, region);
+ cairo_region_destroy (region);
+}
+
+
+static void
+photos_tool_crop_surface_create (PhotosToolCrop *self)
+{
+ GdkWindow *window;
+ gfloat scale;
+
+ g_clear_pointer (&self->surface, (GDestroyNotify) cairo_surface_destroy);
+
+ window = gtk_widget_get_window (self->view);
+ scale = gegl_gtk_view_get_scale (GEGL_GTK_VIEW (self->view));
+ self->bbox_scaled.height = (gint) (scale * self->bbox_source.height + 0.5);
+ self->bbox_scaled.width = (gint) (scale * self->bbox_source.width + 0.5);
+ self->surface = gdk_window_create_similar_surface (window,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ self->bbox_scaled.width,
+ self->bbox_scaled.height);
+}
+
+
+static void
+photos_tool_crop_surface_draw (PhotosToolCrop *self)
+{
+ cairo_t *cr;
+
+ cr = cairo_create (self->surface);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
+ cairo_paint (cr);
+
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
+ cairo_rectangle (cr, self->crop_x, self->crop_y, self->crop_width, self->crop_height);
+ cairo_fill (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ cairo_set_source_rgba (cr, 0.25, 0.507, 0.828, 1.0);
+ photos_utils_draw_rectangle_handles (cr,
+ self->crop_x,
+ self->crop_y,
+ self->crop_width,
+ self->crop_height,
+ HANDLE_OFFSET,
+ HANDLE_RADIUS);
+
+ cairo_set_source_rgba (cr, 0.8, 0.8, 0.8, 1.0);
+ cairo_set_line_width (cr, 0.5);
+ photos_utils_draw_rectangle_thirds (cr, self->crop_x, self->crop_y, self->crop_width, self->crop_height);
+
+ cairo_destroy (cr);
+}
+
+
+static void
+photos_tool_crop_change_constraint (PhotosToolCrop *self)
+{
+ gdouble crop_center_x;
+ gdouble crop_center_y;
+ gdouble old_area;
+
+ photos_tool_crop_redraw_damaged_area (self);
+
+ crop_center_x = self->crop_x + self->crop_width / 2.0;
+ crop_center_y = self->crop_y + self->crop_height / 2.0;
+ old_area = self->crop_height * self->crop_width;
+
+ self->crop_height = sqrt (old_area / self->crop_aspect_ratio);
+ self->crop_width = sqrt (old_area * self->crop_aspect_ratio);
+ self->crop_x = crop_center_x - self->crop_width / 2.0;
+ self->crop_y = crop_center_y - self->crop_height / 2.0;
+
+ photos_tool_crop_surface_draw (self);
+ photos_tool_crop_redraw_damaged_area (self);
+}
+
+
+static void
+photos_tool_crop_init_crop (PhotosToolCrop *self)
+{
+ gdouble aspect_ratio;
+ gint active;
+
+ active = gtk_combo_box_get_active (GTK_COMBO_BOX (self->combo_box));
+ g_return_if_fail (active >= 0);
+
+ self->crop_aspect_ratio = photos_tool_crop_calculate_aspect_ratio (self, (guint) active);
+ if (self->crop_aspect_ratio < 0.0)
+ goto out;
+
+ aspect_ratio = (gdouble) self->bbox_source.width / self->bbox_source.height;
+
+ if (self->crop_aspect_ratio < aspect_ratio)
+ {
+ self->crop_height = 0.7 * self->bbox_scaled.height;
+ self->crop_width = self->crop_height * self->crop_aspect_ratio;
+ }
+ else
+ {
+ self->crop_width = 0.7 * self->bbox_scaled.width;
+ self->crop_height = self->crop_width / self->crop_aspect_ratio;
+ }
+
+ self->crop_x = ((gdouble) self->bbox_scaled.width - self->crop_width) / 2.0;
+ self->crop_y = ((gdouble) self->bbox_scaled.height - self->crop_height) / 2.0;
+
+ out:
+ photos_tool_crop_surface_draw (self);
+}
+
+
+static void
+photos_tool_crop_set_horiz_from_vert (PhotosToolCrop *self, gboolean centered, gboolean move_origin)
+{
+ gdouble crop_width_new;
+
+ crop_width_new = self->crop_height * self->crop_aspect_ratio;
+
+ if (move_origin)
+ {
+ gdouble x_offset;
+
+ if (centered)
+ x_offset = (crop_width_new - self->crop_width) / 2.0;
+ else
+ x_offset = crop_width_new - self->crop_width;
+
+ self->crop_x -= x_offset;
+ }
+
+ self->crop_width = crop_width_new;
+}
+
+
+static void
+photos_tool_crop_set_vert_from_horiz (PhotosToolCrop *self, gboolean centered, gboolean move_origin)
+{
+ gdouble crop_height_new;
+
+ crop_height_new = self->crop_width / self->crop_aspect_ratio;
+
+ if (move_origin)
+ {
+ gdouble y_offset;
+
+ if (centered)
+ y_offset = (crop_height_new - self->crop_height) / 2.0;
+ else
+ y_offset = crop_height_new - self->crop_height;
+
+ self->crop_y -= y_offset;
+ }
+
+ self->crop_height = crop_height_new;
+}
+
+
+static void
+photos_tool_crop_set_crop (PhotosToolCrop *self, gdouble event_x, gdouble event_y)
+{
+ PhotosToolCropLocation location;
+ gboolean centered;
+ gboolean changed = TRUE;
+ gboolean move_origin;
+ gdouble crop_center_x;
+ gdouble crop_center_y;
+ gdouble crop_height_old;
+ gdouble crop_width_old;
+ gdouble crop_x_old;
+ gdouble crop_y_old;
+ gdouble delta_x;
+ gdouble delta_y;
+
+ photos_tool_crop_redraw_damaged_area (self);
+
+ crop_height_old = self->crop_height;
+ crop_width_old = self->crop_width;
+ crop_x_old = self->crop_x;
+ crop_y_old = self->crop_y;
+
+ crop_center_x = self->crop_x + self->crop_width / 2.0;
+ crop_center_y = self->crop_y + self->crop_height / 2.0;
+
+ delta_x = event_x - self->event_x_last;
+ delta_y = event_y - self->event_y_last;
+
+ self->event_x_last = event_x;
+ self->event_y_last = event_y;
+
+ switch (self->location)
+ {
+ case PHOTOS_TOOL_CROP_LOCATION_BOTTOM_SIDE:
+ case PHOTOS_TOOL_CROP_LOCATION_LEFT_SIDE:
+ case PHOTOS_TOOL_CROP_LOCATION_RIGHT_SIDE:
+ case PHOTOS_TOOL_CROP_LOCATION_TOP_SIDE:
+ location = self->location;
+ centered = TRUE;
+ move_origin = TRUE;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_CENTER:
+ location = self->location;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_BOTTOM_LEFT:
+ if (self->crop_aspect_ratio < 0.0)
+ {
+ self->crop_height = event_y - self->crop_y;
+ self->crop_width -= event_x - self->crop_x;
+ self->crop_x = event_x;
+ location = PHOTOS_TOOL_CROP_LOCATION_NONE;
+ }
+ else
+ {
+ if (event_y < photos_utils_eval_radial_line (crop_center_x,
+ crop_center_y,
+ self->crop_x,
+ self->crop_y + self->crop_height,
+ event_x))
+ {
+ location = PHOTOS_TOOL_CROP_LOCATION_LEFT_SIDE;
+ move_origin = FALSE;
+ }
+ else
+ {
+ location = PHOTOS_TOOL_CROP_LOCATION_BOTTOM_SIDE;
+ move_origin = TRUE;
+ }
+
+ centered = FALSE;
+ }
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_BOTTOM_RIGHT:
+ if (self->crop_aspect_ratio < 0.0)
+ {
+ self->crop_height = event_y - self->crop_y;
+ self->crop_width = event_x - self->crop_x;
+ location = PHOTOS_TOOL_CROP_LOCATION_NONE;
+ }
+ else
+ {
+ if (event_y < photos_utils_eval_radial_line (crop_center_x,
+ crop_center_y,
+ self->crop_x + self->crop_width,
+ self->crop_y + self->crop_height,
+ event_x))
+ location = PHOTOS_TOOL_CROP_LOCATION_RIGHT_SIDE;
+ else
+ location = PHOTOS_TOOL_CROP_LOCATION_BOTTOM_SIDE;
+
+ centered = FALSE;
+ move_origin = FALSE;
+ }
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_TOP_LEFT:
+ if (self->crop_aspect_ratio < 0.0)
+ {
+ self->crop_height -= event_y - self->crop_y;
+ self->crop_width -= event_x - self->crop_x;
+ self->crop_x = event_x;
+ self->crop_y = event_y;
+ location = PHOTOS_TOOL_CROP_LOCATION_NONE;
+ }
+ else
+ {
+ if (event_y < photos_utils_eval_radial_line (crop_center_x,
+ crop_center_y,
+ self->crop_x,
+ self->crop_y,
+ event_x))
+ location = PHOTOS_TOOL_CROP_LOCATION_TOP_SIDE;
+ else
+
+ location = PHOTOS_TOOL_CROP_LOCATION_LEFT_SIDE;
+
+ centered = FALSE;
+ move_origin = TRUE;
+ }
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_TOP_RIGHT:
+ if (self->crop_aspect_ratio < 0.0)
+ {
+ self->crop_height -= event_y - self->crop_y;
+ self->crop_width = event_x - self->crop_x;
+ self->crop_y = event_y;
+ location = PHOTOS_TOOL_CROP_LOCATION_NONE;
+ }
+ else
+ {
+ if (event_y < photos_utils_eval_radial_line (crop_center_x,
+ crop_center_y,
+ self->crop_x + self->crop_width,
+ self->crop_y,
+ event_x))
+ {
+ location = PHOTOS_TOOL_CROP_LOCATION_TOP_SIDE;
+ move_origin = FALSE;
+ }
+ else
+ {
+ location = PHOTOS_TOOL_CROP_LOCATION_RIGHT_SIDE;
+ move_origin = TRUE;
+ }
+
+ centered = FALSE;
+ }
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_NONE:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (location == PHOTOS_TOOL_CROP_LOCATION_NONE)
+ goto check_bounds;
+
+ switch (location)
+ {
+ case PHOTOS_TOOL_CROP_LOCATION_BOTTOM_SIDE:
+ self->crop_height = event_y - self->crop_y;
+ if (self->crop_aspect_ratio > 0.0)
+ photos_tool_crop_set_horiz_from_vert (self, centered, move_origin);
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_CENTER:
+ {
+ gboolean x_adj = FALSE;
+ gboolean y_adj = FALSE;
+ gdouble bbox_scaled_height = (gdouble) self->bbox_scaled.height;
+ gdouble bbox_scaled_width = (gdouble) self->bbox_scaled.width;
+
+ self->crop_x += delta_x;
+ self->crop_y += delta_y;
+
+ if (self->crop_x < 0.0)
+ {
+ self->crop_width += self->crop_x;
+ self->crop_x = 0.0;
+ x_adj = TRUE;
+ }
+
+ if (self->crop_y < 0.0)
+ {
+ self->crop_height += self->crop_y;
+ self->crop_y = 0.0;
+ y_adj = TRUE;
+ }
+
+ if (self->crop_x + self->crop_width > bbox_scaled_width)
+ {
+ self->crop_width = bbox_scaled_width - self->crop_x;
+ x_adj = TRUE;
+ }
+
+ if (self->crop_y + self->crop_height > bbox_scaled_height)
+ {
+ self->crop_height = bbox_scaled_height - self->crop_y;
+ y_adj = TRUE;
+ }
+
+ if (x_adj)
+ {
+ if (delta_x < 0.0)
+ self->crop_width = crop_width_old;
+ else
+ {
+ self->crop_x = bbox_scaled_width - crop_width_old;
+ self->crop_width = crop_width_old;
+ }
+ }
+
+ if (y_adj)
+ {
+ if (delta_y < 0.0)
+ self->crop_height = crop_height_old;
+ else
+ {
+ self->crop_y = bbox_scaled_height - crop_height_old;
+ self->crop_height = crop_height_old;
+ }
+ }
+ }
+
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_LEFT_SIDE:
+ self->crop_width -= event_x - self->crop_x;
+ self->crop_x = event_x;
+ if (self->crop_aspect_ratio > 0.0)
+ photos_tool_crop_set_vert_from_horiz (self, centered, move_origin);
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_RIGHT_SIDE:
+ self->crop_width = event_x - self->crop_x;
+ if (self->crop_aspect_ratio > 0.0)
+ photos_tool_crop_set_vert_from_horiz (self, centered, move_origin);
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_TOP_SIDE:
+ self->crop_height -= event_y - self->crop_y;
+ self->crop_y = event_y;
+ if (self->crop_aspect_ratio > 0.0)
+ photos_tool_crop_set_horiz_from_vert (self, centered, move_origin);
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_NONE:
+ case PHOTOS_TOOL_CROP_LOCATION_BOTTOM_LEFT:
+ case PHOTOS_TOOL_CROP_LOCATION_BOTTOM_RIGHT:
+ case PHOTOS_TOOL_CROP_LOCATION_TOP_LEFT:
+ case PHOTOS_TOOL_CROP_LOCATION_TOP_RIGHT:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (location == PHOTOS_TOOL_CROP_LOCATION_CENTER)
+ goto out;
+
+ check_bounds:
+ if (self->crop_height < CROP_MIN_SIZE
+ || self->crop_width < CROP_MIN_SIZE
+ || self->crop_x < 0.0
+ || self->crop_x + self->crop_width > self->bbox_scaled.width
+ || self->crop_y < 0.0
+ || self->crop_y + self->crop_height > self->bbox_scaled.height)
+ {
+ self->crop_height = crop_height_old;
+ self->crop_width = crop_width_old;
+ self->crop_x = crop_x_old;
+ self->crop_y = crop_y_old;
+ changed = FALSE;
+ }
+
+ out:
+ if (changed)
+ {
+ photos_tool_crop_surface_draw (self);
+ photos_tool_crop_redraw_damaged_area (self);
+ }
+}
+
+
+static void
+photos_tool_crop_set_cursor (PhotosToolCrop *self)
+{
+ GdkCursor *cursor = NULL;
+ GdkCursorType cursor_type;
+ GdkDisplay *display;
+ GdkWindow *window;
+
+ window = gtk_widget_get_window (self->view);
+
+ switch (self->location)
+ {
+ case PHOTOS_TOOL_CROP_LOCATION_NONE:
+ goto set_cursor;
+
+ case PHOTOS_TOOL_CROP_LOCATION_BOTTOM_LEFT:
+ cursor_type = GDK_BOTTOM_LEFT_CORNER;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_BOTTOM_RIGHT:
+ cursor_type = GDK_BOTTOM_RIGHT_CORNER;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_BOTTOM_SIDE:
+ cursor_type = GDK_BOTTOM_SIDE;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_CENTER:
+ cursor_type = GDK_FLEUR;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_LEFT_SIDE:
+ cursor_type = GDK_LEFT_SIDE;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_RIGHT_SIDE:
+ cursor_type = GDK_RIGHT_SIDE;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_TOP_LEFT:
+ cursor_type = GDK_TOP_LEFT_CORNER;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_TOP_RIGHT:
+ cursor_type = GDK_TOP_RIGHT_CORNER;
+ break;
+
+ case PHOTOS_TOOL_CROP_LOCATION_TOP_SIDE:
+ cursor_type = GDK_TOP_SIDE;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ display = gdk_window_get_display (window);
+ cursor = gdk_cursor_new_for_display (display, cursor_type);
+
+ set_cursor:
+ gdk_window_set_cursor (window, cursor);
+ g_clear_object (&cursor);
+}
+
+
+static void
+photos_tool_crop_set_location (PhotosToolCrop *self, gdouble event_x, gdouble event_y)
+{
+ const gdouble edge_fuzz = 12.0;
+
+ self->location = PHOTOS_TOOL_CROP_LOCATION_NONE;
+
+ if (event_x > self->crop_x - edge_fuzz
+ && event_y > self->crop_y - edge_fuzz
+ && event_x < self->crop_x + self->crop_width + edge_fuzz
+ && event_y < self->crop_y + self->crop_height + edge_fuzz)
+ {
+ if (event_x < self->crop_x + edge_fuzz && event_y < self->crop_y + edge_fuzz)
+ self->location = PHOTOS_TOOL_CROP_LOCATION_TOP_LEFT;
+ else if (event_x > self->crop_x + self->crop_width - edge_fuzz && event_y < self->crop_y + edge_fuzz)
+ self->location = PHOTOS_TOOL_CROP_LOCATION_TOP_RIGHT;
+ else if (event_x > self->crop_x + self->crop_width - edge_fuzz
+ && event_y > self->crop_y + self->crop_height - edge_fuzz)
+ self->location = PHOTOS_TOOL_CROP_LOCATION_BOTTOM_RIGHT;
+ else if (event_x < self->crop_x + edge_fuzz && event_y > self->crop_y + self->crop_height - edge_fuzz)
+ self->location = PHOTOS_TOOL_CROP_LOCATION_BOTTOM_LEFT;
+ else if (event_y < self->crop_y + edge_fuzz)
+ self->location = PHOTOS_TOOL_CROP_LOCATION_TOP_SIDE;
+ else if (event_x > self->crop_x + self->crop_width - edge_fuzz)
+ self->location = PHOTOS_TOOL_CROP_LOCATION_RIGHT_SIDE;
+ else if (event_y > self->crop_y + self->crop_height - edge_fuzz)
+ self->location = PHOTOS_TOOL_CROP_LOCATION_BOTTOM_SIDE;
+ else if (event_x < self->crop_x + edge_fuzz)
+ self->location = PHOTOS_TOOL_CROP_LOCATION_LEFT_SIDE;
+ else
+ self->location = PHOTOS_TOOL_CROP_LOCATION_CENTER;
+ }
+}
+
+
+static void
+photos_tool_crop_changed (PhotosToolCrop *self)
+{
+ gint active;
+
+ active = gtk_combo_box_get_active (GTK_COMBO_BOX (self->combo_box));
+ g_return_if_fail (active >= 0);
+
+ self->crop_aspect_ratio = photos_tool_crop_calculate_aspect_ratio (self, (guint) active);
+ if (self->crop_aspect_ratio < 0.0)
+ return;
+
+ photos_tool_crop_change_constraint (self);
+}
+
+
+static void
+photos_tool_crop_process (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ PhotosToolCrop *self = PHOTOS_TOOL_CROP (user_data);
+ GError *error = NULL;
+ PhotosBaseItem *item = PHOTOS_BASE_ITEM (source_object);
+ gfloat scale;
+ guint active;
+
+ photos_base_item_process_finish (item, res, &error);
+ if (error != NULL)
+ {
+ g_warning ("Unable to process item: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ scale = gegl_gtk_view_get_scale (GEGL_GTK_VIEW (self->view));
+ self->crop_height *= scale;
+ self->crop_width *= scale;
+ self->crop_x *= scale;
+ self->crop_y *= scale;
+
+ self->crop_aspect_ratio = self->crop_width / self->crop_height;
+ active = photos_tool_crop_find_constraint (self, self->crop_aspect_ratio);
+
+ g_signal_handlers_block_by_func (self->combo_box, photos_tool_crop_changed, self);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (self->combo_box), (gint) active);
+ g_signal_handlers_unblock_by_func (self->combo_box, photos_tool_crop_changed, self);
+
+ photos_tool_crop_surface_draw (self);
+
+ out:
+ gtk_widget_queue_draw (self->view);
+ g_object_unref (self);
+}
+
+
+static void
+photos_tool_crop_reset_clicked (PhotosToolCrop *self)
+{
+ self->reset = TRUE;
+ g_signal_emit_by_name (self, "hide-requested");
+}
+
+
+static void
+photos_tool_crop_size_allocate (PhotosToolCrop *self, GdkRectangle *allocation)
+{
+ gdouble crop_height_ratio;
+ gdouble crop_width_ratio;
+ gdouble crop_x_ratio;
+ gdouble crop_y_ratio;
+
+ crop_height_ratio = self->crop_height / (gdouble) self->bbox_scaled.height;
+ crop_width_ratio = self->crop_width / (gdouble) self->bbox_scaled.width;
+ crop_x_ratio = self->crop_x / (gdouble) self->bbox_scaled.width;
+ crop_y_ratio = self->crop_y / (gdouble) self->bbox_scaled.height;
+
+ photos_tool_crop_surface_create (self);
+
+ self->crop_height = crop_height_ratio * (gdouble) self->bbox_scaled.height;
+ self->crop_width = crop_width_ratio * (gdouble) self->bbox_scaled.width;
+ self->crop_x = crop_x_ratio * (gdouble) self->bbox_scaled.width;
+ self->crop_y = crop_y_ratio * (gdouble) self->bbox_scaled.height;
+
+ photos_tool_crop_surface_draw (self);
+}
+
+
+static void
+photos_tool_crop_activate (PhotosTool *tool, PhotosBaseItem *item, GeglGtkView *view)
+{
+ PhotosToolCrop *self = PHOTOS_TOOL_CROP (tool);
+ gboolean got_bbox_source;
+ gdouble height = -1.0;
+ gdouble width = -1.0;
+ gdouble x = -1.0;
+ gdouble y = -1.0;
+
+ got_bbox_source = photos_base_item_get_bbox_source (item, &self->bbox_source);
+ g_return_if_fail (got_bbox_source);
+
+ self->view = GTK_WIDGET (view);
+ photos_tool_crop_surface_create (self);
+
+ if (photos_base_item_operation_get (item,
+ "gegl:crop",
+ "height", &height,
+ "width", &width,
+ "x", &x,
+ "y", &y,
+ NULL))
+ {
+ g_return_if_fail (height >= 0.0);
+ g_return_if_fail (width >= 0.0);
+ g_return_if_fail (x >= 0.0);
+ g_return_if_fail (y >= 0.0);
+
+ /* These values are invalid until they are multiplied by the
+ * view's scale, which we won't know until we have finished
+ * processing with the reset gegl:crop values.
+ */
+ self->crop_height = height;
+ self->crop_width = width;
+ self->crop_x = x;
+ self->crop_y = y;
+
+ photos_base_item_operation_add (item,
+ "gegl:crop",
+ "height", (gdouble) self->bbox_source.height,
+ "width", (gdouble) self->bbox_source.width,
+ "x", (gdouble) self->bbox_source.x,
+ "y", (gdouble) self->bbox_source.y,
+ NULL);
+ photos_base_item_process_async (item, NULL, photos_tool_crop_process, g_object_ref (self));
+ }
+ else
+ {
+ photos_tool_crop_init_crop (self);
+ }
+
+ self->size_allocate_id = g_signal_connect_object (self->view,
+ "size-allocate",
+ G_CALLBACK (photos_tool_crop_size_allocate),
+ self,
+ G_CONNECT_SWAPPED);
+
+ self->activated = TRUE;
+ self->reset = FALSE;
+}
+
+
+static void
+photos_tool_crop_deactivate (PhotosTool *tool)
+{
+ PhotosToolCrop *self = PHOTOS_TOOL_CROP (tool);
+
+ g_return_if_fail (self->activated);
+
+ if (!self->reset)
+ {
+ GVariantBuilder parameter;
+ GVariantType *parameter_type;
+ gfloat scale;
+
+ scale = gegl_gtk_view_get_scale (GEGL_GTK_VIEW (self->view));
+
+ parameter_type = g_variant_type_new ("a{sd}");
+ g_variant_builder_init (¶meter, parameter_type);
+ g_variant_builder_add (¶meter, "{sd}", "height", self->crop_height / scale);
+ g_variant_builder_add (¶meter, "{sd}", "width", self->crop_width / scale);
+ g_variant_builder_add (¶meter, "{sd}", "x", self->crop_x / scale);
+ g_variant_builder_add (¶meter, "{sd}", "y", self->crop_y / scale);
+ g_action_activate (self->crop, g_variant_builder_end (¶meter));
+
+ g_variant_type_free (parameter_type);
+ }
+
+ if (self->size_allocate_id != 0)
+ {
+ g_signal_handler_disconnect (self->view, self->size_allocate_id);
+ self->size_allocate_id = 0;
+ }
+
+ self->view = NULL;
+}
+
+
+static void
+photos_tool_crop_draw (PhotosTool *tool, cairo_t *cr, GdkRectangle *rect)
+{
+ PhotosToolCrop *self = PHOTOS_TOOL_CROP (tool);
+ gdouble x;
+ gdouble y;
+
+ g_return_if_fail (self->activated);
+ g_return_if_fail (self->view != NULL);
+
+ x = (gdouble) gegl_gtk_view_get_x (GEGL_GTK_VIEW (self->view));
+ x = -x;
+
+ y = (gdouble) gegl_gtk_view_get_y (GEGL_GTK_VIEW (self->view));
+ y = -y;
+
+ cairo_save (cr);
+ cairo_set_source_surface (cr, self->surface, x, y);
+ cairo_paint (cr);
+ cairo_restore (cr);
+}
+
+
+static GtkWidget *
+photos_tool_crop_get_widget (PhotosTool *tool)
+{
+ PhotosToolCrop *self = PHOTOS_TOOL_CROP (tool);
+ return self->grid;
+}
+
+
+static gboolean
+photos_tool_crop_left_click_event (PhotosTool *tool, GdkEventButton *event)
+{
+ PhotosToolCrop *self = PHOTOS_TOOL_CROP (tool);
+ gdouble x;
+ gdouble y;
+
+ g_return_val_if_fail (self->activated, GDK_EVENT_PROPAGATE);
+ g_return_val_if_fail (self->view != NULL, GDK_EVENT_PROPAGATE);
+
+ self->grabbed = TRUE;
+
+ x = (gdouble) gegl_gtk_view_get_x (GEGL_GTK_VIEW (self->view));
+ y = (gdouble) gegl_gtk_view_get_y (GEGL_GTK_VIEW (self->view));
+ self->event_x_last = event->x + x;
+ self->event_y_last = event->y + y;
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+
+static gboolean
+photos_tool_crop_left_unclick_event (PhotosTool *tool, GdkEventButton *event)
+{
+ PhotosToolCrop *self = PHOTOS_TOOL_CROP (tool);
+
+ g_return_val_if_fail (self->activated, GDK_EVENT_PROPAGATE);
+ g_return_val_if_fail (self->view != NULL, GDK_EVENT_PROPAGATE);
+
+ self->grabbed = FALSE;
+ self->event_x_last = -1.0;
+ self->event_y_last = -1.0;
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+
+static gboolean
+photos_tool_crop_motion_event (PhotosTool *tool, GdkEventMotion *event)
+{
+ PhotosToolCrop *self = PHOTOS_TOOL_CROP (tool);
+ gdouble event_x;
+ gdouble event_y;
+ gdouble x;
+ gdouble y;
+
+ g_return_val_if_fail (self->activated, GDK_EVENT_PROPAGATE);
+ g_return_val_if_fail (self->view != NULL, GDK_EVENT_PROPAGATE);
+
+ x = (gdouble) gegl_gtk_view_get_x (GEGL_GTK_VIEW (self->view));
+ y = (gdouble) gegl_gtk_view_get_y (GEGL_GTK_VIEW (self->view));
+ event_x = event->x + x;
+ event_y = event->y + y;
+
+ if (self->grabbed)
+ {
+ if (self->location != PHOTOS_TOOL_CROP_LOCATION_NONE)
+ photos_tool_crop_set_crop (self, event_x, event_y);
+ }
+ else
+ {
+ photos_tool_crop_set_location (self, event_x, event_y);
+ photos_tool_crop_set_cursor (self);
+ }
+
+ return GDK_EVENT_STOP;
+}
+
+
+static void
+photos_tool_crop_dispose (GObject *object)
+{
+ PhotosToolCrop *self = PHOTOS_TOOL_CROP (object);
+
+ g_clear_object (&self->model);
+ g_clear_object (&self->grid);
+ g_clear_pointer (&self->surface, (GDestroyNotify) cairo_surface_destroy);
+
+ G_OBJECT_CLASS (photos_tool_crop_parent_class)->dispose (object);
+}
+
+
+static void
+photos_tool_crop_init (PhotosToolCrop *self)
+{
+ GApplication *app;
+ GtkCellRenderer *renderer;
+ guint i;
+
+ app = g_application_get_default ();
+ self->crop = g_action_map_lookup_action (G_ACTION_MAP (app), "crop-current");
+
+ self->model = gtk_list_store_new (4, G_TYPE_INT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT);
+
+ for (i = 0; i < G_N_ELEMENTS (CONSTRAINTS); i++)
+ {
+ GtkTreeIter iter;
+
+ gtk_list_store_append (self->model, &iter);
+ gtk_list_store_set (self->model,
+ &iter,
+ CONSTRAINT_COLUMN_ASPECT_RATIO, CONSTRAINTS[i].aspect_ratio_type,
+ CONSTRAINT_COLUMN_NAME, CONSTRAINTS[i].name,
+ CONSTRAINT_COLUMN_BASIS_HEIGHT, CONSTRAINTS[i].basis_height,
+ CONSTRAINT_COLUMN_BASIS_WIDTH, CONSTRAINTS[i].basis_width,
+ -1);
+ }
+
+ self->grid = g_object_ref_sink (gtk_grid_new ());
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (self->grid), GTK_ORIENTATION_VERTICAL);
+ gtk_grid_set_row_spacing (GTK_GRID (self->grid), 12);
+
+ self->combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (self->model));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (self->combo_box), 1);
+ gtk_container_add (GTK_CONTAINER (self->grid), self->combo_box);
+ g_signal_connect_swapped (self->combo_box, "changed", G_CALLBACK (photos_tool_crop_changed), self);
+
+ self->reset_button = gtk_button_new_with_label (_("Reset"));
+ gtk_widget_set_halign (self->reset_button, GTK_ALIGN_END);
+ gtk_container_add (GTK_CONTAINER (self->grid), self->reset_button);
+ g_signal_connect_swapped (self->reset_button, "clicked", G_CALLBACK (photos_tool_crop_reset_clicked),
self);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->combo_box), renderer, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self->combo_box), renderer, "text",
CONSTRAINT_COLUMN_NAME);
+
+ self->event_x_last = -1.0;
+ self->event_y_last = -1.0;
+}
+
+
+static void
+photos_tool_crop_class_init (PhotosToolCropClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ PhotosToolClass *tool_class = PHOTOS_TOOL_CLASS (class);
+
+ tool_class->icon_name = PHOTOS_ICON_IMAGE_CROP_SYMBOLIC;
+ tool_class->name = _("Crop");
+
+ object_class->dispose = photos_tool_crop_dispose;
+ tool_class->activate = photos_tool_crop_activate;
+ tool_class->deactivate = photos_tool_crop_deactivate;
+ tool_class->draw = photos_tool_crop_draw;
+ tool_class->get_widget = photos_tool_crop_get_widget;
+ tool_class->left_click_event = photos_tool_crop_left_click_event;
+ tool_class->left_unclick_event = photos_tool_crop_left_unclick_event;
+ tool_class->motion_event = photos_tool_crop_motion_event;
+}
diff --git a/src/photos-tool-crop.h b/src/photos-tool-crop.h
new file mode 100644
index 0000000..bd921d5
--- /dev/null
+++ b/src/photos-tool-crop.h
@@ -0,0 +1,45 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef PHOTOS_TOOL_CROP_H
+#define PHOTOS_TOOL_CROP_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define PHOTOS_TYPE_TOOL_CROP (photos_tool_crop_get_type ())
+
+#define PHOTOS_TOOL_CROP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ PHOTOS_TYPE_TOOL_CROP, PhotosToolCrop))
+
+#define PHOTOS_IS_TOOL_CROP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ PHOTOS_TYPE_TOOL_CROP))
+
+typedef struct _PhotosToolCrop PhotosToolCrop;
+typedef struct _PhotosToolCropClass PhotosToolCropClass;
+
+GType photos_tool_crop_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* PHOTOS_TOOL_CROP_H */
diff --git a/src/photos-utils.c b/src/photos-utils.c
index 57689fd..2cfdec2 100644
--- a/src/photos-utils.c
+++ b/src/photos-utils.c
@@ -1,6 +1,7 @@
/*
* Photos - access, organize and share your photos on GNOME
* Copyright © 2012, 2013, 2014, 2015 Red Hat, Inc.
+ * Copyright © 2009 Yorba Foundation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,6 +22,7 @@
/* Based on code from:
* + Documents
* + Eye of GNOME
+ * + Shotwell
* + Totem
*/
@@ -29,7 +31,6 @@
#include <string.h>
-#include <cairo.h>
#include <glib.h>
#include <libgnome-desktop/gnome-desktop-thumbnail.h>
#include <tracker-sparql.h>
@@ -46,6 +47,7 @@
#include "photos-query.h"
#include "photos-source.h"
#include "photos-tool.h"
+#include "photos-tool-crop.h"
#include "photos-tool-enhance.h"
#include "photos-tool-filters.h"
#include "photos-tracker-collections-controller.h"
@@ -57,6 +59,9 @@
#include "photos-utils.h"
+static const gdouble EPSILON = 1e-5;
+
+
static void
photos_utils_put_pixel (guchar *p)
{
@@ -509,6 +514,65 @@ photos_utils_downscale_pixbuf_for_scale (GdkPixbuf *pixbuf, gint size, gint scal
void
+photos_utils_draw_rectangle_handles (cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ gdouble offset,
+ gdouble radius)
+{
+ cairo_save (cr);
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x - offset, y - offset, radius, 0.0, 2.0 * M_PI);
+ cairo_fill (cr);
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x + width + offset, y - offset, radius, 0.0, 2.0 * M_PI);
+ cairo_fill (cr);
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x + width + offset, y + height + offset, radius, 0.0, 2.0 * M_PI);
+ cairo_fill (cr);
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x - offset, y + height + offset, radius, 0.0, 2.0 * M_PI);
+ cairo_fill (cr);
+
+ cairo_restore (cr);
+}
+
+
+void
+photos_utils_draw_rectangle_thirds (cairo_t *cr, gdouble x, gdouble y, gdouble width, gdouble height)
+{
+ const gdouble one_third_x = width / 3.0;
+ const gdouble one_third_y = height / 3.0;
+
+ cairo_save (cr);
+
+ cairo_move_to (cr, x + one_third_x, y);
+ cairo_line_to (cr, x + one_third_x, y + height);
+ cairo_stroke (cr);
+
+ cairo_move_to (cr, x + 2.0 * one_third_x, y);
+ cairo_line_to (cr, x + 2.0 * one_third_x, y + height);
+ cairo_stroke (cr);
+
+ cairo_move_to (cr, x, y + one_third_y);
+ cairo_line_to (cr, x + width, y + one_third_y);
+ cairo_stroke (cr);
+
+ cairo_move_to (cr, x, y + 2.0 * one_third_y);
+ cairo_line_to (cr, x + width, y + 2.0 * one_third_y);
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+}
+
+
+void
photos_utils_ensure_builtins (void)
{
static gsize once_init_value = 0;
@@ -526,6 +590,7 @@ photos_utils_ensure_builtins (void)
g_type_ensure (PHOTOS_TYPE_OPERATION_INSTA_CURVE);
g_type_ensure (PHOTOS_TYPE_OPERATION_INSTA_FILTER);
+ g_type_ensure (PHOTOS_TYPE_TOOL_CROP);
g_type_ensure (PHOTOS_TYPE_TOOL_ENHANCE);
g_type_ensure (PHOTOS_TYPE_TOOL_FILTERS);
@@ -562,6 +627,14 @@ photos_utils_ensure_extension_points (void)
}
+gboolean
+photos_utils_equal_double (gdouble a, gdouble b)
+{
+ const gdouble diff = a - b;
+ return diff > -EPSILON && diff < EPSILON;
+}
+
+
GQuark
photos_utils_error_quark (void)
{
@@ -569,6 +642,25 @@ photos_utils_error_quark (void)
}
+gdouble
+photos_utils_eval_radial_line (gdouble crop_center_x,
+ gdouble crop_center_y,
+ gdouble corner_x,
+ gdouble corner_y,
+ gdouble event_x)
+{
+ gdouble decision_intercept;
+ gdouble decision_slope;
+ gdouble projected_y;
+
+ decision_slope = (corner_y - crop_center_y) / (corner_x - crop_center_x);
+ decision_intercept = corner_y - (decision_slope * corner_x);
+ projected_y = decision_slope * event_x + decision_intercept;
+
+ return projected_y;
+}
+
+
static gchar *
photos_utils_filename_get_extension_offset (const gchar *filename)
{
diff --git a/src/photos-utils.h b/src/photos-utils.h
index f124ae2..111d376 100644
--- a/src/photos-utils.h
+++ b/src/photos-utils.h
@@ -21,12 +21,14 @@
/* Based on code from:
* + Documents
* + Eye of GNOME
+ * + Shotwell
* + Totem
*/
#ifndef PHOTOS_UTILS_H
#define PHOTOS_UTILS_H
+#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include <gio/gio.h>
@@ -66,12 +68,34 @@ GIcon *photos_utils_get_icon_from_cursor (TrackerSparqlCursor *
GdkPixbuf *photos_utils_downscale_pixbuf_for_scale (GdkPixbuf *pixbuf, gint size, gint scale);
+void photos_utils_draw_rectangle_handles (cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ gdouble offset,
+ gdouble radius);
+
+void photos_utils_draw_rectangle_thirds (cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+
void photos_utils_ensure_builtins (void);
void photos_utils_ensure_extension_points (void);
+gboolean photos_utils_equal_double (gdouble a, gdouble b);
+
GQuark photos_utils_error_quark (void);
+gdouble photos_utils_eval_radial_line (gdouble crop_center_x,
+ gdouble crop_center_y,
+ gdouble corner_x,
+ gdouble corner_y,
+ gdouble event_x);
+
gchar *photos_utils_filename_strip_extension (const gchar *filename_with_extension);
GQuark photos_utils_flash_off_quark (void);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]