Added type hints

Co-authored-by: Nulano <nulano@nulano.eu>
This commit is contained in:
Andrew Murray 2024-06-03 21:58:02 +10:00
parent 95a69ec698
commit f5da04adb0
3 changed files with 37 additions and 22 deletions

View File

@ -1511,7 +1511,7 @@ class Image:
self._exif._loaded = False self._exif._loaded = False
self.getexif() self.getexif()
def get_child_images(self): def get_child_images(self) -> list[ImageFile.ImageFile]:
child_images = [] child_images = []
exif = self.getexif() exif = self.getexif()
ifds = [] ifds = []
@ -1535,10 +1535,7 @@ class Image:
fp = self.fp fp = self.fp
thumbnail_offset = ifd.get(513) thumbnail_offset = ifd.get(513)
if thumbnail_offset is not None: if thumbnail_offset is not None:
try: thumbnail_offset += getattr(self, "_exif_offset", 0)
thumbnail_offset += self._exif_offset
except AttributeError:
pass
self.fp.seek(thumbnail_offset) self.fp.seek(thumbnail_offset)
data = self.fp.read(ifd.get(514)) data = self.fp.read(ifd.get(514))
fp = io.BytesIO(data) fp = io.BytesIO(data)
@ -1604,7 +1601,7 @@ class Image:
or "transparency" in self.info or "transparency" in self.info
) )
def apply_transparency(self): def apply_transparency(self) -> None:
""" """
If a P mode image has a "transparency" key in the info dictionary, If a P mode image has a "transparency" key in the info dictionary,
remove the key and instead apply the transparency to the palette. remove the key and instead apply the transparency to the palette.
@ -1616,6 +1613,7 @@ class Image:
from . import ImagePalette from . import ImagePalette
palette = self.getpalette("RGBA") palette = self.getpalette("RGBA")
assert palette is not None
transparency = self.info["transparency"] transparency = self.info["transparency"]
if isinstance(transparency, bytes): if isinstance(transparency, bytes):
for i, alpha in enumerate(transparency): for i, alpha in enumerate(transparency):

View File

@ -908,7 +908,13 @@ def getdraw(im=None, hints=None):
return im, handler return im, handler
def floodfill(image: Image.Image, xy, value, border=None, thresh=0) -> None: def floodfill(
image: Image.Image,
xy: tuple[int, int],
value: float | tuple[int, ...],
border: float | tuple[int, ...] | None = None,
thresh: float = 0,
) -> None:
""" """
(experimental) Fills a bounded region with a given color. (experimental) Fills a bounded region with a given color.

View File

