| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
# store all clolor space objects, to convert check, convert and measure color values |
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Graphics::Toolkit::Color::Space::Hub; |
|
5
|
14
|
|
|
14
|
|
187244
|
use v5.12; |
|
|
14
|
|
|
|
|
41
|
|
|
6
|
14
|
|
|
14
|
|
64
|
use warnings; |
|
|
14
|
|
|
|
|
32
|
|
|
|
14
|
|
|
|
|
22035
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
#### internal space loading ############################################ |
|
9
|
|
|
|
|
|
|
our $default_space_name = 'RGB'; |
|
10
|
|
|
|
|
|
|
my @search_order = ($default_space_name, |
|
11
|
|
|
|
|
|
|
qw/CMY CMYK HSL HSV HSB HWB NCol YIQ YUV/, |
|
12
|
|
|
|
|
|
|
qw/CIEXYZ CIELAB CIELUV CIELCHab CIELCHuv OKLAB OKLCH HunterLAB/); |
|
13
|
|
|
|
|
|
|
my %space_obj; |
|
14
|
|
|
|
|
|
|
add_space( require "Graphics/Toolkit/Color/Space/Instance/$_.pm" ) for @search_order; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
#### space API ######################################################### |
|
17
|
198
|
100
|
|
198
|
0
|
25264
|
sub is_space_name { (ref get_space($_[0])) ? 1 : 0 } |
|
18
|
128
|
|
|
128
|
0
|
3217
|
sub all_space_names { sort keys %space_obj } |
|
19
|
131
|
|
|
131
|
0
|
1230
|
sub default_space_name { $default_space_name } |
|
20
|
944
|
|
|
944
|
1
|
1294
|
sub default_space { get_space( $default_space_name ) } |
|
21
|
6728
|
100
|
100
|
6728
|
1
|
27210
|
sub get_space { (defined $_[0] and exists $space_obj{ uc $_[0] }) ? $space_obj{ uc $_[0] } : '' } |
|
22
|
|
|
|
|
|
|
sub try_get_space { |
|
23
|
2135
|
|
66
|
2135
|
1
|
4253
|
my $name = shift || $default_space_name; |
|
24
|
2135
|
|
|
|
|
2688
|
my $space = get_space( $name ); |
|
25
|
2135
|
100
|
66
|
|
|
4139
|
return $name if ref $name eq 'Graphics::Toolkit::Color::Space' and is_space_name( $name->name ); |
|
26
|
2075
|
100
|
|
|
|
3750
|
return (ref $space) ? $space |
|
27
|
|
|
|
|
|
|
: "$name is an unknown color space, try one of: ".(join ', ', all_space_names()); |
|
28
|
|
|
|
|
|
|
} |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub add_space { |
|
31
|
253
|
|
|
253
|
0
|
325
|
my $space = shift; |
|
32
|
253
|
50
|
|
|
|
492
|
return 'got no Graphics::Toolkit::Color::Space object' if ref $space ne 'Graphics::Toolkit::Color::Space'; |
|
33
|
253
|
|
|
|
|
477
|
my $name = $space->name; |
|
34
|
253
|
50
|
|
|
|
427
|
return "space objct has no name" unless $name; |
|
35
|
253
|
50
|
|
|
|
416
|
return "color space name $name is already taken" if ref get_space( $name ); |
|
36
|
253
|
|
|
|
|
507
|
my @converter_target = $space->converter_names; |
|
37
|
253
|
50
|
66
|
|
|
508
|
return "can not add color space $name, it has no converter" unless @converter_target or $name eq $default_space_name; |
|
38
|
253
|
|
|
|
|
351
|
for my $converter_target (@converter_target){ |
|
39
|
239
|
|
|
|
|
295
|
my $target_space = get_space( $converter_target ); |
|
40
|
239
|
50
|
|
|
|
416
|
return "space object $name does convert into $converter_target, which is no known color space" unless $target_space; |
|
41
|
239
|
100
|
|
|
|
390
|
$space->alias_converter_name( $converter_target, $target_space->alias ) if $target_space->alias; |
|
42
|
|
|
|
|
|
|
} |
|
43
|
253
|
|
|
|
|
490
|
$space_obj{ uc $name } = $space; |
|
44
|
253
|
100
|
66
|
|
|
362
|
$space_obj{ uc $space->alias } = $space if $space->alias and not ref get_space( $space->alias ); |
|
45
|
253
|
|
|
|
|
99344
|
return 1; |
|
46
|
|
|
|
|
|
|
} |
|
47
|
|
|
|
|
|
|
sub remove_space { |
|
48
|
3
|
|
|
3
|
0
|
606
|
my $name = shift; |
|
49
|
3
|
50
|
33
|
|
|
22
|
return "need name of color space as argument in order to remove the space" unless defined $name and $name; |
|
50
|
3
|
|
|
|
|
33
|
my $space = get_space( $name ); |
|
51
|
3
|
100
|
|
|
|
19
|
return "can not remove unknown color space: $name" unless ref $space; |
|
52
|
1
|
50
|
|
|
|
5
|
delete $space_obj{ uc $space->alias } if $space->alias; |
|
53
|
1
|
|
|
|
|
5
|
delete $space_obj{ uc $space->name }; |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
#### value API ######################################################### |
|
57
|
|
|
|
|
|
|
sub convert { # normalized RGB tuple, ~space_name --> ?normalized tuple in wanted space |
|
58
|
469
|
|
|
469
|
1
|
195176
|
my ($values, $target_space_name, $want_result_normalized, $source_space_name, $source_values) = @_; |
|
59
|
469
|
|
|
|
|
629
|
my $target_space = try_get_space( $target_space_name ); |
|
60
|
469
|
|
|
|
|
672
|
my $source_space = try_get_space( $source_space_name ); |
|
61
|
469
|
|
100
|
|
|
877
|
$want_result_normalized //= 0; |
|
62
|
469
|
100
|
|
|
|
703
|
return "need an ARRAY ref with 3 RGB values as first argument in order to convert them" |
|
63
|
|
|
|
|
|
|
unless default_space()->is_value_tuple( $values ); |
|
64
|
466
|
100
|
|
|
|
872
|
return $target_space unless ref $target_space; |
|
65
|
465
|
50
|
50
|
|
|
1461
|
return "arguments source_space_name and source_values have to be provided both or none." |
|
66
|
|
|
|
|
|
|
if defined $source_space_name xor defined $source_values; |
|
67
|
465
|
50
|
66
|
|
|
930
|
return "argument source_values has to be a tuple, if provided" |
|
68
|
|
|
|
|
|
|
if $source_values and not $source_space->is_value_tuple( $source_values ); |
|
69
|
465
|
|
|
|
|
1004
|
$values = [@$values]; |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# none conversion cases |
|
72
|
465
|
100
|
100
|
|
|
963
|
$values = $source_values if ref $source_values and $source_space eq $target_space; |
|
73
|
465
|
100
|
100
|
|
|
798
|
if ($target_space->name eq default_space()->name or $source_space eq $target_space) { |
|
74
|
350
|
100
|
|
|
|
1242
|
return ($want_result_normalized) ? $values : $target_space->round($target_space->denormalize( $values )); |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
# find conversion chain |
|
77
|
115
|
|
|
|
|
243
|
my $current_space = $target_space; |
|
78
|
115
|
|
|
|
|
212
|
my @convert_chain = ($target_space->name); |
|
79
|
115
|
|
|
|
|
247
|
while ($current_space->name ne $default_space_name ){ |
|
80
|
130
|
|
|
|
|
306
|
my ($next_space_name, @next_options) = $current_space->converter_names; |
|
81
|
130
|
|
100
|
|
|
580
|
$next_space_name = shift @next_options while @next_options and $next_space_name ne $default_space_name; |
|
82
|
130
|
100
|
|
|
|
315
|
unshift @convert_chain, $next_space_name if $next_space_name ne $default_space_name; |
|
83
|
130
|
|
|
|
|
244
|
$current_space = get_space( $next_space_name ); |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
# actual conversion |
|
86
|
115
|
|
|
|
|
168
|
my $values_are_normal = 1; |
|
87
|
115
|
|
|
|
|
208
|
my $space_name_before = default_space_name(); |
|
88
|
115
|
|
|
|
|
247
|
for my $space_name (@convert_chain){ |
|
89
|
130
|
|
|
|
|
206
|
my $current_space = get_space( $space_name ); |
|
90
|
130
|
100
|
|
|
|
357
|
if ($current_space eq $source_space){ |
|
91
|
1
|
|
|
|
|
2
|
$values = $source_values; |
|
92
|
1
|
|
|
|
|
2
|
$values_are_normal = 1; |
|
93
|
|
|
|
|
|
|
} else { |
|
94
|
129
|
|
|
|
|
378
|
my @normal_in_out = $current_space->converter_normal_states( 'from', $space_name_before ); |
|
95
|
129
|
0
|
33
|
|
|
264
|
$values = $current_space->normalize( $values ) if not $values_are_normal and $normal_in_out[0]; |
|
96
|
129
|
50
|
33
|
|
|
432
|
$values = $current_space->denormalize( $values ) if $values_are_normal and not $normal_in_out[0]; |
|
97
|
129
|
|
|
|
|
282
|
$values = $current_space->convert_from( $space_name_before, $values); |
|
98
|
129
|
|
|
|
|
240
|
$values_are_normal = $normal_in_out[1]; |
|
99
|
|
|
|
|
|
|
} |
|
100
|
130
|
|
|
|
|
272
|
$space_name_before = $current_space->name; |
|
101
|
|
|
|
|
|
|
} |
|
102
|
115
|
50
|
33
|
|
|
247
|
$values = $target_space->normalize( $values ) if not $values_are_normal and $want_result_normalized; |
|
103
|
115
|
100
|
66
|
|
|
370
|
$values = $target_space->denormalize( $values ) if $values_are_normal and not $want_result_normalized; |
|
104
|
115
|
100
|
|
|
|
309
|
return $target_space->clamp( $values, ($want_result_normalized ? 'normal' : undef)); |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
sub deconvert { # normalizd value tuple --> RGB tuple |
|
107
|
136
|
|
|
136
|
1
|
9281
|
my ($space_name, $values, $want_result_normalized) = @_; |
|
108
|
136
|
100
|
|
|
|
273
|
return "need a space name to convert to as first argument" unless defined $space_name; |
|
109
|
135
|
|
|
|
|
289
|
my $original_space = try_get_space( $space_name ); |
|
110
|
135
|
100
|
|
|
|
271
|
return $original_space unless ref $original_space; |
|
111
|
133
|
100
|
66
|
|
|
600
|
return "need an ARRAY ref with 3 or 4 values as first argument in order to deconvert them" |
|
|
|
|
100
|
|
|
|
|
|
112
|
|
|
|
|
|
|
unless ref $values eq 'ARRAY' and (@$values == 3 or @$values == 4); |
|
113
|
132
|
|
100
|
|
|
276
|
$want_result_normalized //= 0; |
|
114
|
132
|
100
|
|
|
|
241
|
if ($original_space->name eq $default_space_name) { # nothing to convert |
|
115
|
2
|
100
|
|
|
|
6
|
return ($want_result_normalized) ? $values : $original_space->round( $original_space->denormalize( $values )); |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
|
|
118
|
130
|
|
|
|
|
188
|
my $current_space = $original_space; |
|
119
|
130
|
|
|
|
|
185
|
my $values_are_normal = 1; |
|
120
|
130
|
|
|
|
|
213
|
while (uc $current_space->name ne $default_space_name){ |
|
121
|
145
|
|
|
|
|
298
|
my ($next_space_name, @next_options) = $current_space->converter_names; |
|
122
|
145
|
|
100
|
|
|
604
|
$next_space_name = shift @next_options while @next_options and $next_space_name ne $default_space_name; |
|
123
|
145
|
|
|
|
|
343
|
my @normal_in_out = $current_space->converter_normal_states( 'to', $next_space_name ); |
|
124
|
145
|
0
|
33
|
|
|
288
|
$values = $current_space->normalize( $values ) if not $values_are_normal and $normal_in_out[0]; |
|
125
|
145
|
50
|
33
|
|
|
413
|
$values = $current_space->denormalize( $values ) if $values_are_normal and not $normal_in_out[0]; |
|
126
|
145
|
|
|
|
|
297
|
$values = $current_space->convert_to( $next_space_name, $values); |
|
127
|
145
|
|
|
|
|
184
|
$values_are_normal = $normal_in_out[1]; |
|
128
|
145
|
|
|
|
|
229
|
$current_space = get_space( $next_space_name ); |
|
129
|
|
|
|
|
|
|
} |
|
130
|
130
|
100
|
|
|
|
394
|
return ($want_result_normalized) ? $values : $current_space->round( $current_space->denormalize( $values )); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub deformat { # formatted color def --> normalized values |
|
134
|
118
|
|
|
118
|
1
|
270599
|
my ($color_def, $ranges, $suffix) = @_; |
|
135
|
118
|
50
|
|
|
|
324
|
return 'got no color definition' unless defined $color_def; |
|
136
|
118
|
|
|
|
|
225
|
my ($values, $original_space, $format_name); |
|
137
|
118
|
|
|
|
|
278
|
for my $space_name (all_space_names()) { |
|
138
|
2204
|
|
|
|
|
3948
|
my $color_space = get_space( $space_name ); |
|
139
|
2204
|
|
|
|
|
5401
|
($values, $format_name) = $color_space->deformat( $color_def ); |
|
140
|
2204
|
100
|
|
|
|
4631
|
if (defined $format_name){ |
|
141
|
71
|
|
|
|
|
118
|
$original_space = $color_space; |
|
142
|
71
|
|
|
|
|
148
|
last; |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
} |
|
145
|
118
|
100
|
|
|
|
951
|
return 'could not deformat color definition: "$color_def"' unless ref $original_space; |
|
146
|
71
|
|
|
|
|
291
|
return $values, $original_space->name, $format_name; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
sub deformat_partial_hash { # convert partial hash into |
|
149
|
40
|
|
|
40
|
1
|
32007
|
my ($value_hash, $space_name) = @_; |
|
150
|
40
|
100
|
|
|
|
141
|
return unless ref $value_hash eq 'HASH'; |
|
151
|
39
|
|
|
|
|
80
|
my $space = try_get_space( $space_name ); |
|
152
|
39
|
50
|
|
|
|
107
|
return $space unless ref $space; |
|
153
|
39
|
100
|
66
|
|
|
217
|
my @space_name_options = (defined $space_name and $space_name) ? ($space->name) : (@search_order); |
|
154
|
39
|
|
|
|
|
72
|
for my $space_name (@space_name_options) { |
|
155
|
209
|
|
|
|
|
298
|
my $color_space = get_space( $space_name ); |
|
156
|
209
|
|
|
|
|
412
|
my $values = $color_space->tuple_from_partial_hash( $value_hash ); |
|
157
|
209
|
100
|
|
|
|
418
|
next unless ref $values; |
|
158
|
25
|
50
|
|
|
|
79
|
return wantarray ? ($values, $color_space->name) : $values; |
|
159
|
|
|
|
|
|
|
} |
|
160
|
14
|
|
|
|
|
56
|
return undef; |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub distance { # @c1 @c2 -- ~space ~select @range --> + |
|
164
|
28
|
|
|
28
|
0
|
93
|
my ($values_a, $values_b, $space_name, $select_axis, $range) = @_; |
|
165
|
28
|
|
|
|
|
63
|
my $color_space = try_get_space( $space_name ); |
|
166
|
28
|
50
|
|
|
|
68
|
return $color_space unless ref $color_space; |
|
167
|
28
|
|
|
|
|
106
|
$values_a = convert( $values_a, $space_name, 'normal' ); |
|
168
|
28
|
|
|
|
|
63
|
$values_b = convert( $values_b, $space_name, 'normal' ); |
|
169
|
28
|
|
|
|
|
103
|
my $delta = $color_space->delta( $values_a, $values_b ); |
|
170
|
28
|
|
|
|
|
68
|
$delta = $color_space->denormalize_delta( $delta, $range ); |
|
171
|
28
|
100
|
|
|
|
63
|
if (defined $select_axis){ |
|
172
|
17
|
100
|
|
|
|
77
|
$select_axis = [$select_axis] unless ref $select_axis; |
|
173
|
21
|
|
|
|
|
49
|
my @selected_values = grep {defined $_} map {$delta->[$_]} |
|
|
21
|
|
|
|
|
40
|
|
|
174
|
17
|
|
|
|
|
38
|
grep {defined $_} map {$color_space->pos_from_axis_name($_)} @$select_axis; |
|
|
21
|
|
|
|
|
47
|
|
|
|
21
|
|
|
|
|
57
|
|
|
175
|
17
|
|
|
|
|
43
|
$delta = \@selected_values; |
|
176
|
|
|
|
|
|
|
} |
|
177
|
28
|
|
|
|
|
43
|
my $d = 0; |
|
178
|
28
|
|
|
|
|
90
|
$d += $_ * $_ for @$delta; |
|
179
|
28
|
|
|
|
|
284
|
return sqrt $d; |
|
180
|
|
|
|
|
|
|
} |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
1; |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
__END__ |