Worrying about composition

Gah! I never had proper maths at school, and I dare say that if someone had tried to teach me maths at the tender age of eleven or twelve, I’d probably have bucked like race horse that’s harnessed in front of a brewery sledge. But the fact that my maths teacher walked out of the class when I was in the second form, to never return, didn’t help either. Not that I ever thought I had missed something essential.

Until now, that is. Graphics == maths, it seems. And not simple algebra eiher. So I am trying to understand what the various composition modes do — reading the literature, starting with the Porter-Duff article that appears to have started it all, and working my way through the GraphicsMagick source code, and I am finding it hard going.

And then there’s the mess that Krita’s UI is, a rich archeological dig where you can find code from 1999 to 2003, all written by different people with different goals and different styles, and practically nothing finished, or even in a working state. I should scrap that GUI and start again, only that’s going to take even more time from that most elusive of goals: a working eraser too.

Anyway, here’s a snippet of GraphicsMagick code, for the ‘in’ composition operation:

	if (source.opacity == TransparentOpacity)
	{
		destination=source;
		break;
	}
	if (destination.opacity == TransparentOpacity)
		break;
	pixel.opacity=(double) (((double) MaxRGB-source.opacity)*
				(MaxRGB-destination.opacity)/MaxRGB);

	destination.red=(Quantum) (((double) MaxRGB-source.opacity)*
	   (MaxRGB-destination.opacity)*source.red/MaxRGB/pixel.opacity+0.5);

	destination.green=(Quantum) (((double) MaxRGB-source.opacity)*
	     (MaxRGB-destination.opacity)*source.green/MaxRGB/pixel.opacity+0.5);

	destination.blue=(Quantum) (((double) MaxRGB-source.opacity)*
	    (MaxRGB-destination.opacity)*source.blue/MaxRGB/pixel.opacity+0.5);
	destination.opacity=(Quantum) (MaxRGB-pixel.opacity+0.5);
	break;

And here’s how I think it should be translated into Krita:

d = dst;
s = src;

if (d[PIXEL_ALPHA] == OPACITY_TRANSPARENT)
	continue;

if (s[PIXEL_ALPHA] == OPACITY_TRANSPARENT) {
	memcpy(d, s, stride * sizeof(QUANTUM));
	continue;

}

alpha = (double) (((double) QUANTUM_MAX - s[PIXEL_ALPHA]) * (QUANTUM_MAX - d[PIXEL_ALPHA]) / QUANTUM_MAX);
d[PIXEL_RED]   = (QUANTUM) (((double) QUANTUM_MAX - s[PIXEL_ALPHA]) * (QUANTUM_MAX - d[PIXEL_ALPHA]) * s[PIXEL_RED] / QUANTUM_MAX / alpha + 0.5);
d[PIXEL_GREEN] = (QUANTUM) (((double) QUANTUM_MAX - s[PIXEL_ALPHA]) * (QUANTUM_MAX - d[PIXEL_ALPHA]) * s[PIXEL_GREEN] / QUANTUM_MAX / alpha + 0.5);
d[PIXEL_BLUE]  = (QUANTUM) (((double) QUANTUM_MAX - s[PIXEL_ALPHA]) * (QUANTUM_MAX - d[PIXEL_ALPHA]) * s[PIXEL_BLUE] / QUANTUM_MAX / alpha + 0.5);
d[PIXEL_ALPHA] = (QUANTUM) (QUANTUM_MAX - alpha + 0.5);

dst += dststride;
src += srcstride;

But it doesn’t seem to work:

I am not completely sure what ‘in’ ought to do, but I am fairly certain that it isn’t ‘paint vertical black lines’ if I’ve selected a purple brush…