mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-17 05:40:25 +00:00
Merge branch 'develop' into release/2.0
* develop: IKContentFile accepts format hint Additional mimetype utils Don't get extension of empty filename Tell people to import fields from the models module Refactored AutoConvert into prepare_image Docstring for save_image Kill PIL's chattiness; fixes #91 PIL bug workaround Use StringIO instead of temp file Extract reusable save_image function Rename SpecFile and move it to utils Extract suggest_extension util from generator Woah, globals Add SpecFile.__unicode__
This commit is contained in:
commit
209afac9e3
9 changed files with 249 additions and 180 deletions
|
|
@ -34,7 +34,7 @@ Much like ``django.db.models.ImageField``, Specs are defined as properties
|
|||
of a model class::
|
||||
|
||||
from django.db import models
|
||||
from imagekit.models.fields import ImageSpecField
|
||||
from imagekit.models import ImageSpecField
|
||||
|
||||
class Photo(models.Model):
|
||||
original_image = models.ImageField(upload_to='photos')
|
||||
|
|
@ -49,7 +49,7 @@ an ImageFile-like object (just like with a normal
|
|||
photo.original_image.url # > '/media/photos/birthday.tiff'
|
||||
photo.formatted_image.url # > '/media/cache/photos/birthday_formatted_image.jpeg'
|
||||
|
||||
Check out ``imagekit.models.fields.ImageSpecField`` for more information.
|
||||
Check out ``imagekit.models.ImageSpecField`` for more information.
|
||||
|
||||
If you only want to save the processed image (without maintaining the original),
|
||||
you can use a ``ProcessedImageField``::
|
||||
|
|
@ -71,7 +71,7 @@ something to it, and return the result. By providing a list of processors to
|
|||
your spec, you can expose different versions of the original image::
|
||||
|
||||
from django.db import models
|
||||
from imagekit.models.fields import ImageSpecField
|
||||
from imagekit.models import ImageSpecField
|
||||
from imagekit.processors import ResizeToFill, Adjust
|
||||
|
||||
class Photo(models.Model):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ API Reference
|
|||
:mod:`models` Module
|
||||
--------------------
|
||||
|
||||
.. automodule:: imagekit.models.fields
|
||||
.. automodule:: imagekit.models
|
||||
:members:
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,39 +1,17 @@
|
|||
import mimetypes
|
||||
import os
|
||||
|
||||
from StringIO import StringIO
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from .processors import ProcessorPipeline, AutoConvert
|
||||
from .utils import img_to_fobj, open_image, \
|
||||
format_to_extension, extension_to_format, UnknownFormatError, \
|
||||
UnknownExtensionError
|
||||
|
||||
|
||||
class SpecFile(ContentFile):
|
||||
"""
|
||||
Wraps a ContentFile in a file-like object with a filename
|
||||
and a content_type.
|
||||
"""
|
||||
def __init__(self, filename, content):
|
||||
self.file = ContentFile(content)
|
||||
self.file.name = filename
|
||||
try:
|
||||
self.file.content_type = mimetypes.guess_type(filename)[0]
|
||||
except IndexError:
|
||||
self.file.content_type = None
|
||||
|
||||
def __str__(self):
|
||||
return self.file.name
|
||||
from .processors import ProcessorPipeline
|
||||
from .utils import (img_to_fobj, open_image, IKContentFile, extension_to_format,
|
||||
UnknownExtensionError)
|
||||
|
||||
|
||||
class SpecFileGenerator(object):
|
||||
def __init__(self, processors=None, format=None, options={},
|
||||
def __init__(self, processors=None, format=None, options=None,
|
||||
autoconvert=True, storage=None):
|
||||
self.processors = processors
|
||||
self.format = format
|
||||
self.options = options
|
||||
self.options = options or {}
|
||||
self.autoconvert = autoconvert
|
||||
self.storage = storage
|
||||
|
||||
|
|
@ -51,7 +29,7 @@ class SpecFileGenerator(object):
|
|||
|
||||
# Determine the format.
|
||||
format = self.format
|
||||
if not format:
|
||||
if filename and not format:
|
||||
# Try to guess the format from the extension.
|
||||
extension = os.path.splitext(filename)[1].lower()
|
||||
if extension:
|
||||
|
|
@ -61,39 +39,10 @@ class SpecFileGenerator(object):
|
|||
pass
|
||||
format = format or img.format or original_format or 'JPEG'
|
||||
|
||||
# Run the AutoConvert processor
|
||||
if self.autoconvert:
|
||||
autoconvert_processor = AutoConvert(format)
|
||||
img = autoconvert_processor.process(img)
|
||||
options = dict(autoconvert_processor.save_kwargs.items() + \
|
||||
options.items())
|
||||
|
||||
imgfile = img_to_fobj(img, format, **options)
|
||||
content = SpecFile(filename, imgfile.read())
|
||||
content = IKContentFile(filename, imgfile.read(), format=format)
|
||||
return img, content
|
||||
|
||||
def suggest_extension(self, name):
|
||||
original_extension = os.path.splitext(name)[1]
|
||||
try:
|
||||
suggested_extension = format_to_extension(self.format)
|
||||
except UnknownFormatError:
|
||||
extension = original_extension
|
||||
else:
|
||||
if suggested_extension.lower() == original_extension.lower():
|
||||
extension = original_extension
|
||||
else:
|
||||
try:
|
||||
original_format = extension_to_format(original_extension)
|
||||
except UnknownExtensionError:
|
||||
extension = suggested_extension
|
||||
else:
|
||||
# If the formats match, give precedence to the original extension.
|
||||
if self.format.lower() == original_format.lower():
|
||||
extension = original_extension
|
||||
else:
|
||||
extension = suggested_extension
|
||||
return extension
|
||||
|
||||
def generate_file(self, filename, source_file, save=True):
|
||||
"""
|
||||
Generates a new image file by processing the source file and returns
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@ import warnings
|
|||
class ImageSpec(ImageSpecField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn('ImageSpec has been moved to'
|
||||
' imagekit.models.fields.ImageSpecField. Please use that'
|
||||
' instead.', DeprecationWarning)
|
||||
' imagekit.models.ImageSpecField. Please use that instead.',
|
||||
DeprecationWarning)
|
||||
super(ImageSpec, self).__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from ...imagecache import get_default_image_cache_backend
|
|||
from ...generators import SpecFileGenerator
|
||||
from .files import ImageSpecFieldFile, ProcessedImageFieldFile
|
||||
from .utils import ImageSpecFileDescriptor, ImageKitMeta, BoundImageKitMeta
|
||||
from ...utils import suggest_extension
|
||||
|
||||
|
||||
class ImageSpecField(object):
|
||||
|
|
@ -15,7 +16,7 @@ class ImageSpecField(object):
|
|||
variants of uploaded images to your models.
|
||||
|
||||
"""
|
||||
def __init__(self, processors=None, format=None, options={},
|
||||
def __init__(self, processors=None, format=None, options=None,
|
||||
image_field=None, pre_cache=None, storage=None, cache_to=None,
|
||||
autoconvert=True, image_cache_backend=None):
|
||||
"""
|
||||
|
|
@ -47,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
|
||||
|
|
@ -146,14 +147,14 @@ class ProcessedImageField(models.ImageField):
|
|||
"""
|
||||
attr_class = ProcessedImageFieldFile
|
||||
|
||||
def __init__(self, processors=None, format=None, options={},
|
||||
def __init__(self, processors=None, format=None, options=None,
|
||||
verbose_name=None, name=None, width_field=None, height_field=None,
|
||||
autoconvert=True, **kwargs):
|
||||
"""
|
||||
The ProcessedImageField constructor accepts all of the arguments that
|
||||
the :class:`django.db.models.ImageField` constructor accepts, as well
|
||||
as the ``processors``, ``format``, and ``options`` arguments of
|
||||
:class:`imagekit.models.fields.ImageSpecField`.
|
||||
:class:`imagekit.models.ImageSpecField`.
|
||||
|
||||
"""
|
||||
if 'quality' in kwargs:
|
||||
|
|
@ -169,7 +170,7 @@ class ProcessedImageField(models.ImageField):
|
|||
filename = os.path.normpath(self.storage.get_valid_name(
|
||||
os.path.basename(filename)))
|
||||
name, ext = os.path.splitext(filename)
|
||||
ext = self.generator.suggest_extension(filename)
|
||||
ext = suggest_extension(filename, self.generator.format)
|
||||
return u'%s%s' % (name, ext)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import datetime
|
|||
from django.db.models.fields.files import ImageField, ImageFieldFile
|
||||
from django.utils.encoding import force_unicode, smart_str
|
||||
|
||||
from ...utils import suggest_extension
|
||||
|
||||
|
||||
class ImageSpecFieldFile(ImageFieldFile):
|
||||
def __init__(self, instance, field, attname):
|
||||
|
|
@ -118,9 +120,8 @@ class ImageSpecFieldFile(ImageFieldFile):
|
|||
raise Exception('No cache_to or default_cache_to value'
|
||||
' specified')
|
||||
if callable(cache_to):
|
||||
suggested_extension = \
|
||||
self.field.generator.suggest_extension(
|
||||
self.source_file.name)
|
||||
suggested_extension = suggest_extension(
|
||||
self.source_file.name, self.field.generator.format)
|
||||
new_filename = force_unicode(
|
||||
datetime.datetime.now().strftime(
|
||||
smart_str(cache_to(self.instance,
|
||||
|
|
|
|||
|
|
@ -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,90 +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.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,28 +1,48 @@
|
|||
import tempfile
|
||||
import os
|
||||
import mimetypes
|
||||
from StringIO import StringIO
|
||||
import sys
|
||||
import types
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db.models.loading import cache
|
||||
from django.utils.functional import wraps
|
||||
from django.utils.encoding import smart_str, smart_unicode
|
||||
|
||||
from imagekit.lib import Image, ImageFile
|
||||
from .lib import Image, ImageFile
|
||||
|
||||
|
||||
def img_to_fobj(img, format, **kwargs):
|
||||
tmp = tempfile.TemporaryFile()
|
||||
try:
|
||||
img.save(tmp, format, **kwargs)
|
||||
except IOError:
|
||||
# PIL can have problems saving large JPEGs if MAXBLOCK isn't big enough,
|
||||
# So if we have a problem saving, we temporarily increase it. See
|
||||
# http://github.com/jdriscoll/django-imagekit/issues/50
|
||||
old_maxblock = ImageFile.MAXBLOCK
|
||||
ImageFile.MAXBLOCK = img.size[0] * img.size[1]
|
||||
try:
|
||||
img.save(tmp, format, **kwargs)
|
||||
finally:
|
||||
ImageFile.MAXBLOCK = old_maxblock
|
||||
tmp.seek(0)
|
||||
return tmp
|
||||
RGBA_TRANSPARENCY_FORMATS = ['PNG']
|
||||
PALETTE_TRANSPARENCY_FORMATS = ['PNG', 'GIF']
|
||||
|
||||
|
||||
class IKContentFile(ContentFile):
|
||||
"""
|
||||
Wraps a ContentFile in a file-like object with a filename and a
|
||||
content_type. A PIL image format can be optionally be provided as a content
|
||||
type hint.
|
||||
|
||||
"""
|
||||
def __init__(self, filename, content, format=None):
|
||||
self.file = ContentFile(content)
|
||||
self.file.name = filename
|
||||
mimetype = getattr(self.file, 'content_type', None)
|
||||
if format and not mimetype:
|
||||
mimetype = format_to_mimetype(format)
|
||||
if not mimetype:
|
||||
ext = os.path.splitext(filename or '')[1]
|
||||
mimetype = extension_to_mimetype(ext)
|
||||
self.file.content_type = mimetype
|
||||
|
||||
def __str__(self):
|
||||
return smart_str(self.file.name or '')
|
||||
|
||||
def __unicode__(self):
|
||||
return smart_unicode(self.file.name or u'')
|
||||
|
||||
|
||||
def img_to_fobj(img, format, autoconvert=True, **options):
|
||||
return save_image(img, StringIO(), format, options, autoconvert)
|
||||
|
||||
|
||||
def get_spec_files(instance):
|
||||
|
|
@ -107,6 +127,19 @@ def _format_to_extension(format):
|
|||
return None
|
||||
|
||||
|
||||
def extension_to_mimetype(ext):
|
||||
try:
|
||||
filename = 'a%s' % (ext or '') # guess_type requires a full filename, not just an extension
|
||||
mimetype = mimetypes.guess_type(filename)[0]
|
||||
except IndexError:
|
||||
mimetype = None
|
||||
return mimetype
|
||||
|
||||
|
||||
def format_to_mimetype(format):
|
||||
return extension_to_mimetype(format_to_extension(format))
|
||||
|
||||
|
||||
def extension_to_format(extension):
|
||||
"""Returns the format that corresponds to the provided extension.
|
||||
|
||||
|
|
@ -165,3 +198,176 @@ def validate_app_cache(apps, force_revalidation=False):
|
|||
if force_revalidation:
|
||||
f.invalidate()
|
||||
f.validate()
|
||||
|
||||
|
||||
def suggest_extension(name, format):
|
||||
original_extension = os.path.splitext(name)[1]
|
||||
try:
|
||||
suggested_extension = format_to_extension(format)
|
||||
except UnknownFormatError:
|
||||
extension = original_extension
|
||||
else:
|
||||
if suggested_extension.lower() == original_extension.lower():
|
||||
extension = original_extension
|
||||
else:
|
||||
try:
|
||||
original_format = extension_to_format(original_extension)
|
||||
except UnknownExtensionError:
|
||||
extension = suggested_extension
|
||||
else:
|
||||
# If the formats match, give precedence to the original extension.
|
||||
if format.lower() == original_format.lower():
|
||||
extension = original_extension
|
||||
else:
|
||||
extension = suggested_extension
|
||||
return extension
|
||||
|
||||
|
||||
def save_image(img, outfile, format, options=None, autoconvert=True):
|
||||
"""
|
||||
Wraps PIL's ``Image.save()`` method. There are two main benefits of using
|
||||
this function over PIL's:
|
||||
|
||||
1. It gracefully handles the infamous "Suspension not allowed here" errors.
|
||||
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:
|
||||
img, save_kwargs = prepare_image(img, format)
|
||||
options = dict(save_kwargs.items() + options.items())
|
||||
|
||||
# Attempt to reset the file pointer.
|
||||
try:
|
||||
outfile.seek(0)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
with quiet():
|
||||
img.save(outfile, format, **options)
|
||||
except IOError:
|
||||
# PIL can have problems saving large JPEGs if MAXBLOCK isn't big enough,
|
||||
# So if we have a problem saving, we temporarily increase it. See
|
||||
# http://github.com/jdriscoll/django-imagekit/issues/50
|
||||
old_maxblock = ImageFile.MAXBLOCK
|
||||
ImageFile.MAXBLOCK = img.size[0] * img.size[1]
|
||||
try:
|
||||
img.save(outfile, format, **options)
|
||||
finally:
|
||||
ImageFile.MAXBLOCK = old_maxblock
|
||||
|
||||
try:
|
||||
outfile.seek(0)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return outfile
|
||||
|
||||
|
||||
class quiet(object):
|
||||
"""
|
||||
A context manager for suppressing the stderr activity of PIL's C libraries.
|
||||
Based on http://stackoverflow.com/a/978264/155370
|
||||
|
||||
"""
|
||||
def __enter__(self):
|
||||
self.stderr_fd = sys.__stderr__.fileno()
|
||||
self.null_fd = os.open(os.devnull, os.O_RDWR)
|
||||
self.old = os.dup(self.stderr_fd)
|
||||
os.dup2(self.null_fd, self.stderr_fd)
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from django.db import models
|
||||
|
||||
from imagekit.models.fields import ImageSpecField
|
||||
from imagekit.models import ImageSpecField
|
||||
from imagekit.processors import Adjust
|
||||
from imagekit.processors import ResizeToFill
|
||||
from imagekit.processors import SmartCrop
|
||||
|
|
|
|||
Loading…
Reference in a new issue