Merge branch 'main' into improved_dds

This commit is contained in:
Andrew Murray 2023-10-21 14:27:31 +11:00
commit a54d28a7bc
73 changed files with 856 additions and 300 deletions

View File

@ -2,6 +2,8 @@ name: CIFuzz
on:
push:
branches:
- "**"
paths:
- ".github/workflows/cifuzz.yml"
- "**.c"

View File

@ -2,6 +2,8 @@ name: Docs
on:
push:
branches:
- "**"
paths:
- ".github/workflows/docs.yml"
- "docs/**"

View File

@ -2,6 +2,8 @@ name: Test Cygwin
on:
push:
branches:
- "**"
paths-ignore:
- ".github/workflows/docs.yml"
- ".github/workflows/wheels*"

View File

@ -2,6 +2,8 @@ name: Test Docker
on:
push:
branches:
- "**"
paths-ignore:
- ".github/workflows/docs.yml"
- ".github/workflows/wheels*"

View File

@ -2,6 +2,8 @@ name: Test MinGW
on:
push:
branches:
- "**"
paths-ignore:
- ".github/workflows/docs.yml"
- ".github/workflows/wheels*"

View File

@ -1,9 +1,11 @@
name: Test Valgrind
# like the docker tests, but running valgrind only on *.c/*.h changes.
# like the Docker tests, but running valgrind only on *.c/*.h changes.
on:
push:
branches:
- "**"
paths:
- ".github/workflows/test-valgrind.yml"
- "**.c"

View File

@ -2,6 +2,8 @@ name: Test
on:
push:
branches:
- "**"
paths-ignore:
- ".github/workflows/docs.yml"
- ".github/workflows/wheels*"

View File

