Only load Image once when creating all three Thumbnail sizes.

This commit is contained in:
Will Stott
2019-07-18 14:42:40 +01:00
parent cb24cebd94
commit 510966633a
10 changed files with 122 additions and 140 deletions

View File

@@ -1,13 +1,45 @@
from contextlib import contextmanager
from io import BytesIO
import PIL
from PIL import Image
@contextmanager
def open_django_file(fieldfile):
fieldfile.open()
try:
yield fieldfile
finally:
fieldfile.close()
def scale_and_crop_iter(image, options):
"""
Generator which will yield several variations on the input image.
Resize, crop and/or change quality of image.
:param image: Source image file
:param type: :class:`django.core.files.images.ImageFile
:param`options: List of option dictionaries, See scale_and_crop_single
argument names for available keys.
:type options: list of dict
"""
with open_django_file(image) as img:
im = Image.open(img)
im.load()
for opts in options:
# Use already-loaded file when cropping.
yield scale_and_crop_single(im, **opts)
# this neat function is based on easy-thumbnails
def scale_and_crop(image, size, crop=False, upscale=False, quality=None):
def scale_and_crop_single(image, size, crop=False, upscale=False, quality=None):
"""
Resize, crop and/or change quality of an image.
:param image: Source image file
:param type: :class:`django.core.files.images.ImageFile`
:param type: :class:`PIL.Image`
:param size: Size as width & height, zero as either means unrestricted
:type size: tuple of two int
@@ -24,15 +56,7 @@ def scale_and_crop(image, size, crop=False, upscale=False, quality=None):
:return: Handled image
:rtype: class:`PIL.Image`
"""
# Open image and store format/metadata.
image.open()
im = Image.open(image)
im_format, im_info = im.format, im.info
if quality:
im_info['quality'] = quality
# Force PIL to load image data.
im.load()
im = image
source_x, source_y = [float(v) for v in im.size]
target_x, target_y = [float(v) for v in size]
@@ -68,6 +92,31 @@ def scale_and_crop(image, size, crop=False, upscale=False, quality=None):
im = im.crop(box)
# Close image and replace format/metadata, as PIL blows this away.
im.format, im.info = im_format, im_info
image.close()
# We mutate the quality, but needs to passed into save() to actually
# do anything.
info = image.info
if quality is not None:
info['quality'] = quality
im.format, im.info = image.format, info
return im
def write_image_in_memory(img):
# save to memory
buf = BytesIO()
try:
img.save(buf, img.format, **img.info)
except IOError:
if img.info.get('progression'):
orig_MAXBLOCK = PIL.ImageFile.MAXBLOCK
temp_MAXBLOCK = 1048576
if orig_MAXBLOCK >= temp_MAXBLOCK:
raise
PIL.ImageFile.MAXBLOCK = temp_MAXBLOCK
try:
img.save(buf, img.format, **img.info)
finally:
PIL.ImageFile.MAXBLOCK = orig_MAXBLOCK
else:
raise
return buf