1 /* LIBGIMP - The GIMP Library
2 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3 *
4 * gimpwidgets.c
5 * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
6 *
7 * This library is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <https://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23
24 #include <lcms2.h>
25
26 #include <gegl.h>
27 #include <gtk/gtk.h>
28
29 #ifdef G_OS_WIN32
30 #ifdef _WIN32_WINNT
31 #undef _WIN32_WINNT
32 #endif
33 #define _WIN32_WINNT 0x0600
34 #include <windows.h>
35 #include <icm.h>
36 #endif
37
38 #ifdef GDK_WINDOWING_QUARTZ
39 #include <Carbon/Carbon.h>
40 #include <ApplicationServices/ApplicationServices.h>
41 #include <CoreServices/CoreServices.h>
42 #endif
43
44 #include "libgimpcolor/gimpcolor.h"
45 #include "libgimpconfig/gimpconfig.h"
46
47 #include "gimpwidgetstypes.h"
48
49 #include "gimp3migration.h"
50 #include "gimpsizeentry.h"
51 #include "gimpwidgetsutils.h"
52
53 #include "libgimp/libgimp-intl.h"
54
55
56 /**
57 * SECTION: gimpwidgetsutils
58 * @title: GimpWidgetsUtils
59 * @short_description: A collection of helper functions.
60 *
61 * A collection of helper functions.
62 **/
63
64
65 static GtkWidget *
find_mnemonic_widget(GtkWidget * widget,gint level)66 find_mnemonic_widget (GtkWidget *widget,
67 gint level)
68 {
69 gboolean can_focus;
70
71 g_object_get (widget, "can-focus", &can_focus, NULL);
72
73 if (GTK_WIDGET_GET_CLASS (widget)->activate_signal ||
74 can_focus ||
75 GTK_WIDGET_GET_CLASS (widget)->mnemonic_activate !=
76 GTK_WIDGET_CLASS (g_type_class_peek (GTK_TYPE_WIDGET))->mnemonic_activate)
77 {
78 return widget;
79 }
80
81 if (GIMP_IS_SIZE_ENTRY (widget))
82 {
83 GimpSizeEntry *entry = GIMP_SIZE_ENTRY (widget);
84
85 return gimp_size_entry_get_help_widget (entry,
86 entry->number_of_fields - 1);
87 }
88 else if (GTK_IS_CONTAINER (widget))
89 {
90 GtkWidget *mnemonic_widget = NULL;
91 GList *children;
92 GList *list;
93
94 children = gtk_container_get_children (GTK_CONTAINER (widget));
95
96 for (list = children; list; list = g_list_next (list))
97 {
98 mnemonic_widget = find_mnemonic_widget (list->data, level + 1);
99
100 if (mnemonic_widget)
101 break;
102 }
103
104 g_list_free (children);
105
106 return mnemonic_widget;
107 }
108
109 return NULL;
110 }
111
112 /**
113 * gimp_table_attach_aligned:
114 * @table: The #GtkTable the widgets will be attached to.
115 * @column: The column to start with.
116 * @row: The row to attach the widgets.
117 * @label_text: The text for the #GtkLabel which will be attached left of
118 * the widget.
119 * @xalign: The horizontal alignment of the #GtkLabel.
120 * @yalign: The vertical alignment of the #GtkLabel.
121 * @widget: The #GtkWidget to attach right of the label.
122 * @colspan: The number of columns the widget will use.
123 * @left_align: %TRUE if the widget should be left-aligned.
124 *
125 * Note that the @label_text can be %NULL and that the widget will be
126 * attached starting at (@column + 1) in this case, too.
127 *
128 * Returns: The created #GtkLabel.
129 **/
130 GtkWidget *
gimp_table_attach_aligned(GtkTable * table,gint column,gint row,const gchar * label_text,gfloat xalign,gfloat yalign,GtkWidget * widget,gint colspan,gboolean left_align)131 gimp_table_attach_aligned (GtkTable *table,
132 gint column,
133 gint row,
134 const gchar *label_text,
135 gfloat xalign,
136 gfloat yalign,
137 GtkWidget *widget,
138 gint colspan,
139 gboolean left_align)
140 {
141 GtkWidget *label = NULL;
142
143 if (label_text)
144 {
145 GtkWidget *mnemonic_widget;
146
147 label = gtk_label_new_with_mnemonic (label_text);
148 gtk_label_set_xalign (GTK_LABEL (label), xalign);
149 gtk_label_set_yalign (GTK_LABEL (label), yalign);
150 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
151 gtk_table_attach (table, label,
152 column, column + 1,
153 row, row + 1,
154 GTK_FILL, GTK_FILL, 0, 0);
155 gtk_widget_show (label);
156
157 mnemonic_widget = find_mnemonic_widget (widget, 0);
158
159 if (mnemonic_widget)
160 gtk_label_set_mnemonic_widget (GTK_LABEL (label), mnemonic_widget);
161 }
162
163 if (left_align)
164 {
165 GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
166
167 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
168 gtk_widget_show (widget);
169
170 widget = hbox;
171 }
172
173 gtk_table_attach (table, widget,
174 column + 1, column + 1 + colspan,
175 row, row + 1,
176 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
177
178 gtk_widget_show (widget);
179
180 return label;
181 }
182
183 /**
184 * gimp_label_set_attributes:
185 * @label: a #GtkLabel
186 * @...: a list of PangoAttrType and value pairs terminated by -1.
187 *
188 * Sets Pango attributes on a #GtkLabel in a more convenient way than
189 * gtk_label_set_attributes().
190 *
191 * This function is useful if you want to change the font attributes
192 * of a #GtkLabel. This is an alternative to using PangoMarkup which
193 * is slow to parse and awkward to handle in an i18n-friendly way.
194 *
195 * The attributes are set on the complete label, from start to end. If
196 * you need to set attributes on part of the label, you will have to
197 * use the PangoAttributes API directly.
198 *
199 * Since: 2.2
200 **/
201 void
gimp_label_set_attributes(GtkLabel * label,...)202 gimp_label_set_attributes (GtkLabel *label,
203 ...)
204 {
205 PangoAttribute *attr = NULL;
206 PangoAttrList *attrs;
207 va_list args;
208
209 g_return_if_fail (GTK_IS_LABEL (label));
210
211 attrs = pango_attr_list_new ();
212
213 va_start (args, label);
214
215 do
216 {
217 PangoAttrType attr_type = va_arg (args, PangoAttrType);
218
219 if (attr_type == -1)
220 attr_type = PANGO_ATTR_INVALID;
221
222 switch (attr_type)
223 {
224 case PANGO_ATTR_LANGUAGE:
225 attr = pango_attr_language_new (va_arg (args, PangoLanguage *));
226 break;
227
228 case PANGO_ATTR_FAMILY:
229 attr = pango_attr_family_new (va_arg (args, const gchar *));
230 break;
231
232 case PANGO_ATTR_STYLE:
233 attr = pango_attr_style_new (va_arg (args, PangoStyle));
234 break;
235
236 case PANGO_ATTR_WEIGHT:
237 attr = pango_attr_weight_new (va_arg (args, PangoWeight));
238 break;
239
240 case PANGO_ATTR_VARIANT:
241 attr = pango_attr_variant_new (va_arg (args, PangoVariant));
242 break;
243
244 case PANGO_ATTR_STRETCH:
245 attr = pango_attr_stretch_new (va_arg (args, PangoStretch));
246 break;
247
248 case PANGO_ATTR_SIZE:
249 attr = pango_attr_size_new (va_arg (args, gint));
250 break;
251
252 case PANGO_ATTR_FONT_DESC:
253 attr = pango_attr_font_desc_new (va_arg (args,
254 const PangoFontDescription *));
255 break;
256
257 case PANGO_ATTR_FOREGROUND:
258 {
259 const PangoColor *color = va_arg (args, const PangoColor *);
260
261 attr = pango_attr_foreground_new (color->red,
262 color->green,
263 color->blue);
264 }
265 break;
266
267 case PANGO_ATTR_BACKGROUND:
268 {
269 const PangoColor *color = va_arg (args, const PangoColor *);
270
271 attr = pango_attr_background_new (color->red,
272 color->green,
273 color->blue);
274 }
275 break;
276
277 case PANGO_ATTR_UNDERLINE:
278 attr = pango_attr_underline_new (va_arg (args, PangoUnderline));
279 break;
280
281 case PANGO_ATTR_STRIKETHROUGH:
282 attr = pango_attr_strikethrough_new (va_arg (args, gboolean));
283 break;
284
285 case PANGO_ATTR_RISE:
286 attr = pango_attr_rise_new (va_arg (args, gint));
287 break;
288
289 case PANGO_ATTR_SCALE:
290 attr = pango_attr_scale_new (va_arg (args, gdouble));
291 break;
292
293 default:
294 g_warning ("%s: invalid PangoAttribute type %d",
295 G_STRFUNC, attr_type);
296 case PANGO_ATTR_INVALID:
297 attr = NULL;
298 break;
299 }
300
301 if (attr)
302 {
303 attr->start_index = 0;
304 attr->end_index = -1;
305 pango_attr_list_insert (attrs, attr);
306 }
307 }
308 while (attr);
309
310 va_end (args);
311
312 gtk_label_set_attributes (label, attrs);
313 pango_attr_list_unref (attrs);
314 }
315
316 gint
gimp_widget_get_monitor(GtkWidget * widget)317 gimp_widget_get_monitor (GtkWidget *widget)
318 {
319 GdkWindow *window;
320 GdkScreen *screen;
321 GtkAllocation allocation;
322 gint x, y;
323
324 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
325
326 window = gtk_widget_get_window (widget);
327
328 if (! window)
329 return gimp_get_monitor_at_pointer (&screen);
330
331 screen = gtk_widget_get_screen (widget);
332
333 gdk_window_get_origin (window, &x, &y);
334 gtk_widget_get_allocation (widget, &allocation);
335
336 if (! gtk_widget_get_has_window (widget))
337 {
338 x += allocation.x;
339 y += allocation.y;
340 }
341
342 x += allocation.width / 2;
343 y += allocation.height / 2;
344
345 return gdk_screen_get_monitor_at_point (screen, x, y);
346 }
347
348 gint
gimp_get_monitor_at_pointer(GdkScreen ** screen)349 gimp_get_monitor_at_pointer (GdkScreen **screen)
350 {
351 gint x, y;
352
353 g_return_val_if_fail (screen != NULL, 0);
354
355 gdk_display_get_pointer (gdk_display_get_default (),
356 screen, &x, &y, NULL);
357
358 return gdk_screen_get_monitor_at_point (*screen, x, y);
359 }
360
361 typedef void (* MonitorChangedCallback) (GtkWidget *, gpointer);
362
363 typedef struct
364 {
365 GtkWidget *widget;
366 gint monitor;
367
368 MonitorChangedCallback callback;
369 gpointer user_data;
370 } TrackMonitorData;
371
372 static gboolean
track_monitor_configure_event(GtkWidget * toplevel,GdkEvent * event,TrackMonitorData * track_data)373 track_monitor_configure_event (GtkWidget *toplevel,
374 GdkEvent *event,
375 TrackMonitorData *track_data)
376 {
377 gint monitor = gimp_widget_get_monitor (toplevel);
378
379 if (monitor != track_data->monitor)
380 {
381 track_data->monitor = monitor;
382
383 track_data->callback (track_data->widget, track_data->user_data);
384 }
385
386 return FALSE;
387 }
388
389 static void
track_monitor_hierarchy_changed(GtkWidget * widget,GtkWidget * previous_toplevel,TrackMonitorData * track_data)390 track_monitor_hierarchy_changed (GtkWidget *widget,
391 GtkWidget *previous_toplevel,
392 TrackMonitorData *track_data)
393 {
394 GtkWidget *toplevel;
395
396 if (previous_toplevel)
397 {
398 g_signal_handlers_disconnect_by_func (previous_toplevel,
399 track_monitor_configure_event,
400 track_data);
401 }
402
403 toplevel = gtk_widget_get_toplevel (widget);
404
405 if (GTK_IS_WINDOW (toplevel))
406 {
407 GClosure *closure;
408 gint monitor;
409
410 closure = g_cclosure_new (G_CALLBACK (track_monitor_configure_event),
411 track_data, NULL);
412 g_object_watch_closure (G_OBJECT (widget), closure);
413 g_signal_connect_closure (toplevel, "configure-event", closure, FALSE);
414
415 monitor = gimp_widget_get_monitor (toplevel);
416
417 if (monitor != track_data->monitor)
418 {
419 track_data->monitor = monitor;
420
421 track_data->callback (track_data->widget, track_data->user_data);
422 }
423 }
424 }
425
426 /**
427 * gimp_widget_track_monitor:
428 * @widget: a #GtkWidget
429 * @monitor_changed_callback: the callback when @widget's monitor changes
430 * @user_data: data passed to @monitor_changed_callback
431 *
432 * This function behaves as if #GtkWidget had a signal
433 *
434 * GtkWidget::monitor_changed(GtkWidget *widget, gpointer user_data)
435 *
436 * That is emitted whenever @widget's toplevel window is moved from
437 * one monitor to another. This function automatically connects to
438 * the right toplevel #GtkWindow, even across moving @widget between
439 * toplevel windows.
440 *
441 * Note that this function tracks the toplevel, not @widget itself, so
442 * all a window's widgets are always considered to be on the same
443 * monitor. This is because this function is mainly used for fetching
444 * the new monitor's color profile, and it makes little sense to use
445 * different profiles for the widgets of one window.
446 *
447 * Since: 2.10
448 **/
449 void
gimp_widget_track_monitor(GtkWidget * widget,GCallback monitor_changed_callback,gpointer user_data)450 gimp_widget_track_monitor (GtkWidget *widget,
451 GCallback monitor_changed_callback,
452 gpointer user_data)
453 {
454 TrackMonitorData *track_data;
455 GtkWidget *toplevel;
456
457 g_return_if_fail (GTK_IS_WIDGET (widget));
458 g_return_if_fail (monitor_changed_callback != NULL);
459
460 track_data = g_new0 (TrackMonitorData, 1);
461
462 track_data->widget = widget;
463 track_data->callback = (MonitorChangedCallback) monitor_changed_callback;
464 track_data->user_data = user_data;
465
466 g_object_weak_ref (G_OBJECT (widget), (GWeakNotify) g_free, track_data);
467
468 g_signal_connect (widget, "hierarchy-changed",
469 G_CALLBACK (track_monitor_hierarchy_changed),
470 track_data);
471
472 toplevel = gtk_widget_get_toplevel (widget);
473
474 if (GTK_IS_WINDOW (toplevel))
475 track_monitor_hierarchy_changed (widget, NULL, track_data);
476 }
477
478 /**
479 * gimp_screen_get_color_profile:
480 * @screen: a #GdkScreen
481 * @monitor: the monitor number
482 *
483 * This function returns the #GimpColorProfile of monitor number @monitor
484 * of @screen, or %NULL if there is no profile configured.
485 *
486 * Since: 2.10
487 *
488 * Return value: the monitor's #GimpColorProfile, or %NULL.
489 **/
490 GimpColorProfile *
gimp_screen_get_color_profile(GdkScreen * screen,gint monitor)491 gimp_screen_get_color_profile (GdkScreen *screen,
492 gint monitor)
493 {
494 GimpColorProfile *profile = NULL;
495
496 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
497 g_return_val_if_fail (monitor >= 0, NULL);
498 g_return_val_if_fail (monitor < gdk_screen_get_n_monitors (screen), NULL);
499
500 #if defined GDK_WINDOWING_X11
501 {
502 GdkAtom type = GDK_NONE;
503 gint format = 0;
504 gint nitems = 0;
505 gchar *atom_name;
506 guchar *data = NULL;
507
508 if (monitor > 0)
509 atom_name = g_strdup_printf ("_ICC_PROFILE_%d", monitor);
510 else
511 atom_name = g_strdup ("_ICC_PROFILE");
512
513 if (gdk_property_get (gdk_screen_get_root_window (screen),
514 gdk_atom_intern (atom_name, FALSE),
515 GDK_NONE,
516 0, 64 * 1024 * 1024, FALSE,
517 &type, &format, &nitems, &data) && nitems > 0)
518 {
519 profile = gimp_color_profile_new_from_icc_profile (data, nitems,
520 NULL);
521 g_free (data);
522 }
523
524 g_free (atom_name);
525 }
526 #elif defined GDK_WINDOWING_QUARTZ
527 {
528 CGColorSpaceRef space = NULL;
529
530 space = CGDisplayCopyColorSpace (monitor);
531
532 if (space)
533 {
534 CFDataRef data;
535
536 data = CGColorSpaceCopyICCProfile (space);
537
538 if (data)
539 {
540 UInt8 *buffer = g_malloc (CFDataGetLength (data));
541
542 /* We cannot use CFDataGetBytesPtr(), because that returns
543 * a const pointer where cmsOpenProfileFromMem wants a
544 * non-const pointer.
545 */
546 CFDataGetBytes (data, CFRangeMake (0, CFDataGetLength (data)),
547 buffer);
548
549 profile = gimp_color_profile_new_from_icc_profile (buffer,
550 CFDataGetLength (data),
551 NULL);
552
553 g_free (buffer);
554 CFRelease (data);
555 }
556
557 CFRelease (space);
558 }
559 }
560 #elif defined G_OS_WIN32
561 {
562 GdkRectangle monitor_geometry;
563 POINT point;
564 gint offsetx = GetSystemMetrics (SM_XVIRTUALSCREEN);
565 gint offsety = GetSystemMetrics (SM_YVIRTUALSCREEN);
566 HMONITOR monitor_handle;
567 MONITORINFOEX info;
568 DISPLAY_DEVICE display_device;
569
570 info.cbSize = sizeof (MONITORINFOEX);
571 display_device.cb = sizeof (DISPLAY_DEVICE);
572
573 /* If the first monitor is not set as the main monitor,
574 * the monitor variable may not match the index used in
575 * EnumDisplayDevices(devicename, index, displaydevice, flags).
576 */
577 gdk_screen_get_monitor_geometry (screen, monitor, &monitor_geometry);
578 point.x = monitor_geometry.x + offsetx;
579 point.y = monitor_geometry.y + offsety;
580 monitor_handle = MonitorFromPoint (point, MONITOR_DEFAULTTONEAREST);
581
582 if (GetMonitorInfo (monitor_handle, (LPMONITORINFO)&info))
583 {
584 if (EnumDisplayDevices (info.szDevice, 0, &display_device, 0))
585 {
586 gchar *device_key = g_convert (display_device.DeviceKey, -1, "UTF-16LE", "WINDOWS-1252", NULL, NULL, NULL);
587 gchar *filename = NULL;
588 gchar *dir = NULL;
589 gchar *fullpath = NULL;
590 GFile *file;
591 DWORD len = 0;
592 gboolean per_user;
593 WCS_PROFILE_MANAGEMENT_SCOPE scope;
594
595 WcsGetUsePerUserProfiles ((LPWSTR)device_key, CLASS_MONITOR, &per_user);
596 scope = per_user ? WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER : WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
597
598 if (WcsGetDefaultColorProfileSize (scope,
599 (LPWSTR)device_key,
600 CPT_ICC,
601 CPST_NONE,
602 0,
603 &len))
604 {
605 gchar *filename_utf16 = g_new (gchar, len);
606
607 WcsGetDefaultColorProfile (scope,
608 (LPWSTR)device_key,
609 CPT_ICC,
610 CPST_NONE,
611 0,
612 len,
613 (LPWSTR)filename_utf16);
614
615 /* filename_utf16 must be native endian */
616 filename = g_utf16_to_utf8 ((gunichar2 *)filename_utf16, -1, NULL, NULL, NULL);
617 g_free (filename_utf16);
618 }
619 else
620 {
621 /* Due to a bug in Windows, the meanings of LCS_sRGB and
622 * LCS_WINDOWS_COLOR_SPACE are swapped.
623 */
624 GetStandardColorSpaceProfile (NULL, LCS_sRGB, NULL, &len);
625 filename = g_new (gchar, len);
626 GetStandardColorSpaceProfile (NULL, LCS_sRGB, filename, &len);
627 }
628
629 GetColorDirectory (NULL, NULL, &len);
630 dir = g_new (gchar, len);
631 GetColorDirectory (NULL, dir, &len);
632
633 fullpath = g_build_filename (dir, filename, NULL);
634 file = g_file_new_for_path (fullpath);
635
636 profile = gimp_color_profile_new_from_file (file, NULL);
637 g_object_unref (file);
638
639 g_free (fullpath);
640 g_free (dir);
641 g_free (filename);
642 g_free (device_key);
643 }
644 }
645 }
646 #endif
647
648 return profile;
649 }
650
651 GimpColorProfile *
gimp_widget_get_color_profile(GtkWidget * widget)652 gimp_widget_get_color_profile (GtkWidget *widget)
653 {
654 GdkScreen *screen;
655 gint monitor;
656
657 g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), NULL);
658
659 if (widget)
660 {
661 screen = gtk_widget_get_screen (widget);
662 monitor = gimp_widget_get_monitor (widget);
663 }
664 else
665 {
666 screen = gdk_screen_get_default ();
667 monitor = 0;
668 }
669
670 return gimp_screen_get_color_profile (screen, monitor);
671 }
672
673 static GimpColorProfile *
get_display_profile(GtkWidget * widget,GimpColorConfig * config)674 get_display_profile (GtkWidget *widget,
675 GimpColorConfig *config)
676 {
677 GimpColorProfile *profile = NULL;
678
679 if (gimp_color_config_get_display_profile_from_gdk (config))
680 /* get the toplevel's profile so all a window's colors look the same */
681 profile = gimp_widget_get_color_profile (gtk_widget_get_toplevel (widget));
682
683 if (! profile)
684 profile = gimp_color_config_get_display_color_profile (config, NULL);
685
686 if (! profile)
687 profile = gimp_color_profile_new_rgb_srgb ();
688
689 return profile;
690 }
691
692 typedef struct _TransformCache TransformCache;
693
694 struct _TransformCache
695 {
696 GimpColorTransform *transform;
697
698 GimpColorConfig *config;
699 GimpColorProfile *src_profile;
700 const Babl *src_format;
701 GimpColorProfile *dest_profile;
702 const Babl *dest_format;
703 GimpColorProfile *proof_profile;
704
705 gulong notify_id;
706 };
707
708 static GList *transform_caches = NULL;
709 static gboolean debug_cache = FALSE;
710
711 static gboolean
profiles_equal(GimpColorProfile * profile1,GimpColorProfile * profile2)712 profiles_equal (GimpColorProfile *profile1,
713 GimpColorProfile *profile2)
714 {
715 return ((profile1 == NULL && profile2 == NULL) ||
716 (profile1 != NULL && profile2 != NULL &&
717 gimp_color_profile_is_equal (profile1, profile2)));
718 }
719
720 static TransformCache *
transform_cache_get(GimpColorConfig * config,GimpColorProfile * src_profile,const Babl * src_format,GimpColorProfile * dest_profile,const Babl * dest_format,GimpColorProfile * proof_profile)721 transform_cache_get (GimpColorConfig *config,
722 GimpColorProfile *src_profile,
723 const Babl *src_format,
724 GimpColorProfile *dest_profile,
725 const Babl *dest_format,
726 GimpColorProfile *proof_profile)
727 {
728 GList *list;
729
730 for (list = transform_caches; list; list = g_list_next (list))
731 {
732 TransformCache *cache = list->data;
733
734 if (config == cache->config &&
735 src_format == cache->src_format &&
736 dest_format == cache->dest_format &&
737 profiles_equal (src_profile, cache->src_profile) &&
738 profiles_equal (dest_profile, cache->dest_profile) &&
739 profiles_equal (proof_profile, cache->proof_profile))
740 {
741 if (debug_cache)
742 g_printerr ("found cache %p\n", cache);
743
744 return cache;
745 }
746 }
747
748 return NULL;
749 }
750
751 static void
transform_cache_config_notify(GObject * config,const GParamSpec * pspec,TransformCache * cache)752 transform_cache_config_notify (GObject *config,
753 const GParamSpec *pspec,
754 TransformCache *cache)
755 {
756 transform_caches = g_list_remove (transform_caches, cache);
757
758 g_signal_handler_disconnect (config, cache->notify_id);
759
760 if (cache->transform)
761 g_object_unref (cache->transform);
762
763 g_object_unref (cache->src_profile);
764 g_object_unref (cache->dest_profile);
765
766 if (cache->proof_profile)
767 g_object_unref (cache->proof_profile);
768
769 g_free (cache);
770
771 if (debug_cache)
772 g_printerr ("deleted cache %p\n", cache);
773 }
774
775 GimpColorTransform *
gimp_widget_get_color_transform(GtkWidget * widget,GimpColorConfig * config,GimpColorProfile * src_profile,const Babl * src_format,const Babl * dest_format)776 gimp_widget_get_color_transform (GtkWidget *widget,
777 GimpColorConfig *config,
778 GimpColorProfile *src_profile,
779 const Babl *src_format,
780 const Babl *dest_format)
781 {
782 static gboolean initialized = FALSE;
783 GimpColorProfile *dest_profile = NULL;
784 GimpColorProfile *proof_profile = NULL;
785 TransformCache *cache;
786
787 g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), NULL);
788 g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), NULL);
789 g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (src_profile), NULL);
790 g_return_val_if_fail (src_format != NULL, NULL);
791 g_return_val_if_fail (dest_format != NULL, NULL);
792
793 if (G_UNLIKELY (! initialized))
794 {
795 initialized = TRUE;
796
797 debug_cache = g_getenv ("GIMP_DEBUG_TRANSFORM_CACHE") != NULL;
798 }
799
800 switch (gimp_color_config_get_mode (config))
801 {
802 case GIMP_COLOR_MANAGEMENT_OFF:
803 return NULL;
804
805 case GIMP_COLOR_MANAGEMENT_SOFTPROOF:
806 proof_profile = gimp_color_config_get_simulation_color_profile (config,
807 NULL);
808 /* fallthru */
809
810 case GIMP_COLOR_MANAGEMENT_DISPLAY:
811 dest_profile = get_display_profile (widget, config);
812 break;
813 }
814
815 cache = transform_cache_get (config,
816 src_profile,
817 src_format,
818 dest_profile,
819 dest_format,
820 proof_profile);
821
822 if (cache)
823 {
824 g_object_unref (dest_profile);
825
826 if (proof_profile)
827 g_object_unref (proof_profile);
828
829 if (cache->transform)
830 return g_object_ref (cache->transform);
831
832 return NULL;
833 }
834
835 if (! proof_profile &&
836 gimp_color_profile_is_equal (src_profile, dest_profile))
837 {
838 g_object_unref (dest_profile);
839
840 return NULL;
841 }
842
843 cache = g_new0 (TransformCache, 1);
844
845 if (debug_cache)
846 g_printerr ("creating cache %p\n", cache);
847
848 cache->config = g_object_ref (config);
849 cache->src_profile = g_object_ref (src_profile);
850 cache->src_format = src_format;
851 cache->dest_profile = dest_profile;
852 cache->dest_format = dest_format;
853 cache->proof_profile = proof_profile;
854
855 cache->notify_id =
856 g_signal_connect (cache->config, "notify",
857 G_CALLBACK (transform_cache_config_notify),
858 cache);
859
860 transform_caches = g_list_prepend (transform_caches, cache);
861
862 if (cache->proof_profile)
863 {
864 GimpColorTransformFlags flags = 0;
865
866 if (gimp_color_config_get_simulation_bpc (config))
867 flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
868
869 if (! gimp_color_config_get_simulation_optimize (config))
870 flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
871
872 if (gimp_color_config_get_simulation_gamut_check (config))
873 {
874 cmsUInt16Number alarmCodes[cmsMAXCHANNELS] = { 0, };
875 guchar r, g, b;
876
877 flags |= GIMP_COLOR_TRANSFORM_FLAGS_GAMUT_CHECK;
878
879 gimp_rgb_get_uchar (&config->out_of_gamut_color, &r, &g, &b);
880
881 alarmCodes[0] = (cmsUInt16Number) r * 256;
882 alarmCodes[1] = (cmsUInt16Number) g * 256;
883 alarmCodes[2] = (cmsUInt16Number) b * 256;
884
885 cmsSetAlarmCodes (alarmCodes);
886 }
887
888 cache->transform =
889 gimp_color_transform_new_proofing (cache->src_profile,
890 cache->src_format,
891 cache->dest_profile,
892 cache->dest_format,
893 cache->proof_profile,
894 gimp_color_config_get_simulation_intent (config),
895 gimp_color_config_get_display_intent (config),
896 flags);
897 }
898 else
899 {
900 GimpColorTransformFlags flags = 0;
901
902 if (gimp_color_config_get_display_bpc (config))
903 flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
904
905 if (! gimp_color_config_get_display_optimize (config))
906 flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
907
908 cache->transform =
909 gimp_color_transform_new (cache->src_profile,
910 cache->src_format,
911 cache->dest_profile,
912 cache->dest_format,
913 gimp_color_config_get_display_intent (config),
914 flags);
915 }
916
917 if (cache->transform)
918 return g_object_ref (cache->transform);
919
920 return NULL;
921 }
922