[IMP] don't reimplement randint manually
[odoo/odoo.git] / openerp / tools / image.py
index f673906..d6a9e3b 100644 (file)
 #
 ##############################################################################
 
-import io
-import StringIO
+try:
+    import cStringIO as StringIO
+except ImportError:
+    import StringIO
 
 from PIL import Image
-from PIL import ImageFilter
-from random import random
+from PIL import ImageOps
+from random import randint
 
 # ----------------------------------------
 # Image resizing
@@ -36,6 +38,7 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
         filled with transparent background. The image will not be stretched if
         smaller than the expected size.
         Steps of the resizing:
+        - Compute width and height if not specified.
         - if avoid_if_small: if both image sizes are smaller than the requested
           sizes, the original image is returned. This is used to avoid adding
           transparent content around images that we do not want to alter but
@@ -46,14 +49,14 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
           function. Aspect ratios are preserved when using it. Note that if the
           source image is smaller than the expected size, it will not be
           extended, but filled to match the size.
-        - create a transparent background that will hold the final
-          image.
-        - past the thumbnail on the transparent background and center
-          it.
+        - create a transparent background that will hold the final image.
+        - paste the thumbnail on the transparent background and center it.
 
         :param base64_source: base64-encoded version of the source
             image; if False, returns False
-        :param size: tuple(height, width)
+        :param size: 2-tuple(width, height). A None value for any of width or
+            height mean an automatically computed value based respectivelly
+            on height or width of the source image.
         :param encoding: the output encoding
         :param filetype: the output filetype
         :param avoid_if_small: do not resize if image height and width
@@ -61,21 +64,28 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
     """
     if not base64_source:
         return False
-    image_stream = io.BytesIO(base64_source.decode(encoding))
+    if size == (None, None):
+        return base64_source
+    image_stream = StringIO.StringIO(base64_source.decode(encoding))
     image = Image.open(image_stream)
+
+    asked_width, asked_height = size
+    if asked_width is None:
+        asked_width = int(image.size[0] * (float(asked_height) / image.size[1]))
+    if asked_height is None:
+        asked_height = int(image.size[1] * (float(asked_width) / image.size[0]))
+    size = asked_width, asked_height
+
     # check image size: do not create a thumbnail if avoiding smaller images
     if avoid_if_small and image.size[0] <= size[0] and image.size[1] <= size[1]:
         return base64_source
-    # create a thumbnail: will resize and keep ratios
-    image.thumbnail(size, Image.ANTIALIAS)
-    image = image.filter(ImageFilter.SHARPEN)
-    # create a transparent image for background
-    background = Image.new('RGBA', size, (255, 255, 255, 0))
-    # past the resized image on the background
-    background.paste(image, ((size[0] - image.size[0]) / 2, (size[1] - image.size[1]) / 2))
-    # return an encoded image
+
+    if image.size != size:
+        # If you need faster thumbnails you may use use Image.NEAREST
+        image = ImageOps.fit(image, size, Image.ANTIALIAS)
+
     background_stream = StringIO.StringIO()
-    background.save(background_stream, filetype)
+    image.save(background_stream, filetype)
     return background_stream.getvalue().encode(encoding)
 
 def image_resize_image_big(base64_source, size=(1204, 1204), encoding='base64', filetype='PNG', avoid_if_small=True):
@@ -110,11 +120,11 @@ def image_colorize(original, randomize=True, color=(255, 255, 255)):
         :param color: background-color, if not randomize
     """
     # create a new image, based on the original one
-    original = Image.open(io.BytesIO(original))
+    original = Image.open(StringIO.StringIO(original))
     image = Image.new('RGB', original.size)
     # generate the background color, past it as background
     if randomize:
-        color = (int(random() * 192 + 32), int(random() * 192 + 32), int(random() * 192 + 32))
+        color = (randint(32, 224), randint(32, 224), randint(32, 224))
     image.paste(color)
     image.paste(original, mask=original)
     # return the new image
@@ -156,3 +166,14 @@ def image_get_resized_images(base64_source, return_big=False, return_medium=True
     if return_small:
         return_dict[small_name] = image_resize_image_small(base64_source, avoid_if_small=avoid_resize_small)
     return return_dict
+
+
+if __name__=="__main__":
+    import sys
+
+    assert len(sys.argv)==3, 'Usage to Test: image.py SRC.png DEST.png'
+
+    img = file(sys.argv[1],'rb').read().encode('base64')
+    new = image_resize_image(img, (128,100))
+    file(sys.argv[2], 'wb').write(new.decode('base64'))
+