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