roundedrectangles

Method A

From the cairo samples, modified. Gives a very nice shape but takes width and height only as guides, however width and height do not render properly and are affected by radius.

def roundedrecA(self,cr,x,y,width,height,radius=5):
    #/* a custom shape, that could be wrapped in a function */
    #radius = 5  #/*< and an approximate curvature radius */
    x0       = x+radius/2.0   #/*< parameters like cairo_rectangle */
    y0       = y+radius/2.0
    rect_width  = width - radius
    rect_height = height - radius

    cr.save()
    #cr.set_line_width (0.04)
    #self.snippet_normalize (cr, width, height)

    x1=x0+rect_width
    y1=y0+rect_height
    #if (!rect_width || !rect_height)
    #    return
    if rect_width/2<radius:
        if rect_height/2<radius:
            cr.move_to  (x0, (y0 + y1)/2)
            cr.curve_to (x0 ,y0, x0, y0, (x0 + x1)/2, y0)
            cr.curve_to (x1, y0, x1, y0, x1, (y0 + y1)/2)
            cr.curve_to (x1, y1, x1, y1, (x1 + x0)/2, y1)
            cr.curve_to (x0, y1, x0, y1, x0, (y0 + y1)/2)
        else:
            cr.move_to  (x0, y0 + radius)
            cr.curve_to (x0 ,y0, x0, y0, (x0 + x1)/2, y0)
            cr.curve_to (x1, y0, x1, y0, x1, y0 + radius)
            cr.line_to (x1 , y1 - radius)
            cr.curve_to (x1, y1, x1, y1, (x1 + x0)/2, y1)
            cr.curve_to (x0, y1, x0, y1, x0, y1- radius)

    else:
        if rect_height/2<radius:
            cr.move_to  (x0, (y0 + y1)/2)
            cr.curve_to (x0 , y0, x0 , y0, x0 + radius, y0)
            cr.line_to (x1 - radius, y0)
            cr.curve_to (x1, y0, x1, y0, x1, (y0 + y1)/2)
            cr.curve_to (x1, y1, x1, y1, x1 - radius, y1)
            cr.line_to (x0 + radius, y1)
            cr.curve_to (x0, y1, x0, y1, x0, (y0 + y1)/2)
        else:
            cr.move_to  (x0, y0 + radius)
            cr.curve_to (x0 , y0, x0 , y0, x0 + radius, y0)
            cr.line_to (x1 - radius, y0)
            cr.curve_to (x1, y0, x1, y0, x1, y0 + radius)
            cr.line_to (x1 , y1 - radius)
            cr.curve_to (x1, y1, x1, y1, x1 - radius, y1)
            cr.line_to (x0 + radius, y1)
            cr.curve_to (x0, y1, x0, y1, x0, y1- radius)

    cr.close_path ()

    cr.restore()

Method B

From mono moonlight aka mono silverlight. Works very well on larger shapes.

def roundedrecMoonlight(self,cr,x,y,w,h,radius_x=5,radius_y=5):
    #from mono moonlight aka mono silverlight
    #test limits (without using multiplications)
    # http://graphics.stanford.edu/courses/cs248-98-fall/Final/q1.html
    ARC_TO_BEZIER = 0.55228475
    if radius_x > w - radius_x:
        radius_x = w / 2
    if radius_y > h - radius_y:
        radius_y = h / 2

    #approximate (quite close) the arc using a bezier curve
    c1 = ARC_TO_BEZIER * radius_x
    c2 = ARC_TO_BEZIER * radius_y

    cr.new_path();
    cr.move_to ( x + radius_x, y)
    cr.rel_line_to ( w - 2 * radius_x, 0.0)
    cr.rel_curve_to ( c1, 0.0, radius_x, c2, radius_x, radius_y)
    cr.rel_line_to ( 0, h - 2 * radius_y)
    cr.rel_curve_to ( 0.0, c2, c1 - radius_x, radius_y, -radius_x, radius_y)
    cr.rel_line_to ( -w + 2 * radius_x, 0)
    cr.rel_curve_to ( -c1, 0, -radius_x, -c2, -radius_x, -radius_y)
    cr.rel_line_to (0, -h + 2 * radius_y)
    cr.rel_curve_to (0.0, -c2, radius_x - c1, -radius_y, radius_x, -radius_y)
    cr.close_path ()

Method C

I can't remember where I got this. If you are the author please add your name. Works well on smaller shapes.

def roundedrec(self,context,x,y,w,h,r = 10):
    "Draw a rounded rectangle"
    #   A****BQ
    #  H      C
    #  *      *
    #  G      D
    #   F****E

    context.move_to(x+r,y)                      # Move to A
    context.line_to(x+w-r,y)                    # Straight line to B
    context.curve_to(x+w,y,x+w,y,x+w,y+r)       # Curve to C, Control points are both at Q
    context.line_to(x+w,y+h-r)                  # Move to D
    context.curve_to(x+w,y+h,x+w,y+h,x+w-r,y+h) # Curve to E
    context.line_to(x+r,y+h)                    # Line to F
    context.curve_to(x,y+h,x,y+h,x,y+h-r)       # Curve to G
    context.line_to(x,y+r)                      # Line to H
    context.curve_to(x,y,x,y,x+r,y)             # Curve to A
    return

Method D

Created by Helton Moraes (heltonbiker at gmail dot com). Uses round arcs and takes advantage of the segment-creation property of cairo.arc() - just draw the arcs, no need to draw straight segments in between.

def draw_rounded(cr, area, radius):
    """ draws rectangles with rounded (circular arc) corners """
    from math import pi
    a,b,c,d=area
    cr.arc(a + radius, c + radius, radius, 2*(pi/2), 3*(pi/2))
    cr.arc(b - radius, c + radius, radius, 3*(pi/2), 4*(pi/2))
    cr.arc(b - radius, d - radius, radius, 0*(pi/2), 1*(pi/2))  # ;o)
    cr.arc(a + radius, d - radius, radius, 1*(pi/2), 2*(pi/2))
    cr.close_path()
    cr.stroke()

################################################################

### EXAMPLE
import cairo, Image

w,h = 800, 600
offset = 100
fig_size = (w,h)

# an area with coordinates of
# (top, bottom, left, right) edges in absolute coordinates:
inside_area = (offset, w-offset, offset, h-offset)

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, *fig_size)
cr = cairo.Context(surface)
cr.set_line_width(3)
cr.set_source_rgb(1,1,1)

draw_rounded(cr, inside_area, 150)

im = Image.frombuffer("RGBA",
                       fig_size,
                       surface.get_data(),
                       "raw",
                       "BGRA",
                       0,1)
im.show()