Serebyte


Home | Tutorials | Portfolio

Win32 Software Renderer in C: Part 12 - Rotating a Bitmap


Note: this tutorial series requires significant knowledge of the C programming language


In this tutorial, we will look at rotating a bitmap. We will write a function to rotate our spr_test sprite (the red astronaut).




The Theory:

To rotate a sprite, we should define a drawRotatedSprite function. It should be avoid function, and should take four parameters. The first two should be integers, and should be the x and y coordinates to draw the point (centreX, centreY) at. These will be named "x" and "y". The next is a floating point number - the angle of rotation, which should be named "angle". The last should be a pointer to the sprite to render. This should be called "sprite". We can create this function with the line: "void drawRotatedSprite(int x, int y, float angle, Sprite * sprite){".




An Overview of Sprite Rotation

Before we write the code inside our function, we must first understand how sprite rotation works. Note that we do not just want to rotate a sprite, but we want to enlarge it by its xScale and yScale as well.


As with our drawSprite function, we first want to define a set of vertices representing the bounding box. We can start with the coordinates (0,0), (0, height), (width, height), (width, 0), and subtract centreX and centreY from the x and y coordinates of each vertex. This will produce a bounding box for the sprite, where the defined centre of the sprite is at the origin point: (0, 0).



Becomes...



Next, we want to enlarge our bounding box by the horizontal and vertical scale factors. We can do this by multiplying each of the x coordinates by the sprite's xScale value, and each of the y coordinates by the sprite's yScale value.



Now we want to rotate the vertices around the origin (remember that we translated the bounding box so that the (centreX, centreY) point was moved to the origin). For each vertex, the x coordinate = lcos(a), where l is the distance between the vertex and the origin, and a is the angle the vertex makes with the positive x-axis. The y coordinate = lsin(a). We want to rotate each vertex by an angle. I will call this b, but in our actual code it is simply called "angle". This means that the total angle after rotation = a + b. Therefore, the new x coordinate of any given vertex = lcos(a + b), and the new y coordinate = lsin(a + b). Now, we can use the double angle formulae to simplify these. cos(a + b) = cos(a)cos(b) - sin(a)sin(b). sin(a + b) = sin(a)cos(b) + cos(a)sin(b). Therefore, the new x coordinate = lcos(a)cos(b) - lsin(a)sin(b). As lcos(a) = the previous x coordinate of the vertex, and lsin(a) = the previous y coordinate of the vertex, the new x = xcos(b) - ysin(b), where x and y are the coordinates of the vertex before rotation. The new y coordinate can be expressed as lsin(a)cos(b) + lcos(a)sin(b), which is equal to ycos(b) + xsin(b). A diagram showing the rotation of a single vertex can be found below.



With the rotated vertices found, we want to add the x and y coordinates (which were passed into the function as parameters) to each vertex. This will translate the origin (the defined centre of the sprite) to the coordinates we wish to draw the sprite at. After we have done this, we should compare each of the vertices to get the mininum x and y coordinates and the maximum x and y coordiates. We will call these minX, maxX, minY and maxY.


Now we can simply iterate through each of the pixels between (minX, minY) and (maxX, maxY) and reverse the tranformation to get the coordinates of source bitmap pixel that corresponds to the screen pixel. We should check that this pixel is within the (startX, startY) to (endX, endY) bounds of the sprite. If so, we should check if the sprite is not transparent. If it isn't transparent, we will need to check if the sprite is in plain colour mode. If so, we should draw the colour stored in the sprite structure. If it is not in plain colour mode, we should get the source pixel and display it to the screen.




Writing the drawRotatedSprite Function Code

The first thing to do in our function is to calculate the width and height of the sprite in the bitmap (without scaling applied). We should store these to two integer variables named "width" and "height". We can calculate width by subtracting startX from endX and taking the absolute value of the result. We can calculate height by taking the absolute value of endY - startY. We can accomplish this with the lines: "int width = abs(sprite->endX - sprite->startX);" and "int height = abs(sprite->endY - sprite->startY);".


Next, we need to create a vertex array, using the line: "float vertices[4][2];". This array is 2d, where each sub-array consists of two floating point numbers: the x coordinate and the y coordinate in that order. We then want to set the bottom left corner to (-centreX, -centreY) so that the defined centre of the sprite is translated to the origin. We should set the bottom left corner with the lines: "vertices[0][0] = -sprite->centreX;" and "vertices[0][1] = -sprite->centreY;". We should then set the top left vertex with the lines: "vertices[1][0] = vertices[0][0];" and "vertices[1][1] = vertices[0][1] + height;". Next, we should set the top right vertex with the lines: "vertices[2][0] = vertices[1][0] + width;" and "vertices[2][1] = vertices[1][1];". After this, we need to set the bottom right coordinate, using the lines: "vertices[3][0] = vertices[2][0];" and "vertices[3][1] = vertices[0][1];".


After this, we need to multiply the x and y coordinates of each vertex by our sprite's xScale and yScale values respectively. We should first create a loop counter with the line: "int i;". Now we need to iterate through each of the vertices with the for loop: "for(i = 0; i < 4; i++){". Inside this loop, we need to multiply the x and y coordinates of the vertex at index i by xScale and yScale respectively. This can be done with the lines: "vertices[i][0] *= sprite->xScale;" and "vertices[i][1] *= sprite->yScale;".


Next, we should rotate each vertex by the angle passed into our function as a parameter. We should first pre-calculate the sine and cosine of our angle, so that we will not have to calculate these values more than necessary. We should do this with the lines: "float sinAngle = sin(angle);" and "float cosAngle = cos(angle);". Next, we should start a loop to iterate through each vertex, using the line: "for(i = 0; i < 4; i++){". Inside this loop, we should store the current x and y coordinates of the vertex, as we will need them in the next few lines. We should store these values to a pair of local variables called prevX and prevY. We can do this with the lines: "float prevX = vertices[i][0];" and "float prevY = vertices[i][1];". After this, we should use the formulae: new x = prevX * cosAngle - prevY * sinAngle and newY = prevY * cosAngle + prevX * sinAngle. We can do this with the lines: "vertices[i][0] = prevX * cosAngle - prevY * sinAngle;" and "vertices[i][1] = prevY * cosAngle + prevX * sinAngle;".


