Merge pull request #2240 from uploadcare/draft-min-scale

Use minimal scale for jpeg drafts
This commit is contained in:
wiredfool 2016-11-22 15:22:59 +00:00 committed by GitHub
commit 620d082457
2 changed files with 63 additions and 22 deletions

View File

@ -341,6 +341,10 @@ class JpegImageFile(ImageFile.ImageFile):
if len(self.tile) != 1: if len(self.tile) != 1:
return return
# Protect from second call
if self.decoderconfig:
return
d, e, o, a = self.tile[0] d, e, o, a = self.tile[0]
scale = 0 scale = 0
@ -349,7 +353,7 @@ class JpegImageFile(ImageFile.ImageFile):
a = mode, "" a = mode, ""
if size: if size:
scale = max(self.size[0] // size[0], self.size[1] // size[1]) scale = min(self.size[0] // size[0], self.size[1] // size[1])
for s in [8, 4, 2, 1]: for s in [8, 4, 2, 1]:
if scale >= s: if scale >= s:
break break

View File

@ -2,36 +2,73 @@ from helper import unittest, PillowTestCase, fromstring, tostring
from PIL import Image from PIL import Image
CODECS = dir(Image.core)
FILENAME = "Tests/images/hopper.jpg"
DATA = tostring(Image.open(FILENAME).resize((512, 512)), "JPEG")
def draft(mode, size):
im = fromstring(DATA)
im.draft(mode, size)
return im
class TestImageDraft(PillowTestCase): class TestImageDraft(PillowTestCase):
def setUp(self): def setUp(self):
if "jpeg_encoder" not in CODECS or "jpeg_decoder" not in CODECS: codecs = dir(Image.core)
if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs:
self.skipTest("jpeg support not available") self.skipTest("jpeg support not available")
def draft_roundtrip(self, in_mode, in_size, req_mode, req_size):
im = Image.new(in_mode, in_size)
data = tostring(im, 'JPEG')
im = fromstring(data)
im.draft(req_mode, req_size)
return im
def test_size(self): def test_size(self):
self.assertEqual(draft("RGB", (512, 512)).size, (512, 512)) for in_size, req_size, out_size in [
self.assertEqual(draft("RGB", (256, 256)).size, (256, 256)) ((435, 361), (2048, 2048), (435, 361)), # bigger
self.assertEqual(draft("RGB", (128, 128)).size, (128, 128)) ((435, 361), (435, 361), (435, 361)), # same
self.assertEqual(draft("RGB", (64, 64)).size, (64, 64)) ((128, 128), (64, 64), (64, 64)),
self.assertEqual(draft("RGB", (32, 32)).size, (64, 64)) ((128, 128), (32, 32), (32, 32)),
((128, 128), (16, 16), (16, 16)),
# large requested width
((435, 361), (218, 128), (435, 361)), # almost 2x
((435, 361), (217, 128), (218, 181)), # more than 2x
((435, 361), (109, 64), (218, 181)), # almost 4x
((435, 361), (108, 64), (109, 91)), # more than 4x
((435, 361), (55, 32), (109, 91)), # almost 8x
((435, 361), (54, 32), (55, 46)), # more than 8x
((435, 361), (27, 16), (55, 46)), # more than 16x
# and vice versa
((435, 361), (128, 181), (435, 361)), # almost 2x
((435, 361), (128, 180), (218, 181)), # more than 2x
((435, 361), (64, 91), (218, 181)), # almost 4x
((435, 361), (64, 90), (109, 91)), # more than 4x
((435, 361), (32, 46), (109, 91)), # almost 8x
((435, 361), (32, 45), (55, 46)), # more than 8x
((435, 361), (16, 22), (55, 46)), # more than 16x
]:
im = self.draft_roundtrip('L', in_size, None, req_size)
im.load()
self.assertEqual(im.size, out_size)
def test_mode(self): def test_mode(self):
self.assertEqual(draft("1", (512, 512)).mode, "RGB") for in_mode, req_mode, out_mode in [
self.assertEqual(draft("L", (512, 512)).mode, "L") ("RGB", "1", "RGB"),
self.assertEqual(draft("RGB", (512, 512)).mode, "RGB") ("RGB", "L", "L"),
self.assertEqual(draft("YCbCr", (512, 512)).mode, "YCbCr") ("RGB", "RGB", "RGB"),
("RGB", "YCbCr", "YCbCr"),
("L", "1", "L"),
("L", "L", "L"),
("L", "RGB", "L"),
("L", "YCbCr", "L"),
("CMYK", "1", "CMYK"),
("CMYK", "L", "CMYK"),
("CMYK", "RGB", "CMYK"),
("CMYK", "YCbCr", "CMYK"),
]:
im = self.draft_roundtrip(in_mode, (64, 64), req_mode, None)
im.load()
self.assertEqual(im.mode, out_mode)
def test_several_drafts(self):
im = self.draft_roundtrip('L', (128, 128), None, (64, 64))
im.draft(None, (64, 64))
im.load()
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()