[MERGE] Small image cleaning

- moved the avatar colorize part of the image code to tools/image.py, where it should have been placed at revs 4373-4375
- added the option for not up-sizing image smaller than the resize limits
- default image_small size is now 64x64 px
- default image_medium size is now 128x128 px
- overall code and comments cleaning

bzr revid: tde@openerp.com-20120910074931-b7cqu46r9xx7obmm
This commit is contained in:
Thibault Delavallée 2012-09-10 09:49:31 +02:00
commit 29de4b2ade
3 changed files with 94 additions and 89 deletions

View File

@ -21,7 +21,6 @@
import math
import openerp
import os
from osv import osv, fields
import re
import tools
@ -193,17 +192,16 @@ class res_partner(osv.osv):
'birthdate': fields.char('Birthdate', size=64),
'is_company': fields.boolean('Company', help="Check if the contact is a company, otherwise it is a person"),
'use_parent_address': fields.boolean('Use Company Address', help="Select this if you want to set company's address information for this contact"),
# image: all image fields are base64 encoded and PIL-supported
'image': fields.binary("Image",
help="This field holds the image used as avatar for the "\
"partner. The image is base64 encoded, and PIL-supported. "\
"It is limited to a 1024x1024 px image."),
help="This field holds the image used as avatar for the partner, limited to 1024x1024px"),
'image_medium': fields.function(_get_image, fnct_inv=_set_image,
string="Medium-sized image", type="binary", multi="_get_image",
store={
'res.partner': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Medium-sized image of the partner. It is automatically "\
"resized as a 180x180 px image, with aspect ratio preserved. "\
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views."),
'image_small': fields.function(_get_image, fnct_inv=_set_image,
string="Small-sized image", type="binary", multi="_get_image",
@ -211,7 +209,7 @@ class res_partner(osv.osv):
'res.partner': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Small-sized image of the partner. It is automatically "\
"resized as a 50x50 px image, with aspect ratio preserved. "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required."),
'company_id': fields.many2one('res.company', 'Company', select=1),
'color': fields.integer('Color Index'),
@ -230,33 +228,21 @@ class res_partner(osv.osv):
if is_company:
image = open(openerp.modules.get_module_resource('base', 'static/src/img', 'company_image.png')).read()
else:
from PIL import Image
from StringIO import StringIO
color = (255,255,255)
if colorize:
from random import random
color = (int(random() * 192 + 32), int(random() * 192 + 32), int(random() * 192 + 32))
face = Image.open(openerp.modules.get_module_resource('base', 'static/src/img', 'avatar.png'))
avatar = Image.new('RGB', face.size)
avatar.paste(color)
avatar.paste(face, mask=face)
buffer = StringIO()
avatar.save(buffer, 'PNG')
image = buffer.getvalue()
return image.encode('base64')
image = tools.image_colorize(open(openerp.modules.get_module_resource('base', 'static/src/img', 'avatar.png')).read())
return tools.image_resize_image_big(image.encode('base64'))
_defaults = {
'active': True,
'lang': lambda self, cr, uid, context: context.get('lang', 'en_US'),
'tz': lambda self, cr, uid, context: context.get('tz', False),
'lang': lambda self, cr, uid, ctx: ctx.get('lang', 'en_US'),
'tz': lambda self, cr, uid, ctx: ctx.get('tz', False),
'customer': True,
'category_id': _default_category,
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'res.partner', context=c),
'company_id': lambda self, cr, uid, ctx: self.pool.get('res.company')._company_default_get(cr, uid, 'res.partner', context=ctx),
'color': 0,
'is_company': False,
'type': 'default',
'use_parent_address': True,
'image': lambda self, cr, uid, context: self._get_default_image(cr, uid, context.get('default_is_company', False), context),
'image': lambda self, cr, uid, ctx: self._get_default_image(cr, uid, ctx.get('default_is_company', False), ctx),
}
def copy(self, cr, uid, id, default=None, context=None):

View File

@ -251,7 +251,7 @@ class res_users(osv.osv):
'company_id': _get_company,
'company_ids': _get_companies,
'groups_id': _get_group,
'image': lambda self, cr, uid, context: self.pool.get('res.partner')._get_default_image(cr, uid, False, context, colorize=True),
'image': lambda self, cr, uid, ctx={}: self.pool.get('res.partner')._get_default_image(cr, uid, False, ctx, colorize=True),
}
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):

View File

