python-pillow / Pillow

Python Imaging Library (Fork)
https://python-pillow.org
Other
12.2k stars 2.22k forks source link

How can I create squircle (not rounded rectangle)? #7023

Closed difhel closed 1 year ago

difhel commented 1 year ago

I want to put squircle to my image. A squircle is a shape intermediate between a square and a circle. image The task I am doing is to generate image from my Figma layout: temp In this case squircle is rounded rectangle with corner smoothing. It has following parameters: corner radius (in px) and corner smothing (from 0 to 1). image The existing rounded_rectangle function cannot be used because it does not have a corner smoothing parameter.

def rounded_rectangle(self, xy, radius=0, fill=None, outline=None, width=1):

How can I do it?

radarhere commented 1 year ago

Just to note - the diagram at the beginning of your post comes from https://en.wikipedia.org/wiki/Squircle.

I initially wondered in this could be done using ImageDraw.chord(), but I don't think so.

Instead, I found https://www.codespeedy.com/implement-a-superellipse-in-python/, which used matplotlib and NumPy.

I presume you're already using other parts of Pillow, so here is code that creates the plot, saves it in memory as an image, rereads that image with Pillow, and creates an animated GIF from the different values.

import matplotlib.pyplot as plt
import numpy as np
import io
from PIL import Image
a = 5
b = 4
t = np.linspace(0, 2 * np.pi, 100)
ims = []
for n in range(2, 10):
    x = ((np.abs(np.cos(t))) ** (2 / n)) * a * np.sign(np.cos(t))
    y = ((np.abs(np.sin(t))) ** (2 / n)) * b * np.sign(np.sin(t))
    plt.axis("off")
    plt.plot(x, y)
    with io.BytesIO() as f:
        plt.savefig(f, bbox_inches="tight")
        im = Image.open(f)
        im.load()
        ims.append(im)
    plt.clf()
ims[0].save("squircle.gif", save_all=True, append_images=ims[1:], disposal=1, duration=350, loop=0)

squircle

difhel commented 1 year ago

Thank you for your answer. Could you explain how the parameters in your code correlate with those in the screenshot in Figma? The thing is that I need to achieve a pixel-perfect implementation as on the layout.

Yay295 commented 1 year ago

I found a blog post by Figma about their squircles. It seems like they use Bézier curves, so I don't believe the code radarhere posted will be an exact match. They don't specify their exact formula, but I think it could be determined by following what they talk about in that blog post (you can start at the section titled "Keep it simple, squircle").

radarhere commented 1 year ago

I've found https://github.com/tienphaw/figma-squircle, which provides "Figma-flavored squircles for everyone" but "does not guarantee to produce the same results as you would get in Figma"

If a library dedicated to this purpose can't guarantee pixel perfect results, then I don't imagine that Figma has released enough information about their closed source software to make exact replication possible.

I thought about exporting the shape from Figma, but testing this with the CSS and SVG export methods, corner smoothing didn't change the output. You could export your squircle from Figma as a PNG, and then open and use that image in Pillow, either to paste or as a mask, but I suspect that wouldn't be satisfactory for your purposes.

difhel commented 1 year ago

Thanks for this git repository. It sounds like what I'm looking for. I wish I could do it with Pillow. I guess I'll be implementing it in JS then. I also tried the idea of exporting a PNG-squircle before writing the issue. However, the height of my squircle depends on the text inside it, so you can't export the whole squircle. I tried exporting only the top of the squircle to substitute text with background fill under it, but got corners too small. It turns out that corners rounding also depends on the size of the squircle, so the option to export in any form (even SVG) is unlikely to work.

In any case, thanks for your help. I'll leave the issue open for a while and close it if I don't get more options.

radarhere commented 1 year ago

"Figma corner smoothing" (or Figma squircles, or Figma superellipses) seems like very specific functionality to be a part of Pillow to my mind.

If you do want to try and approximate the functionality in Python, you could follow the documentation linked. Bezier curves are possible in matplotlib - https://matplotlib.org/stable/gallery/shapes_and_collections/quad_bezier.html

@nightadmin was there anything else, or can this be closed?

difhel commented 1 year ago

Thanks, I'll try JS Figma squircles realization.