Skip to content

DaveJeffery/gimp_retro

Repository files navigation

gimp_retro

I documented my work on these plug-ins in a series of blog posts. I include these here, so they can be kept together with the code.

Attribute Clash for The GIMP

When I was browsing through The GIMP plug-in registry, I came across a very interesting sounding filter called zx spectrum filter by nitrofurano that promised it would imbue your images with all the glorious display limitations of the Sinclair ZX Spectrum.

As it was written in Python I thought I’d give it a go, but the plug-in refused to work. It took about an hour spuddling about to get the plug-in working. The main problem was incorrect indentation—Python is fussy about that. I also added some code to allow undoing and for the plug-in to appear in the Filters menu and then I could start playing!

Replay Expo logo

Before

Replay Expo logo put through the Python ZX Spectrum filter

After

My fixed version of the plug-in can be downloaded from here, and adds the plug-in to Filters -> Artistic -> ZX Spectrum.

GNU/Linux users need to set the Execute permission for the file zxspectrum.py before the The GIMP will recognise it.

The plug-in is a very simple proof of concept and doesn’t work particularly well on small (as in ZX Spectrum sized) images as it just averages out the values in character squares, but it certainly creates some interesting effects on large images. 1024 x 768 seems to be the optimum size.

Repton put through the Python ZX Spectrum filter

Large images work best

The next step would be to speed it up using the array library and to stop it from falling over nastily if you have an alpha channel on your image—if you do, you'll have to remove it to get the filter to work.

ZX Spectrum Filter Revisited

Well, a day is a long time in Free Software. Since I posted yesterday about the ZX Spectrum filter for The GIMP, I’ve had a lovely exchange of e-mails with the original author nitrofurano, I’ve improved the filter further and I’ve found out why it was written.

A ZX Spectrum put through the Python ZX Spectrum filter

Spectral Spectrum

First things first, improving the filter. I had become rather rusty at working on filters for The GIMP but eventually everything came flooding back to me.

The first thing that helps when writing a Python filter in The GIMP is to run The GIMP from the command line in a terminal window. That way you get to see all the error messages the plug-in produces and are not working “blind”. You can also see the output of any print statements you add to help you debug.

The second thing I remembered was that you should use a symbolic link to the filter in The GIMP’s plug-in folder, so you can work somewhere more convenient than a hidden folder that’s several levels down.

A ZX Spectrum +3 put through the Python ZX Spectrum filter

Sugary Spectrum

Once I’d got myself working sensibly I could have a look at improving the filter. The first thing I did was to speed the filter using this technique described in Akkana’s blog. It cuts down on writing to the actual image, which is slow. Instead you copy the image to a byte array, work with that and then copy all the bytes back to the image when you have finished. Using Akkana’s technique had the added bonus of allowing the filter to be adapted easily work with either RGB or RGBA images.

However, the resulting changes didn’t seem to generate the desired increase in speed until I realised I had stupidly queried the image class’s size and width repeatedly instead of storing the values in variables. Once I did that the filter literally flew.

Nitrofurano (Paulo Silva) has been lovely and very encouraging as I’ve been hacking his lovely code to bits. He’s also as enthusiastic about free software as I am. I think it’s fantastic that people who have never met before can work on each other’s software, share ideas and get to know each other—the GPL really does work as advertised.

A Sinclair pocket TV put through the Python ZX Spectrum filter

Sinclair TV - thanks to Nitrofurano

The reason the code was written originally was to be part of a very interesting project Paulo is working on to create “retro” vision web-cams. You can find out more about it here.

ZX Spectrum +3

Two days ago I blogged about getting nitrofurano’s Python ZX Spectrum image filter for The GIMP working, and yesterday I blogged about speeding it up. However, Paulo e-mailed me just after I'd posted and said that the filter wasn't working as it should.

I had assumed that the filter just wasn’t supposed to work on small images—such as ones at the ZX Spectrum resolution of 256 x 192. So if we took a 256 x 192 image like this:

A Somerset cottage photographed by John Livens

Original image at 256 x 192

The best we could hope for would be this:

Above image put through a broken GIMP ZX Spectrum filter. The image has a huge loss of resolution.

Put through The GIMP version of filter

However, Paulo pointed out that his sdlBasic version of the filter would produce this:

Above image put through an SDLBasic filter. The image has been filtered perfectly.

Put through sdlBasic version of filter

But having looked at the Python code for The GIMP filter he couldn’t work out what was wrong. I was intrigued, and decided to have a look too.

Paulo thought that the problem probably lay in one of the loops that were processing the image, but I thought that was unlikely, particularly as he’d checked them so thoroughly against his sdlBasic original. The loops just contained maths, and maths tends to be pretty similar in any language.

Sure enough, the problem lay not in the maths but in the idiosyncratic weirdness of Python. I blame Eric Idle, personally.

The first problem was the way in which Paulo had dimensioned the lists (elderly gentlemen like me call them arrays) he used to work on character blocks. He’d done this:

r0 = [[0] * 8] * 8
g0 = [[0] * 8] * 8
b0 = [[0] * 8] * 8

Which is perfectly sensible, but the problem is in Python almost everything is copied by reference rather than by value. Numbers are copied by value, so [0] * 8 does create a list containing eight different zeros.

However lists are copied by reference so [[0] * 8] * 8 creates a list containing eight references to the same list. That means that any change made to one row of this multidimensional list affects all the other rows too—effectively cutting the resolution of the filter down to the character block level. This caused the blockiness.

To solve it we needed to do this:

r0 = [[None] * 8 for i in range(8)]
g0 = [[None] * 8 for i in range(8)]
b0 = [[None] * 8 for i in range(8)]`

What we are doing now is using a list comprehension to create a brand new list eight times, which is what we are after.

Another problem was that instead of referring to the nested lists using list[y][x] Paulo had used list[x][y]. So, for instance we had this:

b0[x2][y2] = str1[2]
g0[x2][y2] = str1[1]
r0[x2][y2] = str1[0]`

When we should have had this:

b0[y2][x2] = str1[2]
g0[y2][x2] = str1[1]
r0[y2][x2] = str1[0]`

It’s very easy to do—in fact I’ve done it myself. Many times!

Regarding Python’s weirdness, it more often works for you than against you and that’s why I love the language. For instance, in Paulo’s sdlBasic version of the filter he had to do this to swap two values:

if ikattr < paattr:
    tmpr = ikattr
    ikattr = paattr
    paattr = tmpr`

Whereas in Python you can use the far more “Pythonic”:

if ikattr < paattr:
 ikattr, paattr = paattr, ikattr`

Anyway, now the filter was fixed and I could have some fun with some ZX Spectrum proportioned stupid rubbish. Here is Central News at 256 x 192:

A 1982 Central News caption put through ZX Spectrum filter

It's 1982 again!

And here is a Tyne Tees/Channel 4 endcap - again at 256 x 192:

A 1986 Tyne Tees Channel 4 co production caption put through ZX Spectrum filter

Unworthy of Half Man Half Biscuit

However, Paulo used it to produce something much grander:

A large Japanese painting of a tsunami put through a ZX Spectrum filter

by nitrofurano

This isn’t the end of the story, unfortunately, as now I have to get the filter’s Undo feature working.

Unweaving the Rainbow

As I promised, I've tweaked nitrofurano's ZX Spectrum filter for The GIMP so that you can now undo (and redo!) the effects of the filter properly.

The cover of Face To Face by Barclay James Harvest put through the Python ZX Spectrum filter

Not a favourite album, but a nice cover

To get undo to work I needed to create a duplicate of the current layer to work on, and then merge that down into the original layer when the filter has finished its work.

The cover of Victims of Circumstance by Barclay James Harvest put through the Python ZX Spectrum filter

From XOR to AOR

That way, The GIMP seemed to remember the original layer and could go back to it when you used undo.

MSX Picture Filter for The GIMP

Now that Paulo Silva's (nitrofurano) ZX Spectrum filter for The GIMP was working nicely, I thought I'd like to try converting one of his other sdlBasic picture filters into a Python GIMP plug-in.

I chose the MSX1 Screen 2 filter, as it looked quite similar to the ZX Spectrum filter. I'd never actually seen an MSX computer working (I saw some switched off in a shop once) so I didn't really know what to expect until I read up on Wikipedia.

Whereas the ZX Spectrum suffered from attribute clash on the character square level, MSX1 suffered from attribute clash on the character row level so I was expecting the resulting images to look like slightly better ZX Spectrum images. And so it turned out.

To compare, here is John Liven's photograph of a cottage:

A Somerset cottage photographed by John Livens

Cottage - Photo: John Livens

And here it is processed by the ZX Spectrum filter:

Above image put through GIMP ZX Spectrum filter.

Cottage - ZX Spectrum

And finally by the MSX1 filter:

Above image put through GIMP MSX filter.

Cottage - MSX1

Converting the filter was straightforward, and I managed to find and fix a small bug in the sdlBasic original whilst I was going along.

Appley Within

As I had enjoyed converting nitrofurano’s sdlBasic MSX and ZX Spectrum picture converters to The GIMP, I thought I’d tackle the Apple II colour picture filter next.

Mind you, saying that the Apple II had a colour mode is a bit like saying that Steve Wozniak was a professional ballroom dancer. Whilst technically correct, it really is wide of the mark.

Apple II colour is a strange world in which ”red or yellow” becomes orange and “blue or cyan” becomes blue. Because of this Paulo's filter was rather more complicated than the ones I've tackled previously. In all, it took me about three days to understand what was going on and finally iron out all the bugs (mine, not Paulo’s!).

Because of the complexity of the filter, I decided to implement some functions to emulate the ink(), dot(), point() and line() commands of sdlBasic. This made the Python much more readable (and eaiser to debug), even if it did mean I lost a bit of speed.

In order to make up some of the lost speed, I used tuples instead of lists for the look-up tables. I should have done this in my other filters too.

Once I finished the filter I dug out my usual cottage picture as a test:

A Somerset cottage photographed by John Livens

Photo: John Livens

The resulting image had me crying into my coffee:

A Somerset cottage put through a non-functional Apple II filter resulting in a green and orange dithered mess

Soundtrack from the film More?

It looked like something out of “The Lost World of Friese-Greene”! Having picked out some bugs I got something a bit closer, but the white stripes were a real pain to get rid of:

A Somerset cottage put through Apple II a semi-functional filter with correct, stippled, colours but white vertical stripes

It took ages to fix...

Finally, after I had remembered how to count to six, I got a successful image:

A Somerset cottage put through working Apple II filter

...but the result was worth it.

The filter runs in two modes, a halftone mode or a posterised mode. The posterise mode doesn’t stipple the colours. Here is the posterised output:

A Somerset cottage put through Apple II filter with Posterized option

Posterised, it’s very striking

I added a little dialogue box to the filter to allow users to pick which mode they want:

The user interface of the Apple II filter

The filter’s complex user interface

My overall impression is that the Apple II produced orangey mush—a bit like the NTSC pictures put through the IBA’s DICE standards convertor we used to see on British television in the 70s. But, I must admit, it does have a certain kind of charm. And, above all, Paulo did an incredible job in coming up with an Apple II filter—it’s an ingenious bit of coding.

If you want to try it out for yourself, the filter is available to download from here. Bear in mind that the filter is pretty slow, so it’s best to stick to small images unless you have a fast computer.

If you see SID, tell him...

I’ve converted nitrofurano’s sdlBasic Commodore 64 Low Resolution mode picture converter into a Python-Fu image filter for The GIMP.

In Commodore 64 Low Resolution mode, each block of 4 x 8 2:1 aspect ratio pixels can contain four colours from a choice of 16. Only, it’s a bit more complicated than that! Fortunately, this article explains how it is supposed to behave very nicely. Paulo’s algorithm has to go through eight separate stages to create the finished image.

The main novelty for me in this filter was that in order to avoid having to use a three dimensional list (which would have entailed syntax to boggle the mind) I used a Python new-style class. That meant I could use a one dimensional list and let the class take care of getting and setting the right bit of it when required through method calls.

Here’s an example image before:

Uridium cover art

Original scan

And after conversion in The GIMP using the filter:

Uridium cover art put through the Commodore 64 filter

Commodore 64 Low Res Filter

As usual this plug-in, along with ones for the ZX Spectrum, Apple II and MSX1 can be downloaded from here.

About

GIMP Retro Filters

Resources

License

GPL-3.0, GPL-3.0 licenses found

Licenses found

GPL-3.0
LICENSE
GPL-3.0
COPYING

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages