[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky]
[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Fill a polygon with a RadialGradientBrush with PIL in Python

[Four polygons filled with radial gradient brushes using PIL and Python]

The post Make a RadialGradientBrush class for use with PIL in Python explained how you can make a RadialGradientBrush class and use it to fill a rectangle with PIL in Python. This example shows how you can fill a polygon with that brush.

pil_fill_polygon

The RadialGradientBrush class is the same as before. The only new element is the following pil_fill_polygon method.

def pil_fill_polygon(image, brush, points): '''Fill the polygon with the brush.''' # Get the polygon's bounds. bounds = get_bounds(points) # Get the filled bounding rectangle. rect_image = brush.make_image(bounds) # Translate the points. xmin = bounds[0] ymin = bounds[1] points = [(point[0]-xmin, point[1]-ymin) for point in points] # Make a polygon mask image. wid = bounds[2] - bounds[0] hgt = bounds[3] - bounds[1] mask = Image.new('L', (wid, hgt), 'black') dr = ImageDraw.Draw(mask) dr.polygon(points, 'white') # Draw the filled rectangle on the PIL image. image.paste(rect_image, (bounds[0], bounds[1]), mask)

This method calls the get_bounds function described shortly to get the polygon's bounds. It then uses the brush's make_image function to make a rectangle sized to fit the polygon and filled with the brush's gradient. The code then translates the polygon's points so they lie on the new image, which has upper left corner at (0, 0).

Next, the method creates a mask image. It makes a new black image with the same size and the brush image. It then draws the polygon on the mask image, filling it with white.

Now the code pastes the brush's filled rectangle onto the image passed into the function. It uses the mask image as a mask so only the parts of the brush image that correspond to white pixels in the mask are copied onto the result.

That's all there is to it. Overall it's a relatively simple exercise in pasting an image with a mask.

get_bounds

The following code shows the get_bounds helper function.

def get_bounds(points): '''Get the bounds for these points.''' xmin = ymin = math.inf xmax = ymax = -math.inf for x, y in points: xmin = min(xmin, x) xmax = max(xmax, x) ymin = min(ymin, y) ymax = max(ymax, y) return round(xmin), round(ymin), round(xmax), round(ymax)

This function loops through the points in a list and keeps track of the points' bounds. After it has processed all of the points, it returns the points' smallest and largest X and Y coordinates.

Main Program

The main program creates its brush much as the previous example did. It then uses the new pil_fill_polygon method to fill a polygon with it.

The following code shows how the program draws its first polygon.

def fill_polygons(self): # Get the canvas's dimensions. self.canvas.update() wid = self.canvas.winfo_width() hgt = self.canvas.winfo_height() # Make an image to draw on. image = Image.new('RGBA', (wid, hgt), 'wheat') dr = ImageDraw.Draw(image) margin = 10 rect_wid = round((wid - 4 * margin) / 2) rect_hgt = round((hgt - 4 * margin) / 2) show_centers = False # Polygon 1. bbox = [margin, margin, margin + rect_wid, margin + rect_hgt] points = get_star_points(-math.pi / 2, 7, 3, bbox) center = ((bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2) center_color = (255, 255, 0, 255) outer_color = (0, 200, 0, 255) brush = RadialGradientBrush(center, center_color, outer_color) pil_fill_polygon(image, brush, points) dr.polygon(points, fill=None, outline='black') if show_centers: pil_draw_circle(dr, center, 3, None, 'black')

The code gets the dimensions of the program's canvas widget and makes an image to fit. It creates a bounding box and then uses the get_star_points from the post Draw stars in Python to get the vertices of a seven-pointed star. Next, the code defines the brush's center point and colors.

The code then calls pil_fill_polygon to draw the filled star on the image and uses PIL's polygon method to outline the star. Finally, if show_centers is True, the program draws a circle to show at the brush's center.

Conclusion

The only new code here is the pil_fill_polygon method, which uses a mask to draw the brush's filled rectangle only over the polygon. You can use a similar technique to draw filled text, ellipses, pie slices, rounded rectangles, and any other shape that PIL can draw. I'll probably implement a few of those in later posts, but in my next post I want to show how you can make a more powerful radial gradient brush.

Download the example to experiment with it and to see additional details.

© 2024 - 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.