Now that we have our rotated vertices, we need to translate each vertex over to x and y coordinates we wish to draw at. To do this, we need to iterate through each vertex, which we can do using the line: "for(i = 0; i < 4; i++){". Inside the for loop, we should add the lines "vertices[i][0] += x;" and "vertices[i][1] += y;", to translate our vertices to the correct drawing coordinates.


We now need to find the minimum and maximum x and y coordinates. We should initialise four integer variables: minX, maxX, minY and maxY. These should each be initialised to the coordinates of the first vertex, so that these values can be compared with each vertex. We can initialise these variables with the lines: " int minX = vertices[0][0];", "int maxX = vertices[0][0];", "int minY = vertices[0][1];" and "int maxY = vertices[0][1];". Now we should iterate through each of the vertices, using the for loop "for(i = 0; i < 4; i++){", and compare the coordinates of the vertices with minX, maxX, minY and maxY. For any vertex, if the x coordinate is greater than maxX, we want to set maxX to the x coordinate. Likewise, if it is smaller than minX, we want to set minX to the x coordinate. We then want to do this for the y coordinates. We can check if the x coordinate is smaller than minX using the if statement: "if(vertices[i][0] < minX){". If so, we should run the line: "minX = vertices[i][0];". We should next check to see if maxX has been exceeded. We can check this using the if statement: "if(vertices[i][0] > maxX){". Inside this if statement, we should add the line: "maxX = vertices[i][0];". We can check if the y coordinate is less than minY using the if statement: "if(vertices[i][1] < minY){". Inside this if statement, we should add the line: "minY = vertices[i][1];". We can check if maxX has been exceeded using the if statement: "if(vertices[i][1] > maxY){". If so, we want to run the line: "maxY = vertices[i][1];".


