File Coverage

lib/Graphics/Toolkit/Color/Space/Instance/OKHSL.pm
Criterion Covered Total %
statement 56 56 100.0
branch 13 18 72.2
condition 4 12 33.3
subroutine 6 6 100.0
pod 0 2 0.0
total 79 94 84.0


line stmt bran cond sub pod time code
1              
2             # OKHSL, Conveter under Copyright (c) 2021 Björn Ottosson, see LICENSE.OK
3              
4             package Graphics::Toolkit::Color::Space::Instance::OKHSL;
5 16     16   269406 use v5.12;
  16         51  
6 16     16   116 use warnings;
  16         23  
  16         697  
7 16     16   763 use Graphics::Toolkit::Color::Space qw/spow/;
  16         24  
  16         936  
8             use Graphics::Toolkit::Color::Space::Instance::Helper::OK
9 16     16   7081 qw/toe toe_inv get_Cs oklab_to_linear_srgb linear_srgb_to_oklab/;
  16         31  
  16         9994  
10             my $PI = 4 * atan2(1, 1);
11             my $mid = 0.8;
12             my $mid_inv = 1.25;
13              
14              
15             sub from_hsl {
16 15     15 0 24 my ($hsl) = @_;
17 15 100       35 return [0, 0, 0] if $hsl->[2] <= 0; # black
18 14 50       48 return [1, 1, 1] if $hsl->[2] >= 1; # white
19              
20 14         46 my $L = toe_inv($hsl->[2]);
21 14         50 my $a = cos(2 * $PI * $hsl->[0]); # Farbtonrichtung (Einheitsvektor)
22 14         32 my $b = sin(2 * $PI * $hsl->[0]);
23 14         32 my ($C_0, $C_mid, $C_max) = get_Cs($L, $a, $b);
24 14         20 my $C;
25             # Sattigung -> absolute Chroma (Vorwaerts-Interpolation, Inverse zum Hinweg)
26 14 100       32 if ($hsl->[1] < $mid) {
27 5         13 my $t = $hsl->[1] / $mid;
28 5         6 my $k1 = $mid * $C_0;
29 5         10 my $k2 = 1 - $k1 / $C_mid;
30 5         6 my $den = 1 - $k2 * $t;
31 5 50 33     13 $den = 1e-6 if $den < 1e-6 and $den >= 0;
32 5 50 33     15 $den = -1e-6 if $den > -1e-6 and $den < 0;
33 5         7 $C = $t * $k1 / $den;
34             }
35             else {
36 9         19 my $t = ($hsl->[1] - $mid) / (1 - $mid);
37 9         11 my $k0 = $C_mid;
38 9         15 my $k1 = (1 - $mid) * $C_mid * $C_mid * $mid_inv * $mid_inv / $C_0;
39 9         13 my $k2 = 1 - $k1 / ($C_max - $C_mid);
40 9         12 my $den = 1 - $k2 * $t;
41 9 50 33     16 $den = 1e-6 if $den < 1e-6 and $den >= 0;
42 9 50 33     28 $den = -1e-6 if $den > -1e-6 and $den < 0;
43 9         14 $C = $k0 + $t * $k1 / $den;
44             }
45              
46 14         133 return oklab_to_linear_srgb([$L, $C * $a, $C * $b]);
47             }
48              
49             sub to_hsl {
50 17     17 0 25 my ($rgb) = @_; # bereits LINEARES sRGB, Arrayref
51 17         42 my $lab = linear_srgb_to_oklab($rgb); # roh, a/b um 0 zentriert
52 17         52 my $C = spow($lab->[1]**2 + $lab->[2]**2, 1/2); # Chroma
53 17 100       37 return [0, 0, toe($lab->[0])] if $C < 1e-9; # achromatisch: Hue undefiniert -> 0, keine Division durch 0
54              
55 16         45 my $a = $lab->[1] / $C; # Farbtonrichtung (Einheitsvektor)
56 16         21 my $b = $lab->[2] / $C;
57 16         68 my $h = (0.5 + 0.5 * atan2(-$lab->[2], -$lab->[1]) / $PI);# Hue in Grad
58 16         52 my ($C_0, $C_mid, $C_max) = get_Cs($lab->[0], $a, $b);
59 16         21 my $s;
60 16 100       24 if ($C < $C_mid) {
61 2         4 my $k1 = $mid * $C_0;
62 2         3 my $k2 = 1 - $k1 / $C_mid;
63 2         3 my $t = $C / ($k1 + $k2 * $C);
64 2         3 $s = $t * $mid;
65             }
66             else {
67 14         18 my $k0 = $C_mid;
68 14         25 my $k1 = (1 - $mid) * $C_mid * $C_mid * $mid_inv * $mid_inv / $C_0;
69 14         18 my $k2 = 1 - $k1 / ($C_max - $C_mid);
70 14         21 my $t = ($C - $k0) / ($k1 + $k2 * ($C - $k0));
71 14         19 $s = $mid + (1 - $mid) * $t;
72             }
73              
74 16         34 return [$h, $s, toe($lab->[0])];
75             }
76              
77             Graphics::Toolkit::Color::Space->new(
78             name => 'OKHSL',
79             family => 'HSL',
80             axis => [qw/hue saturation lightness/],
81             type => [qw/angular linear linear/],
82             range => [360, 1, 1],
83             precision => 5,
84             convert => {LinearRGB => [\&from_hsl, \&to_hsl]},
85             );