This project was generally more frustrating then the previous one, being much more finicky. I had to fix little bugs in which order to include headers, the declaratons of inline functions, the conversion of Bresenham's Algorithm to work in octants other than the first one, and several other places. None of these were particularly challenging conceptually, but I repeatedly got stuck on them and they gave the whole project a feel of being unbearably fragile.
I implemented Bresenham's algoithm for lines and similar ones for circles and elipses. This worked alright, but they looked awfully jagged. I decided to fix this by implementing antialiasing based on the error term, something that I thought wouldn't be that hard. I implemented this first for lines, and it ended up taking long enough to get that working that I did not implement it for circles or ellipses.
The other reason for not expanding the implementation of antialiasing is that it didn't look that good. The logic was based on the assumption that two pairs of pixels which summed to the same color value would appear to be of the same intensity to the average viewer. Unfortunately, this is not true. People see a full-on pixel, even next to a full-off pixel, as brighter than a pair of half-on pixels. This made the lines look to be varying in color. Even worse, this varied by monitor, with some monitors making them look about the same and others presenting the half-on pair as very dark. If not for this perhaps the implementation of a non-linear coloring algorithm would fix the problem, but with these monitor issues I'm not sure what to say but that this is about as good as antialiasing can look, at least on some monitors.
I also modified the line drawing code to plot two points at each iteration. As tested through the "testbench.c" program, this didn't help very much. On an intel CS lab machine running at 3.0 GHz, it averaged 310,000 lines per second with one point at a time and 315,000 per second with two at a time. I considered this to be a small enough gain that when I redid my line algorithm to add antialiasing I removed the double plotting to simplify the logic.
Making lines and other objects come out in exactly the right places was tricky, and I don't think I have it quite right. The initial value of the error term needs to be set properly, and in some cases my lines look like I might have gotten that wrong. They tend to be close, but I need to do more testing to get the right.
When I was implementing antialiasing for my lines, I found that I wanted a way to rapidly blend lines into an image, without having to read the value, calculate the replacement value, and set. I wrote two functions, then Image_set_mix and Image_set_mix_int that expand the Image_set of the basic API by taking blending values. In the first version, in addition to the arguments required by Image_set there is a float value that indicates how much to mix in the new color. So we have:
A silly test image of my own is below. The radial lines are very useful for debugging, as you can easily see how the code is handling all eight cases. The shaded lines do a decent job of showing the antialiasing.