diff options
Diffstat (limited to 'rapid/dropshadow.py')
-rwxr-xr-x | rapid/dropshadow.py | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/rapid/dropshadow.py b/rapid/dropshadow.py new file mode 100755 index 0000000..68b5398 --- /dev/null +++ b/rapid/dropshadow.py @@ -0,0 +1,183 @@ +#!/usr/bin/python + + +import StringIO +import gtk +from PIL import Image, ImageFilter + +def image_to_pixbuf(image): + # this one handles transparency, unlike the default example in the pygtk FAQ + # this is also from the pygtk FAQ + IS_RGBA = image.mode=='RGBA' + return gtk.gdk.pixbuf_new_from_data( + image.tostring(), # data + gtk.gdk.COLORSPACE_RGB, # color mode + IS_RGBA, # has alpha + 8, # bits + image.size[0], # width + image.size[1], # height + (IS_RGBA and 4 or 3) * image.size[0] # rowstride + ) + + +def image_to_pixbuf_no_transparency(image): + fd = StringIO.StringIO() + image.save(fd, "ppm") + contents = fd.getvalue() + fd.close() + loader = gtk.gdk.PixbufLoader("pnm") + loader.write(contents, len(contents)) + pixbuf = loader.get_pixbuf() + loader.close() + return pixbuf + +def pixbuf_to_image(pb): + assert(pb.get_colorspace() == gtk.gdk.COLORSPACE_RGB) + dimensions = pb.get_width(), pb.get_height() + stride = pb.get_rowstride() + pixels = pb.get_pixels() + + mode = pb.get_has_alpha() and "RGBA" or "RGB" + image = Image.frombuffer(mode, dimensions, pixels, + "raw", mode, stride, 1) + + if mode == "RGB": + # convert to having an alpha value, so that the image can + # act as a mask in the drop shadow paste + image = image.convert("RGBA") + + return image + + +class DropShadow(): + """ + Adds a gaussian blur drop shadow to a PIL image. + + Caches backgrounds of particular sizes for improved performance. + + Backgrounds can be made transparent. + + Modification of code from Kevin Schluff and Matimus + License: Python license + See: + http://code.activestate.com/recipes/474116/ (r2) + http://bytes.com/topic/python/answers/606952-pil-paste-image-top-other-dropshadow + + """ + + def __init__(self, offset=(5,5), background_color=0xffffff, shadow = (0x44, 0x44, 0x44, 0xff), + border=8, iterations=3, trim_border=False): + """ + offset - Offset of the shadow from the image as an (x,y) tuple. Can be + positive or negative. + background_color - Background colour behind the image. + shadow - Shadow colour (darkness). + border - Width of the border around the image. This must be wide + enough to account for the blurring of the shadow. + trim_border - If true, the border will only be created on the + sides it needs to be (i.e. only on two sides) + iterations - Number of times to apply the filter. More iterations + produce a more blurred shadow, but increase processing time. + + To make backgrounds transparent, ensure the alpha value of the shadow color is the + same as the background color, e.g. if background_color is 0xffffff, shadow's alpha should be 0xff + + The image must be in RGBA format. + """ + self.backgrounds = {} + self.offset = offset + self.background_color = background_color + self.shadow = shadow + self.border = border + self.trim_border = trim_border + self.iterations = iterations + + if self.offset[0] < 0 or not self.trim_border: + self.left_spacing = self.border + else: + self.left_spacing = 0 + + if self.offset[1] < 0 or not self.trim_border: + self.top_spacing = self.border + else: + self.top_spacing = 0 + + def dropShadow(self, image): + """ + image - The image to overlay on top of the shadow. + """ + dimensions = (image.size[0], image.size[1]) + if not dimensions in self.backgrounds: + + # Create the backdrop image -- a box in the background colour with a + # shadow on it. + + if self.trim_border: + totalWidth = image.size[0] + abs(self.offset[0]) + self.border + totalHeight = image.size[1] + abs(self.offset[1]) + self.border + else: + totalWidth = image.size[0] + abs(self.offset[0]) + 2 * self.border + totalHeight = image.size[1] + abs(self.offset[1]) + 2 * self.border + + back = Image.new("RGBA", (totalWidth, totalHeight), self.background_color) + + # Place the shadow, taking into account the offset from the image + if self.offset[0] > 0 and self.trim_border: + shadowLeft = max(self.offset[0], 0) + else: + shadowLeft = self.border + max(self.offset[0], 0) + if self.offset[1] > 0 and self.trim_border: + shadowTop = max(self.offset[1], 0) + else: + shadowTop = self.border + max(self.offset[1], 0) + + back.paste(self.shadow, [shadowLeft, shadowTop, shadowLeft + image.size[0], + shadowTop + image.size[1]] ) + + # Apply the filter to blur the edges of the shadow. Since a small kernel + # is used, the filter must be applied repeatedly to get a decent blur. + n = 0 + while n < self.iterations: + back = back.filter(ImageFilter.BLUR) + n += 1 + + self.backgrounds[dimensions] = back + + # Paste the input image onto the shadow backdrop + imageLeft = self.left_spacing - min(self.offset[0], 0) + imageTop = self.top_spacing - min(self.offset[1], 0) + + back = self.backgrounds[dimensions].copy() + back.paste(image, (imageLeft, imageTop), image) + + return back + + + +if __name__ == "__main__": + import sys + import os + import common + + + # create another file with a drop shadow + f = sys.argv[1] + + image = Image.open(f) + image.thumbnail((60,36), Image.ANTIALIAS) + image2 = image.copy() + + path, name = os.path.split(f) + name, ext = os.path.splitext(name) + + #image = dropShadow(image, shadow = (0x44, 0x44, 0x44, 0xff)) + dropShadow = DropShadow(offset=(3,3), shadow = (0x34, 0x34, 0x34, 0xff), border=6) + image = dropShadow.dropShadow(image) + image2 = dropShadow.dropShadow(image2) + + nf = os.path.join(path, "%s_small_shadow%s" % (name, ext)) + nf2 = os.path.join(path, "%s_small_shadow2%s" % (name, ext)) + image.save(nf) + image2.save(nf2) + print "wrote %s , %s" % (nf, nf2) + |