@ -2,9 +2,24 @@
Changelog (Pillow)
==================
10.1.0 (unreleased)
10.1.0 (2023-10-15)
-------------------
- Added TrueType default font to allow for different sizes #7354
[radarhere]
- Fixed invalid argument warning #7442
[radarhere]
- Added ImageOps cover method #7412
[radarhere, hugovk]
- Catch struct.error from truncated EXIF when reading JPEG DPI #7458
[radarhere]
- Consider default image when selecting mode for PNG save_all #7437
[radarhere]
- Support BGR;15, BGR;16 and BGR;24 access, unpacking and putdata #7303
[radarhere]
@ -2176,7 +2191,7 @@ Changelog (Pillow)
- Cache EXIF information #3498
[Glandos]
- Added transparency for all PNG greyscale modes #3744
- Added transparency for all PNG grayscale modes #3744
[radarhere]
- Fix deprecation warnings in Python 3.8 #3749
@ -4678,7 +4693,7 @@ Changelog (Pillow)
- Fix Bicubic interpolation #970
[homm]
- Support for 4-bit greyscale TIFF images #980
- Support for 4-bit grayscale TIFF images #980
[hugovk]
- Updated manifest #957
@ -6753,7 +6768,7 @@ The test suite includes 750 individual tests.
- You can now convert directly between all modes supported by
PIL. When converting colour images to "P", PIL defaults to
a "web" palette and dithering. When converting greyscale
a "web" palette and dithering. When converting grayscale
images to "1", PIL uses a thresholding and dithering.
- Added a "dither" option to "convert". By default, "convert"
@ -6831,13 +6846,13 @@ The test suite includes 530 individual tests.
- Fixed "paste" to allow a mask also for mode "F" images.
- The BMP driver now saves mode "1" images. When loading images, the mode
is set to "L" for 8-bit files with greyscale palettes, and to "P" for
is set to "L" for 8-bit files with grayscale palettes, and to "P" for
other 8-bit files.
- The IM driver now reads and saves "1" images (file modes "0 1" or "L 1").
- The JPEG and GIF drivers now saves "1" images. For JPEG, the image
is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the
is saved as 8-bit grayscale (it will load as mode "L"). For GIF, the
image will be loaded as a "P" image.
- Fixed a potential buffer overrun in the GIF encoder.
@ -7141,7 +7156,7 @@ The test suite includes 400 individual tests.
drawing capabilities can be used to render vector and metafile
formats.
- Added restricted drivers for images from Image Tools (greyscale
- Added restricted drivers for images from Image Tools (grayscale
only) and LabEye/IFUNC (common interchange modes only).
- Some minor improvements to the sample scripts provided in the

View File

@ -103,7 +103,7 @@ Released as needed privately to individual vendors for critical security-related
and copy into `dist/`. For example using [GitHub CLI](https://github.com/cli/cli):
```bash
gh run download --dir dist
# select dist-x.y.z
# select wheels
```
* [ ] Download the Linux aarch64 wheels created by Travis CI from [GitHub releases](https://github.com/python-pillow/Pillow/releases)
and copy into `dist`.

View File

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 331 B

View File

Before

Width:  |  Height:  |  Size: 668 B

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -231,13 +231,13 @@ def test_apng_mode():
assert im.getpixel((0, 0)) == (0, 0, 128, 191)
assert im.getpixel((64, 32)) == (0, 0, 128, 191)
with Image.open("Tests/images/apng/mode_greyscale.png") as im:
with Image.open("Tests/images/apng/mode_grayscale.png") as im:
assert im.mode == "L"
im.seek(im.n_frames - 1)
assert im.getpixel((0, 0)) == 128
assert im.getpixel((64, 32)) == 255
with Image.open("Tests/images/apng/mode_greyscale_alpha.png") as im:
with Image.open("Tests/images/apng/mode_grayscale_alpha.png") as im:
assert im.mode == "LA"
im.seek(im.n_frames - 1)
assert im.getpixel((0, 0)) == (128, 191)
@ -673,10 +673,16 @@ def test_seek_after_close():
@pytest.mark.parametrize("mode", ("RGBA", "RGB", "P"))
def test_different_modes_in_later_frames(mode, tmp_path):
@pytest.mark.parametrize("default_image", (True, False))
def test_different_modes_in_later_frames(mode, default_image, tmp_path):
test_file = str(tmp_path / "temp.png")
im = Image.new("L", (1, 1))
im.save(test_file, save_all=True, append_images=[Image.new(mode, (1, 1))])
im.save(
test_file,
save_all=True,
default_image=default_image,
append_images=[Image.new(mode, (1, 1))],
)
with Image.open(test_file) as reloaded:
assert reloaded.mode == mode

View File

@ -159,7 +159,7 @@ def test_rle8():
with Image.open("Tests/images/hopper_rle8.bmp") as im:
assert_image_similar_tofile(im.convert("RGB"), "Tests/images/hopper.bmp", 12)
with Image.open("Tests/images/hopper_rle8_greyscale.bmp") as im:
with Image.open("Tests/images/hopper_rle8_grayscale.bmp") as im:
assert_image_equal_tofile(im, "Tests/images/bw_gradient.png")
# This test image has been manually hexedited

View File

@ -590,7 +590,7 @@ def test_save_dispose(tmp_path):
def test_dispose2_palette(tmp_path):
out = str(tmp_path / "temp.gif")
# Four colors: white, grey, black, red
# Four colors: white, gray, black, red
circles = [(255, 255, 255), (153, 153, 153), (0, 0, 0), (255, 0, 0)]
im_list = []

View File

@ -767,6 +767,13 @@ class TestFileJpeg:
# This should return the default
assert im.info.get("dpi") == (72, 72)
def test_dpi_exif_truncated(self):
# Arrange
with Image.open("Tests/images/truncated_exif_dpi.jpg") as im:
# Act / Assert
# This should return the default
assert im.info.get("dpi") == (72, 72)
def test_no_dpi_in_exif(self):
# Arrange
# This is photoshop-200dpi.jpg with resolution removed from EXIF:

View File

@ -26,8 +26,7 @@ def open_with_magick(magick, tmp_path, f):
rc = subprocess.call(
magick + [f, outfile], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT
)
if rc:
raise OSError
assert not rc
return Image.open(outfile)

View File

@ -297,7 +297,7 @@ class TestFilePng:
assert_image(im, "RGBA", (10, 10))
assert im.getcolors() == [(100, (0, 0, 0, 0))]
def test_save_greyscale_transparency(self, tmp_path):
def test_save_grayscale_transparency(self, tmp_path):
for mode, num_transparent in {"1": 1994, "L": 559, "I": 559}.items():
in_file = "Tests/images/" + mode.lower() + "_trns.png"
with Image.open(in_file) as im:

View File

@ -10,7 +10,7 @@ GREEN = (0, 255, 0)
ORANGE = (255, 128, 0)
WHITE = (255, 255, 255)
GREY = 128
GRAY = 128
def test_sanity():
@ -121,12 +121,12 @@ def test_constant():
im = Image.new("RGB", (20, 10))
# Act
new = ImageChops.constant(im, GREY)
new = ImageChops.constant(im, GRAY)
# Assert
assert new.size == im.size
assert new.getpixel((0, 0)) == GREY
assert new.getpixel((19, 9)) == GREY
assert new.getpixel((0, 0)) == GRAY
assert new.getpixel((19, 9)) == GRAY
def test_darker_image():

View File

@ -1,8 +1,9 @@
import contextlib
import os.path
import pytest
from PIL import Image, ImageColor, ImageDraw, ImageFont
from PIL import Image, ImageColor, ImageDraw, ImageFont, features
from .helper import (
assert_image_equal,
@ -1353,7 +1354,33 @@ def test_setting_default_font():
assert draw.getfont() == font
finally:
ImageDraw.ImageDraw.font = None
assert isinstance(draw.getfont(), ImageFont.ImageFont)
assert isinstance(draw.getfont(), ImageFont.load_default().__class__)
def test_default_font_size():
freetype_support = features.check_module("freetype2")
text = "Default font at a specific size."
im = Image.new("RGB", (220, 25))
draw = ImageDraw.Draw(im)
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
draw.text((0, 0), text, font_size=16)
assert_image_equal_tofile(im, "Tests/images/imagedraw_default_font_size.png")
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
assert draw.textlength(text, font_size=16) == 216
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
assert draw.textbbox((0, 0), text, font_size=16) == (0, 3, 216, 19)
im = Image.new("RGB", (220, 25))
draw = ImageDraw.Draw(im)
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
draw.multiline_text((0, 0), text, font_size=16)
assert_image_equal_tofile(im, "Tests/images/imagedraw_default_font_size.png")
with contextlib.nullcontext() if freetype_support else pytest.raises(ImportError):
assert draw.multiline_textbbox((0, 0), text, font_size=16) == (0, 3, 216, 19)
@pytest.mark.parametrize("bbox", BBOX)

View File

@ -303,8 +303,8 @@ def test_multiline_spacing(font):
"orientation", (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270)
)
def test_rotated_transposed_font(font, orientation):
img_grey = Image.new("L", (100, 100))
draw = ImageDraw.Draw(img_grey)
img_gray = Image.new("L", (100, 100))
draw = ImageDraw.Draw(img_gray)
word = "testing"
transposed_font = ImageFont.TransposedFont(font, orientation=orientation)
@ -344,8 +344,8 @@ def test_rotated_transposed_font(font, orientation):
),
)
def test_unrotated_transposed_font(font, orientation):
img_grey = Image.new("L", (100, 100))
draw = ImageDraw.Draw(img_grey)
img_gray = Image.new("L", (100, 100))
draw = ImageDraw.Draw(img_gray)
word = "testing"
transposed_font = ImageFont.TransposedFont(font, orientation=orientation)
@ -453,7 +453,7 @@ def test_load_non_font_bytes():
def test_default_font():
# Arrange
txt = 'This is a "better than nothing" default font.'
txt = "This is a default font using FreeType support."
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
@ -461,8 +461,11 @@ def test_default_font():
default_font = ImageFont.load_default()
draw.text((10, 10), txt, font=default_font)
larger_default_font = ImageFont.load_default(size=14)
draw.text((10, 60), txt, font=larger_default_font)
# Assert
assert_image_equal_tofile(im, "Tests/images/default_font.png")
assert_image_equal_tofile(im, "Tests/images/default_font_freetype.png")
@pytest.mark.parametrize("mode", (None, "1", "RGBA"))
@ -485,14 +488,6 @@ def test_render_empty(font):
assert_image_equal(im, target)
def test_unicode_pilfont():
# should not segfault, should return UnicodeDecodeError
# issue #2826
font = ImageFont.load_default()
with pytest.raises(UnicodeEncodeError):
font.getbbox("")
def test_unicode_extended(layout_engine):
# issue #3777
text = "A\u278A\U0001F12B"
@ -722,14 +717,6 @@ def test_variation_set_by_axes(font):
_check_text(font, "Tests/images/variation_tiny_axes.png", 32.5)
def test_textbbox_non_freetypefont():
im = Image.new("RGB", (200, 200))
d = ImageDraw.Draw(im)
default_font = ImageFont.load_default()
assert d.textlength("test", font=default_font) == 24
assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, 24, 11)
@pytest.mark.parametrize(
"anchor, left, top",
(

View File

@ -0,0 +1,45 @@
import pytest
from PIL import Image, ImageDraw, ImageFont, features
from .helper import assert_image_equal_tofile
pytestmark = pytest.mark.skipif(
features.check_module("freetype2"),
reason="PILfont superseded if FreeType is supported",
)
def test_default_font():
# Arrange
txt = 'This is a "better than nothing" default font.'
im = Image.new(mode="RGB", size=(300, 100))
draw = ImageDraw.Draw(im)
# Act
default_font = ImageFont.load_default()
draw.text((10, 10), txt, font=default_font)
# Assert
assert_image_equal_tofile(im, "Tests/images/default_font.png")
def test_size_without_freetype():
with pytest.raises(ImportError):
ImageFont.load_default(size=14)
def test_unicode():
# should not segfault, should return UnicodeDecodeError
# issue #2826
font = ImageFont.load_default()
with pytest.raises(UnicodeEncodeError):
font.getbbox("")
def test_textbbox():
im = Image.new("RGB", (200, 200))
d = ImageDraw.Draw(im)
default_font = ImageFont.load_default()
assert d.textlength("test", font=default_font) == 24
assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, 24, 11)

View File

@ -39,6 +39,9 @@ def test_sanity():
ImageOps.contain(hopper("L"), (128, 128))
ImageOps.contain(hopper("RGB"), (128, 128))
ImageOps.cover(hopper("L"), (128, 128))
ImageOps.cover(hopper("RGB"), (128, 128))
ImageOps.crop(hopper("L"), 1)
ImageOps.crop(hopper("RGB"), 1)
@ -119,6 +122,20 @@ def test_contain_round():
assert new_im.height == 5
@pytest.mark.parametrize(
"image_name, expected_size",
(
("colr_bungee.png", (1024, 256)), # landscape
("imagedraw_stroke_multiline.png", (256, 640)), # portrait
("hopper.png", (256, 256)), # square
),
)
def test_cover(image_name, expected_size):
with Image.open("Tests/images/" + image_name) as im:
new_im = ImageOps.cover(im, (256, 256))
assert new_im.size == expected_size
def test_pad():
# Same ratio
im = hopper()

View File

@ -1,7 +1,7 @@
#!/bin/bash
# install libimagequant
archive=libimagequant-4.2.1
archive=libimagequant-4.2.2
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -26,7 +26,7 @@ image. If the image was not read from a file, it is set to None. The size
attribute is a 2-tuple containing width and height (in pixels). The
:py:attr:`~PIL.Image.Image.mode` attribute defines the number and names of the
bands in the image, and also the pixel type and depth. Common modes are “L”
(luminance) for greyscale images, “RGB” for true color images, and “CMYK” for
(luminance) for grayscale images, “RGB” for true color images, and “CMYK” for
pre-press images.
If the file cannot be opened, an :py:exc:`OSError` exception is raised.
@ -268,6 +268,37 @@ true, to provide for the same changes to the image's size.
A more general form of image transformations can be carried out via the
:py:meth:`~PIL.Image.Image.transform` method.
Relative resizing
^^^^^^^^^^^^^^^^^
Instead of calculating the size of the new image when resizing, you can also
choose to resize relative to a given size.
::
from PIL import Image, ImageOps
size = (100, 150)
with Image.open("Tests/images/hopper.png") as im:
ImageOps.contain(im, size).save("imageops_contain.png")
ImageOps.cover(im, size).save("imageops_cover.png")
ImageOps.fit(im, size).save("imageops_fit.png")
ImageOps.pad(im, size, color="#f00").save("imageops_pad.png")
# thumbnail() can also be used,
# but will modify the image object in place
im.thumbnail(size)
im.save("imageops_thumbnail.png")
+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+
| | :py:meth:`~PIL.Image.Image.thumbnail` | :py:meth:`~PIL.ImageOps.contain` | :py:meth:`~PIL.ImageOps.cover` | :py:meth:`~PIL.ImageOps.fit` | :py:meth:`~PIL.ImageOps.pad` |
+================+===========================================+============================================+==========================================+========================================+========================================+
|Given size | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` |
+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+
|Resulting image | .. image:: ../example/image_thumbnail.png | .. image:: ../example/imageops_contain.png | .. image:: ../example/imageops_cover.png | .. image:: ../example/imageops_fit.png | .. image:: ../example/imageops_pad.png |
+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+
|Resulting size | ``100×100`` | ``100×100`` | ``150×150`` | ``100×150`` | ``100×150`` |
+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+
.. _color-transforms:
Color transforms
@ -568,7 +599,7 @@ Controlling the decoder
Some decoders allow you to manipulate the image while reading it from a file.
This can often be used to speed up decoding when creating thumbnails (when
speed is usually more important than quality) and printing to a monochrome
laser printer (when only a greyscale version of the image is needed).
laser printer (when only a grayscale version of the image is needed).
The :py:meth:`~PIL.Image.Image.draft` method manipulates an opened but not yet
loaded image so it as closely as possible matches the given mode and size. This

View File

@ -26,14 +26,14 @@ Pillow decodes files in two stages:
it.
An image plugin should contain a format handler derived from the
:py:class:`PIL.ImageFile.ImageFile` base class. This class should
provide an ``_open`` method, which reads the file header and
sets up at least the :py:attr:`~PIL.Image.Image.mode` and
:py:attr:`~PIL.Image.Image.size` attributes. To be able to load the
file, the method must also create a list of ``tile`` descriptors,
which contain a decoder name, extents of the tile, and
any decoder-specific data. The format handler class must be explicitly
registered, via a call to the :py:mod:`~PIL.Image` module.
:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an
``_open`` method, which reads the file header and set at least the internal
``_size`` and ``_mode`` attributes so that :py:attr:`~PIL.Image.Image.mode` and
:py:attr:`~PIL.Image.Image.size` are populated. To be able to load the file,
the method must also create a list of ``tile`` descriptors, which contain a
decoder name, extents of the tile, and any decoder-specific data. The format
handler class must be explicitly registered, via a call to the
:py:mod:`~PIL.Image` module.
.. note:: For performance reasons, it is important that the
``_open`` method quickly rejects files that do not have the
@ -45,7 +45,7 @@ Example
The following plugin supports a simple format, which has a 128-byte header
consisting of the words “SPAM” followed by the width, height, and pixel size in
bits. The header fields are separated by spaces. The image data follows
directly after the header, and can be either bi-level, greyscale, or 24-bit
directly after the header, and can be either bi-level, grayscale, or 24-bit
true color.
**SpamImagePlugin.py**::
@ -96,13 +96,13 @@ true color.
)
The format handler must always set the
:py:attr:`~PIL.Image.Image.size` and :py:attr:`~PIL.Image.Image.mode`
attributes. If these are not set, the file cannot be opened. To
simplify the plugin, the calling code considers exceptions like
:py:exc:`SyntaxError`, :py:exc:`KeyError`, :py:exc:`IndexError`,
:py:exc:`EOFError` and :py:exc:`struct.error` as a failure to identify
the file.
The format handler must always set the internal ``_size`` and ``_mode``
attributes so that :py:attr:`~PIL.Image.Image.size` and
:py:attr:`~PIL.Image.Image.mode` are populated. If these are not set, the file
cannot be opened. To simplify the plugin, the calling code considers exceptions
like :py:exc:`SyntaxError`, :py:exc:`KeyError`, :py:exc:`IndexError`,
:py:exc:`EOFError` and :py:exc:`struct.error` as a failure to identify the
file.
Note that the image plugin must be explicitly registered using
:py:func:`PIL.Image.register_open`. Although not required, it is also a good
@ -211,9 +211,9 @@ table describes some commonly used **raw modes**:
| ``1;R`` | | 1-bit reversed bilevel, stored with the leftmost pixel in the |
| | | least significant bit. 0 means black, 1 means white. |
+-----------+-------------------------------------------------------------------+
| ``L`` | 8-bit greyscale. 0 means black, 255 means white. |
| ``L`` | 8-bit grayscale. 0 means black, 255 means white. |
+-----------+-------------------------------------------------------------------+
| ``L;I`` | 8-bit inverted greyscale. 0 means white, 255 means black. |
| ``L;I`` | 8-bit inverted grayscale. 0 means white, 255 means black. |
+-----------+-------------------------------------------------------------------+
| ``P`` | 8-bit palette-mapped image. |
+-----------+-------------------------------------------------------------------+

View File

@ -182,7 +182,7 @@ Many of Pillow's features require external libraries:
* **libimagequant** provides improved color quantization
* Pillow has been tested with libimagequant **2.6-4.2.1**
* Pillow has been tested with libimagequant **2.6-4.2.2**
* Libimagequant is licensed GPLv3, which is more restrictive than
the Pillow license, therefore we will not be distributing binaries
with libimagequant support enabled.
@ -496,93 +496,93 @@ These platforms have been reported to work at the versions mentioned.
Contributors please test Pillow on your platform then update this
document and send a pull request.
+----------------------------------+---------------------------+------------------+--------------+
| Operating system | | Tested Python | | Latest tested | | Tested |
| | | versions | | Pillow version | | processors |
+==================================+===========================+==================+==============+
| macOS 14 Sonoma | 3.8, 3.9, 3.10, 3.11 | 10.0.1 |arm |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 13 Ventura | 3.8, 3.9, 3.10, 3.11 | 10.0.1 |arm |
| +---------------------------+------------------+ |
| | 3.7 | 9.5.0 | |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 12 Monterey | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |arm |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm |
| +---------------------------+------------------+--------------+
| | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.4.0 |x86-64 |
| +---------------------------+------------------+ |
| | 3.6 | 8.4.0 | |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 10.15 Catalina | 3.6, 3.7, 3.8, 3.9 | 8.3.2 |x86-64 |
| +---------------------------+------------------+ |
| | 3.5 | 7.2.0 | |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 10.14 Mojave | 3.5, 3.6, 3.7, 3.8 | 7.2.0 |x86-64 |
| +---------------------------+------------------+ |
| | 2.7 | 6.0.0 | |
| +---------------------------+------------------+ |
| | 3.4 | 5.4.1 | |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 10.13 High Sierra | 2.7, 3.4, 3.5, 3.6 | 4.2.1 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 10.12 Sierra | 2.7, 3.4, 3.5, 3.6 | 4.1.1 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Mac OS X 10.11 El Capitan | 2.7, 3.4, 3.5, 3.6, 3.7 | 5.4.1 |x86-64 |
| +---------------------------+------------------+ |
| | 3.3 | 4.1.0 | |
+----------------------------------+---------------------------+------------------+--------------+
| Mac OS X 10.9 Mavericks | 2.7, 3.2, 3.3, 3.4 | 3.0.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Mac OS X 10.8 Mountain Lion | 2.6, 2.7, 3.2, 3.3 | |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Redhat Linux 6 | 2.6 | |x86 |
+----------------------------------+---------------------------+------------------+--------------+
| CentOS 6.3 | 2.7, 3.3 | |x86 |
+----------------------------------+---------------------------+------------------+--------------+
| CentOS 8 | 3.9 | 9.0.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Fedora 23 | 2.7, 3.4 | 3.1.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Ubuntu Linux 12.04 LTS (Precise) | | 2.6, 3.2, 3.3, 3.4, 3.5 | 3.4.1 |x86,x86-64 |
| | | PyPy5.3.1, PyPy3 v2.4.0 | | |
| +---------------------------+------------------+--------------+
| | 2.7 | 4.3.0 |x86-64 |
| +---------------------------+------------------+--------------+
| | 2.7, 3.2 | 3.4.1 |ppc |
+----------------------------------+---------------------------+------------------+--------------+
| Ubuntu Linux 10.04 LTS (Lucid) | 2.6 | 2.3.0 |x86,x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Debian 8.2 Jessie | 2.7, 3.4 | 3.1.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Raspbian Jessie | 2.7, 3.4 | 3.1.0 |arm |
+----------------------------------+---------------------------+------------------+--------------+
| Raspbian Stretch | 2.7, 3.5 | 4.0.0 |arm |
+----------------------------------+---------------------------+------------------+--------------+
| Raspberry Pi OS | 3.6, 3.7, 3.8, 3.9 | 8.2.0 |arm |
| +---------------------------+------------------+ |
| | 2.7 | 6.2.2 | |
+----------------------------------+---------------------------+------------------+--------------+
| Gentoo Linux | 2.7, 3.2 | 2.1.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| FreeBSD 11.1 | 2.7, 3.4, 3.5, 3.6 | 4.3.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| FreeBSD 10.3 | 2.7, 3.4, 3.5 | 4.2.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| FreeBSD 10.2 | 2.7, 3.4 | 3.1.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Windows 10 | 3.7 | 7.1.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Windows 10/Cygwin 3.3 | 3.6, 3.7, 3.8, 3.9 | 8.4.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Windows 8.1 Pro | 2.6, 2.7, 3.2, 3.3, 3.4 | 2.4.0 |x86,x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Windows 8 Pro | 2.6, 2.7, 3.2, 3.3, 3.4a3 | 2.2.0 |x86,x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Windows 7 Professional | 3.7 | 7.0.0 |x86,x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Windows Server 2008 R2 Enterprise| 3.3 | |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
+----------------------------------+----------------------------+------------------+--------------+
| Operating system | | Tested Python | | Latest tested | | Tested |
| | | versions | | Pillow version | | processors |
+==================================+============================+==================+==============+
| macOS 14 Sonoma | 3.8, 3.9, 3.10, 3.11, 3.12 | 10.1.0 |arm |
+----------------------------------+----------------------------+------------------+--------------+
| macOS 13 Ventura | 3.8, 3.9, 3.10, 3.11 | 10.0.1 |arm |
| +----------------------------+------------------+ |
| | 3.7 | 9.5.0 | |
+----------------------------------+----------------------------+------------------+--------------+
| macOS 12 Monterey | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |arm |
+----------------------------------+----------------------------+------------------+--------------+
| macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm |
| +----------------------------+------------------+--------------+
| | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.4.0 |x86-64 |
| +----------------------------+------------------+ |
| | 3.6 | 8.4.0 | |
+----------------------------------+----------------------------+------------------+--------------+
| macOS 10.15 Catalina | 3.6, 3.7, 3.8, 3.9 | 8.3.2 |x86-64 |
| +----------------------------+------------------+ |
| | 3.5 | 7.2.0 | |
+----------------------------------+----------------------------+------------------+--------------+
| macOS 10.14 Mojave | 3.5, 3.6, 3.7, 3.8 | 7.2.0 |x86-64 |
| +----------------------------+------------------+ |
| | 2.7 | 6.0.0 | |
| +----------------------------+------------------+ |
| | 3.4 | 5.4.1 | |
+----------------------------------+----------------------------+------------------+--------------+
| macOS 10.13 High Sierra | 2.7, 3.4, 3.5, 3.6 | 4.2.1 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| macOS 10.12 Sierra | 2.7, 3.4, 3.5, 3.6 | 4.1.1 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Mac OS X 10.11 El Capitan | 2.7, 3.4, 3.5, 3.6, 3.7 | 5.4.1 |x86-64 |
| +----------------------------+------------------+ |
| | 3.3 | 4.1.0 | |
+----------------------------------+----------------------------+------------------+--------------+
| Mac OS X 10.9 Mavericks | 2.7, 3.2, 3.3, 3.4 | 3.0.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Mac OS X 10.8 Mountain Lion | 2.6, 2.7, 3.2, 3.3 | |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Redhat Linux 6 | 2.6 | |x86 |
+----------------------------------+----------------------------+------------------+--------------+
| CentOS 6.3 | 2.7, 3.3 | |x86 |
+----------------------------------+----------------------------+------------------+--------------+
| CentOS 8 | 3.9 | 9.0.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Fedora 23 | 2.7, 3.4 | 3.1.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Ubuntu Linux 12.04 LTS (Precise) | | 2.6, 3.2, 3.3, 3.4, 3.5 | 3.4.1 |x86,x86-64 |
| | | PyPy5.3.1, PyPy3 v2.4.0 | | |
| +----------------------------+------------------+--------------+
| | 2.7 | 4.3.0 |x86-64 |
| +----------------------------+------------------+--------------+
| | 2.7, 3.2 | 3.4.1 |ppc |
+----------------------------------+----------------------------+------------------+--------------+
| Ubuntu Linux 10.04 LTS (Lucid) | 2.6 | 2.3.0 |x86,x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Debian 8.2 Jessie | 2.7, 3.4 | 3.1.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Raspbian Jessie | 2.7, 3.4 | 3.1.0 |arm |
+----------------------------------+----------------------------+------------------+--------------+
| Raspbian Stretch | 2.7, 3.5 | 4.0.0 |arm |
+----------------------------------+----------------------------+------------------+--------------+
| Raspberry Pi OS | 3.6, 3.7, 3.8, 3.9 | 8.2.0 |arm |
| +----------------------------+------------------+ |
| | 2.7 | 6.2.2 | |
+----------------------------------+----------------------------+------------------+--------------+
| Gentoo Linux | 2.7, 3.2 | 2.1.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| FreeBSD 11.1 | 2.7, 3.4, 3.5, 3.6 | 4.3.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| FreeBSD 10.3 | 2.7, 3.4, 3.5 | 4.2.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| FreeBSD 10.2 | 2.7, 3.4 | 3.1.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Windows 10 | 3.7 | 7.1.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Windows 10/Cygwin 3.3 | 3.6, 3.7, 3.8, 3.9 | 8.4.0 |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Windows 8.1 Pro | 2.6, 2.7, 3.2, 3.3, 3.4 | 2.4.0 |x86,x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Windows 8 Pro | 2.6, 2.7, 3.2, 3.3, 3.4a3 | 2.2.0 |x86,x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Windows 7 Professional | 3.7 | 7.0.0 |x86,x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
| Windows Server 2008 R2 Enterprise| 3.3 | |x86-64 |
+----------------------------------+----------------------------+------------------+--------------+
Old Versions
------------

View File

@ -59,7 +59,7 @@ Functions
.. py:method:: getcolor(color, mode)
Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a
greyscale value if the mode is not color or a palette image. If the string
grayscale value if the mode is not color or a palette image. If the string
cannot be parsed, this function raises a :py:exc:`ValueError` exception.
.. versionadded:: 1.1.4

View File

@ -351,7 +351,7 @@ Methods
Draw a shape.
.. 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, embedded_color=False)
.. 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, embedded_color=False, font_size=None)
Draws the string at the given position.
@ -416,8 +416,14 @@ Methods
.. versionadded:: 8.0.0
:param font_size: If ``font`` is not provided, then the size to use for the default
font.
Keyword-only argument.
.. py:method:: ImageDraw.multiline_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, embedded_color=False)
.. versionadded:: 10.1.0
.. py:method:: ImageDraw.multiline_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, embedded_color=False, font_size=None)
Draws the string at the given position.
@ -477,7 +483,13 @@ Methods
.. versionadded:: 8.0.0
.. py:method:: ImageDraw.textlength(text, font=None, direction=None, features=None, language=None, embedded_color=False)
:param font_size: If ``font`` is not provided, then the size to use for the default
font.
Keyword-only argument.
.. versionadded:: 10.1.0
.. py:method:: ImageDraw.textlength(text, font=None, direction=None, features=None, language=None, embedded_color=False, font_size=None)
Returns length (in pixels with 1/64 precision) of given text when rendered
in font with provided direction, features, and language.
@ -538,9 +550,15 @@ Methods
It should be a `BCP 47 language code`_.
Requires libraqm.
:param embedded_color: Whether to use font embedded color glyphs (COLR, CBDT, SBIX).
:param font_size: If ``font`` is not provided, then the size to use for the default
font.
Keyword-only argument.
.. versionadded:: 10.1.0
:return: Either width for horizontal text, or height for vertical text.
.. py:method:: ImageDraw.textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False)
.. py:method:: ImageDraw.textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False, font_size=None)
Returns bounding box (in pixels) of given text relative to given anchor
when rendered in font with provided direction, features, and language.
@ -588,9 +606,15 @@ Methods
Requires libraqm.
:param stroke_width: The width of the text stroke.
:param embedded_color: Whether to use font embedded color glyphs (COLR, CBDT, SBIX).
:param font_size: If ``font`` is not provided, then the size to use for the default
font.
Keyword-only argument.
.. versionadded:: 10.1.0
:return: ``(left, top, right, bottom)`` bounding box
.. py:method:: ImageDraw.multiline_textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False)
.. py:method:: ImageDraw.multiline_textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False, font_size=None)
Returns bounding box (in pixels) of given text relative to given anchor
when rendered in font with provided direction, features, and language.
@ -632,6 +656,12 @@ Methods
Requires libraqm.
:param stroke_width: The width of the text stroke.
:param embedded_color: Whether to use font embedded color glyphs (COLR, CBDT, SBIX).
:param font_size: If ``font`` is not provided, then the size to use for the default
font.
Keyword-only argument.
.. versionadded:: 10.1.0
:return: ``(left, top, right, bottom)`` bounding box
.. py:method:: getdraw(im=None, hints=None)

View File

@ -58,7 +58,7 @@ method:
This class can be used to control the contrast of an image, similar to the
contrast control on a TV set. An
:ref:`enhancement factor <enhancement-factor>` of 0.0 gives a solid grey
:ref:`enhancement factor <enhancement-factor>` of 0.0 gives a solid gray
image, a factor of 1.0 gives the original image, and greater values
increase the contrast of the image.

View File

@ -72,7 +72,7 @@ pixel bits.
Note that the operands are converted to 32-bit signed integers before the
bitwise operation is applied. This means that youll get negative values if
you invert an ordinary greyscale image. You can use the and (&) operator to
you invert an ordinary grayscale image. You can use the and (&) operator to
mask off unwanted bits.
Bitwise operators dont work on floating point images.

View File

@ -12,14 +12,11 @@ only work on L and RGB images.
.. autofunction:: autocontrast
.. autofunction:: colorize
.. autofunction:: contain
.. autofunction:: pad
.. autofunction:: crop
.. autofunction:: scale
.. autofunction:: deform
.. autofunction:: equalize
.. autofunction:: expand
.. autofunction:: fit
.. autofunction:: flip
.. autofunction:: grayscale
.. autofunction:: invert
@ -27,3 +24,38 @@ only work on L and RGB images.
.. autofunction:: posterize
.. autofunction:: solarize
.. autofunction:: exif_transpose
.. _relative-resize:
Resize relative to a given size
-------------------------------
::
from PIL import Image, ImageOps
size = (100, 150)
with Image.open("Tests/images/hopper.png") as im:
ImageOps.contain(im, size).save("imageops_contain.png")
ImageOps.cover(im, size).save("imageops_cover.png")
ImageOps.fit(im, size).save("imageops_fit.png")
ImageOps.pad(im, size, color="#f00").save("imageops_pad.png")
# thumbnail() can also be used,
# but will modify the image object in place
im.thumbnail(size)
im.save("imageops_thumbnail.png")
+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+
| | :py:meth:`~PIL.Image.Image.thumbnail` | :py:meth:`~PIL.ImageOps.contain` | :py:meth:`~PIL.ImageOps.cover` | :py:meth:`~PIL.ImageOps.fit` | :py:meth:`~PIL.ImageOps.pad` |
+================+===========================================+============================================+==========================================+========================================+========================================+
|Given size | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` |
+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+
|Resulting image | .. image:: ../example/image_thumbnail.png | .. image:: ../example/imageops_contain.png | .. image:: ../example/imageops_cover.png | .. image:: ../example/imageops_fit.png | .. image:: ../example/imageops_pad.png |
+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+
|Resulting size | ``100×100`` | ``100×100`` | ``150×150`` | ``100×150`` | ``100×150`` |
+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+
.. autofunction:: contain
.. autofunction:: cover
.. autofunction:: fit
.. autofunction:: pad

View File

@ -1,8 +1,8 @@
10.1.0
------
Backwards Incompatible Changes
==============================
API Changes
===========
Setting image mode
^^^^^^^^^^^^^^^^^^
@ -13,9 +13,6 @@ not about removing existing functionality, but instead about raising an
explicit error to prevent later consequences. The ``convert`` method is the
correct way to change an image's mode.
API Changes
===========
Accept a list in getpixel()
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -60,9 +57,45 @@ channel, a palette with an alpha channel, or a "transparency" key in the
Even if this attribute is true, the image might still appear solid, if all of
the values shown within are opaque.
ImageOps.cover
^^^^^^^^^^^^^^
Returns a resized version of the image, so that the requested size is covered,
while maintaining the original aspect ratio.
See :ref:`relative-resize` for a comparison between this and similar ``ImageOps``
methods.
size and font_size arguments when using default font
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Pillow has had a "better than nothing" default font, which can only be drawn at
one font size. Now, if FreeType support is available, a version of
`Aileron Regular <https://dotcolon.net/font/aileron>`_ is loaded, which can be
drawn at chosen font sizes.
The following ``size`` and ``font_size`` arguments can now be used to specify a
font size for this new builtin font::
ImageFont.load_default(size=24)
draw.text((0, 0), "test", font_size=24)
draw.textlength((0, 0), "test", font_size=24)
draw.textbbox((0, 0), "test", font_size=24)
draw.multiline_text((0, 0), "test", font_size=24)
draw.multiline_textbbox((0, 0), "test", font_size=24)
Other Changes
=============
Python 3.12
^^^^^^^^^^^
Pillow 10.0.0 had wheels built against Python 3.12 beta, available as a preview to help
others prepare for 3.12, and to ensure Pillow could be used immediately at the release
of 3.12.0 final (2023-10-02, :pep:`693`).
Pillow 10.1.0 now officially supports Python 3.12.
Added support for DDS BC5U and 8-bit color indexed images
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -29,10 +29,10 @@ API Additions
Image.entropy
^^^^^^^^^^^^^
Calculates and returns the entropy for the image. A bilevel image (mode "1") is treated
as a greyscale ("L") image by this method. If a mask is provided, the method employs
as a grayscale ("L") image by this method. If a mask is provided, the method employs
the histogram for those parts of the image where the mask image is non-zero. The mask
image must have the same size as the image, and be either a bi-level image (mode "1") or
a greyscale image ("L").
a grayscale image ("L").
ImageGrab.grab
^^^^^^^^^^^^^^

View File

@ -230,21 +230,21 @@ class BmpImageFile(ImageFile.ImageFile):
else:
padding = file_info["palette_padding"]
palette = read(padding * file_info["colors"])
greyscale = True
grayscale = True
indices = (
(0, 255)
if file_info["colors"] == 2
else list(range(file_info["colors"]))
)
# ----------------- Check if greyscale and ignore palette if so
# ----------------- Check if grayscale and ignore palette if so
for ind, val in enumerate(indices):
rgb = palette[ind * padding : ind * padding + 3]
if rgb != o8(val) * 3:
greyscale = False
grayscale = False
# ------- If all colors are grey, white or black, ditch palette
if greyscale:
# ------- If all colors are gray, white or black, ditch palette
if grayscale:
self._mode = "1" if file_info["colors"] == 2 else "L"
raw_mode = self.mode
else:

View File

@ -150,7 +150,8 @@ class FliImageFile(ImageFile.ImageFile):
s = self.fp.read(4)
if not s:
raise EOFError
msg = "missing frame size"
raise EOFError(msg)
framesize = i32(s)

View File

@ -183,7 +183,8 @@ class GifImageFile(ImageFile.ImageFile):
s = self.fp.read(1)
if not s or s == b";":
raise EOFError
msg = "no more images in GIF file"
raise EOFError(msg)
palette = None
@ -288,7 +289,8 @@ class GifImageFile(ImageFile.ImageFile):
if interlace is None:
# self._fp = None
raise EOFError
msg = "image not found in GIF frame"
raise EOFError(msg)
self.__frame = frame
if not update_image:

View File

@ -888,12 +888,12 @@ class Image:
"L", "RGB" and "CMYK". The ``matrix`` argument only supports "L"
and "RGB".
When translating a color image to greyscale (mode "L"),
When translating a color image to grayscale (mode "L"),
the library uses the ITU-R 601-2 luma transform::
L = R * 299/1000 + G * 587/1000 + B * 114/1000
The default method of converting a greyscale ("L") or "RGB"
The default method of converting a grayscale ("L") or "RGB"
image into a bilevel (mode "1") image uses Floyd-Steinberg
dither to approximate the original image luminosity levels. If
dither is ``None``, all values larger than 127 are set to 255 (white),
@ -943,9 +943,9 @@ class Image:
msg = "illegal conversion"
raise ValueError(msg)
im = self.im.convert_matrix(mode, matrix)
new = self._new(im)
new_im = self._new(im)
if has_transparency and self.im.bands == 3:
transparency = new.info["transparency"]
transparency = new_im.info["transparency"]
def convert_transparency(m, v):
v = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5
@ -958,8 +958,8 @@ class Image:
convert_transparency(matrix[i * 4 : i * 4 + 4], transparency)
for i in range(0, len(transparency))
)
new.info["transparency"] = transparency
return new
new_im.info["transparency"] = transparency
return new_im
if mode == "P" and self.mode == "RGBA":
return self.quantize(colors)
@ -990,7 +990,7 @@ class Image:
else:
# get the new transparency color.
# use existing conversions
trns_im = Image()._new(core.new(self.mode, (1, 1)))
trns_im = new(self.mode, (1, 1))
if self.mode == "P":
trns_im.putpalette(self.palette)
if isinstance(t, tuple):
@ -1031,23 +1031,25 @@ class Image:
if mode == "P" and palette == Palette.ADAPTIVE:
im = self.im.quantize(colors)
new = self._new(im)
new_im = self._new(im)
from . import ImagePalette
new.palette = ImagePalette.ImagePalette("RGB", new.im.getpalette("RGB"))
new_im.palette = ImagePalette.ImagePalette(
"RGB", new_im.im.getpalette("RGB")
)
if delete_trns:
# This could possibly happen if we requantize to fewer colors.
# The transparency would be totally off in that case.
del new.info["transparency"]
del new_im.info["transparency"]
if trns is not None:
try:
new.info["transparency"] = new.palette.getcolor(trns, new)
new_im.info["transparency"] = new_im.palette.getcolor(trns, new_im)
except Exception:
# if we can't make a transparent color, don't leave the old
# transparency hanging around to mess us up.
del new.info["transparency"]
del new_im.info["transparency"]
warnings.warn("Couldn't allocate palette entry for transparency")
return new
return new_im
if "LAB" in (self.mode, mode):
other_mode = mode if self.mode == "LAB" else self.mode
@ -1246,7 +1248,7 @@ class Image:
Configures the image file loader so it returns a version of the
image that as closely as possible matches the given mode and
size. For example, you can use this method to convert a color
JPEG to greyscale while loading it.
JPEG to grayscale while loading it.
If any changes are made, returns a tuple with the chosen ``mode`` and
``box`` with coordinates of the original image within the altered one.
@ -1618,13 +1620,13 @@ class Image:
than one band, the histograms for all bands are concatenated (for
example, the histogram for an "RGB" image contains 768 values).
A bilevel image (mode "1") is treated as a greyscale ("L") image
A bilevel image (mode "1") is treated as a grayscale ("L") image
by this method.
If a mask is provided, the method returns a histogram for those
parts of the image where the mask image is non-zero. The mask
image must have the same size as the image, and be either a
bi-level image (mode "1") or a greyscale image ("L").
bi-level image (mode "1") or a grayscale image ("L").
:param mask: An optional mask.
:param extrema: An optional tuple of manually-specified extrema.
@ -1644,13 +1646,13 @@ class Image:
"""
Calculates and returns the entropy for the image.
A bilevel image (mode "1") is treated as a greyscale ("L")
A bilevel image (mode "1") is treated as a grayscale ("L")
image by this method.
If a mask is provided, the method employs the histogram for
those parts of the image where the mask image is non-zero.
The mask image must have the same size as the image, and be
either a bi-level image (mode "1") or a greyscale image ("L").
either a bi-level image (mode "1") or a grayscale image ("L").
:param mask: An optional mask.
:param extrema: An optional tuple of manually-specified extrema.
@ -1870,7 +1872,8 @@ class Image:
# do things the hard way
im = self.im.convert(mode)
if im.mode not in ("LA", "PA", "RGBA"):
raise ValueError from e # sanity check
msg = "alpha channel could not be added"
raise ValueError(msg) from e # sanity check
self.im = im
self.pyaccess = None
self._mode = self.im.mode
@ -2475,7 +2478,8 @@ class Image:
# overridden by file handlers
if frame != 0:
raise EOFError
msg = "no more images in file"
raise EOFError(msg)
def show(self, title=None):
"""
@ -2882,7 +2886,7 @@ class ImageTransformHandler:
def _wedge():
"""Create greyscale wedge (for debugging only)"""
"""Create grayscale wedge (for debugging only)"""
return Image()._new(core.wedge("L"))
@ -3759,6 +3763,7 @@ class Exif(MutableMapping):
self.endian = self._info._endian
if offset is None:
offset = self._info.next
self.fp.tell()
self.fp.seek(offset)
self._info.load(self.fp)

