mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	Added type hints to ImageDraw shape methods
This commit is contained in:
		
							parent
							
								
									b1d5d7f6f9
								
							
						
					
					
						commit
						9f79e5d768
					
				| 
						 | 
					@ -448,6 +448,7 @@ def test_shape1() -> None:
 | 
				
			||||||
    x3, y3 = 95, 5
 | 
					    x3, y3 = 95, 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
 | 
					    assert ImageDraw.Outline is not None
 | 
				
			||||||
    s = ImageDraw.Outline()
 | 
					    s = ImageDraw.Outline()
 | 
				
			||||||
    s.move(x0, y0)
 | 
					    s.move(x0, y0)
 | 
				
			||||||
    s.curve(x1, y1, x2, y2, x3, y3)
 | 
					    s.curve(x1, y1, x2, y2, x3, y3)
 | 
				
			||||||
| 
						 | 
					@ -469,6 +470,7 @@ def test_shape2() -> None:
 | 
				
			||||||
    x3, y3 = 5, 95
 | 
					    x3, y3 = 5, 95
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
 | 
					    assert ImageDraw.Outline is not None
 | 
				
			||||||
    s = ImageDraw.Outline()
 | 
					    s = ImageDraw.Outline()
 | 
				
			||||||
    s.move(x0, y0)
 | 
					    s.move(x0, y0)
 | 
				
			||||||
    s.curve(x1, y1, x2, y2, x3, y3)
 | 
					    s.curve(x1, y1, x2, y2, x3, y3)
 | 
				
			||||||
| 
						 | 
					@ -487,6 +489,7 @@ def test_transform() -> None:
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
 | 
					    assert ImageDraw.Outline is not None
 | 
				
			||||||
    s = ImageDraw.Outline()
 | 
					    s = ImageDraw.Outline()
 | 
				
			||||||
    s.line(0, 0)
 | 
					    s.line(0, 0)
 | 
				
			||||||
    s.transform((0, 0, 0, 0, 0, 0))
 | 
					    s.transform((0, 0, 0, 0, 0, 0))
 | 
				
			||||||
