File Coverage

lib/Graphics/Toolkit/Color/Space/Instance/OKHSV.pm
Criterion Covered Total %
statement 59 59 100.0
branch 5 6 83.3
condition n/a
subroutine 6 6 100.0
pod 0 2 0.0
total 70 73 95.8


line stmt bran cond sub pod time code
1              
2             # OKHSV color space, converter under Copyright (c) 2021 Björn Ottosson, see LICENSE.OK
3              
4             package Graphics::Toolkit::Color::Space::Instance::OKHSV;
5 16     16   257890 use v5.12;
  16         46  
6 16     16   67 use warnings;
  16         19  
  16         858  
7 16     16   445 use Graphics::Toolkit::Color::Space qw/max spow/;
  16         36  
  16         945  
8             use Graphics::Toolkit::Color::Space::Instance::Helper::OK
9 16     16   497 qw/find_cusp to_ST toe toe_inv oklab_to_linear_srgb linear_srgb_to_oklab/;
  16         19  
  16         9622  
10             my $PI = 4 * atan2(1, 1);
11              
12              
13             sub from_hsv {
14 8     8 0 12 my ($hsv) = @_;
15 8 100       18 return [0, 0, 0] if $hsv->[2] <= 0; # black (v == 0)
16              
17 7         15 my $a = cos(2 * $PI * $hsv->[0]); # Farbtonrichtung
18 7         10 my $b = sin(2 * $PI * $hsv->[0]);
19              
20 7         15 my ($L_cusp, $C_cusp) = find_cusp($a, $b);
21 7         14 my ($S_max, $T_max) = to_ST($L_cusp, $C_cusp);
22 7         8 my $S_0 = 0.5;
23 7         9 my $k = 1 - $S_0 / $S_max;
24              
25             # L, C als waere das Gamut ein perfektes Dreieck (Werte bei v == 1)
26 7         11 my $denom = $S_0 + $T_max - $T_max * $k * $hsv->[1];
27 7 50       10 $denom = 1e-6 if $denom < 1e-6;
28              
29 7         11 my $L_v = 1 - $hsv->[1] * $S_0 / $denom;
30 7         10 my $C_v = $hsv->[1] * $T_max * $S_0 / $denom;
31              
32 7         8 my $L = $hsv->[2] * $L_v;
33 7         7 my $C = $hsv->[2] * $C_v;
34              
35             # Kompensation fuer toe und gekruemmte Dreiecksspitze
36 7         10 my $L_vt = toe_inv($L_v);
37 7         9 my $C_vt = $C_v * $L_vt / $L_v;
38              
39 7         10 my $L_new = toe_inv($L);
40 7         7 $C = $C * $L_new / $L;
41 7         8 $L = $L_new;
42              
43 7         14 my $rgb_scale = oklab_to_linear_srgb([$L_vt, $a * $C_vt, $b * $C_vt]);
44 7         13 my $scale_L = spow(1 / max(@$rgb_scale, 0), 1/3);
45              
46 7         9 $L *= $scale_L;
47 7         7 $C *= $scale_L;
48              
49 7         13 return oklab_to_linear_srgb([$L, $C * $a, $C * $b]);
50             }
51              
52             sub to_hsv {
53 8     8 0 10 my ($rgb) = @_; # bereits LINEARES sRGB
54 8         16 my $lab = linear_srgb_to_oklab($rgb); # roh, a/b um 0 zentriert
55              
56 8         22 my $C = spow($lab->[1]**2 + $lab->[2]**2, 1/2); # Chroma
57 8 100       14 return [0, 0, toe($lab->[0])] if $C < 1e-9; # achromatisch
58              
59 7         14 my $a = $lab->[1] / $C; # Farbtonrichtung
60 7         8 my $b = $lab->[2] / $C;
61 7         18 my $h = 0.5 + 0.5 * atan2(-$lab->[2], -$lab->[1]) / $PI;
62              
63 7         15 my ($L_cusp, $C_cusp) = find_cusp($a, $b);
64 7         15 my ($S_max, $T_max) = to_ST($L_cusp, $C_cusp);
65 7         6 my $S_0 = 0.5;
66 7         10 my $k = 1 - $S_0 / $S_max;
67              
68             # L_v, C_v und ihre toe-kompensierten Varianten
69 7         10 my $t = $T_max / ($C + $lab->[0] * $T_max);
70 7         9 my $L_v = $t * $lab->[0];
71 7         7 my $C_v = $t * $C;
72              
73 7         13 my $L_vt = toe_inv($L_v);
74 7         9 my $C_vt = $C_v * $L_vt / $L_v;
75              
76 7         16 my $rgb_scale = oklab_to_linear_srgb([$L_vt, $a * $C_vt, $b * $C_vt]);
77 7         16 my $scale_L = spow(1 / max(@$rgb_scale, 0), 1/3);
78              
79 7         13 my $L = $lab->[0] / $scale_L;
80 7         8 $C = $C / $scale_L;
81 7         12 $C = $C * toe($L) / $L;
82 7         9 $L = toe($L);
83              
84 7         31 my $s = ($S_0 + $T_max) * $C_v / ($T_max * $S_0 + $T_max * $k * $C_v);
85              
86 7         27 return [$h, $s, $L / $L_v];
87             }
88              
89             Graphics::Toolkit::Color::Space->new(
90             name => 'OKHSV',
91             family => 'HSV',
92             axis => [qw/hue saturation value/],
93             type => [qw/angular linear linear/],
94             range => [360, 1, 1],
95             precision => 5,
96             convert => {LinearRGB => [\&from_hsv, \&to_hsv]},
97             );