1#!perl
2use strict;
3use Imager;
4use Getopt::Long;
5
6my $bg;
7my $shadow_size = "10%";
8my $offset = "0x0";
9my $shadow_color = "#404040";
10
11GetOptions(
12	   "bg=s" => \$bg,
13	   "size|s=s" => \$shadow_size,
14	   "o|offset=s" => \$offset,
15	   "s|shadow=s" => \$shadow_color,
16	   );
17
18my $infile = shift;
19my $outfile = shift
20  or die <<EOS;
21Usage: $0 [options] infile outfile
22Options can be any or all of:
23 -bg color - fill the background with a color instead of using
24             transparency, this can be a translucent color.
25 -size size - size of the shadow in pixels, or percent of min dimension
26 -offset <xsize>x<ysize> - offset of the original image within the shadow
27 -shadow color - color of the shadow
28EOS
29
30my $src = Imager->new(file => $infile)
31  or die "Cannot read image file '$infile': ", Imager->errstr, "\n";
32
33# simplify things by always working in RGB rather than grey
34$src = $src->convert(preset => "rgb");
35
36if ($shadow_size =~ /^([0-9]+)%$/) {
37  my $dim = $src->getwidth < $src->getheight ? $src->getwidth : $src->getheight;
38
39  $shadow_size = int($1 * $dim / 100 + 0.5);
40}
41
42my ($x_offset, $y_offset) = $offset =~ /^([+-]?[0-9]+)x([+-]?[0-9]+)$/
43  or die "$0: invalid offset\n";
44
45my $shc = Imager::Color->new($shadow_color)
46  or die "$0: invalid shadow color: ", Imager->errstr, "\n";
47
48my ($red, $green, $blue) = $shc->rgba;
49
50# First create a new image, either with an alpha channel (if you want
51# transparency behind the shadow) or without, if you want a background
52# colour:
53
54my $out = Imager->new
55  (
56   xsize => $shadow_size * 2 + $src->getwidth,
57   ysize => $shadow_size * 2 + $src->getheight,
58   channels => 4,
59  );
60
61if ($bg) {
62  # fill it with your background color, if you want one
63  my $bgc = Imager::Color->new($bg)
64    or die "$0: invalid color '$bg'\n";
65  $out->box(filled => 1, color => $bgc);
66}
67
68# Make a work image to render the shadow on:
69my $shadow_work = Imager->new
70  (
71   xsize => $out->getwidth,
72   ysize => $out->getheight,
73   channels => 1,
74  );
75
76if ($src->getchannels == 4) {
77  # Extract the alpha channel from the source image, if the image has no
78  # alpha, then a solid box then it's simpler, first the alpha version:
79  my $alpha = $src->convert(preset => "alpha");
80
81  # and draw that on the work shadow:
82  $shadow_work->paste
83    (
84     src => $alpha,
85     left => $shadow_size,
86     top => $shadow_size,
87    );
88}
89else {
90  # otherwise just draw a box for the non-alpha source:
91
92  $shadow_work->box
93    (
94     filled => 1,
95     color => [ 255 ],
96     xmin => $shadow_size,
97     ymin => $shadow_size,
98     xmax => $shadow_size + $src->getwidth() - 1,
99     ymax => $shadow_size + $src->getheight() - 1,
100    );
101}
102
103# Blur the work shadow:
104
105$shadow_work->filter(type => "gaussian", stddev => $shadow_size);
106
107# Convert it to an RGB image with alpha:
108
109$shadow_work = $shadow_work->convert
110  (
111   matrix => [ [ 0, $red / 255 ],
112		[ 0, $green / 255 ],
113		[ 0, $blue / 255 ],
114		[ 1 ] ]
115  ) or die $shadow_work->errstr;
116
117# Draw that on the output image:
118
119$out->rubthrough(src => $shadow_work);
120
121# Draw our original image on the output image, perhaps with an offset:
122$out->rubthrough
123  (
124   src => $src,
125   tx => $shadow_size + $x_offset,
126   ty => $shadow_size + $y_offset,
127  );
128
129$out->write(file => $outfile)
130  or die "Cannot write to '$outfile': ", $out->errstr, "\n";
131
132
133