[FIX] tools: some import cleaning and fixing.
[odoo/odoo.git] / openerp / tools / image.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2012-today OpenERP s.a. (<http://openerp.com>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import io
23 import sys
24 import StringIO
25
26 from PIL import Image
27 from PIL import ImageFilter
28 from random import random
29
30 # ----------------------------------------
31 # Image resizing
32 # ----------------------------------------
33
34 def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', filetype='PNG', avoid_if_small=False):
35     """ Function to resize an image. The image will be resized to the given
36         size, while keeping the aspect ratios, and holes in the image will be
37         filled with transparent background. The image will not be stretched if
38         smaller than the expected size.
39         Steps of the resizing:
40         - if avoid_if_small: if both image sizes are smaller than the requested
41           sizes, the original image is returned. This is used to avoid adding
42           transparent content around images that we do not want to alter but
43           just resize if too big. This is used for example when storing images
44           in the 'image' field: we keep the original image, resized to a maximal
45           size, without adding transparent content around it if smaller.
46         - create a thumbnail of the source image through using the thumbnail
47           function. Aspect ratios are preserved when using it. Note that if the
48           source image is smaller than the expected size, it will not be
49           extended, but filled to match the size.
50         - create a transparent background that will hold the final
51           image.
52         - past the thumbnail on the transparent background and center
53           it.
54
55         :param base64_source: base64-encoded version of the source
56             image; if False, returns False
57         :param size: tuple(height, width)
58         :param encoding: the output encoding
59         :param filetype: the output filetype
60         :param avoid_if_small: do not resize if image height and width
61             are smaller than the expected size.
62     """
63     if not base64_source:
64         return False
65     image_stream = io.BytesIO(base64_source.decode(encoding))
66     image = Image.open(image_stream)
67     # check image size: do not create a thumbnail if avoiding smaller images
68     if avoid_if_small and image.size[0] <= size[0] and image.size[1] <= size[1]:
69         return base64_source
70
71     if (float(image.size[0])/image.size[1]) > (float(size[0]) / size[1]):
72         ibox = (size[1] * image.size[0] / image.size[1] , size[1])
73         deltax = max((size[1] * image.size[0] / image.size[1] - size[0]) / 2, 0)
74         deltay = 0
75     else:
76         ibox = (size[0],size[0] * image.size[1] / image.size[0])
77         deltax = 0
78         deltay = max((size[0] * image.size[1] / image.size[0] - size[1]) / 2, 0)
79
80     im2 = image.resize(ibox, Image.ANTIALIAS)
81     background = im2.crop((deltax, deltay, deltax+size[0], deltay+size[1]))
82     background_stream = StringIO.StringIO()
83     background.save(background_stream, filetype)
84     return background_stream.getvalue().encode(encoding)
85
86 def image_resize_image_big(base64_source, size=(1204, 1204), encoding='base64', filetype='PNG', avoid_if_small=True):
87     """ Wrapper on image_resize_image, to resize images larger than the standard
88         'big' image size: 1024x1024px.
89         :param size, encoding, filetype, avoid_if_small: refer to image_resize_image
90     """
91     return image_resize_image(base64_source, size, encoding, filetype, avoid_if_small)
92
93 def image_resize_image_medium(base64_source, size=(128, 128), encoding='base64', filetype='PNG', avoid_if_small=False):
94     """ Wrapper on image_resize_image, to resize to the standard 'medium'
95         image size: 180x180.
96         :param size, encoding, filetype, avoid_if_small: refer to image_resize_image
97     """
98     return image_resize_image(base64_source, size, encoding, filetype, avoid_if_small)
99
100 def image_resize_image_small(base64_source, size=(64, 64), encoding='base64', filetype='PNG', avoid_if_small=False):
101     """ Wrapper on image_resize_image, to resize to the standard 'small' image
102         size: 50x50.
103         :param size, encoding, filetype, avoid_if_small: refer to image_resize_image
104     """
105     return image_resize_image(base64_source, size, encoding, filetype, avoid_if_small)
106
107 # ----------------------------------------
108 # Colors
109 # ---------------------------------------
110
111 def image_colorize(original, randomize=True, color=(255, 255, 255)):
112     """ Add a color to the transparent background of an image.
113         :param original: file object on the original image file
114         :param randomize: randomize the background color
115         :param color: background-color, if not randomize
116     """
117     # create a new image, based on the original one
118     original = Image.open(io.BytesIO(original))
119     image = Image.new('RGB', original.size)
120     # generate the background color, past it as background
121     if randomize:
122         color = (int(random() * 192 + 32), int(random() * 192 + 32), int(random() * 192 + 32))
123     image.paste(color)
124     image.paste(original, mask=original)
125     # return the new image
126     buffer = StringIO.StringIO()
127     image.save(buffer, 'PNG')
128     return buffer.getvalue()
129
130 # ----------------------------------------
131 # Misc image tools
132 # ---------------------------------------
133
134 def image_get_resized_images(base64_source, return_big=False, return_medium=True, return_small=True,
135     big_name='image', medium_name='image_medium', small_name='image_small',
136     avoid_resize_big=True, avoid_resize_medium=False, avoid_resize_small=False):
137     """ Standard tool function that returns a dictionary containing the
138         big, medium and small versions of the source image. This function
139         is meant to be used for the methods of functional fields for
140         models using images.
141
142         Default parameters are given to be used for the getter of functional
143         image fields,  for example with res.users or res.partner. It returns
144         only image_medium and image_small values, to update those fields.
145
146         :param base64_source: base64-encoded version of the source
147             image; if False, all returnes values will be False
148         :param return_{..}: if set, computes and return the related resizing
149             of the image
150         :param {..}_name: key of the resized image in the return dictionary;
151             'image', 'image_medium' and 'image_small' by default.
152         :param avoid_resize_[..]: see avoid_if_small parameter
153         :return return_dict: dictionary with resized images, depending on
154             previous parameters.
155     """
156     return_dict = dict()
157     if return_big:
158         return_dict[big_name] = image_resize_image_big(base64_source, avoid_if_small=avoid_resize_big)
159     if return_medium:
160         return_dict[medium_name] = image_resize_image_medium(base64_source, avoid_if_small=avoid_resize_medium)
161     if return_small:
162         return_dict[small_name] = image_resize_image_small(base64_source, avoid_if_small=avoid_resize_small)
163     return return_dict
164