File Coverage

blib/lib/Colouring/In.pm
Criterion Covered Total %
statement 173 197 87.8
branch 94 102 92.1
condition 41 61 67.2
subroutine 29 35 82.8
pod 26 26 100.0
total 363 421 86.2


line stmt bran cond sub pod time code
1             package Colouring::In;
2              
3 13     13   744306 use 5.006;
  13         140  
4 13     13   55 use strict;
  13         22  
  13         286  
5 13     13   67 use warnings;
  13         18  
  13         353  
6 13     13   5207 use smallnum;
  13         105801  
  13         46  
7             our $VERSION = '0.24';
8              
9             our (%TOOL, $ANOBJECT);
10              
11             use overload
12 13     13   1565 '""' => sub { $_[0]->toCSS() };
  13     3   20  
  13         61  
  3         386  
13              
14              
15             BEGIN {
16             %TOOL = (
17 30557         330006 clamp => sub { return $TOOL{min}( $TOOL{max}( $_[0], 0 ), $_[1]); },
18 30728 100 100     260510 max => sub { $_[ ($_[0] || 0) < ($_[1] || 0) ] || 0 },
      100        
19 30728 100 100     1432179 min => sub { $_[ ($_[0] || 0) > ($_[1] || 0) ] || 0 },
      100        
20             round => sub {
21 266 100       1976 return sprintf '%.' . ( defined $_[1] ? $_[1] : 0 ) . 'f', $_[0];
22             },
23 10148   100     58152 numIs => sub { return defined $_[0] && $_[0] =~ /^[0-9]+/; },
24 40         481 percent => sub { return ( $_[0] * 100 ) . '%'; },
25 20081         25667 depercent => sub { my $p = shift; $p =~ s/%$//; return $p / 100; },
  20081         29758  
  20081         33232  
26             joinRgb => sub {
27 54         92 return join ',', map { $TOOL{clamp}( $TOOL{round}($_), 255 ); } @_;
  162         2586  
28             },
29             rgb2hs => sub {
30 83         141 my @rgb = map { $_ / 255 } @_;
  249         2059  
31 83         932 push @rgb, $TOOL{max}( $TOOL{max}( $rgb[0], $rgb[1] ), $rgb[2] );
32 83         3501 push @rgb, $TOOL{min}( $TOOL{min}( $rgb[0], $rgb[1] ), $rgb[2] );
33 83         3536 push @rgb, ( $rgb[3] - $rgb[4] );
34 83         1054 return @rgb;
35             },
36             hue => sub {
37 30221         1276885 my ( $h, $m1, $m2 ) = @_;
38 30221 100       52839 $h = $h < 0 ? $h + 1 : ( $h > 1 ? $h - 1 : $h );
    100          
39 30221 100       551738 if ( $h * 6 < 1 ) { return $m1 + ( $m2 - $m1 ) * $h * 6; }
  5091 100       118607  
    100          
40 9993         437105 elsif ( $h * 2 < 1 ) { return $m2; }
41             elsif ( $h * 3 < 2 ) {
42 5063         326221 return $m1 + ( $m2 - $m1 ) * ( 2 / 3 - $h ) * 6;
43             }
44 20228         651142 return $m1;
45             },
46             scaled => sub {
47 197         1343 my ( $n, $size ) = @_;
48 197 100       298 return ( $n =~ s/%// )
49             ? sprintf( '%.f2', (($n * $size) / 100 ))
50             : return sprintf( "%d", $n );
51             },
52             convertColour => sub {
53 10082         28221 my $colour = shift;
54 10082         25883 my %converter = (
55             '#' => 'hex2rgb',
56             'rgb' => 'rgb2rgb',
57             'hsl' => 'hsl2rgb',
58             'hsla' => 'hsl2rgb',
59             );
60 10082         40261 my $reg = join '|', reverse sort keys %converter;
61 10082 100       58110 if ( $colour =~ s/^($reg)// ) {
62 10081         29977 return $TOOL{ $converter{$1} }($colour);
63             }
64 1   50     14 die $TOOL{MESSAGES}{INVALID_COLOUR} || 'Cannot convert the colour format';
65             },
66             rgb2rgb => sub {
67 19         38 my @numbers = $TOOL{numbers}(shift);
68 19 100 50     66 die $TOOL{MESSAGES}{INVALID_RGB} || 'Cannot convert rgb colour format' unless (scalar @numbers > 2);
69 18         252 return @numbers;
70             },
71             hex2rgb => sub {
72 53         653 my $hex = shift;
73 53         77 my $l = length $hex;
74             return $l != 6
75             ? $l == 3
76 13 100 100     121 ? map { my $h = hex( $_ . $_ ); $_ =~ 0 || $h ? $h : die( $TOOL{MESSAGES}{INVALID_HEX} || 'Cannot convert hex colour format' ) } $hex =~ m/./g
  13   50     91  
77             : die 'hex length must be 3 or 6'
78 53 100 66     359 : map { my $h = hex( $_ ); $_ =~ m/00/ || $h ? $h : die( $TOOL{MESSAGES}{INVALID_HEX} || 'Cannot convert hex colour format' ) } $hex =~ m/../g;
  141 50 0     832  
  141 100       800  
79             },
80             hsl2rgb => sub {
81 10074 100       22249 my ( $h, $s, $l, $a, $m1, $m2 ) = scalar @_ > 1 ? @_ : $TOOL{numbers}(shift);
82 10074   66     96166 defined $_ && $_ =~ m/([0-9.]+)/ or die $TOOL{MESSAGES}{INVALID_HSL} || 'Cannot convert hsl colour format' for ($h, $s, $l);
      50        
      100        
83 10073         25809 $h = ( $h % 360 ) / 360;
84 10073 100       203182 unless ($m1) {
85 10009         18495 $s = $TOOL{depercent}($s);
86 10009         119437 $l = $TOOL{depercent}($l);
87             }
88 10073 100       122455 $m2 = $l <= 0.5 ? $l * ( $s + 1 ) : $l + $s - $l * $s;
89 10073         343805 $m1 = $l * 2 - $m2;
90             return (
91             ($TOOL{clamp}($TOOL{hue}( $h + 1 / 3, $m1, $m2 ), 1) * 255),
92             ($TOOL{clamp}($TOOL{hue}( $h, $m1, $m2 ), 1) * 255),
93 10073 100       230512 ($TOOL{clamp}($TOOL{hue}( $h - 1 / 3, $m1, $m2 ), 1) * 255),
94             ( defined $a ? $a : () ),
95             );
96             },
97             numbers => sub {
98 10029         139449 return ( $_[0] =~ m/([0-9.]+)/g );
99             },
100             hsl => sub {
101 63         92 my $colour = shift;
102 63 100       172 if ( ref \$colour eq 'SCALAR' ) {
103 50         131 $colour = Colouring::In->new($colour);
104             }
105 63         112 my $hsl = $TOOL{asHSL}($colour);
106 63         1264 return ( $hsl, $colour );
107             },
108             hash2array => sub {
109 63         88 my $hash = shift;
110 63         95 return map { $hash->{$_} } @_;
  252         424  
111             },
112             asHSL => sub {
113 75         231 my ( $r, $g, $b, $max, $min, $d, $h, $s, $l ) = $TOOL{rgb2hs}( $_[0]->colour );
114              
115 75         157 $l = ( $max + $min ) / 2;
116 75 100       1495 if ( $max == $min ) {
117 68         765 $h = $s = 0;
118             }
119             else {
120 7         79 $d = smallnum::_smallnum($d); #grrr
121 7 100       30 $s = $l > 0.5 ? ($d / ( 2 - $max - $min )) : ($d / ( $max + $min ));
122 7 100       227 $h = ( $max == $r )
    100          
    100          
123             ? ( $g - $b ) / $d + ( $g < $b ? 6 : 0 )
124             : ( $max == $g )
125             ? ( $b - $r ) / $d + +2
126             : ( $r - $g ) / $d + 4;
127 7         359 $h /= 6;
128             }
129              
130             return {
131             h => $h * 360,
132             s => $s,
133             l => $l,
134             a => $_[0]->{alpha},
135 75         197 };
136             }
137 13     13   27483 );
138             }
139              
140             sub import {
141 13     13   264 my ($pkg, @exports) = @_;
142 13         30 my $caller = caller;
143 13 100       55 $TOOL{MESSAGES} = pop @exports if (ref $exports[-1] eq 'HASH');
144 13 100       10353 if (scalar @exports) {
145 13     13   859 no strict 'refs';
  13         22  
  13         528  
146 5         11 *{"${caller}::${_}"} = \&{"${_[0]}::${_}"} foreach @exports;
  5         6789  
  5         32  
147             }
148             }
149              
150             sub rgb {
151 1     1 1 795 return $_[0]->rgba( $_[1], $_[2], $_[3], $_[4] );
152             }
153              
154             sub rgba {
155 65     65 1 1926 my $rgb = [ map { $TOOL{scaled}( $_, 255 ) } ( $_[1], $_[2], $_[3] ) ];
  195         2380  
156 65         1054 return Colouring::In->new( $rgb, $TOOL{clamp}($_[4], 1) );
157             }
158              
159             sub hsl {
160 1     1 1 2 my $self = shift;
161 1         5 return $self->rgba($TOOL{hsl2rgb}(@_, 1));
162             }
163              
164             sub hsla {
165 63     63 1 85 my $self = shift;
166 63         132 return $self->rgba($TOOL{hsl2rgb}(@_, 1));
167             }
168              
169             sub new {
170 10142     10142 1 281585 my ( $pkg, $rgb, $a ) = @_;
171              
172 10142         17895 my $self = bless {}, $pkg;
173             # The end goal here, is to parse the arguments
174             # into an integer triplet, such as `128, 255, 0`
175 10142 100       18077 if ( ref $rgb eq 'ARRAY' ) {
176 78 100       211 scalar @$rgb == 4 and $a = pop @$rgb;
177 78         728 $self->{colour} = $rgb;
178             } else {
179 10064         16751 $self->{colour} = [ $TOOL{convertColour}($rgb) ];
180 10061 100       564722 scalar @{ $self->{colour} } == 4 and $a = pop @{$self->{colour}};
  10014         88062  
  10061         24827  
181             }
182 10139 100       19047 $self->{alpha} = $TOOL{numIs}($a) ? $a : 1;
183 10139         24059 return $self;
184             }
185              
186             sub toCSS {
187 18     18 1 85 my $alpha = $TOOL{round}( $_[0]->{alpha}, $_[1] );
188 18 100       333 return ( $alpha != 1 ) ? $_[0]->toRGBA() : $_[0]->toHEX( $_[2] );
189             }
190              
191             sub toTerm {
192 5     5 1 2756 return sprintf( "r%sg%sb%s", $_[0]->colour );
193             }
194              
195             sub toOnTerm {
196 5     5 1 16 return sprintf( "on_r%sg%sb%s", $_[0]->colour );
197             }
198              
199             sub toRGB {
200 9 50 66 9 1 3343 return $_[0]->toRGBA( $_[1] ) if $TOOL{numIs}( $_[1] ) and $_[1] != 1;
201 9         33 return sprintf( 'rgb(%s)', ( $TOOL{joinRgb}( $_[0]->colour ) ) );
202             }
203              
204             sub toRGBA {
205             return sprintf 'rgba(%s,%s)', $TOOL{joinRgb}( $_[0]->colour ),
206 45     45 1 187 $_[0]->{alpha};
207             }
208              
209             sub toHEX {
210             my $colour = sprintf(
211             "#%02lx%02lx%02lx",
212             (
213 28     28 1 395 map { my $c = $TOOL{clamp}( $TOOL{round}($_), 255 ); $c }
  84         140  
  84         3481  
214             $_[0]->colour
215             )
216             );
217 28 100       367 unless ( $_[1] ) {
218 23 100       148 if ( $colour =~ /#(.)\1(.)\2(.)\3/g ) {
219 22         111 $colour = sprintf "#%s%s%s", $1, $2, $3;
220             }
221             }
222 28         121 return $colour;
223             }
224              
225             sub toHSL {
226 12     12 1 36 my $hsl = $TOOL{asHSL}($_[0]);
227             sprintf( "hsl(%s,%s,%s)",
228             $hsl->{h},
229             $TOOL{percent}( $hsl->{s} ),
230 12         252 $TOOL{percent}( $hsl->{l} ),
231             );
232             }
233              
234             sub toHSV {
235 8     8 1 3971 my ( $r, $g, $b, $max, $min, $d, $h, $s, $v ) = $TOOL{rgb2hs}( $_[0]->colour );
236              
237 8         13 $v = $max;
238 8 100       14 $s = ( $max == 0 ) ? $max : $d / $max;
239              
240 8 100       217 if ( $max == $min ) {
241 2         23 $h = 0;
242             }
243             else {
244 6 100       68 $h = ( $max == $r ) ? ( $g - $b ) / $d + ( $g < $b ? 6 : 0 )
    100          
    100          
245             : ( $max == $g ) ? ( $b - $r ) / $d + 2
246             : ( $r - $g ) / $d + 4;
247 6         217 $h /= 6;
248             }
249              
250             return sprintf( "hsv(%s,%s,%s)",
251             ( $h * 360 ),
252             $TOOL{percent}($s),
253 8         65 $TOOL{percent}($v),
254             );
255             }
256              
257             sub lighten {
258 11     11 1 4308 my ( $colour, $amt, $meth, $hsl ) = @_;
259              
260 11         23 ( $hsl, $colour ) = $TOOL{hsl}($colour);
261              
262 11         22 $amt = $TOOL{depercent}($amt);
263             $hsl->{l} += $TOOL{clamp}(
264             ( $meth && $meth eq 'relative' )
265 11 100 100     152 ? (($hsl->{l} || 1) * $amt)
      50        
266             : $amt, 1
267             );
268              
269 11         536 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
270             }
271              
272             sub darken {
273 12     12 1 5497 my ( $colour, $amt, $meth, $hsl ) = @_;
274              
275 12         26 ( $hsl, $colour ) = $TOOL{hsl}($colour);
276              
277 12         22 $amt = $TOOL{depercent}($amt);
278             $hsl->{l} -= $TOOL{clamp}(
279             ( $meth && $meth eq 'relative' )
280 12 100 100     159 ? $hsl->{l} * $amt
281             : $amt, 1,
282             );
283              
284 12         567 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
285             }
286              
287             sub fade {
288 12     12 1 7022 my ($colour, $amt, $hsl) = @_;
289              
290 12         25 ($hsl, $colour) = $TOOL{hsl}($colour);
291 12         21 $hsl->{a} = $TOOL{depercent}($amt);
292              
293 12         147 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
294             }
295              
296             sub fadeout {
297 14     14 1 8060 my ($colour, $amt, $meth, $hsl) = @_;
298              
299 14         31 ($hsl, $colour) = $TOOL{hsl}($colour);
300             $hsl->{a} -= (($meth && $meth eq 'relative')
301             ? $hsl->{a} * $TOOL{depercent}($amt)
302 14 100 100     55 : $TOOL{depercent}($amt));
303 14         309 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
304             }
305              
306             sub fadein {
307 14     14 1 9305 my ($colour, $amt, $meth, $hsl) = @_;
308 14         37 ($hsl, $colour) = $TOOL{hsl}($colour);
309             $hsl->{a} += ($meth && $meth eq 'relative')
310             ? $hsl->{a} * $TOOL{depercent}($amt)
311 14 100 100     54 : $TOOL{depercent}($amt);
312 14         277 $hsl->{a} = smallnum::_smallnum($hsl->{a});
313 14         66 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
314             }
315              
316             sub mix {
317 0     0 1 0 my ($colour1, $colour2, $weight) = @_;
318 0         0 my ($h1, $c1, $h2, $c2) = ($TOOL{hsl}($colour1), $TOOL{hsl}($colour2));
319 0   0     0 $weight = ($weight || 50) / 100;
320 0         0 my $a = $h1->{a} - $h2->{a};
321 0         0 my $w = ($weight * 2) - 1;
322 0 0       0 my $w1 = ((($w * $a == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2;
323 0         0 my $w2 = 1 - $w1;
324             return Colouring::In->new([
325             ($c1->{colour}[0] * $w1) + ($c2->{colour}[0] * $w2),
326             ($c1->{colour}[1] * $w1) + ($c2->{colour}[1] * $w2),
327             ($c1->{colour}[2] * $w1) + ($c2->{colour}[2] * $w2),
328 0         0 ($c1->{alpha} * $weight) + ($c2->{alpha} * 1 - $weight)
329             ]);
330             }
331              
332             sub tint {
333 0     0 1 0 my ($colour, $weight) = @_;
334 0         0 mix(
335             'rgb(255,255,255)',
336             $colour,
337             $weight
338             );
339             }
340              
341             sub shade {
342 0     0 1 0 my ($colour, $weight) = @_;
343 0         0 mix(
344             'rgb(0, 0, 0)',
345             $colour,
346             $weight
347             );
348             }
349              
350             sub saturate {
351 0     0 1 0 my ($colour, $amt, $meth) = @_;
352 0         0 my ($h1, $c1) = $TOOL{hsl}($colour);
353 0         0 $amt = $TOOL{depercent}($amt);
354             $h1->{s} += $TOOL{clamp}(
355             ( $meth && $meth eq 'relative' )
356 0 0 0     0 ? $h1->{s} * $amt
357             : $amt, 1,
358             );
359 0         0 return $c1->hsla( $TOOL{hash2array}( $h1, 'h', 's', 'l', 'a' ) );
360             }
361              
362             sub desaturate {
363 0     0 1 0 my ($colour, $amt, $meth) = @_;
364 0         0 my ($h1, $c1) = $TOOL{hsl}($colour);
365 0         0 $amt = $TOOL{depercent}($amt);
366             $h1->{s} -= $TOOL{clamp}(
367             ( $meth && $meth eq 'relative' )
368 0 0 0     0 ? $h1->{s} * $amt
369             : $amt, 1,
370             );
371 0         0 return $c1->hsla( $TOOL{hash2array}( $h1, 'h', 's', 'l', 'a' ) );
372             }
373              
374             sub greyscale {
375 0     0 1 0 my ($colour) = @_;
376 0         0 desaturate($colour, 100);
377             }
378              
379             sub colour {
380 10193     10193 1 30234 my @rgb = @{ $_[0]->{colour} };
  10193         18503  
381 10193 100       91665 my $r = defined $rgb[0] ? $rgb[0] : 255;
382 10193 100       16169 my $g = defined $rgb[1] ? $rgb[1] : 255;
383 10193 100       15200 my $b = defined $rgb[2] ? $rgb[2] : 255;
384 10193         21902 return ( $r, $g, $b );
385             }
386              
387             sub validate {
388 4     4 1 1648 my ($self, $colour) = @_;
389 4         6 my $new = eval { $self->new($colour) };
  4         9  
390 4 100       39 if ($@) {
391             return {
392             valid => \0,
393 3   50     18 message => $TOOL{MESSAGES}{VALIDATE_ERROR} || 'The string passed to Colouring::In::validate is not a valid color.',
394             color => $colour
395             };
396             }
397             return {
398             valid => \1,
399 1   50     8 message => $TOOL{MESSAGES}{VALIDATE} || 'The string passed to Colouring::In::validate is a valid color',
400             color => $colour,
401             colour => $new
402             };
403             }
404              
405             1;
406              
407             __END__