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__ |