web/lib/photologue/utils/reflection.py
author ymh <ymh.work@gmail.com>
Thu, 21 Jan 2010 18:41:10 +0100
changeset 5 10b1f6d8a5d2
permissions -rw-r--r--
first debug version
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
5
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
""" Function for generating web 2.0 style image reflection effects.
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
Copyright (c) 2007, Justin C. Driscoll
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
All rights reserved.
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
Redistribution and use in source and binary forms, with or without modification,
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
are permitted provided that the following conditions are met:
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
    1. Redistributions of source code must retain the above copyright notice,
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
       this list of conditions and the following disclaimer.
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
    2. Redistributions in binary form must reproduce the above copyright
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
       notice, this list of conditions and the following disclaimer in the
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
       documentation and/or other materials provided with the distribution.
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
    3. Neither the name of reflection.py nor the names of its contributors may be used
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
       to endorse or promote products derived from this software without
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
       specific prior written permission.
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
"""
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
try:
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
    import Image
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
    import ImageColor
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
except ImportError:
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
    try:
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
        from PIL import Image
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
        from PIL import ImageColor
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
    except ImportError:
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
        raise ImportError("The Python Imaging Library was not found.")
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
def add_reflection(im, bgcolor="#00000", amount=0.4, opacity=0.6):
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
    """ Returns the supplied PIL Image (im) with a reflection effect
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
    bgcolor  The background color of the reflection gradient
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
    amount   The height of the reflection as a percentage of the orignal image
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
    opacity  The initial opacity of the reflection gradient
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
    Originally written for the Photologue image management system for Django
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
    and Based on the original concept by Bernd Schlapsi
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
    """
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
    # convert bgcolor string to rgb value
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
    background_color = ImageColor.getrgb(bgcolor)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
    # copy orignial image and flip the orientation
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
    reflection = im.copy().transpose(Image.FLIP_TOP_BOTTOM)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
    # create a new image filled with the bgcolor the same size
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
    background = Image.new("RGB", im.size, background_color)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
    # calculate our alpha mask
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
    start = int(255 - (255 * opacity)) # The start of our gradient
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
    steps = int(255 * amount) # the number of intermedite values
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
    increment = (255 - start) / float(steps)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
    mask = Image.new('L', (1, 255))
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
    for y in range(255):
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
        if y < steps:
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
            val = int(y * increment + start)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
        else:
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
            val = 255
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
        mask.putpixel((0, y), val)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
    alpha_mask = mask.resize(im.size)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
    # merge the reflection onto our background color using the alpha mask
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
    reflection = Image.composite(background, reflection, alpha_mask)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
    # crop the reflection
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
    reflection_height = int(im.size[1] * amount)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
    reflection = reflection.crop((0, 0, im.size[0], reflection_height))
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
    # create new image sized to hold both the original image and the reflection
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
    composite = Image.new("RGB", (im.size[0], im.size[1]+reflection_height), background_color)
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
    # paste the orignal image and the reflection into the composite image
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
    composite.paste(im, (0, 0))
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
    composite.paste(reflection, (0, im.size[1]))
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
    # return the image complete with reflection effect
10b1f6d8a5d2 first debug version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
    return composite