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