Now we can look at actually drawing pixels to the screen. We should first create a pixel pointer with the line: "Pixel * pixel;". Next, we want to iterate through each row of pixels, using the for loop: "for(i = minY; i < maxY; i++){". Inside this loop, we first want to set the pixel pointer to the start of the row, which can be done with the line: "pixel = renderBuffer.pixels + i * BUFFER_WIDTH + minX;". After this, we should create another loop counter, using the line: "int j;", and should loop through the pixels on the row. We can do this with the for loop: "for(j = minX; j < maxX; j++){". Inside this loop, we first want to check if the pixel is within the screen bounds. We can check this with the if statement: "if(j >= 0 && j < BUFFER_WIDTH && i >= 0 && i < BUFFER_HEIGHT){". Inside this loop, we can get the pixel coordinates of the unrotated (but still scaled_ sprite using the lines: "int pixelX = (j - x) * cosAngle + (i - y) * sinAngle;" and "int pixelY = (i - y) * cosAngle - (j - x) * sinAngle;". These two lines reverse the most recent translation of the sprite, before rotating by -angle. After this, we want to divide the pixelX and pixelY values by the xScale and yScale values. We can do this with the lines: "pixelX /= sprite->xScale;" and "pixelY /= sprite->yScale;". After this, we should add the centreX and centreY values, so that the source image bounding box has its bottom left corner at point (0, 0). We can do this with the lines: "pixelX += sprite->centreX;" and "pixelY += sprite->centreY;". After this, we should add the starting x and y coordinates of the sprite on the source bitmap to our pixelX and pixelY values. This can be done with the lines: "pixelX += sprite->startX;" and "pixelY += sprite->startY;".


With the pixel coordinates set up, we should check that the pixel is in the valid region specified within the sprite. We can check this with the if statement: "if(pixelX >= sprite->startX && pixelX < sprite->endX && pixelY >= sprite->startY && pixelY < sprite->endY){". Inside this if statement, we want to get the source pixel, by adding pixelY * sprite->bitmap->infoHeader.biWidth + pixelX to the sprite bitmap pointer. This can be done with the line: "Pixel * srcPixel = sprite->bitmap->pixels + pixelY * sprite->bitmap->infoHeader.biWidth + pixelX;".


After retrieving our source pixel, we should check that it is not transparent. We can do this with the line: "if(srcPixel->red != TRANSPARENT_RED || srcPixel->green != TRANSPARENT_GREEN || srcPixel->blue != TRANSPARENT_BLUE){". Inside this if statement, we want to check if plain colour mode has been enabled. We can check this with the if statement: "if(sprite->isPlainColour == 1){". Inside this if statement, we should set the current pixel to the colour stored in the sprite structure. We can use the lines: "pixel->red = sprite->red;", "pixel->green = sprite->green;" and "pixel->blue = sprite->blue;" for this. If we are not in plain colour mode (i.e. in the else block of the plain colour if statement), we should set the pixel colour to that of the source pixel. We can do this with the lines: "pixel->red = srcPixel->red;", "pixel->green = srcPixel->green;" and "pixel->blue = srcPixel->blue;".


Just before the end of the pixel row loop (the j loop: "for(j = minX; j < maxX; j++){"), we need to increment the pixel pointer, using the line: "pixel++;".


Inside main.h, we want to create a global angle variable and initialise it to 0. This will allow us to store and change the angle of our sprite in real time. In main.h, we can declare this function with the line: "float angle = 0;". Inside the render function in render.c, we want to draw our rotated sprite, using the line: "drawRotatedSprite(BUFFER_WIDTH / 2, BUFFER_HEIGHT / 2, angle, &spr_test);".


Inside the render function, we should check if the left key was pressed using the line: "if(GetAsyncKeyState(VK_LEFT)){". Inside this if statement, we should run the line "angle += 0.01;" to rotate the sprite anticlockwise. To rotate clockwise, we should use the if statement: "if(GetAsyncKeyState(VK_RIGHT)){", with the line "angle -= 0.01;" inside. We can also scale the sprite in real time. We can have the sprite increase in size when the up arrow key is pressed, using the if statement: "if(GetAsyncKeyState(VK_UP)){", with the lines: "spr_test.xScale += 0.01;" and "spr_test.yScale += 0.01;" inside. Similarly, we can have the sprite shrink when the down arrow key is pressed. We can do this using the if statement: "if(GetAsyncKeyState(VK_DOWN)){", with the lines "spr_test.xScale -= 0.01;" and "spr_test.yScale -= 0.01;" inside.


That's all the theory for this tutorial. As usual, the code can be found below.




The Code:

Below is the code for this section. You will need a main.c file, but I have not shown this below as it has not changed over the last few tutorials. I will be compiling it with the MinGW compiler, using the command: "gcc main.c -lgdi32 -o app". You should be able to use any compiler, so long as you are able to link the gdi32 library.


main.h

/*
 Software Renderer in C - Part 12

 main.h

 This header file groups all our headers, structure declaration, global variables and function declarations into one file.
*/


//include headers
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <string.h>

//define window size constants
#define BUFFER_WIDTH 640
#define BUFFER_HEIGHT 480

//define transparent colour constants
#define TRANSPARENT_RED 163
#define TRANSPARENT_GREEN 73
#define TRANSPARENT_BLUE 164

//character size
#define CHARACTER_SIZE 8

//create pixel structure
//this is a 24-bit structure that stores a red, green and blue byte
typedef struct Pixel {
 uint8_t blue;
 uint8_t green;
 uint8_t red;
} Pixel;

//define render buffer structure
typedef struct RenderBuffer {
 HWND windowHandle; //handle to the window that the buffer belongs to
 HDC deviceContextHandle; //handle to device context (a device context is an area of memory stored by the OS that will be rendered directly to the window client area)
 Pixel * pixels; //pixel buffer to render to the screen
 BITMAPINFO bitmapInfo; //a bitmap info structure that stores necessary metadata for our renderBuffer (required to draw our data to the screen)
 int scale;
 int windowClientWidth;
 int windowClientHeight;
} RenderBuffer;

//bitmap structure
typedef struct Bitmap {
 BITMAPFILEHEADER fileHeader;
 BITMAPINFOHEADER infoHeader;
 Pixel * pixels;
} Bitmap;

//sprite structure
typedef struct Sprite {
 Bitmap * bitmap;
 int startX;
 int startY;
 int endX;
 int endY;
 int centreX;
 int centreY;
 float xScale;
 float yScale;
 int isPlainColour;
 uint8_t red;
 uint8_t green;
 uint8_t blue;
} Sprite;

//create global variables
int running = 1; //1 when program is running, 0 when program is closed
RenderBuffer renderBuffer; //our global render buffer
float angle = 0;

//create bitmaps
Bitmap bmp_test;
Bitmap bmp_font;

//create sprites
Sprite spr_test;

//main loop function
void mainLoop();

//handle events function
void handleEvents();

//declare render functions
void drawRectangle(int startX, int startY, int endX, int endY, uint8_t red, uint8_t green, uint8_t blue);
void drawLine(int startX, int startY, int endX, int endY, uint8_t red, uint8_t green, uint8_t blue);
void drawRow(int x1, int x2, int y, uint8_t red, uint8_t green, uint8_t blue);
void drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, uint8_t red, uint8_t green, uint8_t blue);
void drawCircle(int centreX, int centreY, int radius, uint8_t red, uint8_t green, uint8_t blue);
int loadBMPFile(char filePath[], Bitmap * bitmap);
void loadAllBitmaps();
void drawBitmap(int x, int y, Bitmap * bitmap);
void initialiseSprites();
void drawSprite(int x, int y, Sprite * sprite);
void getCoordinatesFromCharacter(char c, int * x, int * y);
void drawSingleLineText(int x, int y, char text[], int charSize, int length, uint8_t red, uint8_t green, uint8_t blue);
void drawMultiLineText(int x, int y, char text[], int charSize, int maxWidth, uint8_t red, uint8_t green, uint8_t blue);
void render();

//declare window procedure (event handler) function
LRESULT CALLBACK windowProcedure(HWND windowHandle, UINT messageID, WPARAM wParam, LPARAM lParam);

render.c

/*
 render.c

 This file contains all of the code used for rendering shapes, sprites, etc.

 This file is included into main.c directly, so we can still use global variables.
*/


//draw rectangle
void drawRectangle(int startX, int startY, int endX, int endY, uint8_t red, uint8_t green, uint8_t blue){
/*
  drawRectangle
  
  This function draws a rectangle to the render buffer.
  It takes seven parameters:
   -The x coordinate of the bottom left corner
   -The y coordinate of the bottom left corner
   -The x coordinate of the top right corner
   -The y coordinate of the top right corner
   -The red value of the pixel
   -The green value of the pixel
   -The blue value of the pixel
 */


//create pixel pointer
 Pixel * pixel;

//iterate from startY to endY (iterate through each row)
 int i = 0;
 int j = 0;

 for(i = startY; i <= endY; i++){
  /*
   The bottom left corner of the render buffer is (0,0).
   We need to set the pixel pointer to left side of rectangle on row i so that we
   can begin rendering the new row.
   Therefore, we want set the pixel pointer to renderBuffer.pixels + i * BUFFER_WIDTH + startX.
   renderBuffer.pixels marks the start of the buffer. We want to add a row (of width BUFFER_WIDTH) to our
   pixel pointer until we get to the row we want to draw on. Therefore, we want to move i rows
   up. We then add startX to move the pixel pointer to the left side of the rectangle on that row.
  */


  //set pixel pointer to left side of rectangle on current row
  pixel = renderBuffer.pixels + i * BUFFER_WIDTH + startX;
  
  //iterate from startX to endX and draw pixels on row
  for(j = startX; j <= endX; j++){
   //check if pixel is on screen 
   if(i >= 0 && i < BUFFER_HEIGHT && j >= 0 && j < BUFFER_WIDTH){
    //render pixel by setting red, green and blue values
    pixel->red = red;
    pixel->blue = blue;
    pixel->green = green;
   };
   
   //increment pixel
   pixel++;
  };
 };
};

