1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <gegl.h>
21 #include <gtk/gtk.h>
22
23 #include "libgimpmath/gimpmath.h"
24
25 #include "display-types.h"
26 #include "tools/tools-types.h"
27
28 #include "config/gimpguiconfig.h"
29
30 #include "core/gimp.h"
31 #include "core/gimpcontainer.h"
32 #include "core/gimpcontext.h"
33 #include "core/gimpimage.h"
34 #include "core/gimpprogress.h"
35
36 #include "widgets/gimpdialogfactory.h"
37
38 #include "tools/gimptool.h"
39 #include "tools/tool_manager.h"
40
41 #include "gimpdisplay.h"
42 #include "gimpdisplay-handlers.h"
43 #include "gimpdisplayshell.h"
44 #include "gimpdisplayshell-expose.h"
45 #include "gimpdisplayshell-handlers.h"
46 #include "gimpdisplayshell-icon.h"
47 #include "gimpdisplayshell-scroll.h"
48 #include "gimpdisplayshell-scrollbars.h"
49 #include "gimpdisplayshell-title.h"
50 #include "gimpdisplayshell-transform.h"
51 #include "gimpimagewindow.h"
52
53 #include "gimp-intl.h"
54
55
56 #define FLUSH_NOW_INTERVAL (G_TIME_SPAN_SECOND / 60)
57
58 #define PAINT_AREA_CHUNK_WIDTH 32
59 #define PAINT_AREA_CHUNK_HEIGHT 32
60
61
62 enum
63 {
64 PROP_0,
65 PROP_ID,
66 PROP_GIMP,
67 PROP_IMAGE,
68 PROP_SHELL
69 };
70
71
72 typedef struct _GimpDisplayPrivate GimpDisplayPrivate;
73
74 struct _GimpDisplayPrivate
75 {
76 gint ID; /* unique identifier for this display */
77
78 GimpImage *image; /* pointer to the associated image */
79 gint instance; /* the instance # of this display as
80 * taken from the image at creation */
81
82 GeglRectangle bounding_box;
83
84 GtkWidget *shell;
85 cairo_region_t *update_region;
86
87 guint64 last_flush_now;
88 };
89
90 #define GIMP_DISPLAY_GET_PRIVATE(display) \
91 ((GimpDisplayPrivate *) gimp_display_get_instance_private ((GimpDisplay *) (display)))
92
93
94 /* local function prototypes */
95
96 static void gimp_display_progress_iface_init (GimpProgressInterface *iface);
97
98 static void gimp_display_set_property (GObject *object,
99 guint property_id,
100 const GValue *value,
101 GParamSpec *pspec);
102 static void gimp_display_get_property (GObject *object,
103 guint property_id,
104 GValue *value,
105 GParamSpec *pspec);
106
107 static GimpProgress * gimp_display_progress_start (GimpProgress *progress,
108 gboolean cancellable,
109 const gchar *message);
110 static void gimp_display_progress_end (GimpProgress *progress);
111 static gboolean gimp_display_progress_is_active (GimpProgress *progress);
112 static void gimp_display_progress_set_text (GimpProgress *progress,
113 const gchar *message);
114 static void gimp_display_progress_set_value (GimpProgress *progress,
115 gdouble percentage);
116 static gdouble gimp_display_progress_get_value (GimpProgress *progress);
117 static void gimp_display_progress_pulse (GimpProgress *progress);
118 static guint32 gimp_display_progress_get_window_id (GimpProgress *progress);
119 static gboolean gimp_display_progress_message (GimpProgress *progress,
120 Gimp *gimp,
121 GimpMessageSeverity severity,
122 const gchar *domain,
123 const gchar *message);
124 static void gimp_display_progress_canceled (GimpProgress *progress,
125 GimpDisplay *display);
126
127 static void gimp_display_flush_whenever (GimpDisplay *display,
128 gboolean now);
129 static void gimp_display_paint_area (GimpDisplay *display,
130 gint x,
131 gint y,
132 gint w,
133 gint h);
134
135
G_DEFINE_TYPE_WITH_CODE(GimpDisplay,gimp_display,GIMP_TYPE_OBJECT,G_ADD_PRIVATE (GimpDisplay)G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS,gimp_display_progress_iface_init))136 G_DEFINE_TYPE_WITH_CODE (GimpDisplay, gimp_display, GIMP_TYPE_OBJECT,
137 G_ADD_PRIVATE (GimpDisplay)
138 G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS,
139 gimp_display_progress_iface_init))
140
141 #define parent_class gimp_display_parent_class
142
143
144 static void
145 gimp_display_class_init (GimpDisplayClass *klass)
146 {
147 GObjectClass *object_class = G_OBJECT_CLASS (klass);
148
149 object_class->set_property = gimp_display_set_property;
150 object_class->get_property = gimp_display_get_property;
151
152 g_object_class_install_property (object_class, PROP_ID,
153 g_param_spec_int ("id",
154 NULL, NULL,
155 0, G_MAXINT, 0,
156 GIMP_PARAM_READABLE));
157
158 g_object_class_install_property (object_class, PROP_GIMP,
159 g_param_spec_object ("gimp",
160 NULL, NULL,
161 GIMP_TYPE_GIMP,
162 GIMP_PARAM_READWRITE |
163 G_PARAM_CONSTRUCT_ONLY));
164
165 g_object_class_install_property (object_class, PROP_IMAGE,
166 g_param_spec_object ("image",
167 NULL, NULL,
168 GIMP_TYPE_IMAGE,
169 GIMP_PARAM_READABLE));
170
171 g_object_class_install_property (object_class, PROP_SHELL,
172 g_param_spec_object ("shell",
173 NULL, NULL,
174 GIMP_TYPE_DISPLAY_SHELL,
175 GIMP_PARAM_READABLE));
176 }
177
178 static void
gimp_display_init(GimpDisplay * display)179 gimp_display_init (GimpDisplay *display)
180 {
181 }
182
183 static void
gimp_display_progress_iface_init(GimpProgressInterface * iface)184 gimp_display_progress_iface_init (GimpProgressInterface *iface)
185 {
186 iface->start = gimp_display_progress_start;
187 iface->end = gimp_display_progress_end;
188 iface->is_active = gimp_display_progress_is_active;
189 iface->set_text = gimp_display_progress_set_text;
190 iface->set_value = gimp_display_progress_set_value;
191 iface->get_value = gimp_display_progress_get_value;
192 iface->pulse = gimp_display_progress_pulse;
193 iface->get_window_id = gimp_display_progress_get_window_id;
194 iface->message = gimp_display_progress_message;
195 }
196
197 static void
gimp_display_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)198 gimp_display_set_property (GObject *object,
199 guint property_id,
200 const GValue *value,
201 GParamSpec *pspec)
202 {
203 GimpDisplay *display = GIMP_DISPLAY (object);
204 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
205
206 switch (property_id)
207 {
208 case PROP_GIMP:
209 {
210 gint ID;
211
212 display->gimp = g_value_get_object (value); /* don't ref the gimp */
213 display->config = GIMP_DISPLAY_CONFIG (display->gimp->config);
214
215 do
216 {
217 ID = display->gimp->next_display_ID++;
218
219 if (display->gimp->next_display_ID == G_MAXINT)
220 display->gimp->next_display_ID = 1;
221 }
222 while (gimp_display_get_by_ID (display->gimp, ID));
223
224 private->ID = ID;
225 }
226 break;
227
228 default:
229 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
230 break;
231 }
232 }
233
234 static void
gimp_display_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)235 gimp_display_get_property (GObject *object,
236 guint property_id,
237 GValue *value,
238 GParamSpec *pspec)
239 {
240 GimpDisplay *display = GIMP_DISPLAY (object);
241 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
242
243 switch (property_id)
244 {
245 case PROP_ID:
246 g_value_set_int (value, private->ID);
247 break;
248
249 case PROP_GIMP:
250 g_value_set_object (value, display->gimp);
251 break;
252
253 case PROP_IMAGE:
254 g_value_set_object (value, private->image);
255 break;
256
257 case PROP_SHELL:
258 g_value_set_object (value, private->shell);
259 break;
260
261 default:
262 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
263 break;
264 }
265 }
266
267 static GimpProgress *
gimp_display_progress_start(GimpProgress * progress,gboolean cancellable,const gchar * message)268 gimp_display_progress_start (GimpProgress *progress,
269 gboolean cancellable,
270 const gchar *message)
271 {
272 GimpDisplay *display = GIMP_DISPLAY (progress);
273 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
274
275 if (private->shell)
276 return gimp_progress_start (GIMP_PROGRESS (private->shell), cancellable,
277 "%s", message);
278
279 return NULL;
280 }
281
282 static void
gimp_display_progress_end(GimpProgress * progress)283 gimp_display_progress_end (GimpProgress *progress)
284 {
285 GimpDisplay *display = GIMP_DISPLAY (progress);
286 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
287
288 if (private->shell)
289 gimp_progress_end (GIMP_PROGRESS (private->shell));
290 }
291
292 static gboolean
gimp_display_progress_is_active(GimpProgress * progress)293 gimp_display_progress_is_active (GimpProgress *progress)
294 {
295 GimpDisplay *display = GIMP_DISPLAY (progress);
296 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
297
298 if (private->shell)
299 return gimp_progress_is_active (GIMP_PROGRESS (private->shell));
300
301 return FALSE;
302 }
303
304 static void
gimp_display_progress_set_text(GimpProgress * progress,const gchar * message)305 gimp_display_progress_set_text (GimpProgress *progress,
306 const gchar *message)
307 {
308 GimpDisplay *display = GIMP_DISPLAY (progress);
309 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
310
311 if (private->shell)
312 gimp_progress_set_text_literal (GIMP_PROGRESS (private->shell), message);
313 }
314
315 static void
gimp_display_progress_set_value(GimpProgress * progress,gdouble percentage)316 gimp_display_progress_set_value (GimpProgress *progress,
317 gdouble percentage)
318 {
319 GimpDisplay *display = GIMP_DISPLAY (progress);
320 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
321
322 if (private->shell)
323 gimp_progress_set_value (GIMP_PROGRESS (private->shell), percentage);
324 }
325
326 static gdouble
gimp_display_progress_get_value(GimpProgress * progress)327 gimp_display_progress_get_value (GimpProgress *progress)
328 {
329 GimpDisplay *display = GIMP_DISPLAY (progress);
330 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
331
332 if (private->shell)
333 return gimp_progress_get_value (GIMP_PROGRESS (private->shell));
334
335 return 0.0;
336 }
337
338 static void
gimp_display_progress_pulse(GimpProgress * progress)339 gimp_display_progress_pulse (GimpProgress *progress)
340 {
341 GimpDisplay *display = GIMP_DISPLAY (progress);
342 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
343
344 if (private->shell)
345 gimp_progress_pulse (GIMP_PROGRESS (private->shell));
346 }
347
348 static guint32
gimp_display_progress_get_window_id(GimpProgress * progress)349 gimp_display_progress_get_window_id (GimpProgress *progress)
350 {
351 GimpDisplay *display = GIMP_DISPLAY (progress);
352 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
353
354 if (private->shell)
355 return gimp_progress_get_window_id (GIMP_PROGRESS (private->shell));
356
357 return 0;
358 }
359
360 static gboolean
gimp_display_progress_message(GimpProgress * progress,Gimp * gimp,GimpMessageSeverity severity,const gchar * domain,const gchar * message)361 gimp_display_progress_message (GimpProgress *progress,
362 Gimp *gimp,
363 GimpMessageSeverity severity,
364 const gchar *domain,
365 const gchar *message)
366 {
367 GimpDisplay *display = GIMP_DISPLAY (progress);
368 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
369
370 if (private->shell)
371 return gimp_progress_message (GIMP_PROGRESS (private->shell), gimp,
372 severity, domain, message);
373
374 return FALSE;
375 }
376
377 static void
gimp_display_progress_canceled(GimpProgress * progress,GimpDisplay * display)378 gimp_display_progress_canceled (GimpProgress *progress,
379 GimpDisplay *display)
380 {
381 gimp_progress_cancel (GIMP_PROGRESS (display));
382 }
383
384
385 /* public functions */
386
387 GimpDisplay *
gimp_display_new(Gimp * gimp,GimpImage * image,GimpUnit unit,gdouble scale,GimpUIManager * popup_manager,GimpDialogFactory * dialog_factory,GdkScreen * screen,gint monitor)388 gimp_display_new (Gimp *gimp,
389 GimpImage *image,
390 GimpUnit unit,
391 gdouble scale,
392 GimpUIManager *popup_manager,
393 GimpDialogFactory *dialog_factory,
394 GdkScreen *screen,
395 gint monitor)
396 {
397 GimpDisplay *display;
398 GimpDisplayPrivate *private;
399 GimpImageWindow *window = NULL;
400 GimpDisplayShell *shell;
401
402 g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
403 g_return_val_if_fail (image == NULL || GIMP_IS_IMAGE (image), NULL);
404 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
405
406 /* If there isn't an interface, never create a display */
407 if (gimp->no_interface)
408 return NULL;
409
410 display = g_object_new (GIMP_TYPE_DISPLAY,
411 "gimp", gimp,
412 NULL);
413
414 private = GIMP_DISPLAY_GET_PRIVATE (display);
415
416 /* refs the image */
417 if (image)
418 gimp_display_set_image (display, image);
419
420 /* get an image window */
421 if (GIMP_GUI_CONFIG (display->config)->single_window_mode)
422 {
423 GimpDisplay *active_display;
424
425 active_display = gimp_context_get_display (gimp_get_user_context (gimp));
426
427 if (! active_display)
428 {
429 active_display =
430 GIMP_DISPLAY (gimp_container_get_first_child (gimp->displays));
431 }
432
433 if (active_display)
434 {
435 GimpDisplayShell *shell = gimp_display_get_shell (active_display);
436
437 window = gimp_display_shell_get_window (shell);
438 }
439 }
440
441 if (! window)
442 {
443 window = gimp_image_window_new (gimp,
444 private->image,
445 dialog_factory,
446 screen,
447 monitor);
448 }
449
450 /* create the shell for the image */
451 private->shell = gimp_display_shell_new (display, unit, scale,
452 popup_manager,
453 screen,
454 monitor);
455
456 shell = gimp_display_get_shell (display);
457
458 gimp_display_update_bounding_box (display);
459
460 gimp_image_window_add_shell (window, shell);
461 gimp_display_shell_present (shell);
462
463 /* make sure the docks are visible, in case all other image windows
464 * are iconified, see bug #686544.
465 */
466 gimp_dialog_factory_show_with_display (dialog_factory);
467
468 g_signal_connect (gimp_display_shell_get_statusbar (shell), "cancel",
469 G_CALLBACK (gimp_display_progress_canceled),
470 display);
471
472 /* add the display to the list */
473 gimp_container_add (gimp->displays, GIMP_OBJECT (display));
474
475 return display;
476 }
477
478 /**
479 * gimp_display_delete:
480 * @display:
481 *
482 * Closes the display and removes it from the display list. You should
483 * not call this function directly, use gimp_display_close() instead.
484 */
485 void
gimp_display_delete(GimpDisplay * display)486 gimp_display_delete (GimpDisplay *display)
487 {
488 GimpDisplayPrivate *private;
489 GimpTool *active_tool;
490
491 g_return_if_fail (GIMP_IS_DISPLAY (display));
492
493 private = GIMP_DISPLAY_GET_PRIVATE (display);
494
495 /* remove the display from the list */
496 gimp_container_remove (display->gimp->displays, GIMP_OBJECT (display));
497
498 /* unrefs the image */
499 gimp_display_set_image (display, NULL);
500
501 active_tool = tool_manager_get_active (display->gimp);
502
503 if (active_tool && active_tool->focus_display == display)
504 tool_manager_focus_display_active (display->gimp, NULL);
505
506 if (private->shell)
507 {
508 GimpDisplayShell *shell = gimp_display_get_shell (display);
509 GimpImageWindow *window = gimp_display_shell_get_window (shell);
510
511 /* set private->shell to NULL *before* destroying the shell.
512 * all callbacks in gimpdisplayshell-callbacks.c will check
513 * this pointer and do nothing if the shell is in destruction.
514 */
515 private->shell = NULL;
516
517 if (window)
518 {
519 if (gimp_image_window_get_n_shells (window) > 1)
520 {
521 g_object_ref (shell);
522
523 gimp_image_window_remove_shell (window, shell);
524 gtk_widget_destroy (GTK_WIDGET (shell));
525
526 g_object_unref (shell);
527 }
528 else
529 {
530 gimp_image_window_destroy (window);
531 }
532 }
533 else
534 {
535 g_object_unref (shell);
536 }
537 }
538
539 g_object_unref (display);
540 }
541
542 /**
543 * gimp_display_close:
544 * @display:
545 *
546 * Closes the display. If this is the last display, it will remain
547 * open, but without an image.
548 */
549 void
gimp_display_close(GimpDisplay * display)550 gimp_display_close (GimpDisplay *display)
551 {
552 g_return_if_fail (GIMP_IS_DISPLAY (display));
553
554 if (gimp_container_get_n_children (display->gimp->displays) > 1)
555 {
556 gimp_display_delete (display);
557 }
558 else
559 {
560 gimp_display_empty (display);
561 }
562 }
563
564 gint
gimp_display_get_ID(GimpDisplay * display)565 gimp_display_get_ID (GimpDisplay *display)
566 {
567 GimpDisplayPrivate *private;
568
569 g_return_val_if_fail (GIMP_IS_DISPLAY (display), -1);
570
571 private = GIMP_DISPLAY_GET_PRIVATE (display);
572
573 return private->ID;
574 }
575
576 GimpDisplay *
gimp_display_get_by_ID(Gimp * gimp,gint ID)577 gimp_display_get_by_ID (Gimp *gimp,
578 gint ID)
579 {
580 GList *list;
581
582 g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
583
584 for (list = gimp_get_display_iter (gimp);
585 list;
586 list = g_list_next (list))
587 {
588 GimpDisplay *display = list->data;
589
590 if (gimp_display_get_ID (display) == ID)
591 return display;
592 }
593
594 return NULL;
595 }
596
597 /**
598 * gimp_display_get_action_name:
599 * @display:
600 *
601 * Returns: The action name for the given display. The action name
602 * depends on the display ID. The result must be freed with g_free().
603 **/
604 gchar *
gimp_display_get_action_name(GimpDisplay * display)605 gimp_display_get_action_name (GimpDisplay *display)
606 {
607 g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
608
609 return g_strdup_printf ("windows-display-%04d",
610 gimp_display_get_ID (display));
611 }
612
613 Gimp *
gimp_display_get_gimp(GimpDisplay * display)614 gimp_display_get_gimp (GimpDisplay *display)
615 {
616 g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
617
618 return display->gimp;
619 }
620
621 GimpImage *
gimp_display_get_image(GimpDisplay * display)622 gimp_display_get_image (GimpDisplay *display)
623 {
624 g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
625
626 return GIMP_DISPLAY_GET_PRIVATE (display)->image;
627 }
628
629 void
gimp_display_set_image(GimpDisplay * display,GimpImage * image)630 gimp_display_set_image (GimpDisplay *display,
631 GimpImage *image)
632 {
633 GimpDisplayPrivate *private;
634 GimpImage *old_image = NULL;
635 GimpDisplayShell *shell;
636
637 g_return_if_fail (GIMP_IS_DISPLAY (display));
638 g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image));
639
640 private = GIMP_DISPLAY_GET_PRIVATE (display);
641
642 shell = gimp_display_get_shell (display);
643
644 if (private->image)
645 {
646 /* stop any active tool */
647 tool_manager_control_active (display->gimp, GIMP_TOOL_ACTION_HALT,
648 display);
649
650 gimp_display_shell_disconnect (shell);
651
652 gimp_display_disconnect (display);
653
654 g_clear_pointer (&private->update_region, cairo_region_destroy);
655
656 gimp_image_dec_display_count (private->image);
657
658 /* set private->image before unrefing because there may be code
659 * that listens for image removals and then iterates the
660 * display list to find a valid display.
661 */
662 old_image = private->image;
663
664 #if 0
665 g_print ("%s: image->ref_count before unrefing: %d\n",
666 G_STRFUNC, G_OBJECT (old_image)->ref_count);
667 #endif
668 }
669
670 private->image = image;
671
672 if (image)
673 {
674 #if 0
675 g_print ("%s: image->ref_count before refing: %d\n",
676 G_STRFUNC, G_OBJECT (image)->ref_count);
677 #endif
678
679 g_object_ref (image);
680
681 private->instance = gimp_image_get_instance_count (image);
682 gimp_image_inc_instance_count (image);
683
684 gimp_image_inc_display_count (image);
685
686 gimp_display_connect (display);
687
688 if (shell)
689 gimp_display_shell_connect (shell);
690 }
691
692 if (old_image)
693 g_object_unref (old_image);
694
695 gimp_display_update_bounding_box (display);
696
697 if (shell)
698 {
699 if (image)
700 {
701 gimp_display_shell_reconnect (shell);
702 }
703 else
704 {
705 gimp_display_shell_title_update (shell);
706 gimp_display_shell_icon_update (shell);
707 }
708 }
709
710 if (old_image != image)
711 g_object_notify (G_OBJECT (display), "image");
712 }
713
714 gint
gimp_display_get_instance(GimpDisplay * display)715 gimp_display_get_instance (GimpDisplay *display)
716 {
717 GimpDisplayPrivate *private;
718
719 g_return_val_if_fail (GIMP_IS_DISPLAY (display), 0);
720
721 private = GIMP_DISPLAY_GET_PRIVATE (display);
722
723 return private->instance;
724 }
725
726 GimpDisplayShell *
gimp_display_get_shell(GimpDisplay * display)727 gimp_display_get_shell (GimpDisplay *display)
728 {
729 GimpDisplayPrivate *private;
730
731 g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
732
733 private = GIMP_DISPLAY_GET_PRIVATE (display);
734
735 return GIMP_DISPLAY_SHELL (private->shell);
736 }
737
738 void
gimp_display_empty(GimpDisplay * display)739 gimp_display_empty (GimpDisplay *display)
740 {
741 GimpDisplayPrivate *private;
742 GList *iter;
743
744 g_return_if_fail (GIMP_IS_DISPLAY (display));
745
746 private = GIMP_DISPLAY_GET_PRIVATE (display);
747
748 g_return_if_fail (GIMP_IS_IMAGE (private->image));
749
750 for (iter = display->gimp->context_list; iter; iter = g_list_next (iter))
751 {
752 GimpContext *context = iter->data;
753
754 if (gimp_context_get_display (context) == display)
755 gimp_context_set_image (context, NULL);
756 }
757
758 gimp_display_set_image (display, NULL);
759
760 gimp_display_shell_empty (gimp_display_get_shell (display));
761 }
762
763 void
gimp_display_fill(GimpDisplay * display,GimpImage * image,GimpUnit unit,gdouble scale)764 gimp_display_fill (GimpDisplay *display,
765 GimpImage *image,
766 GimpUnit unit,
767 gdouble scale)
768 {
769 GimpDisplayPrivate *private;
770
771 g_return_if_fail (GIMP_IS_DISPLAY (display));
772 g_return_if_fail (GIMP_IS_IMAGE (image));
773
774 private = GIMP_DISPLAY_GET_PRIVATE (display);
775
776 g_return_if_fail (private->image == NULL);
777
778 gimp_display_set_image (display, image);
779
780 gimp_display_shell_fill (gimp_display_get_shell (display),
781 image, unit, scale);
782 }
783
784 void
gimp_display_update_bounding_box(GimpDisplay * display)785 gimp_display_update_bounding_box (GimpDisplay *display)
786 {
787 GimpDisplayPrivate *private;
788 GimpDisplayShell *shell;
789 GeglRectangle bounding_box = {};
790
791 g_return_if_fail (GIMP_IS_DISPLAY (display));
792
793 private = GIMP_DISPLAY_GET_PRIVATE (display);
794 shell = gimp_display_get_shell (display);
795
796 if (shell)
797 {
798 bounding_box = gimp_display_shell_get_bounding_box (shell);
799
800 if (! gegl_rectangle_equal (&bounding_box, &private->bounding_box))
801 {
802 GeglRectangle diff_rects[4];
803 gint n_diff_rects;
804 gint i;
805
806 n_diff_rects = gegl_rectangle_subtract (diff_rects,
807 &private->bounding_box,
808 &bounding_box);
809
810 for (i = 0; i < n_diff_rects; i++)
811 {
812 gimp_display_paint_area (display,
813 diff_rects[i].x, diff_rects[i].y,
814 diff_rects[i].width, diff_rects[i].height);
815 }
816
817 private->bounding_box = bounding_box;
818
819 gimp_display_shell_scroll_clamp_and_update (shell);
820 gimp_display_shell_scrollbars_update (shell);
821 }
822 }
823 else
824 {
825 private->bounding_box = bounding_box;
826 }
827 }
828
829 void
gimp_display_update_area(GimpDisplay * display,gboolean now,gint x,gint y,gint w,gint h)830 gimp_display_update_area (GimpDisplay *display,
831 gboolean now,
832 gint x,
833 gint y,
834 gint w,
835 gint h)
836 {
837 GimpDisplayPrivate *private;
838
839 g_return_if_fail (GIMP_IS_DISPLAY (display));
840
841 private = GIMP_DISPLAY_GET_PRIVATE (display);
842
843 if (now)
844 {
845 gimp_display_paint_area (display, x, y, w, h);
846 }
847 else
848 {
849 cairo_rectangle_int_t rect;
850 gint image_width;
851 gint image_height;
852
853 image_width = gimp_image_get_width (private->image);
854 image_height = gimp_image_get_height (private->image);
855
856 rect.x = CLAMP (x, 0, image_width);
857 rect.y = CLAMP (y, 0, image_height);
858 rect.width = CLAMP (x + w, 0, image_width) - rect.x;
859 rect.height = CLAMP (y + h, 0, image_height) - rect.y;
860
861 if (private->update_region)
862 cairo_region_union_rectangle (private->update_region, &rect);
863 else
864 private->update_region = cairo_region_create_rectangle (&rect);
865 }
866 }
867
868 void
gimp_display_flush(GimpDisplay * display)869 gimp_display_flush (GimpDisplay *display)
870 {
871 g_return_if_fail (GIMP_IS_DISPLAY (display));
872
873 /* FIXME: we can end up being called during shell construction if "show all"
874 * is enabled by default, in which case the shell's display pointer is still
875 * NULL
876 */
877 if (gimp_display_get_shell (display))
878 gimp_display_flush_whenever (display, FALSE);
879 }
880
881 void
gimp_display_flush_now(GimpDisplay * display)882 gimp_display_flush_now (GimpDisplay *display)
883 {
884 g_return_if_fail (GIMP_IS_DISPLAY (display));
885
886 gimp_display_flush_whenever (display, TRUE);
887 }
888
889
890 /* private functions */
891
892 static void
gimp_display_flush_whenever(GimpDisplay * display,gboolean now)893 gimp_display_flush_whenever (GimpDisplay *display,
894 gboolean now)
895 {
896 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
897
898 if (private->update_region)
899 {
900 gint n_rects = cairo_region_num_rectangles (private->update_region);
901 gint i;
902
903 for (i = 0; i < n_rects; i++)
904 {
905 cairo_rectangle_int_t rect;
906
907 cairo_region_get_rectangle (private->update_region,
908 i, &rect);
909
910 gimp_display_paint_area (display,
911 rect.x,
912 rect.y,
913 rect.width,
914 rect.height);
915 }
916
917 g_clear_pointer (&private->update_region, cairo_region_destroy);
918 }
919
920 if (now)
921 {
922 guint64 now = g_get_monotonic_time ();
923
924 if ((now - private->last_flush_now) > FLUSH_NOW_INTERVAL)
925 {
926 gimp_display_shell_flush (gimp_display_get_shell (display), TRUE);
927
928 private->last_flush_now = now;
929 }
930 }
931 else
932 {
933 gimp_display_shell_flush (gimp_display_get_shell (display), now);
934 }
935 }
936
937 static void
gimp_display_paint_area(GimpDisplay * display,gint x,gint y,gint w,gint h)938 gimp_display_paint_area (GimpDisplay *display,
939 gint x,
940 gint y,
941 gint w,
942 gint h)
943 {
944 GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display);
945 GimpDisplayShell *shell = gimp_display_get_shell (display);
946 GeglRectangle rect;
947 gint x1, y1, x2, y2;
948 gdouble x1_f, y1_f, x2_f, y2_f;
949
950 if (! gegl_rectangle_intersect (&rect,
951 &private->bounding_box,
952 GEGL_RECTANGLE (x, y, w, h)))
953 {
954 return;
955 }
956
957 /* display the area */
958 gimp_display_shell_transform_bounds (shell,
959 rect.x,
960 rect.y,
961 rect.x + rect.width,
962 rect.y + rect.height,
963 &x1_f, &y1_f, &x2_f, &y2_f);
964
965 /* make sure to expose a superset of the transformed sub-pixel expose
966 * area, not a subset. bug #126942. --mitch
967 *
968 * also accommodate for spill introduced by potential box filtering.
969 * (bug #474509). --simon
970 */
971 x1 = floor (x1_f - 0.5);
972 y1 = floor (y1_f - 0.5);
973 x2 = ceil (x2_f + 0.5);
974 y2 = ceil (y2_f + 0.5);
975
976 /* align transformed area to a coarse grid, to simplify the
977 * invalidated area
978 */
979 x1 = floor ((gdouble) x1 / PAINT_AREA_CHUNK_WIDTH) * PAINT_AREA_CHUNK_WIDTH;
980 y1 = floor ((gdouble) y1 / PAINT_AREA_CHUNK_HEIGHT) * PAINT_AREA_CHUNK_HEIGHT;
981 x2 = ceil ((gdouble) x2 / PAINT_AREA_CHUNK_WIDTH) * PAINT_AREA_CHUNK_WIDTH;
982 y2 = ceil ((gdouble) y2 / PAINT_AREA_CHUNK_HEIGHT) * PAINT_AREA_CHUNK_HEIGHT;
983
984 gimp_display_shell_expose_area (shell, x1, y1, x2 - x1, y2 - y1);
985 }
986