mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-17 22:00:24 +00:00
220 lines
7.4 KiB
Python
220 lines
7.4 KiB
Python
from imagekit.lib import Image
|
|
import warnings
|
|
from .base import Anchor
|
|
|
|
|
|
class Resize(object):
|
|
"""
|
|
Resizes an image to the specified width and height.
|
|
|
|
"""
|
|
def __init__(self, width, height):
|
|
"""
|
|
:param width: The target width, in pixels.
|
|
:param height: The target height, in pixels.
|
|
|
|
"""
|
|
self.width = width
|
|
self.height = height
|
|
|
|
def process(self, img):
|
|
return img.resize((self.width, self.height), Image.ANTIALIAS)
|
|
|
|
|
|
class ResizeToCover(object):
|
|
"""
|
|
Resizes the image to the smallest possible size that will entirely cover the
|
|
provided dimensions. You probably won't be using this processor directly,
|
|
but it's used internally by ``ResizeToFill`` and ``SmartResize``.
|
|
|
|
"""
|
|
def __init__(self, width, height):
|
|
"""
|
|
:param width: The target width, in pixels.
|
|
:param height: The target height, in pixels.
|
|
|
|
"""
|
|
self.width, self.height = width, height
|
|
|
|
def process(self, img):
|
|
original_width, original_height = img.size
|
|
ratio = max(float(self.width) / original_width,
|
|
float(self.height) / original_height)
|
|
new_width, new_height = (int(original_width * ratio),
|
|
int(original_height * ratio))
|
|
return Resize(new_width, new_height).process(img)
|
|
|
|
|
|
class ResizeToFill(object):
|
|
"""
|
|
Resizes an image, cropping it to the exact specified width and height.
|
|
|
|
"""
|
|
|
|
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.
|
|
"""
|
|
self.width = width
|
|
self.height = height
|
|
self.anchor = anchor
|
|
|
|
def process(self, img):
|
|
from .crop import Crop
|
|
img = ResizeToCover(self.width, self.height).process(img)
|
|
return Crop(self.width, self.height,
|
|
anchor=self.anchor).process(img)
|
|
|
|
|
|
class SmartResize(object):
|
|
"""
|
|
The ``SmartResize`` processor is identical to ``ResizeToFill``, except that
|
|
it uses entropy to crop the image instead of a user-specified anchor point.
|
|
Internally, it simply runs the ``ResizeToCover`` and ``SmartCrop``
|
|
processors in series.
|
|
"""
|
|
def __init__(self, width, height):
|
|
"""
|
|
:param width: The target width, in pixels.
|
|
:param height: The target height, in pixels.
|
|
|
|
"""
|
|
self.width, self.height = width, height
|
|
|
|
def process(self, img):
|
|
from .crop import SmartCrop
|
|
img = ResizeToCover(self.width, self.height).process(img)
|
|
return SmartCrop(self.width, self.height).process(img)
|
|
|
|
|
|
class ResizeCanvas(object):
|
|
"""
|
|
Resizes the canvas, using the provided background color if the new size is
|
|
larger than the current image.
|
|
|
|
"""
|
|
def __init__(self, width, height, color=None, anchor=None, x=None, y=None):
|
|
"""
|
|
:param width: The target width, in pixels.
|
|
:param height: The target height, in pixels.
|
|
:param color: The background color to use for padding.
|
|
:param anchor: Specifies the position of the original image on the new
|
|
canvas. Valid values are:
|
|
|
|
- Anchor.TOP_LEFT
|
|
- Anchor.TOP
|
|
- Anchor.TOP_RIGHT
|
|
- Anchor.LEFT
|
|
- Anchor.CENTER
|
|
- Anchor.RIGHT
|
|
- Anchor.BOTTOM_LEFT
|
|
- Anchor.BOTTOM
|
|
- Anchor.BOTTOM_RIGHT
|
|
|
|
You may also pass a tuple that indicates the position in
|
|
percentages. For example, ``(0, 0)`` corresponds to "top left",
|
|
``(0.5, 0.5)`` to "center" and ``(1, 1)`` to "bottom right". This is
|
|
basically the same as using percentages in CSS background positions.
|
|
|
|
"""
|
|
if x is not None or y is not None:
|
|
if anchor:
|
|
raise Exception('You may provide either an anchor or x and y'
|
|
' coordinate, but not both.')
|
|
else:
|
|
self.x, self.y = x or 0, y or 0
|
|
self.anchor = None
|
|
else:
|
|
self.anchor = anchor or Anchor.CENTER
|
|
self.x = self.y = None
|
|
|
|
self.width = width
|
|
self.height = height
|
|
self.color = color or (255, 255, 255, 0)
|
|
|
|
def process(self, img):
|
|
original_width, original_height = img.size
|
|
|
|
if self.anchor:
|
|
anchor = Anchor.get_tuple(self.anchor)
|
|
trim_x, trim_y = self.width - original_width, \
|
|
self.height - original_height
|
|
x = int(float(trim_x) * float(anchor[0]))
|
|
y = int(float(trim_y) * float(anchor[1]))
|
|
else:
|
|
x, y = self.x, self.y
|
|
|
|
new_img = Image.new('RGBA', (self.width, self.height), self.color)
|
|
new_img.paste(img, (x, y))
|
|
return new_img
|
|
|
|
|
|
class AddBorder(object):
|
|
"""
|
|
Add a border of specific color and size to an image.
|
|
|
|
"""
|
|
def __init__(self, thickness, color=None):
|
|
"""
|
|
:param color: Color to use for the border
|
|
:param thickness: Thickness of the border. Can be either an int or
|
|
a 4-tuple of ints of the form (top, right, bottom, left).
|
|
"""
|
|
self.color = color
|
|
if isinstance(thickness, int):
|
|
self.top = self.right = self.bottom = self.left = thickness
|
|
else:
|
|
self.top, self.right, self.bottom, self.left = thickness
|
|
|
|
def process(self, img):
|
|
new_width = img.size[0] + self.left + self.right
|
|
new_height = img.size[1] + self.top + self.bottom
|
|
return ResizeCanvas(new_width, new_height, color=self.color,
|
|
x=self.left, y=self.top).process(img)
|
|
|
|
|
|
class ResizeToFit(object):
|
|
"""
|
|
Resizes an image to fit within the specified dimensions.
|
|
|
|
"""
|
|
|
|
def __init__(self, width=None, height=None, upscale=None, mat_color=None, anchor=Anchor.CENTER):
|
|
"""
|
|
: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.
|
|
:param mat_color: If set, the target image size will be enforced and the
|
|
specified color will be used as a background color to pad the image.
|
|
|
|
"""
|
|
self.width = width
|
|
self.height = height
|
|
self.upscale = upscale
|
|
self.mat_color = mat_color
|
|
self.anchor = anchor
|
|
|
|
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 (cur_width > new_dimensions[0] or cur_height > new_dimensions[1]) or \
|
|
self.upscale:
|
|
img = Resize(new_dimensions[0],
|
|
new_dimensions[1]).process(img)
|
|
if self.mat_color:
|
|
img = ResizeCanvas(self.width, self.height, self.mat_color, anchor=self.anchor).process(img)
|
|
return img
|