Palette-mode PNG images

  • Thread starter Lawrence D'Oliveiro
  • Start date
L

Lawrence D'Oliveiro

I'm trying to create PNG files to use in menus for authoring DVDs. As you
may know, these menus are only allowed to have limited numbers of colours.

Ideally I'd like to create a PNG file with just two bits per pixel, with
four colour-table entries of my choice. I'm using PyCairo
<http://www.cairographics.org/pycairo/> to do the drawing, but that doesn't
seem to support colour-table images as far as I can tell. So I'm trying to
figure out how to use PIL
<http://www.pythonware.com/library/pil/handbook/index.htm> to save the
images to PNG files with a suitable format.

However, it looks like PIL wants 256 colour-table entries. When I try to
pass fewer, e.g.

ThePix = array.array("B", '\0' * ImageWidth * ImageHeight * 4)
ThePixSurface = cairo.ImageSurface.create_for_data(ThePix,
cairo.FORMAT_ARGB32, ImageWidth, ImageHeight, ImageWidth * 4)
# can't find format_stride_for_width?
TheDraw = cairo.Context(ThePixSurface)
...
ThePixSurface.flush() # prior to writing out pixels myself
TheImage = Image.frombuffer("RGBA", (ImageWidth, ImageHeight),
ThePix, "raw", "RGBA", 0, 1)
TheImage = TheImage.convert("P")
TheImage.putpalette([(0, 0, 0), (255, 255, 255), (0, 255, 0),
(0, 255, 255)])
TheImage.save("png_palette_test.png")

it dies with the following, on the putpalette line:

Traceback (most recent call last):
File "./png_palette_test", line 41, in <module>
TheImage.putpalette([(0, 0, 0), (255, 255, 255), (0, 255, 0),
(0, 255, 255)])
File "/usr/lib64/python2.5/site-packages/PIL/Image.py", line 1205,
in putpalette
data = string.join(map(chr, data), "")
TypeError: an integer is required

Cairo also supports FORMAT_A8 and FORMAT_A1 images--should I be using the
latter, perhaps?

Also I see that the PIL PNG encoder/decoder supports a "bits" output option
<http://www.pythonware.com/library/pil/handbook/format-png.htm> which is
marked as "experimental". Can this be useful for constraining the pixel
depth of the output image?

Thanks for any suggestions.
 
L

Lawrence D'Oliveiro

data = string.join(map(chr, data), "")
TypeError: an integer is required

OK, I figured that out, the putpalette call wants a sequence of integers
being (R, G, B, R, G, B ...), not a sequence of sequences ((R, G, B), (R,
G, B)...).
TheImage = TheImage.convert("P")

This gives me 8 bits per pixel, which is too many. And this automatically
converts the image using a default palette; a subsequent putpalette call
replaces the palette, but doesn't change the pixels, so the colours come
out wrong.

I can also do Image.convert("1"), which gives me a 1-bit-per-pixel image,
but that only allows a fixed colour table (black and white), no option to
change either entry.
Also I see that the PIL PNG encoder/decoder supports a "bits" output
option ...

Hmm, interesting, but adding either "bits = 2" or "bits = 1" to the save
call just seems to turn the image all black. Examination with
ImageMagick's "identify" command shows that the default colour table ramp
has been stair-stepped, with all components turned into multiples of 51 (=
255 / 5).
 
L

Lawrence D'Oliveiro

Lawrence said:
Ideally I'd like to create a PNG file with just two bits per pixel, with
four colour-table entries of my choice. I'm using PyCairo
<http://www.cairographics.org/pycairo/> to do the drawing, but that
doesn't seem to support colour-table images as far as I can tell. So I'm
trying to figure out how to use PIL
<http://www.pythonware.com/library/pil/handbook/index.htm> to save the
images to PNG files with a suitable format.

For those who are interested, I finally gave up on PIL and resorted to direct calls to libpng via ctypes. This successfully lets me write 1-bit-per-pixel PNG images with colour tables of my choice.

One issue I found is that the png_set_packing call doesn't seem to work on my Gentoo AMD64 system. And Cairo and libpng use a different order for sub-byte pixels on little-endian platforms. So I have to do rearrangement of bits within each byte. Which is a bit slow in Python.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,060
Latest member
BuyKetozenseACV

Latest Threads

Top