mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-05-02 23:13:40 +03:00
Added type hints
This commit is contained in:
parent
95255536cf
commit
5a8e7dda79
|
@ -1519,7 +1519,7 @@ def test_compute_regular_polygon_vertices(
|
||||||
[
|
[
|
||||||
(None, (50, 50, 25), 0, TypeError, "n_sides should be an int"),
|
(None, (50, 50, 25), 0, TypeError, "n_sides should be an int"),
|
||||||
(1, (50, 50, 25), 0, ValueError, "n_sides should be an int > 2"),
|
(1, (50, 50, 25), 0, ValueError, "n_sides should be an int > 2"),
|
||||||
(3, 50, 0, TypeError, "bounding_circle should be a tuple"),
|
(3, 50, 0, TypeError, "bounding_circle should be a sequence"),
|
||||||
(
|
(
|
||||||
3,
|
3,
|
||||||
(50, 50, 100, 100),
|
(50, 50, 100, 100),
|
||||||
|
|
|
@ -571,7 +571,7 @@ class Image:
|
||||||
# object is gone.
|
# object is gone.
|
||||||
self.im = DeferredError(ValueError("Operation on closed image"))
|
self.im = DeferredError(ValueError("Operation on closed image"))
|
||||||
|
|
||||||
def _copy(self):
|
def _copy(self) -> None:
|
||||||
self.load()
|
self.load()
|
||||||
self.im = self.im.copy()
|
self.im = self.im.copy()
|
||||||
self.pyaccess = None
|
self.pyaccess = None
|
||||||
|
|
|
@ -48,7 +48,7 @@ directly.
|
||||||
class ImageDraw:
|
class ImageDraw:
|
||||||
font = None
|
font = None
|
||||||
|
|
||||||
def __init__(self, im, mode=None):
|
def __init__(self, im: Image.Image, mode: str | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
Create a drawing instance.
|
Create a drawing instance.
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ class ImageDraw:
|
||||||
self.font = ImageFont.load_default()
|
self.font = ImageFont.load_default()
|
||||||
return self.font
|
return self.font
|
||||||
|
|
||||||
def _getfont(self, font_size):
|
def _getfont(self, font_size: float | None):
|
||||||
if font_size is not None:
|
if font_size is not None:
|
||||||
from . import ImageFont
|
from . import ImageFont
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ class ImageDraw:
|
||||||
font = self.getfont()
|
font = self.getfont()
|
||||||
return font
|
return font
|
||||||
|
|
||||||
def _getink(self, ink, fill=None):
|
def _getink(self, ink, fill=None) -> tuple[int | None, int | None]:
|
||||||
if ink is None and fill is None:
|
if ink is None and fill is None:
|
||||||
if self.fill:
|
if self.fill:
|
||||||
fill = self.ink
|
fill = self.ink
|
||||||
|
@ -145,13 +145,13 @@ class ImageDraw:
|
||||||
fill = self.draw.draw_ink(fill)
|
fill = self.draw.draw_ink(fill)
|
||||||
return ink, fill
|
return ink, fill
|
||||||
|
|
||||||
def arc(self, xy, start, end, fill=None, width=1):
|
def arc(self, xy, start, end, fill=None, width=1) -> None:
|
||||||
"""Draw an arc."""
|
"""Draw an arc."""
|
||||||
ink, fill = self._getink(fill)
|
ink, fill = self._getink(fill)
|
||||||
if ink is not None:
|
if ink is not None:
|
||||||
self.draw.draw_arc(xy, start, end, ink, width)
|
self.draw.draw_arc(xy, start, end, ink, width)
|
||||||
|
|
||||||
def bitmap(self, xy, bitmap, fill=None):
|
def bitmap(self, xy, bitmap, fill=None) -> None:
|
||||||
"""Draw a bitmap."""
|
"""Draw a bitmap."""
|
||||||
bitmap.load()
|
bitmap.load()
|
||||||
ink, fill = self._getink(fill)
|
ink, fill = self._getink(fill)
|
||||||
|
@ -160,7 +160,7 @@ class ImageDraw:
|
||||||
if ink is not None:
|
if ink is not None:
|
||||||
self.draw.draw_bitmap(xy, bitmap.im, ink)
|
self.draw.draw_bitmap(xy, bitmap.im, ink)
|
||||||
|
|
||||||
def chord(self, xy, start, end, fill=None, outline=None, width=1):
|
def chord(self, xy, start, end, fill=None, outline=None, width=1) -> None:
|
||||||
"""Draw a chord."""
|
"""Draw a chord."""
|
||||||
ink, fill = self._getink(outline, fill)
|
ink, fill = self._getink(outline, fill)
|
||||||
if fill is not None:
|
if fill is not None:
|
||||||
|
@ -168,7 +168,7 @@ class ImageDraw:
|
||||||
if ink is not None and ink != fill and width != 0:
|
if ink is not None and ink != fill and width != 0:
|
||||||
self.draw.draw_chord(xy, start, end, ink, 0, width)
|
self.draw.draw_chord(xy, start, end, ink, 0, width)
|
||||||
|
|
||||||
def ellipse(self, xy, fill=None, outline=None, width=1):
|
def ellipse(self, xy, fill=None, outline=None, width=1) -> None:
|
||||||
"""Draw an ellipse."""
|
"""Draw an ellipse."""
|
||||||
ink, fill = self._getink(outline, fill)
|
ink, fill = self._getink(outline, fill)
|
||||||
if fill is not None:
|
if fill is not None:
|
||||||
|
@ -176,7 +176,7 @@ class ImageDraw:
|
||||||
if ink is not None and ink != fill and width != 0:
|
if ink is not None and ink != fill and width != 0:
|
||||||
self.draw.draw_ellipse(xy, ink, 0, width)
|
self.draw.draw_ellipse(xy, ink, 0, width)
|
||||||
|
|
||||||
def line(self, xy, fill=None, width=0, joint=None):
|
def line(self, xy, fill=None, width=0, joint=None) -> None:
|
||||||
"""Draw a line, or a connected sequence of line segments."""
|
"""Draw a line, or a connected sequence of line segments."""
|
||||||
ink = self._getink(fill)[0]
|
ink = self._getink(fill)[0]
|
||||||
if ink is not None:
|
if ink is not None:
|
||||||
|
@ -236,7 +236,7 @@ class ImageDraw:
|
||||||
]
|
]
|
||||||
self.line(gap_coords, fill, width=3)
|
self.line(gap_coords, fill, width=3)
|
||||||
|
|
||||||
def shape(self, shape, fill=None, outline=None):
|
def shape(self, shape, fill=None, outline=None) -> None:
|
||||||
"""(Experimental) Draw a shape."""
|
"""(Experimental) Draw a shape."""
|
||||||
shape.close()
|
shape.close()
|
||||||
ink, fill = self._getink(outline, fill)
|
ink, fill = self._getink(outline, fill)
|
||||||
|
@ -245,7 +245,7 @@ class ImageDraw:
|
||||||
if ink is not None and ink != fill:
|
if ink is not None and ink != fill:
|
||||||
self.draw.draw_outline(shape, ink, 0)
|
self.draw.draw_outline(shape, ink, 0)
|
||||||
|
|
||||||
def pieslice(self, xy, start, end, fill=None, outline=None, width=1):
|
def pieslice(self, xy, start, end, fill=None, outline=None, width=1) -> None:
|
||||||
"""Draw a pieslice."""
|
"""Draw a pieslice."""
|
||||||
ink, fill = self._getink(outline, fill)
|
ink, fill = self._getink(outline, fill)
|
||||||
if fill is not None:
|
if fill is not None:
|
||||||
|
@ -253,13 +253,13 @@ class ImageDraw:
|
||||||
if ink is not None and ink != fill and width != 0:
|
if ink is not None and ink != fill and width != 0:
|
||||||
self.draw.draw_pieslice(xy, start, end, ink, 0, width)
|
self.draw.draw_pieslice(xy, start, end, ink, 0, width)
|
||||||
|
|
||||||
def point(self, xy, fill=None):
|
def point(self, xy, fill=None) -> None:
|
||||||
"""Draw one or more individual pixels."""
|
"""Draw one or more individual pixels."""
|
||||||
ink, fill = self._getink(fill)
|
ink, fill = self._getink(fill)
|
||||||
if ink is not None:
|
if ink is not None:
|
||||||
self.draw.draw_points(xy, ink)
|
self.draw.draw_points(xy, ink)
|
||||||
|
|
||||||
def polygon(self, xy, fill=None, outline=None, width=1):
|
def polygon(self, xy, fill=None, outline=None, width=1) -> None:
|
||||||
"""Draw a polygon."""
|
"""Draw a polygon."""
|
||||||
ink, fill = self._getink(outline, fill)
|
ink, fill = self._getink(outline, fill)
|
||||||
if fill is not None:
|
if fill is not None:
|
||||||
|
@ -267,7 +267,7 @@ class ImageDraw:
|
||||||
if ink is not None and ink != fill and width != 0:
|
if ink is not None and ink != fill and width != 0:
|
||||||
if width == 1:
|
if width == 1:
|
||||||
self.draw.draw_polygon(xy, ink, 0, width)
|
self.draw.draw_polygon(xy, ink, 0, width)
|
||||||
else:
|
elif self.im is not None:
|
||||||
# To avoid expanding the polygon outwards,
|
# To avoid expanding the polygon outwards,
|
||||||
# use the fill as a mask
|
# use the fill as a mask
|
||||||
mask = Image.new("1", self.im.size)
|
mask = Image.new("1", self.im.size)
|
||||||
|
@ -291,12 +291,12 @@ class ImageDraw:
|
||||||
|
|
||||||
def regular_polygon(
|
def regular_polygon(
|
||||||
self, bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1
|
self, bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1
|
||||||
):
|
) -> None:
|
||||||
"""Draw a regular polygon."""
|
"""Draw a regular polygon."""
|
||||||
xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)
|
xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)
|
||||||
self.polygon(xy, fill, outline, width)
|
self.polygon(xy, fill, outline, width)
|
||||||
|
|
||||||
def rectangle(self, xy, fill=None, outline=None, width=1):
|
def rectangle(self, xy, fill=None, outline=None, width=1) -> None:
|
||||||
"""Draw a rectangle."""
|
"""Draw a rectangle."""
|
||||||
ink, fill = self._getink(outline, fill)
|
ink, fill = self._getink(outline, fill)
|
||||||
if fill is not None:
|
if fill is not None:
|
||||||
|
@ -306,7 +306,7 @@ class ImageDraw:
|
||||||
|
|
||||||
def rounded_rectangle(
|
def rounded_rectangle(
|
||||||
self, xy, radius=0, fill=None, outline=None, width=1, *, corners=None
|
self, xy, radius=0, fill=None, outline=None, width=1, *, corners=None
|
||||||
):
|
) -> None:
|
||||||
"""Draw a rounded rectangle."""
|
"""Draw a rounded rectangle."""
|
||||||
if isinstance(xy[0], (list, tuple)):
|
if isinstance(xy[0], (list, tuple)):
|
||||||
(x0, y0), (x1, y1) = xy
|
(x0, y0), (x1, y1) = xy
|
||||||
|
@ -346,7 +346,7 @@ class ImageDraw:
|
||||||
r = d // 2
|
r = d // 2
|
||||||
ink, fill = self._getink(outline, fill)
|
ink, fill = self._getink(outline, fill)
|
||||||
|
|
||||||
def draw_corners(pieslice):
|
def draw_corners(pieslice) -> None:
|
||||||
if full_x:
|
if full_x:
|
||||||
# Draw top and bottom halves
|
# Draw top and bottom halves
|
||||||
parts = (
|
parts = (
|
||||||
|
@ -431,12 +431,12 @@ class ImageDraw:
|
||||||
right[3] -= r + 1
|
right[3] -= r + 1
|
||||||
self.draw.draw_rectangle(right, ink, 1)
|
self.draw.draw_rectangle(right, ink, 1)
|
||||||
|
|
||||||
def _multiline_check(self, text):
|
def _multiline_check(self, text) -> bool:
|
||||||
split_character = "\n" if isinstance(text, str) else b"\n"
|
split_character = "\n" if isinstance(text, str) else b"\n"
|
||||||
|
|
||||||
return split_character in text
|
return split_character in text
|
||||||
|
|
||||||
def _multiline_split(self, text):
|
def _multiline_split(self, text) -> list[str | bytes]:
|
||||||
split_character = "\n" if isinstance(text, str) else b"\n"
|
split_character = "\n" if isinstance(text, str) else b"\n"
|
||||||
|
|
||||||
return text.split(split_character)
|
return text.split(split_character)
|
||||||
|
@ -465,7 +465,7 @@ class ImageDraw:
|
||||||
embedded_color=False,
|
embedded_color=False,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
) -> None:
|
||||||
"""Draw text."""
|
"""Draw text."""
|
||||||
if embedded_color and self.mode not in ("RGB", "RGBA"):
|
if embedded_color and self.mode not in ("RGB", "RGBA"):
|
||||||
msg = "Embedded color supported only in RGB and RGBA modes"
|
msg = "Embedded color supported only in RGB and RGBA modes"
|
||||||
|
@ -497,7 +497,7 @@ class ImageDraw:
|
||||||
return fill
|
return fill
|
||||||
return ink
|
return ink
|
||||||
|
|
||||||
def draw_text(ink, stroke_width=0, stroke_offset=None):
|
def draw_text(ink, stroke_width=0, stroke_offset=None) -> None:
|
||||||
mode = self.fontmode
|
mode = self.fontmode
|
||||||
if stroke_width == 0 and embedded_color:
|
if stroke_width == 0 and embedded_color:
|
||||||
mode = "RGBA"
|
mode = "RGBA"
|
||||||
|
@ -547,7 +547,8 @@ class ImageDraw:
|
||||||
ink_alpha = struct.pack("i", ink)[3]
|
ink_alpha = struct.pack("i", ink)[3]
|
||||||
color.fillband(3, ink_alpha)
|
color.fillband(3, ink_alpha)
|
||||||
x, y = coord
|
x, y = coord
|
||||||
self.im.paste(color, (x, y, x + mask.size[0], y + mask.size[1]), mask)
|
if self.im is not None:
|
||||||
|
self.im.paste(color, (x, y, x + mask.size[0], y + mask.size[1]), mask)
|
||||||
else:
|
else:
|
||||||
self.draw.draw_bitmap(coord, mask, ink)
|
self.draw.draw_bitmap(coord, mask, ink)
|
||||||
|
|
||||||
|
@ -584,7 +585,7 @@ class ImageDraw:
|
||||||
embedded_color=False,
|
embedded_color=False,
|
||||||
*,
|
*,
|
||||||
font_size=None,
|
font_size=None,
|
||||||
):
|
) -> None:
|
||||||
if direction == "ttb":
|
if direction == "ttb":
|
||||||
msg = "ttb direction is unsupported for multiline text"
|
msg = "ttb direction is unsupported for multiline text"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
@ -693,7 +694,7 @@ class ImageDraw:
|
||||||
embedded_color=False,
|
embedded_color=False,
|
||||||
*,
|
*,
|
||||||
font_size=None,
|
font_size=None,
|
||||||
):
|
) -> tuple[int, int, int, int]:
|
||||||
"""Get the bounding box of a given string, in pixels."""
|
"""Get the bounding box of a given string, in pixels."""
|
||||||
if embedded_color and self.mode not in ("RGB", "RGBA"):
|
if embedded_color and self.mode not in ("RGB", "RGBA"):
|
||||||
msg = "Embedded color supported only in RGB and RGBA modes"
|
msg = "Embedded color supported only in RGB and RGBA modes"
|
||||||
|
@ -738,7 +739,7 @@ class ImageDraw:
|
||||||
embedded_color=False,
|
embedded_color=False,
|
||||||
*,
|
*,
|
||||||
font_size=None,
|
font_size=None,
|
||||||
):
|
) -> tuple[int, int, int, int]:
|
||||||
if direction == "ttb":
|
if direction == "ttb":
|
||||||
msg = "ttb direction is unsupported for multiline text"
|
msg = "ttb direction is unsupported for multiline text"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
@ -777,7 +778,7 @@ class ImageDraw:
|
||||||
elif anchor[1] == "d":
|
elif anchor[1] == "d":
|
||||||
top -= (len(lines) - 1) * line_spacing
|
top -= (len(lines) - 1) * line_spacing
|
||||||
|
|
||||||
bbox = None
|
bbox: tuple[int, int, int, int] | None = None
|
||||||
|
|
||||||
for idx, line in enumerate(lines):
|
for idx, line in enumerate(lines):
|
||||||
left = xy[0]
|
left = xy[0]
|
||||||
|
@ -828,7 +829,7 @@ class ImageDraw:
|
||||||
return bbox
|
return bbox
|
||||||
|
|
||||||
|
|
||||||
def Draw(im, mode=None):
|
def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw:
|
||||||
"""
|
"""
|
||||||
A simple 2D drawing interface for PIL images.
|
A simple 2D drawing interface for PIL images.
|
||||||
|
|
||||||
|
@ -876,7 +877,7 @@ def getdraw(im=None, hints=None):
|
||||||
return im, handler
|
return im, handler
|
||||||
|
|
||||||
|
|
||||||
def floodfill(image, xy, value, border=None, thresh=0):
|
def floodfill(image: Image.Image, xy, value, border=None, thresh=0) -> None:
|
||||||
"""
|
"""
|
||||||
(experimental) Fills a bounded region with a given color.
|
(experimental) Fills a bounded region with a given color.
|
||||||
|
|
||||||
|
@ -932,7 +933,7 @@ def floodfill(image, xy, value, border=None, thresh=0):
|
||||||
edge = new_edge
|
edge = new_edge
|
||||||
|
|
||||||
|
|
||||||
def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation):
|
def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) -> list[tuple[float, float]]:
|
||||||
"""
|
"""
|
||||||
Generate a list of vertices for a 2D regular polygon.
|
Generate a list of vertices for a 2D regular polygon.
|
||||||
|
|
||||||
|
@ -982,7 +983,7 @@ def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation):
|
||||||
|
|
||||||
# 1.2 Check `bounding_circle` has an appropriate value
|
# 1.2 Check `bounding_circle` has an appropriate value
|
||||||
if not isinstance(bounding_circle, (list, tuple)):
|
if not isinstance(bounding_circle, (list, tuple)):
|
||||||
msg = "bounding_circle should be a tuple"
|
msg = "bounding_circle should be a sequence"
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
|
|
||||||
if len(bounding_circle) == 3:
|
if len(bounding_circle) == 3:
|
||||||
|
@ -1014,7 +1015,7 @@ def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation):
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
# 2. Define Helper Functions
|
# 2. Define Helper Functions
|
||||||
def _apply_rotation(point, degrees, centroid):
|
def _apply_rotation(point: list[float], degrees: float) -> tuple[int, int]:
|
||||||
return (
|
return (
|
||||||
round(
|
round(
|
||||||
point[0] * math.cos(math.radians(360 - degrees))
|
point[0] * math.cos(math.radians(360 - degrees))
|
||||||
|
@ -1030,11 +1031,11 @@ def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _compute_polygon_vertex(centroid, polygon_radius, angle):
|
def _compute_polygon_vertex(angle: float) -> tuple[int, int]:
|
||||||
start_point = [polygon_radius, 0]
|
start_point = [polygon_radius, 0]
|
||||||
return _apply_rotation(start_point, angle, centroid)
|
return _apply_rotation(start_point, angle)
|
||||||
|
|
||||||
def _get_angles(n_sides, rotation):
|
def _get_angles(n_sides: int, rotation: float) -> list[float]:
|
||||||
angles = []
|
angles = []
|
||||||
degrees = 360 / n_sides
|
degrees = 360 / n_sides
|
||||||
# Start with the bottom left polygon vertex
|
# Start with the bottom left polygon vertex
|
||||||
|
@ -1051,11 +1052,11 @@ def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation):
|
||||||
|
|
||||||
# 4. Compute Vertices
|
# 4. Compute Vertices
|
||||||
return [
|
return [
|
||||||
_compute_polygon_vertex(centroid, polygon_radius, angle) for angle in angles
|
_compute_polygon_vertex(angle) for angle in angles
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def _color_diff(color1, color2):
|
def _color_diff(color1, color2: float | tuple[int, ...]) -> float:
|
||||||
"""
|
"""
|
||||||
Uses 1-norm distance to calculate difference between two values.
|
Uses 1-norm distance to calculate difference between two values.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -872,7 +872,7 @@ def load_path(filename):
|
||||||
raise OSError(msg)
|
raise OSError(msg)
|
||||||
|
|
||||||
|
|
||||||
def load_default(size=None):
|
def load_default(size: float | None = None) -> FreeTypeFont | ImageFont:
|
||||||
"""If FreeType support is available, load a version of Aileron Regular,
|
"""If FreeType support is available, load a version of Aileron Regular,
|
||||||
https://dotcolon.net/font/aileron, with a more limited character set.
|
https://dotcolon.net/font/aileron, with a more limited character set.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user