//draw line
void drawLine(int startX, int startY, int endX, int endY, uint8_t red, uint8_t green, uint8_t blue){
//calculate changes in x and y
 int dx = endX - startX;
 int dy = endY - startY;

//individual steps for x and y
 float xStep;
 float yStep;
 int totalSteps = 0;

//check if dy > dx
 if(abs(dy) > abs(dx)){
  /*
  The total number of steps is equal to the total increase in y, as we are increasing y by 1 (or -1 if dy < 0) each time.
  We are increasing y by 1 (or -1 if dy < 0) because it dy is larger than dx in terms of magnitude, so we must choose to increment y
  to avoid skipping pixels. If we were to increase x by 1, we would get increases of y larger than 1 as dy/dx > 1. This would cause
  some pixels to be left out of our line.
  */

  totalSteps = abs(dy);
 } else {
  /*
  The total number of steps is equal to the total increase in x, as we are increasing x by 1 (or -1 if dx < 0) each time.
  We are increasing x by 1 (or -1 if dx < 0) because it dx is larger than dy in terms of magnitude, so we must choose to increment x
  to avoid skipping pixels. If we were to increase y by 1, we would get increases of x larger than 1 as dx/dy > 1. This would cause
  some pixels to be left out of our line.
  */

  totalSteps = abs(dx);
 };

//absolute value of gradient is steeper than 1, so increase by 1 along the y-axis each step
 yStep = (float) dy / totalSteps; //calculate the change in y per step
 xStep = (float) dx / totalSteps; //the increase in x per unit y (i.e. the amount to increase x by each step)

//create loop counter and set to 0
 int i = 0;

//create x and y values
 float x = startX;
 float y = startY;

//create pixel pointer - this will always point to the pixel to set
 Pixel * pixel;

//iterate for all steps
 for(int i = 0; i <= totalSteps; i++){
  //check that x and y are within bounds
  if(x >= 0 && x < BUFFER_WIDTH && y >= 0 && y < BUFFER_HEIGHT){
   //plot (x,y)
   //first, set pixel pointer to current pixel (equal to renderBuffer.pixels + y * BUFFER_WIDTH + x)
   //Note that we add y * BUFFER_WIDTH, as we are adding y rows of size BUFFER_WIDTH to the start of our pixel pointer to move it to the correct y-value
   pixel = renderBuffer.pixels + ((int) y) * BUFFER_WIDTH + (int) x;
   
   //set pixel colours
   pixel->red = red;
   pixel->green = green;
   pixel->blue = blue;
  };
  
  //increase x and y
  x += xStep;
  y += yStep;
 };
};

//draw row
void drawRow(int x1, int x2, int y, uint8_t red, uint8_t green, uint8_t blue){
//create pixel pointer
 Pixel * pixel;

//create min and max x coordinates
 int minX;
 int maxX;

//check x coordinates
 if(x1 < x2){
  minX = x1;
  maxX = x2;
 } else {
  minX = x2;
  maxX = x1;
 };

//set pixel to start of row
 pixel = renderBuffer.pixels + (BUFFER_WIDTH * y) + minX;

//iterate through all pixels in the row
 int i;
 for(i = minX; i <= maxX; i++){
  //check if on screen
  if(i >= 0 && i < BUFFER_WIDTH && y >= 0 && y < BUFFER_HEIGHT){
   pixel->red = red;
   pixel->green = green;
   pixel->blue = blue;
  };
  
  //increment pixel
  pixel ++;
 };
};

//render triangle
void drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, uint8_t red, uint8_t green, uint8_t blue){
//create lists of points
 int pointsX[3];
 int pointsY[3];

 pointsX[0] = x1;
 pointsX[1] = x2;
 pointsX[2] = x3;

 pointsY[0] = y1;
 pointsY[1] = y2;
 pointsY[2] = y3;

 int i = 0;

 for(i = 1; i < 3; i++){
  int j = i;
  
  while(j > 0 && pointsY[j - 1] > pointsY[j]){
   //swap
   int temp = pointsY[j];
   pointsY[j] = pointsY[j - 1];
   pointsY[j - 1] = temp;
   
   temp = pointsX[j];
   pointsX[j] = pointsX[j - 1];
   pointsX[j - 1] = temp;
   
   //decrement j
   j -= 1;
  };
 };

//draw first half of triangle
//create x coordinates for lines of triangles
 float lineX1 = pointsX[0];
 float lineX2 = pointsX[0];

//calculate the change in x per unit change in y for the lines 0->1 and 0->2
 float xStep1 = (float) (pointsX[1] - pointsX[0]) / (pointsY[1] - pointsY[0]);
 float xStep2 = (float) (pointsX[2] - pointsX[0]) / (pointsY[2] - pointsY[0]);

//iterate through all rows
 for(i = pointsY[0]; i < pointsY[1]; i++){
  //draw row between x1 and y1
  drawRow((int) lineX1, (int) lineX2, i, red, green, blue);
  
  //increase line x coordinates
  lineX1 += xStep1;
  lineX2 += xStep2;
 };

//draw second half of triangle
//recalculate xStep1, so that it is now the change in x per unit y
 xStep1 = (float) (pointsX[2] - pointsX[1]) / (pointsY[2] - pointsY[1]);

//iterate through all rows
 for(i = pointsY[1]; i < pointsY[2]; i++){
  //draw row between x1 and y1
  drawRow((int) lineX1, (int) lineX2, i, red, green, blue);
  
  //increase line x coordinates
  lineX1 += xStep1;
  lineX2 += xStep2;
 };
};

