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   413727 use strict;
  2         4  
  2         110  
3 2     2   12 use warnings FATAL => 'all';
  2         3  
  2         177  
4             our $VERSION = '1.16';
5              
6 2     2   1203 use POSIX;
  2         19156  
  2         10  
7 2     2   5472 use Carp;
  2         5  
  2         122  
8              
9 2     2   11 use Exporter 'import';
  2         3  
  2         100  
10             our @EXPORT_OK = qw( generate rgb2hsi hsi2rgb );
11              
12 2     2   1120 use Color::Library;
  2         373590  
  2         3067  
13              
14             sub new {
15 1     1 1 268860 my $class = shift;
16 1         4 my $self = {};
17 1         3 bless $self, $class;
18 1         3 return $self;
19             }
20              
21             sub generate {
22 10 100   10 1 18971 my $self = shift if ref($_[0]) eq __PACKAGE__;
23 10 50       39 croak "ColorCount and at least one color needed" if @_ < 2;
24 10         21 my $cnt = $_[0];
25 10         22 my $col1 = $_[1];
26 10   33     35 $_[2] ||= $_[1];
27 10         21 my $col2 = $_[2];
28              
29             # expand 3 hex chars to 6
30 10         54 $col1 =~ s/^([a-f0-9])([a-f0-9])([a-f0-9])$/$1$1$2$2$3$3/i;
31 10         34 $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       77 $col1 = Color::Library->color( $col1 ) unless $col1 =~ /^#?[a-f0-9]{6}$/i;
35 10 100       5091122 $col2 = Color::Library->color( $col2 ) unless $col2 =~ /^#?[a-f0-9]{6}$/i;
36              
37 10 100       3529 croak "Invalid color $_[1]" unless $col1;
38 9 100       345 croak "Invalid color $_[2]" unless $col2;
39              
40             # remove leading hash (we'll add it back later)
41 8         116 $col1 =~s/^#//;
42 8         87 $col2 =~s/^#//;
43              
44 8         93 my $clockwise = 0;
45 8 50       29 $clockwise++ if ( $cnt < 0 );
46 8         19 $cnt = int( abs( $cnt ) );
47              
48 8         32 my @murtceps = ( uc "#$col1" );
49 8 50       33 return ( wantarray() ? @murtceps : \@murtceps ) if $cnt <= 1;
    100          
50 7 50       37 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         31 my ( $h1, $s1, $i1 ) = rgb2hsi( map { hex() / 255 } unpack( 'a2a2a2', $col1 ) );
  18         72  
55 6         29 my ( $h2, $s2, $i2 ) = rgb2hsi( map { hex() / 255 } unpack( 'a2a2a2', $col2 ) );
  18         47  
56 6         14 $cnt--;
57 6         14 my $sd = ( $s2 - $s1 ) / $cnt;
58 6         15 my $id = ( $i2 - $i1 ) / $cnt;
59 6         12 my $hd = $h2 - $h1;
60 6 50       25 if ( uc( $col1 ) eq uc( $col2 ) ) {
61 0 0       0 $hd = ( $clockwise ? -1 : 1 ) / $cnt;
62             } else {
63 6 50       20 $hd = ( ( $hd < 0 ? 1 : 0 ) + $hd - $clockwise) / $cnt;
64             }
65              
66 6         20 while (--$cnt) {
67 36         63 $s1 += $sd;
68 36         57 $i1 += $id;
69 36         60 $h1 += $hd;
70 36 50       109 $h1 -= 1 if $h1>1;
71 36 50       112 $h1 += 1 if $h1<0;
72             push @murtceps, sprintf "#%02X%02X%02X",
73 36         90 map { int( $_ * 255 +.5) } hsi2rgb( $h1, $s1, $i1 );
  108         416  
74             }
75 6         19 push @murtceps, uc "#$col2";
76 6 50       80 return wantarray() ? @murtceps : \@murtceps;
77             }
78              
79             sub rgb2hsi {
80 12     12 1 32 my ( $r, $g, $b ) = @_;
81 12         32 my ( $h, $s, $i ) = ( 0, 0, 0 );
82              
83 12         29 $i = ( $r + $g + $b ) / 3;
84 12 100       48 return ( $h, $s, $i ) if $i == 0;
85              
86 6         16 my $x = $r - 0.5 * ( $g + $b );
87 6         18 my $y = 0.866025403 * ( $g - $b );
88 6         25 $s = ( $x ** 2 + $y ** 2 ) ** 0.5;
89 6 100       57 return ( $h, $s, $i ) if $s == 0;
90              
91 1         13 $h = POSIX::atan2( $y , $x ) / ( 2 * 3.1415926535 );
92 1         335 return ( $h, $s, $i );
93             }
94              
95             sub hsi2rgb {
96 36     36 1 75 my ( $h, $s, $i ) = @_;
97 36         73 my ( $r, $g, $b ) = ( 0, 0, 0 );
98              
99             # degenerate cases. If !intensity it's black, if !saturation it's grey
100 36 50       89 return ( $r, $g, $b ) if ( $i == 0 );
101 36 100       181 return ( $i, $i, $i ) if ( $s == 0 );
102              
103 8         19 $h = $h * 2 * 3.1415926535;
104 8         17 my $x = $s * cos( $h );
105 8         17 my $y = $s * sin( $h );
106              
107 8         16 $r = $i + ( 2 / 3 * $x );
108 8         18 $g = $i - ( $x / 3 ) + ( $y / 2 / 0.866025403 );
109 8         18 $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       42 ( $r, $b, $g ) = map { $_ < 0 ? 0 : $_ > 1 ? 1 : $_ } ( $r, $b, $g );
  24 50       80  
113              
114 8         23 return ( $r, $g, $b );
115             }
116              
117             1;
118              
119             __END__