View File

@ -19,7 +19,7 @@ from . import Image
def constant(image, value):
"""Fill a channel with a given grey level.
"""Fill a channel with a given gray level.
:rtype: :py:class:`~PIL.Image.Image`
"""

View File

@ -124,7 +124,7 @@ def getcolor(color, mode):
"""
Same as :py:func:`~PIL.ImageColor.getrgb` for most modes. However, if
``mode`` is HSV, converts the RGB value to a HSV value, or if ``mode`` is
not color or a palette image, converts the RGB value to a greyscale value.
not color or a palette image, converts the RGB value to a grayscale value.
If the string cannot be parsed, this function raises a :py:exc:`ValueError`
exception.

View File

@ -113,6 +113,15 @@ class ImageDraw:
self.font = ImageFont.load_default()
return self.font
def _getfont(self, font_size):
if font_size is not None:
from . import ImageFont
font = ImageFont.load_default(font_size)
else:
font = self.getfont()
return font
def _getink(self, ink, fill=None):
if ink is None and fill is None:
if self.fill:
@ -456,6 +465,13 @@ class ImageDraw:
**kwargs,
):
"""Draw text."""
if embedded_color and self.mode not in ("RGB", "RGBA"):
msg = "Embedded color supported only in RGB and RGBA modes"
raise ValueError(msg)
if font is None:
font = self._getfont(kwargs.get("font_size"))
if self._multiline_check(text):
return self.multiline_text(
xy,
@ -473,13 +489,6 @@ class ImageDraw:
embedded_color,
)
if embedded_color and self.mode not in ("RGB", "RGBA"):
msg = "Embedded color supported only in RGB and RGBA modes"
raise ValueError(msg)
if font is None:
font = self.getfont()
def getink(fill):
ink, fill = self._getink(fill)
if ink is None:
@ -570,6 +579,8 @@ class ImageDraw:
stroke_width=0,
stroke_fill=None,
embedded_color=False,
*,
font_size=None,
):
if direction == "ttb":
msg = "ttb direction is unsupported for multiline text"
@ -584,6 +595,9 @@ class ImageDraw:
msg = "anchor not supported for multiline text"
raise ValueError(msg)
if font is None:
font = self._getfont(font_size)
widths = []
max_width = 0
lines = self._multiline_split(text)
@ -645,6 +659,8 @@ class ImageDraw:
features=None,
language=None,
embedded_color=False,
*,
font_size=None,
):
"""Get the length of a given string, in pixels with 1/64 precision."""
if self._multiline_check(text):
@ -655,7 +671,7 @@ class ImageDraw:
raise ValueError(msg)
if font is None:
font = self.getfont()
font = self._getfont(font_size)
mode = "RGBA" if embedded_color else self.fontmode
return font.getlength(text, mode, direction, features, language)
@ -672,12 +688,17 @@ class ImageDraw:
language=None,
stroke_width=0,
embedded_color=False,
*,
font_size=None,
):
"""Get the bounding box of a given string, in pixels."""
if embedded_color and self.mode not in ("RGB", "RGBA"):
msg = "Embedded color supported only in RGB and RGBA modes"
raise ValueError(msg)
if font is None:
font = self._getfont(font_size)
if self._multiline_check(text):
return self.multiline_textbbox(
xy,
@ -693,8 +714,6 @@ class ImageDraw:
embedded_color,
)
if font is None:
font = self.getfont()
mode = "RGBA" if embedded_color else self.fontmode
bbox = font.getbbox(
text, mode, direction, features, language, stroke_width, anchor
@ -714,6 +733,8 @@ class ImageDraw:
language=None,
stroke_width=0,
embedded_color=False,
*,
font_size=None,
):
if direction == "ttb":
msg = "ttb direction is unsupported for multiline text"
@ -728,6 +749,9 @@ class ImageDraw:
msg = "anchor not supported for multiline text"
raise ValueError(msg)
if font is None:
font = self._getfont(font_size)
widths = []
max_width = 0
lines = self._multiline_split(text)

