Search code examples
pythonpygamespriteblending

Setting a pygame surface to have rounded corners


This Rectangle class extends from pygame.sprite. I'd like to use set_rounded to modify how round the corners of the rect are. For example https://i.sstatic.net/uyKWC.jpg

class Rectangle(pg.sprite.Sprite):
    def __init__(self):
        pg.sprite.Sprite.__init__(self)
        self.original_image = pg.Surface((10, 10))
        self.image = self.original_image
        self.rect = self.image.get_rect()


    def set_rounded(self, roundness):
        pass

The roundness argument would determine the radius of the rounded rect.


Solution

  • You can achieve what you want by setting the key word argument border_radius of the function pygame.draw.rect.

    Create a rectangle the same size as the image and and per pixel alpha (SRCALPHA) and draw a completely white, opaque image with round corners on it:

    size = self.original_image.get_size()
    self.rect_image = pg.Surface(size, pg.SRCALPHA)
    pg.draw.rect(self.rect_image, (255, 255, 255), (0, 0, *size), border_radius=roundness)
    

    Copy the original image and use the BLEND_RGBA_MIN blending mode to blend the rectangle with the image (see pygame.Surface.blit):

    self.image = self.original_image.copy().convert_alpha()
    self.image.blit(self.rect_image, (0, 0), None, pg.BLEND_RGBA_MIN) 
    

    Note, the keyword attribute border_radius is a new feature in Pygame 2.0.


    If you can't use version 2.0, you'll need to stick the rounded rectangle together yourself:

    class Rectangle(pg.sprite.Sprite):
        # [...]
    
        def set_rounded(self, roundness):
            size = self.original_image.get_size()
            self.rect_image = pg.Surface(size, pg.SRCALPHA)
            
            #pg.draw.rect(self.rect_image, (255, 255, 255), (0, 0, *size), border_radius=roundness)
    
            r, c = roundness, (255, 255, 255)
            pg.draw.rect(self.rect_image, c, (r, 0, size[0]-2*r, size[1]))
            pg.draw.rect(self.rect_image, c, (0, r, size[0], size[1]-2*r))
            for cpt in [(r, r), (size[0]-r, r), (r, size[1]-r), (size[0]-r, size[1]-r)]:  
                pg.draw.circle(self.rect_image, c, cpt, r)
    
            self.image = self.original_image.copy().convert_alpha()
            self.image.blit(self.rect_image, (0, 0), None, pg.BLEND_RGBA_MIN) 
    

    See the example:

    import pygame as pg
    
    class Rectangle(pg.sprite.Sprite):
        def __init__(self):
            pg.sprite.Sprite.__init__(self)
            self.original_image = pg.Surface((100, 100))
            self.original_image.fill((255, 0, 0))
            self.image = self.original_image
            self.rect = self.image.get_rect()
    
        def set_rounded(self, roundness):
            size = self.original_image.get_size()
            self.rect_image = pg.Surface(size, pg.SRCALPHA)
            pg.draw.rect(self.rect_image, (255, 255, 255), (0, 0, *size), border_radius=roundness)
    
            self.image = self.original_image.copy().convert_alpha()
            self.image.blit(self.rect_image, (0, 0), None, pg.BLEND_RGBA_MIN) 
    
    pg.init()
    window = pg.display.set_mode((200, 200))
    
    rect_object = Rectangle()
    rect_object.set_rounded(30)
    rect_object.rect.center = window.get_rect().center
    group = pg.sprite.Group(rect_object)
    
    run = True
    while run:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                run = False
    
        window.fill((128, 128, 128))
        group.draw(window)
        pg.display.flip()