mirror of https://gitlab.crans.org/bde/nk20
Add animated profile picture support
This commit is contained in:
parent
72c004cb56
commit
82924c999a
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image, ImageSequence
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.forms import AuthenticationForm
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
|
@ -82,13 +82,19 @@ class ImageForm(forms.Form):
|
||||||
height = forms.FloatField(widget=forms.HiddenInput())
|
height = forms.FloatField(widget=forms.HiddenInput())
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""Load image and crop"""
|
"""
|
||||||
|
Load image and crop
|
||||||
|
|
||||||
|
In the future, when Pillow will support APNG we will be able to
|
||||||
|
simplify this code to save only PNG/APNG.
|
||||||
|
"""
|
||||||
cleaned_data = super().clean()
|
cleaned_data = super().clean()
|
||||||
|
|
||||||
# Image size is limited by Django DATA_UPLOAD_MAX_MEMORY_SIZE
|
# Image size is limited by Django DATA_UPLOAD_MAX_MEMORY_SIZE
|
||||||
image = cleaned_data.get('image')
|
image = cleaned_data.get('image')
|
||||||
if image:
|
if image:
|
||||||
# Let Pillow detect and load image
|
# Let Pillow detect and load image
|
||||||
|
# If it is an animation, then there will be multiple frames
|
||||||
try:
|
try:
|
||||||
im = Image.open(image)
|
im = Image.open(image)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -96,20 +102,30 @@ class ImageForm(forms.Form):
|
||||||
# but Pil is unable to load it
|
# but Pil is unable to load it
|
||||||
raise forms.ValidationError(_('This image cannot be loaded.'))
|
raise forms.ValidationError(_('This image cannot be loaded.'))
|
||||||
|
|
||||||
# Crop image
|
# Crop each frame
|
||||||
x = cleaned_data.get('x', 0)
|
x = cleaned_data.get('x', 0)
|
||||||
y = cleaned_data.get('y', 0)
|
y = cleaned_data.get('y', 0)
|
||||||
w = cleaned_data.get('width', 200)
|
w = cleaned_data.get('width', 200)
|
||||||
h = cleaned_data.get('height', 200)
|
h = cleaned_data.get('height', 200)
|
||||||
im = im.crop((x, y, x + w, y + h))
|
frames = []
|
||||||
im = im.resize(
|
for frame in ImageSequence.Iterator(im):
|
||||||
|
frame = frame.crop((x, y, x + w, y + h))
|
||||||
|
frame = frame.resize(
|
||||||
(settings.PIC_WIDTH, settings.PIC_RATIO * settings.PIC_WIDTH),
|
(settings.PIC_WIDTH, settings.PIC_RATIO * settings.PIC_WIDTH),
|
||||||
Image.ANTIALIAS,
|
Image.ANTIALIAS,
|
||||||
)
|
)
|
||||||
|
frames.append(frame)
|
||||||
|
|
||||||
# Save
|
# Save
|
||||||
|
om = frames.pop(0) # Get first frame
|
||||||
|
om.info = im.info # Copy metadata
|
||||||
image.file = io.BytesIO()
|
image.file = io.BytesIO()
|
||||||
im.save(image.file, "PNG")
|
if len(frames) > 1:
|
||||||
|
# Save as GIF
|
||||||
|
om.save(image.file, "GIF", save_all=True, append_images=list(frames), loop=0)
|
||||||
|
else:
|
||||||
|
# Save as PNG
|
||||||
|
om.save(image.file, "PNG")
|
||||||
|
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
|
@ -271,9 +271,17 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
"""Save image to note"""
|
"""Save image to note"""
|
||||||
image_field = form.cleaned_data['image']
|
image = form.cleaned_data['image']
|
||||||
image_field.name = "{}_pic.png".format(self.object.note.pk)
|
|
||||||
self.object.note.display_image = image_field
|
# Rename as a PNG or GIF
|
||||||
|
extension = image.name.split(".")[-1]
|
||||||
|
if extension == "gif":
|
||||||
|
image.name = "{}_pic.gif".format(self.object.note.pk)
|
||||||
|
else:
|
||||||
|
image.name = "{}_pic.png".format(self.object.note.pk)
|
||||||
|
|
||||||
|
# Save
|
||||||
|
self.object.note.display_image = image
|
||||||
self.object.note.save()
|
self.object.note.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue