Saving PNG from PyGame

The latest version, 1.8, of PyGame can save PNGs directly from a Surface: pygame.Image.save(mySurface, 'myimagefile.png'). But what if you want to support an older version of PyGame, such as the one available for the N800 or the XO? Well, assuming you have access to the Python Image Library, you can use that:

import Image # from PIL
import pygame

def pygame_to_pil_img(pg_surface):
    imgstr = pygame.image.tostring(pg_surface, 'RGB')
    return Image.fromstring('RGB', pg_surface.get_size(), imgstr)

def pil_to_pygame_img(pil_img):
    imgstr = pil_img.tostring()
    return pygame.image.fromstring(imgstr, pil_img.size, 'RGB')

Once you have a PyGame Image, you can save it to PNG easily: myImage.save('myfilename.png')

I’ve found myself looking for this code snippet more than once, now I can Google for it more readily, and maybe someone else will find it helpful too.

Drawing Hexmaps

The other day, Thomas Guest talked about drawing chessboards, and ended with a challenge. I wanted to answer a different challenge, however. What if, instead of drawing on a rectangular grid, we wanted to draw on a hexagonal grid? The following is my slapdash answer. For real-world use I’d make nice classes and pass more parameters to the methods, but to demonstrate the math I’m just going to use global constants and functions.

Like Thomas’ article, I will show solutions using several different tools, in this case Apple’s Core Graphics, PyGame, Python Imaging Library (PIL), and SVG. All of these solutions will use the same constants and math:

# Constants used by each solution

from math import sin, cos, pi, sqrt
THETA = pi / 3.0 # Angle from one point to the next
HEXES_HIGH = 8 # How many rows of hexes
HEXES_WIDE = 5 # How many hexes in a row
RADIUS = 30 # Size of a hex
HALF_RADIUS = RADIUS / 2.0
HALF_HEX_HEIGHT = sqrt(RADIUS ** 2 - HALF_RADIUS ** 2)
IMAGE_WIDTH = int(RADIUS * (HEXES_WIDE * 3 + .5))
IMAGE_HEIGHT = int(HALF_HEX_HEIGHT * (HEXES_HIGH + 1))

# Functions (generators) used by each solution

def hex_points(x,y):
    '''Given x and y of the origin, return the six points around the origin of RADIUS distance'''
    for i in range(6):
        yield cos(THETA * i) * RADIUS + x, sin(THETA * i) * RADIUS + y

def hex_centres():
    for x in range(HEXES_WIDE):
        for y in range(HEXES_HIGH):
            yield (x * 3 + 1) * RADIUS + RADIUS * 1.5 * (y % 2), (y + 1) * HALF_HEX_HEIGHT

Now, given the above, what does the code look like to draw the hexes? Because each library handles colours slightly differently, we will need a generator for colours (and we will need more than just black and white as the chessboard used, because each hex borders on six others). I haven’t given a lot of thought to optimal colouring schemes: each colour generator simply produces red, yellow, blue, and green in a cycle. Here is the image produced by the Core Graphics solution, followed by the code:

Hex Image 1

def quartz_colours():
    while True:
        yield 1,0,0,1 # red
        yield 1,1,0,1 # yellow
        yield 0,0,1,1 # blue
        yield 0,1,0,1 # green

def quartz_hex():
    '''Requires a Mac with OS 10.4 or better and the Developer Tools installed'''
    import CoreGraphics as cg
    colours = quartz_colours()
    cs = cg.CGColorSpaceCreateDeviceRGB()
    c = cg.CGBitmapContextCreateWithColor(IMAGE_WIDTH, IMAGE_HEIGHT, cs, (0,0,0,.2))
    c.saveGState()
    c.setRGBStrokeColor(0,0,0,0)
    c.setLineWidth(0)
    for x,y in hex_centres():
        c.beginPath()
        c.setRGBFillColor(*colours.next())
        points = list(hex_points(x,y))
        c.moveToPoint(*points[-1])
        [c.addLineToPoint(*pt) for pt in points]
        c.drawPath(cg.kCGPathFill)
    c.restoreGState()
    c.writeToFile("quartz_hexes.png", cg.kCGImageFormatPNG)

Now for some cross-platform examples. Here is the image generated by PyGame, followed by that code:

Hex Image 2

def pygame_colours():
    while True:
        yield 255, 0, 0 # red
        yield 255, 255, 0 # yellow
        yield 0, 0, 255 # blue
        yield 0, 255, 0 # green

def pygame_hex():
    '''Requires PyGame 1.8 or better to save as PNG'''
    import pygame
    pygame.init()
    screen = pygame.display.set_mode((IMAGE_WIDTH, IMAGE_HEIGHT))
    colours = pygame_colours()
    for x,y in hex_centres():
        pygame.draw.polygon(screen, colours.next(), list(hex_points(x,y)))
    pygame.image.save(screen, 'pygame_hexes.png')

When you run the PyGame script, it will actually pop up a window very briefly, draw into the window, save the result, and close the window. I also didn’t get the PyGame script to add transparency for the background, although I think it could be added fairly easily. Now, for the web, here is a solution in SVG, with the image captured by screenshot in Safari, followed by the Python code, and the resulting SVG code:

Hex Image 3

def svg_colours():
    while True:
        yield 'rgb(255, 0, 0)'
        yield 'rgb(255, 255, 0)'
        yield 'rgb(0, 0, 255)'
        yield 'rgb(0, 255, 0)'

def svg_hex():
    out = open('svg_hexes.svg', 'w')
    print >> out, '''<?xml version="1.0" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <svg width="%spx" height="%spx" version="1.1" xmlns="http://www.w3.org/2000/svg">''' % (IMAGE_WIDTH, IMAGE_HEIGHT)
    colours = svg_colours()
    for pt in hex_centres():
        print >> out, '<polygon fill="%s" stroke-width="0" points="%s" />' % (colours.next(),  ' '.join(["%s,%s" % (x,y) for (x,y) in hex_points(*pt)]))
    print >> out, '</svg>'
    out.close()

And here is the SVG created by the above script: SVG Hexes

Finally, one library which overlaps with the ones used by the Chessboard example: Python Imaging Library.

Hex Image 4

pil_colours = pygame_colours  # same format works, so we'll re-use it

def pil_hex():
    import Image, ImageDraw
    image = Image.new("RGBA", (IMAGE_WIDTH,IMAGE_HEIGHT), (0,0,0,0))
    colours = pil_colours()
    draw = ImageDraw.Draw(image)
    for x,y in hex_centres():
        draw.polygon(list(hex_points(x,y)), fill=colours.next())
    image.save('pil_hexes.png', 'PNG')

That’s it for my examples. Thomas ended with a challenge for displaying chess, and for describing the position. To describe the position, I would use a standard chess notation, such as described here. For my challenge, what other formats would be useful to create hex maps in? POVRay? Flash? Any other examples out there?

Coming attractions

I’m slowly making progress on ZenPaint, a simple animation tool for kids. I started writing it using PyGame, but hit the wall in terms of display, having to redefine my own widgets, etc. So I’ve taken a couple of step back and now I’m working on it using PyObjC. I keep waffling back and forth on whether to use Interface Builder or not. I don’t find Interface Builder very intuitive at all, but maybe I need to buckle down and get good at it. Knowing about nibtool helps–at least I can do text diffs and simple renames from the command-line.

One thing I would like to point out, the Apple NSDocument FAQ is one of the best bits of documentation I’ve ever seen out of Apple. So I’m mentally translating it into Python and trying to apply it to ZenPaint. As soon as I can load and save files reliably I’ll post the program and code up here for comments.

Losing my Nibs

I’ve been working with PyGame a lot and it’s been hard slogging. PyGame is great for moving images around quickly, but I’m more interested in vector drawing, native UI widgets, and higher-level events than I can get from PyGame.

So lately I’ve been turning my attention more and more toward PyObjC. I’m writing games for my kids, who are playing them on OS X, and I’m much less willing to make sacrifices to get cross-platform behaviour. And when I only have a few hours a week to write code, I need to focus on high productivity tools. So I’m writing Cocoa code, using Python, and it’s great. Bob Ippolito, Bill Bumgarner, Ronald Oussoren, Jack Jansen and the rest of the MacPython developers have produced an amazingly great tool. on top of Apple/Next’s highly evolved Cocoa framework. The mapping is so good that standard Cocoa references work for me to bootstrap my way up the Cocoa learning curve, my favorite reference being AppKiDo, which gives a series of views on every class (being able to separate instance methods of a class from all the intance methods it inherits, for example). Great stuff.

My only problem with Cocoa development (besides all the things I still have to learn about it, I’m not yet a proficient Cocoa developer) is the reliance on Interface Builder and NIB files. I think Interface Builder is one of the best visual layout tools I’ve used, and it’s great as far as it goes, but the problem is that you pretty much have to use it. I’ve tried to create applications which built their own interfaces from code, but I was unable to get the menus working because the Cocoa framework expects you to load the menus from a NIB at startup. I’m sure it can be done, but I couldn’t figure out how, even with the help of the MacPython mailing list and wiki.

And NIB files, which is how Interface Builder stores the interface, are a problem for me too, because they are serialized objects, not descriptions, so they are binary goop which is inaccessible except to Interface Builder. I can’t grep them or do a global search-and-replace if I change a method name. It’s hard enough for me to find the right places in IB to hook into my application, but finding all the places later which I need to change when my code changes rapidly becomes nightmarish.

And all this leads to examples like Build a text editor in 15 minutes or An example using NSDocument which consist of a few lines of code (the power of Cocoa) accompanied by tons of screenshots of Interface Builder and paragraphs of instructions for wiring up your application. Even worse, as the second example demonstrates, since the author no longer hosts that article and Google doesn’t cache the images, the screenshots no longer exist.

One more gripe. Because IB stores serialized objects, it doesn’t pick up changes to your code for those object unless you specifically force it to. So what I want in a UI tool:

  • Declarative
  • Simple and powerful
  • Able to replace NIB files and Interface Builder
  • Text-based, not binary
  • Agile for rapid development, adapts to rapidly changing code
  • Able to specify simple apps completely, in code, without resorting to pictures or talking the user through mouse gestures

And the best part is that I think I’ve found it. More in my next post.

google

google

asus