//draw circle
void drawCircle(int centreX, int centreY, int radius, uint8_t red, uint8_t green, uint8_t blue){
/*
  The circle is made up of 2 * radius rows of pixels.
  We want to iterate from the bottom-most row (centreY - radius) to
  the top-most row (centreY + radius).
 */

 int i = 0;

 for(i = centreY - radius; i <= centreY + radius; i++){
  /*
   The equation of a circle is given by the formula (x - a)^2 + (y - b)^2 = r^2,
   where (a,b) is the centre of the circle and r is the radius.
   We know that y = i, a = centreX, b = centreY and r = radius.
   This gives the equation: (x - a)^2 = k, where k is a constant
   equal to r^2 - (y - b)^2
   Therefore, x - a = + or - sqrt(k).
   Therefore the two values of x to draw a row between are:
   a + sqrt(k)
   a - sqrt(k)
  */

  
  //set k value
  double k = radius * radius - (i - centreY) * (i - centreY);
  
  //draw row from centreX - sqrt(k) to centreX + sqrt(k)
  drawRow((int) centreX - sqrt(k), (int) centreX + sqrt(k), i, red, green, blue);
 }; 
};

//load BMP file
int loadBMPFile(char filePath[], Bitmap * bitmap){
//attempt to open file
 FILE * file = fopen(filePath, "rb");

//check if file opened correctly
 if(!file){
  //return unsuccessful
  return 0;
 };

//get file size
 fseek(file, 0, SEEK_END);
 int fileSize = ftell(file);
 rewind(file);

//check if file is too small to be a bitmap file
 if(fileSize < sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)){
  fclose(file);
  
  //return unsuccessful
  return 0;
 };

//read bitmap file header
 fread(&(bitmap->fileHeader), sizeof(BITMAPFILEHEADER), 1, file);

//read bitmap info header
 fread(&(bitmap->infoHeader), sizeof(BITMAPINFOHEADER), 1, file);

//allocate memory for pixels
//the size of the bitmap can be found using bitmap->infoHeader.biWidth * bitmap->infoHeader.biHeight * sizeof(Pixel)
 bitmap->pixels = (Pixel *) malloc(bitmap->infoHeader.biWidth * bitmap->infoHeader.biHeight * sizeof(Pixel));

//copy bitmap data into pixel buffer
 fread(bitmap->pixels, sizeof(Pixel), bitmap->infoHeader.biWidth * bitmap->infoHeader.biHeight, file);

//close file
 fclose(file);

//return successful
 return 1;
};

//load all bitmaps
void loadAllBitmaps(){
//load test bitmap
 loadBMPFile("test.bmp", &bmp_test);

//load font bitmap
 loadBMPFile("font.bmp", &bmp_font);
};

//draw BMP file
void drawBitmap(int x, int y, Bitmap * bitmap){
//create pixel pointer
 Pixel * pixel;

//create loop counters
 int i = 0;
 int j = 0;

//iterate through all rows of bitmap
 for(i = 0; i < bitmap->infoHeader.biHeight; i++){
  //set pixel pointer to start of row y + i
  pixel = renderBuffer.pixels + (y + i) * BUFFER_WIDTH + x;
  
  //iterate through x coordinates
  for(j = 0; j < bitmap->infoHeader.biWidth; j++){
   //check that pixel is on screen
   if(j >= 0 && j < BUFFER_WIDTH && y + i >= 0 && y + i < BUFFER_HEIGHT){
    //get pointer to source pixel
    Pixel * srcPixel = bitmap->pixels + i * bitmap->infoHeader.biWidth + j;
   
    //check that pixel is not the colour we will be using as transparent
    if(srcPixel->red != TRANSPARENT_RED || srcPixel->green != TRANSPARENT_GREEN || srcPixel->blue != TRANSPARENT_BLUE){ 
     //pixel is not colour used to indicate transparency
     
     //set colour of pixel on renderBuffer pointed to by pixel pointer
     pixel->red = srcPixel->red;
     pixel->green = srcPixel->green;
     pixel->blue = srcPixel->blue;
    };
    
    //increment pixel pointer
    pixel++;
   };
  };
 };
};

//initialiseSprites
void initialiseSprites(){
//test sprite
 spr_test.bitmap = &bmp_test;
 spr_test.startX = 64;
 spr_test.endX = 128;
 spr_test.startY = 0;
 spr_test.endY = 64;
 spr_test.centreX = 32;
 spr_test.centreY = 32;
 spr_test.xScale = 1.0;
 spr_test.yScale = 1.0;
 spr_test.isPlainColour = 0;
};

