Pygame Curve Library for Anti-Aliased Circles and Curved Surface Corners.

god0fgod 0 Tallied Votes 812 Views Share

I've created a few functions to be used with pygame which are based on anti-aliased curves. At the moment and likely forever, it has two useful functions:

aacircle(s,x,y,r,colour):

Draws an anti-aliased circle on the surface s at the starting point x and y with the radius r. The circle will be coloured with the colour parameter which takes the usual colour tuples eg. (160,200,255).

rounded_corners(s,r)

Takes the surface s and creates rounded corners with the radius r. The corners are anti-aliased. Please not due to a problem with pygame's aaline function, 2 pixels will be cropped from the top and bottom and 1 pixel with be cropped from the sides.

Run the library directly to load a demonstration. You should get

It's been tested on Mac OSX 10.6.1 Snow Leopard

#!/usr/bin/env python2.3
#
#  curvelib.py
#  
#
#  Created by Matthew Mitchell on 20/09/2009.
#  Copyright (c) 2009 Matthew Mitchell. All rights reserved.
#
import math,numpy,pygame
from pygame.locals import *
def basic_arc_gen(r): #For basic aliased circles
	arc = []
	radius_sq = float(r) ** float(2)
	for y in xrange(r):
		x = (radius_sq - float(y) ** float(2)) ** 0.5
		arc.append((x,y))
	for x in xrange(r):
		y = (radius_sq - float(x) ** float(2)) ** 0.5
		if not (x,y) in arc:
			arc.append((x,y))
	return arc
def arc_gen(r):
	arc = []
	for d in xrange(0,46):
		rad = math.radians(d)
		arc.append([math.cos(rad) * r,math.sin(rad) * r])
	for co in reversed(arc):
		arc.append([co[1],co[0]])
	return arc
def aacircle(s,x,y,r,colour):
	r -= 2
	x += 1
	y += 1
	circle = []
	arc = arc_gen(r)
	for c in arc:
		circle.append([c[0] + r + x,c[1] + r + y])
	arc.reverse()
	for c in arc:
		c[0] = r - c[0]
		circle.append([c[0] + x,c[1] + r + y])
	arc.reverse()
	for c in arc:
		c[1] = r - c[1]
		circle.append([c[0] +x,c[1] +y])
	arc.reverse()
	for c in arc:
		c[0] = r - c[0]
		circle.append([c[0] + r + x,c[1] + y])
	pxb = 0
	for px in circle:
		if pxb != 0:
			pygame.draw.aaline(s, colour, px, pxb)
		pxb = px
def rounded_corners(s,r):
	t = pygame.Surface((r,r))
	r -= 2
	arc = arc_gen(r)
	pxb = 0
	for px in arc:
		if pxb != 0:
			pygame.draw.aaline(t, (255,255,255), px, pxb)
		pxb = px
	pa = pygame.PixelArray(t)
	ab = pygame.surfarray.pixels_alpha(s)
	xlen = len(pa[0])
	ablen = [len(ab)-1,len(ab[1])-2]
	for y in xrange(len(pa)):
		a = 0
		for x in xrange(xlen):
			if pa[y][x] < a:
				alpha = 0
				ab[(ablen[0] - r) + y][(ablen[1] - r) + x] = alpha
				ab[(ablen[0] - r) + y][r - x] = alpha
				ab[r - y][(ablen[1] - r) + x] = alpha
				ab[r - y][ r - x] = alpha
			else:
				if pa[y][x] != 0:
					alpha = int(float(255) - float(pa[y][x]) ** (float(1)/float(3)))
					ab[(ablen[0] - r) + y][(ablen[1] - r) + x] = alpha
					ab[(ablen[0] - r) + y][r - x] = alpha
					ab[r - y][(ablen[1] - r) + x] = alpha
					ab[r - y][r - x] = alpha
				a = pa[y][x]
	for y in xrange(ablen[0]):
		ab[y][0] = 0
		ab[y][1] = 0
		ab[y][ablen[1]] = 0
		ab[y][ablen[1] + 1] = 0
	for x in xrange(ablen[1]):
		ab[0][x] = 0
		ab[ablen[0]][x] = 0
	del ab
	del pa
if __name__ == '__main__':	
	import sys
	pygame.init()
	window = pygame.display.set_mode((500,500))
	screen = pygame.Surface((400,400),SRCALPHA)
	screen.fill((200,120,250))
	rounded_corners(screen,60)
	aacircle(screen,100,100,100,(10,60,200))
	aacircle(screen,30,20,80,(180,150,60))
	window.blit(screen,(50,50))
	pygame.display.flip()
	while 1:
		events = pygame.event.get()
		for event in events:
			if event.type == QUIT:
				sys.exit()

Dani AI

Generated

provided a useful, hands‑on implementation for smoothed circles and corner curves. The approach is a good starting point, but modern Pygame offers built‑in primitives and safer array handling that simplify anti‑aliased shapes and avoid some of the surface‑locking/alpha pitfalls in older examples.

A practical, low‑effort replacement for drawing smooth filled circles is to use pygame.gfxdraw: draw the antialiased outline with aacircle and then fill it with filled_circle. This is the recommended pattern in the gfxdraw docs and is commonly used in examples. Example pattern:

import pygame
from pygame import gfxdraw

surf = pygame.Surface((r*2, r*2), pygame.SRCALPHA)
gfxdraw.aacircle(surf, r, r, r, color)
gfxdraw.filled_circle(surf, r, r, r, color)
dest_surface.blit(surf, (x-r, y-r))

This keeps the AA work on a small SRCALPHA surface and then blits the pre‑rendered corner/circle to the target, which is fast when you cache the small surfaces. (scuba.cs.uchicago.edu)

When manipulating per‑pixel alpha or doing masking, be careful: PixelArray and surfarray.pixels_* create direct references that lock the surface while they exist. Always close() / delete those references (or use the context manager) before doing further blits or saving; failing to release them is a common source of invisible/incorrect results. For alpha channel edits, surfarray.pixels_alpha returns a NumPy view — delete it (del alphas) or exit the scope before using the surface again. (pygame.org)

Troubleshooting tips: pygame.draw.aaline is fine for single AA lines but has no endcaps/thickness control, so for wider or closed shapes prefer gfxdraw (or aapolygon). If you need very high‑quality filled shapes, render them larger and pygame.transform.smoothscale() down (cache results), but be aware of alpha blending issues when shrinking transparent surfaces — prefill or manage alpha to avoid black fringes. Also consider porting to Python 3 / current pygame‑ce releases for ongoing fixes and performance improvements. (pyga.me)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.