Pygame draw antialiased filled polygon

Question:

The documentation says “For aapolygon, use aalines with the ‘closed’ parameter.”, but pygame.draw.aalines doesn’t let me specify the width (0 = filled), making it not fill the surface. This looks just terrible:
enter image description here

These circles look much better:

enter image description here

How can I do this?

I generate the surface using quadratic beziers, whose coordinates get appended to a list. Then I draw it onto the surface like this (In the image above I did this twice, once for the outer circle and once for the inner circle):

pygame.draw.polygon(self.image,fclr,self.points)

And the drawing code (shape.image is the same surface as self.image in the code above):

screen.fill((0,0,0))
screen.blit(shape.image,(100,100))
pygame.display.flip()
Asked By: user2746752

||

Answers:

martineau’s comment is on point: In order to draw antialiased filled shapes with pygame, use the module gfxdraw and draw one regular filled shape and one antialiased outline. From http://www.pygame.org/docs/ref/gfxdraw.html:

“To draw an anti aliased and filled shape, first use the aa* version of the function, and then use the filled version.”

Note that you need to import gfxdraw explicitly, i.e. from pygame import gfxdraw.

Answered By: andreasdr

I made some tests and with my pygame version (2.1.2) drawing aapolygons (lines) on top of a polygon (jagged edges) results in jaggies on the right and lower sides because the lines are inside the jagged polygon area. I whipped up a quick test for a workaround function that uses supersampling to convert the jagged polygon into a smooth one. For anyone interested here is the code:

def draw_aapolygon(surface, color, points, scale=2):                            
    """                                                                         
    Draw antialiased polygon using supersampling.                               
    """                                                                         
    # Calculate minimum x and y values.                                         
    x_coords = tuple(x for x, _ in points)                                      
    x_min, x_max = min(x_coords), max(x_coords)                                 
    y_coords = tuple(y for _, y in points)                                      
    y_min, y_max = min(y_coords), max(y_coords)                                 
    # Calculate width and height of target area.                                
    w = x_max - x_min + 1                                                       
    h = y_max - y_min + 1                                                       
    # Create scaled surface with properties of target surface.                  
    s = pygame.Surface((w * scale, h * scale), 0, surface)                      
    s_points = [((x - x_min) * scale, (y - y_min) * scale)                      
                for x, y in points]                                             
    pygame.draw.polygon(s, color, s_points)                                     
    # Scale down surface to target size for supersampling effect.               
    s2 = pygame.transform.smoothscale(s, (w, h))                                
    # Paint smooth polygon on target surface.                                   
    surface.blit(s2, (x_min, y_min))

Feel free to point out errors, except that is uses quite a bit of resources, which is obvious.

Answered By: hochl