summaryrefslogtreecommitdiff
path: root/rapid/dropshadow.py
blob: 68b539882aad62679601250900916fe4231dd26a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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)