//draw sprite
void drawSprite(int x, int y, Sprite * sprite){
//get width and height
 int width = abs(sprite->endX - sprite->startX);
 int height = abs(sprite->endY - sprite->startY);

//create vertex array
//4 vertices in the format {x, y}
 float vertices[4][2];

//bottom left
 vertices[0][0] = -sprite->centreX;
 vertices[0][1] = -sprite->centreY;

//top left
 vertices[1][0] = vertices[0][0];
 vertices[1][1] = vertices[0][1] + height;

//top right
 vertices[2][0] = vertices[0][0] + width;
 vertices[2][1] = vertices[1][1];

//bottom right
 vertices[3][0] = vertices[2][0];
 vertices[3][1] = vertices[0][1];

//multiply each vertex by xScale and yScale
 int i;

 for(i = 0; i < 4; i++){
  vertices[i][0] *= sprite->xScale;
  vertices[i][1] *= sprite->yScale;
 };

//create minimum and maximum coordinates
 int minX = vertices[0][0];
 int maxX = vertices[0][0];
 int minY = vertices[0][1];
 int maxY = vertices[0][1];

//calculate minimum and maximum coordinates
 for(i = 0; i < 4; i++){
  if(vertices[i][0] > maxX){
   maxX = vertices[i][0];
  };
  
  if(vertices[i][0] < minX){
   minX = vertices[i][0];
  };
  
  if(vertices[i][1] > maxY){
   maxY = vertices[i][1];
  };
  
  if(vertices[i][1] < minY){
   minY = vertices[i][1];
  };
 };

//add x and y coordinates to bounding coordiantes to translate sprite to screen
 minX += x;
 maxX += x;
 minY += y;
 maxY += y;

//create pixel pointer
 Pixel * pixel;

//create second loop counter
 int j;

//iterate from min y to max y
 for(i = minY; i < maxY; i++){
  //set pixel pointer to start of row
  pixel = renderBuffer.pixels + i * BUFFER_WIDTH + minX;
  
  //iterate through x coordiantes
  for(j = minX; j < maxX; j++){
   //check the coordinates (j, i) are on screen
   if(j >= 0 && j < BUFFER_WIDTH && i >= 0 && i < BUFFER_HEIGHT){
    //get coordinates of src pixel
    int pixelX = (int) ((j - x) / sprite->xScale) + sprite->centreX + sprite->startX;
    int pixelY = (int) ((i - y) / sprite->yScale) + sprite->centreY + sprite->startY;
    
    //check that pixelX and pixelY are within sprite bounds
    if(pixelX >= sprite->startX && pixelX < sprite->endX && pixelY >= sprite->startY && pixelY < sprite->endY){
     //retrieve source pixel
     Pixel * srcPixel = sprite->bitmap->pixels + (pixelY * sprite->bitmap->infoHeader.biWidth) + pixelX;
     
     //check that pixel is not transparent
     if(srcPixel->red != TRANSPARENT_RED || srcPixel->green != TRANSPARENT_GREEN || srcPixel->blue != TRANSPARENT_BLUE){
      //check if plain colour is enabled
      if(sprite->isPlainColour){
       //if plain colour is enable, replace all non-transparent pixels with the colour value stored in the sprite
       pixel->red = sprite->red;
       pixel->green = sprite->green;
       pixel->blue = sprite->blue;
      } else {
       //draw pixel
       pixel->red = srcPixel->red;
       pixel->green = srcPixel->green;
       pixel->blue = srcPixel->blue;
      };
     };
    };
   };
   
   //increment pixel pointer
   pixel ++;
  };
 };
};

//get coordinates from character
void getCoordinatesFromCharacter(char c, int * x, int * y){
//get the coordinates of the character

//note that the numerical value of 'a' is 97, and the numerical value of A is 65

//check if character is lower case - if so, convert to upper case (as font.bmp only supports upper case characters)
 if(c >= 'a' && c <= 'z'){
  /*
   Convert to upper case.
   We can do this by subtracting the difference between the numerical
   values of 'a' and 'A' from c.
  */

  c -= ('a' - 'A');
 };

//check if character is an upper case letter
 if(c >= 'A' && c <= 'Z'){
  /*
   Set the x coordinate of the character on the font bitmap.
   The font.bmp file is made of two rows of 26 characters.
   Each character is 8x8 px. Therefore, the starting x coordinate
   of the character is the number of the letter (starting at A = 0)
   multiplied by the character size. We can get the number of the letter by subtrating
   the value of 'A' from our character (c).
  */

  *x = (c - ('A')) * CHARACTER_SIZE; //letter number multiplied by size of one character
  
  
  /*
   Set the y coordinate of the character on the font bitmap.
   There are two rows in the font.bmp file. All of the letters
   are located on the top row. Remember that the bottom left
   corner is the point (0, 0). Therefore, we can get the y coordinate
   of the top row by getting the height of the bitmap and subtracting
   the height of one character (8 pixels).
  */

  *y = bmp_font.infoHeader.biHeight - CHARACTER_SIZE; //bitmap height subtract height of one character
 } else if(c >= '0' && c <= '9'){
  /*
   The numbers are on the second row down. They start at x coordinate 0, and
   there are ten digits that we may need to render. Therefore the x coordinate
   of each number is (c - '0') * CHARACTER_SIZE. The y coordinate of each number is the height
   of the bitmap - 2 * CHARACTER_SIZE - the y coordinate of the second row down.
  */

  
  *x = (c - '0') * CHARACTER_SIZE; //number value multiplied by width of character
  *y = bmp_font.infoHeader.biHeight - 2 * CHARACTER_SIZE; //bitmap height - 2 * height of character
 } else {
  //set default y coordinate (second row down)
  *y = bmp_font.infoHeader.biHeight - 2 * CHARACTER_SIZE;
  
  //all other characters we want to render should be handled here
  switch(c){
   case '.':{
    *x = 10 * CHARACTER_SIZE;
    break;
   };
   
   case ',':{
    *x = 11 * CHARACTER_SIZE;
    break;
   };
   
   case ':':{
    *x = 12 * CHARACTER_SIZE;
    break;
   };
   
   case '!':{
    *x = 13 * CHARACTER_SIZE;
    break;
   };
   
   case '?':{
    *x = 14 * CHARACTER_SIZE;
    break;
   };
   
   case '-':{
    *x = 15 * CHARACTER_SIZE;
    break;
   };
   
   case '+':{
    *x = 16 * CHARACTER_SIZE;
    break;
   };
   
   default:{
    //render blank space
    *x = 17 * CHARACTER_SIZE;
    break;
   };
  };
 };
};