View File

@ -59,7 +59,7 @@ class Contrast(_Enhance):
This class can be used to control the contrast of an image, similar
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 gray image. A factor of 1.0 gives the original image.
"""
def __init__(self, image):

View File

@ -201,8 +201,8 @@ class ImageFile(Image.Image):
with open(self.filename) as fp:
self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
if offset + self.size[1] * args[1] > self.map.size():
# buffer is not large enough
raise OSError
msg = "buffer is not large enough"
raise OSError(msg)
self.im = Image.core.map_buffer(
self.map, self.size, decoder_name, offset, args
)
@ -691,7 +691,8 @@ class PyDecoder(PyCodec):
If finished with decoding return -1 for the bytes consumed.
Err codes are from :data:`.ImageFile.ERRORS`.
"""
raise NotImplementedError()
msg = "unavailable in base decoder"
raise NotImplementedError(msg)
def set_as_raw(self, data, rawmode=None):
"""
@ -740,7 +741,8 @@ class PyEncoder(PyCodec):
If finished with encoding return 1 for the error code.
Err codes are from :data:`.ImageFile.ERRORS`.
"""
raise NotImplementedError()
msg = "unavailable in base encoder"
raise NotImplementedError(msg)
def encode_to_pyfd(self):
"""

View File

@ -861,19 +861,258 @@ def load_path(filename):
raise OSError(msg)
def load_default():
"""Load a "better than nothing" default font.
def load_default(size=None):
"""If FreeType support is available, load a version of Aileron Regular,
https://dotcolon.net/font/aileron, with a more limited character set.
Otherwise, load a "better than nothing" font.
.. versionadded:: 1.1.4
:param size: The font size of Aileron Regular.
.. versionadded:: 10.1.0
:return: A font object.
"""
f = ImageFont()
f._load_pilfont_data(
# courB08
BytesIO(
base64.b64decode(
b"""
if core.__class__.__name__ == "module" or size is not None:
f = truetype(
BytesIO(
base64.b64decode(
b"""
AAEAAAAPAIAAAwBwRkZUTYwDlUAAADFoAAAAHEdERUYAqADnAAAo8AAAACRHUE9ThhmITwAAKfgAA
AduR1NVQnHxefoAACkUAAAA4k9TLzJovoHLAAABeAAAAGBjbWFw5lFQMQAAA6gAAAGqZ2FzcP//AA
MAACjoAAAACGdseWYmRXoPAAAGQAAAHfhoZWFkE18ayQAAAPwAAAA2aGhlYQboArEAAAE0AAAAJGh
tdHjjERZ8AAAB2AAAAdBsb2NhuOexrgAABVQAAADqbWF4cAC7AEYAAAFYAAAAIG5hbWUr+h5lAAAk
OAAAA6Jwb3N0D3oPTQAAJ9wAAAEKAAEAAAABGhxJDqIhXw889QALA+gAAAAA0Bqf2QAAAADhCh2h/
2r/LgOxAyAAAAAIAAIAAAAAAAAAAQAAA8r/GgAAA7j/av9qA7EAAQAAAAAAAAAAAAAAAAAAAHQAAQ
AAAHQAQwAFAAAAAAACAAAAAQABAAAAQAAAAAAAAAADAfoBkAAFAAgCigJYAAAASwKKAlgAAAFeADI
BPgAAAAAFAAAAAAAAAAAAAAcAAAAAAAAAAAAAAABVS1dOAEAAIPsCAwL/GgDIA8oA5iAAAJMAAAAA
AhICsgAAACAAAwH0AAAAAAAAAU0AAADYAAAA8gA5AVMAVgJEAEYCRAA1AuQAKQKOAEAAsAArATsAZ
AE7AB4CMABVAkQAUADc/+EBEgAgANwAJQEv//sCRAApAkQAggJEADwCRAAtAkQAIQJEADkCRAArAk
QAMgJEACwCRAAxANwAJQDc/+ECRABnAkQAUAJEAEQB8wAjA1QANgJ/AB0CcwBkArsALwLFAGQCSwB
kAjcAZALGAC8C2gBkAQgAZAIgADcCYQBkAj8AZANiAGQCzgBkAuEALwJWAGQC3QAvAmsAZAJJADQC
ZAAiAqoAXgJuACADuAAaAnEAGQJFABMCTwAuATMAYgEv//sBJwAiAkQAUAH0ADIBLAApAhMAJAJjA
EoCEQAeAmcAHgIlAB4BIgAVAmcAHgJRAEoA7gA+AOn/8wIKAEoA9wBGA1cASgJRAEoCSgAeAmMASg
JnAB4BSgBKAcsAGAE5ABQCUABCAgIAAQMRAAEB4v/6AgEAAQHOABQBLwBAAPoAYAEvACECRABNA0Y
AJAItAHgBKgAcAkQAUAEsAHQAygAgAi0AOQD3ADYA9wAWAaEANgGhABYCbAAlAYMAeAGDADkA6/9q
AhsAFAIKABUB/QAVAAAAAwAAAAMAAAAcAAEAAAAAAKQAAwABAAAAHAAEAIgAAAAeABAAAwAOAH4Aq
QCrALEAtAC3ALsgGSAdICYgOiBEISL7Av//AAAAIACpAKsAsAC0ALcAuyAYIBwgJiA5IEQhIvsB//
//4/+5/7j/tP+y/7D/reBR4E/gR+A14CzfTwVxAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAEGAAABAAAAAAAAAAECAAAAAgAAAAAAAAAAAAAAAAAAAAEAAAMEBQYHCAkKCwwNDg8QERIT
FBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMT
U5PUFFSU1RVVldYWVpbXF1eX2BhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAA
AAAAAAYnFmAAAAAABlAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2htAAAAAAAAAABrbGlqAAAAAHAAbm9
ycwBnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmACYAJgAmAD4AUgCCAMoBCgFO
AVwBcgGIAaYBvAHKAdYB6AH2AgwCIAJKAogCpgLWAw4DIgNkA5wDugPUA+gD/AQQBEYEogS8BPoFJ
gVSBWoFgAWwBcoF1gX6BhQGJAZMBmgGiga0BuIHGgdUB2YHkAeiB8AH3AfyCAoIHAgqCDoITghcCG
oIogjSCPoJKglYCXwJwgnqCgIKKApACl4Klgq8CtwLDAs8C1YLjAuyC9oL7gwMDCYMSAxgDKAMrAz
qDQoNTA1mDYQNoA2uDcAN2g3oDfYODA4iDkoOXA5sDnoOnA7EDvwAAAAFAAAAAAH0ArwAAwAGAAkA
DAAPAAAxESERAxMhExcRASELARETAfT6qv6syKr+jgFUqsiqArz9RAGLAP/+1P8B/v3VAP8BLP4CA
P8AAgA5//IAuQKyAAMACwAANyMDMwIyFhQGIiY0oE4MZk84JCQ4JLQB/v3AJDgkJDgAAgBWAeUBPA
LfAAMABwAAEyMnMxcjJzOmRgpagkYKWgHl+vr6AAAAAAIARgAAAf4CsgAbAB8AAAEHMxUjByM3Iwc
jNyM1MzcjNTM3MwczNzMHMxUrAQczAZgdZXEvOi9bLzovWmYdZXEvOi9bLzovWp9bHlsBn4w429vb
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
EQQ/BCQvEjQCFQZWFSEWQv40AcxCDiUblhP9uSEAAAAAAAAWAQ4AAQAAAAAAAAATACgAAQAAAAAAA
QAHAEwAAQAAAAAAAgAHAGQAAQAAAAAAAwAaAKIAAQAAAAAABAAHAM0AAQAAAAAABQA8AU8AAQAAAA
AABgAPAawAAQAAAAAACAALAdQAAQAAAAAACQALAfgAAQAAAAAACwAXAjQAAQAAAAAADAAXAnwAAwA
BBAkAAAAmAAAAAwABBAkAAQAOADwAAwABBAkAAgAOAFQAAwABBAkAAwA0AGwAAwABBAkABAAOAL0A
AwABBAkABQB4ANUAAwABBAkABgAeAYwAAwABBAkACAAWAbwAAwABBAkACQAWAeAAAwABBAkACwAuA
gQAAwABBAkADAAuAkwATgBvACAAUgBpAGcAaAB0AHMAIABSAGUAcwBlAHIAdgBlAGQALgAATm8gUm
lnaHRzIFJlc2VydmVkLgAAQQBpAGwAZQByAG8AbgAAQWlsZXJvbgAAUgBlAGcAdQBsAGEAcgAAUmV
ndWxhcgAAMQAuADEAMAAyADsAVQBLAFcATgA7AEEAaQBsAGUAcgBvAG4ALQBSAGUAZwB1AGwAYQBy
AAAxLjEwMjtVS1dOO0FpbGVyb24tUmVndWxhcgAAQQBpAGwAZQByAG8AbgAAQWlsZXJvbgAAVgBlA
HIAcwBpAG8AbgAgADEALgAxADAAMgA7AFAAUwAgADAAMAAxAC4AMQAwADIAOwBoAG8AdABjAG8Abg
B2ACAAMQAuADAALgA3ADAAOwBtAGEAawBlAG8AdABmAC4AbABpAGIAMgAuADUALgA1ADgAMwAyADk
AAFZlcnNpb24gMS4xMDI7UFMgMDAxLjEwMjtob3Rjb252IDEuMC43MDttYWtlb3RmLmxpYjIuNS41
ODMyOQAAQQBpAGwAZQByAG8AbgAtAFIAZQBnAHUAbABhAHIAAEFpbGVyb24tUmVndWxhcgAAUwBvA
HIAYQAgAFMAYQBnAGEAbgBvAABTb3JhIFNhZ2FubwAAUwBvAHIAYQAgAFMAYQBnAGEAbgBvAABTb3
JhIFNhZ2FubwAAaAB0AHQAcAA6AC8ALwB3AHcAdwAuAGQAbwB0AGMAbwBsAG8AbgAuAG4AZQB0AAB
odHRwOi8vd3d3LmRvdGNvbG9uLm5ldAAAaAB0AHQAcAA6AC8ALwB3AHcAdwAuAGQAbwB0AGMAbwBs
AG8AbgAuAG4AZQB0AABodHRwOi8vd3d3LmRvdGNvbG9uLm5ldAAAAAACAAAAAAAA/4MAMgAAAAAAA
AAAAAAAAAAAAAAAAAAAAHQAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATAB
QAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAA
xADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0A
TgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAIsAqQCDAJMAjQDDAKoAtgC3A
LQAtQCrAL4AvwC8AIwAwADBAAAAAAAB//8AAgABAAAADAAAABwAAAACAAIAAwBxAAEAcgBzAAIABA
AAAAIAAAABAAAACgBMAGYAAkRGTFQADmxhdG4AGgAEAAAAAP//AAEAAAAWAANDQVQgAB5NT0wgABZ
ST00gABYAAP//AAEAAAAA//8AAgAAAAEAAmxpZ2EADmxvY2wAFAAAAAEAAQAAAAEAAAACAAYAEAAG
AAAAAgASADQABAAAAAEATAADAAAAAgAQABYAAQAcAAAAAQABAE8AAQABAGcAAQABAE8AAwAAAAIAE
AAWAAEAHAAAAAEAAQAvAAEAAQBnAAEAAQAvAAEAGgABAAgAAgAGAAwAcwACAE8AcgACAEwAAQABAE
kAAAABAAAACgBGAGAAAkRGTFQADmxhdG4AHAAEAAAAAP//AAIAAAABABYAA0NBVCAAFk1PTCAAFlJ
PTSAAFgAA//8AAgAAAAEAAmNwc3AADmtlcm4AFAAAAAEAAAAAAAEAAQACAAYADgABAAAAAQASAAIA
AAACAB4ANgABAAoABQAFAAoAAgABACQAPQAAAAEAEgAEAAAAAQAMAAEAOP/nAAEAAQAkAAIGigAEA
AAFJAXKABoAGQAA//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAD/sv+4/+z/7v/MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9T/6AAAAAD/8QAA
ABD/vQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7gAAAAAAAAAAAAAAAAAA//MAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAAAAAAAAAP/5AAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAD/4AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//L/9AAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAA/+gAAAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/zAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/mAAAAAAAAAAAAAAAAAAD
/4gAA//AAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+AAAAAAAAP/OAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/zv/qAAAAAP/0AAAACAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/ZAAD/egAA/1kAAAAA/5D/rgAAAAAAAAAAAA
AAAAAAAAAAAAAAAAD/9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAD/8AAA/7b/8P+wAAD/8P/E/98AAAAA/8P/+P/0//oAAAAAAAAAAAAA//gA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/w//C/9MAAP/SAAD/9wAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAD/yAAA/+kAAAAA//QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9wAAAAD//QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAP/cAAAAAAAAAAAAAAAA/7YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/6AAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAkAFAAEAAAAAQACwAAABcA
BgAAAAAAAAAIAA4AAAAAAAsAEgAAAAAAAAATABkAAwANAAAAAQAJAAAAAAAAAAAAAAAAAAAAGAAAA
AAABwAAAAAAAAAAAAAAFQAFAAAAAAAYABgAAAAUAAAACgAAAAwAAgAPABEAFgAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAEAEQBdAAYAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAcAAAAAAAAABwAAAAAACAAAAAAAAAAAAAcAAAAHAAAAEwAJ
ABUADgAPAAAACwAQAAAAAAAAAAAAAAAAAAUAGAACAAIAAgAAAAIAGAAXAAAAGAAAABYAFgACABYAA
gAWAAAAEQADAAoAFAAMAA0ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAEgAGAAEAHgAkAC
YAJwApACoALQAuAC8AMgAzADcAOAA5ADoAPAA9AEUASABOAE8AUgBTAFUAVwBZAFoAWwBcAF0AcwA
AAAAAAQAAAADa3tfFAAAAANAan9kAAAAA4QodoQ==
"""
)
),
10 if size is None else size,
layout_engine=Layout.BASIC,
)
else:
f = ImageFont()
f._load_pilfont_data(
# courB08
BytesIO(
base64.b64decode(
b"""
UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
@ -966,12 +1205,12 @@ pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG
AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA////
+QAGAAIAzgAKANUAEw==
"""
)
),
Image.open(
BytesIO(
base64.b64decode(
b"""
)
),
Image.open(
BytesIO(
base64.b64decode(
b"""
iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u
Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9
M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g
@ -996,8 +1235,8 @@ AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v//
Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR
w7IkEbzhVQAAAABJRU5ErkJggg==
"""
)
)
)
),
)
),
)
return f

