By now (this was started a year ago already) I would assume most of the most interested readers having developed their own drawing functions for creating more useful output. But just in case there are newcomers or someone needs a push to the right direction... Let's take a look at drawing some basic shapes.
In part eight we drew some vertical and horizontal lines by repeating calls to put_pixel for adjacent pixels. How about sloping lines? Not so trivial (except exactly 1:1 slope) - as one pixel step in one direction may need multiple pixels step in the other direction and normally gaps would not be wanted. Luckily this has been studied decades ago and there exists a well known algorithm for drawing sloping lines called the Bresenham's line algorithm which in our case can be implemented as:
...which should work for any line orientations.
void draw_line(int x0, int y0, int x1, int y1, int c) {
int dx = x1 - x0;
dx = (dx >= 0) ? dx : -dx; // abs()
int dy = y1 - y0;
dy = (dy >= 0) ? dy : -dy; // abs()
int sx;
int sy;
if (x0 < x1)
sx = 1;
else
sx = -1;
if (y0 < y1)
sy = 1;
else
sy = -1;
int err = dx - dy;
int e2;
int done = 0;
while (!done) {
put_pixel(x0, y0, c);
if ((x0 == x1) && (y0 == y1))
done = 1;
else {
e2 = 2 * err;
if (e2 > -dy) {
err = err - dy;
x0 = x0 + sx;
}
if (e2 < dx) {
err = err + dx;
y0 = y0 + sy;
}
}
}
}
Drawing other shapes becomes a lot easier using the line function - like rectangle outline:
Even filling a rectangle can be implemented using the same:
...for each line/y we draw a horizontal line... Of course it might be a good idea to optimize this using memset()...
// (x0, y0) = left top corner coordinates
// w = width and h = height
void draw_rect(int x0, int y0, int w, int h, int c) {
draw_line(x0, y0, x0 + w, y0, c); // top
draw_line(x0, y0, x0, y0 + h, c); // left
draw_line(x0, y0 + h, x0 + w, y0 + h, c); // bottom
draw_line(x0 + w, y0, x0 + w, y0 + h, c); // right
}
void fill_rect(int x0, int y0, int w, int h, int c) {
int y;
for (y = 0; y < h; y++) {
draw_line(x0, y0 + y, x0 + w, y0 + y, c);
}
}
The Bresenham's line algorithm has been extended to draw circles as well - this is very clever optimisation only calculating the points for one eight of the circle and just mirroring the rest:
...and it is trivial to modify this to draw filled circles (in four horizontal slices):
void draw_circle(int x0, int y0, int r, int c)
{
int x = r;
int y = 0;
int radiusError = 1 - x;
while(x >= y)
{
// top left
put_pixel(-y + x0, -x + y0, c);
// top right
put_pixel(y + x0, -x + y0, c);
// upper middle left
put_pixel(-x + x0, -y + y0, c);
// upper middle right
put_pixel(x + x0, -y + y0, c);
// lower middle left
put_pixel(-x + x0, y + y0, c);
// lower middle right
put_pixel(x + x0, y + y0, c);
// bottom left
put_pixel(-y + x0, x + y0, c);
// bottom right
put_pixel(y + x0, x + y0, c);
y++;
if (radiusError < 0)
{
radiusError += 2 * y + 1;
} else {
x--;
radiusError+= 2 * (y - x + 1);
}
}
}
void fill_circle(int x0, int y0, int r, int c) {
int x = r;
int y = 0;
int radiusError = 1 - x;
while(x >= y)
{
// top
draw_line(-y + x0, -x + y0, y + x0, -x + y0, c);
// upper middle
draw_line(-x + x0, -y + y0, x + x0, -y + y0, c);
// lower middle
draw_line(-x + x0, y + y0, x + x0, y + y0, c);
// bottom
draw_line(-y + x0, x + y0, y + x0, x + y0, c);
y++;
if (radiusError < 0)
{
radiusError += 2 * y + 1;
} else {
x--;
radiusError+= 2 * (y - x + 1);
}
}
}
Now use these to draw something - a quick (admittedly pretty much mindless) example:
...might be a good idea to make sure we are in 8 bit mode for the color constants to work... Full code in GitHub.
void draw() {
int x;
// some pixels
for (x = 0; x < vinfo.xres; x+=5) {
put_pixel(x, vinfo.yres / 2, WHITE);
}
// some lines (note the quite likely 'Moire pattern')
for (x = 0; x < vinfo.xres; x+=20) {
draw_line(0, 0, x, vinfo.yres, GREEN);
}
// some rectangles
draw_rect(vinfo.xres / 4, vinfo.yres / 2 + 10,
vinfo.xres / 4, vinfo.yres / 4, PURPLE);
draw_rect(vinfo.xres / 4 + 10, vinfo.yres / 2 + 20,
vinfo.xres / 4 - 20, vinfo.yres / 4 - 20, PURPLE);
fill_rect(vinfo.xres / 4 + 20, vinfo.yres / 2 + 30,
vinfo.xres / 4 - 40, vinfo.yres / 4 - 40, YELLOW);
// some circles
int d;
for(d = 10; d < vinfo.yres / 6; d+=10) {
draw_circle(3 * vinfo.xres / 4, vinfo.yres / 4, d, RED);
}
fill_circle(3 * vinfo.xres / 4, 3 * vinfo.yres / 4,
vinfo.yres / 6, ORANGE);
fill_circle(3 * vinfo.xres / 4, 3 * vinfo.yres / 4,
vinfo.yres / 8, RED);
}
[Continued in next part Text]
No comments:
Post a Comment
Note: only a member of this blog may post a comment.