Python Imaging Library (PIL) Drawing--Rounded rectangle with gradient

后端 未结 3 1574
半阙折子戏
半阙折子戏 2020-12-16 23:25

I am trying to use PIL to draw a rectangle with rounded corners and a gradient fill for the color. I found a cool web site ( http://web.archive.org/web/20130306020911/http:/

相关标签:
3条回答
  • 2020-12-16 23:59

    In case someone in the future is looking for a slightly more turn-key solution that can be monkey patched onto ImageDraw, I wrote the following.

    Hopefully it helps.

    Example: Code:

    from PIL.ImageDraw import ImageDraw
    
    
    def rounded_rectangle(self: ImageDraw, xy, corner_radius, fill=None, outline=None):
        upper_left_point = xy[0]
        bottom_right_point = xy[1]
        self.rectangle(
            [
                (upper_left_point[0], upper_left_point[1] + corner_radius),
                (bottom_right_point[0], bottom_right_point[1] - corner_radius)
            ],
            fill=fill,
            outline=outline
        )
        self.rectangle(
            [
                (upper_left_point[0] + corner_radius, upper_left_point[1]),
                (bottom_right_point[0] - corner_radius, bottom_right_point[1])
            ],
            fill=fill,
            outline=outline
        )
        self.pieslice([upper_left_point, (upper_left_point[0] + corner_radius * 2, upper_left_point[1] + corner_radius * 2)],
            180,
            270,
            fill=fill,
            outline=outline
        )
        self.pieslice([(bottom_right_point[0] - corner_radius * 2, bottom_right_point[1] - corner_radius * 2), bottom_right_point],
            0,
            90,
            fill=fill,
            outline=outline
        )
        self.pieslice([(upper_left_point[0], bottom_right_point[1] - corner_radius * 2), (upper_left_point[0] + corner_radius * 2, bottom_right_point[1])],
            90,
            180,
            fill=fill,
            outline=outline
        )
        self.pieslice([(bottom_right_point[0] - corner_radius * 2, upper_left_point[1]), (bottom_right_point[0], upper_left_point[1] + corner_radius * 2)],
            270,
            360,
            fill=fill,
            outline=outline
        )
    
    
    ImageDraw.rounded_rectangle = rounded_rectangle
    
    0 讨论(0)
  • 2020-12-17 00:07

    This is a very brute force method, but it gets the job done. Code to produce the gradients was borrowed from here.

    from PIL import Image, ImageDraw
    
    def channel(i, c, size, startFill, stopFill):
        """calculate the value of a single color channel for a single pixel"""
        return startFill[c] + int((i * 1.0 / size) * (stopFill[c] - startFill[c]))
    
    def color(i, size, startFill, stopFill):
        """calculate the RGB value of a single pixel"""
        return tuple([channel(i, c, size, startFill, stopFill) for c in range(3)])
    
    def round_corner(radius):
        """Draw a round corner"""
        corner = Image.new('RGBA', (radius, radius), (0, 0, 0, 0))
        draw = ImageDraw.Draw(corner)
        draw.pieslice((0, 0, radius * 2, radius * 2), 180, 270, fill="blue")
        return corner
    
    def apply_grad_to_corner(corner, gradient, backwards = False, topBottom = False):
        width, height = corner.size
        widthIter = range(width)
    
        if backwards:
            widthIter.reverse()
    
        for i in xrange(height):
            gradPos = 0
        for j in widthIter:
                    if topBottom:
                        pos = (i,j)
                    else:
                        pos = (j,i)
            pix = corner.getpixel(pos)
                gradPos+=1
            if pix[3] != 0:
                corner.putpixel(pos,gradient[gradPos])
    
        return corner
    
    def round_rectangle(size, radius, startFill, stopFill, runTopBottom = False):
        """Draw a rounded rectangle"""
        width, height = size
        rectangle = Image.new('RGBA', size)
    
        if runTopBottom:
          si = height
        else:
          si = width
    
        gradient = [ color(i, width, startFill, stopFill) for i in xrange(si) ]
    
        if runTopBottom:
            modGrad = []
            for i in xrange(height):
               modGrad += [gradient[i]] * width
            rectangle.putdata(modGrad)
        else:
            rectangle.putdata(gradient*height)
    
        origCorner = round_corner(radius)
    
        # upper left
        corner = origCorner
        apply_grad_to_corner(corner,gradient,False,runTopBottom)
        rectangle.paste(corner, (0, 0))
    
        # lower left
        if runTopBottom: 
            gradient.reverse()
            backwards = True
        else:
            backwards = False
    
    
        corner = origCorner.rotate(90)
        apply_grad_to_corner(corner,gradient,backwards,runTopBottom)
        rectangle.paste(corner, (0, height - radius))
    
        # lower right
        if not runTopBottom: 
            gradient.reverse()
    
        corner = origCorner.rotate(180)
        apply_grad_to_corner(corner,gradient,True,runTopBottom)
        rectangle.paste(corner, (width - radius, height - radius))
    
        # upper right
        if runTopBottom: 
            gradient.reverse()
            backwards = False
        else:
            backwards = True
    
        corner = origCorner.rotate(270)
        apply_grad_to_corner(corner,gradient,backwards,runTopBottom)
        rectangle.paste(corner, (width - radius, 0))
    
        return rectangle
    
    img = round_rectangle((200, 200), 70, (255,0,0), (0,255,0), True)
    img.save("test.png", 'PNG')
    

    Running from left to right (runTopBottom = False):

    enter image description here

    Running from top to bottom (runTopBottom = True):

    enter image description here

    0 讨论(0)
  • 2020-12-17 00:20

    For anyone looking for an updated version, this is a modified version of Whelchel's answer using Pillow 7.2.0 instead of PIL. (I had a problem with outlines using the previous version)

    Code:

    def rounded_rectangle(self: ImageDraw, xy, corner_radius, fill=None, outline=None):
        upper_left_point = xy[0]
        bottom_right_point = xy[1]
    
    
        self.pieslice([upper_left_point, (upper_left_point[0] + corner_radius * 2, upper_left_point[1] + corner_radius * 2)],
            180,
            270,
            fill=fill,
            outline=outline
        )
        self.pieslice([(bottom_right_point[0] - corner_radius * 2, bottom_right_point[1] - corner_radius * 2), bottom_right_point],
            0,
            90,
            fill=fill,
            outline=outline
        )
        self.pieslice([(upper_left_point[0], bottom_right_point[1] - corner_radius * 2), (upper_left_point[0] + corner_radius * 2, bottom_right_point[1])],
            90,
            180,
            fill=fill,
            outline=outline
        )
        self.pieslice([(bottom_right_point[0] - corner_radius * 2, upper_left_point[1]), (bottom_right_point[0], upper_left_point[1] + corner_radius * 2)],
            270,
            360,
            fill=fill,
            outline=outline
        )
        self.rectangle(
            [
                (upper_left_point[0], upper_left_point[1] + corner_radius),
                (bottom_right_point[0], bottom_right_point[1] - corner_radius)
            ],
            fill=fill,
            outline=fill
        )
        self.rectangle(
            [
                (upper_left_point[0] + corner_radius, upper_left_point[1]),
                (bottom_right_point[0] - corner_radius, bottom_right_point[1])
            ],
            fill=fill,
            outline=fill
        )
        self.line([(upper_left_point[0] + corner_radius, upper_left_point[1]), (bottom_right_point[0] - corner_radius, upper_left_point[1])], fill=outline)
        self.line([(upper_left_point[0] + corner_radius, bottom_right_point[1]), (bottom_right_point[0] - corner_radius, bottom_right_point[1])], fill=outline)
        self.line([(upper_left_point[0], upper_left_point[1] + corner_radius), (upper_left_point[0], bottom_right_point[1] - corner_radius)], fill=outline)
        self.line([(bottom_right_point[0], upper_left_point[1] + corner_radius), (bottom_right_point[0], bottom_right_point[1] - corner_radius)], fill=outline)
    
    0 讨论(0)
提交回复
热议问题