mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-26 17:24:31 +03:00
Merge pull request #4231 from uploadcare/box-in-thumbnail
Fix thumbnail geometry when DCT scaling is used
This commit is contained in:
commit
b5d06baa5f
|
@ -13,7 +13,11 @@ class TestImageDraft(PillowTestCase):
|
|||
im = Image.new(in_mode, in_size)
|
||||
data = tostring(im, "JPEG")
|
||||
im = fromstring(data)
|
||||
im.draft(req_mode, req_size)
|
||||
mode, box = im.draft(req_mode, req_size)
|
||||
scale, _ = im.decoderconfig
|
||||
self.assertEqual(box[:2], (0, 0))
|
||||
self.assertTrue((im.width - scale) < box[2] <= im.width)
|
||||
self.assertTrue((im.height - scale) < box[3] <= im.height)
|
||||
return im
|
||||
|
||||
def test_size(self):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from PIL import Image
|
||||
|
||||
from .helper import PillowTestCase, hopper
|
||||
from .helper import PillowTestCase, fromstring, hopper, tostring
|
||||
|
||||
|
||||
class TestImageThumbnail(PillowTestCase):
|
||||
|
@ -58,3 +58,15 @@ class TestImageThumbnail(PillowTestCase):
|
|||
with Image.open("Tests/images/hopper.jpg") as im:
|
||||
im.thumbnail((64, 64))
|
||||
self.assertEqual(im.size, (64, 64))
|
||||
|
||||
def test_DCT_scaling_edges(self):
|
||||
# Make an image with red borders and size (N * 8) + 1 to cross DCT grid
|
||||
im = Image.new("RGB", (257, 257), "red")
|
||||
im.paste(Image.new("RGB", (235, 235)), (11, 11))
|
||||
|
||||
thumb = fromstring(tostring(im, "JPEG", quality=99, subsampling=0))
|
||||
thumb.thumbnail((32, 32), Image.BICUBIC)
|
||||
|
||||
ref = im.resize((32, 32), Image.BICUBIC)
|
||||
# This is still JPEG, some error is present. Without the fix it is 11.5
|
||||
self.assert_image_similar(thumb, ref, 1.5)
|
||||
|
|
|
@ -58,6 +58,20 @@ and :py:meth:`~PIL.ImageOps.fit` functions.
|
|||
See :ref:`concept-filters` to learn the difference. In short,
|
||||
``Image.NEAREST`` is a very fast filter, but simple and low-quality.
|
||||
|
||||
Image.draft() return value
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If the :py:meth:`~PIL.Image.Image.draft` method has no effect, it returns ``None``.
|
||||
If it does have an effect, then it previously returned the image itself.
|
||||
However, unlike other `chain methods`_, :py:meth:`~PIL.Image.Image.draft` does not
|
||||
return a modified version of the image, but modifies it in-place. So instead, if
|
||||
:py:meth:`~PIL.Image.Image.draft` has an effect, Pillow will now return a tuple
|
||||
of the image mode and a co-ordinate box. The box is the original coordinates in the
|
||||
bounds of resulting image. This may be useful in a subsequent
|
||||
:py:meth:`~PIL.Image.Image.resize` call.
|
||||
|
||||
.. _chain methods: https://en.wikipedia.org/wiki/Method_chaining
|
||||
|
||||
|
||||
API Changes
|
||||
===========
|
||||
|
@ -104,8 +118,12 @@ Use instead:
|
|||
with Image.open("hopper.png") as im:
|
||||
im.save("out.jpg")
|
||||
|
||||
Better thumbnail aspect ratio preservation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Better thumbnail geometry
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When calculating the new dimensions in :py:meth:`~PIL.Image.Image.thumbnail`,
|
||||
round to the nearest integer, instead of always rounding down.
|
||||
This better preserves the original aspect ratio.
|
||||
|
||||
When the image width or height is not divisible by 8 the last row and column
|
||||
in the image get the correct weight after JPEG DCT scaling.
|
||||
|
|
|
@ -1126,11 +1126,14 @@ class Image:
|
|||
"""
|
||||
Configures the image file loader so it returns a version of the
|
||||
image that as closely as possible matches the given mode and
|
||||
size. For example, you can use this method to convert a color
|
||||
size. For example, you can use this method to convert a color
|
||||
JPEG to greyscale while loading it.
|
||||
|
||||
If any changes are made, returns a tuple with the chosen ``mode`` and
|
||||
``box`` with coordinates of the original image within the altered one.
|
||||
|
||||
Note that this method modifies the :py:class:`~PIL.Image.Image` object
|
||||
in place. If the image has already been loaded, this method has no
|
||||
in place. If the image has already been loaded, this method has no
|
||||
effect.
|
||||
|
||||
Note: This method is not implemented for most images. It is
|
||||
|
@ -2143,14 +2146,17 @@ class Image:
|
|||
x = max(round(x * size[1] / y), 1)
|
||||
y = round(size[1])
|
||||
size = x, y
|
||||
box = None
|
||||
|
||||
if size == self.size:
|
||||
return
|
||||
|
||||
self.draft(None, size)
|
||||
res = self.draft(None, size)
|
||||
if res is not None:
|
||||
box = res[1]
|
||||
|
||||
if self.size != size:
|
||||
im = self.resize(size, resample)
|
||||
im = self.resize(size, resample, box=box)
|
||||
|
||||
self.im = im.im
|
||||
self._size = size
|
||||
|
|
|
@ -122,11 +122,6 @@ class ImageFile(Image.Image):
|
|||
self.fp.close()
|
||||
raise
|
||||
|
||||
def draft(self, mode, size):
|
||||
"""Set draft mode"""
|
||||
|
||||
pass
|
||||
|
||||
def get_format_mimetype(self):
|
||||
if self.custom_mimetype:
|
||||
return self.custom_mimetype
|
||||
|
|
|
@ -413,7 +413,8 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
return
|
||||
|
||||
d, e, o, a = self.tile[0]
|
||||
scale = 0
|
||||
scale = 1
|
||||
original_size = self.size
|
||||
|
||||
if a[0] == "RGB" and mode in ["L", "YCbCr"]:
|
||||
self.mode = mode
|
||||
|
@ -436,7 +437,8 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
self.tile = [(d, e, o, a)]
|
||||
self.decoderconfig = (scale, 0)
|
||||
|
||||
return self
|
||||
box = (0, 0, original_size[0] / float(scale), original_size[1] / float(scale))
|
||||
return (self.mode, box)
|
||||
|
||||
def load_djpeg(self):
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user