Serebyte


Home | Tutorials | Portfolio

Win32 Software Renderer in C: Part 10 - Drawing Single-Line Text


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


In this tutorial, we will look at drawing single-line text to the screen.




The Theory:

When drawinng our text, we will effectively just be cutting out a series of sprites. We will cut these sprites out from a larger bitmap image, called font.bmp. I have created a font image, shown below. Feel free to use this font image in any of your own projects.






An Overview of How we Will Draw Text

We will draw our text by specifying the coordinates of the bottom left corner of the text. We will then iterate through each character in the inputted string, and calculate the coordinates of its corresponding sprite inside the font.bmp file. To do this, we will first need to load in our font as a bitmap structure. After this, we need to draw the corresponding sprite. We will need to slightly modify our drawSprite function from the previous tutorial, as we want to be able to set our text to whatever colour we want, rather than to just display the bitmap. We can accomplish this by adding an "isPlainColour" property to our sprite structure. If this is set to 1, we should replace all of the non-transparent pixels in the sprite with a custom colour. If it is set to 0, we should render the actual pixels that make up the sprite.


To get the coordinates of a character on the font image, we need to create a getCoordinatesFromCharacter function.




Additions to main.h

Before we can start writing our drawSingleLineText function, we will need to add some code to our main.h file. We should include the string.h header file, as we will need some of its functions later on. We should then add four new properties to our Sprite structure. The first should be an integer (or a charater), and should be the isPlainColour property. If this is set to 1, then all the non-transparent pixels in the sprite should be set to a given colour. If not, then the regular sprite should be drawn. The next three are all of the uint8_t type. They are the red, green and blue values of the colour to draw if the sprite is in plain colour mode.


Next, we should define a new constant. This should be the size of a character and should be set to 8, as each character in the font bitmap I will be using is 8 pixels wide and 8 pixels tall. This constant should be called CHARACTER_SIZE. We can define this constant with the line "#define CHARACTER_SIZE 8". Next, we will need to declare a new global variable. This will be the font bitmap. I will call this bmp_font.




Loading Our Font

Inside are render.c file, we will need to load our font bitmap. We should do this inside the loadAllBitmaps function. We can load our bitmap with the line "loadBMPFile("font.bmp", &bmp_font);".




Modifying the drawSprite Function

Now we need to modify our drawSprite function to draw sprites in plain colour mode if it is enabled. We should do this inside the transparency check if statement (i.e. 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 do this with the line "if(sprite->isPlainColour){". If plain colour mode is enabled, we want to set the red, green and blue values of the pixel to the red, green and blue values stored in the sprite structure. We can do this with the lines: "pixel->red = sprite->red;", "pixel->green = sprite->green;" and "pixel->blue = sprite->blue;".


If plain colour mode is disabled (i.e., in the else block of the "if(sprite->isPlainColour){" statement), then we want to draw the pixels on the source bitmap to the screen. This can be done with the lines: "pixel->red = srcPixel->red;", "pixel->green = srcPixel->green;" and "pixel->blue = srcPixel->blue;". These are the same lines that we wrote in the previous tutorial for drawing an individual pixel to the screen, so they should be familiar.


That should complete our drawSprite function.




Getting the Coordinates of a Character Sprite

Next, we need to write a function to get the coordinates of a character sprite on the source bitmap image. This should be done inside the render.c file. I will call this function "getCoordinatesFromCharacter". This should be a void function, and should take three parameters. The first is the character (named c). The second is a pointer to an integer that will store the x coordinate of the sprite on the source bitmap (named x). The last is a pointer to an integer that will store the y oorinate of the sprite on the source bitmap (named y). We can create this with the line "void getCoordinatesFromCharacter(char c, int * x, int * y){".


The font bitmap I will be using only contains upper case letters. Because of this, the first thing we should do is check if the character is a lower case letter. If so we want to convert it to its upper case form. We can check if the character is a lower case letter using the line "if(c >= 'a' && c <= 'z'){". This simply checks if the value of c is inbetween the integer values of 'a' and 'z' inclusively. If the character is a lower case letter, we want to convert it to its upper case form by subtracting the differene between a lower and upper case 'a'. We can convert a lower case letter to upper case with the line: "c -= ('a' - 'A');".


Now we want to check if our character is an upper case letter. If so, we want to set our x and y pointers to the correct coordinates for the bottom left corner of the corresponding character. We can check if the character is an upper case letter with the line "if(c >= 'A' && c <= 'Z'){". My font bitmap is made up of two rows of 8x8 characters. Each row is 26 characters long. All of the upper case letters are on the top row. This means that we can set the x coordinate to: (integer value of our letter - integer value of 'A') * CHARACTER_SIZE. In code, this can be done with the line: "*x = (c - ('A')) * CHARACTER_SIZE;". We should set the y coordinate of the character to the height of the bitmap subtract the size of one character, using the line: "*y = bmp_font.infoHeader.biHeight - CHARACTER_SIZE;". Remember that the point (0, 0) is the bottom left coordinate of the source bitmap.


If the character is not an upper-case letter, then we next want to check if it is a number. The digits 0 to 9 make up the first 10 characters of the second row. We can check if the character is a number using the line: "} else if(c >= '0' && c <= '9'){". If the character is a number, then we want to set its x coordinate to the value of the digit (equal to the character value - the integer value of '0') multiplied by CHARACTER_SIZE. This an be done with the line: "*x = (c - '0') * CHARACTER_SIZE;". We want to set the y coordinate to the bottom of the second row. This can be done with the line: "*y = bmp_font.infoHeader.biHeight - 2 * CHARACTER_SIZE;".


If the character is not a number, then we will want to check for each special character on the font bitmap and set its corresponding x and y coordinates. We should set the y coordinate of the sprite to the bottom of the second row, as all of the special characters are locate here on our bitmap image. This can be done with the line: "*y = bmp_font.infoHeader.biHeight - 2 * CHARACTER_SIZE;". Then, we can start a switch-case statement to check for each drawn special character. This should be done with the line: "switch(c){". Inside this switch-case statement, we need to check for the following special characters:




The drawSingleLineText Function

Now we are ready to write our drawSingleLineText function. This should be a void function, and should take seven parameters. The first two should be the x and y coordinates of the bottom left corner of the line of text. These should be integers. The next parameter should be a string, and is the text to display. The next parameter is an integer, and is the size of each character in pixels. The last three are all of the uint8_t type, and are the red, green and blue values used to draw the sprite. We can create this function with the line: "void drawSingleLineText(int x, int y, char text[], float scale, uint8_t red, uint8_t green, uint8_t blue){".


Inside this function, we should first retrieve the length of our text and save it to an integer variable called "length". This can be done with the line "int length = strlen(text);". After this, we should get the scale of our sprite and save it to a floating point variable. The scale is equal to the inputted character size (charSize) divided by the size of a character on the bitmap image. This can be done with the following line of code: "float scale = (float) charSize / CHARACTER_SIZE;".


Next, we need to create a sprite for drawing characters. We can create our sprite with the line: "Sprite character;". Next, we need to set the properties of this sprite. To set the bitmap pointer of the sprite, we should use the line: "character.bitmap = &bmp_font;". To set the x an y scales of the sprite, we should use the lines: "character.xScale = scale;" and "character.yScale = scale;". Next we should set the centre coordinates. We want to draw from the bottom left corner, so should set the centre coordinates to the point (0,0). This can be done with the lines: "character.centreX = 0;" and "character.centreY = 0;". After this we should enable plain colour mode with the line: "character.isPlainColour = 1;". To finish off the initial properties, we should set the RGB values of the sprite to the RGB values passed into the function. This can be done with the lines: "character.red = red;", "character.green = green;" and "character.blue = blue;".


Now we need to iterate through each character. To do this, we should first create a loop counter with the line: "int i;". After this, we can start the loop with the line: "for(i = 0; i < length; i++){".


Inside the loop, we should first get the coordinates of our character on the font bitmap. We should use the getCoordinatesFromCharacter function from this, and save the output to the sprite startX and startY properties. We can do this with the line: "getCoordinatesFromCharacter(text[i], &character.startX, &character.startY);". Once we have retrieved the starting coordinates, we can calculate the ending coordinates using the lines: "character.endX = character.startX + CHARACTER_SIZE;" and "character.endY = character.startY + CHARACTER_SIZE;".


With the coordinates on our character on the font bitmap calculated, we can now draw the current character using the line: "drawSprite(x + i * scale * CHARACTER_SIZE, y, &character);". That finishes our drawSingleLineText function.


With our drawSingleLineText function written, we can now call it inside the render function. As an example, I will draw three lines of text to the screen, using the lines: 'drawSingleLineText(50, 400, "Hello world!", 48, 255, 0, 0);', 'drawSingleLineText(200, 250, "This is a test", 16, 0, 255, 0);' and 'drawSingleLineText(100, 100, "0123456789.,:!?-+", 8, 0, 0, 255);'.




The Code:

Below is the code for this tutorial. I have only included the main.h and render.c files, as we have made no changes to our main.c file. I will be compiling the code with the MinGW compiler. I will use the compile command "gcc main.c -lgdi32 -o app" for this. 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 10

 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

//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, 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, uint8_t red, uint8_t green, uint8_t blue){
//get string length
 int 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);
 };
};

//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 text
 drawSingleLineText(50, 400, "Hello world!", 48, 255, 0, 0);
 drawSingleLineText(200, 250, "This is a test", 16, 0, 255, 0);
 drawSingleLineText(100, 100, "0123456789.,:!?-+", 8, 0, 0, 255);

/*
  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:

Running our program should give the following output:


That's all for this tutorial. In the next tutorial, we will look at drawing multi-line text.