"""
State for thumbnails; derivates of Images
"""
import filesystem, exif
import PIL.Image
class Thumbnail (filesystem.File) :
"""
A Thumbnail is a derivate of an Image, usually resized to some other size.
"""
def __init__ (self, image, subdir, target_size) :
"""
Initialize to link against the given `image`.
`subdir` specifies the directory to store this thumbnail in.
`target_size` determines the target resolution of the output thumbnail.
"""
# our file path, image name inside of the given subdir
super(Thumbnail, self).__init__(subdir, image.fsname)
# store
self.image = image
self.target_size = target_size
def stale (self) :
"""
Tests if this thumbnail is stale
"""
if self.image.older_than(self) :
# both exist and this is newer
return False
else :
# this thumb doesn't exist or is older
return True
def calcsize (self, size) :
"""
Compute the *real* size of this thumbnail, from the give actual size and our target size.
Preserves the aspect ratio etc.
"""
# initial image size
width, height = size
# target size
thumb_width, thumb_height = self.target_size
# calc new size, preserving aspect ratio
# calculations ripped from PIL.Image.thumbnail :)
if width > thumb_width :
height = max(height * thumb_width / width, 1)
width = thumb_width
if height > thumb_height :
width = max(width * thumb_height / height, 1)
height = thumb_height
return width, height
## operations
def resize (self, img) :
"""
Resize the given image as needed.
"""
return img.resize(self.calcsize(img.size), resample=PIL.Image.ANTIALIAS)
def transform (self, img, orient_info) :
"""
Transform this image into the output version, resizing, rotating and mirroring in a single step.
"""
# get sizes
img_width, img_height = img_size = img.size
# unpack
mirroring, rotation = orient_info
if mirroring :
# XXX: does anyone actually use this? Untested
# interaction with rotate is probably wrong
p0, p1 = {
exif.MIRROR_HORIZONTAL: ((0, img_height), (img_width, 0)),
exif.MIRROR_VERTICAL: ((img_width, 0), (0, img_height)),
}
if rotation in (exif.ROTATE_90, exif.ROTATE_270) :
# flip the dimensions to take rotate into account
img_size = img_height, img_width
# calc new size
thumb_size = self.calcsize(img_size)
if rotation in (exif.ROTATE_90, exif.ROTATE_270) :
# flip the dimensions back before rotate
thumb_size = thumb_size[1], thumb_size[0]
if mirroring :
# perform the transform, can't use ANTIALIAS here :/
img = img.transform(thumb_size, PIL.Image.EXTENT, p0 + p1, PIL.Image.NEAREST)
else :
# resize with ANTIALIAS
img = img.resize(thumb_size, resample=PIL.Image.ANTIALIAS)
if rotation :
# transform can't rotate
# since these are in steps of 90 degrees, it should keep the right size
# but gah, PIL wants these as counter-clockwise!
img = img.rotate(360 - rotation)
return img
def update (self) :
"""
Render new output thumbnail.
"""
# start with origional image
img = self.image.img
if self.image.orientation and (self.image.orientation[0] or self.image.orientation[1]) :
# rotate
img = self.transform(img, self.image.orientation)
else :
# just create resized copy of main image, using our size
img = self.resize(img)
# write it out
img.save(self.path)