Skip to content

Commit 997eac9

Browse files
committed
Added option to display color ranges which are out of RGB gamut as a stripe pattern for Lab and LCH color spaces.
1 parent 851f392 commit 997eac9

8 files changed

+259
-37
lines changed

res/gray-pattern.svg

+133
Loading

share/gpick/gpick-gray-pattern.png

237 Bytes
Loading

source/Color.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -755,3 +755,11 @@ const ReferenceObserver color_get_observer(const char *observer)
755755
return REFERENCE_OBSERVER_2;
756756
}
757757

758+
bool color_is_rgb_out_of_gamut(const Color* a)
759+
{
760+
if (a->rgb.red < 0 || a->rgb.red > 1) return true;
761+
if (a->rgb.green < 0 || a->rgb.green > 1) return true;
762+
if (a->rgb.blue < 0 || a->rgb.blue > 1) return true;
763+
return false;
764+
}
765+

source/Color.h

+1
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ void color_cmyk_to_rgb(const Color* a, Color* b);
178178

179179

180180
void color_rgb_normalize(Color* a);
181+
bool color_is_rgb_out_of_gamut(const Color* a);
181182

182183
void color_rgb_get_linear(const Color* a, Color* b);
183184
void color_linear_get_rgb(const Color* a, Color* b);

source/ColorPicker.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -1056,13 +1056,17 @@ static int source_activate(ColorPickerArgs *args){
10561056
gtk_widget_hide(colorspaces[i].widget);
10571057
}
10581058

1059+
bool out_of_gamut_mask = dynv_get_bool_wd(args->params, "out_of_gamut_mask", true);
1060+
1061+
gtk_color_component_set_out_of_gamut_mask(GTK_COLOR_COMPONENT(args->lab_control), out_of_gamut_mask);
10591062
gtk_color_component_set_lab_illuminant(GTK_COLOR_COMPONENT(args->lab_control), color_get_illuminant(dynv_get_string_wd(args->params, "lab.illuminant", "D50")));
10601063
gtk_color_component_set_lab_observer(GTK_COLOR_COMPONENT(args->lab_control), color_get_observer(dynv_get_string_wd(args->params, "lab.observer", "2")));
10611064
updateComponentText(args, GTK_COLOR_COMPONENT(args->lab_control), "lab");
10621065

1066+
gtk_color_component_set_out_of_gamut_mask(GTK_COLOR_COMPONENT(args->lch_control), out_of_gamut_mask);
10631067
gtk_color_component_set_lab_illuminant(GTK_COLOR_COMPONENT(args->lch_control), color_get_illuminant(dynv_get_string_wd(args->params, "lab.illuminant", "D50")));
10641068
gtk_color_component_set_lab_observer(GTK_COLOR_COMPONENT(args->lch_control), color_get_observer(dynv_get_string_wd(args->params, "lab.observer", "2")));
1065-
updateComponentText(args, GTK_COLOR_COMPONENT(args->lch_control), "lab");
1069+
updateComponentText(args, GTK_COLOR_COMPONENT(args->lch_control), "lch");
10661070

10671071
transformation::Chain *chain = static_cast<transformation::Chain*>(dynv_get_pointer_wdc(args->gs->params, "TransformationChain", 0));
10681072
gtk_swatch_set_transformation_chain(GTK_SWATCH(args->swatch_display), chain);

source/gtk/ColorComponent.cpp

+93-36
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121

2222
#include "../Color.h"
2323
#include "../MathUtil.h"
24+
#include "../Paths.h"
2425
#include <math.h>
2526
#include <string.h>
2627

2728
#include <iostream>
29+
#include <vector>
2830
using namespace std;
2931

