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   912539 use 5.006;
  13         147  
4 13     13   67 use strict;
  13         24  
  13         372  
5 13     13   70 use warnings;
  13         39  
  13         421  
6 13     13   6327 use smallnum;
  13         129582  
  13         61  
7             our $VERSION = '0.25';
8              
9             our (%TOOL, $ANOBJECT);
10              
11             use overload
12 13     13   1918 '""' => sub { $_[0]->toCSS() };
  13     3   27  
  13         80  
  3         389  
13              
14              
15             BEGIN {
16             %TOOL = (
17 30557         352582 clamp => sub { return $TOOL{min}( $TOOL{max}( $_[0], 0 ), $_[1]); },
18 30728 100 100     282335 max => sub { $_[ ($_[0] || 0) < ($_[1] || 0) ] || 0 },
      100        
19 30728 100 100     1537384 min => sub { $_[ ($_[0] || 0) > ($_[1] || 0) ] || 0 },
      100        
20             round => sub {
21 266 100       2188 return sprintf '%.' . ( defined $_[1] ? $_[1] : 0 ) . 'f', $_[0];
22             },
23 10148   100     61846 numIs => sub { return defined $_[0] && $_[0] =~ /^[0-9]+/; },
24 40         601 percent => sub { return ( $_[0] * 100 ) . '%'; },
25 20081         27290 depercent => sub { my $p = shift; $p =~ s/%$//; return $p / 100; },
  20081         30675  
  20081         34493  
26             joinRgb => sub {
27 54         116 return join ',', map { $TOOL{clamp}( $TOOL{round}($_), 255 ); } @_;
  162         3174  
28             },
29             rgb2hs => sub {
30 83         181 my @rgb = map { $_ / 255 } @_;
  249         2550  
31 83         1109 push @rgb, $TOOL{max}( $TOOL{max}( $rgb[0], $rgb[1] ), $rgb[2] );
32 83         4338 push @rgb, $TOOL{min}( $TOOL{min}( $rgb[0], $rgb[1] ), $rgb[2] );
33 83         4310 push @rgb, ( $rgb[3] - $rgb[4] );
34 83         1272 return @rgb;
35             },
36             hue => sub {
37 30221         1363475 my ( $h, $m1, $m2 ) = @_;
38 30221 100       58138 $h = $h < 0 ? $h + 1 : ( $h > 1 ? $h - 1 : $h );
    100          
39 30221 100       595148 if ( $h * 6 < 1 ) { return $m1 + ( $m2 - $m1 ) * $h * 6; }
  5091 100       126063  
    100          
40 9993         469249 elsif ( $h * 2 < 1 ) { return $m2; }
41             elsif ( $h * 3 < 2 ) {
42 5063         349072 return $m1 + ( $m2 - $m1 ) * ( 2 / 3 - $h ) * 6;
43             }
44 20228         696980 return $m1;
45             },
46             scaled => sub {
47 197         1786 my ( $n, $size ) = @_;
48 197 100       397 return ( $n =~ s/%// )
49             ? sprintf( '%.f2', (($n * $size) / 100 ))
50             : return sprintf( "%d", $n );
51             },
52             convertColour => sub {
53 10082         33585 my $colour = shift;
54 10082         28375 my %converter = (
55             '#' => 'hex2rgb',
56             'rgb' => 'rgb2rgb',
57             'hsl' => 'hsl2rgb',
58             'hsla' => 'hsl2rgb',
59             );
60 10082         41277 my $reg = join '|', reverse sort keys %converter;
61 10082 100       59573 if ( $colour =~ s/^($reg)// ) {
62 10081         32559 return $TOOL{ $converter{$1} }($colour);
63             }
64 1   50     24 die $TOOL{MESSAGES}{INVALID_COLOUR} || 'Cannot convert the colour format';
65             },
66             rgb2rgb => sub {
67 19         47 my @numbers = $TOOL{numbers}(shift);
68 19 100 50     79 die $TOOL{MESSAGES}{INVALID_RGB} || 'Cannot convert rgb colour format' unless (scalar @numbers > 2);
69 18         309 return @numbers;
70             },
71             hex2rgb => sub {
72 53         734 my $hex = shift;
73 53         108 my $l = length $hex;
74             return $l != 6
75             ? $l == 3
76 13 100 100     152 ? map { my $h = hex( $_ . $_ ); $_ =~ 0 || $h ? $h : die( $TOOL{MESSAGES}{INVALID_HEX} || 'Cannot convert hex colour format' ) } $hex =~ m/./g
  13   50     117  
77             : die 'hex length must be 3 or 6'
78 53 100 66     460 : map { my $h = hex( $_ ); $_ =~ m/00/ || $h ? $h : die( $TOOL{MESSAGES}{INVALID_HEX} || 'Cannot convert hex colour format' ) } $hex =~ m/../g;
  141 50 0     1074  
  141 100       1017  
79             },
80             hsl2rgb => sub {
81 10074 100       24882 my ( $h, $s, $l, $a, $m1, $m2 ) = scalar @_ > 1 ? @_ : $TOOL{numbers}(shift);
82 10074   66     95285 defined $_ && $_ =~ m/([0-9.]+)/ or die $TOOL{MESSAGES}{INVALID_HSL} || 'Cannot convert hsl colour format' for ($h, $s, $l);
      50        
      100        
83 10073         28452 $h = ( $h % 360 ) / 360;
84 10073 100       216764 unless ($m1) {
85 10009         17774 $s = $TOOL{depercent}($s);
86 10009         126138 $l = $TOOL{depercent}($l);
87             }
88 10073 100       129040 $m2 = $l <= 0.5 ? $l * ( $s + 1 ) : $l + $s - $l * $s;
89 10073         367655 $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       244703 ($TOOL{clamp}($TOOL{hue}( $h - 1 / 3, $m1, $m2 ), 1) * 255),
94             ( defined $a ? $a : () ),
95             );
96             },
97             numbers => sub {
98 10029         145410 return ( $_[0] =~ m/([0-9.]+)/g );
99             },
100             hsl => sub {
101 63         114 my $colour = shift;
102 63 100       226 if ( ref \$colour eq 'SCALAR' ) {
103 50         196 $colour = Colouring::In->new($colour);
104             }
105 63         142 my $hsl = $TOOL{asHSL}($colour);
106 63         1577 return ( $hsl, $colour );
107             },
108             hash2array => sub {
109 63         92 my $hash = shift;
110 63         124 return map { $hash->{$_} } @_;
  252         529  
111             },
112             asHSL => sub {
113 75         293 my ( $r, $g, $b, $max, $min, $d, $h, $s, $l ) = $TOOL{rgb2hs}( $_[0]->colour );
114              
115 75         209 $l = ( $max + $min ) / 2;
116 75 100       1812 if ( $max == $min ) {
117 68         927 $h = $s = 0;
118             }
119             else {
120 7         99 $d = smallnum::_smallnum($d); #grrr
121 7 100       35 $s = $l > 0.5 ? ($d / ( 2 - $max - $min )) : ($d / ( $max + $min ));
122 7 100       282 $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         475 $h /= 6;
128             }
129              
130             return {
131             h => $h * 360,
132             s => $s,
133             l => $l,
134             a => $_[0]->{alpha},
135 75         254 };
136             }
137 13     13   33546 );
138             }
139              
140             sub import {
141 13     13   329 my ($pkg, @exports) = @_;
142 13         36 my $caller = caller;
143 13 100       63 $TOOL{MESSAGES} = pop @exports if (ref $exports[-1] eq 'HASH');
144 13 100       12369 if (scalar @exports) {
145 13     13   1039 no strict 'refs';
  13         44  
  13         655  
146 5         16 *{"${caller}::${_}"} = \&{"${_[0]}::${_}"} foreach @exports;
  5         8187  
  5         22  
147             }
148             }
149              
150             sub rgb {
151 1     1 1 1289 return $_[0]->rgba( $_[1], $_[2], $_[3], $_[4] );
152             }
153              
154             sub rgba {
155 65     65 1 2550 my $rgb = [ map { $TOOL{scaled}( $_, 255 ) } ( $_[1], $_[2], $_[3] ) ];
  195         2845  
156 65         1402 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 126 my $self = shift;
166 63         148 return $self->rgba($TOOL{hsl2rgb}(@_, 1));
167             }
168              
169             sub new {
170 10142     10142 1 309700 my ( $pkg, $rgb, $a ) = @_;
171              
172 10142         19294 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       20511 if ( ref $rgb eq 'ARRAY' ) {
176 78 100       266 scalar @$rgb == 4 and $a = pop @$rgb;
177 78         921 $self->{colour} = $rgb;
178             } else {
179 10064         18079 $self->{colour} = [ $TOOL{convertColour}($rgb) ];
180 10061 100       605095 scalar @{ $self->{colour} } == 4 and $a = pop @{$self->{colour}};
  10014         93410  
  10061         25945  
181             }
182 10139 100       20014 $self->{alpha} = $TOOL{numIs}($a) ? $a : 1;
183 10139         26707 return $self;
184             }
185              
186             sub toCSS {
187 18     18 1 105 my $alpha = $TOOL{round}( $_[0]->{alpha}, $_[1] );
188 18 100       401 return ( $alpha != 1 ) ? $_[0]->toRGBA() : $_[0]->toHEX( $_[2] );
189             }
190              
191             sub toTerm {
192 5     5 1 3450 return sprintf( "r%sg%sb%s", $_[0]->colour );
193             }
194              
195             sub toOnTerm {
196 5     5 1 19 return sprintf( "on_r%sg%sb%s", $_[0]->colour );
197             }
198              
199             sub toRGB {
200 9 50 66 9 1 4787 return $_[0]->toRGBA( $_[1] ) if $TOOL{numIs}( $_[1] ) and $_[1] != 1;
201 9         36 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 261 $_[0]->{alpha};
207             }
208              
209             sub toHEX {
210             my $colour = sprintf(
211             "#%02lx%02lx%02lx",
212             (
213 28     28 1 457 map { my $c = $TOOL{clamp}( $TOOL{round}($_), 255 ); $c }
  84         172  
  84         4192  
214             $_[0]->colour
215             )
216             );
217 28 100       461 unless ( $_[1] ) {
218 23 100       168 if ( $colour =~ /#(.)\1(.)\2(.)\3/g ) {
219 22         109 $colour = sprintf "#%s%s%s", $1, $2, $3;
220             }
221             }
222 28         139 return $colour;
223             }
224              
225             sub toHSL {
226 12     12 1 44 my $hsl = $TOOL{asHSL}($_[0]);
227             sprintf( "hsl(%s,%s,%s)",
228             $hsl->{h},
229             $TOOL{percent}( $hsl->{s} ),
230 12         295 $TOOL{percent}( $hsl->{l} ),
231             );
232             }
233              
234             sub toHSV {
235 8     8 1 5358 my ( $r, $g, $b, $max, $min, $d, $h, $s, $v ) = $TOOL{rgb2hs}( $_[0]->colour );
236              
237 8         18 $v = $max;
238 8 100       18 $s = ( $max == 0 ) ? $max : $d / $max;
239              
240 8 100       194 if ( $max == $min ) {
241 2         28 $h = 0;
242             }
243             else {
244 6 100       85 $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         265 $h /= 6;
248             }
249              
250             return sprintf( "hsv(%s,%s,%s)",
251             ( $h * 360 ),
252             $TOOL{percent}($s),
253 8         80 $TOOL{percent}($v),
254             );
255             }
256              
257             sub lighten {
258 11     11 1 5237 my ( $colour, $amt, $meth, $hsl ) = @_;
259              
260 11         32 ( $hsl, $colour ) = $TOOL{hsl}($colour);
261              
262 11         28 $amt = $TOOL{depercent}($amt);
263             $hsl->{l} += $TOOL{clamp}(
264             ( $meth && $meth eq 'relative' )
265 11 100 100     197 ? (($hsl->{l} || 1) * $amt)
      50        
266             : $amt, 1
267             );
268              
269 11         674 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
270             }
271              
272             sub darken {
273 12     12 1 6963 my ( $colour, $amt, $meth, $hsl ) = @_;
274              
275 12         34 ( $hsl, $colour ) = $TOOL{hsl}($colour);
276              
277 12         30 $amt = $TOOL{depercent}($amt);
278             $hsl->{l} -= $TOOL{clamp}(
279             ( $meth && $meth eq 'relative' )
280 12 100 100     198 ? $hsl->{l} * $amt
281             : $amt, 1,
282             );
283              
284 12         728 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
285             }
286              
287             sub fade {
288 12     12 1 10873 my ($colour, $amt, $hsl) = @_;
289              
290 12         50 ($hsl, $colour) = $TOOL{hsl}($colour);
291 12         40 $hsl->{a} = $TOOL{depercent}($amt);
292              
293 12         189 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
294             }
295              
296             sub fadeout {
297 14     14 1 9523 my ($colour, $amt, $meth, $hsl) = @_;
298              
299 14         47 ($hsl, $colour) = $TOOL{hsl}($colour);
300             $hsl->{a} -= (($meth && $meth eq 'relative')
301             ? $hsl->{a} * $TOOL{depercent}($amt)
302 14 100 100     67 : $TOOL{depercent}($amt));
303 14         382 return $colour->hsla( $TOOL{hash2array}( $hsl, 'h', 's', 'l', 'a' ) );
304             }
305              
306             sub fadein {
307 14     14 1 10116 my ($colour, $amt, $meth, $hsl) = @_;
308 14         41 ($hsl, $colour) = $TOOL{hsl}($colour);
309             $hsl->{a} += ($meth && $meth eq 'relative')
310             ? $hsl->{a} * $TOOL{depercent}($amt)
311 14 100 100     64 : $TOOL{depercent}($amt);
312 14         335 $hsl->{a} = smallnum::_smallnum($hsl->{a});
313 14         85 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 33420 my @rgb = @{ $_[0]->{colour} };
  10193         20877  
381 10193 100       95783 my $r = defined $rgb[0] ? $rgb[0] : 255;
382 10193 100       16380 my $g = defined $rgb[1] ? $rgb[1] : 255;
383 10193 100       16045 my $b = defined $rgb[2] ? $rgb[2] : 255;
384 10193         23943 return ( $r, $g, $b );
385             }
386              
387             sub validate {
388 4     4 1 2704 my ($self, $colour) = @_;
389 4         9 my $new = eval { $self->new($colour) };
  4         16  
390 4 100       55 if ($@) {
391             return {
392             valid => \0,
393 3   50     23 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     12 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__