Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent masking of Image reduce method in Jpeg2KImagePlugin #4474

Merged
merged 4 commits into from Mar 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions Tests/test_file_jpeg2k.py
Expand Up @@ -127,10 +127,17 @@ def test_prog_res_rt():

def test_reduce():
with Image.open("Tests/images/test-card-lossless.jp2") as im:
assert callable(im.reduce)

im.reduce = 2
assert im.reduce == 2

im.load()
assert im.size == (160, 120)

im.thumbnail((40, 40))
assert im.size == (40, 30)


def test_layers_type(tmp_path):
outfile = str(tmp_path / "temp_layers.jp2")
Expand Down
11 changes: 11 additions & 0 deletions Tests/test_image_reduce.py
Expand Up @@ -3,6 +3,9 @@

from .helper import convert_to_comparable

codecs = dir(Image.core)


# There are several internal implementations
remarkable_factors = [
# special implementations
Expand Down Expand Up @@ -247,3 +250,11 @@ def test_mode_F():
for factor in remarkable_factors:
compare_reduce_with_reference(im, factor, 0, 0)
compare_reduce_with_box(im, factor)


@pytest.mark.skipif(
"jpeg2k_decoder" not in codecs, reason="JPEG 2000 support not available"
)
def test_jpeg2k():
with Image.open("Tests/images/test-card-lossless.jp2") as im:
assert im.reduce(2).size == (320, 240)
6 changes: 5 additions & 1 deletion src/PIL/Image.py
Expand Up @@ -1866,7 +1866,11 @@ def resize(self, size, resample=BICUBIC, box=None, reducing_gap=None):
factor_y = int((box[3] - box[1]) / size[1] / reducing_gap) or 1
if factor_x > 1 or factor_y > 1:
reduce_box = self._get_safe_box(size, resample, box)
self = self.reduce((factor_x, factor_y), box=reduce_box)
factor = (factor_x, factor_y)
if callable(self.reduce):
self = self.reduce(factor, box=reduce_box)
else:
self = Image.reduce(self, factor, box=reduce_box)
homm marked this conversation as resolved.
Show resolved Hide resolved
box = (
(box[0] - reduce_box[0]) / factor_x,
(box[1] - reduce_box[1]) / factor_y,
Expand Down
22 changes: 16 additions & 6 deletions src/PIL/Jpeg2KImagePlugin.py
Expand Up @@ -176,7 +176,7 @@ def _open(self):
if self.size is None or self.mode is None:
raise SyntaxError("unable to determine size/mode")

self.reduce = 0
self._reduce = 0
self.layers = 0

fd = -1
Expand All @@ -200,23 +200,33 @@ def _open(self):
"jpeg2k",
(0, 0) + self.size,
0,
(self.codec, self.reduce, self.layers, fd, length),
(self.codec, self._reduce, self.layers, fd, length),
)
]

@property
def reduce(self):
# https://github.com/python-pillow/Pillow/issues/4343 found that the
# new Image 'reduce' method was shadowed by this plugin's 'reduce'
# property. This attempts to allow for both scenarios
return self._reduce or super().reduce
homm marked this conversation as resolved.
Show resolved Hide resolved

@reduce.setter
def reduce(self, value):
self._reduce = value

def load(self):
if self.reduce:
power = 1 << self.reduce
if self.tile and self._reduce:
power = 1 << self._reduce
adjust = power >> 1
self._size = (
int((self.size[0] + adjust) / power),
int((self.size[1] + adjust) / power),
)

if self.tile:
# Update the reduce and layers settings
t = self.tile[0]
t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4])
t3 = (t[3][0], self._reduce, self.layers, t[3][3], t[3][4])
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]

return ImageFile.ImageFile.load(self)
Expand Down