| 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
|
|
265858
|
use v5.12; |
|
|
14
|
|
|
|
|
54
|
|
|
6
|
14
|
|
|
14
|
|
101
|
use warnings; |
|
|
14
|
|
|
|
|
26
|
|
|
|
14
|
|
|
|
|
30691
|
|
|
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
|
192
|
100
|
|
192
|
0
|
47752
|
sub is_space_name { (ref get_space($_[0])) ? 1 : 0 } |
|
18
|
122
|
|
|
122
|
0
|
3972
|
sub all_space_names { sort keys %space_obj } |
|
19
|
129
|
|
|
129
|
0
|
1213
|
sub default_space_name { $default_space_name } |
|
20
|
898
|
|
|
898
|
1
|
1788
|
sub default_space { get_space( $default_space_name ) } |
|
21
|
6451
|
100
|
100
|
6451
|
1
|
35548
|
sub get_space { (defined $_[0] and exists $space_obj{ uc $_[0] }) ? $space_obj{ uc $_[0] } : '' } |
|
22
|
|
|
|
|
|
|
sub try_get_space { |
|
23
|
2051
|
|
66
|
2051
|
1
|
5839
|
my $name = shift || $default_space_name; |
|
24
|
2051
|
|
|
|
|
3923
|
my $space = get_space( $name ); |
|
25
|
2051
|
100
|
66
|
|
|
5888
|
return $name if ref $name eq 'Graphics::Toolkit::Color::Space' and is_space_name( $name->name ); |
|
26
|
1991
|
100
|
|
|
|
5348
|
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
|
492
|
my $space = shift; |
|
32
|
253
|
50
|
|
|
|
851
|
return 'got no Graphics::Toolkit::Color::Space object' if ref $space ne 'Graphics::Toolkit::Color::Space'; |
|
33
|
253
|
|
|
|
|
674
|
my $name = $space->name; |
|
34
|
253
|
50
|
|
|
|
580
|
return "space objct has no name" unless $name; |
|
35
|
253
|
50
|
|
|
|
524
|
return "color space name $name is already taken" if ref get_space( $name ); |
|
36
|
253
|
|
|
|
|
711
|
my @converter_target = $space->converter_names; |
|
37
|
253
|
50
|
66
|
|
|
733
|
return "can not add color space $name, it has no converter" unless @converter_target or $name eq $default_space_name; |
|
38
|
253
|
|
|
|
|
484
|
for my $converter_target (@converter_target){ |
|
39
|
239
|
|
|
|
|
410
|
my $target_space = get_space( $converter_target ); |
|
40
|
239
|
50
|
|
|
|
573
|
return "space object $name does convert into $converter_target, which is no known color space" unless $target_space; |
|
41
|
239
|
100
|
|
|
|
600
|
$space->alias_converter_name( $converter_target, $target_space->alias ) if $target_space->alias; |
|
42
|
|
|
|
|
|
|
} |
|
43
|
253
|
|
|
|
|
739
|
$space_obj{ uc $name } = $space; |
|
44
|
253
|
100
|
66
|
|
|
521
|
$space_obj{ uc $space->alias } = $space if $space->alias and not ref get_space( $space->alias ); |
|
45
|
253
|
|
|
|
|
134684
|
return 1; |
|
46
|
|
|
|
|
|
|
} |
|
47
|
|
|
|
|
|
|
sub remove_space { |
|
48
|
3
|
|
|
3
|
0
|
928
|
my $name = shift; |
|
49
|
3
|
50
|
33
|
|
|
21
|
return "need name of color space as argument in order to remove the space" unless defined $name and $name; |
|
50
|
3
|
|
|
|
|
8
|
my $space = get_space( $name ); |
|
51
|
3
|
100
|
|
|
|
20
|
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
|
446
|
|
|
446
|
1
|
295221
|
my ($values, $target_space_name, $want_result_normalized, $source_space_name, $source_values) = @_; |
|
59
|
446
|
|
|
|
|
880
|
my $target_space = try_get_space( $target_space_name ); |
|
60
|
446
|
|
|
|
|
928
|
my $source_space = try_get_space( $source_space_name ); |
|
61
|
446
|
|
100
|
|
|
1133
|
$want_result_normalized //= 0; |
|
62
|
446
|
100
|
|
|
|
885
|
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
|
443
|
100
|
|
|
|
1112
|
return $target_space unless ref $target_space; |
|
65
|
442
|
50
|
50
|
|
|
1895
|
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
|
442
|
50
|
66
|
|
|
1292
|
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
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# none conversion cases |
|
71
|
442
|
100
|
100
|
|
|
1312
|
$values = $source_values if ref $source_values and $source_space eq $target_space; |
|
72
|
442
|
100
|
100
|
|
|
1131
|
if ($target_space->name eq default_space()->name or $source_space eq $target_space) { |
|
73
|
329
|
100
|
|
|
|
1861
|
return ($want_result_normalized) ? $values : $target_space->round($target_space->denormalize( $values )); |
|
74
|
|
|
|
|
|
|
} |
|
75
|
|
|
|
|
|
|
# find conversion chain |
|
76
|
113
|
|
|
|
|
304
|
my $current_space = $target_space; |
|
77
|
113
|
|
|
|
|
297
|
my @convert_chain = ($target_space->name); |
|
78
|
113
|
|
|
|
|
281
|
while ($current_space->name ne $default_space_name ){ |
|
79
|
128
|
|
|
|
|
425
|
my ($next_space_name, @next_options) = $current_space->converter_names; |
|
80
|
128
|
|
66
|
|
|
471
|
$next_space_name = shift @next_options while @next_options and $next_space_name ne $default_space_name; |
|
81
|
128
|
100
|
|
|
|
335
|
unshift @convert_chain, $next_space_name if $next_space_name ne $default_space_name; |
|
82
|
128
|
|
|
|
|
255
|
$current_space = get_space( $next_space_name ); |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
# actual conversion |
|
85
|
113
|
|
|
|
|
194
|
my $values_are_normal = 1; |
|
86
|
113
|
|
|
|
|
236
|
my $space_name_before = default_space_name(); |
|
87
|
113
|
|
|
|
|
337
|
for my $space_name (@convert_chain){ |
|
88
|
128
|
|
|
|
|
234
|
my $current_space = get_space( $space_name ); |
|
89
|
128
|
100
|
|
|
|
394
|
if ($current_space eq $source_space){ |
|
90
|
1
|
|
|
|
|
3
|
$values = $source_values; |
|
91
|
1
|
|
|
|
|
2
|
$values_are_normal = 1; |
|
92
|
|
|
|
|
|
|
} else { |
|
93
|
127
|
|
|
|
|
412
|
my @normal_in_out = $current_space->converter_normal_states( 'from', $space_name_before ); |
|
94
|
127
|
0
|
33
|
|
|
366
|
$values = $current_space->normalize( $values ) if not $values_are_normal and $normal_in_out[0]; |
|
95
|
127
|
50
|
33
|
|
|
517
|
$values = $current_space->denormalize( $values ) if $values_are_normal and not $normal_in_out[0]; |
|
96
|
127
|
|
|
|
|
338
|
$values = $current_space->convert_from( $space_name_before, $values); |
|
97
|
127
|
|
|
|
|
300
|
$values_are_normal = $normal_in_out[1]; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
128
|
|
|
|
|
339
|
$space_name_before = $current_space->name; |
|
100
|
|
|
|
|
|
|
} |
|
101
|
113
|
50
|
33
|
|
|
391
|
$values = $target_space->normalize( $values ) if not $values_are_normal and $want_result_normalized; |
|
102
|
113
|
100
|
66
|
|
|
544
|
$values = $target_space->denormalize( $values ) if $values_are_normal and not $want_result_normalized; |
|
103
|
113
|
100
|
|
|
|
448
|
return $target_space->clamp( $values, ($want_result_normalized ? 'normal' : undef)); |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
sub deconvert { # normalizd value tuple --> RGB tuple |
|
106
|
133
|
|
|
133
|
1
|
15163
|
my ($space_name, $values, $want_result_normalized) = @_; |
|
107
|
133
|
100
|
|
|
|
439
|
return "need a space name to convert to as first argument" unless defined $space_name; |
|
108
|
132
|
|
|
|
|
368
|
my $original_space = try_get_space( $space_name ); |
|
109
|
132
|
100
|
|
|
|
347
|
return $original_space unless ref $original_space; |
|
110
|
130
|
100
|
66
|
|
|
768
|
return "need an ARRAY ref with 3 or 4 values as first argument in order to deconvert them" |
|
|
|
|
100
|
|
|
|
|
|
111
|
|
|
|
|
|
|
unless ref $values eq 'ARRAY' and (@$values == 3 or @$values == 4); |
|
112
|
129
|
|
100
|
|
|
331
|
$want_result_normalized //= 0; |
|
113
|
129
|
100
|
|
|
|
410
|
if ($original_space->name eq $default_space_name) { # nothing to convert |
|
114
|
2
|
100
|
|
|
|
12
|
return ($want_result_normalized) ? $values : $original_space->round( $original_space->denormalize( $values )); |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
|
|
117
|
127
|
|
|
|
|
256
|
my $current_space = $original_space; |
|
118
|
127
|
|
|
|
|
204
|
my $values_are_normal = 1; |
|
119
|
127
|
|
|
|
|
321
|
while (uc $current_space->name ne $default_space_name){ |
|
120
|
142
|
|
|
|
|
491
|
my ($next_space_name, @next_options) = $current_space->converter_names; |
|
121
|
142
|
|
66
|
|
|
608
|
$next_space_name = shift @next_options while @next_options and $next_space_name ne $default_space_name; |
|
122
|
142
|
|
|
|
|
471
|
my @normal_in_out = $current_space->converter_normal_states( 'to', $next_space_name ); |
|
123
|
142
|
0
|
33
|
|
|
406
|
$values = $current_space->normalize( $values ) if not $values_are_normal and $normal_in_out[0]; |
|
124
|
142
|
50
|
33
|
|
|
612
|
$values = $current_space->denormalize( $values ) if $values_are_normal and not $normal_in_out[0]; |
|
125
|
142
|
|
|
|
|
505
|
$values = $current_space->convert_to( $next_space_name, $values); |
|
126
|
142
|
|
|
|
|
302
|
$values_are_normal = $normal_in_out[1]; |
|
127
|
142
|
|
|
|
|
330
|
$current_space = get_space( $next_space_name ); |
|
128
|
|
|
|
|
|
|
} |
|
129
|
127
|
100
|
|
|
|
554
|
return ($want_result_normalized) ? $values : $current_space->round( $current_space->denormalize( $values )); |
|
130
|
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub deformat { # formatted color def --> normalized values |
|
133
|
112
|
|
|
112
|
1
|
309423
|
my ($color_def, $ranges, $suffix) = @_; |
|
134
|
112
|
50
|
|
|
|
311
|
return 'got no color definition' unless defined $color_def; |
|
135
|
112
|
|
|
|
|
238
|
my ($values, $original_space, $format_name); |
|
136
|
112
|
|
|
|
|
283
|
for my $space_name (all_space_names()) { |
|
137
|
2075
|
|
|
|
|
4296
|
my $color_space = get_space( $space_name ); |
|
138
|
2075
|
|
|
|
|
5999
|
($values, $format_name) = $color_space->deformat( $color_def ); |
|
139
|
2075
|
100
|
|
|
|
5179
|
if (defined $format_name){ |
|
140
|
65
|
|
|
|
|
105
|
$original_space = $color_space; |
|
141
|
65
|
|
|
|
|
160
|
last; |
|
142
|
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
} |
|
144
|
112
|
100
|
|
|
|
960
|
return 'could not deformat color definition: "$color_def"' unless ref $original_space; |
|
145
|
65
|
|
|
|
|
266
|
return $values, $original_space->name, $format_name; |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
sub deformat_partial_hash { # convert partial hash into |
|
148
|
37
|
|
|
37
|
1
|
19187
|
my ($value_hash, $space_name) = @_; |
|
149
|
37
|
100
|
|
|
|
131
|
return unless ref $value_hash eq 'HASH'; |
|
150
|
36
|
|
|
|
|
83
|
my $space = try_get_space( $space_name ); |
|
151
|
36
|
50
|
|
|
|
83
|
return $space unless ref $space; |
|
152
|
36
|
100
|
66
|
|
|
247
|
my @space_name_options = (defined $space_name and $space_name) ? ($space->name) : (@search_order); |
|
153
|
36
|
|
|
|
|
85
|
for my $space_name (@space_name_options) { |
|
154
|
223
|
|
|
|
|
358
|
my $color_space = get_space( $space_name ); |
|
155
|
223
|
|
|
|
|
484
|
my $values = $color_space->tuple_from_partial_hash( $value_hash ); |
|
156
|
223
|
100
|
|
|
|
491
|
next unless ref $values; |
|
157
|
23
|
50
|
|
|
|
109
|
return wantarray ? ($values, $color_space->name) : $values; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
13
|
|
|
|
|
65
|
return undef; |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub distance { # @c1 @c2 -- ~space ~select @range --> + |
|
163
|
28
|
|
|
28
|
0
|
139
|
my ($values_a, $values_b, $space_name, $select_axis, $range) = @_; |
|
164
|
28
|
|
|
|
|
71
|
my $color_space = try_get_space( $space_name ); |
|
165
|
28
|
50
|
|
|
|
72
|
return $color_space unless ref $color_space; |
|
166
|
28
|
|
|
|
|
71
|
$values_a = convert( $values_a, $space_name, 'normal' ); |
|
167
|
28
|
|
|
|
|
72
|
$values_b = convert( $values_b, $space_name, 'normal' ); |
|
168
|
28
|
|
|
|
|
110
|
my $delta = $color_space->delta( $values_a, $values_b ); |
|
169
|
28
|
|
|
|
|
89
|
$delta = $color_space->denormalize_delta( $delta, $range ); |
|
170
|
28
|
100
|
|
|
|
123
|
if (defined $select_axis){ |
|
171
|
17
|
100
|
|
|
|
61
|
$select_axis = [$select_axis] unless ref $select_axis; |
|
172
|
21
|
|
|
|
|
70
|
my @selected_values = grep {defined $_} |
|
173
|
17
|
|
|
|
|
39
|
map {$color_space->select_tuple_value_from_name($_, $delta) } @$select_axis; |
|
|
21
|
|
|
|
|
65
|
|
|
174
|
17
|
|
|
|
|
46
|
$delta = \@selected_values; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
28
|
|
|
|
|
44
|
my $d = 0; |
|
177
|
28
|
|
|
|
|
137
|
$d += $_ * $_ for @$delta; |
|
178
|
28
|
|
|
|
|
307
|
return sqrt $d; |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
1; |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
__END__ |