mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-04-27 08:14:42 +00:00
Moved Crop and Fit to resize module.
Crop doesn't necessarily imply the any scaling is taking place. Several ideas were discussed, from renaming Crop to combining both processors into a single Resize processor (as they were in the original IK), but those solutions were felt to either precluded future extension (alternative resize modes) or make the API too verbose.
This commit is contained in:
parent
6751d78504
commit
da00e2a5da
4 changed files with 134 additions and 129 deletions
13
README.rst
13
README.rst
|
|
@ -45,12 +45,13 @@ your spec, you can expose different versions of the original image::
|
|||
|
||||
from django.db import models
|
||||
from imagekit.models import ImageSpec
|
||||
from imagekit.processors import Crop, Adjust
|
||||
from imagekit.processors import resize, Adjust
|
||||
|
||||
class Photo(models.Model):
|
||||
original_image = models.ImageField(upload_to'photos')
|
||||
thumbnail = ImageSpec([Adjust(contrast=1.2, sharpness=1.1), Crop(50, 50)],
|
||||
image_field='original_image', format='JPEG', quality=90)
|
||||
thumbnail = ImageSpec([Adjust(contrast=1.2, sharpness=1.1),
|
||||
resize.Crop(50, 50)], image_field='original_image',
|
||||
format='JPEG', quality=90)
|
||||
|
||||
The ``thumbnail`` property will now return a cropped image::
|
||||
|
||||
|
|
@ -60,7 +61,7 @@ The ``thumbnail`` property will now return a cropped image::
|
|||
photo.original_image.width # > 1000
|
||||
|
||||
The original image is not modified; ``thumbnail`` is a new file that is the
|
||||
result of running the ``imagekit.processors.Crop`` processor on the
|
||||
result of running the ``imagekit.processors.resize.Crop`` processor on the
|
||||
original.
|
||||
|
||||
The ``imagekit.processors`` module contains processors for many common
|
||||
|
|
@ -89,11 +90,11 @@ AdminThumbnailView is used as a property on Django model classes::
|
|||
|
||||
from django.db import models
|
||||
from imagekit.models import ImageSpec
|
||||
from imagekit.processors import Crop, AdminThumbnailView
|
||||
from imagekit.processors import resize, AdminThumbnailView
|
||||
|
||||
class Photo(models.Model):
|
||||
original_image = models.ImageField(upload_to'photos')
|
||||
thumbnail = ImageSpec([Crop(50, 50)], image_field='original_image')
|
||||
thumbnail = ImageSpec([resize.Crop(50, 50)], image_field='original_image')
|
||||
admin_thumbnail_view = AdminThumbnailView(image_field='thumbnail')
|
||||
|
||||
You can then then add this property to the `list_display`__ field of your admin
|
||||
|
|
|
|||
|
|
@ -13,3 +13,6 @@ API Reference
|
|||
|
||||
.. automodule:: imagekit.processors
|
||||
:members:
|
||||
|
||||
.. automodule:: imagekit.processors.resize
|
||||
:members:
|
||||
|
|
|
|||
|
|
@ -106,129 +106,6 @@ class Reflection(object):
|
|||
return composite
|
||||
|
||||
|
||||
class _Resize(object):
|
||||
|
||||
width = None
|
||||
height = None
|
||||
|
||||
def __init__(self, width=None, height=None):
|
||||
if width is not None:
|
||||
self.width = width
|
||||
if height is not None:
|
||||
self.height = height
|
||||
|
||||
def process(self, img):
|
||||
raise NotImplementedError('process must be overridden by subclasses.')
|
||||
|
||||
|
||||
class Crop(_Resize):
|
||||
"""Resizes an image , cropping it to the specified width and height.
|
||||
|
||||
"""
|
||||
|
||||
TOP_LEFT = 'tl'
|
||||
TOP = 't'
|
||||
TOP_RIGHT = 'tr'
|
||||
BOTTOM_LEFT = 'bl'
|
||||
BOTTOM = 'b'
|
||||
BOTTOM_RIGHT = 'br'
|
||||
CENTER = 'c'
|
||||
LEFT = 'l'
|
||||
RIGHT = 'r'
|
||||
|
||||
_ANCHOR_PTS = {
|
||||
TOP_LEFT: (0, 0),
|
||||
TOP: (0.5, 0),
|
||||
TOP_RIGHT: (1, 0),
|
||||
LEFT: (0, 0.5),
|
||||
CENTER: (0.5, 0.5),
|
||||
RIGHT: (1, 0.5),
|
||||
BOTTOM_LEFT: (0, 1),
|
||||
BOTTOM: (0.5, 1),
|
||||
BOTTOM_RIGHT: (1, 1),
|
||||
}
|
||||
|
||||
def __init__(self, width=None, height=None, anchor=None):
|
||||
"""
|
||||
:param width: The target width, in pixels.
|
||||
:param height: The target height, in pixels.
|
||||
:param anchor: Specifies which part of the image should be retained when
|
||||
cropping. Valid values are:
|
||||
|
||||
- Crop.TOP_LEFT
|
||||
- Crop.TOP
|
||||
- Crop.TOP_RIGHT
|
||||
- Crop.LEFT
|
||||
- Crop.CENTER
|
||||
- Crop.RIGHT
|
||||
- Crop.BOTTOM_LEFT
|
||||
- Crop.BOTTOM
|
||||
- Crop.BOTTOM_RIGHT
|
||||
|
||||
"""
|
||||
super(Crop, self).__init__(width, height)
|
||||
self.anchor = anchor
|
||||
|
||||
def process(self, img):
|
||||
cur_width, cur_height = img.size
|
||||
horizontal_anchor, vertical_anchor = Crop._ANCHOR_PTS[self.anchor or \
|
||||
Crop.CENTER]
|
||||
ratio = max(float(self.width)/cur_width, float(self.height)/cur_height)
|
||||
resize_x, resize_y = ((cur_width * ratio), (cur_height * ratio))
|
||||
crop_x, crop_y = (abs(self.width - resize_x), abs(self.height - resize_y))
|
||||
x_diff, y_diff = (int(crop_x / 2), int(crop_y / 2))
|
||||
box_left, box_right = {
|
||||
0: (0, self.width),
|
||||
0.5: (int(x_diff), int(x_diff + self.width)),
|
||||
1: (int(crop_x), int(resize_x)),
|
||||
}[horizontal_anchor]
|
||||
box_upper, box_lower = {
|
||||
0: (0, self.height),
|
||||
0.5: (int(y_diff), int(y_diff + self.height)),
|
||||
1: (int(crop_y), int(resize_y)),
|
||||
}[vertical_anchor]
|
||||
box = (box_left, box_upper, box_right, box_lower)
|
||||
img = img.resize((int(resize_x), int(resize_y)), Image.ANTIALIAS).crop(box)
|
||||
return img
|
||||
|
||||
|
||||
class Fit(_Resize):
|
||||
"""Resizes an image to fit within the specified dimensions.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, width=None, height=None, upscale=None):
|
||||
"""
|
||||
:param width: The maximum width of the desired image.
|
||||
:param height: The maximum height of the desired image.
|
||||
:param upscale: A boolean value specifying whether the image should be
|
||||
enlarged if its dimensions are smaller than the target
|
||||
dimensions.
|
||||
|
||||
"""
|
||||
super(Fit, self).__init__(width, height)
|
||||
self.upscale = upscale
|
||||
|
||||
def process(self, img):
|
||||
cur_width, cur_height = img.size
|
||||
if not self.width is None and not self.height is None:
|
||||
ratio = min(float(self.width)/cur_width,
|
||||
float(self.height)/cur_height)
|
||||
else:
|
||||
if self.width is None:
|
||||
ratio = float(self.height)/cur_height
|
||||
else:
|
||||
ratio = float(self.width)/cur_width
|
||||
new_dimensions = (int(round(cur_width*ratio)),
|
||||
int(round(cur_height*ratio)))
|
||||
if new_dimensions[0] > cur_width or \
|
||||
new_dimensions[1] > cur_height:
|
||||
if not self.upscale:
|
||||
return img
|
||||
img = img.resize(new_dimensions, Image.ANTIALIAS)
|
||||
return img
|
||||
|
||||
|
||||
class Transpose(object):
|
||||
""" Rotates or flips the image
|
||||
|
||||
124
imagekit/processors/resize.py
Normal file
124
imagekit/processors/resize.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
from imagekit.lib import *
|
||||
|
||||
|
||||
class _Resize(object):
|
||||
|
||||
width = None
|
||||
height = None
|
||||
|
||||
def __init__(self, width=None, height=None):
|
||||
if width is not None:
|
||||
self.width = width
|
||||
if height is not None:
|
||||
self.height = height
|
||||
|
||||
def process(self, img):
|
||||
raise NotImplementedError('process must be overridden by subclasses.')
|
||||
|
||||
|
||||
class Crop(_Resize):
|
||||
"""Resizes an image , cropping it to the specified width and height.
|
||||
|
||||
"""
|
||||
|
||||
TOP_LEFT = 'tl'
|
||||
TOP = 't'
|
||||
TOP_RIGHT = 'tr'
|
||||
BOTTOM_LEFT = 'bl'
|
||||
BOTTOM = 'b'
|
||||
BOTTOM_RIGHT = 'br'
|
||||
CENTER = 'c'
|
||||
LEFT = 'l'
|
||||
RIGHT = 'r'
|
||||
|
||||
_ANCHOR_PTS = {
|
||||
TOP_LEFT: (0, 0),
|
||||
TOP: (0.5, 0),
|
||||
TOP_RIGHT: (1, 0),
|
||||
LEFT: (0, 0.5),
|
||||
CENTER: (0.5, 0.5),
|
||||
RIGHT: (1, 0.5),
|
||||
BOTTOM_LEFT: (0, 1),
|
||||
BOTTOM: (0.5, 1),
|
||||
BOTTOM_RIGHT: (1, 1),
|
||||
}
|
||||
|
||||
def __init__(self, width=None, height=None, anchor=None):
|
||||
"""
|
||||
:param width: The target width, in pixels.
|
||||
:param height: The target height, in pixels.
|
||||
:param anchor: Specifies which part of the image should be retained when
|
||||
cropping. Valid values are:
|
||||
|
||||
- Crop.TOP_LEFT
|
||||
- Crop.TOP
|
||||
- Crop.TOP_RIGHT
|
||||
- Crop.LEFT
|
||||
- Crop.CENTER
|
||||
- Crop.RIGHT
|
||||
- Crop.BOTTOM_LEFT
|
||||
- Crop.BOTTOM
|
||||
- Crop.BOTTOM_RIGHT
|
||||
|
||||
"""
|
||||
super(Crop, self).__init__(width, height)
|
||||
self.anchor = anchor
|
||||
|
||||
def process(self, img):
|
||||
cur_width, cur_height = img.size
|
||||
horizontal_anchor, vertical_anchor = Crop._ANCHOR_PTS[self.anchor or \
|
||||
Crop.CENTER]
|
||||
ratio = max(float(self.width)/cur_width, float(self.height)/cur_height)
|
||||
resize_x, resize_y = ((cur_width * ratio), (cur_height * ratio))
|
||||
crop_x, crop_y = (abs(self.width - resize_x), abs(self.height - resize_y))
|
||||
x_diff, y_diff = (int(crop_x / 2), int(crop_y / 2))
|
||||
box_left, box_right = {
|
||||
0: (0, self.width),
|
||||
0.5: (int(x_diff), int(x_diff + self.width)),
|
||||
1: (int(crop_x), int(resize_x)),
|
||||
}[horizontal_anchor]
|
||||
box_upper, box_lower = {
|
||||
0: (0, self.height),
|
||||
0.5: (int(y_diff), int(y_diff + self.height)),
|
||||
1: (int(crop_y), int(resize_y)),
|
||||
}[vertical_anchor]
|
||||
box = (box_left, box_upper, box_right, box_lower)
|
||||
img = img.resize((int(resize_x), int(resize_y)), Image.ANTIALIAS).crop(box)
|
||||
return img
|
||||
|
||||
|
||||
class Fit(_Resize):
|
||||
"""Resizes an image to fit within the specified dimensions.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, width=None, height=None, upscale=None):
|
||||
"""
|
||||
:param width: The maximum width of the desired image.
|
||||
:param height: The maximum height of the desired image.
|
||||
:param upscale: A boolean value specifying whether the image should be
|
||||
enlarged if its dimensions are smaller than the target
|
||||
dimensions.
|
||||
|
||||
"""
|
||||
super(Fit, self).__init__(width, height)
|
||||
self.upscale = upscale
|
||||
|
||||
def process(self, img):
|
||||
cur_width, cur_height = img.size
|
||||
if not self.width is None and not self.height is None:
|
||||
ratio = min(float(self.width)/cur_width,
|
||||
float(self.height)/cur_height)
|
||||
else:
|
||||
if self.width is None:
|
||||
ratio = float(self.height)/cur_height
|
||||
else:
|
||||
ratio = float(self.width)/cur_width
|
||||
new_dimensions = (int(round(cur_width*ratio)),
|
||||
int(round(cur_height*ratio)))
|
||||
if new_dimensions[0] > cur_width or \
|
||||
new_dimensions[1] > cur_height:
|
||||
if not self.upscale:
|
||||
return img
|
||||
img = img.resize(new_dimensions, Image.ANTIALIAS)
|
||||
return img
|
||||
Loading…
Reference in a new issue