2010-10-30 16:14:30 -04:00
|
|
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
|
|
*
|
|
|
|
* Copyright 2009 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
|
2012-09-06 07:43:05 -04:00
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
2010-10-30 16:14:30 -04:00
|
|
|
* (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
|
2014-01-23 12:57:27 +01:00
|
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
2010-10-30 16:14:30 -04:00
|
|
|
*
|
|
|
|
* Written by: Matthias Clasen <mclasen@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
#include <glib/gi18n.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
2014-10-15 13:40:08 +01:00
|
|
|
#include "cc-crop-area.h"
|
2010-10-30 16:14:30 -04:00
|
|
|
|
2014-10-15 13:40:08 +01:00
|
|
|
struct _CcCropAreaPrivate {
|
2010-10-30 16:14:30 -04:00
|
|
|
GdkPixbuf *browse_pixbuf;
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
GdkPixbuf *color_shifted;
|
|
|
|
gdouble scale;
|
|
|
|
GdkRectangle image;
|
|
|
|
GdkCursorType current_cursor;
|
|
|
|
GdkRectangle crop;
|
|
|
|
gint active_region;
|
|
|
|
gint last_press_x;
|
|
|
|
gint last_press_y;
|
|
|
|
gint base_width;
|
|
|
|
gint base_height;
|
|
|
|
gdouble aspect;
|
|
|
|
};
|
|
|
|
|
2014-10-15 13:40:08 +01:00
|
|
|
G_DEFINE_TYPE (CcCropArea, cc_crop_area, GTK_TYPE_DRAWING_AREA);
|
2010-10-30 16:14:30 -04:00
|
|
|
|
|
|
|
static inline guchar
|
|
|
|
shift_color_byte (guchar b,
|
|
|
|
int shift)
|
|
|
|
{
|
|
|
|
return CLAMP(b + shift, 0, 255);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shift_colors (GdkPixbuf *pixbuf,
|
|
|
|
gint red,
|
|
|
|
gint green,
|
|
|
|
gint blue,
|
|
|
|
gint alpha)
|
|
|
|
{
|
|
|
|
gint x, y, offset, y_offset, rowstride, width, height;
|
|
|
|
guchar *pixels;
|
|
|
|
gint channels;
|
|
|
|
|
|
|
|
width = gdk_pixbuf_get_width (pixbuf);
|
|
|
|
height = gdk_pixbuf_get_height (pixbuf);
|
|
|
|
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
|
|
|
|
pixels = gdk_pixbuf_get_pixels (pixbuf);
|
|
|
|
channels = gdk_pixbuf_get_n_channels (pixbuf);
|
|
|
|
|
|
|
|
for (y = 0; y < height; y++) {
|
|
|
|
y_offset = y * rowstride;
|
|
|
|
for (x = 0; x < width; x++) {
|
|
|
|
offset = y_offset + x * channels;
|
|
|
|
if (red != 0)
|
|
|
|
pixels[offset] = shift_color_byte (pixels[offset], red);
|
|
|
|
if (green != 0)
|
|
|
|
pixels[offset + 1] = shift_color_byte (pixels[offset + 1], green);
|
|
|
|
if (blue != 0)
|
|
|
|
pixels[offset + 2] = shift_color_byte (pixels[offset + 2], blue);
|
|
|
|
if (alpha != 0 && channels >= 4)
|
|
|
|
pixels[offset + 3] = shift_color_byte (pixels[offset + 3], blue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-15 13:40:08 +01:00
|
|
|
update_pixbufs (CcCropArea *area)
|
2010-10-30 16:14:30 -04:00
|
|
|
{
|
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
GtkAllocation allocation;
|
|
|
|
gdouble scale;
|
2014-11-28 19:28:30 +01:00
|
|
|
gint dest_width, dest_height;
|
2010-10-30 16:14:30 -04:00
|
|
|
GtkWidget *widget;
|
|
|
|
|
|
|
|
widget = GTK_WIDGET (area);
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
2014-11-28 19:28:30 +01:00
|
|
|
|
|
|
|
width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
|
|
|
|
height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
|
|
|
|
|
|
|
|
scale = allocation.height / (gdouble)height;
|
|
|
|
if (scale * width > allocation.width)
|
|
|
|
scale = allocation.width / (gdouble)width;
|
|
|
|
|
|
|
|
dest_width = width * scale;
|
|
|
|
dest_height = height * scale;
|
2010-10-30 16:14:30 -04:00
|
|
|
|
|
|
|
if (area->priv->pixbuf == NULL ||
|
|
|
|
gdk_pixbuf_get_width (area->priv->pixbuf) != allocation.width ||
|
|
|
|
gdk_pixbuf_get_height (area->priv->pixbuf) != allocation.height) {
|
|
|
|
if (area->priv->pixbuf != NULL)
|
|
|
|
g_object_unref (area->priv->pixbuf);
|
|
|
|
area->priv->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
|
|
|
|
gdk_pixbuf_get_has_alpha (area->priv->browse_pixbuf),
|
|
|
|
8,
|
2014-11-28 19:28:30 +01:00
|
|
|
dest_width, dest_height);
|
|
|
|
gdk_pixbuf_fill (area->priv->pixbuf, 0x0);
|
2010-10-30 16:14:30 -04:00
|
|
|
|
|
|
|
gdk_pixbuf_scale (area->priv->browse_pixbuf,
|
|
|
|
area->priv->pixbuf,
|
2014-11-28 19:28:30 +01:00
|
|
|
0, 0,
|
2010-10-30 16:14:30 -04:00
|
|
|
dest_width, dest_height,
|
2014-11-28 19:28:30 +01:00
|
|
|
0, 0,
|
2010-10-30 16:14:30 -04:00
|
|
|
scale, scale,
|
|
|
|
GDK_INTERP_BILINEAR);
|
|
|
|
|
|
|
|
if (area->priv->color_shifted)
|
|
|
|
g_object_unref (area->priv->color_shifted);
|
|
|
|
area->priv->color_shifted = gdk_pixbuf_copy (area->priv->pixbuf);
|
|
|
|
shift_colors (area->priv->color_shifted, -32, -32, -32, 0);
|
|
|
|
|
|
|
|
if (area->priv->scale == 0.0) {
|
2014-11-10 09:35:21 +01:00
|
|
|
gdouble scale_to_80, scale_to_image, crop_scale;
|
|
|
|
|
|
|
|
/* Scale the crop rectangle to 80% of the area, or less to fit the image */
|
|
|
|
scale_to_80 = MIN ((gdouble)gdk_pixbuf_get_width (area->priv->pixbuf) * 0.8 / area->priv->base_width,
|
|
|
|
(gdouble)gdk_pixbuf_get_height (area->priv->pixbuf) * 0.8 / area->priv->base_height);
|
|
|
|
scale_to_image = MIN ((gdouble)dest_width / area->priv->base_width,
|
|
|
|
(gdouble)dest_height / area->priv->base_height);
|
|
|
|
crop_scale = MIN (scale_to_80, scale_to_image);
|
|
|
|
|
|
|
|
area->priv->crop.width = crop_scale * area->priv->base_width / scale;
|
|
|
|
area->priv->crop.height = crop_scale * area->priv->base_height / scale;
|
2010-10-30 16:14:30 -04:00
|
|
|
area->priv->crop.x = (gdk_pixbuf_get_width (area->priv->browse_pixbuf) - area->priv->crop.width) / 2;
|
|
|
|
area->priv->crop.y = (gdk_pixbuf_get_height (area->priv->browse_pixbuf) - area->priv->crop.height) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
area->priv->scale = scale;
|
2014-11-28 19:28:30 +01:00
|
|
|
area->priv->image.x = (allocation.width - dest_width) / 2;
|
|
|
|
area->priv->image.y = (allocation.height - dest_height) / 2;
|
2010-10-30 16:14:30 -04:00
|
|
|
area->priv->image.width = dest_width;
|
|
|
|
area->priv->image.height = dest_height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-15 13:40:08 +01:00
|
|
|
crop_to_widget (CcCropArea *area,
|
2010-10-30 16:14:30 -04:00
|
|
|
GdkRectangle *crop)
|
|
|
|
{
|
|
|
|
crop->x = area->priv->image.x + area->priv->crop.x * area->priv->scale;
|
|
|
|
crop->y = area->priv->image.y + area->priv->crop.y * area->priv->scale;
|
|
|
|
crop->width = area->priv->crop.width * area->priv->scale;
|
|
|
|
crop->height = area->priv->crop.height * area->priv->scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
OUTSIDE,
|
|
|
|
INSIDE,
|
|
|
|
TOP,
|
|
|
|
TOP_LEFT,
|
|
|
|
TOP_RIGHT,
|
|
|
|
BOTTOM,
|
|
|
|
BOTTOM_LEFT,
|
|
|
|
BOTTOM_RIGHT,
|
|
|
|
LEFT,
|
|
|
|
RIGHT
|
|
|
|
} Location;
|
|
|
|
|
|
|
|
static gboolean
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_draw (GtkWidget *widget,
|
2010-10-30 16:14:30 -04:00
|
|
|
cairo_t *cr)
|
|
|
|
{
|
|
|
|
GdkRectangle crop;
|
2014-11-28 19:28:30 +01:00
|
|
|
gint width, height, ix, iy;
|
2014-10-15 13:40:08 +01:00
|
|
|
CcCropArea *uarea = CC_CROP_AREA (widget);
|
2010-10-30 16:14:30 -04:00
|
|
|
|
|
|
|
if (uarea->priv->browse_pixbuf == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
update_pixbufs (uarea);
|
|
|
|
|
|
|
|
width = gdk_pixbuf_get_width (uarea->priv->pixbuf);
|
|
|
|
height = gdk_pixbuf_get_height (uarea->priv->pixbuf);
|
|
|
|
crop_to_widget (uarea, &crop);
|
|
|
|
|
2014-11-28 19:28:30 +01:00
|
|
|
ix = uarea->priv->image.x;
|
|
|
|
iy = uarea->priv->image.y;
|
|
|
|
|
|
|
|
gdk_cairo_set_source_pixbuf (cr, uarea->priv->color_shifted, ix, iy);
|
|
|
|
cairo_rectangle (cr, ix, iy, width, crop.y - iy);
|
|
|
|
cairo_rectangle (cr, ix, crop.y, crop.x - ix, crop.height);
|
|
|
|
cairo_rectangle (cr, crop.x + crop.width, crop.y, width - crop.width - (crop.x - ix), crop.height);
|
|
|
|
cairo_rectangle (cr, ix, crop.y + crop.height, width, height - crop.height - (crop.y - iy));
|
2010-10-30 16:14:30 -04:00
|
|
|
cairo_fill (cr);
|
|
|
|
|
2014-11-28 19:28:30 +01:00
|
|
|
gdk_cairo_set_source_pixbuf (cr, uarea->priv->pixbuf, ix, iy);
|
2010-10-30 16:14:30 -04:00
|
|
|
cairo_rectangle (cr, crop.x, crop.y, crop.width, crop.height);
|
|
|
|
cairo_fill (cr);
|
|
|
|
|
|
|
|
if (uarea->priv->active_region != OUTSIDE) {
|
|
|
|
gint x1, x2, y1, y2;
|
|
|
|
cairo_set_source_rgb (cr, 1, 1, 1);
|
|
|
|
cairo_set_line_width (cr, 1.0);
|
|
|
|
x1 = crop.x + crop.width / 3.0;
|
|
|
|
x2 = crop.x + 2 * crop.width / 3.0;
|
|
|
|
y1 = crop.y + crop.height / 3.0;
|
|
|
|
y2 = crop.y + 2 * crop.height / 3.0;
|
|
|
|
|
|
|
|
cairo_move_to (cr, x1 + 0.5, crop.y);
|
|
|
|
cairo_line_to (cr, x1 + 0.5, crop.y + crop.height);
|
|
|
|
|
|
|
|
cairo_move_to (cr, x2 + 0.5, crop.y);
|
|
|
|
cairo_line_to (cr, x2 + 0.5, crop.y + crop.height);
|
|
|
|
|
|
|
|
cairo_move_to (cr, crop.x, y1 + 0.5);
|
|
|
|
cairo_line_to (cr, crop.x + crop.width, y1 + 0.5);
|
|
|
|
|
|
|
|
cairo_move_to (cr, crop.x, y2 + 0.5);
|
|
|
|
cairo_line_to (cr, crop.x + crop.width, y2 + 0.5);
|
|
|
|
cairo_stroke (cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
|
|
cairo_set_line_width (cr, 1.0);
|
|
|
|
cairo_rectangle (cr,
|
|
|
|
crop.x + 0.5,
|
|
|
|
crop.y + 0.5,
|
|
|
|
crop.width - 1.0,
|
|
|
|
crop.height - 1.0);
|
|
|
|
cairo_stroke (cr);
|
|
|
|
|
|
|
|
cairo_set_source_rgb (cr, 1, 1, 1);
|
|
|
|
cairo_set_line_width (cr, 2.0);
|
|
|
|
cairo_rectangle (cr,
|
|
|
|
crop.x + 2.0,
|
|
|
|
crop.y + 2.0,
|
|
|
|
crop.width - 4.0,
|
|
|
|
crop.height - 4.0);
|
|
|
|
cairo_stroke (cr);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
BELOW,
|
|
|
|
LOWER,
|
|
|
|
BETWEEN,
|
|
|
|
UPPER,
|
|
|
|
ABOVE
|
|
|
|
} Range;
|
|
|
|
|
|
|
|
static Range
|
|
|
|
find_range (gint x,
|
|
|
|
gint min,
|
|
|
|
gint max)
|
|
|
|
{
|
|
|
|
gint tolerance = 12;
|
|
|
|
|
|
|
|
if (x < min - tolerance)
|
|
|
|
return BELOW;
|
|
|
|
if (x <= min + tolerance)
|
|
|
|
return LOWER;
|
|
|
|
if (x < max - tolerance)
|
|
|
|
return BETWEEN;
|
|
|
|
if (x <= max + tolerance)
|
|
|
|
return UPPER;
|
|
|
|
return ABOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Location
|
|
|
|
find_location (GdkRectangle *rect,
|
|
|
|
gint x,
|
|
|
|
gint y)
|
|
|
|
{
|
|
|
|
Range x_range, y_range;
|
|
|
|
Location location[5][5] = {
|
|
|
|
{ OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE },
|
|
|
|
{ OUTSIDE, TOP_LEFT, TOP, TOP_RIGHT, OUTSIDE },
|
|
|
|
{ OUTSIDE, LEFT, INSIDE, RIGHT, OUTSIDE },
|
|
|
|
{ OUTSIDE, BOTTOM_LEFT, BOTTOM, BOTTOM_RIGHT, OUTSIDE },
|
|
|
|
{ OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE }
|
|
|
|
};
|
|
|
|
|
|
|
|
x_range = find_range (x, rect->x, rect->x + rect->width);
|
|
|
|
y_range = find_range (y, rect->y, rect->y + rect->height);
|
|
|
|
|
|
|
|
return location[y_range][x_range];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-15 13:40:08 +01:00
|
|
|
update_cursor (CcCropArea *area,
|
2010-10-30 16:14:30 -04:00
|
|
|
gint x,
|
|
|
|
gint y)
|
|
|
|
{
|
|
|
|
gint cursor_type;
|
|
|
|
GdkRectangle crop;
|
|
|
|
gint region;
|
|
|
|
|
|
|
|
region = area->priv->active_region;
|
|
|
|
if (region == OUTSIDE) {
|
|
|
|
crop_to_widget (area, &crop);
|
|
|
|
region = find_location (&crop, x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (region) {
|
|
|
|
case OUTSIDE:
|
|
|
|
cursor_type = GDK_LEFT_PTR;
|
|
|
|
break;
|
|
|
|
case TOP_LEFT:
|
|
|
|
cursor_type = GDK_TOP_LEFT_CORNER;
|
|
|
|
break;
|
|
|
|
case TOP:
|
|
|
|
cursor_type = GDK_TOP_SIDE;
|
|
|
|
break;
|
|
|
|
case TOP_RIGHT:
|
|
|
|
cursor_type = GDK_TOP_RIGHT_CORNER;
|
|
|
|
break;
|
|
|
|
case LEFT:
|
|
|
|
cursor_type = GDK_LEFT_SIDE;
|
|
|
|
break;
|
|
|
|
case INSIDE:
|
|
|
|
cursor_type = GDK_FLEUR;
|
|
|
|
break;
|
|
|
|
case RIGHT:
|
|
|
|
cursor_type = GDK_RIGHT_SIDE;
|
|
|
|
break;
|
|
|
|
case BOTTOM_LEFT:
|
|
|
|
cursor_type = GDK_BOTTOM_LEFT_CORNER;
|
|
|
|
break;
|
|
|
|
case BOTTOM:
|
|
|
|
cursor_type = GDK_BOTTOM_SIDE;
|
|
|
|
break;
|
|
|
|
case BOTTOM_RIGHT:
|
|
|
|
cursor_type = GDK_BOTTOM_RIGHT_CORNER;
|
|
|
|
break;
|
2011-02-01 01:08:55 +00:00
|
|
|
default:
|
|
|
|
g_assert_not_reached ();
|
2010-10-30 16:14:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cursor_type != area->priv->current_cursor) {
|
2014-11-28 19:41:11 +01:00
|
|
|
GdkCursor *cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (area)),
|
|
|
|
cursor_type);
|
2010-10-30 16:14:30 -04:00
|
|
|
gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (area)), cursor);
|
2011-01-25 00:32:10 -05:00
|
|
|
g_object_unref (cursor);
|
2010-10-30 16:14:30 -04:00
|
|
|
area->priv->current_cursor = cursor_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
eval_radial_line (gdouble center_x, gdouble center_y,
|
|
|
|
gdouble bounds_x, gdouble bounds_y,
|
|
|
|
gdouble user_x)
|
|
|
|
{
|
|
|
|
gdouble decision_slope;
|
|
|
|
gdouble decision_intercept;
|
|
|
|
|
|
|
|
decision_slope = (bounds_y - center_y) / (bounds_x - center_x);
|
2011-02-24 19:54:08 +01:00
|
|
|
decision_intercept = -(decision_slope * bounds_x);
|
2010-10-30 16:14:30 -04:00
|
|
|
|
|
|
|
return (int) (decision_slope * user_x + decision_intercept);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_motion_notify_event (GtkWidget *widget,
|
2010-10-30 16:14:30 -04:00
|
|
|
GdkEventMotion *event)
|
|
|
|
{
|
2014-10-15 13:40:08 +01:00
|
|
|
CcCropArea *area = CC_CROP_AREA (widget);
|
2010-10-30 16:14:30 -04:00
|
|
|
gint x, y;
|
|
|
|
gint delta_x, delta_y;
|
|
|
|
gint width, height;
|
|
|
|
gint adj_width, adj_height;
|
|
|
|
gint pb_width, pb_height;
|
|
|
|
GdkRectangle damage;
|
|
|
|
gint left, right, top, bottom;
|
|
|
|
gdouble new_width, new_height;
|
|
|
|
gdouble center_x, center_y;
|
|
|
|
gint min_width, min_height;
|
|
|
|
|
|
|
|
if (area->priv->browse_pixbuf == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
update_cursor (area, event->x, event->y);
|
|
|
|
|
|
|
|
crop_to_widget (area, &damage);
|
|
|
|
gtk_widget_queue_draw_area (widget,
|
|
|
|
damage.x - 1, damage.y - 1,
|
|
|
|
damage.width + 2, damage.height + 2);
|
|
|
|
|
|
|
|
pb_width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
|
|
|
|
pb_height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
|
|
|
|
|
|
|
|
x = (event->x - area->priv->image.x) / area->priv->scale;
|
|
|
|
y = (event->y - area->priv->image.y) / area->priv->scale;
|
|
|
|
|
|
|
|
delta_x = x - area->priv->last_press_x;
|
|
|
|
delta_y = y - area->priv->last_press_y;
|
|
|
|
area->priv->last_press_x = x;
|
|
|
|
area->priv->last_press_y = y;
|
|
|
|
|
|
|
|
left = area->priv->crop.x;
|
|
|
|
right = area->priv->crop.x + area->priv->crop.width - 1;
|
|
|
|
top = area->priv->crop.y;
|
|
|
|
bottom = area->priv->crop.y + area->priv->crop.height - 1;
|
|
|
|
|
|
|
|
center_x = (left + right) / 2.0;
|
|
|
|
center_y = (top + bottom) / 2.0;
|
|
|
|
|
|
|
|
switch (area->priv->active_region) {
|
|
|
|
case INSIDE:
|
|
|
|
width = right - left + 1;
|
|
|
|
height = bottom - top + 1;
|
|
|
|
|
|
|
|
left += delta_x;
|
|
|
|
right += delta_x;
|
|
|
|
top += delta_y;
|
|
|
|
bottom += delta_y;
|
|
|
|
|
|
|
|
if (left < 0)
|
|
|
|
left = 0;
|
|
|
|
if (top < 0)
|
|
|
|
top = 0;
|
|
|
|
if (right > pb_width)
|
|
|
|
right = pb_width;
|
|
|
|
if (bottom > pb_height)
|
|
|
|
bottom = pb_height;
|
|
|
|
|
|
|
|
adj_width = right - left + 1;
|
|
|
|
adj_height = bottom - top + 1;
|
|
|
|
if (adj_width != width) {
|
|
|
|
if (delta_x < 0)
|
|
|
|
right = left + width - 1;
|
|
|
|
else
|
|
|
|
left = right - width + 1;
|
|
|
|
}
|
|
|
|
if (adj_height != height) {
|
|
|
|
if (delta_y < 0)
|
|
|
|
bottom = top + height - 1;
|
|
|
|
else
|
|
|
|
top = bottom - height + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOP_LEFT:
|
|
|
|
if (area->priv->aspect < 0) {
|
|
|
|
top = y;
|
|
|
|
left = x;
|
|
|
|
}
|
|
|
|
else if (y < eval_radial_line (center_x, center_y, left, top, x)) {
|
|
|
|
top = y;
|
|
|
|
new_width = (bottom - top) * area->priv->aspect;
|
|
|
|
left = right - new_width;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
left = x;
|
|
|
|
new_height = (right - left) / area->priv->aspect;
|
|
|
|
top = bottom - new_height;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOP:
|
|
|
|
top = y;
|
|
|
|
if (area->priv->aspect > 0) {
|
|
|
|
new_width = (bottom - top) * area->priv->aspect;
|
|
|
|
right = left + new_width;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOP_RIGHT:
|
|
|
|
if (area->priv->aspect < 0) {
|
|
|
|
top = y;
|
|
|
|
right = x;
|
|
|
|
}
|
|
|
|
else if (y < eval_radial_line (center_x, center_y, right, top, x)) {
|
|
|
|
top = y;
|
|
|
|
new_width = (bottom - top) * area->priv->aspect;
|
|
|
|
right = left + new_width;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
right = x;
|
|
|
|
new_height = (right - left) / area->priv->aspect;
|
|
|
|
top = bottom - new_height;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LEFT:
|
|
|
|
left = x;
|
|
|
|
if (area->priv->aspect > 0) {
|
|
|
|
new_height = (right - left) / area->priv->aspect;
|
|
|
|
bottom = top + new_height;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BOTTOM_LEFT:
|
|
|
|
if (area->priv->aspect < 0) {
|
|
|
|
bottom = y;
|
|
|
|
left = x;
|
|
|
|
}
|
|
|
|
else if (y < eval_radial_line (center_x, center_y, left, bottom, x)) {
|
|
|
|
left = x;
|
|
|
|
new_height = (right - left) / area->priv->aspect;
|
|
|
|
bottom = top + new_height;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bottom = y;
|
|
|
|
new_width = (bottom - top) * area->priv->aspect;
|
|
|
|
left = right - new_width;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RIGHT:
|
|
|
|
right = x;
|
|
|
|
if (area->priv->aspect > 0) {
|
|
|
|
new_height = (right - left) / area->priv->aspect;
|
|
|
|
bottom = top + new_height;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BOTTOM_RIGHT:
|
|
|
|
if (area->priv->aspect < 0) {
|
|
|
|
bottom = y;
|
|
|
|
right = x;
|
|
|
|
}
|
|
|
|
else if (y < eval_radial_line (center_x, center_y, right, bottom, x)) {
|
|
|
|
right = x;
|
|
|
|
new_height = (right - left) / area->priv->aspect;
|
|
|
|
bottom = top + new_height;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bottom = y;
|
|
|
|
new_width = (bottom - top) * area->priv->aspect;
|
|
|
|
right = left + new_width;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BOTTOM:
|
|
|
|
bottom = y;
|
|
|
|
if (area->priv->aspect > 0) {
|
|
|
|
new_width = (bottom - top) * area->priv->aspect;
|
|
|
|
right= left + new_width;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
min_width = area->priv->base_width / area->priv->scale;
|
|
|
|
min_height = area->priv->base_height / area->priv->scale;
|
|
|
|
|
|
|
|
width = right - left + 1;
|
|
|
|
height = bottom - top + 1;
|
|
|
|
if (area->priv->aspect < 0) {
|
|
|
|
if (left < 0)
|
|
|
|
left = 0;
|
|
|
|
if (top < 0)
|
|
|
|
top = 0;
|
|
|
|
if (right > pb_width)
|
|
|
|
right = pb_width;
|
|
|
|
if (bottom > pb_height)
|
|
|
|
bottom = pb_height;
|
|
|
|
|
|
|
|
width = right - left + 1;
|
|
|
|
height = bottom - top + 1;
|
|
|
|
|
|
|
|
switch (area->priv->active_region) {
|
|
|
|
case LEFT:
|
|
|
|
case TOP_LEFT:
|
|
|
|
case BOTTOM_LEFT:
|
|
|
|
if (width < min_width)
|
|
|
|
left = right - min_width;
|
|
|
|
break;
|
|
|
|
case RIGHT:
|
|
|
|
case TOP_RIGHT:
|
|
|
|
case BOTTOM_RIGHT:
|
|
|
|
if (width < min_width)
|
|
|
|
right = left + min_width;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: ;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (area->priv->active_region) {
|
|
|
|
case TOP:
|
|
|
|
case TOP_LEFT:
|
|
|
|
case TOP_RIGHT:
|
|
|
|
if (height < min_height)
|
|
|
|
top = bottom - min_height;
|
|
|
|
break;
|
|
|
|
case BOTTOM:
|
|
|
|
case BOTTOM_LEFT:
|
|
|
|
case BOTTOM_RIGHT:
|
|
|
|
if (height < min_height)
|
|
|
|
bottom = top + min_height;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (left < 0 || top < 0 ||
|
|
|
|
right > pb_width || bottom > pb_height ||
|
|
|
|
width < min_width || height < min_height) {
|
|
|
|
left = area->priv->crop.x;
|
|
|
|
right = area->priv->crop.x + area->priv->crop.width - 1;
|
|
|
|
top = area->priv->crop.y;
|
|
|
|
bottom = area->priv->crop.y + area->priv->crop.height - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
area->priv->crop.x = left;
|
|
|
|
area->priv->crop.y = top;
|
|
|
|
area->priv->crop.width = right - left + 1;
|
|
|
|
area->priv->crop.height = bottom - top + 1;
|
|
|
|
|
|
|
|
crop_to_widget (area, &damage);
|
|
|
|
gtk_widget_queue_draw_area (widget,
|
|
|
|
damage.x - 1, damage.y - 1,
|
|
|
|
damage.width + 2, damage.height + 2);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_button_press_event (GtkWidget *widget,
|
2010-10-30 16:14:30 -04:00
|
|
|
GdkEventButton *event)
|
|
|
|
{
|
2014-10-15 13:40:08 +01:00
|
|
|
CcCropArea *area = CC_CROP_AREA (widget);
|
2010-10-30 16:14:30 -04:00
|
|
|
GdkRectangle crop;
|
|
|
|
|
|
|
|
if (area->priv->browse_pixbuf == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
crop_to_widget (area, &crop);
|
|
|
|
|
|
|
|
area->priv->last_press_x = (event->x - area->priv->image.x) / area->priv->scale;
|
|
|
|
area->priv->last_press_y = (event->y - area->priv->image.y) / area->priv->scale;
|
|
|
|
area->priv->active_region = find_location (&crop, event->x, event->y);
|
|
|
|
|
|
|
|
gtk_widget_queue_draw_area (widget,
|
|
|
|
crop.x - 1, crop.y - 1,
|
|
|
|
crop.width + 2, crop.height + 2);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_button_release_event (GtkWidget *widget,
|
2010-10-30 16:14:30 -04:00
|
|
|
GdkEventButton *event)
|
|
|
|
{
|
2014-10-15 13:40:08 +01:00
|
|
|
CcCropArea *area = CC_CROP_AREA (widget);
|
2010-10-30 16:14:30 -04:00
|
|
|
GdkRectangle crop;
|
|
|
|
|
|
|
|
if (area->priv->browse_pixbuf == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
crop_to_widget (area, &crop);
|
|
|
|
|
|
|
|
area->priv->last_press_x = -1;
|
|
|
|
area->priv->last_press_y = -1;
|
|
|
|
area->priv->active_region = OUTSIDE;
|
|
|
|
|
|
|
|
gtk_widget_queue_draw_area (widget,
|
|
|
|
crop.x - 1, crop.y - 1,
|
|
|
|
crop.width + 2, crop.height + 2);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-11-28 19:38:32 +01:00
|
|
|
static void
|
|
|
|
cc_crop_area_set_size_request (CcCropArea *area)
|
|
|
|
{
|
|
|
|
gtk_widget_set_size_request (GTK_WIDGET (area),
|
|
|
|
area->priv->base_width,
|
|
|
|
area->priv->base_height);
|
|
|
|
}
|
|
|
|
|
2010-10-30 16:14:30 -04:00
|
|
|
static void
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_finalize (GObject *object)
|
2010-10-30 16:14:30 -04:00
|
|
|
{
|
2014-10-15 13:40:08 +01:00
|
|
|
CcCropArea *area = CC_CROP_AREA (object);
|
2010-10-30 16:14:30 -04:00
|
|
|
|
|
|
|
if (area->priv->browse_pixbuf) {
|
|
|
|
g_object_unref (area->priv->browse_pixbuf);
|
|
|
|
area->priv->browse_pixbuf = NULL;
|
|
|
|
}
|
|
|
|
if (area->priv->pixbuf) {
|
|
|
|
g_object_unref (area->priv->pixbuf);
|
|
|
|
area->priv->pixbuf = NULL;
|
|
|
|
}
|
|
|
|
if (area->priv->color_shifted) {
|
|
|
|
g_object_unref (area->priv->color_shifted);
|
|
|
|
area->priv->color_shifted = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_class_init (CcCropAreaClass *klass)
|
2010-10-30 16:14:30 -04:00
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
|
2014-10-15 13:40:08 +01:00
|
|
|
object_class->finalize = cc_crop_area_finalize;
|
|
|
|
widget_class->draw = cc_crop_area_draw;
|
|
|
|
widget_class->button_press_event = cc_crop_area_button_press_event;
|
|
|
|
widget_class->button_release_event = cc_crop_area_button_release_event;
|
|
|
|
widget_class->motion_notify_event = cc_crop_area_motion_notify_event;
|
2010-10-30 16:14:30 -04:00
|
|
|
|
2014-10-15 13:40:08 +01:00
|
|
|
g_type_class_add_private (klass, sizeof (CcCropAreaPrivate));
|
2010-10-30 16:14:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_init (CcCropArea *area)
|
2010-10-30 16:14:30 -04:00
|
|
|
{
|
2014-10-15 13:40:08 +01:00
|
|
|
area->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((area), CC_TYPE_CROP_AREA,
|
|
|
|
CcCropAreaPrivate));
|
2010-10-30 16:14:30 -04:00
|
|
|
|
|
|
|
gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_MASK |
|
|
|
|
GDK_BUTTON_PRESS_MASK |
|
|
|
|
GDK_BUTTON_RELEASE_MASK);
|
|
|
|
|
|
|
|
area->priv->scale = 0.0;
|
|
|
|
area->priv->image.x = 0;
|
|
|
|
area->priv->image.y = 0;
|
|
|
|
area->priv->image.width = 0;
|
|
|
|
area->priv->image.height = 0;
|
|
|
|
area->priv->active_region = OUTSIDE;
|
|
|
|
area->priv->base_width = 48;
|
|
|
|
area->priv->base_height = 48;
|
|
|
|
area->priv->aspect = 1;
|
2014-11-28 19:38:32 +01:00
|
|
|
|
|
|
|
cc_crop_area_set_size_request (area);
|
2010-10-30 16:14:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
GtkWidget *
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_new (void)
|
2010-10-30 16:14:30 -04:00
|
|
|
{
|
2014-10-15 13:40:08 +01:00
|
|
|
return g_object_new (CC_TYPE_CROP_AREA, NULL);
|
2010-10-30 16:14:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
GdkPixbuf *
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_get_picture (CcCropArea *area)
|
2010-10-30 16:14:30 -04:00
|
|
|
{
|
|
|
|
gint width, height;
|
|
|
|
|
|
|
|
width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
|
|
|
|
height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
|
|
|
|
width = MIN (area->priv->crop.width, width - area->priv->crop.x);
|
|
|
|
height = MIN (area->priv->crop.height, height - area->priv->crop.y);
|
|
|
|
|
|
|
|
return gdk_pixbuf_new_subpixbuf (area->priv->browse_pixbuf,
|
|
|
|
area->priv->crop.x,
|
|
|
|
area->priv->crop.y,
|
|
|
|
width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_set_picture (CcCropArea *area,
|
2010-10-30 16:14:30 -04:00
|
|
|
GdkPixbuf *pixbuf)
|
|
|
|
{
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
|
|
|
|
if (area->priv->browse_pixbuf) {
|
|
|
|
g_object_unref (area->priv->browse_pixbuf);
|
|
|
|
area->priv->browse_pixbuf = NULL;
|
|
|
|
}
|
|
|
|
if (pixbuf) {
|
|
|
|
area->priv->browse_pixbuf = g_object_ref (pixbuf);
|
|
|
|
width = gdk_pixbuf_get_width (pixbuf);
|
|
|
|
height = gdk_pixbuf_get_height (pixbuf);
|
|
|
|
} else {
|
|
|
|
width = 0;
|
|
|
|
height = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
area->priv->crop.width = 2 * area->priv->base_width;
|
|
|
|
area->priv->crop.height = 2 * area->priv->base_height;
|
|
|
|
area->priv->crop.x = (width - area->priv->crop.width) / 2;
|
|
|
|
area->priv->crop.y = (height - area->priv->crop.height) / 2;
|
|
|
|
|
|
|
|
area->priv->scale = 0.0;
|
|
|
|
area->priv->image.x = 0;
|
|
|
|
area->priv->image.y = 0;
|
|
|
|
area->priv->image.width = 0;
|
|
|
|
area->priv->image.height = 0;
|
|
|
|
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (area));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_set_min_size (CcCropArea *area,
|
2010-10-30 16:14:30 -04:00
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
area->priv->base_width = width;
|
|
|
|
area->priv->base_height = height;
|
|
|
|
|
2014-11-28 19:38:32 +01:00
|
|
|
cc_crop_area_set_size_request (area);
|
|
|
|
|
2010-10-30 16:14:30 -04:00
|
|
|
if (area->priv->aspect > 0) {
|
|
|
|
area->priv->aspect = area->priv->base_width / (gdouble)area->priv->base_height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-10-15 13:40:08 +01:00
|
|
|
cc_crop_area_set_constrain_aspect (CcCropArea *area,
|
2010-10-30 16:14:30 -04:00
|
|
|
gboolean constrain)
|
|
|
|
{
|
|
|
|
if (constrain) {
|
|
|
|
area->priv->aspect = area->priv->base_width / (gdouble)area->priv->base_height;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
area->priv->aspect = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|