mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
Merge pull request #8032 from nulano/type_hints
Added type hints for PixelAccess related methods and others
This commit is contained in:
commit
d2b5e11d2b
|
@ -89,6 +89,7 @@ $bmp = New-Object Drawing.Bitmap 200, 200
|
|||
p.communicate()
|
||||
|
||||
im = ImageGrab.grabclipboard()
|
||||
assert isinstance(im, list)
|
||||
assert len(im) == 1
|
||||
assert os.path.samefile(im[0], "Tests/images/hopper.gif")
|
||||
|
||||
|
@ -105,6 +106,7 @@ $ms = new-object System.IO.MemoryStream(, $bytes)
|
|||
p.communicate()
|
||||
|
||||
im = ImageGrab.grabclipboard()
|
||||
assert isinstance(im, Image.Image)
|
||||
assert_image_equal_tofile(im, "Tests/images/hopper.png")
|
||||
|
||||
@pytest.mark.skipif(
|
||||
|
@ -120,6 +122,7 @@ $ms = new-object System.IO.MemoryStream(, $bytes)
|
|||
with open(image_path, "rb") as fp:
|
||||
subprocess.call(["wl-copy"], stdin=fp)
|
||||
im = ImageGrab.grabclipboard()
|
||||
assert isinstance(im, Image.Image)
|
||||
assert_image_equal_tofile(im, image_path)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
|
|
|
@ -691,23 +691,7 @@ Methods
|
|||
:param hints: An optional list of hints.
|
||||
:returns: A (drawing context, drawing resource factory) tuple.
|
||||
|
||||
.. py:method:: floodfill(image, xy, value, border=None, thresh=0)
|
||||
|
||||
.. warning:: This method is experimental.
|
||||
|
||||
Fills a bounded region with a given color.
|
||||
|
||||
:param image: Target image.
|
||||
:param xy: Seed position (a 2-item coordinate tuple).
|
||||
:param value: Fill color.
|
||||
:param border: Optional border value. If given, the region consists of
|
||||
pixels with a color different from the border color. If not given,
|
||||
the region consists of pixels having the same color as the seed
|
||||
pixel.
|
||||
:param thresh: Optional threshold value which specifies a maximum
|
||||
tolerable difference of a pixel value from the 'background' in
|
||||
order for it to be replaced. Useful for filling regions of non-
|
||||
homogeneous, but similar, colors.
|
||||
.. autofunction:: PIL.ImageDraw.floodfill
|
||||
|
||||
.. _BCP 47 language code: https://www.w3.org/International/articles/language-tags/
|
||||
.. _OpenType docs: https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist
|
||||
|
|
|
@ -44,42 +44,23 @@ Access using negative indexes is also possible. ::
|
|||
-----------------------------
|
||||
|
||||
.. class:: PixelAccess
|
||||
:canonical: PIL.Image.core.PixelAccess
|
||||
|
||||
.. method:: __setitem__(self, xy, color):
|
||||
.. method:: __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]
|
||||
|
||||
Modifies the pixel at x,y. The color is given as a single
|
||||
numerical value for single band images, and a tuple for
|
||||
multi-band images
|
||||
|
||||
:param xy: The pixel coordinate, given as (x, y).
|
||||
:param color: The pixel value according to its mode. e.g. tuple (r, g, b) for RGB mode)
|
||||
|
||||
.. method:: __getitem__(self, xy):
|
||||
|
||||
Returns the pixel at x,y. The pixel is returned as a single
|
||||
value for single band images or a tuple for multiple band
|
||||
images
|
||||
Returns the pixel at x,y. The pixel is returned as a single
|
||||
value for single band images or a tuple for multi-band images.
|
||||
|
||||
:param xy: The pixel coordinate, given as (x, y).
|
||||
:returns: a pixel value for single band images, a tuple of
|
||||
pixel values for multiband images.
|
||||
pixel values for multiband images.
|
||||
|
||||
.. method:: putpixel(self, xy, color):
|
||||
.. method:: __setitem__(self, xy: tuple[int, int], color: float | tuple[int, ...]) -> None
|
||||
|
||||
Modifies the pixel at x,y. The color is given as a single
|
||||
numerical value for single band images, and a tuple for
|
||||
multi-band images. In addition to this, RGB and RGBA tuples
|
||||
are accepted for P and PA images.
|
||||
multi-band images.
|
||||
|
||||
:param xy: The pixel coordinate, given as (x, y).
|
||||
:param color: The pixel value according to its mode. e.g. tuple (r, g, b) for RGB mode)
|
||||
|
||||
.. method:: getpixel(self, xy):
|
||||
|
||||
Returns the pixel at x,y. The pixel is returned as a single
|
||||
value for single band images or a tuple for multiple band
|
||||
images
|
||||
|
||||
:param xy: The pixel coordinate, given as (x, y).
|
||||
:returns: a pixel value for single band images, a tuple of
|
||||
pixel values for multiband images.
|
||||
:param color: The pixel value according to its mode,
|
||||
e.g. tuple (r, g, b) for RGB mode.
|
||||
|
|
|
@ -44,3 +44,4 @@ Access using negative indexes is also possible. ::
|
|||
|
||||
.. autoclass:: PIL.PyAccess.PyAccess()
|
||||
:members:
|
||||
:special-members: __getitem__, __setitem__
|
||||
|
|
|
@ -41,7 +41,16 @@ import warnings
|
|||
from collections.abc import Callable, MutableMapping
|
||||
from enum import IntEnum
|
||||
from types import ModuleType
|
||||
from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, Tuple, cast
|
||||
from typing import (
|
||||
IO,
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Literal,
|
||||
Protocol,
|
||||
Sequence,
|
||||
Tuple,
|
||||
cast,
|
||||
)
|
||||
|
||||
# VERSION was removed in Pillow 6.0.0.
|
||||
# PILLOW_VERSION was removed in Pillow 9.0.0.
|
||||
|
@ -218,7 +227,7 @@ if hasattr(core, "DEFAULT_STRATEGY"):
|
|||
# Registries
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import ImageFile
|
||||
from . import ImageFile, PyAccess
|
||||
ID: list[str] = []
|
||||
OPEN: dict[
|
||||
str,
|
||||
|
@ -871,7 +880,7 @@ class Image:
|
|||
msg = "cannot decode image data"
|
||||
raise ValueError(msg)
|
||||
|
||||
def load(self):
|
||||
def load(self) -> core.PixelAccess | PyAccess.PyAccess | None:
|
||||
"""
|
||||
Allocates storage for the image and loads the pixel data. In
|
||||
normal cases, you don't need to call this method, since the
|
||||
|
@ -884,7 +893,7 @@ class Image:
|
|||
operations. See :ref:`file-handling` for more information.
|
||||
|
||||
:returns: An image access object.
|
||||
:rtype: :ref:`PixelAccess` or :py:class:`PIL.PyAccess`
|
||||
:rtype: :py:class:`.PixelAccess` or :py:class:`.PyAccess`
|
||||
"""
|
||||
if self.im is not None and self.palette and self.palette.dirty:
|
||||
# realize palette
|
||||
|
@ -913,6 +922,7 @@ class Image:
|
|||
if self.pyaccess:
|
||||
return self.pyaccess
|
||||
return self.im.pixel_access(self.readonly)
|
||||
return None
|
||||
|
||||
def verify(self) -> None:
|
||||
"""
|
||||
|
@ -1102,7 +1112,10 @@ class Image:
|
|||
del new_im.info["transparency"]
|
||||
if trns is not None:
|
||||
try:
|
||||
new_im.info["transparency"] = new_im.palette.getcolor(trns, new_im)
|
||||
new_im.info["transparency"] = new_im.palette.getcolor(
|
||||
cast(Tuple[int, int, int], trns), # trns was converted to RGB
|
||||
new_im,
|
||||
)
|
||||
except Exception:
|
||||
# if we can't make a transparent color, don't leave the old
|
||||
# transparency hanging around to mess us up.
|
||||
|
@ -1152,7 +1165,10 @@ class Image:
|
|||
if trns is not None:
|
||||
if new_im.mode == "P":
|
||||
try:
|
||||
new_im.info["transparency"] = new_im.palette.getcolor(trns, new_im)
|
||||
new_im.info["transparency"] = new_im.palette.getcolor(
|
||||
cast(Tuple[int, int, int], trns), # trns was converted to RGB
|
||||
new_im,
|
||||
)
|
||||
except ValueError as e:
|
||||
del new_im.info["transparency"]
|
||||
if str(e) != "cannot allocate more than 256 colors":
|
||||
|
@ -1657,7 +1673,9 @@ class Image:
|
|||
|
||||
del self.info["transparency"]
|
||||
|
||||
def getpixel(self, xy):
|
||||
def getpixel(
|
||||
self, xy: tuple[int, int] | list[int]
|
||||
) -> float | tuple[int, ...] | None:
|
||||
"""
|
||||
Returns the pixel value at a given position.
|
||||
|
||||
|
@ -1941,15 +1959,14 @@ class Image:
|
|||
flatLut = [round(i) for i in flatLut]
|
||||
return self._new(self.im.point(flatLut, mode))
|
||||
|
||||
def putalpha(self, alpha):
|
||||
def putalpha(self, alpha: Image | int) -> None:
|
||||
"""
|
||||
Adds or replaces the alpha layer in this image. If the image
|
||||
does not have an alpha layer, it's converted to "LA" or "RGBA".
|
||||
The new layer must be either "L" or "1".
|
||||
|
||||
:param alpha: The new alpha layer. This can either be an "L" or "1"
|
||||
image having the same size as this image, or an integer or
|
||||
other color value.
|
||||
image having the same size as this image, or an integer.
|
||||
"""
|
||||
|
||||
self._ensure_mutable()
|
||||
|
@ -1988,6 +2005,7 @@ class Image:
|
|||
alpha = alpha.convert("L")
|
||||
else:
|
||||
# constant alpha
|
||||
alpha = cast(int, alpha) # see python/typing#1013
|
||||
try:
|
||||
self.im.fillband(band, alpha)
|
||||
except (AttributeError, ValueError):
|
||||
|
@ -2056,7 +2074,9 @@ class Image:
|
|||
self.palette.mode = "RGBA" if "A" in rawmode else "RGB"
|
||||
self.load() # install new palette
|
||||
|
||||
def putpixel(self, xy, value):
|
||||
def putpixel(
|
||||
self, xy: tuple[int, int], value: float | tuple[int, ...] | list[int]
|
||||
) -> None:
|
||||
"""
|
||||
Modifies the pixel at the given position. The color is given as
|
||||
a single numerical value for single-band images, and a tuple for
|
||||
|
@ -2094,9 +2114,8 @@ class Image:
|
|||
if self.mode == "PA":
|
||||
alpha = value[3] if len(value) == 4 else 255
|
||||
value = value[:3]
|
||||
value = self.palette.getcolor(value, self)
|
||||
if self.mode == "PA":
|
||||
value = (value, alpha)
|
||||
palette_index = self.palette.getcolor(value, self)
|
||||
value = (palette_index, alpha) if self.mode == "PA" else palette_index
|
||||
return self.im.putpixel(xy, value)
|
||||
|
||||
def remap_palette(self, dest_map, source_palette=None):
|
||||
|
|
|
@ -1007,7 +1007,9 @@ def floodfill(
|
|||
thresh: float = 0,
|
||||
) -> None:
|
||||
"""
|
||||
(experimental) Fills a bounded region with a given color.
|
||||
.. warning:: This method is experimental.
|
||||
|
||||
Fills a bounded region with a given color.
|
||||
|
||||
:param image: Target image.
|
||||
:param xy: Seed position (a 2-item coordinate tuple). See
|
||||
|
@ -1025,6 +1027,7 @@ def floodfill(
|
|||
# based on an implementation by Eric S. Raymond
|
||||
# amended by yo1995 @20180806
|
||||
pixel = image.load()
|
||||
assert pixel is not None
|
||||
x, y = xy
|
||||
try:
|
||||
background = pixel[x, y]
|
||||
|
|
|
@ -26,7 +26,13 @@ import tempfile
|
|||
from . import Image
|
||||
|
||||
|
||||
def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None):
|
||||
def grab(
|
||||
bbox: tuple[int, int, int, int] | None = None,
|
||||
include_layered_windows: bool = False,
|
||||
all_screens: bool = False,
|
||||
xdisplay: str | None = None,
|
||||
) -> Image.Image:
|
||||
im: Image.Image
|
||||
if xdisplay is None:
|
||||
if sys.platform == "darwin":
|
||||
fh, filepath = tempfile.mkstemp(".png")
|
||||
|
@ -63,14 +69,16 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N
|
|||
left, top, right, bottom = bbox
|
||||
im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
|
||||
return im
|
||||
# Cast to Optional[str] needed for Windows and macOS.
|
||||
display_name: str | None = xdisplay
|
||||
try:
|
||||
if not Image.core.HAVE_XCB:
|
||||
msg = "Pillow was built without XCB support"
|
||||
raise OSError(msg)
|
||||
size, data = Image.core.grabscreen_x11(xdisplay)
|
||||
size, data = Image.core.grabscreen_x11(display_name)
|
||||
except OSError:
|
||||
if (
|
||||
xdisplay is None
|
||||
display_name is None
|
||||
and sys.platform not in ("darwin", "win32")
|
||||
and shutil.which("gnome-screenshot")
|
||||
):
|
||||
|
@ -94,7 +102,7 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N
|
|||
return im
|
||||
|
||||
|
||||
def grabclipboard():
|
||||
def grabclipboard() -> Image.Image | list[str] | None:
|
||||
if sys.platform == "darwin":
|
||||
fh, filepath = tempfile.mkstemp(".png")
|
||||
os.close(fh)
|
||||
|
|
|
@ -77,7 +77,11 @@ class PyAccess:
|
|||
def _post_init(self) -> None:
|
||||
pass
|
||||
|
||||
def __setitem__(self, xy, color):
|
||||
def __setitem__(
|
||||
self,
|
||||
xy: tuple[int, int] | list[int],
|
||||
color: float | tuple[int, ...] | list[int],
|
||||
) -> None:
|
||||
"""
|
||||
Modifies the pixel at x,y. The color is given as a single
|
||||
numerical value for single band images, and a tuple for
|
||||
|
@ -107,13 +111,12 @@ class PyAccess:
|
|||
if self._im.mode == "PA":
|
||||
alpha = color[3] if len(color) == 4 else 255
|
||||
color = color[:3]
|
||||
color = self._palette.getcolor(color, self._img)
|
||||
if self._im.mode == "PA":
|
||||
color = (color, alpha)
|
||||
palette_index = self._palette.getcolor(color, self._img)
|
||||
color = (palette_index, alpha) if self._im.mode == "PA" else palette_index
|
||||
|
||||
return self.set_pixel(x, y, color)
|
||||
|
||||
def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]:
|
||||
def __getitem__(self, xy: tuple[int, int] | list[int]) -> float | tuple[int, ...]:
|
||||
"""
|
||||
Returns the pixel at x,y. The pixel is returned as a single
|
||||
value for single band images or a tuple for multiple band
|
||||
|
@ -145,7 +148,9 @@ class PyAccess:
|
|||
def get_pixel(self, x: int, y: int) -> float | tuple[int, ...]:
|
||||
raise NotImplementedError()
|
||||
|
||||
def set_pixel(self, x: int, y: int, color: float | tuple[int, ...]) -> None:
|
||||
def set_pixel(
|
||||
self, x: int, y: int, color: float | tuple[int, ...] | list[int]
|
||||
) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,10 @@ class ImagingDraw:
|
|||
def __getattr__(self, name: str) -> Any: ...
|
||||
|
||||
class PixelAccess:
|
||||
def __getattr__(self, name: str) -> Any: ...
|
||||
def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: ...
|
||||
def __setitem__(
|
||||
self, xy: tuple[int, int], color: float | tuple[int, ...]
|
||||
) -> None: ...
|
||||
|
||||
class ImagingDecoder:
|
||||
def __getattr__(self, name: str) -> Any: ...
|
||||
|
|
Loading…
Reference in New Issue
Block a user