1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2012-today OpenERP s.a. (<http://openerp.com>).
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.
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.
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/>.
20 ##############################################################################
26 from PIL import ImageEnhance
27 from random import random
29 # ----------------------------------------
31 # ----------------------------------------
33 def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', filetype='PNG', avoid_if_small=False):
34 """ Function to resize an image. The image will be resized to the given
35 size, while keeping the aspect ratios, and holes in the image will be
36 filled with transparent background. The image will not be stretched if
37 smaller than the expected size.
38 Steps of the resizing:
39 - Compute width and height if not specified.
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 image.
51 - paste the thumbnail on the transparent background and center it.
53 :param base64_source: base64-encoded version of the source
54 image; if False, returns False
55 :param size: 2-tuple(width, height). A None value for any of width or
56 height mean an automatically computed value based respectivelly
57 on height or width of the source image.
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.
65 if size == (None, None):
68 image_stream = io.BytesIO(base64_source.decode(encoding))
69 image = Image.open(image_stream)
71 asked_width, asked_height = size
72 if asked_width is None:
73 asked_width = int(image.size[0] * (float(asked_height) / image.size[1]))
74 if asked_height is None:
75 asked_height = int(image.size[1] * (float(asked_width) / image.size[0]))
76 size = asked_width, asked_height
78 # check image size: do not create a thumbnail if avoiding smaller images
79 if avoid_if_small and image.size[0] <= size[0] and image.size[1] <= size[1]:
81 # create a thumbnail: will resize and keep ratios, then sharpen for better looking result
82 image.thumbnail(size, Image.ANTIALIAS)
83 sharpener = ImageEnhance.Sharpness(image.convert('RGBA'))
84 image = sharpener.enhance(2.0)
85 # create a transparent image for background
86 background = Image.new('RGBA', size, (255, 255, 255, 0))
87 # past the resized image on the background
88 background.paste(image, ((size[0] - image.size[0]) / 2, (size[1] - image.size[1]) / 2))
89 # return an encoded image
90 background_stream = StringIO.StringIO()
91 background.save(background_stream, filetype)
92 return background_stream.getvalue().encode(encoding)
94 def image_resize_image_big(base64_source, size=(1204, 1204), encoding='base64', filetype='PNG', avoid_if_small=True):
95 """ Wrapper on image_resize_image, to resize images larger than the standard
96 'big' image size: 1024x1024px.
97 :param size, encoding, filetype, avoid_if_small: refer to image_resize_image
99 return image_resize_image(base64_source, size, encoding, filetype, avoid_if_small)
101 def image_resize_image_medium(base64_source, size=(128, 128), encoding='base64', filetype='PNG', avoid_if_small=False):
102 """ Wrapper on image_resize_image, to resize to the standard 'medium'
104 :param size, encoding, filetype, avoid_if_small: refer to image_resize_image
106 return image_resize_image(base64_source, size, encoding, filetype, avoid_if_small)
108 def image_resize_image_small(base64_source, size=(64, 64), encoding='base64', filetype='PNG', avoid_if_small=False):
109 """ Wrapper on image_resize_image, to resize to the standard 'small' image
111 :param size, encoding, filetype, avoid_if_small: refer to image_resize_image
113 return image_resize_image(base64_source, size, encoding, filetype, avoid_if_small)
115 # ----------------------------------------
117 # ---------------------------------------
119 def image_colorize(original, randomize=True, color=(255, 255, 255)):
120 """ Add a color to the transparent background of an image.
121 :param original: file object on the original image file
122 :param randomize: randomize the background color
123 :param color: background-color, if not randomize
125 # create a new image, based on the original one
126 original = Image.open(io.BytesIO(original))
127 image = Image.new('RGB', original.size)
128 # generate the background color, past it as background
130 color = (int(random() * 192 + 32), int(random() * 192 + 32), int(random() * 192 + 32))
132 image.paste(original, mask=original)
133 # return the new image
134 buffer = StringIO.StringIO()
135 image.save(buffer, 'PNG')
136 return buffer.getvalue()
138 # ----------------------------------------
140 # ---------------------------------------
142 def image_get_resized_images(base64_source, return_big=False, return_medium=True, return_small=True,
143 big_name='image', medium_name='image_medium', small_name='image_small',
144 avoid_resize_big=True, avoid_resize_medium=False, avoid_resize_small=False):
145 """ Standard tool function that returns a dictionary containing the
146 big, medium and small versions of the source image. This function
147 is meant to be used for the methods of functional fields for
150 Default parameters are given to be used for the getter of functional
151 image fields, for example with res.users or res.partner. It returns
152 only image_medium and image_small values, to update those fields.
154 :param base64_source: base64-encoded version of the source
155 image; if False, all returnes values will be False
156 :param return_{..}: if set, computes and return the related resizing
158 :param {..}_name: key of the resized image in the return dictionary;
159 'image', 'image_medium' and 'image_small' by default.
160 :param avoid_resize_[..]: see avoid_if_small parameter
161 :return return_dict: dictionary with resized images, depending on
166 return_dict[big_name] = image_resize_image_big(base64_source, avoid_if_small=avoid_resize_big)
168 return_dict[medium_name] = image_resize_image_medium(base64_source, avoid_if_small=avoid_resize_medium)
170 return_dict[small_name] = image_resize_image_small(base64_source, avoid_if_small=avoid_resize_small)