blob: 234703e493d103f2ff187a16981e78690799b757 [file] [log] [blame]
Stepan Salenikovich5c54b352016-09-14 14:28:19 -04001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright 2009 Red Hat, Inc,
aviau4c87b832016-12-13 14:18:53 -05004 * Copyright (C) 2017 Savoir-faire Linux Inc.
Stepan Salenikovich5c54b352016-09-14 14:28:19 -04005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Written by: Matthias Clasen <mclasen@redhat.com>
20 */
21
22#include "config.h"
23
24#include <stdlib.h>
25
26#include <glib.h>
27#include <glib/gi18n.h>
28#include <gtk/gtk.h>
29
30#include "cc-crop-area.h"
31
32struct _CcCropAreaPrivate {
33 GdkPixbuf *browse_pixbuf;
34 GdkPixbuf *pixbuf;
35 GdkPixbuf *color_shifted;
36 gdouble scale;
37 GdkRectangle image;
38 GdkCursorType current_cursor;
39 GdkRectangle crop;
40 gint active_region;
41 gint last_press_x;
42 gint last_press_y;
43 gint base_width;
44 gint base_height;
45 gdouble aspect;
46};
47
48G_DEFINE_TYPE (CcCropArea, cc_crop_area, GTK_TYPE_DRAWING_AREA);
49
50static inline guchar
51shift_color_byte (guchar b,
52 int shift)
53{
54 return CLAMP(b + shift, 0, 255);
55}
56
57static void
58shift_colors (GdkPixbuf *pixbuf,
59 gint red,
60 gint green,
61 gint blue,
62 gint alpha)
63{
64 gint x, y, offset, y_offset, rowstride, width, height;
65 guchar *pixels;
66 gint channels;
67
68 width = gdk_pixbuf_get_width (pixbuf);
69 height = gdk_pixbuf_get_height (pixbuf);
70 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
71 pixels = gdk_pixbuf_get_pixels (pixbuf);
72 channels = gdk_pixbuf_get_n_channels (pixbuf);
73
74 for (y = 0; y < height; y++) {
75 y_offset = y * rowstride;
76 for (x = 0; x < width; x++) {
77 offset = y_offset + x * channels;
78 if (red != 0)
79 pixels[offset] = shift_color_byte (pixels[offset], red);
80 if (green != 0)
81 pixels[offset + 1] = shift_color_byte (pixels[offset + 1], green);
82 if (blue != 0)
83 pixels[offset + 2] = shift_color_byte (pixels[offset + 2], blue);
84 if (alpha != 0 && channels >= 4)
85 pixels[offset + 3] = shift_color_byte (pixels[offset + 3], blue);
86 }
87 }
88}
89
90static void
91update_pixbufs (CcCropArea *area)
92{
93 gint width;
94 gint height;
95 GtkAllocation allocation;
96 gdouble scale;
97 gint dest_width, dest_height;
98 GtkWidget *widget;
99
100 widget = GTK_WIDGET (area);
101 gtk_widget_get_allocation (widget, &allocation);
102
103 width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
104 height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
105
106 scale = allocation.height / (gdouble)height;
107 if (scale * width > allocation.width)
108 scale = allocation.width / (gdouble)width;
109
110 dest_width = width * scale;
111 dest_height = height * scale;
112
113 if (area->priv->pixbuf == NULL ||
114 gdk_pixbuf_get_width (area->priv->pixbuf) != allocation.width ||
115 gdk_pixbuf_get_height (area->priv->pixbuf) != allocation.height) {
116 if (area->priv->pixbuf != NULL)
117 g_object_unref (area->priv->pixbuf);
118 area->priv->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
119 gdk_pixbuf_get_has_alpha (area->priv->browse_pixbuf),
120 8,
121 dest_width, dest_height);
122 gdk_pixbuf_fill (area->priv->pixbuf, 0x0);
123
124 gdk_pixbuf_scale (area->priv->browse_pixbuf,
125 area->priv->pixbuf,
126 0, 0,
127 dest_width, dest_height,
128 0, 0,
129 scale, scale,
130 GDK_INTERP_BILINEAR);
131
132 if (area->priv->color_shifted)
133 g_object_unref (area->priv->color_shifted);
134 area->priv->color_shifted = gdk_pixbuf_copy (area->priv->pixbuf);
135 shift_colors (area->priv->color_shifted, -32, -32, -32, 0);
136
137 if (area->priv->scale == 0.0) {
138 gdouble scale_to_80, scale_to_image, crop_scale;
139
140 /* Scale the crop rectangle to 80% of the area, or less to fit the image */
141 scale_to_80 = MIN ((gdouble)gdk_pixbuf_get_width (area->priv->pixbuf) * 0.8 / area->priv->base_width,
142 (gdouble)gdk_pixbuf_get_height (area->priv->pixbuf) * 0.8 / area->priv->base_height);
143 scale_to_image = MIN ((gdouble)dest_width / area->priv->base_width,
144 (gdouble)dest_height / area->priv->base_height);
145 crop_scale = MIN (scale_to_80, scale_to_image);
146
147 area->priv->crop.width = crop_scale * area->priv->base_width / scale;
148 area->priv->crop.height = crop_scale * area->priv->base_height / scale;
149 area->priv->crop.x = (gdk_pixbuf_get_width (area->priv->browse_pixbuf) - area->priv->crop.width) / 2;
150 area->priv->crop.y = (gdk_pixbuf_get_height (area->priv->browse_pixbuf) - area->priv->crop.height) / 2;
151 }
152
153 area->priv->scale = scale;
154 area->priv->image.x = (allocation.width - dest_width) / 2;
155 area->priv->image.y = (allocation.height - dest_height) / 2;
156 area->priv->image.width = dest_width;
157 area->priv->image.height = dest_height;
158 }
159}
160
161static void
162crop_to_widget (CcCropArea *area,
163 GdkRectangle *crop)
164{
165 crop->x = area->priv->image.x + area->priv->crop.x * area->priv->scale;
166 crop->y = area->priv->image.y + area->priv->crop.y * area->priv->scale;
167 crop->width = area->priv->crop.width * area->priv->scale;
168 crop->height = area->priv->crop.height * area->priv->scale;
169}
170
171typedef enum {
172 OUTSIDE,
173 INSIDE,
174 TOP,
175 TOP_LEFT,
176 TOP_RIGHT,
177 BOTTOM,
178 BOTTOM_LEFT,
179 BOTTOM_RIGHT,
180 LEFT,
181 RIGHT
182} Location;
183
184static gboolean
185cc_crop_area_draw (GtkWidget *widget,
186 cairo_t *cr)
187{
188 GdkRectangle crop;
189 gint width, height, ix, iy;
190 CcCropArea *uarea = CC_CROP_AREA (widget);
191
192 if (uarea->priv->browse_pixbuf == NULL)
193 return FALSE;
194
195 update_pixbufs (uarea);
196
197 width = gdk_pixbuf_get_width (uarea->priv->pixbuf);
198 height = gdk_pixbuf_get_height (uarea->priv->pixbuf);
199 crop_to_widget (uarea, &crop);
200
201 ix = uarea->priv->image.x;
202 iy = uarea->priv->image.y;
203
204 gdk_cairo_set_source_pixbuf (cr, uarea->priv->color_shifted, ix, iy);
205 cairo_rectangle (cr, ix, iy, width, crop.y - iy);
206 cairo_rectangle (cr, ix, crop.y, crop.x - ix, crop.height);
207 cairo_rectangle (cr, crop.x + crop.width, crop.y, width - crop.width - (crop.x - ix), crop.height);
208 cairo_rectangle (cr, ix, crop.y + crop.height, width, height - crop.height - (crop.y - iy));
209 cairo_fill (cr);
210
211 gdk_cairo_set_source_pixbuf (cr, uarea->priv->pixbuf, ix, iy);
212 cairo_rectangle (cr, crop.x, crop.y, crop.width, crop.height);
213 cairo_fill (cr);
214
215 if (uarea->priv->active_region != OUTSIDE) {
216 gint x1, x2, y1, y2;
217 cairo_set_source_rgb (cr, 1, 1, 1);
218 cairo_set_line_width (cr, 1.0);
219 x1 = crop.x + crop.width / 3.0;
220 x2 = crop.x + 2 * crop.width / 3.0;
221 y1 = crop.y + crop.height / 3.0;
222 y2 = crop.y + 2 * crop.height / 3.0;
223
224 cairo_move_to (cr, x1 + 0.5, crop.y);
225 cairo_line_to (cr, x1 + 0.5, crop.y + crop.height);
226
227 cairo_move_to (cr, x2 + 0.5, crop.y);
228 cairo_line_to (cr, x2 + 0.5, crop.y + crop.height);
229
230 cairo_move_to (cr, crop.x, y1 + 0.5);
231 cairo_line_to (cr, crop.x + crop.width, y1 + 0.5);
232
233 cairo_move_to (cr, crop.x, y2 + 0.5);
234 cairo_line_to (cr, crop.x + crop.width, y2 + 0.5);
235 cairo_stroke (cr);
236 }
237
238 cairo_set_source_rgb (cr, 0, 0, 0);
239 cairo_set_line_width (cr, 1.0);
240 cairo_rectangle (cr,
241 crop.x + 0.5,
242 crop.y + 0.5,
243 crop.width - 1.0,
244 crop.height - 1.0);
245 cairo_stroke (cr);
246
247 cairo_set_source_rgb (cr, 1, 1, 1);
248 cairo_set_line_width (cr, 2.0);
249 cairo_rectangle (cr,
250 crop.x + 2.0,
251 crop.y + 2.0,
252 crop.width - 4.0,
253 crop.height - 4.0);
254 cairo_stroke (cr);
255
256 return FALSE;
257}
258
259typedef enum {
260 BELOW,
261 LOWER,
262 BETWEEN,
263 UPPER,
264 ABOVE
265} Range;
266
267static Range
268find_range (gint x,
269 gint min,
270 gint max)
271{
272 gint tolerance = 12;
273
274 if (x < min - tolerance)
275 return BELOW;
276 if (x <= min + tolerance)
277 return LOWER;
278 if (x < max - tolerance)
279 return BETWEEN;
280 if (x <= max + tolerance)
281 return UPPER;
282 return ABOVE;
283}
284
285static Location
286find_location (GdkRectangle *rect,
287 gint x,
288 gint y)
289{
290 Range x_range, y_range;
291 Location location[5][5] = {
292 { OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE },
293 { OUTSIDE, TOP_LEFT, TOP, TOP_RIGHT, OUTSIDE },
294 { OUTSIDE, LEFT, INSIDE, RIGHT, OUTSIDE },
295 { OUTSIDE, BOTTOM_LEFT, BOTTOM, BOTTOM_RIGHT, OUTSIDE },
296 { OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE }
297 };
298
299 x_range = find_range (x, rect->x, rect->x + rect->width);
300 y_range = find_range (y, rect->y, rect->y + rect->height);
301
302 return location[y_range][x_range];
303}
304
305static void
306update_cursor (CcCropArea *area,
307 gint x,
308 gint y)
309{
310 gint cursor_type;
311 GdkRectangle crop;
312 gint region;
313
314 region = area->priv->active_region;
315 if (region == OUTSIDE) {
316 crop_to_widget (area, &crop);
317 region = find_location (&crop, x, y);
318 }
319
320 switch (region) {
321 case OUTSIDE:
322 cursor_type = GDK_LEFT_PTR;
323 break;
324 case TOP_LEFT:
325 cursor_type = GDK_TOP_LEFT_CORNER;
326 break;
327 case TOP:
328 cursor_type = GDK_TOP_SIDE;
329 break;
330 case TOP_RIGHT:
331 cursor_type = GDK_TOP_RIGHT_CORNER;
332 break;
333 case LEFT:
334 cursor_type = GDK_LEFT_SIDE;
335 break;
336 case INSIDE:
337 cursor_type = GDK_FLEUR;
338 break;
339 case RIGHT:
340 cursor_type = GDK_RIGHT_SIDE;
341 break;
342 case BOTTOM_LEFT:
343 cursor_type = GDK_BOTTOM_LEFT_CORNER;
344 break;
345 case BOTTOM:
346 cursor_type = GDK_BOTTOM_SIDE;
347 break;
348 case BOTTOM_RIGHT:
349 cursor_type = GDK_BOTTOM_RIGHT_CORNER;
350 break;
351 default:
352 g_assert_not_reached ();
353 }
354
355 if (cursor_type != area->priv->current_cursor) {
356 GdkCursor *cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (area)),
357 cursor_type);
358 gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (area)), cursor);
359 g_object_unref (cursor);
360 area->priv->current_cursor = cursor_type;
361 }
362}
363
364static int
365eval_radial_line (gdouble center_x, gdouble center_y,
366 gdouble bounds_x, gdouble bounds_y,
367 gdouble user_x)
368{
369 gdouble decision_slope;
370 gdouble decision_intercept;
371
372 decision_slope = (bounds_y - center_y) / (bounds_x - center_x);
373 decision_intercept = -(decision_slope * bounds_x);
374
375 return (int) (decision_slope * user_x + decision_intercept);
376}
377
378static gboolean
379cc_crop_area_motion_notify_event (GtkWidget *widget,
380 GdkEventMotion *event)
381{
382 CcCropArea *area = CC_CROP_AREA (widget);
383 gint x, y;
384 gint delta_x, delta_y;
385 gint width, height;
386 gint adj_width, adj_height;
387 gint pb_width, pb_height;
388 GdkRectangle damage;
389 gint left, right, top, bottom;
390 gdouble new_width, new_height;
391 gdouble center_x, center_y;
392 gint min_width, min_height;
393
394 if (area->priv->browse_pixbuf == NULL)
395 return FALSE;
396
397 update_cursor (area, event->x, event->y);
398
399 crop_to_widget (area, &damage);
400 gtk_widget_queue_draw_area (widget,
401 damage.x - 1, damage.y - 1,
402 damage.width + 2, damage.height + 2);
403
404 pb_width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
405 pb_height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
406
407 x = (event->x - area->priv->image.x) / area->priv->scale;
408 y = (event->y - area->priv->image.y) / area->priv->scale;
409
410 delta_x = x - area->priv->last_press_x;
411 delta_y = y - area->priv->last_press_y;
412 area->priv->last_press_x = x;
413 area->priv->last_press_y = y;
414
415 left = area->priv->crop.x;
416 right = area->priv->crop.x + area->priv->crop.width - 1;
417 top = area->priv->crop.y;
418 bottom = area->priv->crop.y + area->priv->crop.height - 1;
419
420 center_x = (left + right) / 2.0;
421 center_y = (top + bottom) / 2.0;
422
423 switch (area->priv->active_region) {
424 case INSIDE:
425 width = right - left + 1;
426 height = bottom - top + 1;
427
428 left += delta_x;
429 right += delta_x;
430 top += delta_y;
431 bottom += delta_y;
432
433 if (left < 0)
434 left = 0;
435 if (top < 0)
436 top = 0;
437 if (right > pb_width)
438 right = pb_width;
439 if (bottom > pb_height)
440 bottom = pb_height;
441
442 adj_width = right - left + 1;
443 adj_height = bottom - top + 1;
444 if (adj_width != width) {
445 if (delta_x < 0)
446 right = left + width - 1;
447 else
448 left = right - width + 1;
449 }
450 if (adj_height != height) {
451 if (delta_y < 0)
452 bottom = top + height - 1;
453 else
454 top = bottom - height + 1;
455 }
456
457 break;
458
459 case TOP_LEFT:
460 if (area->priv->aspect < 0) {
461 top = y;
462 left = x;
463 }
464 else if (y < eval_radial_line (center_x, center_y, left, top, x)) {
465 top = y;
466 new_width = (bottom - top) * area->priv->aspect;
467 left = right - new_width;
468 }
469 else {
470 left = x;
471 new_height = (right - left) / area->priv->aspect;
472 top = bottom - new_height;
473 }
474 break;
475
476 case TOP:
477 top = y;
478 if (area->priv->aspect > 0) {
479 new_width = (bottom - top) * area->priv->aspect;
480 right = left + new_width;
481 }
482 break;
483
484 case TOP_RIGHT:
485 if (area->priv->aspect < 0) {
486 top = y;
487 right = x;
488 }
489 else if (y < eval_radial_line (center_x, center_y, right, top, x)) {
490 top = y;
491 new_width = (bottom - top) * area->priv->aspect;
492 right = left + new_width;
493 }
494 else {
495 right = x;
496 new_height = (right - left) / area->priv->aspect;
497 top = bottom - new_height;
498 }
499 break;
500
501 case LEFT:
502 left = x;
503 if (area->priv->aspect > 0) {
504 new_height = (right - left) / area->priv->aspect;
505 bottom = top + new_height;
506 }
507 break;
508
509 case BOTTOM_LEFT:
510 if (area->priv->aspect < 0) {
511 bottom = y;
512 left = x;
513 }
514 else if (y < eval_radial_line (center_x, center_y, left, bottom, x)) {
515 left = x;
516 new_height = (right - left) / area->priv->aspect;
517 bottom = top + new_height;
518 }
519 else {
520 bottom = y;
521 new_width = (bottom - top) * area->priv->aspect;
522 left = right - new_width;
523 }
524 break;
525
526 case RIGHT:
527 right = x;
528 if (area->priv->aspect > 0) {
529 new_height = (right - left) / area->priv->aspect;
530 bottom = top + new_height;
531 }
532 break;
533
534 case BOTTOM_RIGHT:
535 if (area->priv->aspect < 0) {
536 bottom = y;
537 right = x;
538 }
539 else if (y < eval_radial_line (center_x, center_y, right, bottom, x)) {
540 right = x;
541 new_height = (right - left) / area->priv->aspect;
542 bottom = top + new_height;
543 }
544 else {
545 bottom = y;
546 new_width = (bottom - top) * area->priv->aspect;
547 right = left + new_width;
548 }
549 break;
550
551 case BOTTOM:
552 bottom = y;
553 if (area->priv->aspect > 0) {
554 new_width = (bottom - top) * area->priv->aspect;
555 right= left + new_width;
556 }
557 break;
558
559 default:
560 return FALSE;
561 }
562
563 min_width = area->priv->base_width / area->priv->scale;
564 min_height = area->priv->base_height / area->priv->scale;
565
566 width = right - left + 1;
567 height = bottom - top + 1;
568 if (area->priv->aspect < 0) {
569 if (left < 0)
570 left = 0;
571 if (top < 0)
572 top = 0;
573 if (right > pb_width)
574 right = pb_width;
575 if (bottom > pb_height)
576 bottom = pb_height;
577
578 width = right - left + 1;
579 height = bottom - top + 1;
580
581 switch (area->priv->active_region) {
582 case LEFT:
583 case TOP_LEFT:
584 case BOTTOM_LEFT:
585 if (width < min_width)
586 left = right - min_width;
587 break;
588 case RIGHT:
589 case TOP_RIGHT:
590 case BOTTOM_RIGHT:
591 if (width < min_width)
592 right = left + min_width;
593 break;
594
595 default: ;
596 }
597
598 switch (area->priv->active_region) {
599 case TOP:
600 case TOP_LEFT:
601 case TOP_RIGHT:
602 if (height < min_height)
603 top = bottom - min_height;
604 break;
605 case BOTTOM:
606 case BOTTOM_LEFT:
607 case BOTTOM_RIGHT:
608 if (height < min_height)
609 bottom = top + min_height;
610 break;
611
612 default: ;
613 }
614 }
615 else {
616 if (left < 0 || top < 0 ||
617 right > pb_width || bottom > pb_height ||
618 width < min_width || height < min_height) {
619 left = area->priv->crop.x;
620 right = area->priv->crop.x + area->priv->crop.width - 1;
621 top = area->priv->crop.y;
622 bottom = area->priv->crop.y + area->priv->crop.height - 1;
623 }
624 }
625
626 area->priv->crop.x = left;
627 area->priv->crop.y = top;
628 area->priv->crop.width = right - left + 1;
629 area->priv->crop.height = bottom - top + 1;
630
631 crop_to_widget (area, &damage);
632 gtk_widget_queue_draw_area (widget,
633 damage.x - 1, damage.y - 1,
634 damage.width + 2, damage.height + 2);
635
636 return FALSE;
637}
638
639static gboolean
640cc_crop_area_button_press_event (GtkWidget *widget,
641 GdkEventButton *event)
642{
643 CcCropArea *area = CC_CROP_AREA (widget);
644 GdkRectangle crop;
645
646 if (area->priv->browse_pixbuf == NULL)
647 return FALSE;
648
649 crop_to_widget (area, &crop);
650
651 area->priv->last_press_x = (event->x - area->priv->image.x) / area->priv->scale;
652 area->priv->last_press_y = (event->y - area->priv->image.y) / area->priv->scale;
653 area->priv->active_region = find_location (&crop, event->x, event->y);
654
655 gtk_widget_queue_draw_area (widget,
656 crop.x - 1, crop.y - 1,
657 crop.width + 2, crop.height + 2);
658
659 return FALSE;
660}
661
662static gboolean
663cc_crop_area_button_release_event (GtkWidget *widget,
aviau4c87b832016-12-13 14:18:53 -0500664 G_GNUC_UNUSED GdkEventButton *event)
Stepan Salenikovich5c54b352016-09-14 14:28:19 -0400665{
666 CcCropArea *area = CC_CROP_AREA (widget);
667 GdkRectangle crop;
668
669 if (area->priv->browse_pixbuf == NULL)
670 return FALSE;
671
672 crop_to_widget (area, &crop);
673
674 area->priv->last_press_x = -1;
675 area->priv->last_press_y = -1;
676 area->priv->active_region = OUTSIDE;
677
678 gtk_widget_queue_draw_area (widget,
679 crop.x - 1, crop.y - 1,
680 crop.width + 2, crop.height + 2);
681
682 return FALSE;
683}
684
685static void
686cc_crop_area_set_size_request (CcCropArea *area)
687{
688 gtk_widget_set_size_request (GTK_WIDGET (area),
689 area->priv->base_width,
690 area->priv->base_height);
691}
692
693static void
694cc_crop_area_finalize (GObject *object)
695{
696 CcCropArea *area = CC_CROP_AREA (object);
697
698 if (area->priv->browse_pixbuf) {
699 g_object_unref (area->priv->browse_pixbuf);
700 area->priv->browse_pixbuf = NULL;
701 }
702 if (area->priv->pixbuf) {
703 g_object_unref (area->priv->pixbuf);
704 area->priv->pixbuf = NULL;
705 }
706 if (area->priv->color_shifted) {
707 g_object_unref (area->priv->color_shifted);
708 area->priv->color_shifted = NULL;
709 }
710}
711
712static void
713cc_crop_area_class_init (CcCropAreaClass *klass)
714{
715 GObjectClass *object_class = G_OBJECT_CLASS (klass);
716 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
717
718 object_class->finalize = cc_crop_area_finalize;
719 widget_class->draw = cc_crop_area_draw;
720 widget_class->button_press_event = cc_crop_area_button_press_event;
721 widget_class->button_release_event = cc_crop_area_button_release_event;
722 widget_class->motion_notify_event = cc_crop_area_motion_notify_event;
723
724 g_type_class_add_private (klass, sizeof (CcCropAreaPrivate));
725}
726
727static void
728cc_crop_area_init (CcCropArea *area)
729{
730 area->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((area), CC_TYPE_CROP_AREA,
731 CcCropAreaPrivate));
732
733 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_MASK |
734 GDK_BUTTON_PRESS_MASK |
735 GDK_BUTTON_RELEASE_MASK);
736
737 area->priv->scale = 0.0;
738 area->priv->image.x = 0;
739 area->priv->image.y = 0;
740 area->priv->image.width = 0;
741 area->priv->image.height = 0;
742 area->priv->active_region = OUTSIDE;
743 area->priv->base_width = 48;
744 area->priv->base_height = 48;
745 area->priv->aspect = 1;
746
747 cc_crop_area_set_size_request (area);
748}
749
750GtkWidget *
751cc_crop_area_new (void)
752{
753 return g_object_new (CC_TYPE_CROP_AREA, NULL);
754}
755
756GdkPixbuf *
757cc_crop_area_get_picture (CcCropArea *area)
758{
759 gint width, height;
760
761 width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
762 height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
763 width = MIN (area->priv->crop.width, width - area->priv->crop.x);
764 height = MIN (area->priv->crop.height, height - area->priv->crop.y);
765
766 return gdk_pixbuf_new_subpixbuf (area->priv->browse_pixbuf,
767 area->priv->crop.x,
768 area->priv->crop.y,
769 width, height);
770}
771
772void
773cc_crop_area_set_picture (CcCropArea *area,
774 GdkPixbuf *pixbuf)
775{
776 int width;
777 int height;
778
779 if (area->priv->browse_pixbuf) {
780 g_object_unref (area->priv->browse_pixbuf);
781 area->priv->browse_pixbuf = NULL;
782 }
783 if (pixbuf) {
784 area->priv->browse_pixbuf = g_object_ref (pixbuf);
785 width = gdk_pixbuf_get_width (pixbuf);
786 height = gdk_pixbuf_get_height (pixbuf);
787 } else {
788 width = 0;
789 height = 0;
790 }
791
792 area->priv->crop.width = 2 * area->priv->base_width;
793 area->priv->crop.height = 2 * area->priv->base_height;
794 area->priv->crop.x = (width - area->priv->crop.width) / 2;
795 area->priv->crop.y = (height - area->priv->crop.height) / 2;
796
797 area->priv->scale = 0.0;
798 area->priv->image.x = 0;
799 area->priv->image.y = 0;
800 area->priv->image.width = 0;
801 area->priv->image.height = 0;
802
803 gtk_widget_queue_draw (GTK_WIDGET (area));
804}
805
806void
807cc_crop_area_set_min_size (CcCropArea *area,
808 gint width,
809 gint height)
810{
811 area->priv->base_width = width;
812 area->priv->base_height = height;
813
814 cc_crop_area_set_size_request (area);
815
816 if (area->priv->aspect > 0) {
817 area->priv->aspect = area->priv->base_width / (gdouble)area->priv->base_height;
818 }
819}
820
821void
822cc_crop_area_set_constrain_aspect (CcCropArea *area,
823 gboolean constrain)
824{
825 if (constrain) {
826 area->priv->aspect = area->priv->base_width / (gdouble)area->priv->base_height;
827 }
828 else {
829 area->priv->aspect = -1;
830 }
831}