1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpfgbgeditor.c
5 * Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <string.h>
24
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27
28 #include "libgimpcolor/gimpcolor.h"
29 #include "libgimpconfig/gimpconfig.h"
30 #include "libgimpwidgets/gimpwidgets.h"
31
32 #include "widgets-types.h"
33
34 #include "config/gimpcoreconfig.h"
35
36 #include "core/gimp.h"
37 #include "core/gimpcontext.h"
38 #include "core/gimpimage.h"
39 #include "core/gimpimage-colormap.h"
40 #include "core/gimpmarshal.h"
41 #include "core/gimppalette.h"
42
43 #include "gimpdnd.h"
44 #include "gimpfgbgeditor.h"
45 #include "gimpwidgets-utils.h"
46
47 #define CHANNEL_EPSILON 1e-3
48
49 enum
50 {
51 PROP_0,
52 PROP_CONTEXT,
53 PROP_ACTIVE_COLOR
54 };
55
56 enum
57 {
58 COLOR_CLICKED,
59 TOOLTIP,
60 LAST_SIGNAL
61 };
62
63
64 static void gimp_fg_bg_editor_dispose (GObject *object);
65 static void gimp_fg_bg_editor_set_property (GObject *object,
66 guint property_id,
67 const GValue *value,
68 GParamSpec *pspec);
69 static void gimp_fg_bg_editor_get_property (GObject *object,
70 guint property_id,
71 GValue *value,
72 GParamSpec *pspec);
73
74 static void gimp_fg_bg_editor_style_set (GtkWidget *widget,
75 GtkStyle *prev_style);
76 static gboolean gimp_fg_bg_editor_expose (GtkWidget *widget,
77 GdkEventExpose *eevent);
78 static gboolean gimp_fg_bg_editor_button_press (GtkWidget *widget,
79 GdkEventButton *bevent);
80 static gboolean gimp_fg_bg_editor_button_release (GtkWidget *widget,
81 GdkEventButton *bevent);
82 static gboolean gimp_fg_bg_editor_drag_motion (GtkWidget *widget,
83 GdkDragContext *context,
84 gint x,
85 gint y,
86 guint time);
87 static gboolean gimp_fg_bg_editor_query_tooltip (GtkWidget *widget,
88 gint x,
89 gint y,
90 gboolean keyboard_mode,
91 GtkTooltip *tooltip);
92
93 static void gimp_fg_bg_editor_drag_color (GtkWidget *widget,
94 GimpRGB *color,
95 gpointer data);
96 static void gimp_fg_bg_editor_drop_color (GtkWidget *widget,
97 gint x,
98 gint y,
99 const GimpRGB *color,
100 gpointer data);
101
102 static void gimp_fg_bg_editor_create_transform (GimpFgBgEditor *editor);
103 static void gimp_fg_bg_editor_destroy_transform (GimpFgBgEditor *editor);
104
105 static void gimp_fg_bg_editor_image_changed (GimpFgBgEditor *editor,
106 GimpImage *image);
107
108 static void gimp_fg_bg_editor_draw_color_frame (GimpFgBgEditor *editor,
109 cairo_t *cr,
110 const GimpRGB *color,
111 gint x,
112 gint y,
113 gint width,
114 gint height,
115 gint corner_dx,
116 gint corner_dy);
117
118 G_DEFINE_TYPE (GimpFgBgEditor, gimp_fg_bg_editor, GTK_TYPE_EVENT_BOX)
119
120 #define parent_class gimp_fg_bg_editor_parent_class
121
122 static guint editor_signals[LAST_SIGNAL] = { 0 };
123
124
125 static void
gimp_fg_bg_editor_class_init(GimpFgBgEditorClass * klass)126 gimp_fg_bg_editor_class_init (GimpFgBgEditorClass *klass)
127 {
128 GObjectClass *object_class = G_OBJECT_CLASS (klass);
129 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
130
131 editor_signals[COLOR_CLICKED] =
132 g_signal_new ("color-clicked",
133 G_TYPE_FROM_CLASS (klass),
134 G_SIGNAL_RUN_FIRST,
135 G_STRUCT_OFFSET (GimpFgBgEditorClass, color_clicked),
136 NULL, NULL,
137 gimp_marshal_VOID__ENUM,
138 G_TYPE_NONE, 1,
139 GIMP_TYPE_ACTIVE_COLOR);
140
141 editor_signals[TOOLTIP] =
142 g_signal_new ("tooltip",
143 G_TYPE_FROM_CLASS (klass),
144 G_SIGNAL_RUN_FIRST,
145 G_STRUCT_OFFSET (GimpFgBgEditorClass, tooltip),
146 NULL, NULL,
147 gimp_marshal_VOID__INT_OBJECT,
148 G_TYPE_NONE, 2,
149 G_TYPE_INT,
150 GTK_TYPE_TOOLTIP);
151
152 object_class->dispose = gimp_fg_bg_editor_dispose;
153 object_class->set_property = gimp_fg_bg_editor_set_property;
154 object_class->get_property = gimp_fg_bg_editor_get_property;
155
156 widget_class->style_set = gimp_fg_bg_editor_style_set;
157 widget_class->expose_event = gimp_fg_bg_editor_expose;
158 widget_class->button_press_event = gimp_fg_bg_editor_button_press;
159 widget_class->button_release_event = gimp_fg_bg_editor_button_release;
160 widget_class->drag_motion = gimp_fg_bg_editor_drag_motion;
161 widget_class->query_tooltip = gimp_fg_bg_editor_query_tooltip;
162
163 g_object_class_install_property (object_class, PROP_CONTEXT,
164 g_param_spec_object ("context",
165 NULL, NULL,
166 GIMP_TYPE_CONTEXT,
167 GIMP_PARAM_READWRITE));
168
169 g_object_class_install_property (object_class, PROP_ACTIVE_COLOR,
170 g_param_spec_enum ("active-color",
171 NULL, NULL,
172 GIMP_TYPE_ACTIVE_COLOR,
173 GIMP_ACTIVE_COLOR_FOREGROUND,
174 GIMP_PARAM_READWRITE));
175 }
176
177 static void
gimp_fg_bg_editor_init(GimpFgBgEditor * editor)178 gimp_fg_bg_editor_init (GimpFgBgEditor *editor)
179 {
180 editor->active_color = GIMP_ACTIVE_COLOR_FOREGROUND;
181
182 gtk_event_box_set_visible_window (GTK_EVENT_BOX (editor), FALSE);
183
184 gtk_widget_add_events (GTK_WIDGET (editor),
185 GDK_BUTTON_PRESS_MASK |
186 GDK_BUTTON_RELEASE_MASK);
187
188 gimp_dnd_color_source_add (GTK_WIDGET (editor),
189 gimp_fg_bg_editor_drag_color, NULL);
190 gimp_dnd_color_dest_add (GTK_WIDGET (editor),
191 gimp_fg_bg_editor_drop_color, NULL);
192
193 gimp_widget_track_monitor (GTK_WIDGET (editor),
194 G_CALLBACK (gimp_fg_bg_editor_destroy_transform),
195 NULL);
196 }
197
198 static void
gimp_fg_bg_editor_dispose(GObject * object)199 gimp_fg_bg_editor_dispose (GObject *object)
200 {
201 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (object);
202
203 if (editor->context)
204 gimp_fg_bg_editor_set_context (editor, NULL);
205
206 g_clear_object (&editor->default_icon);
207 g_clear_object (&editor->swap_icon);
208
209 G_OBJECT_CLASS (parent_class)->dispose (object);
210 }
211
212 static void
gimp_fg_bg_editor_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)213 gimp_fg_bg_editor_set_property (GObject *object,
214 guint property_id,
215 const GValue *value,
216 GParamSpec *pspec)
217 {
218 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (object);
219
220 switch (property_id)
221 {
222 case PROP_CONTEXT:
223 gimp_fg_bg_editor_set_context (editor, g_value_get_object (value));
224 break;
225 case PROP_ACTIVE_COLOR:
226 gimp_fg_bg_editor_set_active (editor, g_value_get_enum (value));
227 break;
228 default:
229 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
230 break;
231 }
232 }
233
234 static void
gimp_fg_bg_editor_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)235 gimp_fg_bg_editor_get_property (GObject *object,
236 guint property_id,
237 GValue *value,
238 GParamSpec *pspec)
239 {
240 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (object);
241
242 switch (property_id)
243 {
244 case PROP_CONTEXT:
245 g_value_set_object (value, editor->context);
246 break;
247 case PROP_ACTIVE_COLOR:
248 g_value_set_enum (value, editor->active_color);
249 break;
250 default:
251 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
252 break;
253 }
254 }
255
256 static void
gimp_fg_bg_editor_style_set(GtkWidget * widget,GtkStyle * prev_style)257 gimp_fg_bg_editor_style_set (GtkWidget *widget,
258 GtkStyle *prev_style)
259 {
260 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
261
262 GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
263
264 g_clear_object (&editor->default_icon);
265 g_clear_object (&editor->swap_icon);
266 }
267
268 static gboolean
gimp_fg_bg_editor_expose(GtkWidget * widget,GdkEventExpose * eevent)269 gimp_fg_bg_editor_expose (GtkWidget *widget,
270 GdkEventExpose *eevent)
271 {
272 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
273 cairo_t *cr;
274 GtkAllocation allocation;
275 gint width, height;
276 gint default_w, default_h;
277 gint swap_w, swap_h;
278 gint rect_w, rect_h;
279 GimpRGB color;
280
281 if (! gtk_widget_is_drawable (widget))
282 return FALSE;
283
284 cr = gdk_cairo_create (eevent->window);
285 gdk_cairo_region (cr, eevent->region);
286 cairo_clip (cr);
287
288 gtk_widget_get_allocation (widget, &allocation);
289
290 width = allocation.width;
291 height = allocation.height;
292
293 cairo_translate (cr, allocation.x, allocation.y);
294
295 /* draw the default colors pixbuf */
296 if (! editor->default_icon)
297 editor->default_icon = gimp_widget_load_icon (widget,
298 GIMP_ICON_COLORS_DEFAULT, 12);
299
300 default_w = gdk_pixbuf_get_width (editor->default_icon);
301 default_h = gdk_pixbuf_get_height (editor->default_icon);
302
303 if (default_w < width / 2 && default_h < height / 2)
304 {
305 gdk_cairo_set_source_pixbuf (cr, editor->default_icon,
306 0, height - default_h);
307 cairo_paint (cr);
308 }
309 else
310 {
311 default_w = default_h = 0;
312 }
313
314 /* draw the swap colors pixbuf */
315 if (! editor->swap_icon)
316 editor->swap_icon = gimp_widget_load_icon (widget,
317 GIMP_ICON_COLORS_SWAP, 12);
318
319 swap_w = gdk_pixbuf_get_width (editor->swap_icon);
320 swap_h = gdk_pixbuf_get_height (editor->swap_icon);
321
322 if (swap_w < width / 2 && swap_h < height / 2)
323 {
324 gdk_cairo_set_source_pixbuf (cr, editor->swap_icon,
325 width - swap_w, 0);
326 cairo_paint (cr);
327 }
328 else
329 {
330 swap_w = swap_h = 0;
331 }
332
333 rect_h = height - MAX (default_h, swap_h) - 2;
334 rect_w = width - MAX (default_w, swap_w) - 4;
335
336 if (rect_h > (height * 3 / 4))
337 rect_w = MAX (rect_w - (rect_h - ((height * 3 / 4))),
338 width * 2 / 3);
339
340 editor->rect_width = rect_w;
341 editor->rect_height = rect_h;
342
343 if (! editor->transform)
344 gimp_fg_bg_editor_create_transform (editor);
345
346 if (editor->context)
347 {
348 /* draw the background frame */
349 gimp_context_get_background (editor->context, &color);
350 gimp_fg_bg_editor_draw_color_frame (editor, cr, &color,
351 width - rect_w, height - rect_h,
352 rect_w, rect_h,
353 +1, +1);
354
355
356 /* draw the foreground frame */
357 gimp_context_get_foreground (editor->context, &color);
358 gimp_fg_bg_editor_draw_color_frame (editor, cr, &color,
359 0, 0,
360 rect_w, rect_h,
361 -1, -1);
362 }
363
364 cairo_destroy (cr);
365
366 return TRUE;
367 }
368
369 static GimpFgBgTarget
gimp_fg_bg_editor_target(GimpFgBgEditor * editor,gint x,gint y)370 gimp_fg_bg_editor_target (GimpFgBgEditor *editor,
371 gint x,
372 gint y)
373 {
374 GtkAllocation allocation;
375 gint width;
376 gint height;
377 gint rect_w = editor->rect_width;
378 gint rect_h = editor->rect_height;
379
380 gtk_widget_get_allocation (GTK_WIDGET (editor), &allocation);
381
382 width = allocation.width;
383 height = allocation.height;
384
385 if (x > 0 && x < rect_w && y > 0 && y < rect_h)
386 {
387 return GIMP_FG_BG_TARGET_FOREGROUND;
388 }
389 else if (x > (width - rect_w) && x < width &&
390 y > (height - rect_h) && y < height)
391 {
392 return GIMP_FG_BG_TARGET_BACKGROUND;
393 }
394 else if (x > 0 && x < (width - rect_w) &&
395 y > rect_h && y < height)
396 {
397 return GIMP_FG_BG_TARGET_DEFAULT;
398 }
399 else if (x > rect_w && x < width &&
400 y > 0 && y < (height - rect_h))
401 {
402 return GIMP_FG_BG_TARGET_SWAP;
403 }
404
405 return GIMP_FG_BG_TARGET_INVALID;
406 }
407
408 static gboolean
gimp_fg_bg_editor_button_press(GtkWidget * widget,GdkEventButton * bevent)409 gimp_fg_bg_editor_button_press (GtkWidget *widget,
410 GdkEventButton *bevent)
411 {
412 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
413
414 if (bevent->button == 1 && bevent->type == GDK_BUTTON_PRESS)
415 {
416 GimpFgBgTarget target = gimp_fg_bg_editor_target (editor,
417 bevent->x, bevent->y);
418
419 editor->click_target = GIMP_FG_BG_TARGET_INVALID;
420
421 switch (target)
422 {
423 case GIMP_FG_BG_TARGET_FOREGROUND:
424 if (editor->active_color != GIMP_ACTIVE_COLOR_FOREGROUND)
425 gimp_fg_bg_editor_set_active (editor,
426 GIMP_ACTIVE_COLOR_FOREGROUND);
427 editor->click_target = GIMP_FG_BG_TARGET_FOREGROUND;
428 break;
429
430 case GIMP_FG_BG_TARGET_BACKGROUND:
431 if (editor->active_color != GIMP_ACTIVE_COLOR_BACKGROUND)
432 gimp_fg_bg_editor_set_active (editor,
433 GIMP_ACTIVE_COLOR_BACKGROUND);
434 editor->click_target = GIMP_FG_BG_TARGET_BACKGROUND;
435 break;
436
437 case GIMP_FG_BG_TARGET_SWAP:
438 if (editor->context)
439 gimp_context_swap_colors (editor->context);
440 break;
441
442 case GIMP_FG_BG_TARGET_DEFAULT:
443 if (editor->context)
444 gimp_context_set_default_colors (editor->context);
445 break;
446
447 default:
448 break;
449 }
450 }
451
452 return FALSE;
453 }
454
455 static gboolean
gimp_fg_bg_editor_button_release(GtkWidget * widget,GdkEventButton * bevent)456 gimp_fg_bg_editor_button_release (GtkWidget *widget,
457 GdkEventButton *bevent)
458 {
459 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
460
461 if (bevent->button == 1)
462 {
463 GimpFgBgTarget target = gimp_fg_bg_editor_target (editor,
464 bevent->x, bevent->y);
465
466 if (target == editor->click_target)
467 {
468 switch (target)
469 {
470 case GIMP_FG_BG_TARGET_FOREGROUND:
471 g_signal_emit (editor, editor_signals[COLOR_CLICKED], 0,
472 GIMP_ACTIVE_COLOR_FOREGROUND);
473 break;
474
475 case GIMP_FG_BG_TARGET_BACKGROUND:
476 g_signal_emit (editor, editor_signals[COLOR_CLICKED], 0,
477 GIMP_ACTIVE_COLOR_BACKGROUND);
478 break;
479
480 default:
481 break;
482 }
483 }
484
485 editor->click_target = GIMP_FG_BG_TARGET_INVALID;
486 }
487
488 return FALSE;
489 }
490
491 static gboolean
gimp_fg_bg_editor_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)492 gimp_fg_bg_editor_drag_motion (GtkWidget *widget,
493 GdkDragContext *context,
494 gint x,
495 gint y,
496 guint time)
497 {
498 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
499 GimpFgBgTarget target = gimp_fg_bg_editor_target (editor, x, y);
500
501 if (target == GIMP_FG_BG_TARGET_FOREGROUND ||
502 target == GIMP_FG_BG_TARGET_BACKGROUND)
503 {
504 gdk_drag_status (context, GDK_ACTION_COPY, time);
505
506 return TRUE;
507 }
508
509 gdk_drag_status (context, 0, time);
510
511 return FALSE;
512 }
513
514 static gboolean
gimp_fg_bg_editor_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip)515 gimp_fg_bg_editor_query_tooltip (GtkWidget *widget,
516 gint x,
517 gint y,
518 gboolean keyboard_mode,
519 GtkTooltip *tooltip)
520 {
521 if (! keyboard_mode)
522 {
523 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
524 GimpFgBgTarget target = gimp_fg_bg_editor_target (editor, x, y);
525
526 if (target != GIMP_FG_BG_TARGET_INVALID)
527 {
528 g_signal_emit (widget, editor_signals[TOOLTIP], 0,
529 target, tooltip);
530
531 return TRUE;
532 }
533 }
534
535 return FALSE;
536 }
537
538
539 /* public functions */
540
541 GtkWidget *
gimp_fg_bg_editor_new(GimpContext * context)542 gimp_fg_bg_editor_new (GimpContext *context)
543 {
544 g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
545
546 return g_object_new (GIMP_TYPE_FG_BG_EDITOR,
547 "context", context,
548 NULL);
549 }
550
551 void
gimp_fg_bg_editor_set_context(GimpFgBgEditor * editor,GimpContext * context)552 gimp_fg_bg_editor_set_context (GimpFgBgEditor *editor,
553 GimpContext *context)
554 {
555 g_return_if_fail (GIMP_IS_FG_BG_EDITOR (editor));
556 g_return_if_fail (context == NULL || GIMP_IS_CONTEXT (context));
557
558 if (context != editor->context)
559 {
560 if (editor->context)
561 {
562 g_signal_handlers_disconnect_by_func (editor->context,
563 gtk_widget_queue_draw,
564 editor);
565 g_signal_handlers_disconnect_by_func (editor->context,
566 G_CALLBACK (gimp_fg_bg_editor_image_changed),
567 editor);
568 g_object_unref (editor->context);
569
570 g_signal_handlers_disconnect_by_func (editor->color_config,
571 gimp_fg_bg_editor_destroy_transform,
572 editor);
573 g_clear_object (&editor->color_config);
574 }
575
576 editor->context = context;
577
578 if (context)
579 {
580 g_object_ref (context);
581
582 g_signal_connect_swapped (context, "foreground-changed",
583 G_CALLBACK (gtk_widget_queue_draw),
584 editor);
585 g_signal_connect_swapped (context, "background-changed",
586 G_CALLBACK (gtk_widget_queue_draw),
587 editor);
588 g_signal_connect_swapped (context, "image-changed",
589 G_CALLBACK (gimp_fg_bg_editor_image_changed),
590 editor);
591
592 editor->color_config = g_object_ref (context->gimp->config->color_management);
593
594 g_signal_connect_swapped (editor->color_config, "notify",
595 G_CALLBACK (gimp_fg_bg_editor_destroy_transform),
596 editor);
597 }
598
599 gimp_fg_bg_editor_destroy_transform (editor);
600
601 g_object_notify (G_OBJECT (editor), "context");
602 }
603 }
604
605 void
gimp_fg_bg_editor_set_active(GimpFgBgEditor * editor,GimpActiveColor active)606 gimp_fg_bg_editor_set_active (GimpFgBgEditor *editor,
607 GimpActiveColor active)
608 {
609 g_return_if_fail (GIMP_IS_FG_BG_EDITOR (editor));
610
611 editor->active_color = active;
612 gtk_widget_queue_draw (GTK_WIDGET (editor));
613 g_object_notify (G_OBJECT (editor), "active-color");
614 }
615
616
617 /* private functions */
618
619 static void
gimp_fg_bg_editor_drag_color(GtkWidget * widget,GimpRGB * color,gpointer data)620 gimp_fg_bg_editor_drag_color (GtkWidget *widget,
621 GimpRGB *color,
622 gpointer data)
623 {
624 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
625
626 if (editor->context)
627 {
628 switch (editor->active_color)
629 {
630 case GIMP_ACTIVE_COLOR_FOREGROUND:
631 gimp_context_get_foreground (editor->context, color);
632 break;
633
634 case GIMP_ACTIVE_COLOR_BACKGROUND:
635 gimp_context_get_background (editor->context, color);
636 break;
637 }
638 }
639 }
640
641 static void
gimp_fg_bg_editor_drop_color(GtkWidget * widget,gint x,gint y,const GimpRGB * color,gpointer data)642 gimp_fg_bg_editor_drop_color (GtkWidget *widget,
643 gint x,
644 gint y,
645 const GimpRGB *color,
646 gpointer data)
647 {
648 GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
649
650 if (editor->context)
651 {
652 switch (gimp_fg_bg_editor_target (editor, x, y))
653 {
654 case GIMP_FG_BG_TARGET_FOREGROUND:
655 gimp_context_set_foreground (editor->context, color);
656 break;
657
658 case GIMP_FG_BG_TARGET_BACKGROUND:
659 gimp_context_set_background (editor->context, color);
660 break;
661
662 default:
663 break;
664 }
665 }
666 }
667
668 static void
gimp_fg_bg_editor_create_transform(GimpFgBgEditor * editor)669 gimp_fg_bg_editor_create_transform (GimpFgBgEditor *editor)
670 {
671 if (editor->color_config)
672 {
673 static GimpColorProfile *profile = NULL;
674
675 if (G_UNLIKELY (! profile))
676 profile = gimp_color_profile_new_rgb_srgb ();
677
678 editor->transform =
679 gimp_widget_get_color_transform (GTK_WIDGET (editor),
680 editor->color_config,
681 profile,
682 babl_format ("R'G'B'A double"),
683 babl_format ("R'G'B'A double"));
684 }
685 }
686
687 static void
gimp_fg_bg_editor_destroy_transform(GimpFgBgEditor * editor)688 gimp_fg_bg_editor_destroy_transform (GimpFgBgEditor *editor)
689 {
690 g_clear_object (&editor->transform);
691
692 gtk_widget_queue_draw (GTK_WIDGET (editor));
693 }
694
695 static void
gimp_fg_bg_editor_image_changed(GimpFgBgEditor * editor,GimpImage * image)696 gimp_fg_bg_editor_image_changed (GimpFgBgEditor *editor,
697 GimpImage *image)
698 {
699 gtk_widget_queue_draw (GTK_WIDGET (editor));
700
701 if (editor->active_image)
702 {
703 g_signal_handlers_disconnect_by_func (editor->active_image,
704 G_CALLBACK (gtk_widget_queue_draw),
705 editor);
706 if (gimp_image_get_base_type (editor->active_image) == GIMP_INDEXED)
707 {
708 GimpPalette *palette;
709
710 palette = gimp_image_get_colormap_palette (editor->active_image);
711 g_signal_handlers_disconnect_by_func (palette,
712 G_CALLBACK (gtk_widget_queue_draw),
713 editor);
714 }
715 }
716 editor->active_image = image;
717 if (image)
718 {
719 g_signal_connect_swapped (image, "notify::base-type",
720 G_CALLBACK (gtk_widget_queue_draw),
721 editor);
722 g_signal_connect_swapped (image, "colormap-changed",
723 G_CALLBACK (gtk_widget_queue_draw),
724 editor);
725
726 if (gimp_image_get_base_type (image) == GIMP_INDEXED)
727 {
728 GimpPalette *palette;
729
730 palette = gimp_image_get_colormap_palette (editor->active_image);
731 g_signal_connect_swapped (palette, "dirty",
732 G_CALLBACK (gtk_widget_queue_draw),
733 editor);
734 }
735 }
736 }
737
738 static void
gimp_fg_bg_editor_draw_color_frame(GimpFgBgEditor * editor,cairo_t * cr,const GimpRGB * color,gint x,gint y,gint width,gint height,gint corner_dx,gint corner_dy)739 gimp_fg_bg_editor_draw_color_frame (GimpFgBgEditor *editor,
740 cairo_t *cr,
741 const GimpRGB *color,
742 gint x,
743 gint y,
744 gint width,
745 gint height,
746 gint corner_dx,
747 gint corner_dy)
748 {
749 GimpPalette *colormap_palette = NULL;
750 GimpImageBaseType base_type = GIMP_RGB;
751 GimpRGB transformed_color;
752
753 if (editor->active_image)
754 {
755 base_type = gimp_image_get_base_type (editor->active_image);
756
757 if (base_type == GIMP_INDEXED)
758 {
759 colormap_palette = gimp_image_get_colormap_palette (
760 editor->active_image);
761 }
762 }
763
764 if (editor->transform)
765 {
766 gimp_color_transform_process_pixels (editor->transform,
767 babl_format ("R'G'B'A double"),
768 color,
769 babl_format ("R'G'B'A double"),
770 &transformed_color,
771 1);
772 }
773 else
774 {
775 transformed_color = *color;
776 }
777
778 cairo_save (cr);
779
780 gimp_cairo_set_source_rgb (cr, &transformed_color);
781
782 cairo_rectangle (cr, x, y, width, height);
783 cairo_fill (cr);
784
785 if (editor->color_config &&
786 /* Common out-of-gamut case */
787 ((color->r < 0.0 || color->r > 1.0 ||
788 color->g < 0.0 || color->g > 1.0 ||
789 color->b < 0.0 || color->b > 1.0) ||
790 /* Indexed images */
791 (colormap_palette &&
792 ! gimp_palette_find_entry (colormap_palette, color, NULL)) ||
793 /* Grayscale images */
794 (base_type == GIMP_GRAY &&
795 (ABS (color->r - color->g) > CHANNEL_EPSILON ||
796 ABS (color->r - color->b) > CHANNEL_EPSILON ||
797 ABS (color->g - color->b) > CHANNEL_EPSILON))))
798 {
799 gint corner_x = x + 0.5 * (1.0 + corner_dx) * width;
800 gint corner_y = y + 0.5 * (1.0 + corner_dy) * height;
801 gint side = MIN (width, height) * 2 / 3;
802
803 cairo_move_to (cr, corner_x, corner_y);
804 cairo_line_to (cr, corner_x + side * corner_dx, corner_y);
805 cairo_line_to (cr, corner_x, corner_y + side * corner_dy);
806 cairo_close_path (cr);
807
808 gimp_cairo_set_source_rgb (cr,
809 &editor->color_config->out_of_gamut_color);
810 cairo_fill (cr);
811 }
812
813 cairo_set_line_width (cr, 1.0);
814
815 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
816 cairo_rectangle (cr, x + 0.5, y + 0.5, width - 1.0, height - 1.0);
817 cairo_stroke (cr);
818
819 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
820 cairo_rectangle (cr, x + 1.5, y + 1.5, width - 3.0, height - 3.0);
821 cairo_stroke (cr);
822
823 cairo_restore (cr);
824 }
825