| 
						 | 
					@ -913,7 +916,12 @@ def test_rounded_rectangle_translucent(
 | 
				
			||||||
def test_floodfill(bbox: Coords) -> None:
 | 
					def test_floodfill(bbox: Coords) -> None:
 | 
				
			||||||
    red = ImageColor.getrgb("red")
 | 
					    red = ImageColor.getrgb("red")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for mode, value in [("L", 1), ("RGBA", (255, 0, 0, 0)), ("RGB", red)]:
 | 
					    mode_values: list[tuple[str, int | tuple[int, ...]]] = [
 | 
				
			||||||
 | 
					        ("L", 1),
 | 
				
			||||||
 | 
					        ("RGBA", (255, 0, 0, 0)),
 | 
				
			||||||
 | 
					        ("RGB", red),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    for mode, value in mode_values:
 | 
				
			||||||
        # Arrange
 | 
					        # Arrange
 | 
				
			||||||
        im = Image.new(mode, (W, H))
 | 
					        im = Image.new(mode, (W, H))
 | 
				
			||||||
        draw = ImageDraw.Draw(im)
 | 
					        draw = ImageDraw.Draw(im)
 | 
				
			||||||
| 
						 | 
					@ -1429,6 +1437,7 @@ def test_same_color_outline(bbox: Coords) -> None:
 | 
				
			||||||
    x2, y2 = 95, 50
 | 
					    x2, y2 = 95, 50
 | 
				
			||||||
    x3, y3 = 95, 5
 | 
					    x3, y3 = 95, 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert ImageDraw.Outline is not None
 | 
				
			||||||
    s = ImageDraw.Outline()
 | 
					    s = ImageDraw.Outline()
 | 
				
			||||||
    s.move(x0, y0)
 | 
					    s.move(x0, y0)
 | 
				
			||||||
    s.curve(x1, y1, x2, y2, x3, y3)
 | 
					    s.curve(x1, y1, x2, y2, x3, y3)
 | 
				
			||||||
| 
						 | 
					@ -1467,7 +1476,7 @@ def test_same_color_outline(bbox: Coords) -> None:
 | 
				
			||||||
        (4, "square", {}),
 | 
					        (4, "square", {}),
 | 
				
			||||||
        (8, "regular_octagon", {}),
 | 
					        (8, "regular_octagon", {}),
 | 
				
			||||||
        (4, "square_rotate_45", {"rotation": 45}),
 | 
					        (4, "square_rotate_45", {"rotation": 45}),
 | 
				
			||||||
        (3, "triangle_width", {"width": 5, "outline": "yellow"}),
 | 
					        (3, "triangle_width", {"outline": "yellow", "width": 5}),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
def test_draw_regular_polygon(
 | 
					def test_draw_regular_polygon(
 | 
				
			||||||
| 
						 | 
					@ -1477,7 +1486,10 @@ def test_draw_regular_polygon(
 | 
				
			||||||
    filename = f"Tests/images/imagedraw_{polygon_name}.png"
 | 
					    filename = f"Tests/images/imagedraw_{polygon_name}.png"
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
    bounding_circle = ((W // 2, H // 2), 25)
 | 
					    bounding_circle = ((W // 2, H // 2), 25)
 | 
				
			||||||
    draw.regular_polygon(bounding_circle, n_sides, fill="red", **args)
 | 
					    rotation = int(args.get("rotation", 0))
 | 
				
			||||||
 | 
					    outline = args.get("outline")
 | 
				
			||||||
 | 
					    width = int(args.get("width", 1))
 | 
				
			||||||
 | 
					    draw.regular_polygon(bounding_circle, n_sides, rotation, "red", outline, width)
 | 
				
			||||||
    assert_image_equal_tofile(im, filename)
 | 
					    assert_image_equal_tofile(im, filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1630,6 +1642,6 @@ def test_incorrectly_ordered_coordinates(xy: tuple[int, int, int, int]) -> None:
 | 
				
			||||||
        draw.rounded_rectangle(xy)
 | 
					        draw.rounded_rectangle(xy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_getdraw():
 | 
					def test_getdraw() -> None:
 | 
				
			||||||
    with pytest.warns(DeprecationWarning):
 | 
					    with pytest.warns(DeprecationWarning):
 | 
				
			||||||
        ImageDraw.getdraw(None, [])
 | 
					        ImageDraw.getdraw(None, [])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,15 +35,24 @@ import math
 | 
				
			||||||
import numbers
 | 
					import numbers
 | 
				
			||||||
import struct
 | 
					import struct
 | 
				
			||||||
from types import ModuleType
 | 
					from types import ModuleType
 | 
				
			||||||
from typing import TYPE_CHECKING, AnyStr, Sequence, cast
 | 
					from typing import TYPE_CHECKING, AnyStr, Callable, List, Sequence, Tuple, Union, cast
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import Image, ImageColor
 | 
					from . import Image, ImageColor
 | 
				
			||||||
from ._deprecate import deprecate
 | 
					from ._deprecate import deprecate
 | 
				
			||||||
from ._typing import Coords
 | 
					from ._typing import Coords
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# experimental access to the outline API
 | 
				
			||||||
 | 
					Outline: Callable[[], Image.core._Outline] | None
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    Outline = Image.core.outline
 | 
				
			||||||
 | 
					except AttributeError:
 | 
				
			||||||
 | 
					    Outline = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if TYPE_CHECKING:
 | 
					if TYPE_CHECKING:
 | 
				
			||||||
    from . import ImageDraw2, ImageFont
 | 
					    from . import ImageDraw2, ImageFont
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_Ink = Union[float, Tuple[int, ...], str]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
A simple 2D drawing interface for PIL images.
 | 
					A simple 2D drawing interface for PIL images.
 | 
				
			||||||
<p>
 | 
					<p>
 | 
				
			||||||
| 
						 | 
					@ -134,34 +143,47 @@ class ImageDraw:
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return self.getfont()
 | 
					            return self.getfont()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _getink(self, ink, fill=None) -> tuple[int | None, int | None]:
 | 
					    def _getink(
 | 
				
			||||||
 | 
					        self, ink: _Ink | None, fill: _Ink | None = None
 | 
				
			||||||
 | 
					    ) -> tuple[int | None, int | None]:
 | 
				
			||||||
 | 
					        result_ink = None
 | 
				
			||||||
 | 
					        result_fill = 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
 | 
					                result_fill = self.ink
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                ink = self.ink
 | 
					                result_ink = self.ink
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            if ink is not None:
 | 
					            if ink is not None:
 | 
				
			||||||
                if isinstance(ink, str):
 | 
					                if isinstance(ink, str):
 | 
				
			||||||
                    ink = ImageColor.getcolor(ink, self.mode)
 | 
					                    ink = ImageColor.getcolor(ink, self.mode)
 | 
				
			||||||
                if self.palette and not isinstance(ink, numbers.Number):
 | 
					                if self.palette and not isinstance(ink, numbers.Number):
 | 
				
			||||||
                    ink = self.palette.getcolor(ink, self._image)
 | 
					                    ink = self.palette.getcolor(ink, self._image)
 | 
				
			||||||
                ink = self.draw.draw_ink(ink)
 | 
					                result_ink = self.draw.draw_ink(ink)
 | 
				
			||||||
            if fill is not None:
 | 
					            if fill is not None:
 | 
				
			||||||
                if isinstance(fill, str):
 | 
					                if isinstance(fill, str):
 | 
				
			||||||
                    fill = ImageColor.getcolor(fill, self.mode)
 | 
					                    fill = ImageColor.getcolor(fill, self.mode)
 | 
				
			||||||
                if self.palette and not isinstance(fill, numbers.Number):
 | 
					                if self.palette and not isinstance(fill, numbers.Number):
 | 
				
			||||||
                    fill = self.palette.getcolor(fill, self._image)
 | 
					                    fill = self.palette.getcolor(fill, self._image)
 | 
				
			||||||
                fill = self.draw.draw_ink(fill)
 | 
					                result_fill = self.draw.draw_ink(fill)
 | 
				
			||||||
        return ink, fill
 | 
					        return result_ink, result_fill
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def arc(self, xy: Coords, start, end, fill=None, width=1) -> None:
 | 
					    def arc(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        xy: Coords,
 | 
				
			||||||
 | 
					        start: float,
 | 
				
			||||||
 | 
					        end: float,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 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: Sequence[int], bitmap, fill=None) -> None:
 | 
					    def bitmap(
 | 
				
			||||||
 | 
					        self, xy: Sequence[int], bitmap: Image.Image, fill: _Ink | None = None
 | 
				
			||||||
 | 
					    ) -> None:
 | 
				
			||||||
        """Draw a bitmap."""
 | 
					        """Draw a bitmap."""
 | 
				
			||||||
        bitmap.load()
 | 
					        bitmap.load()
 | 
				
			||||||
        ink, fill = self._getink(fill)
 | 
					        ink, fill = self._getink(fill)
 | 
				
			||||||
| 
						 | 
					@ -170,30 +192,55 @@ 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: Coords, start, end, fill=None, outline=None, width=1) -> None:
 | 
					    def chord(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        xy: Coords,
 | 
				
			||||||
 | 
					        start: float,
 | 
				
			||||||
 | 
					        end: float,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        outline: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 1,
 | 
				
			||||||
 | 
					    ) -> None:
 | 
				
			||||||
        """Draw a chord."""
 | 
					        """Draw a chord."""
 | 
				
			||||||
        ink, fill = self._getink(outline, fill)
 | 
					        ink, fill_ink = self._getink(outline, fill)
 | 
				
			||||||
        if fill is not None:
 | 
					        if fill_ink is not None:
 | 
				
			||||||
            self.draw.draw_chord(xy, start, end, fill, 1)
 | 
					            self.draw.draw_chord(xy, start, end, fill_ink, 1)
 | 
				
			||||||
        if ink is not None and ink != fill and width != 0:
 | 
					        if ink is not None and ink != fill_ink 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: Coords, fill=None, outline=None, width=1) -> None:
 | 
					    def ellipse(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        xy: Coords,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        outline: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 1,
 | 
				
			||||||
 | 
					    ) -> None:
 | 
				
			||||||
        """Draw an ellipse."""
 | 
					        """Draw an ellipse."""
 | 
				
			||||||
        ink, fill = self._getink(outline, fill)
 | 
					        ink, fill_ink = self._getink(outline, fill)
 | 
				
			||||||
        if fill is not None:
 | 
					        if fill_ink is not None:
 | 
				
			||||||
            self.draw.draw_ellipse(xy, fill, 1)
 | 
					            self.draw.draw_ellipse(xy, fill_ink, 1)
 | 
				
			||||||
        if ink is not None and ink != fill and width != 0:
 | 
					        if ink is not None and ink != fill_ink and width != 0:
 | 
				
			||||||
            self.draw.draw_ellipse(xy, ink, 0, width)
 | 
					            self.draw.draw_ellipse(xy, ink, 0, width)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def circle(
 | 
					    def circle(
 | 
				
			||||||
        self, xy: Sequence[float], radius: float, fill=None, outline=None, width=1
 | 
					        self,
 | 
				
			||||||
 | 
					        xy: Sequence[float],
 | 
				
			||||||
 | 
					        radius: float,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        outline: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 1,
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> None:
 | 
				
			||||||
        """Draw a circle given center coordinates and a radius."""
 | 
					        """Draw a circle given center coordinates and a radius."""
 | 
				
			||||||
        ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius)
 | 
					        ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius)
 | 
				
			||||||
        self.ellipse(ellipse_xy, fill, outline, width)
 | 
					        self.ellipse(ellipse_xy, fill, outline, width)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def line(self, xy: Coords, fill=None, width=0, joint=None) -> None:
 | 
					    def line(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        xy: Coords,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 0,
 | 
				
			||||||
 | 
					        joint: str | None = 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:
 | 
				
			||||||
| 
						 | 
					@ -223,7 +270,7 @@ class ImageDraw:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    def coord_at_angle(
 | 
					                    def coord_at_angle(
 | 
				
			||||||
                        coord: Sequence[float], angle: float
 | 
					                        coord: Sequence[float], angle: float
 | 
				
			||||||
                    ) -> tuple[float, float]:
 | 
					                    ) -> tuple[float, ...]:
 | 
				
			||||||
                        x, y = coord
 | 
					                        x, y = coord
 | 
				
			||||||
                        angle -= 90
 | 
					                        angle -= 90
 | 
				
			||||||
                        distance = width / 2 - 1
 | 
					                        distance = width / 2 - 1
 | 
				
			||||||
| 
						 | 
					@ -264,37 +311,54 @@ class ImageDraw:
 | 
				
			||||||
                            ]
 | 
					                            ]
 | 
				
			||||||
                        self.line(gap_coords, fill, width=3)
 | 
					                        self.line(gap_coords, fill, width=3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def shape(self, shape, fill=None, outline=None) -> None:
 | 
					    def shape(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        shape: Image.core._Outline,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        outline: _Ink | None = None,
 | 
				
			||||||
 | 
					    ) -> None:
 | 
				
			||||||
        """(Experimental) Draw a shape."""
 | 
					        """(Experimental) Draw a shape."""
 | 
				
			||||||
        shape.close()
 | 
					        shape.close()
 | 
				
			||||||
        ink, fill = self._getink(outline, fill)
 | 
					        ink, fill_ink = self._getink(outline, fill)
 | 
				
			||||||
        if fill is not None:
 | 
					        if fill_ink is not None:
 | 
				
			||||||
            self.draw.draw_outline(shape, fill, 1)
 | 
					            self.draw.draw_outline(shape, fill_ink, 1)
 | 
				
			||||||
        if ink is not None and ink != fill:
 | 
					        if ink is not None and ink != fill_ink:
 | 
				
			||||||
            self.draw.draw_outline(shape, ink, 0)
 | 
					            self.draw.draw_outline(shape, ink, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def pieslice(
 | 
					    def pieslice(
 | 
				
			||||||
        self, xy: Coords, start, end, fill=None, outline=None, width=1
 | 
					        self,
 | 
				
			||||||
 | 
					        xy: Coords,
 | 
				
			||||||
 | 
					        start: float,
 | 
				
			||||||
 | 
					        end: float,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        outline: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 1,
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> None:
 | 
				
			||||||
        """Draw a pieslice."""
 | 
					        """Draw a pieslice."""
 | 
				
			||||||
        ink, fill = self._getink(outline, fill)
 | 
					        ink, fill_ink = self._getink(outline, fill)
 | 
				
			||||||
        if fill is not None:
 | 
					        if fill_ink is not None:
 | 
				
			||||||
            self.draw.draw_pieslice(xy, start, end, fill, 1)
 | 
					            self.draw.draw_pieslice(xy, start, end, fill_ink, 1)
 | 
				
			||||||
        if ink is not None and ink != fill and width != 0:
 | 
					        if ink is not None and ink != fill_ink 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: Coords, fill=None) -> None:
 | 
					    def point(self, xy: Coords, fill: _Ink | None = 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: Coords, fill=None, outline=None, width=1) -> None:
 | 
					    def polygon(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        xy: Coords,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        outline: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 1,
 | 
				
			||||||
 | 
					    ) -> None:
 | 
				
			||||||
        """Draw a polygon."""
 | 
					        """Draw a polygon."""
 | 
				
			||||||
        ink, fill = self._getink(outline, fill)
 | 
					        ink, fill_ink = self._getink(outline, fill)
 | 
				
			||||||
        if fill is not None:
 | 
					        if fill_ink is not None:
 | 
				
			||||||
            self.draw.draw_polygon(xy, fill, 1)
 | 
					            self.draw.draw_polygon(xy, fill_ink, 1)
 | 
				
			||||||
        if ink is not None and ink != fill and width != 0:
 | 
					        if ink is not None and ink != fill_ink 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)
 | 
				
			||||||
            elif self.im is not None:
 | 
					            elif self.im is not None:
 | 
				
			||||||
| 
						 | 
					@ -320,22 +384,41 @@ class ImageDraw:
 | 
				
			||||||
                self.im.paste(im.im, (0, 0) + im.size, mask.im)
 | 
					                self.im.paste(im.im, (0, 0) + im.size, mask.im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def regular_polygon(
 | 
					    def regular_polygon(
 | 
				
			||||||
        self, bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1
 | 
					        self,
 | 
				
			||||||
 | 
					        bounding_circle: Sequence[Sequence[float] | float],
 | 
				
			||||||
 | 
					        n_sides: int,
 | 
				
			||||||
 | 
					        rotation: float = 0,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        outline: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 1,
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> 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: Coords, fill=None, outline=None, width=1) -> None:
 | 
					    def rectangle(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        xy: Coords,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        outline: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 1,
 | 
				
			||||||
 | 
					    ) -> None:
 | 
				
			||||||
        """Draw a rectangle."""
 | 
					        """Draw a rectangle."""
 | 
				
			||||||
        ink, fill = self._getink(outline, fill)
 | 
					        ink, fill_ink = self._getink(outline, fill)
 | 
				
			||||||
        if fill is not None:
 | 
					        if fill_ink is not None:
 | 
				
			||||||
            self.draw.draw_rectangle(xy, fill, 1)
 | 
					            self.draw.draw_rectangle(xy, fill_ink, 1)
 | 
				
			||||||
        if ink is not None and ink != fill and width != 0:
 | 
					        if ink is not None and ink != fill_ink and width != 0:
 | 
				
			||||||
            self.draw.draw_rectangle(xy, ink, 0, width)
 | 
					            self.draw.draw_rectangle(xy, ink, 0, width)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def rounded_rectangle(
 | 
					    def rounded_rectangle(
 | 
				
			||||||
        self, xy: Coords, radius=0, fill=None, outline=None, width=1, *, corners=None
 | 
					        self,
 | 
				
			||||||
 | 
					        xy: Coords,
 | 
				
			||||||
 | 
					        radius: float = 0,
 | 
				
			||||||
 | 
					        fill: _Ink | None = None,
 | 
				
			||||||
 | 
					        outline: _Ink | None = None,
 | 
				
			||||||
 | 
					        width: int = 1,
 | 
				
			||||||
 | 
					        *,
 | 
				
			||||||
 | 
					        corners: tuple[bool, bool, bool, bool] | None = None,
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> None:
 | 
				
			||||||
        """Draw a rounded rectangle."""
 | 
					        """Draw a rounded rectangle."""
 | 
				
			||||||
        if isinstance(xy[0], (list, tuple)):
 | 
					        if isinstance(xy[0], (list, tuple)):
 | 
				
			||||||
| 
						 | 
					@ -377,10 +460,10 @@ class ImageDraw:
 | 
				
			||||||
            # that is a rectangle
 | 
					            # that is a rectangle
 | 
				
			||||||
            return self.rectangle(xy, fill, outline, width)
 | 
					            return self.rectangle(xy, fill, outline, width)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        r = d // 2
 | 
					        r = int(d // 2)
 | 
				
			||||||
        ink, fill = self._getink(outline, fill)
 | 
					        ink, fill_ink = self._getink(outline, fill)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def draw_corners(pieslice) -> None:
 | 
					        def draw_corners(pieslice: bool) -> None:
 | 
				
			||||||
            parts: tuple[tuple[tuple[float, float, float, float], int, int], ...]
 | 
					            parts: tuple[tuple[tuple[float, float, float, float], int, int], ...]
 | 
				
			||||||
            if full_x:
 | 
					            if full_x:
 | 
				
			||||||
                # Draw top and bottom halves
 | 
					                # Draw top and bottom halves
 | 
				
			||||||
| 
						 | 
					@ -410,32 +493,32 @@ class ImageDraw:
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            for part in parts:
 | 
					            for part in parts:
 | 
				
			||||||
                if pieslice:
 | 
					                if pieslice:
 | 
				
			||||||
                    self.draw.draw_pieslice(*(part + (fill, 1)))
 | 
					                    self.draw.draw_pieslice(*(part + (fill_ink, 1)))
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    self.draw.draw_arc(*(part + (ink, width)))
 | 
					                    self.draw.draw_arc(*(part + (ink, width)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if fill is not None:
 | 
					        if fill_ink is not None:
 | 
				
			||||||
            draw_corners(True)
 | 
					            draw_corners(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if full_x:
 | 
					            if full_x:
 | 
				
			||||||
                self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill, 1)
 | 
					                self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill_ink, 1)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill, 1)
 | 
					                self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill_ink, 1)
 | 
				
			||||||
            if not full_x and not full_y:
 | 
					            if not full_x and not full_y:
 | 
				
			||||||
                left = [x0, y0, x0 + r, y1]
 | 
					                left = [x0, y0, x0 + r, y1]
 | 
				
			||||||
                if corners[0]:
 | 
					                if corners[0]:
 | 
				
			||||||
                    left[1] += r + 1
 | 
					                    left[1] += r + 1
 | 
				
			||||||
                if corners[3]:
 | 
					                if corners[3]:
 | 
				
			||||||
                    left[3] -= r + 1
 | 
					                    left[3] -= r + 1
 | 
				
			||||||
                self.draw.draw_rectangle(left, fill, 1)
 | 
					                self.draw.draw_rectangle(left, fill_ink, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                right = [x1 - r, y0, x1, y1]
 | 
					                right = [x1 - r, y0, x1, y1]
 | 
				
			||||||
                if corners[1]:
 | 
					                if corners[1]:
 | 
				
			||||||
                    right[1] += r + 1
 | 
					                    right[1] += r + 1
 | 
				
			||||||
                if corners[2]:
 | 
					                if corners[2]:
 | 
				
			||||||
                    right[3] -= r + 1
 | 
					                    right[3] -= r + 1
 | 
				
			||||||
                self.draw.draw_rectangle(right, fill, 1)
 | 
					                self.draw.draw_rectangle(right, fill_ink, 1)
 | 
				
			||||||
        if ink is not None and ink != fill and width != 0:
 | 
					        if ink is not None and ink != fill_ink and width != 0:
 | 
				
			||||||
            draw_corners(False)
 | 
					            draw_corners(False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not full_x:
 | 
					            if not full_x:
 | 
				
			||||||
| 
						 | 
					@ -530,10 +613,11 @@ class ImageDraw:
 | 
				
			||||||
                embedded_color,
 | 
					                embedded_color,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def getink(fill):
 | 
					        def getink(fill: _Ink | None) -> int:
 | 
				
			||||||
            ink, fill = self._getink(fill)
 | 
					            ink, fill_ink = self._getink(fill)
 | 
				
			||||||
            if ink is None:
 | 
					            if ink is None:
 | 
				
			||||||
                return fill
 | 
					                assert fill_ink is not None
 | 
				
			||||||
 | 
					                return fill_ink
 | 
				
			||||||
            return ink
 | 
					            return ink
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def draw_text(ink, stroke_width=0, stroke_offset=None) -> None:
 | 
					        def draw_text(ink, stroke_width=0, stroke_offset=None) -> None:
 | 
				
			||||||
| 
						 | 
					@ -897,13 +981,6 @@ def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw:
 | 
				
			||||||
        return ImageDraw(im, mode)
 | 
					        return ImageDraw(im, mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# experimental access to the outline API
 | 
					 | 
				
			||||||
try:
 | 
					 | 
				
			||||||
    Outline = Image.core.outline
 | 
					 | 
				
			||||||
except AttributeError:
 | 
					 | 
				
			||||||
    Outline = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def getdraw(
 | 
					def getdraw(
 | 
				
			||||||
    im: Image.Image | None = None, hints: list[str] | None = None
 | 
					    im: Image.Image | None = None, hints: list[str] | None = None
 | 
				
			||||||
) -> tuple[ImageDraw2.Draw | None, ModuleType]:
 | 
					) -> tuple[ImageDraw2.Draw | None, ModuleType]:
 | 
				
			||||||
| 
						 | 
					@ -983,12 +1060,12 @@ def floodfill(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _compute_regular_polygon_vertices(
 | 
					def _compute_regular_polygon_vertices(
 | 
				
			||||||
    bounding_circle, n_sides, rotation
 | 
					    bounding_circle: Sequence[Sequence[float] | float], n_sides: int, rotation: float
 | 
				
			||||||
) -> list[tuple[float, float]]:
 | 
					) -> list[tuple[float, float]]:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Generate a list of vertices for a 2D regular polygon.
 | 
					    Generate a list of vertices for a 2D regular polygon.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param bounding_circle: The bounding circle is a tuple defined
 | 
					    :param bounding_circle: The bounding circle is a sequence defined
 | 
				
			||||||
        by a point and radius. The polygon is inscribed in this circle.
 | 
					        by a point and radius. The polygon is inscribed in this circle.
 | 
				
			||||||
        (e.g. ``bounding_circle=(x, y, r)`` or ``((x, y), r)``)
 | 
					        (e.g. ``bounding_circle=(x, y, r)`` or ``((x, y), r)``)
 | 
				
			||||||
    :param n_sides: Number of sides
 | 
					    :param n_sides: Number of sides
 | 
				
			||||||
| 
						 | 
					@ -1026,7 +1103,7 @@ def _compute_regular_polygon_vertices(
 | 
				
			||||||
    # 1. Error Handling
 | 
					    # 1. Error Handling
 | 
				
			||||||
    # 1.1 Check `n_sides` has an appropriate value
 | 
					    # 1.1 Check `n_sides` has an appropriate value
 | 
				
			||||||
    if not isinstance(n_sides, int):
 | 
					    if not isinstance(n_sides, int):
 | 
				
			||||||
        msg = "n_sides should be an int"
 | 
					        msg = "n_sides should be an int"  # type: ignore[unreachable]
 | 
				
			||||||
        raise TypeError(msg)
 | 
					        raise TypeError(msg)
 | 
				
			||||||
    if n_sides < 3:
 | 
					    if n_sides < 3:
 | 
				
			||||||
        msg = "n_sides should be an int > 2"
 | 
					        msg = "n_sides should be an int > 2"
 | 
				
			||||||
| 
						 | 
					@ -1038,9 +1115,24 @@ def _compute_regular_polygon_vertices(
 | 
				
			||||||
        raise TypeError(msg)
 | 
					        raise TypeError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if len(bounding_circle) == 3:
 | 
					    if len(bounding_circle) == 3:
 | 
				
			||||||
        *centroid, polygon_radius = bounding_circle
 | 
					        if not all(isinstance(i, (int, float)) for i in bounding_circle):
 | 
				
			||||||
    elif len(bounding_circle) == 2:
 | 
					            msg = "bounding_circle should only contain numeric data"
 | 
				
			||||||
        centroid, polygon_radius = bounding_circle
 | 
					            raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        *centroid, polygon_radius = cast(List[float], list(bounding_circle))
 | 
				
			||||||
 | 
					    elif len(bounding_circle) == 2 and isinstance(bounding_circle[0], (list, tuple)):
 | 
				
			||||||
 | 
					        if not all(
 | 
				
			||||||
 | 
					            isinstance(i, (int, float)) for i in bounding_circle[0]
 | 
				
			||||||
 | 
					        ) or not isinstance(bounding_circle[1], (int, float)):
 | 
				
			||||||
 | 
					            msg = "bounding_circle should only contain numeric data"
 | 
				
			||||||
 | 
					            raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if len(bounding_circle[0]) != 2:
 | 
				
			||||||
 | 
					            msg = "bounding_circle centre should contain 2D coordinates (e.g. (x, y))"
 | 
				
			||||||
 | 
					            raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        centroid = cast(List[float], list(bounding_circle[0]))
 | 
				
			||||||
 | 
					        polygon_radius = cast(float, bounding_circle[1])
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        msg = (
 | 
					        msg = (
 | 
				
			||||||
            "bounding_circle should contain 2D coordinates "
 | 
					            "bounding_circle should contain 2D coordinates "
 | 
				
			||||||
| 
						 | 
					@ -1048,25 +1140,17 @@ def _compute_regular_polygon_vertices(
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        raise ValueError(msg)
 | 
					        raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if not all(isinstance(i, (int, float)) for i in (*centroid, polygon_radius)):
 | 
					 | 
				
			||||||
        msg = "bounding_circle should only contain numeric data"
 | 
					 | 
				
			||||||
        raise ValueError(msg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not len(centroid) == 2:
 | 
					 | 
				
			||||||
        msg = "bounding_circle centre should contain 2D coordinates (e.g. (x, y))"
 | 
					 | 
				
			||||||
        raise ValueError(msg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if polygon_radius <= 0:
 | 
					    if polygon_radius <= 0:
 | 
				
			||||||
        msg = "bounding_circle radius should be > 0"
 | 
					        msg = "bounding_circle radius should be > 0"
 | 
				
			||||||
        raise ValueError(msg)
 | 
					        raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # 1.3 Check `rotation` has an appropriate value
 | 
					    # 1.3 Check `rotation` has an appropriate value
 | 
				
			||||||
    if not isinstance(rotation, (int, float)):
 | 
					    if not isinstance(rotation, (int, float)):
 | 
				
			||||||
        msg = "rotation should be an int or float"
 | 
					        msg = "rotation should be an int or float"  # type: ignore[unreachable]
 | 
				
			||||||
        raise ValueError(msg)
 | 
					        raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # 2. Define Helper Functions
 | 
					    # 2. Define Helper Functions
 | 
				
			||||||
    def _apply_rotation(point: list[float], degrees: float) -> tuple[int, int]:
 | 
					    def _apply_rotation(point: list[float], degrees: float) -> tuple[float, float]:
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            round(
 | 
					            round(
 | 
				
			||||||
                point[0] * math.cos(math.radians(360 - degrees))
 | 
					                point[0] * math.cos(math.radians(360 - degrees))
 | 
				
			||||||
| 
						 | 
					@ -1082,7 +1166,7 @@ def _compute_regular_polygon_vertices(
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _compute_polygon_vertex(angle: float) -> tuple[int, int]:
 | 
					    def _compute_polygon_vertex(angle: float) -> tuple[float, float]:
 | 
				
			||||||
        start_point = [polygon_radius, 0]
 | 
					        start_point = [polygon_radius, 0]
 | 
				
			||||||
        return _apply_rotation(start_point, angle)
 | 
					        return _apply_rotation(start_point, angle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,5 +18,10 @@ class ImagingDecoder:
 | 
				
			||||||
class ImagingEncoder:
 | 
					class ImagingEncoder:
 | 
				
			||||||
    def __getattr__(self, name: str) -> Any: ...
 | 
					    def __getattr__(self, name: str) -> Any: ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _Outline:
 | 
				
			||||||
 | 
					    def close(self) -> None: ...
 | 
				
			||||||
 | 
					    def __getattr__(self, name: str) -> Any: ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def font(image: ImagingCore, glyphdata: bytes) -> ImagingFont: ...
 | 
					def font(image: ImagingCore, glyphdata: bytes) -> ImagingFont: ...
 | 
				
			||||||
 | 
					def outline() -> _Outline: ...
 | 
				
			||||||
def __getattr__(name: str) -> Any: ...
 | 
					def __getattr__(name: str) -> Any: ...
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user