3032
#define GTK_COLOR_COMPONENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COLOR_COMPONENT, GtkColorComponentPrivate))
@@ -60,9 +62,14 @@ typedef struct GtkColorComponentPrivate{
6062
gint last_event_position;
6163
bool changing_color;
6264

65+
bool out_of_gamut_mask;
66+
6367
ReferenceIlluminant lab_illuminant;
6468
ReferenceObserver lab_observer;
6569

70+
cairo_surface_t *pattern_surface;
71+
cairo_pattern_t *pattern;
72+
6673
const char *label[MaxNumberOfComponents][2];
6774
gchar *text[MaxNumberOfComponents];
6875
double range[MaxNumberOfComponents];
@@ -121,6 +128,11 @@ static void gtk_color_component_finalize(GObject *color_obj){
121128
ns->text[i] = 0;
122129
}
123130
}
131+
if (ns->pattern_surface)
132+
cairo_surface_destroy(ns->pattern_surface);
133+
if (ns->pattern)
134+
cairo_pattern_destroy(ns->pattern);
135+
124136
gpointer parent_class = g_type_class_peek_parent(G_OBJECT_CLASS(GTK_COLOR_COMPONENT_GET_CLASS(color_obj)));
125137
G_OBJECT_CLASS(parent_class)->finalize(color_obj);
126138
}
@@ -130,11 +142,19 @@ GtkWidget *gtk_color_component_new (GtkColorComponentComp component){
130142
GtkWidget* widget = (GtkWidget*)g_object_new(GTK_TYPE_COLOR_COMPONENT, NULL);
131143
GtkColorComponentPrivate *ns = GTK_COLOR_COMPONENT_GET_PRIVATE(widget);
132144

145+
gchar* pattern_filename = build_filename("gpick-gray-pattern.png");
146+
ns->pattern_surface = cairo_image_surface_create_from_png(pattern_filename);
147+
g_free(pattern_filename);
148+
149+
ns->pattern = cairo_pattern_create_for_surface(ns->pattern_surface);
150+
cairo_pattern_set_extend(ns->pattern, CAIRO_EXTEND_REPEAT);
151+
133152
ns->component = component;
134153
ns->last_event_position = -1;
135154
ns->changing_color = false;
136155
ns->lab_illuminant = REFERENCE_ILLUMINANT_D50;
137156
ns->lab_observer= REFERENCE_OBSERVER_2;
157+
ns->out_of_gamut_mask = false;
138158

139159
for (int i = 0; i != sizeof(ns->text) / sizeof(gchar*); i++){
140160
ns->text[i] = 0;
@@ -323,6 +343,8 @@ static gboolean gtk_color_component_expose (GtkWidget *widget, GdkEventExpose *e
323343
double int_part;
324344
matrix3x3 adaptation_matrix;
325345

346+
vector<vector<bool> > out_of_gamut(MaxNumberOfComponents, vector<bool>(false, 1));
347+
326348
switch (ns->component) {
327349
case rgb:
328350
steps = 1;
@@ -477,25 +499,22 @@ static gboolean gtk_color_component_expose (GtkWidget *widget, GdkEventExpose *e
477499

478500
color_get_chromatic_adaptation_matrix(color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_reference(REFERENCE_ILLUMINANT_D65, REFERENCE_OBSERVER_2), &adaptation_matrix);
479501

480-
for (i = 0; i < 3; ++i){
481-
color_copy(&ns->color, &c[i]);
482-
}
483-
for (i = 0; i <= steps; ++i){
484-
c[0].lab.L = (i / steps) * ns->range[0] + ns->offset[0];
485-
color_lab_to_rgb(&c[0], &rgb_points[0 * (int(steps) + 1) + i], color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_inverted_sRGB_transformation_matrix(), &adaptation_matrix);
486-
color_rgb_normalize(&rgb_points[0 * (int(steps) + 1) + i]);
487-
}
502+
for (j = 0; j < 3; ++j){
503+
color_copy(&ns->color, &c[j]);
488504

489-
for (i = 0; i <= steps; ++i){
490-
c[1].lab.a = (i / steps) * ns->range[1] + ns->offset[1];
491-
color_lab_to_rgb(&c[1], &rgb_points[1 * (int(steps) + 1) + i], color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_inverted_sRGB_transformation_matrix(), &adaptation_matrix);
492-
color_rgb_normalize(&rgb_points[1 * (int(steps) + 1) + i]);
493-
}
494-
for (i = 0; i <= steps; ++i){
495-
c[2].lab.b = (i / steps) * ns->range[2] + ns->offset[2];
496-
color_lab_to_rgb(&c[2], &rgb_points[2 * (int(steps) + 1) + i], color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_inverted_sRGB_transformation_matrix(), &adaptation_matrix);
497-
color_rgb_normalize(&rgb_points[2 * (int(steps) + 1) + i]);
505+
out_of_gamut[j] = vector<bool>(steps + 1, false);
506+
507+
for (i = 0; i <= steps; ++i){
508+
c[j].ma[j] = (i / steps) * ns->range[j] + ns->offset[j];
509+
color_lab_to_rgb(&c[j], &rgb_points[j * (int(steps) + 1) + i], color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_inverted_sRGB_transformation_matrix(), &adaptation_matrix);
510+
511+
if (color_is_rgb_out_of_gamut(&rgb_points[j * (int(steps) + 1) + i])){
512+
out_of_gamut[j][i] = true;
513+
}
514+
color_rgb_normalize(&rgb_points[j * (int(steps) + 1) + i]);
515+
}
498516
}
517+
499518
for (i = 0; i < surface_width; ++i){
500519

501520
float position = modf(i * steps / surface_width, &int_part);
@@ -529,24 +548,19 @@ static gboolean gtk_color_component_expose (GtkWidget *widget, GdkEventExpose *e
529548

530549
color_get_chromatic_adaptation_matrix(color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_reference(REFERENCE_ILLUMINANT_D65, REFERENCE_OBSERVER_2), &adaptation_matrix);
531550

532-
for (i = 0; i < 3; ++i){
533-
color_copy(&ns->color, &c[i]);
534-
}
535-
for (i = 0; i <= steps; ++i){
536-
c[0].lch.L = (i / steps) * ns->range[0] + ns->offset[0];
537-
color_lch_to_rgb(&c[0], &rgb_points[0 * (int(steps) + 1) + i], color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_inverted_sRGB_transformation_matrix(), &adaptation_matrix);
538-
color_rgb_normalize(&rgb_points[0 * (int(steps) + 1) + i]);
539-
}
551+
for (j = 0; j < 3; ++j){
552+
color_copy(&ns->color, &c[j]);
540553

541-
for (i = 0; i <= steps; ++i){
542-
c[1].lch.C = (i / steps) * ns->range[1] + ns->offset[1];
543-
color_lch_to_rgb(&c[1], &rgb_points[1 * (int(steps) + 1) + i], color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_inverted_sRGB_transformation_matrix(), &adaptation_matrix);
544-
color_rgb_normalize(&rgb_points[1 * (int(steps) + 1) + i]);
545-
}
546-
for (i = 0; i <= steps; ++i){
547-
c[2].lch.h = (i / steps) * ns->range[2] + ns->offset[2];
548-
color_lch_to_rgb(&c[2], &rgb_points[2 * (int(steps) + 1) + i], color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_inverted_sRGB_transformation_matrix(), &adaptation_matrix);
549-
color_rgb_normalize(&rgb_points[2 * (int(steps) + 1) + i]);
554+
out_of_gamut[j] = vector<bool>(steps + 1, false);
555+
556+
for (i = 0; i <= steps; ++i){
557+
c[j].ma[j] = (i / steps) * ns->range[j] + ns->offset[j];
558+
color_lch_to_rgb(&c[j], &rgb_points[j * (int(steps) + 1) + i], color_get_reference(ns->lab_illuminant, ns->lab_observer), color_get_inverted_sRGB_transformation_matrix(), &adaptation_matrix);
559+
if (color_is_rgb_out_of_gamut(&rgb_points[j * (int(steps) + 1) + i])){
560+
out_of_gamut[j][i] = true;
561+
}
562+
color_rgb_normalize(&rgb_points[j * (int(steps) + 1) + i]);
563+
}
550564
}
551565
for (i = 0; i < surface_width; ++i){
552566

@@ -597,7 +611,38 @@ static gboolean gtk_color_component_expose (GtkWidget *widget, GdkEventExpose *e
597611

598612
cairo_restore(cr);
599613

614+
600615
for (i = 0; i < ns->n_components; ++i){
616+
if (ns->out_of_gamut_mask){
617+
int first_out_of_gamut = 0;
618+
bool out_of_gamut_found = false;
619+
620+
cairo_matrix_t matrix;
621+
cairo_matrix_init_translate(&matrix, -offset_x, i * 8);
622+
cairo_pattern_set_matrix(ns->pattern, &matrix);
623+
cairo_set_source(cr, ns->pattern);
624+
625+
for (int j = 0; j < out_of_gamut[i].size(); j++){
626+
if (out_of_gamut[i][j]){
627+
if (!out_of_gamut_found){
628+
out_of_gamut_found = true;
629+
first_out_of_gamut = j;
630+
}
631+
}else{
632+
if (out_of_gamut_found){
633+
cairo_rectangle(cr, offset_x + (first_out_of_gamut * 200.0 / out_of_gamut[i].size()), 16 * i, (j - first_out_of_gamut) * 200.0 / out_of_gamut[i].size(), 15);
634+
cairo_fill(cr);
635+
out_of_gamut_found = false;
636+
}
637+
}
638+
}
639+
640+
if (out_of_gamut_found){
641+
cairo_rectangle(cr, offset_x + (first_out_of_gamut * 200.0 / out_of_gamut[i].size()), 16 * i, (out_of_gamut[i].size() - first_out_of_gamut) * 200.0 / out_of_gamut[i].size(), 15);
642+
cairo_fill(cr);
643+
}
644+
}
645+
601646
cairo_move_to(cr, offset_x + 200 * pointer_pos[i], 16 * i + 9);
602647
cairo_line_to(cr, offset_x + 200 * pointer_pos[i] + 3, 16 * i + 16);
603648
cairo_line_to(cr, offset_x + 200 * pointer_pos[i] - 3, 16 * i + 16);
@@ -656,8 +701,7 @@ static gboolean gtk_color_component_expose (GtkWidget *widget, GdkEventExpose *e
656701
pango_font_description_free(font_description);
657702
}
658703
}
659-
660-
cairo_destroy (cr);
704+
cairo_destroy(cr);
661705

662706
return TRUE;
663707
}
@@ -811,3 +855,16 @@ void gtk_color_component_set_lab_observer(GtkColorComponent* color_component, Re
811855
gtk_widget_queue_draw(GTK_WIDGET(color_component));
812856
}
813857

858+
void gtk_color_component_set_out_of_gamut_mask(GtkColorComponent* color_component, bool mask_enabled)
859+
{
860+
GtkColorComponentPrivate *ns = GTK_COLOR_COMPONENT_GET_PRIVATE(color_component);
861+
ns->out_of_gamut_mask = mask_enabled;
862+
gtk_widget_queue_draw(GTK_WIDGET(color_component));
863+
}
864+
865+
bool gtk_color_component_get_out_of_gamut_mask(GtkColorComponent* color_component)
866+
{
867+
GtkColorComponentPrivate *ns = GTK_COLOR_COMPONENT_GET_PRIVATE(color_component);
868+
return ns->out_of_gamut_mask;
869+
}
870+

source/gtk/ColorComponent.h

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ void gtk_color_component_set_raw_color(GtkColorComponent* color_component, Color
7070
void gtk_color_component_get_transformed_color(GtkColorComponent* color_component, Color* color);
7171
void gtk_color_component_set_transformed_color(GtkColorComponent* color_component, Color* color);
7272

73+
void gtk_color_component_set_out_of_gamut_mask(GtkColorComponent* color_component, bool mask_enabled);
74+
bool gtk_color_component_get_out_of_gamut_mask(GtkColorComponent* color_component);
75+
7376
void gtk_color_component_set_lab_illuminant(GtkColorComponent* color_component, ReferenceIlluminant illuminant);
7477
void gtk_color_component_set_lab_observer(GtkColorComponent* color_component, ReferenceObserver observer);
7578

0 commit comments

Comments
 (0)