| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Color::Fade; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
25844
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
50
|
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
2057
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
require Exporter; |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our @EXPORT_OK = qw ( |
|
11
|
|
|
|
|
|
|
color_fade |
|
12
|
|
|
|
|
|
|
format_color |
|
13
|
|
|
|
|
|
|
); |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our @EXPORT = qw( |
|
16
|
|
|
|
|
|
|
); |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
|
19
|
|
|
|
|
|
|
our $debug = 0; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub debug { |
|
22
|
0
|
|
|
0
|
0
|
|
my $val = shift; |
|
23
|
0
|
0
|
|
|
|
|
if ($val) { |
|
24
|
0
|
|
|
|
|
|
$debug = 1; |
|
25
|
|
|
|
|
|
|
} |
|
26
|
0
|
|
|
|
|
|
return $debug; |
|
27
|
|
|
|
|
|
|
} |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub format_color { |
|
30
|
0
|
|
|
0
|
1
|
|
my ($format,@codes) = @_; |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# Some pre-defined formats. |
|
33
|
0
|
|
|
|
|
|
my %formats = ( |
|
34
|
|
|
|
|
|
|
html => '$char', |
|
35
|
|
|
|
|
|
|
ubb => '[color=$color]$char[/color]', |
|
36
|
|
|
|
|
|
|
css => '$char', |
|
37
|
|
|
|
|
|
|
); |
|
38
|
0
|
0
|
|
|
|
|
if (exists $formats{$format}) { |
|
39
|
0
|
|
|
|
|
|
$format = $formats{$format}; |
|
40
|
|
|
|
|
|
|
} |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Loop through the given codes. |
|
43
|
0
|
|
|
|
|
|
my @out = (); |
|
44
|
0
|
|
|
|
|
|
foreach my $part (@codes) { |
|
45
|
0
|
|
|
|
|
|
my ($color,$char) = $part =~ /^]+?)>(.+?)$/i; |
|
46
|
0
|
|
|
|
|
|
my $result = $format; |
|
47
|
0
|
|
|
|
|
|
$result =~ s/\$color/$color/ig; |
|
48
|
0
|
|
|
|
|
|
$result =~ s/\$char/$char/ig; |
|
49
|
0
|
|
|
|
|
|
push (@out,$result); |
|
50
|
|
|
|
|
|
|
} |
|
51
|
0
|
0
|
|
|
|
|
return wantarray ? (@out) : join("",@out); |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub color_fade { |
|
55
|
0
|
|
|
0
|
1
|
|
my ($text,@in_colors) = @_; |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# Validate the arguments. |
|
58
|
0
|
0
|
|
|
|
|
if (not length $text) { |
|
59
|
0
|
|
|
|
|
|
warn "You must pass a string with a length > 0 to color_fade."; |
|
60
|
0
|
|
|
|
|
|
return; |
|
61
|
|
|
|
|
|
|
} |
|
62
|
0
|
0
|
|
|
|
|
if (not scalar(@in_colors)) { |
|
63
|
0
|
|
|
|
|
|
warn "You must pass a series of hexadecimal color codes to color_fade."; |
|
64
|
0
|
|
|
|
|
|
return; |
|
65
|
|
|
|
|
|
|
} |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# There must be at least two colors. |
|
68
|
0
|
0
|
|
|
|
|
if (scalar(@in_colors) < 2) { |
|
69
|
0
|
|
|
|
|
|
warn "color_fade requires at least two colors."; |
|
70
|
0
|
|
|
|
|
|
return; |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Validate and clean up color codes. |
|
74
|
0
|
|
|
|
|
|
my @nodes = (); |
|
75
|
0
|
|
|
|
|
|
foreach my $ccode (@in_colors) { |
|
76
|
0
|
|
|
|
|
|
$ccode =~ s/#//g; # Remove hex indicators. |
|
77
|
0
|
0
|
|
|
|
|
if (length $ccode != 6) { |
|
78
|
0
|
|
|
|
|
|
warn "You must pass 6 digit hexadecimal color codes to color_fade."; |
|
79
|
0
|
|
|
|
|
|
return; |
|
80
|
|
|
|
|
|
|
} |
|
81
|
0
|
0
|
|
|
|
|
if ($ccode =~ /^[^A-Fa-f0-9]$/i) { |
|
82
|
0
|
|
|
|
|
|
warn "You must pass 6 digit hexadecimal color codes to color_fade."; |
|
83
|
0
|
|
|
|
|
|
return; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
0
|
|
|
|
|
|
push (@nodes,$ccode); |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# Get the length of the string. |
|
89
|
0
|
|
|
|
|
|
my $len = length $text; |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# Divide the length into segments (number of colors - 1) |
|
92
|
0
|
|
|
|
|
|
my $sections = $len / (scalar(@nodes) - 1); |
|
93
|
0
|
0
|
|
|
|
|
if ($sections =~ /\./) { |
|
94
|
|
|
|
|
|
|
# If it was a decimal, add one and int it. |
|
95
|
0
|
|
|
|
|
|
$sections += 1; |
|
96
|
|
|
|
|
|
|
} |
|
97
|
0
|
|
|
|
|
|
$sections = int($sections); |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# If the length of a given segment of text is too great, no fading will |
|
100
|
|
|
|
|
|
|
# occur (for ex: 255-0-0 to 0-0-0 has a delta of -255 in the red. If the |
|
101
|
|
|
|
|
|
|
# length of this segment is >255 characters, 255/(>255) will result in |
|
102
|
|
|
|
|
|
|
# a fraction less than 1, so no per-character offset will be computed. |
|
103
|
|
|
|
|
|
|
# So, if the length of the segments is gonna be too long, double the |
|
104
|
|
|
|
|
|
|
# number of nodes... |
|
105
|
0
|
|
|
|
|
|
my $giveup = 0; |
|
106
|
0
|
|
|
|
|
|
while ($sections > 128) { |
|
107
|
0
|
|
|
|
|
|
my @newNodes = (); |
|
108
|
0
|
|
|
|
|
|
for (my $i = 0; $i < scalar(@nodes); $i++) { |
|
109
|
0
|
|
|
|
|
|
my $color = $nodes[$i]; |
|
110
|
0
|
|
|
|
|
|
push (@newNodes,$color); |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# If we have another color after the one we're looping |
|
113
|
|
|
|
|
|
|
# on right now--good. |
|
114
|
0
|
0
|
|
|
|
|
if ($i < scalar(@nodes)) { |
|
115
|
0
|
|
|
|
|
|
my $neighbor = $nodes[$i + 1]; |
|
116
|
0
|
0
|
|
|
|
|
$neighbor = $color unless defined $neighbor; |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# Find the average between these two colors. |
|
119
|
0
|
|
|
|
|
|
my $average = Color::Fade::average_colors($color,$neighbor); |
|
120
|
0
|
|
|
|
|
|
push (@newNodes,$average); |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
} |
|
123
|
0
|
|
|
|
|
|
(@nodes) = @newNodes; |
|
124
|
|
|
|
|
|
|
|
|
125
|
0
|
|
|
|
|
|
$sections = $len / (scalar(@nodes) - 1); |
|
126
|
0
|
0
|
|
|
|
|
if ($sections =~ /\./) { |
|
127
|
0
|
|
|
|
|
|
$sections += 1; |
|
128
|
|
|
|
|
|
|
} |
|
129
|
0
|
|
|
|
|
|
$sections = int($sections); |
|
130
|
0
|
|
|
|
|
|
$giveup++; |
|
131
|
0
|
0
|
|
|
|
|
if ($giveup > 100) { |
|
132
|
|
|
|
|
|
|
# After 100 tries to factor this down, let's just give up. |
|
133
|
0
|
|
|
|
|
|
last; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# Split the string into individual characters. |
|
138
|
0
|
|
|
|
|
|
my @chars = split(//, $text); |
|
139
|
0
|
|
|
|
|
|
my @faded = (); |
|
140
|
|
|
|
|
|
|
|
|
141
|
0
|
0
|
|
|
|
|
print "Color::Fade: preparing to fade a string.\n" |
|
142
|
|
|
|
|
|
|
. "length of string: $len\n" |
|
143
|
|
|
|
|
|
|
. "number of nodes (colors): " . scalar(@nodes) . "\n" |
|
144
|
|
|
|
|
|
|
. "number of char per segment: $sections\n" if $debug; |
|
145
|
|
|
|
|
|
|
|
|
146
|
0
|
0
|
|
|
|
|
if ($giveup > 0) { |
|
147
|
0
|
0
|
|
|
|
|
print "Note: the input string was very long: a given\n" |
|
148
|
|
|
|
|
|
|
. "segment would be >128 characters in length, which\n" |
|
149
|
|
|
|
|
|
|
. "doesn't make for a good fade effect. It was factors\n" |
|
150
|
|
|
|
|
|
|
. "down $giveup times.\n" if $debug; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
0
|
0
|
|
|
|
|
print "Color::Fade: beginning the segment loop\n" if $debug; |
|
154
|
|
|
|
|
|
|
|
|
155
|
0
|
|
|
|
|
|
my $nodeStart = 0; |
|
156
|
0
|
|
|
|
|
|
for (my $i = 0; $i < $len; $i += $sections) { |
|
157
|
|
|
|
|
|
|
# Find the length of this segment. |
|
158
|
0
|
|
|
|
|
|
my $seglen = ($i + $sections) - $i; |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# Separate the RGB components of the start and end colors. |
|
161
|
0
|
|
|
|
|
|
my (@RGB_Hex_Start) = $nodes[$nodeStart] =~ /^(..)(..)(..)$/i; # /^([0-9A-Fa-f]{2}){3}$/i; |
|
162
|
0
|
|
|
|
|
|
my (@RGB_Hex_End) = $nodes[$nodeStart + 1] =~ /^(..)(..)(..)$/i; # /^([0-9A-Fa-f]{2}){3}$/i; |
|
163
|
0
|
0
|
|
|
|
|
(@RGB_Hex_End) = (@RGB_Hex_Start) unless scalar(@RGB_Hex_End); |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Convert hexadecimal to decimal. |
|
166
|
0
|
|
|
|
|
|
my @RGB_Dec_Start = ( |
|
167
|
|
|
|
|
|
|
hex ("0x" . $RGB_Hex_Start[0]), |
|
168
|
|
|
|
|
|
|
hex ("0x" . $RGB_Hex_Start[1]), |
|
169
|
|
|
|
|
|
|
hex ("0x" . $RGB_Hex_Start[2]), |
|
170
|
|
|
|
|
|
|
); |
|
171
|
0
|
|
|
|
|
|
my @RGB_Dec_End = ( |
|
172
|
|
|
|
|
|
|
hex ("0x" . $RGB_Hex_End[0]), |
|
173
|
|
|
|
|
|
|
hex ("0x" . $RGB_Hex_End[1]), |
|
174
|
|
|
|
|
|
|
hex ("0x" . $RGB_Hex_End[2]), |
|
175
|
|
|
|
|
|
|
); |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# Find the distances in Red/Green/Blue values. |
|
178
|
0
|
|
|
|
|
|
my $distR = $RGB_Dec_Start[0] - $RGB_Dec_End[0]; |
|
179
|
0
|
|
|
|
|
|
my $distG = $RGB_Dec_Start[1] - $RGB_Dec_End[1]; |
|
180
|
0
|
|
|
|
|
|
my $distB = $RGB_Dec_Start[2] - $RGB_Dec_End[2]; |
|
181
|
|
|
|
|
|
|
|
|
182
|
0
|
0
|
|
|
|
|
$distR < 0 ? $distR = abs($distR) : $distR = -$distR; |
|
183
|
0
|
0
|
|
|
|
|
$distG < 0 ? $distG = abs($distG) : $distG = -$distG; |
|
184
|
0
|
0
|
|
|
|
|
$distB < 0 ? $distB = abs($distB) : $distB = -$distB; |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# Divide each distance by the length of this segment, |
|
187
|
|
|
|
|
|
|
# so we can find out how many characters to operate on. |
|
188
|
0
|
|
|
|
|
|
my $charsR = int($distR / $seglen); |
|
189
|
0
|
|
|
|
|
|
my $charsG = int($distG / $seglen); |
|
190
|
0
|
|
|
|
|
|
my $charsB = int($distB / $seglen); |
|
191
|
|
|
|
|
|
|
|
|
192
|
0
|
0
|
|
|
|
|
print " Segment length: $seglen\n" |
|
193
|
|
|
|
|
|
|
. " RGB Start: " . join("-",@RGB_Dec_Start) . "\n" |
|
194
|
|
|
|
|
|
|
. " RGB End: " . join("-",@RGB_Dec_End) . "\n" |
|
195
|
|
|
|
|
|
|
. " RGB Delta: " . join(" : ",$distR,$distG,$distB) . "\n" |
|
196
|
|
|
|
|
|
|
. " RGB Chars: " . join(" : ",$charsR,$charsG,$charsB) . "\n" if $debug; |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# For each character in this segment... |
|
199
|
0
|
|
|
|
|
|
my ($r,$g,$b) = @RGB_Dec_Start; |
|
200
|
0
|
|
|
|
|
|
for (my $c = $i; $c < ($i + $seglen); $c++) { |
|
201
|
0
|
0
|
|
|
|
|
next unless defined $chars[$c]; |
|
202
|
0
|
0
|
|
|
|
|
print " Working with character $chars[$c]\n" if $debug; |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# Convert each color value back into hex. |
|
205
|
0
|
|
|
|
|
|
my $hexR = sprintf ("%02x", $r); |
|
206
|
0
|
|
|
|
|
|
my $hexG = sprintf ("%02x", $g); |
|
207
|
0
|
|
|
|
|
|
my $hexB = sprintf ("%02x", $b); |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# Turn the hex values into a color code. |
|
210
|
0
|
|
|
|
|
|
my $code = join ("", $hexR, $hexG, $hexB); |
|
211
|
|
|
|
|
|
|
|
|
212
|
0
|
0
|
|
|
|
|
print " Hex code: $code => $chars[$c]\n" if $debug; |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# Prepare an easy to parse color marker for this character. |
|
215
|
0
|
0
|
|
|
|
|
$chars[$c] = " " if $chars[$c] =~ /^[\x0d\x0a]$/; |
|
216
|
0
|
|
|
|
|
|
my $marker = "" . $chars[$c]; |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Append this color information to the output array. |
|
219
|
0
|
|
|
|
|
|
push (@faded,$marker); |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# Increment each color by charsR, charsG, and charsB at a time. |
|
222
|
0
|
|
|
|
|
|
$r += $charsR; |
|
223
|
0
|
|
|
|
|
|
$g += $charsG; |
|
224
|
0
|
|
|
|
|
|
$b += $charsB; |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# Keep the numbers within a valid range. |
|
227
|
0
|
0
|
|
|
|
|
$r = 0 if $r < 0; |
|
228
|
0
|
0
|
|
|
|
|
$g = 0 if $g < 0; |
|
229
|
0
|
0
|
|
|
|
|
$b = 0 if $b < 0; |
|
230
|
0
|
0
|
|
|
|
|
$r = 255 if $r > 255; |
|
231
|
0
|
0
|
|
|
|
|
$g = 255 if $g > 255; |
|
232
|
0
|
0
|
|
|
|
|
$b = 255 if $b > 255; |
|
233
|
|
|
|
|
|
|
|
|
234
|
0
|
0
|
|
|
|
|
print " RGB for next char: $r-$g-$b\n" if $debug; |
|
235
|
|
|
|
|
|
|
} |
|
236
|
|
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
|
$nodeStart++; |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
|
|
240
|
0
|
0
|
|
|
|
|
return wantarray ? @faded : join ("",@faded); |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub average_colors { |
|
244
|
0
|
|
|
0
|
1
|
|
my ($alpha,$beta) = @_; |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# This function, given two hex colors, returns the value of the color |
|
247
|
|
|
|
|
|
|
# directly between the two colors (an average of two). |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# Separate the hex values. |
|
250
|
0
|
|
|
|
|
|
my (@hexStart) = $alpha =~ /^(..)(..)(..)$/i; |
|
251
|
0
|
|
|
|
|
|
my (@hexEnd) = $beta =~ /^(..)(..)(..)$/i; |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# Get their numeric counterparts. |
|
254
|
0
|
|
|
|
|
|
my @decStart = ( |
|
255
|
|
|
|
|
|
|
hex("0x" . $hexStart[0]), |
|
256
|
|
|
|
|
|
|
hex("0x" . $hexStart[1]), |
|
257
|
|
|
|
|
|
|
hex("0x" . $hexStart[2]), |
|
258
|
|
|
|
|
|
|
); |
|
259
|
0
|
|
|
|
|
|
my @decEnd = ( |
|
260
|
|
|
|
|
|
|
hex("0x" . $hexEnd[0]), |
|
261
|
|
|
|
|
|
|
hex("0x" . $hexEnd[1]), |
|
262
|
|
|
|
|
|
|
hex("0x" . $hexEnd[2]), |
|
263
|
|
|
|
|
|
|
); |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# Get the averages of each color. |
|
266
|
0
|
|
|
|
|
|
my $avRed = int( ($decStart[0] + $decEnd[0]) / 2 ); |
|
267
|
0
|
|
|
|
|
|
my $avGrn = int( ($decStart[1] + $decEnd[1]) / 2 ); |
|
268
|
0
|
|
|
|
|
|
my $avBlu = int( ($decStart[2] + $decEnd[2]) / 2 ); |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
# And convert the averages back into hex. |
|
271
|
0
|
|
|
|
|
|
my @hexAvg = ( |
|
272
|
|
|
|
|
|
|
sprintf ("%02x", $avRed), |
|
273
|
|
|
|
|
|
|
sprintf ("%02x", $avGrn), |
|
274
|
|
|
|
|
|
|
sprintf ("%02x", $avBlu), |
|
275
|
|
|
|
|
|
|
); |
|
276
|
0
|
|
|
|
|
|
return join("",@hexAvg); |
|
277
|
|
|
|
|
|
|
} |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
1; |
|
280
|
|
|
|
|
|
|
__END__ |