View File

@ -242,7 +242,7 @@ def contain(image, size, method=Image.Resampling.BICUBIC):
Returns a resized version of the image, set to the maximum width and height
within the requested size, while maintaining the original aspect ratio.
:param image: The image to resize and crop.
:param image: The image to resize.
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: Resampling method to use. Default is
@ -266,6 +266,35 @@ def contain(image, size, method=Image.Resampling.BICUBIC):
return image.resize(size, resample=method)
def cover(image, size, method=Image.Resampling.BICUBIC):
"""
Returns a resized version of the image, so that the requested size is
covered, while maintaining the original aspect ratio.
:param image: The image to resize.
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: Resampling method to use. Default is
:py:attr:`~PIL.Image.Resampling.BICUBIC`.
See :ref:`concept-filters`.
:return: An image.
"""
im_ratio = image.width / image.height
dest_ratio = size[0] / size[1]
if im_ratio != dest_ratio:
if im_ratio < dest_ratio:
new_height = round(image.height / image.width * size[0])
if new_height != size[1]:
size = (size[0], new_height)
else:
new_width = round(image.width / image.height * size[1])
if new_width != size[0]:
size = (new_width, size[1])
return image.resize(size, resample=method)
def pad(image, size, method=Image.Resampling.BICUBIC, color=None, centering=(0.5, 0.5)):
"""
Returns a resized and padded version of the image, expanded to fill the
@ -564,7 +593,7 @@ def solarize(image, threshold=128):
Invert all pixel values above a threshold.
:param image: The image to solarize.
:param threshold: All pixels above this greyscale level are inverted.
:param threshold: All pixels above this grayscale level are inverted.
:return: An image.
"""
lut = []

