[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 text with random lines with PIL in Python

[Text filled with random lines using PIL and Python]

Just a few more shape-filling examples before I move on to something else. (These are pretty fun to do and I don't get paid for this so I'm going to have fun while I can!😉)

You could make some sort of RandomLineBrush class to fill objects with random lines, but this seems like something we won't need to do very often so I'm just going to use the existing ImageBrush class. (As will be next couple of examples.)

Because we're reusing the ImageBrush class, all of the work is in the main program's code, which is shown in the following.

def fill_shapes(self): # Make an image to draw on. self.canvas.update() wid = self.canvas.winfo_width() hgt = self.canvas.winfo_height() image = Image.new('RGBA', (wid, hgt)) dr = ImageDraw.Draw(image) # Fill with a gradient. start_point = (0, 0) end_point = (0, hgt) start_color = [200, 200, 255, 255] end_color = [0, 0, 200, 255] brush = LinearGradientBrush(start_point, end_point, start_color, end_color) bounds = (0, 0, wid, hgt) pil_fill_rectangle(image, brush, bounds) # Get text metrics. text = 'Random\nLines' font = ImageFont.truetype('times.ttf', 150) align = 'center' anchor = 'mm' cx = wid / 2 cy = hgt / 2 text_bounds = dr.textbbox((cx, cy), text, font=font, align=align, anchor=anchor) # Make the text fill image. text_xmin, text_ymin, text_xmax, text_ymax = text_bounds text_wid = math.floor(text_xmax - text_xmin) text_hgt = math.floor(text_ymax - text_ymin) fill_image = Image.new('RGBA', (text_wid, text_hgt)) fill_dr = ImageDraw.Draw(fill_image) cx -= text_xmin cy -= text_ymin # Fill the image with random lines. radius = max(text_wid, text_hgt) thickness = 2 num_lines = 300 for i in range(num_lines): # Pick a random point in the region. px = random.uniform(text_xmin, text_xmax) py = random.uniform(text_ymin, text_ymax) # Pick a random direction. theta = random.uniform(0, math.pi) # Generate the line. x1 = px + math.cos(theta) * radius y1 = py + math.sin(theta) * radius x2 = px - math.cos(theta) * radius y2 = py - math.sin(theta) * radius # Generate a random color. color = tuple(random.choices(range(256), k=3) + [255]) # Draw the line. fill_dr.line((x1, y1, x2, y2), fill=color, width=thickness) # Make the image brush. brush = ImageBrush(fill_image) # Fill the text. cx = wid / 2 cy = hgt / 2 thickness = 1 # pil_draw_outline_text(dr, cx, cy, text, 'blue', font, anchor, align, # thickness) pil_fill_text(image, brush, text, font, cx, cy, align, anchor) # Display the result. self.photo_image = ImageTk.PhotoImage(image) self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo_image)

The code begins as usual, getting the canvas's current dimensions, making an image to fit, and filling the image with a gradient courtesy of the LinearGradientBrush class.

Next, the code creates an image to hold the text's fill, which in this example is a bunch of random lines. It then uses a loop to fill the image with lines.

Drawing "random" objects is often tricky because, if you don't do it carefully, you'll end up with the objects arranged in some sort of pattern like clustered in one area or with a higher probability of being in the center. To position the random lines, this code first picks a random point (px, py) within the image for the line to pass through. It then picks a random direction for the line. It uses math.sin and math.cos to find two other points on the line distance radius away from the point (px, py). The radius value is the larger of the image's width and height, so the new points are guaranteed to be off of the image and that means the line will pass completely through the image.

The code picks a random color for the line and draws it.

After the program has created the line-filled background image, it uses the ImageBrush class to fill the text and, as they say, Bob's your uncle. Uncomment the call to pil_draw_outline_text if you want to draw an outline around the text.

Conclusion

Note that the example generates new random lines each time you run it and some runs produce a better result than others. You could seed the random number generator to get consistent "random" lines each time. You could also modify the program to pick other random colors (for example, from a palette of high-contrast colors), change their thicknesses, etc.

The ImageBrush class makes it pretty easy to fill shapes with just about anything. I'll post another example or two soon. Until then, download the example to experiment with it and to see additional details.

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