File Coverage

blib/lib/Color/Spectrum.pm
Criterion Covered Total %
statement 85 86 98.8
branch 32 46 69.5
condition 1 3 33.3
subroutine 10 10 100.0
pod 4 4 100.0
total 132 149 88.5


line stmt bran cond sub pod time code
1             package Color::Spectrum;
2 2     2   164164 use strict;
  2         2  
  2         50  
3 2     2   8 use warnings FATAL => 'all';
  2         2  
  2         78  
4             our $VERSION = '1.14';
5              
6 2     2   1214 use POSIX;
  2         8398  
  2         7  
7 2     2   3991 use Carp;
  2         4  
  2         81  
8              
9 2     2   7 use Exporter 'import';
  2         2  
  2         61  
10             our @EXPORT_OK = qw( generate rgb2hsi hsi2rgb );
11              
12 2     2   803 use Color::Library;
  2         218461  
  2         1397  
13              
14             sub new {
15 1     1 1 89045 my $class = shift;
16 1         3 my $self = {};
17 1         1 bless $self, $class;
18 1         3 return $self;
19             }
20              
21             sub generate {
22 10 100   10 1 7986 my $self = shift if ref($_[0]) eq __PACKAGE__;
23 10 50       24 croak "ColorCount and at least one color needed" if @_ < 2;
24 10         17 my $cnt = $_[0];
25 10         13 my $col1 = $_[1];
26 10   33     16 $_[2] ||= $_[1];
27 10         11 my $col2 = $_[2];
28              
29             # expand 3 hex chars to 6
30 10         34 $col1 =~ s/^([a-f0-9])([a-f0-9])([a-f0-9])$/$1$1$2$2$3$3/i;
31 10         17 $col2 =~ s/^([a-f0-9])([a-f0-9])([a-f0-9])$/$1$1$2$2$3$3/i;
32              
33             # look up hex color if not a hex color
34 10 100       43 $col1 = Color::Library->color( $col1 ) unless $col1 =~ /^#?[a-f0-9]{6}$/i;
35 10 100       593065 $col2 = Color::Library->color( $col2 ) unless $col2 =~ /^#?[a-f0-9]{6}$/i;
36              
37 10 100       1559 croak "Invalid color $_[1]" unless $col1;
38 9 100       92 croak "Invalid color $_[2]" unless $col2;
39              
40             # remove leading hash (we'll add it back later)
41 8         44 $col1 =~s/^#//;
42 8         22 $col2 =~s/^#//;
43              
44 8         14 my $clockwise = 0;
45 8 50       16 $clockwise++ if ( $cnt < 0 );
46 8         8 $cnt = int( abs( $cnt ) );
47              
48 8         18 my @murtceps = ( uc "#$col1" );
49 8 50       17 return ( wantarray() ? @murtceps : \@murtceps ) if $cnt <= 1;
    100          
50 7 50       16 return ( wantarray() ? (uc "#$col1","#$col2") : [uc "#$col1","#$col2"] ) if $cnt == 2;
    100          
51              
52             # The RGB values need to be on the decimal scale,
53             # so we divide em by 255 enpassant.
54 6         23 my ( $h1, $s1, $i1 ) = rgb2hsi( map { hex() / 255 } unpack( 'a2a2a2', $col1 ) );
  18         30  
55 6         16 my ( $h2, $s2, $i2 ) = rgb2hsi( map { hex() / 255 } unpack( 'a2a2a2', $col2 ) );
  18         24  
56 6         7 $cnt--;
57 6         7 my $sd = ( $s2 - $s1 ) / $cnt;
58 6         8 my $id = ( $i2 - $i1 ) / $cnt;
59 6         4 my $hd = $h2 - $h1;
60 6 50       15 if ( uc( $col1 ) eq uc( $col2 ) ) {
61 0 0       0 $hd = ( $clockwise ? -1 : 1 ) / $cnt;
62             } else {
63 6 50       11 $hd = ( ( $hd < 0 ? 1 : 0 ) + $hd - $clockwise) / $cnt;
64             }
65              
66 6         12 while (--$cnt) {
67 36         30 $s1 += $sd;
68 36         17 $i1 += $id;
69 36         28 $h1 += $hd;
70 36 50       43 $h1 -= 1 if $h1>1;
71 36 50       42 $h1 += 1 if $h1<0;
72             push @murtceps, sprintf "#%02X%02X%02X",
73 36         43 map { int( $_ * 255 +.5) } hsi2rgb( $h1, $s1, $i1 );
  108         184  
74             }
75 6         11 push @murtceps, uc "#$col2";
76 6 50       35 return wantarray() ? @murtceps : \@murtceps;
77             }
78              
79             sub rgb2hsi {
80 12     12 1 13 my ( $r, $g, $b ) = @_;
81 12         12 my ( $h, $s, $i ) = ( 0, 0, 0 );
82              
83 12         13 $i = ( $r + $g + $b ) / 3;
84 12 100       22 return ( $h, $s, $i ) if $i == 0;
85              
86 6         8 my $x = $r - 0.5 * ( $g + $b );
87 6         8 my $y = 0.866025403 * ( $g - $b );
88 6         19 $s = ( $x ** 2 + $y ** 2 ) ** 0.5;
89 6 100       17 return ( $h, $s, $i ) if $s == 0;
90              
91 1         10 $h = POSIX::atan2( $y , $x ) / ( 2 * 3.1415926535 );
92 1         197 return ( $h, $s, $i );
93             }
94              
95             sub hsi2rgb {
96 36     36 1 27 my ( $h, $s, $i ) = @_;
97 36         31 my ( $r, $g, $b ) = ( 0, 0, 0 );
98              
99             # degenerate cases. If !intensity it's black, if !saturation it's grey
100 36 50       60 return ( $r, $g, $b ) if ( $i == 0 );
101 36 100       64 return ( $i, $i, $i ) if ( $s == 0 );
102              
103 8         8 $h = $h * 2 * 3.1415926535;
104 8         15 my $x = $s * cos( $h );
105 8         8 my $y = $s * sin( $h );
106              
107 8         5 $r = $i + ( 2 / 3 * $x );
108 8         9 $g = $i - ( $x / 3 ) + ( $y / 2 / 0.866025403 );
109 8         10 $b = $i - ( $x / 3 ) - ( $y / 2 / 0.866025403 );
110              
111             # limit 0<=x<=1 ## YUCK but we go outta range without it.
112 8 50       9 ( $r, $b, $g ) = map { $_ < 0 ? 0 : $_ > 1 ? 1 : $_ } ( $r, $b, $g );
  24 50       41  
113              
114 8         9 return ( $r, $g, $b );
115             }
116              
117             1;
118              
119             __END__