View File

@ -205,7 +205,8 @@ def make_linear_lut(black, white):
for i in range(256):
lut.append(white * i // 255)
else:
raise NotImplementedError # FIXME
msg = "unavailable when black is non-zero"
raise NotImplementedError(msg) # FIXME
return lut

View File

@ -40,7 +40,8 @@ class Iterator:
self.im.seek(ix)
return self.im
except EOFError as e:
raise IndexError from e # end of sequence
msg = "end of sequence"
raise IndexError(msg) from e
def __iter__(self):
return self
@ -51,7 +52,8 @@ class Iterator:
self.position += 1
return self.im
except EOFError as e:
raise StopIteration from e
msg = "end of sequence"
raise StopIteration(msg) from e
def all_frames(im, func=None):

View File

@ -99,7 +99,8 @@ class Viewer:
Returns the command used to display the file.
Not implemented in the base class.
"""
raise NotImplementedError
msg = "unavailable in base viewer"
raise NotImplementedError(msg)
def save_image(self, image):
"""Save to temporary file and return filename."""

View File

@ -54,9 +54,9 @@ class Dib:
"L", "P", or "RGB".
If the display requires a palette, this constructor creates a suitable
palette and associates it with the image. For an "L" image, 128 greylevels
palette and associates it with the image. For an "L" image, 128 graylevels
are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together
with 20 greylevels.
with 20 graylevels.
To make sure that palettes work properly under Windows, you must call the
``palette`` method upon certain events from Windows.

View File

@ -165,16 +165,25 @@ def APP(self, marker):
except TypeError:
dpi = x_resolution
if math.isnan(dpi):
raise ValueError
msg = "DPI is not a number"
raise ValueError(msg)
if resolution_unit == 3: # cm
# 1 dpcm = 2.54 dpi
dpi *= 2.54
self.info["dpi"] = dpi, dpi
except (TypeError, KeyError, SyntaxError, ValueError, ZeroDivisionError):
# SyntaxError for invalid/unreadable EXIF
except (
struct.error,
KeyError,
SyntaxError,
TypeError,
ValueError,
ZeroDivisionError,
):
# struct.error for truncated EXIF
# KeyError for dpi not included
# ZeroDivisionError for invalid dpi rational value
# SyntaxError for invalid/unreadable EXIF
# ValueError or TypeError for dpi being an invalid float
# ZeroDivisionError for invalid dpi rational value
self.info["dpi"] = 72, 72
@ -711,7 +720,8 @@ def _save(im, fp, filename):
for idx, table in enumerate(qtables):
try:
if len(table) != 64:
raise TypeError
msg = "Invalid quantization table"
raise TypeError(msg)
table = array.array("H", table)
except TypeError as e:
msg = "Invalid quantization table"

View File

@ -109,7 +109,7 @@ class PSDraw:
if im.mode == "1":
dpi = 200 # fax
else:
dpi = 100 # greyscale
dpi = 100 # grayscale
# image size (on paper)
x = im.size[0] * 72 / dpi
y = im.size[1] * 72 / dpi

View File

@ -124,7 +124,7 @@ def _save(im, fp, filename):
if im.encoderinfo.get("bpp") in (1, 2, 4):
# this is 8-bit grayscale, so we shift it to get the high-order bits,
# and invert it because
# Palm does greyscale from white (0) to black (1)
# Palm does grayscale from white (0) to black (1)
bpp = im.encoderinfo["bpp"]
im = im.point(
lambda x, shift=8 - bpp, maxval=(1 << bpp) - 1: maxval - (x >> shift)

View File

@ -91,7 +91,7 @@ class PcxImageFile(ImageFile.ImageFile):
self.fp.seek(-769, io.SEEK_END)
s = self.fp.read(769)
if len(s) == 769 and s[0] == 12:
# check if the palette is linear greyscale
# check if the palette is linear grayscale
for i in range(256):
if s[i * 3 + 1 : i * 3 + 4] != o8(i) * 3:
mode = rawmode = "P"
@ -203,7 +203,7 @@ def _save(im, fp, filename):
palette += b"\x00" * (768 - len(palette))
fp.write(palette) # 768 bytes
elif im.mode == "L":
# greyscale palette
# grayscale palette
fp.write(o8(12))
for i in range(256):
fp.write(o8(i) * 3)

View File

@ -56,7 +56,7 @@ _MAGIC = b"\211PNG\r\n\032\n"
_MODES = {
# supported bits/color combinations, and corresponding modes/rawmodes
# Greyscale
# Grayscale
(1, 0): ("1", "1"),
(2, 0): ("L", "L;2"),
(4, 0): ("L", "L;4"),
@ -70,7 +70,7 @@ _MODES = {
(2, 3): ("P", "P;2"),
(4, 3): ("P", "P;4"),
(8, 3): ("P", "P"),
# Greyscale with alpha
# Grayscale with alpha
(8, 4): ("LA", "LA"),
(16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
# Truecolour with alpha
@ -438,11 +438,12 @@ class PngStream(ChunkStream):
tile = [("zip", (0, 0) + self.im_size, pos, self.im_rawmode)]
self.im_tile = tile
self.im_idat = length
raise EOFError
msg = "image data found"
raise EOFError(msg)
def chunk_IEND(self, pos, length):
# end of PNG image
raise EOFError
msg = "end of PNG image"
raise EOFError(msg)
def chunk_PLTE(self, pos, length):
# palette
@ -891,7 +892,8 @@ class PngImageFile(ImageFile.ImageFile):
self.dispose_extent = self.info.get("bbox")
if not self.tile:
raise EOFError
msg = "image not found in APNG frame"
raise EOFError(msg)
# setup frame disposal (actual disposal done when needed in the next _seek())
if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS:
@ -1105,10 +1107,7 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
if im_frame.mode == rawmode:
im_frame = im_frame.copy()
else:
if rawmode == "P":
im_frame = im_frame.convert(rawmode, palette=im.palette)
else:
im_frame = im_frame.convert(rawmode)
im_frame = im_frame.convert(rawmode)
encoderinfo = im.encoderinfo.copy()
if isinstance(duration, (list, tuple)):
encoderinfo["duration"] = duration[frame_count]
@ -1167,6 +1166,8 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
# default image IDAT (if it exists)
if default_image:
if im.mode != rawmode:
im = im.convert(rawmode)
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
seq_num = 0
@ -1228,11 +1229,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
)
modes = set()
append_images = im.encoderinfo.get("append_images", [])
if default_image:
chain = itertools.chain(append_images)
else:
chain = itertools.chain([im], append_images)
for im_seq in chain:
for im_seq in itertools.chain([im], append_images):
for im_frame in ImageSequence.Iterator(im_seq):
modes.add(im_frame.mode)
for mode in ("RGBA", "RGB", "P"):

View File

@ -1,2 +1,2 @@
# Master version for Pillow
__version__ = "10.1.0.dev0"
__version__ = "10.2.0.dev0"

View File

@ -1575,7 +1575,7 @@ if (PySequence_Check(op)) { \
}
double value;
if (image->bands == 1) {
int bigendian;
int bigendian = 0;
if (image->type == IMAGING_TYPE_SPECIAL) {
// I;16*
bigendian = strcmp(image->mode, "I;16B") == 0;

View File

@ -1013,7 +1013,7 @@ static struct {
static void
p2bit(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
/* FIXME: precalculate greyscale palette? */
/* FIXME: precalculate grayscale palette? */
for (x = 0; x < xsize; x++) {
*out++ = (L(&palette->palette[in[x] * 4]) >= 128000) ? 255 : 0;
}
@ -1022,7 +1022,7 @@ p2bit(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
static void
pa2bit(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
/* FIXME: precalculate greyscale palette? */
/* FIXME: precalculate grayscale palette? */
for (x = 0; x < xsize; x++, in += 4) {
*out++ = (L(&palette->palette[in[0] * 4]) >= 128000) ? 255 : 0;
}
@ -1031,7 +1031,7 @@ pa2bit(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
static void
p2l(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
/* FIXME: precalculate greyscale palette? */
/* FIXME: precalculate grayscale palette? */
for (x = 0; x < xsize; x++) {
*out++ = L24(&palette->palette[in[x] * 4]) >> 16;
}
@ -1040,7 +1040,7 @@ p2l(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
static void
pa2l(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
/* FIXME: precalculate greyscale palette? */
/* FIXME: precalculate grayscale palette? */
for (x = 0; x < xsize; x++, in += 4) {
*out++ = L24(&palette->palette[in[0] * 4]) >> 16;
}
@ -1070,7 +1070,7 @@ p2pa(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
static void
p2la(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
/* FIXME: precalculate greyscale palette? */
/* FIXME: precalculate grayscale palette? */
for (x = 0; x < xsize; x++, out += 4) {
const UINT8 *rgba = &palette->palette[*in++ * 4];
out[0] = out[1] = out[2] = L24(rgba) >> 16;
@ -1081,7 +1081,7 @@ p2la(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
static void
pa2la(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
/* FIXME: precalculate greyscale palette? */
/* FIXME: precalculate grayscale palette? */
for (x = 0; x < xsize; x++, in += 4, out += 4) {
out[0] = out[1] = out[2] = L24(&palette->palette[in[0] * 4]) >> 16;
out[3] = in[3];
@ -1335,9 +1335,9 @@ topalette(
imOut->palette = ImagingPaletteDuplicate(palette);
if (imIn->bands == 1) {
/* greyscale image */
/* grayscale image */
/* Greyscale palette: copy data as is */
/* Grayscale palette: copy data as is */
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++) {
if (alpha) {

View File

@ -142,9 +142,9 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) {
GetSystemPaletteEntries(dib->dc, 0, 256, pal->palPalEntry);
if (strcmp(mode, "L") == 0) {
/* Greyscale DIB. Fill all 236 slots with a greyscale ramp
/* Grayscale DIB. Fill all 236 slots with a grayscale ramp
* (this is usually overkill on Windows since VGA only offers
* 6 bits greyscale resolution). Ignore the slots already
* 6 bits grayscale resolution). Ignore the slots already
* allocated by Windows */
i = 10;
@ -160,7 +160,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) {
#ifdef CUBE216
/* Colour DIB. Create a 6x6x6 colour cube (216 entries) and
* add 20 extra greylevels for best result with greyscale
* add 20 extra graylevels for best result with grayscale
* images. */
i = 10;

View File

@ -549,16 +549,16 @@ static struct {
{"1", "1;IR", 1, pack1IR},
{"1", "L", 8, pack1L},
/* greyscale */
/* grayscale */
{"L", "L", 8, copy1},
{"L", "L;16", 16, packL16},
{"L", "L;16B", 16, packL16B},
/* greyscale w. alpha */
/* grayscale w. alpha */
{"LA", "LA", 16, packLA},
{"LA", "LA;L", 16, packLAL},
/* greyscale w. alpha premultiplied */
/* grayscale w. alpha premultiplied */
{"La", "La", 16, packLA},
/* palette */

View File

@ -75,7 +75,7 @@ ImagingPaletteNewBrowser(void) {
}
palette->size = i;
/* FIXME: add 30-level greyscale wedge here? */
/* FIXME: add 30-level grayscale wedge here? */
return palette;
}

View File

@ -1697,7 +1697,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
image data? */
if (!strcmp(im->mode, "L")) {
/* greyscale */
/* grayscale */
/* FIXME: converting a "L" image to "P" with 256 colors
should be done by a simple copy... */

View File

@ -80,18 +80,18 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
im->palette = ImagingPaletteNew("RGB");
} else if (strcmp(mode, "L") == 0) {
/* 8-bit greyscale (luminance) images */
/* 8-bit grayscale (luminance) images */
im->bands = im->pixelsize = 1;
im->linesize = xsize;
} else if (strcmp(mode, "LA") == 0) {
/* 8-bit greyscale (luminance) with alpha */
/* 8-bit grayscale (luminance) with alpha */
im->bands = 2;
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;
} else if (strcmp(mode, "La") == 0) {
/* 8-bit greyscale (luminance) with premultiplied alpha */
/* 8-bit grayscale (luminance) with premultiplied alpha */
im->bands = 2;
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;

View File

@ -819,7 +819,7 @@ ImagingUnpackXBGR(UINT8 *_out, const UINT8 *in, int pixels) {
static void
unpackRGBALA(UINT8 *_out, const UINT8 *in, int pixels) {
int i;
/* greyscale with alpha */
/* grayscale with alpha */
for (i = 0; i < pixels; i++) {
UINT32 iv = MAKE_UINT32(in[0], in[0], in[0], in[1]);
memcpy(_out, &iv, sizeof(iv));
@ -831,7 +831,7 @@ unpackRGBALA(UINT8 *_out, const UINT8 *in, int pixels) {
static void
unpackRGBALA16B(UINT8 *_out, const UINT8 *in, int pixels) {
int i;
/* 16-bit greyscale with alpha, big-endian */
/* 16-bit grayscale with alpha, big-endian */
for (i = 0; i < pixels; i++) {
UINT32 iv = MAKE_UINT32(in[0], in[0], in[0], in[2]);
memcpy(_out, &iv, sizeof(iv));
@ -1108,7 +1108,7 @@ unpackCMYKI(UINT8 *_out, const UINT8 *in, int pixels) {
/* There are two representations of LAB images for whatever precision:
L: Uint (in PS, it's 0-100)
A: Int (in ps, -128 .. 128, or elsewhere 0..255, with 128 as middle.
Channels in PS display a 0 value as middle grey,
Channels in PS display a 0 value as middle gray,
LCMS appears to use 128 as the 0 value for these channels)
B: Int (as above)
@ -1172,7 +1172,7 @@ unpackI16R_I16(UINT8 *out, const UINT8 *in, int pixels) {
static void
unpackI12_I16(UINT8 *out, const UINT8 *in, int pixels) {
/* Fillorder 1/MSB -> LittleEndian, for 12bit integer greyscale tiffs.
/* Fillorder 1/MSB -> LittleEndian, for 12bit integer grayscale tiffs.
According to the TIFF spec:
@ -1527,7 +1527,7 @@ static struct {
{"1", "1;IR", 1, unpack1IR},
{"1", "1;8", 8, unpack18},
/* greyscale */
/* grayscale */
{"L", "L;2", 2, unpackL2},
{"L", "L;2I", 2, unpackL2I},
{"L", "L;2R", 2, unpackL2R},
@ -1544,11 +1544,11 @@ static struct {
{"L", "L;16", 16, unpackL16},
{"L", "L;16B", 16, unpackL16B},
/* greyscale w. alpha */
/* grayscale w. alpha */
{"LA", "LA", 16, unpackLA},
{"LA", "LA;L", 16, unpackLAL},
/* greyscale w. alpha premultiplied */
/* grayscale w. alpha premultiplied */
{"La", "La", 16, unpackLA},
/* palette */

View File

@ -7,7 +7,7 @@ ARCHIVE_SDIR=pillow-depends-main
FREETYPE_VERSION=2.13.2
HARFBUZZ_VERSION=8.2.1
LIBPNG_VERSION=1.6.40
JPEGTURBO_VERSION=3.0.0
JPEGTURBO_VERSION=3.0.1
OPENJPEG_VERSION=2.5.0
XZ_VERSION=5.4.4
TIFF_VERSION=4.6.0

View File

@ -110,9 +110,9 @@ ARCHITECTURES = {
DEPS = {
"libjpeg": {
"url": SF_PROJECTS
+ "/libjpeg-turbo/files/3.0.0/libjpeg-turbo-3.0.0.tar.gz/download",
"filename": "libjpeg-turbo-3.0.0.tar.gz",
"dir": "libjpeg-turbo-3.0.0",
+ "/libjpeg-turbo/files/3.0.1/libjpeg-turbo-3.0.1.tar.gz/download",
"filename": "libjpeg-turbo-3.0.1.tar.gz",
"dir": "libjpeg-turbo-3.0.1",
"license": ["README.ijg", "LICENSE.md"],
"license_pattern": (
"(LEGAL ISSUES\n============\n\n.+?)\n\nREFERENCES\n=========="