//draw single line text
void drawSingleLineText(int x, int y, char text[], int charSize, int length, uint8_t red, uint8_t green, uint8_t blue){
//get string length if not set already
 if(length < 1){
  length = strlen(text);
 };

//set scale
 float scale = (float) charSize / CHARACTER_SIZE;

//set up character sprite
 Sprite character;
 character.bitmap = &bmp_font;
 character.xScale = scale;
 character.yScale = scale;
 character.centreX = 0;
 character.centreY = 0;
 character.isPlainColour = 1;
 character.red = red;
 character.green = green;
 character.blue = blue;

//iterate through each character
 int i;

 for(i = 0; i < length; i++){
  //get character coordinates
  getCoordinatesFromCharacter(text[i], &character.startX, &character.startY);
  
  //add size of character to start x and y to get end x and y
  character.endX = character.startX + CHARACTER_SIZE;
  character.endY = character.startY + CHARACTER_SIZE;
  
  //draw sprite
  drawSprite(x + i * scale * CHARACTER_SIZE, y, &character);
 };
};

//draw multiline text
void drawMultiLineText(int x, int y, char text[], int charSize, int maxWidth, uint8_t red, uint8_t green, uint8_t blue){
//check if size of character is bigger than size of line
 if(charSize > maxWidth){
  //not possible to draw text, exit function
  return;
 };

//get length of text
 int length = strlen(text);

//create start index - this always points to the start of the current line
 int startIndex = 0;

//get row number
 int rowNumber = 0;

//calculate characters per row
 int charactersPerRow = maxWidth / charSize;

//iterate through all characters
 int i;

 for(i = 0; i < length; i++){
  //check if max width has been exceded
  //we start from i + 1, as we want to start from the rightmost pixel of the current character
  if((i + 1 - startIndex) * charSize > maxWidth){
   /*
    Drop down a line before drawing the next character.
    The last space must be found so that we start the
    current word on the new line, and not just the new
    character.
   */

   
   /*
    endIndex stores the index of the previous space.
    If no space could be found on the last line, endIndex is
    set to -1.
   */

   int endIndex = -1;
   
   int j = i;
   
   //find closest space
   //check for previous space until end of previous line is reached, or start of text is reached
   while(j >= 0 && j > i - charactersPerRow){
    //check for space
    if(text[j] == ' '){
     //space found, so exit loop
     endIndex = j;
     break;
    };
    
    //check previous character    
    j -= 1;
   };
   
   //if a space could not be found within previous line, move current character onto next line
   if(endIndex == -1){
    //set end index
    endIndex = i - 1;
    
    //move i back one space, so that current character is drawn on next line
    i -= 1;
   } else {
    //set i to the end of the previous word, so that the loop continues from the end of the line
    i = endIndex;
   };
   
   //draw line from start index to end index
   drawSingleLineText(x, y - rowNumber * (charSize * 1.25), text + startIndex, charSize, (endIndex + 1 - startIndex), red, green, blue);
   
   //increment row value
   rowNumber += 1;
   
   //set start index
   startIndex = endIndex + 1;
  } else if(i == length - 1){
   //the end of the string has been reached draw the final line
   drawSingleLineText(x, y - rowNumber * (charSize * 1.25), text + startIndex, charSize, (i + 1 - startIndex), red, green, blue);
  };
 };
};

