A Gimp Brush Proposal

I've been noting a number of threads on the list about enhancing the brush tools - leading to the gradient brush, image hose, and so on. Of course, as soon as a new brush is invented, someone wants to add some new functionality to it. (And with every new feature, the paintbrush code gets more complicated, and bits of code get copied between the paintbrush and the pen and the pencil...) I would like to propose a brush model and implementation which should satisfy even the most feature-hungry user. The core idea is to make explicit the processing steps between the mouse motion and the brush application, and replace the current, fixed sequence with an editable sequence of user-programmable plugin processes.

Disclaimer - I looked over someone's shoulder for about thirty seconds a few months ago when they were using a (the?) Photoshop image hose plug-in (or whatever it is). This is obviously similar, but I don't know how close it is.

There are three main components in the new brushing engine:

A Brush data packet
The brush packet contains the data which will determine the appearance of the brush stroke - including a (bitmap or pixmap) brush, its sampling rate, the foreground and background colours, an image position, and a button down / drag / up flag.
An array of Modulation Channels
Each modulation channel contains a value between 0.0 and 1.0.
A list of Filters
Each filter takes the brush data packet, processes it in some way and passes the packet to the next filter. The processing is controlled using one or more input values; these values come from the modulation channels. Some filters also create output values, which are fed back into the modulation channels. Every filter contains two integer arrays, inp and out. Filters which require input parameters take the first from modulation channel inp[0], the next from inp[1], and so on. Generated values are written to out[0] and so on.
Note that some filters will operate only on the modulation channels, passing the brush data on to the next filter unchanged. For example, a random modulation generator will simply write a random number to one or more modulation channels on each call.
So let's look at how this works. Selecting the paintbrush tool sets up a series of filters, depending on the option settings, and wires them up to the modulation channels by setting their input and output arrays. The user then starts a brush stroke. The UI samples the mouse (or pen) position and generates a brush data packet. The current alpha setting is written to a modulation channel, as are the pen pressure and tilt, mouse wheel value etc. The brush data packet is then passed to the first filter in the list.

Obviously, filters can be added to or removed from the filter list. So now, any existing or new brush effect can be used with a spray can just by adding a spray filter to the stack, which randomly jiggles the position in the current call. But let's get interesting. Let's write the spray filter so that it takes two modulation inputs, and adds a scaled amount of each input to the x and y values. Using two random modulation generators will give us a standard spray can effect. Instead, let's feed it with the two output channels of a sin/cos cyclic modulation generator which we've placed in the stack. Now we have an "orbiting brush" effect, where the brush actually circles around the cursor as we paint.

If we put the randomised spray filter before the position interpolator, then our brush strokes are all slightly randomly positioned - not so exciting when drawing by hand, but it might be an interesting effect when using rulers.

Or we can create a filter that calls the next filter twice; once with the original values, and once with the position offset by a fixed amount (and, for fun, the colour dimmed slightly). This gives us a double pen effect. (Sadly, the attributes of the two pens can't be set differently by downstream filters. Or can they? Instead of adjusting a specific attribute ourselves, let's just call the next filter N times, and also output a mod channel which steps from 0.0 to 1.0 in steps of 1.0/(N-1). Now we're getting somewhere...)

Some more filters: a canvas filter, which adds texture to any brush stroke by adjusting the alpha channel of the brush according to a background pattern. A filter which varies the fg colour slightly on each call. An alternate interpolation filter to replace the standard brush path interpolator, and produce smoothed curves from the mouse motion. A bristle filter which randomly removes bits of the alpha channel as the brush progresses.

What mod generators are there? We could start with noise, cyclers (sin and cos, triangle, ramp); pen pressure and tilt are obvious, but we can also derive the current angle of the brush path and place that in a modulation channel. There are filters that just process existing mod channels, for example multiplying two channels, or low pass filtering. An envelope generator resets with every new brush stroke, increases to a maximum and then decreases - feed it into a brush fader to get the current brush fadeout effect, but with far more exotic possibilities. A filter which takes a mod channel and maps it through an arbitrary curve would be fun.

So what about the poor user? Well, to start with they don't need to see any of this. The standard brush tools each just set up the appropriate filters to do what they've always done. There might be some interesting differences; for example, the spray tool could become just a toggle to add a spray filter and some random modulators to the current stack, whatever it might be.

Of course, if they're interested, users can open up the brush engine editor. If they do, they are presented with the current stack of filters, and a menu for selecting new ones. Alongside the filter stack run the modulation channels, looking a lot like an old VCS3 patch panel (or maybe more like an ARP 2500. No, go and look it up!); the horizontals are the filter inputs and outputs, the verticals are the modulation channels, and clicking on a crosspoint connects the input or output to that channel. They can add and remove filters from the stack, drive the filters from different modulation channels, and generally play to their hearts' content. Next, of course, they will want to save and restore their new setups ... now, our collection of tools starts to expand like our selection of image filters. Maybe that resizable toolbox came just in time.

Comments to David Hodson or the gimp developers' mailing list.