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   1487912 use 5.006;
  13         53  
4 13     13   73 use strict;
  13         80  
  13         408  
5 13     13   92 use warnings;
  13         28  
  13         1043  
6 13     13   6300 use smallnum;
  13         181885  
  13         72  
7             our $VERSION = '0.27';
8              
9             our (%TOOL, $ANOBJECT);
10              
11             use overload
12 13     13   2253 '""' => sub { $_[0]->toCSS() };
  13     3   36  
  13         97  
  3         453  
13              
14              
15             BEGIN {
16             %TOOL = (
17 30557         588162 clamp => sub { return $TOOL{min}( $TOOL{max}( $_[0], 0 ), $_[1]); },
18 30728 100 100     275738 max => sub { $_[ ($_[0] || 0) < ($_[1] || 0) ] || 0 },
      100        
19 30728 100 100     1478038 min => sub { $_[ ($_[0] || 0) > ($_[1] || 0) ] || 0 },
      100        
20             round => sub {
21 266 100       3138 return sprintf '%.' . ( defined $_[1] ? $_[1] : 0 ) . 'f', $_[0];
22             },
23 10148   100     67193 numIs => sub { return defined $_[0] && $_[0] =~ /^[0-9]+/; },
24 40         779 percent => sub { return ( $_[0] * 100 ) . '%'; },
25 20081         28443 depercent => sub { my $p = shift; $p =~ s/%$//; return $p / 100; },
  20081         34590  
  20081         36717  
26             joinRgb => sub {
27 54         135 return join ',', map { $TOOL{clamp}( $TOOL{round}($_), 255 ); } @_;
  162         3156  
28             },
29             rgb2hs => sub {
30 83         176 my @rgb = map { $_ / 255 } @_;
  249         2740  
31 83         1217 push @rgb, $TOOL{max}( $TOOL{max}( $rgb[0], $rgb[1] ), $rgb[2] );
32 83         4527 push @rgb, $TOOL{min}( $TOOL{min}( $rgb[0], $rgb[1] ), $rgb[2] );
33 83         4538 push @rgb, ( $rgb[3] - $rgb[4] );
34 83         1301 return @rgb;
35             },
36             hue => sub {
37 30221         1340068 my ( $h, $m1, $m2 ) = @_;
38 30221 100       62859 $h = $h < 0 ? $h + 1 : ( $h > 1 ? $h - 1 : $h );
    100          
39 30221 100       559777 if ( $h * 6 < 1 ) { return $m1 + ( $m2 - $m1 ) * $h * 6; }
  5091 100       122429  
    100          
40 9993         454251 elsif ( $h * 2 < 1 ) { return $m2; }
41             elsif ( $h * 3 < 2 ) {
42 5063         347033 return $m1 + ( $m2 - $m1 ) * ( 2 / 3 - $h ) * 6;
43             }
44 20228         692169 return $m1;
45             },
46             scaled => sub {
47 197         2624 my ( $n, $size ) = @_;
48 197 100       431 return ( $n =~ s/%// )
49             ? sprintf( '%.f', (($n * $size) / 100 ))
50             : return sprintf( "%d", $n );
51             },
52             convertColour => sub {
53 10082         236056 my $colour = shift;
54 10082         36771 my %converter = (
55             '#' => 'hex2rgb',
56             'rgb' => 'rgb2rgb',
57             'hsl' => 'hsl2rgb',
58             'hsla' => 'hsl2rgb',
59             );
60 10082         50648 my $reg = join '|', reverse sort keys %converter;
61 10082 100       98856 if ( $colour =~ s/^($reg)// ) {
62 10081         38585 return $TOOL{ $converter{$1} }($colour);
63             }
64 1   50     20 die $TOOL{MESSAGES}{INVALID_COLOUR} || 'Cannot convert the colour format';
65             },
66             rgb2rgb => sub {
67 19         54 my @numbers = $TOOL{numbers}(shift);
68 19 100 50     101 die $TOOL{MESSAGES}{INVALID_RGB} || 'Cannot convert rgb colour format' unless (scalar @numbers > 2);
69 18         396 return @numbers;
70             },
71             hex2rgb => sub {
72 53         923 my $hex = shift;
73 53         106 my $l = length $hex;
74             return $l != 6
75             ? $l == 3
76 13 100 100     161 ? map { my $h = hex( $_ . $_ ); $_ =~ 0 || $h ? $h : die( $TOOL{MESSAGES}{INVALID_HEX} || 'Cannot convert hex colour format' ) } $hex =~ m/./g
  13   50     111  
77             : die 'hex length must be 3 or 6'
78 53 100 66     497 : map { my $h = hex( $_ ); $_ =~ m/00/ || $h ? $h : die( $TOOL{MESSAGES}{INVALID_HEX} || 'Cannot convert hex colour format' ) } $hex =~ m/../g;
  141 50 0     1379  
  141 100       1022  
79             },
80             hsl2rgb => sub {
81 10074 100       26054 my ( $h, $s, $l, $a, $m1, $m2 ) = scalar @_ > 1 ? @_ : $TOOL{numbers}(shift);
82 10074   66     103283 defined $_ && $_ =~ m/([0-9.]+)/ or die $TOOL{MESSAGES}{INVALID_HSL} || 'Cannot convert hsl colour format' for ($h, $s, $l);
      50        
      100        
83 10073         25280 $h = ( $h % 360 ) / 360;
84 10073 100       219154 unless ($m1) {
85 10009         21655 $s = $TOOL{depercent}($s);
86 10009         137671 $l = $TOOL{depercent}($l);
87             }
88 10073 100       128375 $m2 = $l <= 0.5 ? $l * ( $s + 1 ) : $l + $s - $l * $s;
89 10073         385890 $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       249574 ($TOOL{clamp}($TOOL{hue}( $h - 1 / 3, $m1, $m2 ), 1) * 255),
94             ( defined $a ? $a : () ),
95             );
96             },
97             numbers => sub {
98 10029         156448 return ( $_[0] =~ m/([0-9.]+)/g );
99             },
100             hsl => sub {
101 63         116 my $colour = shift;
102 63 100       240 if ( ref \$colour eq 'SCALAR' ) {
103 50         179 $colour = Colouring::In->new($colour);
104             }
105 63         147 my $hsl = $TOOL{asHSL}($colour);
106 63         1838 return ( $hsl, $colour );
107             },
108             hash2array => sub {
109 63         95 my $hash = shift;
110 63         127 return map { $hash->{$_} } @_;
  252         586  
111             },
112             asHSL => sub {
113 75         347 my ( $r, $g, $b, $max, $min, $d, $h, $s, $l ) = $TOOL{rgb2hs}( $_[0]->colour );
114              
115 75         248 $l = ( $max + $min ) / 2;
116 75 100       1974 if ( $max == $min ) {
117 68         874 $h = $s = 0;
118             }
119             else {
120 7         112 $d = smallnum::_smallnum($d); #grrr
121 7 100       42 $s = $l > 0.5 ? ($d / ( 2 - $max - $min )) : ($d / ( $max + $min ));
122 7 100       341 $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         562 $h /= 6;
128             }
129              
130             return {
131             h => $h * 360,
132             s => $s,
133             l => $l,
134             a => $_[0]->{alpha},
135 75         284 };
136             }
137 13     13   38238 );
138             }
139              
140             sub import {
141 13     13   404 my ($pkg, @exports) = @_;
142 13         44 my $caller = caller;
143 13 100       67 $TOOL{MESSAGES} = pop @exports if (ref $exports[-1] eq 'HASH');
144 13 100       14854 if (scalar @exports) {
145 13     13   1160 no strict 'refs';
  13         37  
  13         935  
146 5         12 *{"${caller}::${_}"} = \&{"${_[0]}::${_}"} foreach @exports;
  5         8332  
  5         24  
147             }
148             }
149              
150             sub rgb {
151 1     1 1 1358 return $_[0]->rgba( $_[1], $_[2], $_[3], $_[4] );
152             }
153              
154             sub rgba {
155 65     65 1 2422 my $rgb = [ map { $TOOL{scaled}( $_, 255 ) } ( $_[1], $_[2], $_[3] ) ];
  195         2867  
156 65         1245 return Colouring::In->new( $rgb, $TOOL{clamp}($_[4], 1) );
157             }
158              
159             sub hsl {
160 1     1 1 4 my $self = shift;
161 1         6 return $self->rgba($TOOL{hsl2rgb}(@_, 1));
162             }
163              
164             sub hsla {
165 63     63 1 93 my $self = shift;
166 63         161 return $self->rgba($TOOL{hsl2rgb}(@_, 1));
167             }
168              
169             sub new {
170 10142     10142 1 1129325 my ( $pkg, $rgb, $a ) = @_;
171              
172 10142         20464 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       21303 if ( ref $rgb eq 'ARRAY' ) {
176 78 100       282 scalar @$rgb == 4 and $a = pop @$rgb;
177 78         1379 $self->{colour} = $rgb;
178             } else {
179 10064         20335 $self->{colour} = [ $TOOL{convertColour}($rgb) ];
180 10061 100       607936 scalar @{ $self->{colour} } == 4 and $a = pop @{$self->{colour}};
  10014         91264  
  10061         25895  
181             }
182 10139 100       24001 $self->{alpha} = $TOOL{numIs}($a) ? $a : 1;
183 10139         32901 return $self;
184             }
185              
186             sub toCSS {
187 18     18 1 124 my $alpha = $TOOL{round}( $_[0]->{alpha}, $_[1] );
188 18 100       482 return ( $alpha != 1 ) ? $_[0]->toRGBA() : $_[0]->toHEX( $_[2] );
189             }
190              
191             sub toTerm {
192 5     5 1 5877 return sprintf( "r%sg%sb%s", $_[0]->colour );
193             }
194              
195             sub toOnTerm {
196 5     5 1 26 return sprintf( "on_r%sg%sb%s", $_[0]->colour );
197             }
198              
199             sub toRGB {
200 9 50 66 9 1 6513 return $_[0]->toRGBA( $_[1] ) if $TOOL{numIs}( $_[1] ) and $_[1] != 1;
201 9         45 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 246 $_[0]->{alpha};
207             }
208              
209             sub toHEX {
210             my $colour = sprintf(
211             "#%02lx%02lx%02lx",
212             (
213 28     28 1 529 map { my $c = $TOOL{clamp}( $TOOL{round}($_), 255 ); $c }
  84         239  
  84         4930  
214             $_[0]->colour
215             )
216             );
217 28 100       522 unless ( $_[1] ) {
218 23 100       184 if ( $colour =~ /#(.)\1(.)\2(.)\3/g ) {
219 22         146 $colour = sprintf "#%s%s%s", $1, $2, $3;
220             }
221             }
222 28         217 return $colour;
223             }
224              
225             sub toHSL {
226 12     12 1 59 my $hsl = $TOOL{asHSL}($_[0]);
227             sprintf( "hsl(%s,%s,%s)",
228             $hsl->{h},
229             $TOOL{percent}( $hsl->{s} ),
230 12         375 $TOOL{percent}( $hsl->{l} ),
231             );
232             }
233              
234             sub toHSV {
235 8     8 1 8455 my ( $r, $g, $b, $max, $min, $d, $h, $s, $v ) = $TOOL{rgb2hs}( $_[0]->colour );
236              
237 8         23 $v = $max;
238 8 100       23 $s = ( $max == 0 ) ? $max : $d / $max;
239              
240 8 100       247 if ( $max == $min ) {
241 2         39 $h = 0;
242             }
243             else {
244 6 100       99 $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         336 $h /= 6;
248             }
249              
250             return sprintf( "hsv(%s,%s,%s)",
251             ( $h * 360 ),
252             $TOOL{percent}($s),
253 8         129 $TOOL{percent}($v),
254             );
255             }
256              
257             sub lighten {
258 11     11 1 226929 my ( $colour, $amt, $meth, $hsl ) = @_;
259              
260 11         40 ( $hsl, $colour ) = $TOOL{hsl}($colour);
261              
262 11         36 $amt = $TOOL{depercent}($amt);
263             $hsl->{l} += $TOOL{clamp}(
264             ( $meth && $meth eq 'relative' )
265 11 100 100     238 ? (($hsl->{l} || 1) * $amt)
      50        
266             : $amt, 1
267             );
268              
269 11         802 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
270             }
271              
272             sub darken {
273 12     12 1 242712 my ( $colour, $amt, $meth, $hsl ) = @_;
274              
275 12         44 ( $hsl, $colour ) = $TOOL{hsl}($colour);
276              
277 12         42 $amt = $TOOL{depercent}($amt);
278             $hsl->{l} -= $TOOL{clamp}(
279             ( $meth && $meth eq 'relative' )
280 12 100 100     256 ? $hsl->{l} * $amt
281             : $amt, 1,
282             );
283              
284 12         897 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
285             }
286              
287             sub fade {
288 12     12 1 236009 my ($colour, $amt, $hsl) = @_;
289              
290 12         33 ($hsl, $colour) = $TOOL{hsl}($colour);
291 12         21 $hsl->{a} = $TOOL{depercent}($amt);
292              
293 12         130 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
294             }
295              
296             sub fadeout {
297 14     14 1 230319 my ($colour, $amt, $meth, $hsl) = @_;
298              
299 14         39 ($hsl, $colour) = $TOOL{hsl}($colour);
300             $hsl->{a} -= (($meth && $meth eq 'relative')
301             ? $hsl->{a} * $TOOL{depercent}($amt)
302 14 100 100     69 : $TOOL{depercent}($amt));
303 14         294 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
304             }
305              
306             sub fadein {
307 14     14 1 221474 my ($colour, $amt, $meth, $hsl) = @_;
308 14         57 ($hsl, $colour) = $TOOL{hsl}($colour);
309             $hsl->{a} += ($meth && $meth eq 'relative')
310             ? $hsl->{a} * $TOOL{depercent}($amt)
311 14 100 100     86 : $TOOL{depercent}($amt);
312 14         391 $hsl->{a} = smallnum::_smallnum($hsl->{a});
313 14         90 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 39301 my @rgb = @{ $_[0]->{colour} };
  10193         20751  
381 10193 100       97373 my $r = defined $rgb[0] ? $rgb[0] : 255;
382 10193 100       17809 my $g = defined $rgb[1] ? $rgb[1] : 255;
383 10193 100       16218 my $b = defined $rgb[2] ? $rgb[2] : 255;
384 10193         29115 return ( $r, $g, $b );
385             }
386              
387             sub validate {
388 4     4 1 181884 my ($self, $colour) = @_;
389 4         4 my $new = eval { $self->new($colour) };
  4         11  
390 4 100       29 if ($@) {
391             return {
392             valid => \0,
393 3   50     17 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__