//draw rotated sprite
void drawRotatedSprite(int x, int y, float angle, Sprite * sprite){
//calculate width and height
 int width = abs(sprite->endX - sprite->startX);
 int height = abs(sprite->endY - sprite->startY);

/*
  Create array of vertices.
  These vertices should form the bounding box of the
  sprite, and should be centred around the origin.
 */


//create vertices
 float vertices[4][2];

//bottom left
 vertices[0][0] = -sprite->centreX;
 vertices[0][1] = -sprite->centreY;

//top left
 vertices[1][0] = vertices[0][0];
 vertices[1][1] = vertices[0][1] + height;

//top right
 vertices[2][0] = vertices[1][0] + width;
 vertices[2][1] = vertices[1][1];

//bottom right
 vertices[3][0] = vertices[2][0];
 vertices[3][1] = vertices[0][1];

//multiply vertices by scale factors
 int i;

 for(i = 0; i < 4; i++){
  vertices[i][0] *= sprite->xScale;
  vertices[i][1] *= sprite->yScale;
 };

/*
  Rotate coordinates
  -The x coordinate of each vertex is equal to lcos(a), where l is the distance between
  the origin and the vertex, and a is the angle that the vertex makes with the x-axis.
  -The y coordinates of each vertex is equal to lsin(a).
  -We want to rotate each vertex by a constant angle b.
  -Therefore, the coordinates of each vertex will be (lcos(a + b), lsin(a + b)).
  -Using the compound angle formulae:
   cos(a + b) = cos(a)cos(b) - sin(a)sin(b)
   sin(a + b) = sin(a)cos(b) + cos(a)sin(b)
  -Therefore, the new coordinates of a given vertex can be written as:
   new x: lcos(a)cos(b) - lsin(a)sin(b)
   new y: lsin(a)cos(b) + lcos(a)sin(b)
  -If we substitute in lcos(a) = x and lsin(b) = y, we get
   new x: xcos(b) - ysin(b)
   new y: ycos(b) + xsin(b)
  -This means that we do not need to calculate l or a, making our algorithm more efficient.
  -As b is the angle of rotation, we can calculate sin(b) and cos(b) once and reuse the values
  for all of the vertices.
 */


//pre calculate sine and cosine of the angle of rotation
 float sinAngle = sin(angle);
 float cosAngle = cos(angle);

 for(i = 0; i < 4; i++){
  //store x and y values
  float prevX = vertices[i][0];
  float prevY = vertices[i][1];
  
  //rotate vertices
  //the new x coordinate = x * cosAngle - y * sinAngle
  //the new y coordinate = y * cosAngle + x * sinAngle
  vertices[i][0] = prevX * cosAngle - prevY * sinAngle;
  vertices[i][1] = prevY * cosAngle + prevX * sinAngle;
 };

//translate all vertices over to the x and y coordinates that we want to render to
 for(i = 0; i < 4; i++){
  vertices[i][0] += x;
  vertices[i][1] += y;
 };

//initialise minimum and maximum coordinate variables
//set them to the coordinates of the first vertex
 int minX = vertices[0][0];
 int maxX = vertices[0][0];
 int minY = vertices[0][1];
 int maxY = vertices[0][1];

 for(i = 0; i < 4; i++){
  if(vertices[i][0] < minX){
   minX = vertices[i][0];
  };
  
  if(vertices[i][0] > maxX){
   maxX = vertices[i][0];
  };
  
  if(vertices[i][1] < minY){
   minY = vertices[i][1];
  };
  
  if(vertices[i][1] > maxY){
   maxY = vertices[i][1];
  };
 };

//create pixel pointer to point to the pixel on the render buffer we want to draw to
 Pixel * pixel;

//iterate through each row
 for(i = minY; i < maxY; i++){
  //set pixel pointer to start of row
  pixel = renderBuffer.pixels + i * BUFFER_WIDTH + minX;
  
  //iterate through all pixels in each row
  int j;
  
  for(j = minX; j < maxX; j++){
   //check that point (j, i) is on screen
   if(j >= 0 && j < BUFFER_WIDTH && i >= 0 && i < BUFFER_HEIGHT){
    /*
     Find corresponding pixel on source bitmap by reversing the transformation.
     First, we subtract the x/y coordinate from j/i, so that the sprite is centred at the origin.
     Next we rotate by -angle. We can do this by reversing the signs of the previous rotation equations.
     After this, we should divide the x and y coordinates by the xScale and yScale respectively.
     Finally, we need to add on the centre x and y coordinates, so that the bottom left of the sprite
     is the point (0, 0).
     
     To reverse the rotation, we want to rotate by -angle. This gives the coordinates:
     new x = lcos(a - b) = lcos(a)cos(b) + lsin(a)sin(b)
     new y = lsin(a - b) = lsin(a)cos(b) - lcos(a)sin(b)
    */

    
    //reverse tranlation and rotation
    int pixelX = (j - x) * cosAngle + (i - y) * sinAngle;
    int pixelY = (i - y) * cosAngle - (j - x) * sinAngle;
    
    //reverse rotation
    pixelX /= sprite->xScale;
    pixelY /= sprite->yScale;
    
    //reverse centreX/centreY translation
    pixelX += sprite->centreX;
    pixelY += sprite->centreY;
    
    //add on start x and y to pixel, so that pixel is read relative to the start of the sprite on the source bitmap
    pixelX += sprite->startX;
    pixelY += sprite->startY;
    
    //check that pixel coordinates are within texture bounds
    if(pixelX >= sprite->startX && pixelX < sprite->endX && pixelY >= sprite->startY && pixelY < sprite->endY){
     //create source pixel and set it to the pixel at (pixelX, pixelY) on the source bitmap
     Pixel * srcPixel = sprite->bitmap->pixels + pixelY * sprite->bitmap->infoHeader.biWidth + pixelX;
     
     //check that pixel is not transparent
     if(srcPixel->red != TRANSPARENT_RED || srcPixel->green != TRANSPARENT_GREEN || srcPixel->blue != TRANSPARENT_BLUE){
      //not transparent - check if plain colour should be drawn
      if(sprite->isPlainColour == 1){
       pixel->red = sprite->red;
       pixel->green = sprite->green;
       pixel->blue = sprite->blue;
      } else {
       pixel->red = srcPixel->red;
       pixel->green = srcPixel->green;
       pixel->blue = srcPixel->blue;
      };
     };
    };
   };
   
   //increment pixel pointer
   pixel++;
  };
 };
};

//render function
void render(){
//set all pixels to 0 red, 0 blue, 0 green
 memset(renderBuffer.pixels, 0, BUFFER_WIDTH * BUFFER_HEIGHT * sizeof(Pixel));

//draw texts
 drawRotatedSprite(BUFFER_WIDTH / 2, BUFFER_HEIGHT / 2, angle, &spr_test);

 if(GetAsyncKeyState(VK_LEFT)){
  //rotate left
  angle += 0.01;
 };

 if(GetAsyncKeyState(VK_RIGHT)){
  //rotate right
  angle -= 0.01;
 };

 if(GetAsyncKeyState(VK_UP)){
  //scale
  spr_test.xScale += 0.01;
  spr_test.yScale += 0.01;
 };

 if(GetAsyncKeyState(VK_DOWN)){
  //scale
  spr_test.xScale -= 0.01;
  spr_test.yScale -= 0.01;
 };


/*
  Send renderbuffer data to client area of window.
  We can do this with the StretchDIBits function.
  This takes many parameters, which are detailed below:
 */

 StretchDIBits(
  renderBuffer.deviceContextHandle, //a handle to the device context we wish to render to
  renderBuffer.windowClientWidth / 2 - (renderBuffer.scale * BUFFER_WIDTH) / 2, //the x coordinate of the top left coordinate of our buffer on the window client area
  renderBuffer.windowClientHeight / 2 - (renderBuffer.scale * BUFFER_HEIGHT) / 2, //the y coordinate of the top left coordinate of our buffer on the window client area
  BUFFER_WIDTH * renderBuffer.scale, //the width of the buffer on the window client area
  BUFFER_HEIGHT * renderBuffer.scale, //the height of the buffer on the window client area
  0, //the starting x coordinate on the source buffer to render from (we want to render all data, so this is 0)
  0, //the starting y coordinate on the source buffer to render from (we want to render all data, so this is 0)
  BUFFER_WIDTH, //the width of the source buffer
  BUFFER_HEIGHT, //the height of the source buffer
  renderBuffer.pixels, //a pointer to the actual data we want to send
  &renderBuffer.bitmapInfo, //a pointer to the bitmap info structure for our renderBuffer
  DIB_RGB_COLORS, //use RGB colours
  SRCCOPY //copy the source into the window's buffer
 );
};



Running Our Program:

When we run our program, we should get a sprite that we can rotate and resize using the arrow keys. Below are screenshots from the program:





That's all for this tutorial series. You should now have the skills to write a simple software renderer in C.