diff options
author | Magnus Hagander | 2022-06-26 19:31:44 +0000 |
---|---|---|
committer | Magnus Hagander | 2022-06-26 22:04:44 +0000 |
commit | e4d50c8012b5403aa1e8a2341ab77ce4182cdaac (patch) | |
tree | 2f9bad5f7fdfec66970c9e86bc299d2990a37769 /postgresqleu/util/fields.py | |
parent | 897738b31e86ba297285d232aa4b21409fc25e66 (diff) |
Support higher resolution speaker photos
This adds a new field to the speaker model supporting 512x512 sized
speaker photos.
It also changes the speaker form (as well as the backend form) to accept
any size image and resize it into the bounding box of 512x512. There is
no reason to force the speaker to do that manually, computers are good
at such things. The rezied image will be padded as necessary to the full
512x512 with transparent background, which also means the storage format
of the new photos will be PNG in order to support that transparency.
Whenever the photo is updated, a downscaled copy will automatically be
generated that's 128x128 for backwards compatibilty as well as
thumbnailing. There is no interface to independently edit this lower
resolution photo.
Fixes #15
Diffstat (limited to 'postgresqleu/util/fields.py')
-rw-r--r-- | postgresqleu/util/fields.py | 41 |
1 files changed, 33 insertions, 8 deletions
diff --git a/postgresqleu/util/fields.py b/postgresqleu/util/fields.py index ae41061a..88b381b0 100644 --- a/postgresqleu/util/fields.py +++ b/postgresqleu/util/fields.py @@ -2,7 +2,9 @@ from django.db import models from django.core.exceptions import ValidationError from .forms import ImageBinaryFormField, PdfBinaryFormField -from PIL import ImageFile +import io + +from PIL import Image, ImageFile from postgresqleu.util.magic import magicdb @@ -19,7 +21,8 @@ class ImageBinaryField(models.Field): empty_values = [None, b''] def __init__(self, max_length, *args, **kwargs): - self.max_resolution = kwargs.pop('max_resolution', None) + self.resolution = kwargs.pop('resolution', None) + self.auto_scale = kwargs.pop('auto_scale', False) super(ImageBinaryField, self).__init__(*args, **kwargs) self.max_length = max_length @@ -65,12 +68,34 @@ class ImageBinaryField(models.Field): except Exception as e: raise ValidationError("Could not parse image: %s" % e) - if img.format.upper() != 'JPEG': - raise ValidationError("Only JPEG files are allowed") - - if self.max_resolution: - if img.size[0] > self.max_resolution[0] or img.size[1] > self.max_resolution[1]: - raise ValidationError("Maximum image size is {}x{}".format(*self.max_resolution)) + if img.format.upper() not in ('JPEG', 'PNG'): + raise ValidationError("Only JPEG or PNG files are allowed") + + if self.resolution: + if img.size[0] != self.resolution[0] or img.size[1] != self.resolution[1]: + if self.auto_scale: + scale = min( + float(self.resolution[0]) / float(img.size[0]), + float(self.resolution[1]) / float(img.size[1]), + ) + newimg = img.resize( + (int(img.size[0] * scale), int(img.size[1] * scale)), + Image.BICUBIC, + ) + saver = io.BytesIO() + if newimg.size[0] != newimg.size[1]: + # This is not a square, so we have to roll it again + centeredimg = Image.new('RGBA', self.resolution) + centeredimg.paste(newimg, ( + (self.resolution[0] - newimg.size[0]) // 2, + (self.resolution[1] - newimg.size[1]) // 2, + )) + centeredimg.save(saver, format='PNG') + else: + newimg.save(saver, format="PNG") + value = saver.getvalue() + else: + raise ValidationError("Image size must be {}x{}".format(*self.resolution)) return value |