@ -22,6 +22,7 @@ from __future__ import annotations
import logging import logging
import sys import sys
from typing import TYPE_CHECKING
from ._deprecate import deprecate from ._deprecate import deprecate
@ -48,9 +49,12 @@ except ImportError as ex:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
if TYPE_CHECKING:
from . import Image
class PyAccess: class PyAccess:
def __init__(self, img, readonly=False): def __init__(self, img: Image.Image, readonly: bool = False) -> None:
deprecate("PyAccess", 11) deprecate("PyAccess", 11)
vals = dict(img.im.unsafe_ptrs) vals = dict(img.im.unsafe_ptrs)
self.readonly = readonly self.readonly = readonly
@ -77,7 +81,8 @@ class PyAccess:
""" """
Modifies the pixel at x,y. The color is given as a single Modifies the pixel at x,y. The color is given as a single
numerical value for single band images, and a tuple for numerical value for single band images, and a tuple for
multi-band images multi-band images. In addition to this, RGB and RGBA tuples
are accepted for P and PA images.
:param xy: The pixel coordinate, given as (x, y). See :param xy: The pixel coordinate, given as (x, y). See
:ref:`coordinate-system`. :ref:`coordinate-system`.
@ -108,7 +113,7 @@ class PyAccess:
return self.set_pixel(x, y, color) return self.set_pixel(x, y, color)
def __getitem__(self, xy): def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]:
""" """
Returns the pixel at x,y. The pixel is returned as a single Returns the pixel at x,y. The pixel is returned as a single
value for single band images or a tuple for multiple band value for single band images or a tuple for multiple band
@ -130,13 +135,19 @@ class PyAccess:
putpixel = __setitem__ putpixel = __setitem__
getpixel = __getitem__ getpixel = __getitem__
def check_xy(self, xy): def check_xy(self, xy: tuple[int, int]) -> tuple[int, int]:
(x, y) = xy (x, y) = xy
if not (0 <= x < self.xsize and 0 <= y < self.ysize): if not (0 <= x < self.xsize and 0 <= y < self.ysize):
msg = "pixel location out of range" msg = "pixel location out of range"
raise ValueError(msg) raise ValueError(msg)
return xy return xy
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:
raise NotImplementedError()
class _PyAccess32_2(PyAccess): class _PyAccess32_2(PyAccess):
"""PA, LA, stored in first and last bytes of a 32 bit word""" """PA, LA, stored in first and last bytes of a 32 bit word"""
@ -144,7 +155,7 @@ class _PyAccess32_2(PyAccess):
def _post_init(self, *args, **kwargs): def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> tuple[int, int]:
pixel = self.pixels[y][x] pixel = self.pixels[y][x]
return pixel.r, pixel.a return pixel.r, pixel.a
@ -161,7 +172,7 @@ class _PyAccess32_3(PyAccess):
def _post_init(self, *args, **kwargs): def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> tuple[int, int, int]:
pixel = self.pixels[y][x] pixel = self.pixels[y][x]
return pixel.r, pixel.g, pixel.b return pixel.r, pixel.g, pixel.b
@ -180,7 +191,7 @@ class _PyAccess32_4(PyAccess):
def _post_init(self, *args, **kwargs): def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> tuple[int, int, int, int]:
pixel = self.pixels[y][x] pixel = self.pixels[y][x]
return pixel.r, pixel.g, pixel.b, pixel.a return pixel.r, pixel.g, pixel.b, pixel.a
@ -199,7 +210,7 @@ class _PyAccess8(PyAccess):
def _post_init(self, *args, **kwargs): def _post_init(self, *args, **kwargs):
self.pixels = self.image8 self.pixels = self.image8
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> int:
return self.pixels[y][x] return self.pixels[y][x]
def set_pixel(self, x, y, color): def set_pixel(self, x, y, color):
@ -217,7 +228,7 @@ class _PyAccessI16_N(PyAccess):
def _post_init(self, *args, **kwargs): def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("unsigned short **", self.image) self.pixels = ffi.cast("unsigned short **", self.image)
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> int:
return self.pixels[y][x] return self.pixels[y][x]
def set_pixel(self, x, y, color): def set_pixel(self, x, y, color):
@ -235,7 +246,7 @@ class _PyAccessI16_L(PyAccess):
def _post_init(self, *args, **kwargs): def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("struct Pixel_I16 **", self.image) self.pixels = ffi.cast("struct Pixel_I16 **", self.image)
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> int:
pixel = self.pixels[y][x] pixel = self.pixels[y][x]
return pixel.l + pixel.r * 256 return pixel.l + pixel.r * 256
@ -256,7 +267,7 @@ class _PyAccessI16_B(PyAccess):
def _post_init(self, *args, **kwargs): def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("struct Pixel_I16 **", self.image) self.pixels = ffi.cast("struct Pixel_I16 **", self.image)
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> int:
pixel = self.pixels[y][x] pixel = self.pixels[y][x]
return pixel.l * 256 + pixel.r return pixel.l * 256 + pixel.r
@ -277,7 +288,7 @@ class _PyAccessI32_N(PyAccess):
def _post_init(self, *args, **kwargs): def _post_init(self, *args, **kwargs):
self.pixels = self.image32 self.pixels = self.image32
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> int:
return self.pixels[y][x] return self.pixels[y][x]
def set_pixel(self, x, y, color): def set_pixel(self, x, y, color):
@ -296,7 +307,7 @@ class _PyAccessI32_Swap(PyAccess):
chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], chars[1], chars[0] chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], chars[1], chars[0]
return ffi.cast("int *", chars)[0] return ffi.cast("int *", chars)[0]
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> int:
return self.reverse(self.pixels[y][x]) return self.reverse(self.pixels[y][x])
def set_pixel(self, x, y, color): def set_pixel(self, x, y, color):
@ -309,7 +320,7 @@ class _PyAccessF(PyAccess):
def _post_init(self, *args, **kwargs): def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("float **", self.image32) self.pixels = ffi.cast("float **", self.image32)
def get_pixel(self, x, y): def get_pixel(self, x: int, y: int) -> float:
return self.pixels[y][x] return self.pixels[y][x]
def set_pixel(self, x, y, color): def set_pixel(self, x, y, color):
@ -357,7 +368,7 @@ else:
mode_map["I;32B"] = _PyAccessI32_N mode_map["I;32B"] = _PyAccessI32_N
def new(img, readonly=False): def new(img: Image.Image, readonly: bool = False) -> PyAccess | None:
access_type = mode_map.get(img.mode, None) access_type = mode_map.get(img.mode, None)
if not access_type: if not access_type:
logger.debug("PyAccess Not Implemented: %s", img.mode) logger.debug("PyAccess Not Implemented: %s", img.mode)