degal/thumbnail.py
changeset 125 74f135774567
parent 123 31c4a328ef96
equal deleted inserted replaced
124:cac613118e75 125:74f135774567
     1 """
     1 """
     2     State for thumbnails; derivates of Images
     2     State for thumbnails; derivates of Images
     3 """
     3 """
     4 
     4 
     5 import filesystem
     5 import filesystem, exif
     6 
     6 
     7 import PIL.Image
     7 import PIL.Image
     8 
     8 
     9 class Thumbnail (filesystem.File) :
     9 class Thumbnail (filesystem.File) :
    10     """
    10     """
    37         
    37         
    38         else :
    38         else :
    39             # this thumb doesn't exist or is older
    39             # this thumb doesn't exist or is older
    40             return True
    40             return True
    41     
    41     
    42     @property
    42     def calcsize (self, size) :
    43     def size (self) :
       
    44         """
    43         """
    45             Compute the *real* size of this thumbnail, from the image's actual size and our target size.
    44             Compute the *real* size of this thumbnail, from the give actual size and our target size.
    46 
    45 
    47             Preserves the aspect ratio etc.
    46             Preserves the aspect ratio etc.
    48         """
    47         """
    49         
    48         
    50         # real image size
    49         # initial image size
    51         img_width, img_height = self.image.img_size
    50         width, height = size
    52 
    51 
    53         # target size
    52         # target size
    54         thumb_width, thumb_height = self.target_size
    53         thumb_width, thumb_height = self.target_size
    55 
    54 
    56         # calc new size, preserving aspect ratio
    55         # calc new size, preserving aspect ratio
    57         if img_width > thumb_width : 
    56         # calculations ripped from PIL.Image.thumbnail :)
    58             height = max(img_height * thumb_width / img_width, 1)
    57         if width > thumb_width : 
       
    58             height = max(height * thumb_width / width, 1)
    59             width = thumb_width
    59             width = thumb_width
    60         
    60  
    61         elif img_height > thumb_height :
    61         if height > thumb_height :
    62             width = max(img_width * thumb_height / img_height, 1)
    62             width = max(width * thumb_height / height, 1)
    63             height = thumb_height
    63             height = thumb_height
    64         
    64         
    65         return width, height 
    65         return width, height 
    66    
    66    
    67     ## operations
    67     ## operations
    68     def resize (self, img) :
    68     def resize (self, img) :
    69         """
    69         """
    70             Resize the give image as needed.
    70             Resize the given image as needed.
    71         """
    71         """
    72 
    72 
    73         return img.resize(self.size, resample=PIL.Image.ANTIALIAS)
    73         return img.resize(self.calcsize(img.size), resample=PIL.Image.ANTIALIAS)
    74     
    74     
    75     def auto_orient (self, img, orient_info) :
    75     def transform (self, img, orient_info) :
    76         """
    76         """
    77             Automatically orient the image using the given orientation info.
    77             Transform this image into the output version, resizing, rotating and mirroring in a single step.
    78         """
    78         """
       
    79         
       
    80         # get sizes
       
    81         img_width, img_height = img_size = img.size
    79         
    82         
    80         # unpack
    83         # unpack
    81         mirroring, rotation = orient_info
    84         mirroring, rotation = orient_info
       
    85  
       
    86         if mirroring :
       
    87             # XXX: does anyone actually use this? Untested
       
    88             # interaction with rotate is probably wrong
       
    89             p0, p1 = {
       
    90                 exif.MIRROR_HORIZONTAL: ((0, img_height), (img_width, 0)),
       
    91                 exif.MIRROR_VERTICAL:   ((img_width, 0), (0, img_height)),
       
    92             }
    82 
    93 
       
    94         if rotation in (exif.ROTATE_90, exif.ROTATE_270) :
       
    95             # flip the dimensions to take rotate into account
       
    96             img_size = img_height, img_width
       
    97 
       
    98         # calc new size
       
    99         thumb_size = self.calcsize(img_size)
       
   100  
       
   101         if rotation in (exif.ROTATE_90, exif.ROTATE_270) :
       
   102             # flip the dimensions back before rotate
       
   103             thumb_size = thumb_size[1], thumb_size[0]
       
   104        
    83         if mirroring :
   105         if mirroring :
    84             # XXX: does anyone actually use this?
   106             # perform the transform, can't use ANTIALIAS here :/
    85             pass
   107             img = img.transform(thumb_size, PIL.Image.EXTENT, p0 + p1, PIL.Image.NEAREST)
       
   108 
       
   109         else :
       
   110             # resize with ANTIALIAS
       
   111             img = img.resize(thumb_size, resample=PIL.Image.ANTIALIAS)
    86 
   112 
    87         if rotation :
   113         if rotation :
       
   114             # transform can't rotate
    88             # since these are in steps of 90 degrees, it should keep the right size
   115             # since these are in steps of 90 degrees, it should keep the right size
    89             # but gah, PIL wants these as counter-clockwise!
   116             # but gah, PIL wants these as counter-clockwise!
    90             img = img.rotate(360 - rotation)
   117             img = img.rotate(360 - rotation)
    91         
   118         
    92         # ok
       
    93         return img
   119         return img
    94 
   120 
    95     def update (self) :
   121     def update (self) :
    96         """
   122         """
    97             Render new output thumbnail.
   123             Render new output thumbnail.
    98         """
   124         """
    99 
   125 
   100         # start with origional image
   126         # start with origional image
   101         img = self.image.img
   127         img = self.image.img
   102 
   128 
   103         # create resized copy of main image, using our size
   129         if self.image.orientation and (self.image.orientation[0] or self.image.orientation[1]) :
   104         img = self.resize(img)
   130             # rotate
       
   131             img = self.transform(img, self.image.orientation)
   105 
   132 
   106         # got orientation info?
   133         else :
   107         if self.image.orientation :
   134             # just create resized copy of main image, using our size
   108             img = self.auto_orient(img, self.image.orientation)
   135             img = self.resize(img)
   109 
   136 
   110         # write it out
   137         # write it out
   111         img.save(self.path)
   138         img.save(self.path)
   112         
   139