In this tutorial we will look at drawing a line to our render buffer.
To draw a line, we will need to implement a drawLine function and call it in the render function. Our drawLine function will make use of the digital differential analyser algorithm.
Our drawLine function should be a void function. It should take seven parameters. The first two should be the x and y coordinates of the start of the line. They should be integers. The third and fourth should be the x and y coordinates of the end point of the line. These should also be integers. The last three should be the red, green and blue values of the colour, and should be unsigned 8-bit integers. We should put the drawLine function in the render.c file.
Our drawLine function will make use of the digital differential analyser algorithm. This algorithm works by finding the change in x (dx) and the change in y (dy). We then take the larger of the magnitudes (so we will need to find the absolute value of both) of the two distances (dy and dx) and step from one end to the other, one unit at the time. In the direction of the smaller of the two distances, we increase by the ratio of the smaller distance to the larger (i.e. the x per unit y whe dy is the larger value, and the y per unit x when dx is the larger value). Every step, we round to the nearest pixel and draw it. This is better explained with an example:
To implement DDA in our code, we first want to find the change in y and the change in x. We can do this with the lines "int dx = endX - startX;" and "int dy = endY - startY;". Next, we need to create three variables. Two are floating point numbers: the xStep and yStep. These are the values that we should move along the x and y axes each step. The last is an integer, called "totalSteps". This will store the total number of steps we will use to draw our line. We should set this to the absolute value of the largest of dy and dx.
Next, we want to check if the magnitude of dy or dx is larger. We can do this with the line "if(abs(dy) > abs(dx)){". If dy > dx, then we want to set the totalSteps variable to the absolute value of dy ("totalSteps = abs(dy);"), since we will be moving along by one unit (either up or down) along the y-direction. If dx > dy, we want to do this the other way around. We should set totalSteps to dx ("totalSteps = abs(dx);"). After this if statement, we want to set yStep to dy / totalSteps, and xStep to dx / totalSteps, using the lines "yStep = (float) dy / totalSteps;" and "xStep = (float) dx / totalSteps;". This is because, each step, we want to move along in each direction by the total distance to cover in that direction divided by the total number of steps.
After setting xStep and yStep, we want to create two floating pointer numbers, the x coordinate and the y coordinate. We should set these to startX and endX. We can do this with the lines "float x = startX;" and "float y = startY;". Next, we should create a pixel pointer to point to the pixel we will draw each step. We can do this with the line "Pixel * pixel;". Then we want to create a loop counter, which I will call "i". We then want to create a loop that will iterate for all steps. We can do this with the line "for(int i = 0; i <= totalSteps; i++){".
Inside our loop, we first want to check that the pixel at the coordiantes x and y is actually on screen (to avoid accessing protected memory). We can do this with the line: "if(x >= 0 && x < BUFFER_WIDTH && y >= 0 && y < BUFFER_HEIGHT){". If the current pixel is on screen, we want to set the pixel pointer to the pixel at the current x and y coordinates. We can do this with the line "pixel = renderBuffer.pixels + ((int) y) * BUFFER_WIDTH + (int) x;". Here, we are taking the start of the render buffer and adding on y (rounded down) lots of BUFFER_WIDTH pixels to get to the correct row, before adding on x pixels to get to the pixel we wish to draw to. Then, we can set the colours of the pixel that are pointer is pointing to. We should set the RGB values of said pixel to the RGB valeus we passed into our function. We can do this with the lines: "pixel->red = red;", "pixel->green = green;" and "pixel->blue = blue;". After our on-screen check if statement, we want to increase the x coordinate by xStep, and the y coordinate by yStep.
Now we just have to call our drawLine function in our render function, to have it execute each frame. I will do this with the line "drawLine(320, 240, 400, 400, 0, 255, 0);" but you can pass in whatever arguments you would like.
The code for this section is shown below. I have not modified the main.c file. I have added the drawLine function declaration to the main.h file using the line "void drawLine(int startX, int startY, int endX, int endY, uint8_t red, uint8_t green, uint8_t blue);". As this is thye only modification to main.h, I have not included the main.h file below. Below is the code for our render.c file:
render.c
Running our code should produce the following output:
We should see this scale when we enter fullscreen mode:
That is all for this tutorial. In the next tutorial, we will look at drawing a triangle.