@ -20,9 +20,12 @@
##############################################################################
import io
from PIL import Image
import StringIO
from PIL import Image
from PIL import ImageFilter
from random import random
# ----------------------------------------
# Image resizing
# ----------------------------------------
@ -49,13 +52,15 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
it.
:param base64_source: base64-encoded version of the source
image
image; if False, returns False
:param size: tuple(height, width)
:param encoding: the output encoding
:param filetype: the output filetype
:param avoid_if_small: do not resize if image height and width
are smaller than the expected size.
"""
if not base64_source:
return False
image_stream = io.BytesIO(base64_source.decode(encoding))
image = Image.open(image_stream)
# check image size: do not create a thumbnail if avoiding smaller images
@ -63,6 +68,7 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
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
@ -72,42 +78,57 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
background.save(background_stream, filetype)
return background_stream.getvalue().encode(encoding)
def image_resize_image_big(base64_source, size=(1204, 1204), encoding='base64', filetype='PNG'):
def image_resize_image_big(base64_source, size=(1204, 1204), encoding='base64', filetype='PNG', avoid_if_small=True):
""" Wrapper on image_resize_image, to resize images larger than the standard
'big' image size: 1024x1024px.
:param base64_source: base64 encoded source image. If False,
the function returns False.
:param size, encoding, filetype, avoid_if_small: refer to image_resize_image
"""
if not base64_source:
return False
return image_resize_image(base64_source, size, encoding, filetype, True)
return image_resize_image(base64_source, size, encoding, filetype, avoid_if_small)
def image_resize_image_medium(base64_source, size=(180, 180), encoding='base64', filetype='PNG'):
def image_resize_image_medium(base64_source, size=(128, 128), encoding='base64', filetype='PNG', avoid_if_small=False):
""" Wrapper on image_resize_image, to resize to the standard 'medium'
image size: 180x180.
:param base64_source: base64 encoded source image. If False,
the function returns False.
:param size, encoding, filetype, avoid_if_small: refer to image_resize_image
"""
if not base64_source:
return False
return image_resize_image(base64_source, size, encoding, filetype)
return image_resize_image(base64_source, size, encoding, filetype, avoid_if_small)
def image_resize_image_small(base64_source, size=(50, 50), encoding='base64', filetype='PNG'):
def image_resize_image_small(base64_source, size=(64, 64), encoding='base64', filetype='PNG', avoid_if_small=False):
""" Wrapper on image_resize_image, to resize to the standard 'small' image
size: 50x50.
:param base64_source: base64 encoded source image. If False,
the function returns False.
:param size, encoding, filetype, avoid_if_small: refer to image_resize_image
"""
if not base64_source:
return False
return image_resize_image(base64_source, size, encoding, filetype)
return image_resize_image(base64_source, size, encoding, filetype, avoid_if_small)
# ----------------------------------------
# Colors
# ---------------------------------------
def image_colorize(original, randomize=True, color=(255, 255, 255)):
""" Add a color to the transparent background of an image.
:param original: file object on the original image file
:param randomize: randomize the background color
:param color: background-color, if not randomize
"""
# create a new image, based on the original one
original = Image.open(io.BytesIO(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))
image.paste(color)
image.paste(original, mask=original)
# return the new image
buffer = StringIO.StringIO()
image.save(buffer, 'PNG')
return buffer.getvalue()
# ----------------------------------------
# Misc image tools
# ---------------------------------------
def image_get_resized_images(base64_source, return_big=False, return_medium=True, return_small=True,
big_name='image', medium_name='image_medium', small_name='image_small'):
big_name='image', medium_name='image_medium', small_name='image_small',
avoid_resize_big=True, avoid_resize_medium=False, avoid_resize_small=False):
""" Standard tool function that returns a dictionary containing the
big, medium and small versions of the source image. This function
is meant to be used for the methods of functional fields for
@ -117,23 +138,21 @@ def image_get_resized_images(base64_source, return_big=False, return_medium=True
image fields, for example with res.users or res.partner. It returns
only image_medium and image_small values, to update those fields.
:param base64_source: if set to False, other values are set to False
also. The purpose is to be linked to the fields that hold images in
OpenERP and that are binary fields.
:param return_big: if set, return_dict contains the 'big_name' entry
:param return_medium: if set, return_dict contains the 'medium_name' entry
:param return_small: if set, return_dict contains the 'small_name' entry
:param big_name: name related to the big version of the image;
'image' by default.
:param medium_name: name related to the medium version of the
image; 'image_medium' by default.
:param small_name: name related to the small version of the
image; 'image_small' by default.
:param base64_source: base64-encoded version of the source
image; if False, all returnes values will be False
:param return_{..}: if set, computes and return the related resizing
of the image
:param {..}_name: key of the resized image in the return dictionary;
'image', 'image_medium' and 'image_small' by default.
:param avoid_resize_[..]: see avoid_if_small parameter
:return return_dict: dictionary with resized images, depending on
previous parameters.
"""
return_dict = dict()
if return_big: return_dict[big_name] = image_resize_image_big(base64_source)
if return_medium: return_dict[medium_name] = image_resize_image_medium(base64_source)
if return_small: return_dict[small_name] = image_resize_image_small(base64_source)
if return_big:
return_dict[big_name] = image_resize_image_big(base64_source, avoid_if_small=avoid_resize_big)
if return_medium:
return_dict[medium_name] = image_resize_image_medium(base64_source, avoid_if_small=avoid_resize_medium)
if return_small:
return_dict[small_name] = image_resize_image_small(base64_source, avoid_if_small=avoid_resize_small)
return return_dict