mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-09-21 03:18:57 +03:00
Merge branch 'master' into pre-commit-diff
This commit is contained in:
commit
2d3fd6ea02
|
@ -5,6 +5,15 @@ Changelog (Pillow)
|
||||||
7.2.0 (unreleased)
|
7.2.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Deprecated _showxv #4714
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Deprecate Image.show(command="...") #4646
|
||||||
|
[nulano, hugovk, radarhere]
|
||||||
|
|
||||||
|
- Updated JPEG magic number #4707
|
||||||
|
[Cykooz, radarhere]
|
||||||
|
|
||||||
- Change STRIPBYTECOUNTS to LONG if necessary when saving #4626
|
- Change STRIPBYTECOUNTS to LONG if necessary when saving #4626
|
||||||
[radarhere, hugovk]
|
[radarhere, hugovk]
|
||||||
|
|
||||||
|
|
|
@ -165,12 +165,6 @@ def assert_tuple_approx_equal(actuals, targets, threshold, msg):
|
||||||
assert value, msg + ": " + repr(actuals) + " != " + repr(targets)
|
assert value, msg + ": " + repr(actuals) + " != " + repr(targets)
|
||||||
|
|
||||||
|
|
||||||
def skip_known_bad_test(msg=None):
|
|
||||||
# Skip if PILLOW_RUN_KNOWN_BAD is not true in the environment.
|
|
||||||
if not os.environ.get("PILLOW_RUN_KNOWN_BAD", False):
|
|
||||||
pytest.skip(msg or "Known bad test")
|
|
||||||
|
|
||||||
|
|
||||||
def skip_unless_feature(feature):
|
def skip_unless_feature(feature):
|
||||||
reason = "%s not available" % feature
|
reason = "%s not available" % feature
|
||||||
return pytest.mark.skipif(not features.check(feature), reason=reason)
|
return pytest.mark.skipif(not features.check(feature), reason=reason)
|
||||||
|
|
BIN
Tests/images/imagedraw_wide_line_larger_than_int.png
Normal file
BIN
Tests/images/imagedraw_wide_line_larger_than_int.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 557 B |
|
@ -3,7 +3,14 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import ExifTags, Image, ImageFile, JpegImagePlugin, features
|
from PIL import (
|
||||||
|
ExifTags,
|
||||||
|
Image,
|
||||||
|
ImageFile,
|
||||||
|
JpegImagePlugin,
|
||||||
|
UnidentifiedImageError,
|
||||||
|
features,
|
||||||
|
)
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image,
|
assert_image,
|
||||||
|
@ -709,6 +716,24 @@ class TestFileJpeg:
|
||||||
with Image.open("Tests/images/icc-after-SOF.jpg") as im:
|
with Image.open("Tests/images/icc-after-SOF.jpg") as im:
|
||||||
assert im.info["icc_profile"] == b"profile"
|
assert im.info["icc_profile"] == b"profile"
|
||||||
|
|
||||||
|
def test_jpeg_magic_number(self):
|
||||||
|
size = 4097
|
||||||
|
buffer = BytesIO(b"\xFF" * size) # Many xFF bytes
|
||||||
|
buffer.max_pos = 0
|
||||||
|
orig_read = buffer.read
|
||||||
|
|
||||||
|
def read(n=-1):
|
||||||
|
res = orig_read(n)
|
||||||
|
buffer.max_pos = max(buffer.max_pos, buffer.tell())
|
||||||
|
return res
|
||||||
|
|
||||||
|
buffer.read = read
|
||||||
|
with pytest.raises(UnidentifiedImageError):
|
||||||
|
Image.open(buffer)
|
||||||
|
|
||||||
|
# Assert the entire file has not been read
|
||||||
|
assert 0 < buffer.max_pos < size
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not is_win32(), reason="Windows only")
|
@pytest.mark.skipif(not is_win32(), reason="Windows only")
|
||||||
@skip_unless_feature("jpg")
|
@skip_unless_feature("jpg")
|
||||||
|
|
|
@ -207,6 +207,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
del core_items[tag]
|
del core_items[tag]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
del core_items[320] # colormap is special, tested below
|
||||||
|
|
||||||
# Type codes:
|
# Type codes:
|
||||||
# 2: "ascii",
|
# 2: "ascii",
|
||||||
|
@ -491,6 +492,18 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
with Image.open(out) as im2:
|
with Image.open(out) as im2:
|
||||||
assert_image_equal(im, im2)
|
assert_image_equal(im, im2)
|
||||||
|
|
||||||
|
def test_palette_save(self, tmp_path):
|
||||||
|
im = hopper("P")
|
||||||
|
out = str(tmp_path / "temp.tif")
|
||||||
|
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
|
im.save(out)
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
|
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
# colormap/palette tag
|
||||||
|
assert len(reloaded.tag_v2[320]) == 768
|
||||||
|
|
||||||
def xtest_bw_compression_w_rgb(self, tmp_path):
|
def xtest_bw_compression_w_rgb(self, tmp_path):
|
||||||
""" This test passes, but when running all tests causes a failure due
|
""" This test passes, but when running all tests causes a failure due
|
||||||
to output on stderr from the error thrown by libtiff. We need to
|
to output on stderr from the error thrown by libtiff. We need to
|
||||||
|
|
|
@ -4,13 +4,7 @@ import subprocess
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import (
|
from .helper import IMCONVERT, assert_image_equal, hopper, imagemagick_available
|
||||||
IMCONVERT,
|
|
||||||
assert_image_equal,
|
|
||||||
hopper,
|
|
||||||
imagemagick_available,
|
|
||||||
skip_known_bad_test,
|
|
||||||
)
|
|
||||||
|
|
||||||
_roundtrip = imagemagick_available()
|
_roundtrip = imagemagick_available()
|
||||||
|
|
||||||
|
@ -62,13 +56,13 @@ def test_monochrome(tmp_path):
|
||||||
roundtrip(tmp_path, mode)
|
roundtrip(tmp_path, mode)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reason="Palm P image is wrong")
|
||||||
def test_p_mode(tmp_path):
|
def test_p_mode(tmp_path):
|
||||||
# Arrange
|
# Arrange
|
||||||
mode = "P"
|
mode = "P"
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
helper_save_as_palm(tmp_path, mode)
|
helper_save_as_palm(tmp_path, mode)
|
||||||
skip_known_bad_test("Palm P image is wrong")
|
|
||||||
roundtrip(tmp_path, mode)
|
roundtrip(tmp_path, mode)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import tempfile
|
||||||
|
|
||||||
import PIL
|
import PIL
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageDraw, ImagePalette, UnidentifiedImageError
|
from PIL import Image, ImageDraw, ImagePalette, ImageShow, UnidentifiedImageError
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
|
@ -585,6 +585,22 @@ class TestImage:
|
||||||
expected = Image.new(mode, (100, 100), color)
|
expected = Image.new(mode, (100, 100), color)
|
||||||
assert_image_equal(im.convert(mode), expected)
|
assert_image_equal(im.convert(mode), expected)
|
||||||
|
|
||||||
|
def test_showxv_deprecation(self):
|
||||||
|
class TestViewer(ImageShow.Viewer):
|
||||||
|
def show_image(self, image, **options):
|
||||||
|
return True
|
||||||
|
|
||||||
|
viewer = TestViewer()
|
||||||
|
ImageShow.register(viewer, -1)
|
||||||
|
|
||||||
|
im = Image.new("RGB", (50, 50), "white")
|
||||||
|
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
Image._showxv(im)
|
||||||
|
|
||||||
|
# Restore original state
|
||||||
|
ImageShow._viewers.pop(0)
|
||||||
|
|
||||||
def test_no_resource_warning_on_save(self, tmp_path):
|
def test_no_resource_warning_on_save(self, tmp_path):
|
||||||
# https://github.com/python-pillow/Pillow/issues/835
|
# https://github.com/python-pillow/Pillow/issues/835
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -664,6 +680,18 @@ class TestImage:
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
assert str(e) == "buffer overrun when reading image file"
|
assert str(e) == "buffer overrun when reading image file"
|
||||||
|
|
||||||
|
def test_show_deprecation(self, monkeypatch):
|
||||||
|
monkeypatch.setattr(Image, "_show", lambda *args, **kwargs: None)
|
||||||
|
|
||||||
|
im = Image.new("RGB", (50, 50), "white")
|
||||||
|
|
||||||
|
with pytest.warns(None) as raised:
|
||||||
|
im.show()
|
||||||
|
assert not raised
|
||||||
|
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
im.show(command="mock")
|
||||||
|
|
||||||
|
|
||||||
class MockEncoder:
|
class MockEncoder:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageMath, ImageMode
|
from PIL import Image, ImageMath, ImageMode
|
||||||
|
|
||||||
from .helper import convert_to_comparable
|
from .helper import convert_to_comparable, skip_unless_feature
|
||||||
|
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
|
|
||||||
|
@ -254,9 +254,7 @@ def test_mode_F():
|
||||||
compare_reduce_with_box(im, factor)
|
compare_reduce_with_box(im, factor)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@skip_unless_feature("jpg_2000")
|
||||||
"jpeg2k_decoder" not in codecs, reason="JPEG 2000 support not available"
|
|
||||||
)
|
|
||||||
def test_jpeg2k():
|
def test_jpeg2k():
|
||||||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||||
assert im.reduce(2).size == (320, 240)
|
assert im.reduce(2).size == (320, 240)
|
||||||
|
|
|
@ -218,7 +218,7 @@ class TestImagingCoreResampleAccuracy:
|
||||||
assert_image_equal(im, ref)
|
assert_image_equal(im, ref)
|
||||||
|
|
||||||
|
|
||||||
class CoreResampleConsistencyTest:
|
class TestCoreResampleConsistency:
|
||||||
def make_case(self, mode, fill):
|
def make_case(self, mode, fill):
|
||||||
im = Image.new(mode, (512, 9), fill)
|
im = Image.new(mode, (512, 9), fill)
|
||||||
return im.resize((9, 512), Image.LANCZOS), im.load()[0, 0]
|
return im.resize((9, 512), Image.LANCZOS), im.load()[0, 0]
|
||||||
|
@ -253,7 +253,7 @@ class CoreResampleConsistencyTest:
|
||||||
self.run_case(self.make_case("F", 1.192093e-07))
|
self.run_case(self.make_case("F", 1.192093e-07))
|
||||||
|
|
||||||
|
|
||||||
class CoreResampleAlphaCorrectTest:
|
class TestCoreResampleAlphaCorrect:
|
||||||
def make_levels_case(self, mode):
|
def make_levels_case(self, mode):
|
||||||
i = Image.new(mode, (256, 16))
|
i = Image.new(mode, (256, 16))
|
||||||
px = i.load()
|
px = i.load()
|
||||||
|
@ -274,7 +274,7 @@ class CoreResampleAlphaCorrectTest:
|
||||||
len(used_colors), y
|
len(used_colors), y
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.skip("Current implementation isn't precise enough")
|
@pytest.mark.xfail(reason="Current implementation isn't precise enough")
|
||||||
def test_levels_rgba(self):
|
def test_levels_rgba(self):
|
||||||
case = self.make_levels_case("RGBA")
|
case = self.make_levels_case("RGBA")
|
||||||
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
||||||
|
@ -283,7 +283,7 @@ class CoreResampleAlphaCorrectTest:
|
||||||
self.run_levels_case(case.resize((512, 32), Image.BICUBIC))
|
self.run_levels_case(case.resize((512, 32), Image.BICUBIC))
|
||||||
self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
|
self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
|
||||||
|
|
||||||
@pytest.mark.skip("Current implementation isn't precise enough")
|
@pytest.mark.xfail(reason="Current implementation isn't precise enough")
|
||||||
def test_levels_la(self):
|
def test_levels_la(self):
|
||||||
case = self.make_levels_case("LA")
|
case = self.make_levels_case("LA")
|
||||||
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
||||||
|
@ -329,7 +329,7 @@ class CoreResampleAlphaCorrectTest:
|
||||||
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255,))
|
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255,))
|
||||||
|
|
||||||
|
|
||||||
class CoreResamplePassesTest:
|
class TestCoreResamplePasses:
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def count(self, diff):
|
def count(self, diff):
|
||||||
count = Image.core.get_stats()["new_count"]
|
count = Image.core.get_stats()["new_count"]
|
||||||
|
@ -372,7 +372,7 @@ class CoreResamplePassesTest:
|
||||||
assert_image_similar(with_box, cropped, 0.1)
|
assert_image_similar(with_box, cropped, 0.1)
|
||||||
|
|
||||||
|
|
||||||
class CoreResampleCoefficientsTest:
|
class TestCoreResampleCoefficients:
|
||||||
def test_reduce(self):
|
def test_reduce(self):
|
||||||
test_color = 254
|
test_color = 254
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ class CoreResampleCoefficientsTest:
|
||||||
assert histogram[0x100 * 3 + 0xFF] == 0x10000
|
assert histogram[0x100 * 3 + 0xFF] == 0x10000
|
||||||
|
|
||||||
|
|
||||||
class CoreResampleBoxTest:
|
class TestCoreResampleBox:
|
||||||
def test_wrong_arguments(self):
|
def test_wrong_arguments(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
for resample in (
|
for resample in (
|
||||||
|
|
|
@ -5,7 +5,7 @@ from PIL import Image, ImageColor, ImageDraw, ImageFont
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
assert_image_similar,
|
assert_image_similar_tofile,
|
||||||
hopper,
|
hopper,
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
@ -71,7 +71,7 @@ def helper_arc(bbox, start, end):
|
||||||
draw.arc(bbox, start, end)
|
draw.arc(bbox, start, end)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_arc.png"), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc1():
|
def test_arc1():
|
||||||
|
@ -110,20 +110,19 @@ def test_arc_no_loops():
|
||||||
draw.arc(BBOX1, start=start, end=end)
|
draw.arc(BBOX1, start=start, end=end)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_no_loops.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc_width():
|
def test_arc_width():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_arc_width.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.arc(BBOX1, 10, 260, width=5)
|
draw.arc(BBOX1, 10, 260, width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc_width_pieslice_large():
|
def test_arc_width_pieslice_large():
|
||||||
|
@ -131,26 +130,24 @@ def test_arc_width_pieslice_large():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_arc_width_pieslice.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.arc(BBOX1, 10, 260, fill="yellow", width=100)
|
draw.arc(BBOX1, 10, 260, fill="yellow", width=100)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_pieslice.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc_width_fill():
|
def test_arc_width_fill():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_arc_width_fill.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.arc(BBOX1, 10, 260, fill="yellow", width=5)
|
draw.arc(BBOX1, 10, 260, fill="yellow", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_fill.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc_width_non_whole_angle():
|
def test_arc_width_non_whole_angle():
|
||||||
|
@ -163,7 +160,7 @@ def test_arc_width_non_whole_angle():
|
||||||
draw.arc(BBOX1, 10, 259.5, width=5)
|
draw.arc(BBOX1, 10, 259.5, width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_bitmap():
|
def test_bitmap():
|
||||||
|
@ -190,7 +187,7 @@ def helper_chord(mode, bbox, start, end):
|
||||||
draw.chord(bbox, start, end, fill="red", outline="yellow")
|
draw.chord(bbox, start, end, fill="red", outline="yellow")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_chord1():
|
def test_chord1():
|
||||||
|
@ -209,26 +206,24 @@ def test_chord_width():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_chord_width.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.chord(BBOX1, 10, 260, outline="yellow", width=5)
|
draw.chord(BBOX1, 10, 260, outline="yellow", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_chord_width_fill():
|
def test_chord_width_fill():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_chord_width_fill.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5)
|
draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width_fill.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_chord_zero_width():
|
def test_chord_zero_width():
|
||||||
|
@ -254,7 +249,7 @@ def helper_ellipse(mode, bbox):
|
||||||
draw.ellipse(bbox, fill="green", outline="blue")
|
draw.ellipse(bbox, fill="green", outline="blue")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse1():
|
def test_ellipse1():
|
||||||
|
@ -276,8 +271,8 @@ def test_ellipse_translucent():
|
||||||
draw.ellipse(BBOX1, fill=(0, 255, 0, 127))
|
draw.ellipse(BBOX1, fill=(0, 255, 0, 127))
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
expected = Image.open("Tests/images/imagedraw_ellipse_translucent.png")
|
expected = "Tests/images/imagedraw_ellipse_translucent.png"
|
||||||
assert_image_similar(im, expected, 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_edge():
|
def test_ellipse_edge():
|
||||||
|
@ -289,7 +284,7 @@ def test_ellipse_edge():
|
||||||
draw.ellipse(((0, 0), (W - 1, H)), fill="white")
|
draw.ellipse(((0, 0), (W - 1, H)), fill="white")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_edge.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_symmetric():
|
def test_ellipse_symmetric():
|
||||||
|
@ -304,39 +299,36 @@ def test_ellipse_width():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_ellipse_width.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.ellipse(BBOX1, outline="blue", width=5)
|
draw.ellipse(BBOX1, outline="blue", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_width_large():
|
def test_ellipse_width_large():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (500, 500))
|
im = Image.new("RGB", (500, 500))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_ellipse_width_large.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.ellipse((25, 25, 475, 475), outline="blue", width=75)
|
draw.ellipse((25, 25, 475, 475), outline="blue", width=75)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_large.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_width_fill():
|
def test_ellipse_width_fill():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_ellipse_width_fill.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.ellipse(BBOX1, fill="green", outline="blue", width=5)
|
draw.ellipse(BBOX1, fill="green", outline="blue", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_fill.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_zero_width():
|
def test_ellipse_zero_width():
|
||||||
|
@ -423,7 +415,7 @@ def helper_pieslice(bbox, start, end):
|
||||||
draw.pieslice(bbox, start, end, fill="white", outline="blue")
|
draw.pieslice(bbox, start, end, fill="white", outline="blue")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_pieslice.png"), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_pieslice1():
|
def test_pieslice1():
|
||||||
|
@ -440,13 +432,12 @@ def test_pieslice_width():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_pieslice_width.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.pieslice(BBOX1, 10, 260, outline="blue", width=5)
|
draw.pieslice(BBOX1, 10, 260, outline="blue", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice_width.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_pieslice_width_fill():
|
def test_pieslice_width_fill():
|
||||||
|
@ -459,7 +450,7 @@ def test_pieslice_width_fill():
|
||||||
draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5)
|
draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_pieslice_zero_width():
|
def test_pieslice_zero_width():
|
||||||
|
@ -571,13 +562,12 @@ def test_big_rectangle():
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
bbox = [(-1, -1), (W + 1, H + 1)]
|
bbox = [(-1, -1), (W + 1, H + 1)]
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_big_rectangle.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.rectangle(bbox, fill="orange")
|
draw.rectangle(bbox, fill="orange")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_big_rectangle.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_rectangle_width():
|
def test_rectangle_width():
|
||||||
|
@ -878,13 +868,25 @@ def test_wide_line_dot():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_wide_line_dot.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.line([(50, 50), (50, 50)], width=3)
|
draw.line([(50, 50), (50, 50)], width=3)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_wide_line_dot.png", 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_wide_line_larger_than_int():
|
||||||
|
# Arrange
|
||||||
|
im = Image.new("RGB", (W, H))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
expected = "Tests/images/imagedraw_wide_line_larger_than_int.png"
|
||||||
|
|
||||||
|
# Act
|
||||||
|
draw.line([(0, 0), (32768, 32768)], width=3)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -971,13 +973,12 @@ def test_wide_line_dot():
|
||||||
def test_line_joint(xy):
|
def test_line_joint(xy):
|
||||||
im = Image.new("RGB", (500, 325))
|
im = Image.new("RGB", (500, 325))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_line_joint_curve.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.line(xy, GRAY, 50, "curve")
|
draw.line(xy, GRAY, 50, "curve")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 3)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_line_joint_curve.png", 3)
|
||||||
|
|
||||||
|
|
||||||
def test_textsize_empty_string():
|
def test_textsize_empty_string():
|
||||||
|
@ -1018,8 +1019,8 @@ def test_stroke():
|
||||||
draw.text((10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill)
|
draw.text((10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(
|
assert_image_similar_tofile(
|
||||||
im, Image.open("Tests/images/imagedraw_stroke_" + suffix + ".png"), 3.1
|
im, "Tests/images/imagedraw_stroke_" + suffix + ".png", 3.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1034,9 +1035,7 @@ def test_stroke_descender():
|
||||||
draw.text((10, 0), "y", "#f00", font, stroke_width=2, stroke_fill="#0f0")
|
draw.text((10, 0), "y", "#f00", font, stroke_width=2, stroke_fill="#0f0")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_descender.png", 6.76)
|
||||||
im, Image.open("Tests/images/imagedraw_stroke_descender.png"), 6.76
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("freetype2")
|
@skip_unless_feature("freetype2")
|
||||||
|
@ -1052,9 +1051,7 @@ def test_stroke_multiline():
|
||||||
)
|
)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_multiline.png", 3.3)
|
||||||
im, Image.open("Tests/images/imagedraw_stroke_multiline.png"), 3.3
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_same_color_outline():
|
def test_same_color_outline():
|
||||||
|
@ -1093,4 +1090,4 @@ def test_same_color_outline():
|
||||||
expected = "Tests/images/imagedraw_outline_{}_{}.png".format(
|
expected = "Tests/images/imagedraw_outline_{}_{}.png".format(
|
||||||
operation, mode
|
operation, mode
|
||||||
)
|
)
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
|
@ -455,7 +455,7 @@ class TestImageFont:
|
||||||
with pytest.raises(UnicodeEncodeError):
|
with pytest.raises(UnicodeEncodeError):
|
||||||
font.getsize("’")
|
font.getsize("’")
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="failing on PyPy")
|
@pytest.mark.xfail(is_pypy(), reason="failing on PyPy with Raqm")
|
||||||
def test_unicode_extended(self):
|
def test_unicode_extended(self):
|
||||||
# issue #3777
|
# issue #3777
|
||||||
text = "A\u278A\U0001F12B"
|
text = "A\u278A\U0001F12B"
|
||||||
|
|
|
@ -5,7 +5,7 @@ import sys
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageGrab
|
from PIL import Image, ImageGrab
|
||||||
|
|
||||||
from .helper import assert_image, assert_image_equal_tofile
|
from .helper import assert_image, assert_image_equal_tofile, skip_unless_feature
|
||||||
|
|
||||||
|
|
||||||
class TestImageGrab:
|
class TestImageGrab:
|
||||||
|
@ -23,7 +23,7 @@ class TestImageGrab:
|
||||||
im = ImageGrab.grab(bbox=(10, 20, 50, 80))
|
im = ImageGrab.grab(bbox=(10, 20, 50, 80))
|
||||||
assert_image(im, im.mode, (40, 60))
|
assert_image(im, im.mode, (40, 60))
|
||||||
|
|
||||||
@pytest.mark.skipif(not Image.core.HAVE_XCB, reason="requires XCB")
|
@skip_unless_feature("xcb")
|
||||||
def test_grab_x11(self):
|
def test_grab_x11(self):
|
||||||
try:
|
try:
|
||||||
if sys.platform not in ("win32", "darwin"):
|
if sys.platform not in ("win32", "darwin"):
|
||||||
|
@ -46,7 +46,7 @@ class TestImageGrab:
|
||||||
ImageGrab.grab(xdisplay="")
|
ImageGrab.grab(xdisplay="")
|
||||||
assert str(e.value).startswith("Pillow was built without XCB support")
|
assert str(e.value).startswith("Pillow was built without XCB support")
|
||||||
|
|
||||||
@pytest.mark.skipif(not Image.core.HAVE_XCB, reason="requires XCB")
|
@skip_unless_feature("xcb")
|
||||||
def test_grab_invalid_xdisplay(self):
|
def test_grab_invalid_xdisplay(self):
|
||||||
with pytest.raises(OSError) as e:
|
with pytest.raises(OSError) as e:
|
||||||
ImageGrab.grab(xdisplay="error.test:0.0")
|
ImageGrab.grab(xdisplay="error.test:0.0")
|
||||||
|
|
36
docs/PIL.rst
36
docs/PIL.rst
|
@ -4,6 +4,14 @@ PIL Package (autodoc of remaining modules)
|
||||||
Reference for modules whose documentation has not yet been ported or written
|
Reference for modules whose documentation has not yet been ported or written
|
||||||
can be found here.
|
can be found here.
|
||||||
|
|
||||||
|
:mod:`PIL` Module
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. py:module:: PIL
|
||||||
|
|
||||||
|
.. autoexception:: UnidentifiedImageError
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
:mod:`BdfFontFile` Module
|
:mod:`BdfFontFile` Module
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
@ -52,21 +60,12 @@ can be found here.
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
.. intentionally skipped documenting this because it's not documented anywhere
|
|
||||||
|
|
||||||
:mod:`ImageDraw2` Module
|
:mod:`ImageDraw2` Module
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
.. automodule:: PIL.ImageDraw2
|
.. automodule:: PIL.ImageDraw2
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:member-order: bysource
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
:mod:`ImageShow` Module
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. automodule:: PIL.ImageShow
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
@ -78,14 +77,6 @@ can be found here.
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
:mod:`JpegPresets` Module
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
.. automodule:: PIL.JpegPresets
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
:mod:`PaletteFile` Module
|
:mod:`PaletteFile` Module
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
@ -140,12 +131,3 @@ can be found here.
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
:mod:`_binary` Module
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
.. automodule:: PIL._binary
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import sphinx_rtd_theme
|
||||||
# -- General configuration ------------------------------------------------
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
# needs_sphinx = '1.0'
|
needs_sphinx = "2.4"
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
|
|
@ -12,6 +12,23 @@ Deprecated features
|
||||||
Below are features which are considered deprecated. Where appropriate,
|
Below are features which are considered deprecated. Where appropriate,
|
||||||
a ``DeprecationWarning`` is issued.
|
a ``DeprecationWarning`` is issued.
|
||||||
|
|
||||||
|
Image.show command parameter
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. deprecated:: 7.2.0
|
||||||
|
|
||||||
|
The ``command`` parameter was deprecated and will be removed in a future release.
|
||||||
|
Use a subclass of ``ImageShow.Viewer`` instead.
|
||||||
|
|
||||||
|
Image._showxv
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. deprecated:: 7.2.0
|
||||||
|
|
||||||
|
``Image._showxv`` has been deprecated. Use :py:meth:`~PIL.Image.Image.show`
|
||||||
|
instead. If custom behaviour is required, use :py:meth:`~PIL.ImageShow.register` to add
|
||||||
|
a custom :py:class:`~PIL.ImageShow.Viewer` class.
|
||||||
|
|
||||||
ImageFile.raise_ioerror
|
ImageFile.raise_ioerror
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -249,8 +249,8 @@ class DXT1Decoder(ImageFile.PyDecoder):
|
||||||
def decode(self, buffer):
|
def decode(self, buffer):
|
||||||
try:
|
try:
|
||||||
self.set_as_raw(_dxt1(self.fd, self.state.xsize, self.state.ysize))
|
self.set_as_raw(_dxt1(self.fd, self.state.xsize, self.state.ysize))
|
||||||
except struct.error:
|
except struct.error as e:
|
||||||
raise OSError("Truncated DDS file")
|
raise OSError("Truncated DDS file") from e
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -260,8 +260,8 @@ class DXT5Decoder(ImageFile.PyDecoder):
|
||||||
def decode(self, buffer):
|
def decode(self, buffer):
|
||||||
try:
|
try:
|
||||||
self.set_as_raw(_dxt5(self.fd, self.state.xsize, self.state.ysize))
|
self.set_as_raw(_dxt5(self.fd, self.state.xsize, self.state.ysize))
|
||||||
except struct.error:
|
except struct.error as e:
|
||||||
raise OSError("Truncated DDS file")
|
raise OSError("Truncated DDS file") from e
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ Over 30 different file formats can be identified and read by the library.
|
||||||
Write support is less extensive, but most common interchange and presentation
|
Write support is less extensive, but most common interchange and presentation
|
||||||
formats are supported.
|
formats are supported.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` function identifies files from their
|
The :py:meth:`~PIL.Image.open` function identifies files from their
|
||||||
contents, not their names, but the :py:meth:`~PIL.Image.Image.save` method
|
contents, not their names, but the :py:meth:`~PIL.Image.Image.save` method
|
||||||
looks at the name to determine which format to use, unless the format is given
|
looks at the name to determine which format to use, unless the format is given
|
||||||
explicitly.
|
explicitly.
|
||||||
|
@ -25,7 +25,7 @@ Pillow reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P`
|
||||||
or ``RGB`` data. 16-colour images are read as ``P`` images. Run-length encoding
|
or ``RGB`` data. 16-colour images are read as ``P`` images. Run-length encoding
|
||||||
is not supported.
|
is not supported.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
The :py:meth:`~PIL.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties:
|
:py:attr:`~PIL.Image.Image.info` properties:
|
||||||
|
|
||||||
**compression**
|
**compression**
|
||||||
|
@ -74,7 +74,7 @@ are used or GIF89a is already in use.
|
||||||
Note that GIF files are always read as grayscale (``L``)
|
Note that GIF files are always read as grayscale (``L``)
|
||||||
or palette mode (``P``) images.
|
or palette mode (``P``) images.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
The :py:meth:`~PIL.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties:
|
:py:attr:`~PIL.Image.Image.info` properties:
|
||||||
|
|
||||||
**background**
|
**background**
|
||||||
|
@ -203,7 +203,7 @@ ICNS
|
||||||
Pillow reads and (macOS only) writes macOS ``.icns`` files. By default, the
|
Pillow reads and (macOS only) writes macOS ``.icns`` files. By default, the
|
||||||
largest available icon is read, though you can override this by setting the
|
largest available icon is read, though you can override this by setting the
|
||||||
:py:attr:`~PIL.Image.Image.size` property before calling
|
:py:attr:`~PIL.Image.Image.size` property before calling
|
||||||
:py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.Image.open` method
|
:py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.open` method
|
||||||
sets the following :py:attr:`~PIL.Image.Image.info` property:
|
sets the following :py:attr:`~PIL.Image.Image.info` property:
|
||||||
|
|
||||||
**sizes**
|
**sizes**
|
||||||
|
@ -257,7 +257,7 @@ Using the :py:meth:`~PIL.Image.Image.draft` method, you can speed things up by
|
||||||
converting ``RGB`` images to ``L``, and resize images to 1/2, 1/4 or 1/8 of
|
converting ``RGB`` images to ``L``, and resize images to 1/2, 1/4 or 1/8 of
|
||||||
their original size while loading them.
|
their original size while loading them.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method may set the following
|
The :py:meth:`~PIL.Image.open` method may set the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties if available:
|
:py:attr:`~PIL.Image.Image.info` properties if available:
|
||||||
|
|
||||||
**jfif**
|
**jfif**
|
||||||
|
@ -697,7 +697,7 @@ Pillow also reads SPIDER stack files containing sequences of SPIDER images. The
|
||||||
:py:meth:`~PIL.Image.Image.seek` and :py:meth:`~PIL.Image.Image.tell` methods are supported, and
|
:py:meth:`~PIL.Image.Image.seek` and :py:meth:`~PIL.Image.Image.tell` methods are supported, and
|
||||||
random access is allowed.
|
random access is allowed.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following attributes:
|
The :py:meth:`~PIL.Image.open` method sets the following attributes:
|
||||||
|
|
||||||
**format**
|
**format**
|
||||||
Set to ``SPIDER``
|
Set to ``SPIDER``
|
||||||
|
@ -750,7 +750,7 @@ uncompressed files.
|
||||||
support for reading Packbits, LZW and JPEG compressed TIFFs
|
support for reading Packbits, LZW and JPEG compressed TIFFs
|
||||||
without using libtiff.
|
without using libtiff.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
The :py:meth:`~PIL.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties:
|
:py:attr:`~PIL.Image.Image.info` properties:
|
||||||
|
|
||||||
**compression**
|
**compression**
|
||||||
|
@ -1021,7 +1021,7 @@ FLI, FLC
|
||||||
|
|
||||||
Pillow reads Autodesk FLI and FLC animations.
|
Pillow reads Autodesk FLI and FLC animations.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
The :py:meth:`~PIL.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties:
|
:py:attr:`~PIL.Image.Image.info` properties:
|
||||||
|
|
||||||
**duration**
|
**duration**
|
||||||
|
@ -1054,7 +1054,7 @@ GBR
|
||||||
|
|
||||||
The GBR decoder reads GIMP brush files, version 1 and 2.
|
The GBR decoder reads GIMP brush files, version 1 and 2.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
The :py:meth:`~PIL.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties:
|
:py:attr:`~PIL.Image.Image.info` properties:
|
||||||
|
|
||||||
**comment**
|
**comment**
|
||||||
|
@ -1069,7 +1069,7 @@ GD
|
||||||
Pillow reads uncompressed GD2 files. Note that you must use
|
Pillow reads uncompressed GD2 files. Note that you must use
|
||||||
:py:func:`PIL.GdImageFile.open` to read such a file.
|
:py:func:`PIL.GdImageFile.open` to read such a file.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
The :py:meth:`~PIL.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties:
|
:py:attr:`~PIL.Image.Image.info` properties:
|
||||||
|
|
||||||
**transparency**
|
**transparency**
|
||||||
|
@ -1185,7 +1185,7 @@ XPM
|
||||||
|
|
||||||
Pillow reads X pixmap files (mode ``P``) with 256 colors or less.
|
Pillow reads X pixmap files (mode ``P``) with 256 colors or less.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
The :py:meth:`~PIL.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties:
|
:py:attr:`~PIL.Image.Image.info` properties:
|
||||||
|
|
||||||
**transparency**
|
**transparency**
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
The :py:mod:`ExifTags` module exposes two dictionaries which
|
The :py:mod:`ExifTags` module exposes two dictionaries which
|
||||||
provide constants and clear-text names for various well-known EXIF tags.
|
provide constants and clear-text names for various well-known EXIF tags.
|
||||||
|
|
||||||
.. py:class:: PIL.ExifTags.TAGS
|
.. py:data:: TAGS
|
||||||
|
:type: dict
|
||||||
|
|
||||||
The TAG dictionary maps 16-bit integer EXIF tag enumerations to
|
The TAG dictionary maps 16-bit integer EXIF tag enumerations to
|
||||||
descriptive string names. For instance:
|
descriptive string names. For instance:
|
||||||
|
@ -16,7 +17,8 @@ provide constants and clear-text names for various well-known EXIF tags.
|
||||||
>>> TAGS[0x010e]
|
>>> TAGS[0x010e]
|
||||||
'ImageDescription'
|
'ImageDescription'
|
||||||
|
|
||||||
.. py:class:: PIL.ExifTags.GPSTAGS
|
.. py:data:: GPSTAGS
|
||||||
|
:type: dict
|
||||||
|
|
||||||
The GPSTAGS dictionary maps 8-bit integer EXIF gps enumerations to
|
The GPSTAGS dictionary maps 8-bit integer EXIF gps enumerations to
|
||||||
descriptive string names. For instance:
|
descriptive string names. For instance:
|
||||||
|
|
|
@ -76,9 +76,16 @@ Constructing images
|
||||||
.. autofunction:: new
|
.. autofunction:: new
|
||||||
.. autofunction:: fromarray
|
.. autofunction:: fromarray
|
||||||
.. autofunction:: frombytes
|
.. autofunction:: frombytes
|
||||||
.. autofunction:: fromstring
|
|
||||||
.. autofunction:: frombuffer
|
.. autofunction:: frombuffer
|
||||||
|
|
||||||
|
Generating images
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autofunction:: effect_mandelbrot
|
||||||
|
.. autofunction:: effect_noise
|
||||||
|
.. autofunction:: linear_gradient
|
||||||
|
.. autofunction:: radial_gradient
|
||||||
|
|
||||||
Registering plugins
|
Registering plugins
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -88,12 +95,14 @@ Registering plugins
|
||||||
ignore them.
|
ignore them.
|
||||||
|
|
||||||
.. autofunction:: register_open
|
.. autofunction:: register_open
|
||||||
.. autofunction:: register_decoder
|
|
||||||
.. autofunction:: register_mime
|
.. autofunction:: register_mime
|
||||||
.. autofunction:: register_save
|
.. autofunction:: register_save
|
||||||
.. autofunction:: register_encoder
|
.. autofunction:: register_save_all
|
||||||
.. autofunction:: register_extension
|
.. autofunction:: register_extension
|
||||||
|
.. autofunction:: register_extensions
|
||||||
|
.. autofunction:: registered_extensions
|
||||||
|
.. autofunction:: register_decoder
|
||||||
|
.. autofunction:: register_encoder
|
||||||
|
|
||||||
The Image Class
|
The Image Class
|
||||||
---------------
|
---------------
|
||||||
|
@ -140,6 +149,8 @@ This crops the input image with the provided coordinates:
|
||||||
|
|
||||||
|
|
||||||
.. automethod:: PIL.Image.Image.draft
|
.. automethod:: PIL.Image.Image.draft
|
||||||
|
.. automethod:: PIL.Image.Image.effect_spread
|
||||||
|
.. automethod:: PIL.Image.Image.entropy
|
||||||
.. automethod:: PIL.Image.Image.filter
|
.. automethod:: PIL.Image.Image.filter
|
||||||
|
|
||||||
This blurs the input image using a filter from the ``ImageFilter`` module:
|
This blurs the input image using a filter from the ``ImageFilter`` module:
|
||||||
|
@ -176,12 +187,14 @@ This helps to get the bounding box coordinates of the input image:
|
||||||
print(im.getbbox())
|
print(im.getbbox())
|
||||||
# Returns four coordinates in the format (left, upper, right, lower)
|
# Returns four coordinates in the format (left, upper, right, lower)
|
||||||
|
|
||||||
|
.. automethod:: PIL.Image.Image.getchannel
|
||||||
.. automethod:: PIL.Image.Image.getcolors
|
.. automethod:: PIL.Image.Image.getcolors
|
||||||
.. automethod:: PIL.Image.Image.getdata
|
.. automethod:: PIL.Image.Image.getdata
|
||||||
.. automethod:: PIL.Image.Image.getextrema
|
|
||||||
.. automethod:: PIL.Image.Image.getexif
|
.. automethod:: PIL.Image.Image.getexif
|
||||||
|
.. automethod:: PIL.Image.Image.getextrema
|
||||||
.. automethod:: PIL.Image.Image.getpalette
|
.. automethod:: PIL.Image.Image.getpalette
|
||||||
.. automethod:: PIL.Image.Image.getpixel
|
.. automethod:: PIL.Image.Image.getpixel
|
||||||
|
.. automethod:: PIL.Image.Image.getprojection
|
||||||
.. automethod:: PIL.Image.Image.histogram
|
.. automethod:: PIL.Image.Image.histogram
|
||||||
.. automethod:: PIL.Image.Image.offset
|
.. automethod:: PIL.Image.Image.offset
|
||||||
.. automethod:: PIL.Image.Image.paste
|
.. automethod:: PIL.Image.Image.paste
|
||||||
|
@ -191,6 +204,8 @@ This helps to get the bounding box coordinates of the input image:
|
||||||
.. automethod:: PIL.Image.Image.putpalette
|
.. automethod:: PIL.Image.Image.putpalette
|
||||||
.. automethod:: PIL.Image.Image.putpixel
|
.. automethod:: PIL.Image.Image.putpixel
|
||||||
.. automethod:: PIL.Image.Image.quantize
|
.. automethod:: PIL.Image.Image.quantize
|
||||||
|
.. automethod:: PIL.Image.Image.reduce
|
||||||
|
.. automethod:: PIL.Image.Image.remap_palette
|
||||||
.. automethod:: PIL.Image.Image.resize
|
.. automethod:: PIL.Image.Image.resize
|
||||||
|
|
||||||
This resizes the given image from ``(width, height)`` to ``(width/2, height/2)``:
|
This resizes the given image from ``(width, height)`` to ``(width/2, height/2)``:
|
||||||
|
@ -205,7 +220,6 @@ This resizes the given image from ``(width, height)`` to ``(width/2, height/2)``
|
||||||
(width, height) = (im.width // 2, im.height // 2)
|
(width, height) = (im.width // 2, im.height // 2)
|
||||||
im_resized = im.resize((width, height))
|
im_resized = im.resize((width, height))
|
||||||
|
|
||||||
.. automethod:: PIL.Image.Image.remap_palette
|
|
||||||
.. automethod:: PIL.Image.Image.rotate
|
.. automethod:: PIL.Image.Image.rotate
|
||||||
|
|
||||||
This rotates the input image by ``theta`` degrees counter clockwise:
|
This rotates the input image by ``theta`` degrees counter clockwise:
|
||||||
|
@ -225,7 +239,6 @@ This rotates the input image by ``theta`` degrees counter clockwise:
|
||||||
.. automethod:: PIL.Image.Image.seek
|
.. automethod:: PIL.Image.Image.seek
|
||||||
.. automethod:: PIL.Image.Image.show
|
.. automethod:: PIL.Image.Image.show
|
||||||
.. automethod:: PIL.Image.Image.split
|
.. automethod:: PIL.Image.Image.split
|
||||||
.. automethod:: PIL.Image.Image.getchannel
|
|
||||||
.. automethod:: PIL.Image.Image.tell
|
.. automethod:: PIL.Image.Image.tell
|
||||||
.. automethod:: PIL.Image.Image.thumbnail
|
.. automethod:: PIL.Image.Image.thumbnail
|
||||||
.. automethod:: PIL.Image.Image.tobitmap
|
.. automethod:: PIL.Image.Image.tobitmap
|
||||||
|
|
|
@ -124,7 +124,7 @@ Example: Draw Multiline Text
|
||||||
Functions
|
Functions
|
||||||
---------
|
---------
|
||||||
|
|
||||||
.. py:class:: PIL.ImageDraw.Draw(im, mode=None)
|
.. py:method:: Draw(im, mode=None)
|
||||||
|
|
||||||
Creates an object that can be used to draw in the given image.
|
Creates an object that can be used to draw in the given image.
|
||||||
|
|
||||||
|
@ -140,13 +140,13 @@ Functions
|
||||||
Methods
|
Methods
|
||||||
-------
|
-------
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.getfont()
|
.. py:method:: ImageDraw.getfont()
|
||||||
|
|
||||||
Get the current default font.
|
Get the current default font.
|
||||||
|
|
||||||
:returns: An image font.
|
:returns: An image font.
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.arc(xy, start, end, fill=None, width=0)
|
.. py:method:: ImageDraw.arc(xy, start, end, fill=None, width=0)
|
||||||
|
|
||||||
Draws an arc (a portion of a circle outline) between the start and end
|
Draws an arc (a portion of a circle outline) between the start and end
|
||||||
angles, inside the given bounding box.
|
angles, inside the given bounding box.
|
||||||
|
@ -162,7 +162,7 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 5.3.0
|
.. versionadded:: 5.3.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.bitmap(xy, bitmap, fill=None)
|
.. py:method:: ImageDraw.bitmap(xy, bitmap, fill=None)
|
||||||
|
|
||||||
Draws a bitmap (mask) at the given position, using the current fill color
|
Draws a bitmap (mask) at the given position, using the current fill color
|
||||||
for the non-zero portions. The bitmap should be a valid transparency mask
|
for the non-zero portions. The bitmap should be a valid transparency mask
|
||||||
|
@ -173,7 +173,7 @@ Methods
|
||||||
To paste pixel data into an image, use the
|
To paste pixel data into an image, use the
|
||||||
:py:meth:`~PIL.Image.Image.paste` method on the image itself.
|
:py:meth:`~PIL.Image.Image.paste` method on the image itself.
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.chord(xy, start, end, fill=None, outline=None, width=1)
|
.. py:method:: ImageDraw.chord(xy, start, end, fill=None, outline=None, width=1)
|
||||||
|
|
||||||
Same as :py:meth:`~PIL.ImageDraw.ImageDraw.arc`, but connects the end points
|
Same as :py:meth:`~PIL.ImageDraw.ImageDraw.arc`, but connects the end points
|
||||||
with a straight line.
|
with a straight line.
|
||||||
|
@ -187,7 +187,7 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 5.3.0
|
.. versionadded:: 5.3.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.ellipse(xy, fill=None, outline=None, width=1)
|
.. py:method:: ImageDraw.ellipse(xy, fill=None, outline=None, width=1)
|
||||||
|
|
||||||
Draws an ellipse inside the given bounding box.
|
Draws an ellipse inside the given bounding box.
|
||||||
|
|
||||||
|
@ -200,9 +200,9 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 5.3.0
|
.. versionadded:: 5.3.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.line(xy, fill=None, width=0, joint=None)
|
.. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None)
|
||||||
|
|
||||||
Draws a line between the coordinates in the **xy** list.
|
Draws a line between the coordinates in the ``xy`` list.
|
||||||
|
|
||||||
:param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
|
:param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
|
||||||
numeric values like ``[x, y, x, y, ...]``.
|
numeric values like ``[x, y, x, y, ...]``.
|
||||||
|
@ -216,7 +216,7 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 5.3.0
|
.. versionadded:: 5.3.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.pieslice(xy, start, end, fill=None, outline=None, width=1)
|
.. py:method:: ImageDraw.pieslice(xy, start, end, fill=None, outline=None, width=1)
|
||||||
|
|
||||||
Same as arc, but also draws straight lines between the end points and the
|
Same as arc, but also draws straight lines between the end points and the
|
||||||
center of the bounding box.
|
center of the bounding box.
|
||||||
|
@ -233,7 +233,7 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 5.3.0
|
.. versionadded:: 5.3.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.point(xy, fill=None)
|
.. py:method:: ImageDraw.point(xy, fill=None)
|
||||||
|
|
||||||
Draws points (individual pixels) at the given coordinates.
|
Draws points (individual pixels) at the given coordinates.
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ Methods
|
||||||
numeric values like ``[x, y, x, y, ...]``.
|
numeric values like ``[x, y, x, y, ...]``.
|
||||||
:param fill: Color to use for the point.
|
:param fill: Color to use for the point.
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.polygon(xy, fill=None, outline=None)
|
.. py:method:: ImageDraw.polygon(xy, fill=None, outline=None)
|
||||||
|
|
||||||
Draws a polygon.
|
Draws a polygon.
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ Methods
|
||||||
:param outline: Color to use for the outline.
|
:param outline: Color to use for the outline.
|
||||||
:param fill: Color to use for the fill.
|
:param fill: Color to use for the fill.
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.rectangle(xy, fill=None, outline=None, width=1)
|
.. py:method:: ImageDraw.rectangle(xy, fill=None, outline=None, width=1)
|
||||||
|
|
||||||
Draws a rectangle.
|
Draws a rectangle.
|
||||||
|
|
||||||
|
@ -267,13 +267,13 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 5.3.0
|
.. versionadded:: 5.3.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.shape(shape, fill=None, outline=None)
|
.. py:method:: ImageDraw.shape(shape, fill=None, outline=None)
|
||||||
|
|
||||||
.. warning:: This method is experimental.
|
.. warning:: This method is experimental.
|
||||||
|
|
||||||
Draw a shape.
|
Draw a shape.
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None)
|
.. py:method:: ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None)
|
||||||
|
|
||||||
Draws the string at the given position.
|
Draws the string at the given position.
|
||||||
|
|
||||||
|
@ -325,7 +325,7 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 6.2.0
|
.. versionadded:: 6.2.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None)
|
.. py:method:: ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None)
|
||||||
|
|
||||||
Draws the string at the given position.
|
Draws the string at the given position.
|
||||||
|
|
||||||
|
@ -362,7 +362,7 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 6.0.0
|
.. versionadded:: 6.0.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
.. py:method:: ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
||||||
|
|
||||||
Return the size of the given string, in pixels.
|
Return the size of the given string, in pixels.
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 6.2.0
|
.. versionadded:: 6.2.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
.. py:method:: ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
||||||
|
|
||||||
Return the size of the given string, in pixels.
|
Return the size of the given string, in pixels.
|
||||||
|
|
||||||
|
@ -439,7 +439,7 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 6.2.0
|
.. versionadded:: 6.2.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.getdraw(im=None, hints=None)
|
.. py:method:: getdraw(im=None, hints=None)
|
||||||
|
|
||||||
.. warning:: This method is experimental.
|
.. warning:: This method is experimental.
|
||||||
|
|
||||||
|
@ -450,7 +450,7 @@ Methods
|
||||||
:param hints: An optional list of hints.
|
:param hints: An optional list of hints.
|
||||||
:returns: A (drawing context, drawing resource factory) tuple.
|
:returns: A (drawing context, drawing resource factory) tuple.
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.floodfill(image, xy, value, border=None, thresh=0)
|
.. py:method:: floodfill(image, xy, value, border=None, thresh=0)
|
||||||
|
|
||||||
.. warning:: This method is experimental.
|
.. warning:: This method is experimental.
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ Classes
|
||||||
All enhancement classes implement a common interface, containing a single
|
All enhancement classes implement a common interface, containing a single
|
||||||
method:
|
method:
|
||||||
|
|
||||||
.. py:class:: PIL.ImageEnhance._Enhance
|
.. py:class:: _Enhance
|
||||||
|
|
||||||
.. py:method:: enhance(factor)
|
.. py:method:: enhance(factor)
|
||||||
|
|
||||||
Returns an enhanced image.
|
Returns an enhanced image.
|
||||||
|
@ -40,7 +41,7 @@ method:
|
||||||
etc), and higher values more. There are no restrictions
|
etc), and higher values more. There are no restrictions
|
||||||
on this value.
|
on this value.
|
||||||
|
|
||||||
.. py:class:: PIL.ImageEnhance.Color(image)
|
.. py:class:: Color(image)
|
||||||
|
|
||||||
Adjust image color balance.
|
Adjust image color balance.
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ method:
|
||||||
factor of 0.0 gives a black and white image. A factor of 1.0 gives
|
factor of 0.0 gives a black and white image. A factor of 1.0 gives
|
||||||
the original image.
|
the original image.
|
||||||
|
|
||||||
.. py:class:: PIL.ImageEnhance.Contrast(image)
|
.. py:class:: Contrast(image)
|
||||||
|
|
||||||
Adjust image contrast.
|
Adjust image contrast.
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ method:
|
||||||
to the contrast control on a TV set. An enhancement factor of 0.0
|
to the contrast control on a TV set. An enhancement factor of 0.0
|
||||||
gives a solid grey image. A factor of 1.0 gives the original image.
|
gives a solid grey image. A factor of 1.0 gives the original image.
|
||||||
|
|
||||||
.. py:class:: PIL.ImageEnhance.Brightness(image)
|
.. py:class:: Brightness(image)
|
||||||
|
|
||||||
Adjust image brightness.
|
Adjust image brightness.
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ method:
|
||||||
enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the
|
enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the
|
||||||
original image.
|
original image.
|
||||||
|
|
||||||
.. py:class:: PIL.ImageEnhance.Sharpness(image)
|
.. py:class:: Sharpness(image)
|
||||||
|
|
||||||
Adjust image sharpness.
|
Adjust image sharpness.
|
||||||
|
|
||||||
|
|
27
docs/reference/ImageShow.rst
Normal file
27
docs/reference/ImageShow.rst
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
.. py:module:: PIL.ImageShow
|
||||||
|
.. py:currentmodule:: PIL.ImageShow
|
||||||
|
|
||||||
|
:py:mod:`ImageShow` Module
|
||||||
|
==========================
|
||||||
|
|
||||||
|
The :py:mod:`ImageShow` Module is used to display images.
|
||||||
|
All default viewers convert the image to be shown to PNG format.
|
||||||
|
|
||||||
|
.. autofunction:: PIL.ImageShow.show
|
||||||
|
|
||||||
|
.. autoclass:: WindowsViewer
|
||||||
|
.. autoclass:: MacViewer
|
||||||
|
|
||||||
|
.. class:: UnixViewer
|
||||||
|
|
||||||
|
The following viewers may be registered on Unix-based systems, if the given command is found:
|
||||||
|
|
||||||
|
.. autoclass:: PIL.ImageShow.DisplayViewer
|
||||||
|
.. autoclass:: PIL.ImageShow.EogViewer
|
||||||
|
.. autoclass:: PIL.ImageShow.XVViewer
|
||||||
|
|
||||||
|
.. autofunction:: PIL.ImageShow.register
|
||||||
|
.. autoclass:: PIL.ImageShow.Viewer
|
||||||
|
:member-order: bysource
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
|
@ -7,7 +7,7 @@
|
||||||
The :py:mod:`ImageStat` module calculates global statistics for an image, or
|
The :py:mod:`ImageStat` module calculates global statistics for an image, or
|
||||||
for a region of an image.
|
for a region of an image.
|
||||||
|
|
||||||
.. py:class:: PIL.ImageStat.Stat(image_or_list, mask=None)
|
.. py:class:: Stat(image_or_list, mask=None)
|
||||||
|
|
||||||
Calculate statistics for the given image. If a mask is included,
|
Calculate statistics for the given image. If a mask is included,
|
||||||
only the regions covered by that mask are included in the
|
only the regions covered by that mask are included in the
|
||||||
|
@ -22,13 +22,13 @@ for a region of an image.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
This relies on the :py:meth:`~PIL.Image.histogram` method, and
|
This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and
|
||||||
simply returns the low and high bins used. This is correct for
|
simply returns the low and high bins used. This is correct for
|
||||||
images with 8 bits per channel, but fails for other modes such as
|
images with 8 bits per channel, but fails for other modes such as
|
||||||
``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.getextrema` to
|
``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to
|
||||||
return per-band extrema for the image. This is more correct and
|
return per-band extrema for the image. This is more correct and
|
||||||
efficient because, for non-8-bit modes, the histogram method uses
|
efficient because, for non-8-bit modes, the histogram method uses
|
||||||
:py:meth:`~PIL.Image.getextrema` to determine the bins used.
|
:py:meth:`~PIL.Image.Image.getextrema` to determine the bins used.
|
||||||
|
|
||||||
.. py:attribute:: count
|
.. py:attribute:: count
|
||||||
|
|
||||||
|
|
11
docs/reference/JpegPresets.rst
Normal file
11
docs/reference/JpegPresets.rst
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.. py:currentmodule:: PIL.JpegPresets
|
||||||
|
|
||||||
|
:py:mod:`JpegPresets` Module
|
||||||
|
============================
|
||||||
|
|
||||||
|
.. automodule:: PIL.JpegPresets
|
||||||
|
|
||||||
|
.. data:: presets
|
||||||
|
:type: dict
|
||||||
|
|
||||||
|
A dictionary of all supported presets.
|
|
@ -10,8 +10,8 @@ metadata tag numbers, names, and type information.
|
||||||
.. method:: lookup(tag)
|
.. method:: lookup(tag)
|
||||||
|
|
||||||
:param tag: Integer tag number
|
:param tag: Integer tag number
|
||||||
:returns: Taginfo namedtuple, From the ``TAGS_V2`` info if possible,
|
:returns: Taginfo namedtuple, From the :py:data:`~PIL.TiffTags.TAGS_V2` info if possible,
|
||||||
otherwise just populating the value and name from ``TAGS``.
|
otherwise just populating the value and name from :py:data:`~PIL.TiffTags.TAGS`.
|
||||||
If the tag is not recognized, "unknown" is returned for the name
|
If the tag is not recognized, "unknown" is returned for the name
|
||||||
|
|
||||||
.. versionadded:: 3.1.0
|
.. versionadded:: 3.1.0
|
||||||
|
@ -22,7 +22,7 @@ metadata tag numbers, names, and type information.
|
||||||
|
|
||||||
:param value: Integer Tag Number
|
:param value: Integer Tag Number
|
||||||
:param name: Tag Name
|
:param name: Tag Name
|
||||||
:param type: Integer type from :py:attr:`PIL.TiffTags.TYPES`
|
:param type: Integer type from :py:data:`PIL.TiffTags.TYPES`
|
||||||
:param length: Array length: 0 == variable, 1 == single value, n = fixed
|
:param length: Array length: 0 == variable, 1 == single value, n = fixed
|
||||||
:param enum: Dict of name:integer value options for an enumeration
|
:param enum: Dict of name:integer value options for an enumeration
|
||||||
|
|
||||||
|
@ -33,15 +33,17 @@ metadata tag numbers, names, and type information.
|
||||||
|
|
||||||
.. versionadded:: 3.0.0
|
.. versionadded:: 3.0.0
|
||||||
|
|
||||||
.. py:attribute:: PIL.TiffTags.TAGS_V2
|
.. py:data:: PIL.TiffTags.TAGS_V2
|
||||||
|
:type: dict
|
||||||
|
|
||||||
The ``TAGS_V2`` dictionary maps 16-bit integer tag numbers to
|
The ``TAGS_V2`` dictionary maps 16-bit integer tag numbers to
|
||||||
:py:class:`PIL.TagTypes.TagInfo` tuples for metadata fields defined in the TIFF
|
:py:class:`PIL.TiffTags.TagInfo` tuples for metadata fields defined in the TIFF
|
||||||
spec.
|
spec.
|
||||||
|
|
||||||
.. versionadded:: 3.0.0
|
.. versionadded:: 3.0.0
|
||||||
|
|
||||||
.. py:attribute:: PIL.TiffTags.TAGS
|
.. py:data:: PIL.TiffTags.TAGS
|
||||||
|
:type: dict
|
||||||
|
|
||||||
The ``TAGS`` dictionary maps 16-bit integer TIFF tag number to
|
The ``TAGS`` dictionary maps 16-bit integer TIFF tag number to
|
||||||
descriptive string names. For instance:
|
descriptive string names. For instance:
|
||||||
|
@ -50,10 +52,11 @@ metadata tag numbers, names, and type information.
|
||||||
>>> TAGS[0x010e]
|
>>> TAGS[0x010e]
|
||||||
'ImageDescription'
|
'ImageDescription'
|
||||||
|
|
||||||
This dictionary contains a superset of the tags in TAGS_V2, common
|
This dictionary contains a superset of the tags in :py:data:`~PIL.TiffTags.TAGS_V2`, common
|
||||||
EXIF tags, and other well known metadata tags.
|
EXIF tags, and other well known metadata tags.
|
||||||
|
|
||||||
.. py:attribute:: PIL.TiffTags.TYPES
|
.. py:data:: PIL.TiffTags.TYPES
|
||||||
|
:type: dict
|
||||||
|
|
||||||
The ``TYPES`` dictionary maps the TIFF type short integer to a
|
The ``TYPES`` dictionary maps the TIFF type short integer to a
|
||||||
human readable type name.
|
human readable type name.
|
||||||
|
|
|
@ -7,8 +7,8 @@ Reference
|
||||||
|
|
||||||
Image
|
Image
|
||||||
ImageChops
|
ImageChops
|
||||||
ImageColor
|
|
||||||
ImageCms
|
ImageCms
|
||||||
|
ImageColor
|
||||||
ImageDraw
|
ImageDraw
|
||||||
ImageEnhance
|
ImageEnhance
|
||||||
ImageFile
|
ImageFile
|
||||||
|
@ -22,11 +22,13 @@ Reference
|
||||||
ImagePath
|
ImagePath
|
||||||
ImageQt
|
ImageQt
|
||||||
ImageSequence
|
ImageSequence
|
||||||
|
ImageShow
|
||||||
ImageStat
|
ImageStat
|
||||||
ImageTk
|
ImageTk
|
||||||
ImageWin
|
ImageWin
|
||||||
ExifTags
|
ExifTags
|
||||||
TiffTags
|
TiffTags
|
||||||
|
JpegPresets
|
||||||
PSDraw
|
PSDraw
|
||||||
PixelAccess
|
PixelAccess
|
||||||
PyAccess
|
PyAccess
|
||||||
|
|
|
@ -7,4 +7,4 @@ Internal Reference Docs
|
||||||
open_files
|
open_files
|
||||||
limits
|
limits
|
||||||
block_allocator
|
block_allocator
|
||||||
|
internal_modules
|
||||||
|
|
38
docs/reference/internal_modules.rst
Normal file
38
docs/reference/internal_modules.rst
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
Internal Modules
|
||||||
|
================
|
||||||
|
|
||||||
|
:mod:`_binary` Module
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. automodule:: PIL._binary
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
:mod:`_tkinter_finder` Module
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
.. automodule:: PIL._tkinter_finder
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
:mod:`_util` Module
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. automodule:: PIL._util
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
:mod:`_version` Module
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. module:: PIL._version
|
||||||
|
|
||||||
|
.. data:: __version__
|
||||||
|
:annotation:
|
||||||
|
:type: str
|
||||||
|
|
||||||
|
This is the master version number for Pillow,
|
||||||
|
all other uses reference this module.
|
|
@ -37,6 +37,19 @@ are now read as just a single bytestring.
|
||||||
Deprecations
|
Deprecations
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Image.show command parameter
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``command`` parameter was deprecated and will be removed in a future release.
|
||||||
|
Use a subclass of :py:class:`PIL.ImageShow.Viewer` instead.
|
||||||
|
|
||||||
|
Image._showxv
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``Image._showxv`` has been deprecated. Use :py:meth:`~PIL.Image.Image.show`
|
||||||
|
instead. If custom behaviour is required, use :py:meth:`~PIL.ImageShow.register` to add
|
||||||
|
a custom :py:class:`~PIL.ImageShow.Viewer` class.
|
||||||
|
|
||||||
ImageFile.raise_ioerror
|
ImageFile.raise_ioerror
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,5 @@ pyflakes
|
||||||
pyroma
|
pyroma
|
||||||
pytest
|
pytest
|
||||||
pytest-cov
|
pytest-cov
|
||||||
|
sphinx>=2.4
|
||||||
sphinx-rtd-theme
|
sphinx-rtd-theme
|
||||||
|
|
|
@ -9,5 +9,5 @@ line_length = 88
|
||||||
multi_line_output = 3
|
multi_line_output = 3
|
||||||
|
|
||||||
[tool:pytest]
|
[tool:pytest]
|
||||||
addopts = -rs --color=yes
|
addopts = -ra --color=yes
|
||||||
testpaths = Tests
|
testpaths = Tests
|
||||||
|
|
|
@ -17,13 +17,13 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Parse X Bitmap Distribution Format (BDF)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
from . import FontFile, Image
|
from . import FontFile, Image
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
# parse X Bitmap Distribution Format (BDF)
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
|
|
||||||
bdf_slant = {
|
bdf_slant = {
|
||||||
"R": "Roman",
|
"R": "Roman",
|
||||||
"I": "Italic",
|
"I": "Italic",
|
||||||
|
@ -78,11 +78,9 @@ def bdf_char(f):
|
||||||
return id, int(props["ENCODING"]), bbox, im
|
return id, int(props["ENCODING"]), bbox, im
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# Font file plugin for the X11 BDF format.
|
|
||||||
|
|
||||||
|
|
||||||
class BdfFontFile(FontFile.FontFile):
|
class BdfFontFile(FontFile.FontFile):
|
||||||
|
"""Font file plugin for the X11 BDF format."""
|
||||||
|
|
||||||
def __init__(self, fp):
|
def __init__(self, fp):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
|
|
@ -282,8 +282,8 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
|
||||||
self.magic = self.fd.read(4)
|
self.magic = self.fd.read(4)
|
||||||
self._read_blp_header()
|
self._read_blp_header()
|
||||||
self._load()
|
self._load()
|
||||||
except struct.error:
|
except struct.error as e:
|
||||||
raise OSError("Truncated Blp file")
|
raise OSError("Truncated Blp file") from e
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
|
||||||
def _read_palette(self):
|
def _read_palette(self):
|
||||||
|
|
|
@ -263,7 +263,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
||||||
# read 14 bytes: magic number, filesize, reserved, header final offset
|
# read 14 bytes: magic number, filesize, reserved, header final offset
|
||||||
head_data = self.fp.read(14)
|
head_data = self.fp.read(14)
|
||||||
# choke if the file does not have the required magic bytes
|
# choke if the file does not have the required magic bytes
|
||||||
if head_data[0:2] != b"BM":
|
if not _accept(head_data):
|
||||||
raise SyntaxError("Not a BMP file")
|
raise SyntaxError("Not a BMP file")
|
||||||
# read the start position of the BMP image data (u32)
|
# read the start position of the BMP image data (u32)
|
||||||
offset = i32(head_data[10:14])
|
offset = i32(head_data[10:14])
|
||||||
|
@ -304,8 +304,8 @@ def _dib_save(im, fp, filename):
|
||||||
def _save(im, fp, filename, bitmap_header=True):
|
def _save(im, fp, filename, bitmap_header=True):
|
||||||
try:
|
try:
|
||||||
rawmode, bits, colors = SAVE[im.mode]
|
rawmode, bits, colors = SAVE[im.mode]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise OSError("cannot write mode %s as BMP" % im.mode)
|
raise OSError("cannot write mode %s as BMP" % im.mode) from e
|
||||||
|
|
||||||
info = im.encoderinfo
|
info = im.encoderinfo
|
||||||
|
|
||||||
|
|
|
@ -14,14 +14,16 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
##
|
|
||||||
# A file object that provides read access to a part of an existing
|
|
||||||
# file (for example a TAR file).
|
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
|
||||||
|
|
||||||
class ContainerIO:
|
class ContainerIO:
|
||||||
|
"""
|
||||||
|
A file object that provides read access to a part of an existing
|
||||||
|
file (for example a TAR file).
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, file, offset, length):
|
def __init__(self, file, offset, length):
|
||||||
"""
|
"""
|
||||||
Create file object.
|
Create file object.
|
||||||
|
|
|
@ -46,7 +46,7 @@ class DcxImageFile(PcxImageFile):
|
||||||
|
|
||||||
# Header
|
# Header
|
||||||
s = self.fp.read(4)
|
s = self.fp.read(4)
|
||||||
if i32(s) != MAGIC:
|
if not _accept(s):
|
||||||
raise SyntaxError("not a DCX file")
|
raise SyntaxError("not a DCX file")
|
||||||
|
|
||||||
# Component directory
|
# Component directory
|
||||||
|
|
|
@ -231,8 +231,8 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
m = split.match(s)
|
m = split.match(s)
|
||||||
except re.error:
|
except re.error as e:
|
||||||
raise SyntaxError("not an EPS file")
|
raise SyntaxError("not an EPS file") from e
|
||||||
|
|
||||||
if m:
|
if m:
|
||||||
k, v = m.group(1, 2)
|
k, v = m.group(1, 2)
|
||||||
|
|
|
@ -9,13 +9,11 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
##
|
"""
|
||||||
# This module provides constants and clear-text names for various
|
This module provides constants and clear-text names for various
|
||||||
# well-known EXIF tags.
|
well-known EXIF tags.
|
||||||
##
|
"""
|
||||||
|
|
||||||
##
|
|
||||||
# Maps EXIF tags to tag names.
|
|
||||||
|
|
||||||
TAGS = {
|
TAGS = {
|
||||||
# possibly incomplete
|
# possibly incomplete
|
||||||
|
@ -280,9 +278,8 @@ TAGS = {
|
||||||
0xC74E: "OpcodeList3",
|
0xC74E: "OpcodeList3",
|
||||||
0xC761: "NoiseProfile",
|
0xC761: "NoiseProfile",
|
||||||
}
|
}
|
||||||
|
"""Maps EXIF tags to tag names."""
|
||||||
|
|
||||||
##
|
|
||||||
# Maps EXIF GPS tags to tag names.
|
|
||||||
|
|
||||||
GPSTAGS = {
|
GPSTAGS = {
|
||||||
0: "GPSVersionID",
|
0: "GPSVersionID",
|
||||||
|
@ -318,3 +315,4 @@ GPSTAGS = {
|
||||||
30: "GPSDifferential",
|
30: "GPSDifferential",
|
||||||
31: "GPSHPositioningError",
|
31: "GPSHPositioningError",
|
||||||
}
|
}
|
||||||
|
"""Maps EXIF GPS tags to tag names."""
|
||||||
|
|
|
@ -42,9 +42,8 @@ class FliImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# HEAD
|
# HEAD
|
||||||
s = self.fp.read(128)
|
s = self.fp.read(128)
|
||||||
magic = i16(s[4:6])
|
|
||||||
if not (
|
if not (
|
||||||
magic in [0xAF11, 0xAF12]
|
_accept(s)
|
||||||
and i16(s[14:16]) in [0, 3] # flags
|
and i16(s[14:16]) in [0, 3] # flags
|
||||||
and s[20:22] == b"\x00\x00" # reserved
|
and s[20:22] == b"\x00\x00" # reserved
|
||||||
):
|
):
|
||||||
|
@ -60,6 +59,7 @@ class FliImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# animation speed
|
# animation speed
|
||||||
duration = i32(s[16:20])
|
duration = i32(s[16:20])
|
||||||
|
magic = i16(s[4:6])
|
||||||
if magic == 0xAF11:
|
if magic == 0xAF11:
|
||||||
duration = (duration * 1000) // 70
|
duration = (duration * 1000) // 70
|
||||||
self.info["duration"] = duration
|
self.info["duration"] = duration
|
||||||
|
|
|
@ -23,18 +23,15 @@ WIDTH = 800
|
||||||
|
|
||||||
|
|
||||||
def puti16(fp, values):
|
def puti16(fp, values):
|
||||||
# write network order (big-endian) 16-bit sequence
|
"""Write network order (big-endian) 16-bit sequence"""
|
||||||
for v in values:
|
for v in values:
|
||||||
if v < 0:
|
if v < 0:
|
||||||
v += 65536
|
v += 65536
|
||||||
fp.write(_binary.o16be(v))
|
fp.write(_binary.o16be(v))
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# Base class for raster font file handlers.
|
|
||||||
|
|
||||||
|
|
||||||
class FontFile:
|
class FontFile:
|
||||||
|
"""Base class for raster font file handlers."""
|
||||||
|
|
||||||
bitmap = None
|
bitmap = None
|
||||||
|
|
||||||
|
|
|
@ -59,8 +59,8 @@ class FpxImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.ole = olefile.OleFileIO(self.fp)
|
self.ole = olefile.OleFileIO(self.fp)
|
||||||
except OSError:
|
except OSError as e:
|
||||||
raise SyntaxError("not an FPX file; invalid OLE file")
|
raise SyntaxError("not an FPX file; invalid OLE file") from e
|
||||||
|
|
||||||
if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B":
|
if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B":
|
||||||
raise SyntaxError("not an FPX file; bad root CLSID")
|
raise SyntaxError("not an FPX file; bad root CLSID")
|
||||||
|
|
|
@ -14,26 +14,30 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
# NOTE: This format cannot be automatically recognized, so the
|
"""
|
||||||
# class is not registered for use with Image.open(). To open a
|
.. note::
|
||||||
# gd file, use the GdImageFile.open() function instead.
|
This format cannot be automatically recognized, so the
|
||||||
|
class is not registered for use with :py:func:`PIL.Image.open()`. To open a
|
||||||
|
gd file, use the :py:func:`PIL.GdImageFile.open()` function instead.
|
||||||
|
|
||||||
# THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This
|
.. warning::
|
||||||
# implementation is provided for convenience and demonstrational
|
THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This
|
||||||
# purposes only.
|
implementation is provided for convenience and demonstrational
|
||||||
|
purposes only.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
from . import ImageFile, ImagePalette, UnidentifiedImageError
|
from . import ImageFile, ImagePalette, UnidentifiedImageError
|
||||||
from ._binary import i8, i16be as i16, i32be as i32
|
from ._binary import i8, i16be as i16, i32be as i32
|
||||||
|
|
||||||
##
|
|
||||||
# Image plugin for the GD uncompressed format. Note that this format
|
|
||||||
# is not supported by the standard <b>Image.open</b> function. To use
|
|
||||||
# this plugin, you have to import the <b>GdImageFile</b> module and
|
|
||||||
# use the <b>GdImageFile.open</b> function.
|
|
||||||
|
|
||||||
|
|
||||||
class GdImageFile(ImageFile.ImageFile):
|
class GdImageFile(ImageFile.ImageFile):
|
||||||
|
"""
|
||||||
|
Image plugin for the GD uncompressed format. Note that this format
|
||||||
|
is not supported by the standard :py:func:`PIL.Image.open()` function. To use
|
||||||
|
this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and
|
||||||
|
use the :py:func:`PIL.GdImageFile.open()` function.
|
||||||
|
"""
|
||||||
|
|
||||||
format = "GD"
|
format = "GD"
|
||||||
format_description = "GD uncompressed images"
|
format_description = "GD uncompressed images"
|
||||||
|
@ -81,5 +85,5 @@ def open(fp, mode="r"):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return GdImageFile(fp)
|
return GdImageFile(fp)
|
||||||
except SyntaxError:
|
except SyntaxError as e:
|
||||||
raise UnidentifiedImageError("cannot identify this image file")
|
raise UnidentifiedImageError("cannot identify this image file") from e
|
||||||
|
|
|
@ -63,7 +63,7 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# Screen
|
# Screen
|
||||||
s = self.fp.read(13)
|
s = self.fp.read(13)
|
||||||
if s[:6] not in [b"GIF87a", b"GIF89a"]:
|
if not _accept(s):
|
||||||
raise SyntaxError("not a GIF file")
|
raise SyntaxError("not a GIF file")
|
||||||
|
|
||||||
self.info["version"] = s[:6]
|
self.info["version"] = s[:6]
|
||||||
|
@ -130,9 +130,9 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
for f in range(self.__frame + 1, frame + 1):
|
for f in range(self.__frame + 1, frame + 1):
|
||||||
try:
|
try:
|
||||||
self._seek(f)
|
self._seek(f)
|
||||||
except EOFError:
|
except EOFError as e:
|
||||||
self.seek(last_frame)
|
self.seek(last_frame)
|
||||||
raise EOFError("no more images in GIF file")
|
raise EOFError("no more images in GIF file") from e
|
||||||
|
|
||||||
def _seek(self, frame):
|
def _seek(self, frame):
|
||||||
|
|
||||||
|
|
|
@ -13,17 +13,19 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Stuff to translate curve segments to palette values (derived from
|
||||||
|
the corresponding code in GIMP, written by Federico Mena Quintero.
|
||||||
|
See the GIMP distribution for more information.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
from math import log, pi, sin, sqrt
|
from math import log, pi, sin, sqrt
|
||||||
|
|
||||||
from ._binary import o8
|
from ._binary import o8
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
# Stuff to translate curve segments to palette values (derived from
|
|
||||||
# the corresponding code in GIMP, written by Federico Mena Quintero.
|
|
||||||
# See the GIMP distribution for more information.)
|
|
||||||
#
|
|
||||||
|
|
||||||
EPSILON = 1e-10
|
EPSILON = 1e-10
|
||||||
|
"""""" # Enable auto-doc for data member
|
||||||
|
|
||||||
|
|
||||||
def linear(middle, pos):
|
def linear(middle, pos):
|
||||||
|
@ -58,6 +60,7 @@ def sphere_decreasing(middle, pos):
|
||||||
|
|
||||||
|
|
||||||
SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing]
|
SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing]
|
||||||
|
"""""" # Enable auto-doc for data member
|
||||||
|
|
||||||
|
|
||||||
class GradientFile:
|
class GradientFile:
|
||||||
|
@ -98,11 +101,9 @@ class GradientFile:
|
||||||
return b"".join(palette), "RGBA"
|
return b"".join(palette), "RGBA"
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# File handler for GIMP's gradient format.
|
|
||||||
|
|
||||||
|
|
||||||
class GimpGradientFile(GradientFile):
|
class GimpGradientFile(GradientFile):
|
||||||
|
"""File handler for GIMP's gradient format."""
|
||||||
|
|
||||||
def __init__(self, fp):
|
def __init__(self, fp):
|
||||||
|
|
||||||
if fp.readline()[:13] != b"GIMP Gradient":
|
if fp.readline()[:13] != b"GIMP Gradient":
|
||||||
|
|
|
@ -18,11 +18,9 @@ import re
|
||||||
|
|
||||||
from ._binary import o8
|
from ._binary import o8
|
||||||
|
|
||||||
##
|
|
||||||
# File handler for GIMP's palette format.
|
|
||||||
|
|
||||||
|
|
||||||
class GimpPaletteFile:
|
class GimpPaletteFile:
|
||||||
|
"""File handler for GIMP's palette format."""
|
||||||
|
|
||||||
rawmode = "RGB"
|
rawmode = "RGB"
|
||||||
|
|
||||||
|
|
|
@ -163,8 +163,8 @@ class ImImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
m = split.match(s)
|
m = split.match(s)
|
||||||
except re.error:
|
except re.error as e:
|
||||||
raise SyntaxError("not an IM file")
|
raise SyntaxError("not an IM file") from e
|
||||||
|
|
||||||
if m:
|
if m:
|
||||||
|
|
||||||
|
@ -341,8 +341,8 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image_type, rawmode = SAVE[im.mode]
|
image_type, rawmode = SAVE[im.mode]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise ValueError("Cannot save %s images as IM" % im.mode)
|
raise ValueError("Cannot save %s images as IM" % im.mode) from e
|
||||||
|
|
||||||
frames = im.encoderinfo.get("frames", 1)
|
frames = im.encoderinfo.get("frames", 1)
|
||||||
|
|
||||||
|
|
|
@ -434,8 +434,8 @@ def _getdecoder(mode, decoder_name, args, extra=()):
|
||||||
try:
|
try:
|
||||||
# get decoder
|
# get decoder
|
||||||
decoder = getattr(core, decoder_name + "_decoder")
|
decoder = getattr(core, decoder_name + "_decoder")
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
raise OSError("decoder %s not available" % decoder_name)
|
raise OSError("decoder %s not available" % decoder_name) from e
|
||||||
return decoder(mode, *args + extra)
|
return decoder(mode, *args + extra)
|
||||||
|
|
||||||
|
|
||||||
|
@ -457,8 +457,8 @@ def _getencoder(mode, encoder_name, args, extra=()):
|
||||||
try:
|
try:
|
||||||
# get encoder
|
# get encoder
|
||||||
encoder = getattr(core, encoder_name + "_encoder")
|
encoder = getattr(core, encoder_name + "_encoder")
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
raise OSError("encoder %s not available" % encoder_name)
|
raise OSError("encoder %s not available" % encoder_name) from e
|
||||||
return encoder(mode, *args + extra)
|
return encoder(mode, *args + extra)
|
||||||
|
|
||||||
|
|
||||||
|
@ -971,10 +971,10 @@ class Image:
|
||||||
if isinstance(t, tuple):
|
if isinstance(t, tuple):
|
||||||
try:
|
try:
|
||||||
t = trns_im.palette.getcolor(t)
|
t = trns_im.palette.getcolor(t)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Couldn't allocate a palette color for transparency"
|
"Couldn't allocate a palette color for transparency"
|
||||||
)
|
) from e
|
||||||
trns_im.putpixel((0, 0), t)
|
trns_im.putpixel((0, 0), t)
|
||||||
|
|
||||||
if mode in ("L", "RGB"):
|
if mode in ("L", "RGB"):
|
||||||
|
@ -1027,8 +1027,8 @@ class Image:
|
||||||
# normalize source image and try again
|
# normalize source image and try again
|
||||||
im = self.im.convert(getmodebase(self.mode))
|
im = self.im.convert(getmodebase(self.mode))
|
||||||
im = im.convert(mode, dither)
|
im = im.convert(mode, dither)
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise ValueError("illegal conversion")
|
raise ValueError("illegal conversion") from e
|
||||||
|
|
||||||
new_im = self._new(im)
|
new_im = self._new(im)
|
||||||
if delete_trns:
|
if delete_trns:
|
||||||
|
@ -1625,16 +1625,16 @@ class Image:
|
||||||
mode = getmodebase(self.mode) + "A"
|
mode = getmodebase(self.mode) + "A"
|
||||||
try:
|
try:
|
||||||
self.im.setmode(mode)
|
self.im.setmode(mode)
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError) as e:
|
||||||
# do things the hard way
|
# do things the hard way
|
||||||
im = self.im.convert(mode)
|
im = self.im.convert(mode)
|
||||||
if im.mode not in ("LA", "PA", "RGBA"):
|
if im.mode not in ("LA", "PA", "RGBA"):
|
||||||
raise ValueError # sanity check
|
raise ValueError from e # sanity check
|
||||||
self.im = im
|
self.im = im
|
||||||
self.pyaccess = None
|
self.pyaccess = None
|
||||||
self.mode = self.im.mode
|
self.mode = self.im.mode
|
||||||
except (KeyError, ValueError):
|
except (KeyError, ValueError) as e:
|
||||||
raise ValueError("illegal image mode")
|
raise ValueError("illegal image mode") from e
|
||||||
|
|
||||||
if self.mode in ("LA", "PA"):
|
if self.mode in ("LA", "PA"):
|
||||||
band = 1
|
band = 1
|
||||||
|
@ -2136,8 +2136,8 @@ class Image:
|
||||||
init()
|
init()
|
||||||
try:
|
try:
|
||||||
format = EXTENSION[ext]
|
format = EXTENSION[ext]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise ValueError("unknown file extension: {}".format(ext))
|
raise ValueError("unknown file extension: {}".format(ext)) from e
|
||||||
|
|
||||||
if format.upper() not in SAVE:
|
if format.upper() not in SAVE:
|
||||||
init()
|
init()
|
||||||
|
@ -2181,8 +2181,10 @@ class Image:
|
||||||
|
|
||||||
def show(self, title=None, command=None):
|
def show(self, title=None, command=None):
|
||||||
"""
|
"""
|
||||||
Displays this image. This method is mainly intended for
|
Displays this image. This method is mainly intended for debugging purposes.
|
||||||
debugging purposes.
|
|
||||||
|
This method calls :py:func:`PIL.ImageShow.show` internally. You can use
|
||||||
|
:py:func:`PIL.ImageShow.register` to override its default behaviour.
|
||||||
|
|
||||||
The image is first saved to a temporary file. By default, it will be in
|
The image is first saved to a temporary file. By default, it will be in
|
||||||
PNG format.
|
PNG format.
|
||||||
|
@ -2194,11 +2196,16 @@ class Image:
|
||||||
|
|
||||||
On Windows, the image is opened with the standard PNG display utility.
|
On Windows, the image is opened with the standard PNG display utility.
|
||||||
|
|
||||||
:param title: Optional title to use for the image window,
|
:param title: Optional title to use for the image window, where possible.
|
||||||
where possible.
|
|
||||||
:param command: command used to show the image
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if command is not None:
|
||||||
|
warnings.warn(
|
||||||
|
"The command parameter is deprecated and will be removed in a future "
|
||||||
|
"release. Use a subclass of ImageShow.Viewer instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
|
|
||||||
_show(self, title=title, command=command)
|
_show(self, title=title, command=command)
|
||||||
|
|
||||||
def split(self):
|
def split(self):
|
||||||
|
@ -2238,8 +2245,8 @@ class Image:
|
||||||
if isinstance(channel, str):
|
if isinstance(channel, str):
|
||||||
try:
|
try:
|
||||||
channel = self.getbands().index(channel)
|
channel = self.getbands().index(channel)
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
raise ValueError('The image has no channel "{}"'.format(channel))
|
raise ValueError('The image has no channel "{}"'.format(channel)) from e
|
||||||
|
|
||||||
return self._new(self.im.getband(channel))
|
return self._new(self.im.getband(channel))
|
||||||
|
|
||||||
|
@ -2736,12 +2743,12 @@ def fromarray(obj, mode=None):
|
||||||
if mode is None:
|
if mode is None:
|
||||||
try:
|
try:
|
||||||
typekey = (1, 1) + shape[2:], arr["typestr"]
|
typekey = (1, 1) + shape[2:], arr["typestr"]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise TypeError("Cannot handle this data type")
|
raise TypeError("Cannot handle this data type") from e
|
||||||
try:
|
try:
|
||||||
mode, rawmode = _fromarray_typemap[typekey]
|
mode, rawmode = _fromarray_typemap[typekey]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise TypeError("Cannot handle this data type: %s, %s" % typekey)
|
raise TypeError("Cannot handle this data type: %s, %s" % typekey) from e
|
||||||
else:
|
else:
|
||||||
rawmode = mode
|
rawmode = mode
|
||||||
if mode in ["1", "L", "I", "P", "F"]:
|
if mode in ["1", "L", "I", "P", "F"]:
|
||||||
|
@ -3143,12 +3150,21 @@ def register_encoder(name, encoder):
|
||||||
|
|
||||||
|
|
||||||
def _show(image, **options):
|
def _show(image, **options):
|
||||||
|
options["_internal_pillow"] = True
|
||||||
_showxv(image, **options)
|
_showxv(image, **options)
|
||||||
|
|
||||||
|
|
||||||
def _showxv(image, title=None, **options):
|
def _showxv(image, title=None, **options):
|
||||||
from . import ImageShow
|
from . import ImageShow
|
||||||
|
|
||||||
|
if "_internal_pillow" in options:
|
||||||
|
del options["_internal_pillow"]
|
||||||
|
else:
|
||||||
|
warnings.warn(
|
||||||
|
"_showxv is deprecated and will be removed in a future release. "
|
||||||
|
"Use Image.show instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
ImageShow.show(image, title, **options)
|
ImageShow.show(image, title, **options)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -369,7 +369,7 @@ def profileToProfile(
|
||||||
else:
|
else:
|
||||||
imOut = transform.apply(im)
|
imOut = transform.apply(im)
|
||||||
except (OSError, TypeError, ValueError) as v:
|
except (OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
return imOut
|
return imOut
|
||||||
|
|
||||||
|
@ -393,7 +393,7 @@ def getOpenProfile(profileFilename):
|
||||||
try:
|
try:
|
||||||
return ImageCmsProfile(profileFilename)
|
return ImageCmsProfile(profileFilename)
|
||||||
except (OSError, TypeError, ValueError) as v:
|
except (OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def buildTransform(
|
def buildTransform(
|
||||||
|
@ -474,7 +474,7 @@ def buildTransform(
|
||||||
inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
|
inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
|
||||||
)
|
)
|
||||||
except (OSError, TypeError, ValueError) as v:
|
except (OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def buildProofTransform(
|
def buildProofTransform(
|
||||||
|
@ -585,7 +585,7 @@ def buildProofTransform(
|
||||||
flags,
|
flags,
|
||||||
)
|
)
|
||||||
except (OSError, TypeError, ValueError) as v:
|
except (OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
buildTransformFromOpenProfiles = buildTransform
|
buildTransformFromOpenProfiles = buildTransform
|
||||||
|
@ -640,7 +640,7 @@ def applyTransform(im, transform, inPlace=False):
|
||||||
else:
|
else:
|
||||||
imOut = transform.apply(im)
|
imOut = transform.apply(im)
|
||||||
except (TypeError, ValueError) as v:
|
except (TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
return imOut
|
return imOut
|
||||||
|
|
||||||
|
@ -682,15 +682,15 @@ def createProfile(colorSpace, colorTemp=-1):
|
||||||
if colorSpace == "LAB":
|
if colorSpace == "LAB":
|
||||||
try:
|
try:
|
||||||
colorTemp = float(colorTemp)
|
colorTemp = float(colorTemp)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError) as e:
|
||||||
raise PyCMSError(
|
raise PyCMSError(
|
||||||
'Color temperature must be numeric, "%s" not valid' % colorTemp
|
'Color temperature must be numeric, "%s" not valid' % colorTemp
|
||||||
)
|
) from e
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return core.createProfile(colorSpace, colorTemp)
|
return core.createProfile(colorSpace, colorTemp)
|
||||||
except (TypeError, ValueError) as v:
|
except (TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def getProfileName(profile):
|
def getProfileName(profile):
|
||||||
|
@ -732,7 +732,7 @@ def getProfileName(profile):
|
||||||
return "{} - {}\n".format(model, manufacturer)
|
return "{} - {}\n".format(model, manufacturer)
|
||||||
|
|
||||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def getProfileInfo(profile):
|
def getProfileInfo(profile):
|
||||||
|
@ -772,7 +772,7 @@ def getProfileInfo(profile):
|
||||||
return "\r\n\r\n".join(arr) + "\r\n\r\n"
|
return "\r\n\r\n".join(arr) + "\r\n\r\n"
|
||||||
|
|
||||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def getProfileCopyright(profile):
|
def getProfileCopyright(profile):
|
||||||
|
@ -800,7 +800,7 @@ def getProfileCopyright(profile):
|
||||||
profile = ImageCmsProfile(profile)
|
profile = ImageCmsProfile(profile)
|
||||||
return (profile.profile.copyright or "") + "\n"
|
return (profile.profile.copyright or "") + "\n"
|
||||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def getProfileManufacturer(profile):
|
def getProfileManufacturer(profile):
|
||||||
|
@ -828,7 +828,7 @@ def getProfileManufacturer(profile):
|
||||||
profile = ImageCmsProfile(profile)
|
profile = ImageCmsProfile(profile)
|
||||||
return (profile.profile.manufacturer or "") + "\n"
|
return (profile.profile.manufacturer or "") + "\n"
|
||||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def getProfileModel(profile):
|
def getProfileModel(profile):
|
||||||
|
@ -857,7 +857,7 @@ def getProfileModel(profile):
|
||||||
profile = ImageCmsProfile(profile)
|
profile = ImageCmsProfile(profile)
|
||||||
return (profile.profile.model or "") + "\n"
|
return (profile.profile.model or "") + "\n"
|
||||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def getProfileDescription(profile):
|
def getProfileDescription(profile):
|
||||||
|
@ -886,7 +886,7 @@ def getProfileDescription(profile):
|
||||||
profile = ImageCmsProfile(profile)
|
profile = ImageCmsProfile(profile)
|
||||||
return (profile.profile.profile_description or "") + "\n"
|
return (profile.profile.profile_description or "") + "\n"
|
||||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def getDefaultIntent(profile):
|
def getDefaultIntent(profile):
|
||||||
|
@ -925,7 +925,7 @@ def getDefaultIntent(profile):
|
||||||
profile = ImageCmsProfile(profile)
|
profile = ImageCmsProfile(profile)
|
||||||
return profile.profile.rendering_intent
|
return profile.profile.rendering_intent
|
||||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def isIntentSupported(profile, intent, direction):
|
def isIntentSupported(profile, intent, direction):
|
||||||
|
@ -976,7 +976,7 @@ def isIntentSupported(profile, intent, direction):
|
||||||
else:
|
else:
|
||||||
return -1
|
return -1
|
||||||
except (AttributeError, OSError, TypeError, ValueError) as v:
|
except (AttributeError, OSError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v) from v
|
||||||
|
|
||||||
|
|
||||||
def versions():
|
def versions():
|
||||||
|
|
|
@ -16,21 +16,35 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
(Experimental) WCK-style drawing interface operations
|
||||||
|
|
||||||
|
.. seealso:: :py:mod:`PIL.ImageDraw`
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
|
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
|
||||||
|
|
||||||
|
|
||||||
class Pen:
|
class Pen:
|
||||||
|
"""Stores an outline color and width."""
|
||||||
|
|
||||||
def __init__(self, color, width=1, opacity=255):
|
def __init__(self, color, width=1, opacity=255):
|
||||||
self.color = ImageColor.getrgb(color)
|
self.color = ImageColor.getrgb(color)
|
||||||
self.width = width
|
self.width = width
|
||||||
|
|
||||||
|
|
||||||
class Brush:
|
class Brush:
|
||||||
|
"""Stores a fill color"""
|
||||||
|
|
||||||
def __init__(self, color, opacity=255):
|
def __init__(self, color, opacity=255):
|
||||||
self.color = ImageColor.getrgb(color)
|
self.color = ImageColor.getrgb(color)
|
||||||
|
|
||||||
|
|
||||||
class Font:
|
class Font:
|
||||||
|
"""Stores a TrueType font and color"""
|
||||||
|
|
||||||
def __init__(self, color, file, size=12):
|
def __init__(self, color, file, size=12):
|
||||||
# FIXME: add support for bitmap fonts
|
# FIXME: add support for bitmap fonts
|
||||||
self.color = ImageColor.getrgb(color)
|
self.color = ImageColor.getrgb(color)
|
||||||
|
@ -38,6 +52,10 @@ class Font:
|
||||||
|
|
||||||
|
|
||||||
class Draw:
|
class Draw:
|
||||||
|
"""
|
||||||
|
(Experimental) WCK-style drawing interface
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, image, size=None, color=None):
|
def __init__(self, image, size=None, color=None):
|
||||||
if not hasattr(image, "im"):
|
if not hasattr(image, "im"):
|
||||||
image = Image.new(image, size, color)
|
image = Image.new(image, size, color)
|
||||||
|
@ -73,35 +91,89 @@ class Draw:
|
||||||
getattr(self.draw, op)(xy, fill=fill, outline=outline)
|
getattr(self.draw, op)(xy, fill=fill, outline=outline)
|
||||||
|
|
||||||
def settransform(self, offset):
|
def settransform(self, offset):
|
||||||
|
"""Sets a transformation offset."""
|
||||||
(xoffset, yoffset) = offset
|
(xoffset, yoffset) = offset
|
||||||
self.transform = (1, 0, xoffset, 0, 1, yoffset)
|
self.transform = (1, 0, xoffset, 0, 1, yoffset)
|
||||||
|
|
||||||
def arc(self, xy, start, end, *options):
|
def arc(self, xy, start, end, *options):
|
||||||
|
"""
|
||||||
|
Draws an arc (a portion of a circle outline) between the start and end
|
||||||
|
angles, inside the given bounding box.
|
||||||
|
|
||||||
|
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc`
|
||||||
|
"""
|
||||||
self.render("arc", xy, start, end, *options)
|
self.render("arc", xy, start, end, *options)
|
||||||
|
|
||||||
def chord(self, xy, start, end, *options):
|
def chord(self, xy, start, end, *options):
|
||||||
|
"""
|
||||||
|
Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points
|
||||||
|
with a straight line.
|
||||||
|
|
||||||
|
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord`
|
||||||
|
"""
|
||||||
self.render("chord", xy, start, end, *options)
|
self.render("chord", xy, start, end, *options)
|
||||||
|
|
||||||
def ellipse(self, xy, *options):
|
def ellipse(self, xy, *options):
|
||||||
|
"""
|
||||||
|
Draws an ellipse inside the given bounding box.
|
||||||
|
|
||||||
|
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse`
|
||||||
|
"""
|
||||||
self.render("ellipse", xy, *options)
|
self.render("ellipse", xy, *options)
|
||||||
|
|
||||||
def line(self, xy, *options):
|
def line(self, xy, *options):
|
||||||
|
"""
|
||||||
|
Draws a line between the coordinates in the ``xy`` list.
|
||||||
|
|
||||||
|
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line`
|
||||||
|
"""
|
||||||
self.render("line", xy, *options)
|
self.render("line", xy, *options)
|
||||||
|
|
||||||
def pieslice(self, xy, start, end, *options):
|
def pieslice(self, xy, start, end, *options):
|
||||||
|
"""
|
||||||
|
Same as arc, but also draws straight lines between the end points and the
|
||||||
|
center of the bounding box.
|
||||||
|
|
||||||
|
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice`
|
||||||
|
"""
|
||||||
self.render("pieslice", xy, start, end, *options)
|
self.render("pieslice", xy, start, end, *options)
|
||||||
|
|
||||||
def polygon(self, xy, *options):
|
def polygon(self, xy, *options):
|
||||||
|
"""
|
||||||
|
Draws a polygon.
|
||||||
|
|
||||||
|
The polygon outline consists of straight lines between the given
|
||||||
|
coordinates, plus a straight line between the last and the first
|
||||||
|
coordinate.
|
||||||
|
|
||||||
|
|
||||||
|
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon`
|
||||||
|
"""
|
||||||
self.render("polygon", xy, *options)
|
self.render("polygon", xy, *options)
|
||||||
|
|
||||||
def rectangle(self, xy, *options):
|
def rectangle(self, xy, *options):
|
||||||
|
"""
|
||||||
|
Draws a rectangle.
|
||||||
|
|
||||||
|
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle`
|
||||||
|
"""
|
||||||
self.render("rectangle", xy, *options)
|
self.render("rectangle", xy, *options)
|
||||||
|
|
||||||
def text(self, xy, text, font):
|
def text(self, xy, text, font):
|
||||||
|
"""
|
||||||
|
Draws the string at the given position.
|
||||||
|
|
||||||
|
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text`
|
||||||
|
"""
|
||||||
if self.transform:
|
if self.transform:
|
||||||
xy = ImagePath.Path(xy)
|
xy = ImagePath.Path(xy)
|
||||||
xy.transform(self.transform)
|
xy.transform(self.transform)
|
||||||
self.draw.text(xy, text, font=font.font, fill=font.color)
|
self.draw.text(xy, text, font=font.font, fill=font.color)
|
||||||
|
|
||||||
def textsize(self, text, font):
|
def textsize(self, text, font):
|
||||||
|
"""
|
||||||
|
Return the size of the given string, in pixels.
|
||||||
|
|
||||||
|
.. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize`
|
||||||
|
"""
|
||||||
return self.draw.textsize(text, font=font.font)
|
return self.draw.textsize(text, font=font.font)
|
||||||
|
|
|
@ -122,7 +122,7 @@ class ImageFile(Image.Image):
|
||||||
EOFError, # got header but not the first frame
|
EOFError, # got header but not the first frame
|
||||||
struct.error,
|
struct.error,
|
||||||
) as v:
|
) as v:
|
||||||
raise SyntaxError(v)
|
raise SyntaxError(v) from v
|
||||||
|
|
||||||
if not self.mode or self.size[0] <= 0:
|
if not self.mode or self.size[0] <= 0:
|
||||||
raise SyntaxError("not identified by this driver")
|
raise SyntaxError("not identified by this driver")
|
||||||
|
@ -241,12 +241,12 @@ class ImageFile(Image.Image):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
s = read(self.decodermaxblock)
|
s = read(self.decodermaxblock)
|
||||||
except (IndexError, struct.error):
|
except (IndexError, struct.error) as e:
|
||||||
# truncated png/gif
|
# truncated png/gif
|
||||||
if LOAD_TRUNCATED_IMAGES:
|
if LOAD_TRUNCATED_IMAGES:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise OSError("image file is truncated")
|
raise OSError("image file is truncated") from e
|
||||||
|
|
||||||
if not s: # truncated jpeg
|
if not s: # truncated jpeg
|
||||||
if LOAD_TRUNCATED_IMAGES:
|
if LOAD_TRUNCATED_IMAGES:
|
||||||
|
@ -505,7 +505,7 @@ def _save(im, fp, tile, bufsize=0):
|
||||||
try:
|
try:
|
||||||
fh = fp.fileno()
|
fh = fp.fileno()
|
||||||
fp.flush()
|
fp.flush()
|
||||||
except (AttributeError, io.UnsupportedOperation):
|
except (AttributeError, io.UnsupportedOperation) as e:
|
||||||
# compress to Python file-compatible object
|
# compress to Python file-compatible object
|
||||||
for e, b, o, a in tile:
|
for e, b, o, a in tile:
|
||||||
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
|
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
|
||||||
|
@ -522,7 +522,7 @@ def _save(im, fp, tile, bufsize=0):
|
||||||
if s:
|
if s:
|
||||||
break
|
break
|
||||||
if s < 0:
|
if s < 0:
|
||||||
raise OSError("encoder error %d when writing image file" % s)
|
raise OSError("encoder error %d when writing image file" % s) from e
|
||||||
e.cleanup()
|
e.cleanup()
|
||||||
else:
|
else:
|
||||||
# slight speedup: compress to real file object
|
# slight speedup: compress to real file object
|
||||||
|
|
|
@ -411,10 +411,10 @@ class Color3DLUT(MultibandFilter):
|
||||||
def _check_size(size):
|
def _check_size(size):
|
||||||
try:
|
try:
|
||||||
_, _, _ = size
|
_, _, _ = size
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Size should be either an integer or a tuple of three integers."
|
"Size should be either an integer or a tuple of three integers."
|
||||||
)
|
) from e
|
||||||
except TypeError:
|
except TypeError:
|
||||||
size = (size, size, size)
|
size = (size, size, size)
|
||||||
size = [int(x) for x in size]
|
size = [int(x) for x in size]
|
||||||
|
|
|
@ -505,8 +505,8 @@ class FreeTypeFont:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
names = self.font.getvarnames()
|
names = self.font.getvarnames()
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
raise NotImplementedError("FreeType 2.9.1 or greater is required")
|
raise NotImplementedError("FreeType 2.9.1 or greater is required") from e
|
||||||
return [name.replace(b"\x00", b"") for name in names]
|
return [name.replace(b"\x00", b"") for name in names]
|
||||||
|
|
||||||
def set_variation_by_name(self, name):
|
def set_variation_by_name(self, name):
|
||||||
|
@ -535,8 +535,8 @@ class FreeTypeFont:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
axes = self.font.getvaraxes()
|
axes = self.font.getvaraxes()
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
raise NotImplementedError("FreeType 2.9.1 or greater is required")
|
raise NotImplementedError("FreeType 2.9.1 or greater is required") from e
|
||||||
for axis in axes:
|
for axis in axes:
|
||||||
axis["name"] = axis["name"].replace(b"\x00", b"")
|
axis["name"] = axis["name"].replace(b"\x00", b"")
|
||||||
return axes
|
return axes
|
||||||
|
@ -548,8 +548,8 @@ class FreeTypeFont:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.font.setvaraxes(axes)
|
self.font.setvaraxes(axes)
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
raise NotImplementedError("FreeType 2.9.1 or greater is required")
|
raise NotImplementedError("FreeType 2.9.1 or greater is required") from e
|
||||||
|
|
||||||
|
|
||||||
class TransposedFont:
|
class TransposedFont:
|
||||||
|
|
|
@ -57,8 +57,8 @@ class _Operand:
|
||||||
im1.load()
|
im1.load()
|
||||||
try:
|
try:
|
||||||
op = getattr(_imagingmath, op + "_" + im1.mode)
|
op = getattr(_imagingmath, op + "_" + im1.mode)
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
raise TypeError("bad operand type for '%s'" % op)
|
raise TypeError("bad operand type for '%s'" % op) from e
|
||||||
_imagingmath.unop(op, out.im.id, im1.im.id)
|
_imagingmath.unop(op, out.im.id, im1.im.id)
|
||||||
else:
|
else:
|
||||||
# binary operation
|
# binary operation
|
||||||
|
@ -85,8 +85,8 @@ class _Operand:
|
||||||
im2.load()
|
im2.load()
|
||||||
try:
|
try:
|
||||||
op = getattr(_imagingmath, op + "_" + im1.mode)
|
op = getattr(_imagingmath, op + "_" + im1.mode)
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
raise TypeError("bad operand type for '%s'" % op)
|
raise TypeError("bad operand type for '%s'" % op) from e
|
||||||
_imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)
|
_imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)
|
||||||
return _Operand(out)
|
return _Operand(out)
|
||||||
|
|
||||||
|
|
|
@ -97,13 +97,13 @@ class ImagePalette:
|
||||||
if isinstance(color, tuple):
|
if isinstance(color, tuple):
|
||||||
try:
|
try:
|
||||||
return self.colors[color]
|
return self.colors[color]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
# allocate new color slot
|
# allocate new color slot
|
||||||
if isinstance(self.palette, bytes):
|
if isinstance(self.palette, bytes):
|
||||||
self.palette = bytearray(self.palette)
|
self.palette = bytearray(self.palette)
|
||||||
index = len(self.colors)
|
index = len(self.colors)
|
||||||
if index >= 256:
|
if index >= 256:
|
||||||
raise ValueError("cannot allocate more than 256 colors")
|
raise ValueError("cannot allocate more than 256 colors") from e
|
||||||
self.colors[color] = index
|
self.colors[color] = index
|
||||||
self.palette[index] = color[0]
|
self.palette[index] = color[0]
|
||||||
self.palette[index + 256] = color[1]
|
self.palette[index + 256] = color[1]
|
||||||
|
|
|
@ -38,8 +38,8 @@ class Iterator:
|
||||||
try:
|
try:
|
||||||
self.im.seek(ix)
|
self.im.seek(ix)
|
||||||
return self.im
|
return self.im
|
||||||
except EOFError:
|
except EOFError as e:
|
||||||
raise IndexError # end of sequence
|
raise IndexError from e # end of sequence
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
@ -49,8 +49,8 @@ class Iterator:
|
||||||
self.im.seek(self.position)
|
self.im.seek(self.position)
|
||||||
self.position += 1
|
self.position += 1
|
||||||
return self.im
|
return self.im
|
||||||
except EOFError:
|
except EOFError as e:
|
||||||
raise StopIteration
|
raise StopIteration from e
|
||||||
|
|
||||||
|
|
||||||
def all_frames(im, func=None):
|
def all_frames(im, func=None):
|
||||||
|
|
|
@ -24,6 +24,14 @@ _viewers = []
|
||||||
|
|
||||||
|
|
||||||
def register(viewer, order=1):
|
def register(viewer, order=1):
|
||||||
|
"""
|
||||||
|
The :py:func:`register` function is used to register additional viewers.
|
||||||
|
|
||||||
|
:param viewer: The viewer to be registered.
|
||||||
|
:param order:
|
||||||
|
Zero or a negative integer to prepend this viewer to the list,
|
||||||
|
a positive integer to append it.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
if issubclass(viewer, Viewer):
|
if issubclass(viewer, Viewer):
|
||||||
viewer = viewer()
|
viewer = viewer()
|
||||||
|
@ -42,7 +50,7 @@ def show(image, title=None, **options):
|
||||||
:param image: An image object.
|
:param image: An image object.
|
||||||
:param title: Optional title. Not all viewers can display the title.
|
:param title: Optional title. Not all viewers can display the title.
|
||||||
:param \**options: Additional viewer options.
|
:param \**options: Additional viewer options.
|
||||||
:returns: True if a suitable viewer was found, false otherwise.
|
:returns: ``True`` if a suitable viewer was found, ``False`` otherwise.
|
||||||
"""
|
"""
|
||||||
for viewer in _viewers:
|
for viewer in _viewers:
|
||||||
if viewer.show(image, title=title, **options):
|
if viewer.show(image, title=title, **options):
|
||||||
|
@ -56,6 +64,10 @@ class Viewer:
|
||||||
# main api
|
# main api
|
||||||
|
|
||||||
def show(self, image, **options):
|
def show(self, image, **options):
|
||||||
|
"""
|
||||||
|
The main function for displaying an image.
|
||||||
|
Converts the given image to the target format and displays it.
|
||||||
|
"""
|
||||||
|
|
||||||
# save temporary image to disk
|
# save temporary image to disk
|
||||||
if not (
|
if not (
|
||||||
|
@ -70,25 +82,31 @@ class Viewer:
|
||||||
# hook methods
|
# hook methods
|
||||||
|
|
||||||
format = None
|
format = None
|
||||||
|
"""The format to convert the image into."""
|
||||||
options = {}
|
options = {}
|
||||||
|
"""Additional options used to convert the image."""
|
||||||
|
|
||||||
def get_format(self, image):
|
def get_format(self, image):
|
||||||
"""Return format name, or None to save as PGM/PPM"""
|
"""Return format name, or ``None`` to save as PGM/PPM."""
|
||||||
return self.format
|
return self.format
|
||||||
|
|
||||||
def get_command(self, file, **options):
|
def get_command(self, file, **options):
|
||||||
|
"""
|
||||||
|
Returns the command used to display the file.
|
||||||
|
Not implemented in the base class.
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def save_image(self, image):
|
def save_image(self, image):
|
||||||
"""Save to temporary file, and return filename"""
|
"""Save to temporary file and return filename."""
|
||||||
return image._dump(format=self.get_format(image), **self.options)
|
return image._dump(format=self.get_format(image), **self.options)
|
||||||
|
|
||||||
def show_image(self, image, **options):
|
def show_image(self, image, **options):
|
||||||
"""Display given image"""
|
"""Display the given image."""
|
||||||
return self.show_file(self.save_image(image), **options)
|
return self.show_file(self.save_image(image), **options)
|
||||||
|
|
||||||
def show_file(self, file, **options):
|
def show_file(self, file, **options):
|
||||||
"""Display given file"""
|
"""Display the given file."""
|
||||||
os.system(self.get_command(file, **options))
|
os.system(self.get_command(file, **options))
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@ -96,9 +114,9 @@ class Viewer:
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
|
||||||
|
|
||||||
class WindowsViewer(Viewer):
|
class WindowsViewer(Viewer):
|
||||||
|
"""The default viewer on Windows is the default system application for PNG files."""
|
||||||
|
|
||||||
format = "PNG"
|
format = "PNG"
|
||||||
options = {"compress_level": 1}
|
options = {"compress_level": 1}
|
||||||
|
|
||||||
|
@ -109,11 +127,14 @@ if sys.platform == "win32":
|
||||||
'&& del /f "%s"' % (file, file)
|
'&& del /f "%s"' % (file, file)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if sys.platform == "win32":
|
||||||
register(WindowsViewer)
|
register(WindowsViewer)
|
||||||
|
|
||||||
elif sys.platform == "darwin":
|
|
||||||
|
|
||||||
class MacViewer(Viewer):
|
class MacViewer(Viewer):
|
||||||
|
"""The default viewer on MacOS using ``Preview.app``."""
|
||||||
|
|
||||||
format = "PNG"
|
format = "PNG"
|
||||||
options = {"compress_level": 1}
|
options = {"compress_level": 1}
|
||||||
|
|
||||||
|
@ -140,11 +161,10 @@ elif sys.platform == "darwin":
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if sys.platform == "darwin":
|
||||||
register(MacViewer)
|
register(MacViewer)
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
# unixoids
|
|
||||||
|
|
||||||
class UnixViewer(Viewer):
|
class UnixViewer(Viewer):
|
||||||
format = "PNG"
|
format = "PNG"
|
||||||
|
@ -167,25 +187,29 @@ else:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# implementations
|
|
||||||
|
|
||||||
class DisplayViewer(UnixViewer):
|
class DisplayViewer(UnixViewer):
|
||||||
|
"""The ImageMagick ``display`` command."""
|
||||||
|
|
||||||
def get_command_ex(self, file, **options):
|
def get_command_ex(self, file, **options):
|
||||||
command = executable = "display"
|
command = executable = "display"
|
||||||
return command, executable
|
return command, executable
|
||||||
|
|
||||||
if shutil.which("display"):
|
|
||||||
register(DisplayViewer)
|
|
||||||
|
|
||||||
class EogViewer(UnixViewer):
|
class EogViewer(UnixViewer):
|
||||||
|
"""The GNOME Image Viewer ``eog`` command."""
|
||||||
|
|
||||||
def get_command_ex(self, file, **options):
|
def get_command_ex(self, file, **options):
|
||||||
command = executable = "eog"
|
command = executable = "eog"
|
||||||
return command, executable
|
return command, executable
|
||||||
|
|
||||||
if shutil.which("eog"):
|
|
||||||
register(EogViewer)
|
|
||||||
|
|
||||||
class XVViewer(UnixViewer):
|
class XVViewer(UnixViewer):
|
||||||
|
"""
|
||||||
|
The X Viewer ``xv`` command.
|
||||||
|
This viewer supports the ``title`` parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
def get_command_ex(self, file, title=None, **options):
|
def get_command_ex(self, file, title=None, **options):
|
||||||
# note: xv is pretty outdated. most modern systems have
|
# note: xv is pretty outdated. most modern systems have
|
||||||
# imagemagick's display command instead.
|
# imagemagick's display command instead.
|
||||||
|
@ -194,6 +218,12 @@ else:
|
||||||
command += " -name %s" % quote(title)
|
command += " -name %s" % quote(title)
|
||||||
return command, executable
|
return command, executable
|
||||||
|
|
||||||
|
|
||||||
|
if sys.platform not in ("win32", "darwin"): # unixoids
|
||||||
|
if shutil.which("display"):
|
||||||
|
register(DisplayViewer)
|
||||||
|
if shutil.which("eog"):
|
||||||
|
register(EogViewer)
|
||||||
if shutil.which("xv"):
|
if shutil.which("xv"):
|
||||||
register(XVViewer)
|
register(XVViewer)
|
||||||
|
|
||||||
|
|
|
@ -118,8 +118,8 @@ class IptcImageFile(ImageFile.ImageFile):
|
||||||
# compression
|
# compression
|
||||||
try:
|
try:
|
||||||
compression = COMPRESSION[self.getint((3, 120))]
|
compression = COMPRESSION[self.getint((3, 120))]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise OSError("Unknown IPTC image compression")
|
raise OSError("Unknown IPTC image compression") from e
|
||||||
|
|
||||||
# tile
|
# tile
|
||||||
if tag == (8, 10):
|
if tag == (8, 10):
|
||||||
|
|
|
@ -323,7 +323,8 @@ MARKER = {
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return prefix[0:1] == b"\377"
|
# Magic number was taken from https://en.wikipedia.org/wiki/JPEG
|
||||||
|
return prefix[0:3] == b"\xFF\xD8\xFF"
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -337,10 +338,11 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def _open(self):
|
def _open(self):
|
||||||
|
|
||||||
s = self.fp.read(1)
|
s = self.fp.read(3)
|
||||||
|
|
||||||
if i8(s) != 255:
|
if not _accept(s):
|
||||||
raise SyntaxError("not a JPEG file")
|
raise SyntaxError("not a JPEG file")
|
||||||
|
s = b"\xFF"
|
||||||
|
|
||||||
# Create attributes
|
# Create attributes
|
||||||
self.bits = self.layers = 0
|
self.bits = self.layers = 0
|
||||||
|
@ -503,13 +505,13 @@ def _getmp(self):
|
||||||
file_contents.seek(info.next)
|
file_contents.seek(info.next)
|
||||||
info.load(file_contents)
|
info.load(file_contents)
|
||||||
mp = dict(info)
|
mp = dict(info)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
raise SyntaxError("malformed MP Index (unreadable directory)")
|
raise SyntaxError("malformed MP Index (unreadable directory)") from e
|
||||||
# it's an error not to have a number of images
|
# it's an error not to have a number of images
|
||||||
try:
|
try:
|
||||||
quant = mp[0xB001]
|
quant = mp[0xB001]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise SyntaxError("malformed MP Index (no number of images)")
|
raise SyntaxError("malformed MP Index (no number of images)") from e
|
||||||
# get MP entries
|
# get MP entries
|
||||||
mpentries = []
|
mpentries = []
|
||||||
try:
|
try:
|
||||||
|
@ -545,8 +547,8 @@ def _getmp(self):
|
||||||
mpentry["Attribute"] = mpentryattr
|
mpentry["Attribute"] = mpentryattr
|
||||||
mpentries.append(mpentry)
|
mpentries.append(mpentry)
|
||||||
mp[0xB002] = mpentries
|
mp[0xB002] = mpentries
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise SyntaxError("malformed MP Index (bad MP Entry)")
|
raise SyntaxError("malformed MP Index (bad MP Entry)") from e
|
||||||
# Next we should try and parse the individual image unique ID list;
|
# Next we should try and parse the individual image unique ID list;
|
||||||
# we don't because I've never seen this actually used in a real MPO
|
# we don't because I've never seen this actually used in a real MPO
|
||||||
# file and so can't test it.
|
# file and so can't test it.
|
||||||
|
@ -610,8 +612,8 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rawmode = RAWMODE[im.mode]
|
rawmode = RAWMODE[im.mode]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise OSError("cannot write mode %s as JPEG" % im.mode)
|
raise OSError("cannot write mode %s as JPEG" % im.mode) from e
|
||||||
|
|
||||||
info = im.encoderinfo
|
info = im.encoderinfo
|
||||||
|
|
||||||
|
@ -663,8 +665,8 @@ def _save(im, fp, filename):
|
||||||
for line in qtables.splitlines()
|
for line in qtables.splitlines()
|
||||||
for num in line.split("#", 1)[0].split()
|
for num in line.split("#", 1)[0].split()
|
||||||
]
|
]
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
raise ValueError("Invalid quantization table")
|
raise ValueError("Invalid quantization table") from e
|
||||||
else:
|
else:
|
||||||
qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)]
|
qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)]
|
||||||
if isinstance(qtables, (tuple, list, dict)):
|
if isinstance(qtables, (tuple, list, dict)):
|
||||||
|
@ -679,8 +681,8 @@ def _save(im, fp, filename):
|
||||||
if len(table) != 64:
|
if len(table) != 64:
|
||||||
raise TypeError
|
raise TypeError
|
||||||
table = array.array("B", table)
|
table = array.array("B", table)
|
||||||
except TypeError:
|
except TypeError as e:
|
||||||
raise ValueError("Invalid quantization table")
|
raise ValueError("Invalid quantization table") from e
|
||||||
else:
|
else:
|
||||||
qtables[idx] = list(table)
|
qtables[idx] = list(table)
|
||||||
return qtables
|
return qtables
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
"""
|
"""
|
||||||
JPEG quality settings equivalent to the Photoshop settings.
|
JPEG quality settings equivalent to the Photoshop settings.
|
||||||
|
Can be used when saving JPEG files.
|
||||||
|
|
||||||
More presets can be added to the presets dict if needed.
|
The following presets are available by default:
|
||||||
|
``web_low``, ``web_medium``, ``web_high``, ``web_very_high``, ``web_maximum``,
|
||||||
Can be use when saving JPEG file.
|
``low``, ``medium``, ``high``, ``maximum``.
|
||||||
|
More presets can be added to the :py:data:`presets` dict if needed.
|
||||||
|
|
||||||
To apply the preset, specify::
|
To apply the preset, specify::
|
||||||
|
|
||||||
|
@ -21,7 +23,6 @@ Example::
|
||||||
|
|
||||||
im.save("image_name.jpg", quality="web_high")
|
im.save("image_name.jpg", quality="web_high")
|
||||||
|
|
||||||
|
|
||||||
Subsampling
|
Subsampling
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
|
@ -46,8 +46,8 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.ole = olefile.OleFileIO(self.fp)
|
self.ole = olefile.OleFileIO(self.fp)
|
||||||
except OSError:
|
except OSError as e:
|
||||||
raise SyntaxError("not an MIC file; invalid OLE file")
|
raise SyntaxError("not an MIC file; invalid OLE file") from e
|
||||||
|
|
||||||
# find ACI subfiles with Image members (maybe not the
|
# find ACI subfiles with Image members (maybe not the
|
||||||
# best way to identify MIC files, but what the... ;-)
|
# best way to identify MIC files, but what the... ;-)
|
||||||
|
@ -77,8 +77,8 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
filename = self.images[frame]
|
filename = self.images[frame]
|
||||||
except IndexError:
|
except IndexError as e:
|
||||||
raise EOFError("no such frame")
|
raise EOFError("no such frame") from e
|
||||||
|
|
||||||
self.fp = self.ole.openstream(filename)
|
self.fp = self.ole.openstream(filename)
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ class MspImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# Header
|
# Header
|
||||||
s = self.fp.read(32)
|
s = self.fp.read(32)
|
||||||
if s[:4] not in [b"DanM", b"LinS"]:
|
if not _accept(s):
|
||||||
raise SyntaxError("not an MSP file")
|
raise SyntaxError("not an MSP file")
|
||||||
|
|
||||||
# Header checksum
|
# Header checksum
|
||||||
|
@ -116,8 +116,8 @@ class MspDecoder(ImageFile.PyDecoder):
|
||||||
rowmap = struct.unpack_from(
|
rowmap = struct.unpack_from(
|
||||||
"<%dH" % (self.state.ysize), self.fd.read(self.state.ysize * 2)
|
"<%dH" % (self.state.ysize), self.fd.read(self.state.ysize * 2)
|
||||||
)
|
)
|
||||||
except struct.error:
|
except struct.error as e:
|
||||||
raise OSError("Truncated MSP file in row map")
|
raise OSError("Truncated MSP file in row map") from e
|
||||||
|
|
||||||
for x, rowlen in enumerate(rowmap):
|
for x, rowlen in enumerate(rowmap):
|
||||||
try:
|
try:
|
||||||
|
@ -142,8 +142,8 @@ class MspDecoder(ImageFile.PyDecoder):
|
||||||
img.write(row[idx : idx + runcount])
|
img.write(row[idx : idx + runcount])
|
||||||
idx += runcount
|
idx += runcount
|
||||||
|
|
||||||
except struct.error:
|
except struct.error as e:
|
||||||
raise OSError("Corrupted MSP file in row %d" % x)
|
raise OSError("Corrupted MSP file in row %d" % x) from e
|
||||||
|
|
||||||
self.set_as_raw(img.getvalue(), ("1", 0, 1))
|
self.set_as_raw(img.getvalue(), ("1", 0, 1))
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,9 @@
|
||||||
|
|
||||||
from ._binary import o8
|
from ._binary import o8
|
||||||
|
|
||||||
##
|
|
||||||
# File handler for Teragon-style palette files.
|
|
||||||
|
|
||||||
|
|
||||||
class PaletteFile:
|
class PaletteFile:
|
||||||
|
"""File handler for Teragon-style palette files."""
|
||||||
|
|
||||||
rawmode = "RGB"
|
rawmode = "RGB"
|
||||||
|
|
||||||
|
|
|
@ -48,11 +48,8 @@ def sz(s, o):
|
||||||
return s[o : s.index(b"\0", o)]
|
return s[o : s.index(b"\0", o)]
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# Font file plugin for the X11 PCF format.
|
|
||||||
|
|
||||||
|
|
||||||
class PcfFontFile(FontFile.FontFile):
|
class PcfFontFile(FontFile.FontFile):
|
||||||
|
"""Font file plugin for the X11 PCF format."""
|
||||||
|
|
||||||
name = "name"
|
name = "name"
|
||||||
|
|
||||||
|
|
|
@ -131,8 +131,8 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
version, bits, planes, rawmode = SAVE[im.mode]
|
version, bits, planes, rawmode = SAVE[im.mode]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise ValueError("Cannot save %s images as PCX" % im.mode)
|
raise ValueError("Cannot save %s images as PCX" % im.mode) from e
|
||||||
|
|
||||||
# bytes per plane
|
# bytes per plane
|
||||||
stride = (im.size[0] * bits + 7) // 8
|
stride = (im.size[0] * bits + 7) // 8
|
||||||
|
|
|
@ -43,7 +43,7 @@ class PixarImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# assuming a 4-byte magic label
|
# assuming a 4-byte magic label
|
||||||
s = self.fp.read(4)
|
s = self.fp.read(4)
|
||||||
if s != b"\200\350\000\000":
|
if not _accept(s):
|
||||||
raise SyntaxError("not a PIXAR file")
|
raise SyntaxError("not a PIXAR file")
|
||||||
|
|
||||||
# read rest of header
|
# read rest of header
|
||||||
|
|
|
@ -168,8 +168,10 @@ class ChunkStream:
|
||||||
crc2 = i32(self.fp.read(4))
|
crc2 = i32(self.fp.read(4))
|
||||||
if crc1 != crc2:
|
if crc1 != crc2:
|
||||||
raise SyntaxError("broken PNG file (bad header checksum in %r)" % cid)
|
raise SyntaxError("broken PNG file (bad header checksum in %r)" % cid)
|
||||||
except struct.error:
|
except struct.error as e:
|
||||||
raise SyntaxError("broken PNG file (incomplete checksum in %r)" % cid)
|
raise SyntaxError(
|
||||||
|
"broken PNG file (incomplete checksum in %r)" % cid
|
||||||
|
) from e
|
||||||
|
|
||||||
def crc_skip(self, cid, data):
|
def crc_skip(self, cid, data):
|
||||||
"""Read checksum. Used if the C module is not present"""
|
"""Read checksum. Used if the C module is not present"""
|
||||||
|
@ -186,8 +188,8 @@ class ChunkStream:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
cid, pos, length = self.read()
|
cid, pos, length = self.read()
|
||||||
except struct.error:
|
except struct.error as e:
|
||||||
raise OSError("truncated PNG file")
|
raise OSError("truncated PNG file") from e
|
||||||
|
|
||||||
if cid == endchunk:
|
if cid == endchunk:
|
||||||
break
|
break
|
||||||
|
@ -633,7 +635,7 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def _open(self):
|
def _open(self):
|
||||||
|
|
||||||
if self.fp.read(8) != _MAGIC:
|
if not _accept(self.fp.read(8)):
|
||||||
raise SyntaxError("not a PNG file")
|
raise SyntaxError("not a PNG file")
|
||||||
self.__fp = self.fp
|
self.__fp = self.fp
|
||||||
self.__frame = 0
|
self.__frame = 0
|
||||||
|
@ -737,9 +739,9 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
for f in range(self.__frame + 1, frame + 1):
|
for f in range(self.__frame + 1, frame + 1):
|
||||||
try:
|
try:
|
||||||
self._seek(f)
|
self._seek(f)
|
||||||
except EOFError:
|
except EOFError as e:
|
||||||
self.seek(last_frame)
|
self.seek(last_frame)
|
||||||
raise EOFError("no more images in APNG file")
|
raise EOFError("no more images in APNG file") from e
|
||||||
|
|
||||||
def _seek(self, frame, rewind=False):
|
def _seek(self, frame, rewind=False):
|
||||||
if frame == 0:
|
if frame == 0:
|
||||||
|
@ -1168,8 +1170,8 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
|
||||||
# get the corresponding PNG mode
|
# get the corresponding PNG mode
|
||||||
try:
|
try:
|
||||||
rawmode, mode = _OUTMODES[mode]
|
rawmode, mode = _OUTMODES[mode]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise OSError("cannot write mode %s as PNG" % mode)
|
raise OSError("cannot write mode %s as PNG" % mode) from e
|
||||||
|
|
||||||
#
|
#
|
||||||
# write minimal PNG file
|
# write minimal PNG file
|
||||||
|
|
|
@ -61,7 +61,7 @@ class PsdImageFile(ImageFile.ImageFile):
|
||||||
# header
|
# header
|
||||||
|
|
||||||
s = read(26)
|
s = read(26)
|
||||||
if s[:4] != b"8BPS" or i16(s[4:]) != 1:
|
if not _accept(s) or i16(s[4:]) != 1:
|
||||||
raise SyntaxError("not a PSD file")
|
raise SyntaxError("not a PSD file")
|
||||||
|
|
||||||
psd_bits = i16(s[22:])
|
psd_bits = i16(s[22:])
|
||||||
|
@ -144,8 +144,8 @@ class PsdImageFile(ImageFile.ImageFile):
|
||||||
self.frame = layer
|
self.frame = layer
|
||||||
self.fp = self.__fp
|
self.fp = self.__fp
|
||||||
return name, bbox
|
return name, bbox
|
||||||
except IndexError:
|
except IndexError as e:
|
||||||
raise EOFError("no such layer")
|
raise EOFError("no such layer") from e
|
||||||
|
|
||||||
def tell(self):
|
def tell(self):
|
||||||
# return layer number (0=image, 1..max=layers)
|
# return layer number (0=image, 1..max=layers)
|
||||||
|
|
|
@ -58,8 +58,7 @@ class SgiImageFile(ImageFile.ImageFile):
|
||||||
headlen = 512
|
headlen = 512
|
||||||
s = self.fp.read(headlen)
|
s = self.fp.read(headlen)
|
||||||
|
|
||||||
# magic number : 474
|
if not _accept(s):
|
||||||
if i16(s) != 474:
|
|
||||||
raise ValueError("Not an SGI image file")
|
raise ValueError("Not an SGI image file")
|
||||||
|
|
||||||
# compression : verbatim or RLE
|
# compression : verbatim or RLE
|
||||||
|
|
|
@ -111,8 +111,8 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||||
hdrlen = isSpiderHeader(t)
|
hdrlen = isSpiderHeader(t)
|
||||||
if hdrlen == 0:
|
if hdrlen == 0:
|
||||||
raise SyntaxError("not a valid Spider file")
|
raise SyntaxError("not a valid Spider file")
|
||||||
except struct.error:
|
except struct.error as e:
|
||||||
raise SyntaxError("not a valid Spider file")
|
raise SyntaxError("not a valid Spider file") from e
|
||||||
|
|
||||||
h = (99,) + t # add 1 value : spider header index starts at 1
|
h = (99,) + t # add 1 value : spider header index starts at 1
|
||||||
iform = int(h[5])
|
iform = int(h[5])
|
||||||
|
|
|
@ -53,7 +53,7 @@ class SunImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# HEAD
|
# HEAD
|
||||||
s = self.fp.read(32)
|
s = self.fp.read(32)
|
||||||
if i32(s) != 0x59A66A95:
|
if not _accept(s):
|
||||||
raise SyntaxError("not an SUN raster file")
|
raise SyntaxError("not an SUN raster file")
|
||||||
|
|
||||||
offset = 32
|
offset = 32
|
||||||
|
|
|
@ -18,12 +18,10 @@ import io
|
||||||
|
|
||||||
from . import ContainerIO
|
from . import ContainerIO
|
||||||
|
|
||||||
##
|
|
||||||
# A file object that provides read access to a given member of a TAR
|
|
||||||
# file.
|
|
||||||
|
|
||||||
|
|
||||||
class TarIO(ContainerIO.ContainerIO):
|
class TarIO(ContainerIO.ContainerIO):
|
||||||
|
"""A file object that provides read access to a given member of a TAR file."""
|
||||||
|
|
||||||
def __init__(self, tarfile, file):
|
def __init__(self, tarfile, file):
|
||||||
"""
|
"""
|
||||||
Create file object.
|
Create file object.
|
||||||
|
|
|
@ -167,8 +167,8 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
|
rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise OSError("cannot write mode %s as TGA" % im.mode)
|
raise OSError("cannot write mode %s as TGA" % im.mode) from e
|
||||||
|
|
||||||
if "rle" in im.encoderinfo:
|
if "rle" in im.encoderinfo:
|
||||||
rle = im.encoderinfo["rle"]
|
rle = im.encoderinfo["rle"]
|
||||||
|
|
|
@ -1117,8 +1117,8 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
decoder.setimage(self.im, extents)
|
decoder.setimage(self.im, extents)
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
raise OSError("Couldn't set the image")
|
raise OSError("Couldn't set the image") from e
|
||||||
|
|
||||||
close_self_fp = self._exclusive_fp and not self.is_animated
|
close_self_fp = self._exclusive_fp and not self.is_animated
|
||||||
if hasattr(self.fp, "getvalue"):
|
if hasattr(self.fp, "getvalue"):
|
||||||
|
@ -1231,9 +1231,9 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
logger.debug("format key: {}".format(key))
|
logger.debug("format key: {}".format(key))
|
||||||
try:
|
try:
|
||||||
self.mode, rawmode = OPEN_INFO[key]
|
self.mode, rawmode = OPEN_INFO[key]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
logger.debug("- unsupported format")
|
logger.debug("- unsupported format")
|
||||||
raise SyntaxError("unknown pixel mode")
|
raise SyntaxError("unknown pixel mode") from e
|
||||||
|
|
||||||
logger.debug("- raw mode: {}".format(rawmode))
|
logger.debug("- raw mode: {}".format(rawmode))
|
||||||
logger.debug("- pil mode: {}".format(self.mode))
|
logger.debug("- pil mode: {}".format(self.mode))
|
||||||
|
@ -1400,8 +1400,8 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode]
|
rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise OSError("cannot write mode %s as TIFF" % im.mode)
|
raise OSError("cannot write mode %s as TIFF" % im.mode) from e
|
||||||
|
|
||||||
ifd = ImageFileDirectory_v2(prefix=prefix)
|
ifd = ImageFileDirectory_v2(prefix=prefix)
|
||||||
|
|
||||||
|
@ -1530,7 +1530,6 @@ def _save(im, fp, filename):
|
||||||
# BITSPERSAMPLE, etc), passing arrays with a different length will result in
|
# BITSPERSAMPLE, etc), passing arrays with a different length will result in
|
||||||
# segfaults. Block these tags until we add extra validation.
|
# segfaults. Block these tags until we add extra validation.
|
||||||
blocklist = [
|
blocklist = [
|
||||||
COLORMAP,
|
|
||||||
REFERENCEBLACKWHITE,
|
REFERENCEBLACKWHITE,
|
||||||
SAMPLEFORMAT,
|
SAMPLEFORMAT,
|
||||||
STRIPBYTECOUNTS,
|
STRIPBYTECOUNTS,
|
||||||
|
|
|
@ -483,7 +483,6 @@ LIBTIFF_CORE = {
|
||||||
65537,
|
65537,
|
||||||
}
|
}
|
||||||
|
|
||||||
LIBTIFF_CORE.remove(320) # Array of short, crashes
|
|
||||||
LIBTIFF_CORE.remove(301) # Array of short, crashes
|
LIBTIFF_CORE.remove(301) # Array of short, crashes
|
||||||
LIBTIFF_CORE.remove(532) # Array of long, crashes
|
LIBTIFF_CORE.remove(532) # Array of long, crashes
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,16 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
# NOTE: This format cannot be automatically recognized, so the reader
|
"""
|
||||||
# is not registered for use with Image.open(). To open a WAL file, use
|
This reader is based on the specification available from:
|
||||||
# the WalImageFile.open() function instead.
|
https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml
|
||||||
|
and has been tested with a few sample files found using google.
|
||||||
|
|
||||||
# This reader is based on the specification available from:
|
.. note::
|
||||||
# https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml
|
This format cannot be automatically recognized, so the reader
|
||||||
# and has been tested with a few sample files found using google.
|
is not registered for use with :py:func:`PIL.Image.open()`.
|
||||||
|
To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead.
|
||||||
|
"""
|
||||||
|
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
|
@ -31,7 +34,7 @@ def open(filename):
|
||||||
Load texture from a Quake2 WAL texture file.
|
Load texture from a Quake2 WAL texture file.
|
||||||
|
|
||||||
By default, a Quake2 standard palette is attached to the texture.
|
By default, a Quake2 standard palette is attached to the texture.
|
||||||
To override the palette, use the <b>putpalette</b> method.
|
To override the palette, use the :py:func:`PIL.Image.Image.putpalette()` method.
|
||||||
|
|
||||||
:param filename: WAL file name, or an opened file handle.
|
:param filename: WAL file name, or an opened file handle.
|
||||||
:returns: An image instance.
|
:returns: An image instance.
|
||||||
|
|
|
@ -132,4 +132,8 @@ _plugins = [
|
||||||
|
|
||||||
|
|
||||||
class UnidentifiedImageError(OSError):
|
class UnidentifiedImageError(OSError):
|
||||||
|
"""
|
||||||
|
Raised in :py:meth:`PIL.Image.open` if an image cannot be opened and identified.
|
||||||
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -11,6 +11,10 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
|
"""Binary input/output support routines."""
|
||||||
|
|
||||||
|
|
||||||
from struct import pack, unpack_from
|
from struct import pack, unpack_from
|
||||||
|
|
||||||
|
|
||||||
|
|
23
src/encode.c
23
src/encode.c
|
@ -671,7 +671,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
||||||
// This list also exists in TiffTags.py
|
// This list also exists in TiffTags.py
|
||||||
const int core_tags[] = {
|
const int core_tags[] = {
|
||||||
256, 257, 258, 259, 262, 263, 266, 269, 274, 277, 278, 280, 281, 340,
|
256, 257, 258, 259, 262, 263, 266, 269, 274, 277, 278, 280, 281, 340,
|
||||||
341, 282, 283, 284, 286, 287, 296, 297, 321, 338, 32995, 32998, 32996,
|
341, 282, 283, 284, 286, 287, 296, 297, 320, 321, 338, 32995, 32998, 32996,
|
||||||
339, 32997, 330, 531, 530, 65537
|
339, 32997, 330, 531, 530, 65537
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -801,7 +801,26 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
||||||
TRACE(("Setting from Tuple: %d \n", key_int));
|
TRACE(("Setting from Tuple: %d \n", key_int));
|
||||||
len = PyTuple_Size(value);
|
len = PyTuple_Size(value);
|
||||||
|
|
||||||
if (type == TIFF_SHORT) {
|
if (key_int == TIFFTAG_COLORMAP) {
|
||||||
|
int stride = 256;
|
||||||
|
if (len != 768) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Requiring 768 items for for Colormap");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
UINT16 *av;
|
||||||
|
/* malloc check ok, calloc checks for overflow */
|
||||||
|
av = calloc(len, sizeof(UINT16));
|
||||||
|
if (av) {
|
||||||
|
for (i=0;i<len;i++) {
|
||||||
|
av[i] = (UINT16)PyLong_AsLong(PyTuple_GetItem(value,i));
|
||||||
|
}
|
||||||
|
status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int,
|
||||||
|
av,
|
||||||
|
av + stride,
|
||||||
|
av + stride * 2);
|
||||||
|
free(av);
|
||||||
|
}
|
||||||
|
} else if (type == TIFF_SHORT) {
|
||||||
UINT16 *av;
|
UINT16 *av;
|
||||||
/* malloc check ok, calloc checks for overflow */
|
/* malloc check ok, calloc checks for overflow */
|
||||||
av = calloc(len, sizeof(UINT16));
|
av = calloc(len, sizeof(UINT16));
|
||||||
|
|
|
@ -683,7 +683,7 @@ ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
big_hypotenuse = sqrt((double) (dx*dx + dy*dy));
|
big_hypotenuse = hypot(dx, dy);
|
||||||
small_hypotenuse = (width - 1) / 2.0;
|
small_hypotenuse = (width - 1) / 2.0;
|
||||||
ratio_max = ROUND_UP(small_hypotenuse) / big_hypotenuse;
|
ratio_max = ROUND_UP(small_hypotenuse) / big_hypotenuse;
|
||||||
ratio_min = ROUND_DOWN(small_hypotenuse) / big_hypotenuse;
|
ratio_min = ROUND_DOWN(small_hypotenuse) / big_hypotenuse;
|
||||||
|
|
|
@ -251,9 +251,9 @@ deps = {
|
||||||
"libs": [r"*.lib"],
|
"libs": [r"*.lib"],
|
||||||
},
|
},
|
||||||
"harfbuzz": {
|
"harfbuzz": {
|
||||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.7.zip",
|
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.8.zip",
|
||||||
"filename": "harfbuzz-2.6.7.zip",
|
"filename": "harfbuzz-2.6.8.zip",
|
||||||
"dir": "harfbuzz-2.6.7",
|
"dir": "harfbuzz-2.6.8",
|
||||||
"build": [
|
"build": [
|
||||||
cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"),
|
cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"),
|
||||||
cmd_nmake(target="clean"),
|
cmd_nmake(target="clean"),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user