mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-16 21:30:23 +00:00
Refactored AutoConvert into prepare_image
Because of its need to return kwargs for ``Image.save()``, it never really fit the mold of a processor.
This commit is contained in:
parent
222c9ba22a
commit
667e265c94
3 changed files with 99 additions and 102 deletions
|
|
@ -48,8 +48,8 @@ class ImageSpecField(object):
|
|||
based on that format. if not, the extension of the
|
||||
original file will be passed. You do not have to use
|
||||
this extension, it's only a recommendation.
|
||||
:param autoconvert: Specifies whether the AutoConvert processor
|
||||
should be run before saving.
|
||||
:param autoconvert: Specifies whether automatic conversion using
|
||||
``prepare_image()`` should be performed prior to saving.
|
||||
:param image_cache_backend: An object responsible for managing the state
|
||||
of cached files. Defaults to an instance of
|
||||
IMAGEKIT_DEFAULT_IMAGE_CACHE_BACKEND
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
from imagekit.lib import Image, ImageColor, ImageEnhance
|
||||
|
||||
|
||||
RGBA_TRANSPARENCY_FORMATS = ['PNG']
|
||||
PALETTE_TRANSPARENCY_FORMATS = ['PNG', 'GIF']
|
||||
|
||||
|
||||
class ProcessorPipeline(list):
|
||||
"""
|
||||
A :class:`list` of other processors. This class allows any object that
|
||||
|
|
@ -173,95 +169,6 @@ class Transpose(object):
|
|||
return img
|
||||
|
||||
|
||||
class AutoConvert(object):
|
||||
"""A processor that does some common-sense conversions based on the target
|
||||
format. This includes things like preserving transparency and quantizing.
|
||||
This processors is used automatically by ``ImageSpecField`` and
|
||||
``ProcessedImageField`` immediately before saving the image unless you
|
||||
specify ``autoconvert=False``.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, format):
|
||||
self.format = format
|
||||
|
||||
def process(self, img):
|
||||
matte = False
|
||||
self.save_kwargs = {}
|
||||
self.rgba_ = img.mode == 'RGBA'
|
||||
if self.rgba_:
|
||||
if self.format in RGBA_TRANSPARENCY_FORMATS:
|
||||
pass
|
||||
elif self.format in PALETTE_TRANSPARENCY_FORMATS:
|
||||
# If you're going from a format with alpha transparency to one
|
||||
# with palette transparency, transparency values will be
|
||||
# snapped: pixels that are more opaque than not will become
|
||||
# fully opaque; pixels that are more transparent than not will
|
||||
# become fully transparent. This will not produce a good-looking
|
||||
# result if your image contains varying levels of opacity; in
|
||||
# that case, you'll probably want to use a processor to matte
|
||||
# the image on a solid color. The reason we don't matte by
|
||||
# default is because not doing so allows processors to treat
|
||||
# RGBA-format images as a super-type of P-format images: if you
|
||||
# have an RGBA-format image with only a single transparent
|
||||
# color, and save it as a GIF, it will retain its transparency.
|
||||
# In other words, a P-format image converted to an
|
||||
# RGBA-formatted image by a processor and then saved as a
|
||||
# P-format image will give the expected results.
|
||||
|
||||
# Work around a bug in PIL: split() doesn't check to see if
|
||||
# img is loaded.
|
||||
img.load()
|
||||
|
||||
alpha = img.split()[-1]
|
||||
mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
|
||||
img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE,
|
||||
colors=255)
|
||||
img.paste(255, mask)
|
||||
self.save_kwargs['transparency'] = 255
|
||||
else:
|
||||
# Simply converting an RGBA-format image to an RGB one creates a
|
||||
# gross result, so we matte the image on a white background. If
|
||||
# that's not what you want, that's fine: use a processor to deal
|
||||
# with the transparency however you want. This is simply a
|
||||
# sensible default that will always produce something that looks
|
||||
# good. Or at least, it will look better than just a straight
|
||||
# conversion.
|
||||
matte = True
|
||||
elif img.mode == 'P':
|
||||
if self.format in PALETTE_TRANSPARENCY_FORMATS:
|
||||
try:
|
||||
self.save_kwargs['transparency'] = img.info['transparency']
|
||||
except KeyError:
|
||||
pass
|
||||
elif self.format in RGBA_TRANSPARENCY_FORMATS:
|
||||
# Currently PIL doesn't support any RGBA-mode formats that
|
||||
# aren't also P-mode formats, so this will never happen.
|
||||
img = img.convert('RGBA')
|
||||
else:
|
||||
matte = True
|
||||
else:
|
||||
img = img.convert('RGB')
|
||||
|
||||
# GIFs are always going to be in palette mode, so we can do a little
|
||||
# optimization. Note that the RGBA sources also use adaptive
|
||||
# quantization (above). Images that are already in P mode don't need
|
||||
# any quantization because their colors are already limited.
|
||||
if self.format == 'GIF':
|
||||
img = img.convert('P', palette=Image.ADAPTIVE)
|
||||
|
||||
if matte:
|
||||
img = img.convert('RGBA')
|
||||
bg = Image.new('RGBA', img.size, (255, 255, 255))
|
||||
bg.paste(img, img)
|
||||
img = bg.convert('RGB')
|
||||
|
||||
if self.format == 'JPEG':
|
||||
self.save_kwargs['optimize'] = True
|
||||
|
||||
return img
|
||||
|
||||
|
||||
class Anchor(object):
|
||||
"""
|
||||
Defines all the anchor points needed by the various processor classes.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ from django.utils.functional import wraps
|
|||
from django.utils.encoding import smart_str, smart_unicode
|
||||
|
||||
from .lib import Image, ImageFile
|
||||
from .processors import AutoConvert
|
||||
|
||||
|
||||
RGBA_TRANSPARENCY_FORMATS = ['PNG']
|
||||
PALETTE_TRANSPARENCY_FORMATS = ['PNG', 'GIF']
|
||||
|
||||
|
||||
class IKContentFile(ContentFile):
|
||||
|
|
@ -208,17 +211,15 @@ def save_image(img, outfile, format, options=None, autoconvert=True):
|
|||
this function over PIL's:
|
||||
|
||||
1. It gracefully handles the infamous "Suspension not allowed here" errors.
|
||||
2. It incorporates the AutoConvert processor, which will do some
|
||||
common-sense processing given the target format.
|
||||
2. It prepares the image for saving using ``prepare_image()``, which will do
|
||||
some common-sense processing given the target format.
|
||||
|
||||
"""
|
||||
options = options or {}
|
||||
|
||||
if autoconvert:
|
||||
autoconvert_processor = AutoConvert(format)
|
||||
img = autoconvert_processor.process(img)
|
||||
options = dict(autoconvert_processor.save_kwargs.items() +
|
||||
options.items())
|
||||
img, save_kwargs = prepare_image(img, format)
|
||||
options = dict(save_kwargs.items() + options.items())
|
||||
|
||||
# Attempt to reset the file pointer.
|
||||
try:
|
||||
|
|
@ -263,3 +264,92 @@ class quiet(object):
|
|||
def __exit__(self, *args, **kwargs):
|
||||
os.dup2(self.old, self.stderr_fd)
|
||||
os.close(self.null_fd)
|
||||
|
||||
|
||||
def prepare_image(img, format):
|
||||
"""
|
||||
Prepares the image for saving to the provided format by doing some
|
||||
common-sense conversions. This includes things like preserving transparency
|
||||
and quantizing. This function is used automatically by ``save_image()``
|
||||
(and classes like ``ImageSpecField`` and ``ProcessedImageField``)
|
||||
immediately before saving unless you specify ``autoconvert=False``. It is
|
||||
provided as a utility for those doing their own processing.
|
||||
|
||||
:param img: The image to prepare for saving.
|
||||
:param format: The format that the image will be saved to.
|
||||
|
||||
"""
|
||||
matte = False
|
||||
save_kwargs = {}
|
||||
|
||||
if img.mode == 'RGBA':
|
||||
if format in RGBA_TRANSPARENCY_FORMATS:
|
||||
pass
|
||||
elif format in PALETTE_TRANSPARENCY_FORMATS:
|
||||
# If you're going from a format with alpha transparency to one
|
||||
# with palette transparency, transparency values will be
|
||||
# snapped: pixels that are more opaque than not will become
|
||||
# fully opaque; pixels that are more transparent than not will
|
||||
# become fully transparent. This will not produce a good-looking
|
||||
# result if your image contains varying levels of opacity; in
|
||||
# that case, you'll probably want to use a processor to matte
|
||||
# the image on a solid color. The reason we don't matte by
|
||||
# default is because not doing so allows processors to treat
|
||||
# RGBA-format images as a super-type of P-format images: if you
|
||||
# have an RGBA-format image with only a single transparent
|
||||
# color, and save it as a GIF, it will retain its transparency.
|
||||
# In other words, a P-format image converted to an
|
||||
# RGBA-formatted image by a processor and then saved as a
|
||||
# P-format image will give the expected results.
|
||||
|
||||
# Work around a bug in PIL: split() doesn't check to see if
|
||||
# img is loaded.
|
||||
img.load()
|
||||
|
||||
alpha = img.split()[-1]
|
||||
mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
|
||||
img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE,
|
||||
colors=255)
|
||||
img.paste(255, mask)
|
||||
save_kwargs['transparency'] = 255
|
||||
else:
|
||||
# Simply converting an RGBA-format image to an RGB one creates a
|
||||
# gross result, so we matte the image on a white background. If
|
||||
# that's not what you want, that's fine: use a processor to deal
|
||||
# with the transparency however you want. This is simply a
|
||||
# sensible default that will always produce something that looks
|
||||
# good. Or at least, it will look better than just a straight
|
||||
# conversion.
|
||||
matte = True
|
||||
elif img.mode == 'P':
|
||||
if format in PALETTE_TRANSPARENCY_FORMATS:
|
||||
try:
|
||||
save_kwargs['transparency'] = img.info['transparency']
|
||||
except KeyError:
|
||||
pass
|
||||
elif format in RGBA_TRANSPARENCY_FORMATS:
|
||||
# Currently PIL doesn't support any RGBA-mode formats that
|
||||
# aren't also P-mode formats, so this will never happen.
|
||||
img = img.convert('RGBA')
|
||||
else:
|
||||
matte = True
|
||||
else:
|
||||
img = img.convert('RGB')
|
||||
|
||||
# GIFs are always going to be in palette mode, so we can do a little
|
||||
# optimization. Note that the RGBA sources also use adaptive
|
||||
# quantization (above). Images that are already in P mode don't need
|
||||
# any quantization because their colors are already limited.
|
||||
if format == 'GIF':
|
||||
img = img.convert('P', palette=Image.ADAPTIVE)
|
||||
|
||||
if matte:
|
||||
img = img.convert('RGBA')
|
||||
bg = Image.new('RGBA', img.size, (255, 255, 255))
|
||||
bg.paste(img, img)
|
||||
img = bg.convert('RGB')
|
||||
|
||||
if format == 'JPEG':
|
||||
save_kwargs['optimize'] = True
|
||||
|
||||
return img, save_kwargs
|
||||
|
|
|
|||
Loading…
Reference in a new issue