Implementing the bitmap loader
Go ahead and create Bitmap.h and Bitmap.c inside your src folder.As usual, do not forget to declare the .c source file in the Makefile:
Below are the code snippets of Bitmap.h and Bitmap.c. You should do a little research and make sure to know exactly what is going on, rather than just copy/pasting. LCOM teachers will not tolerate it.
Bitmap.h
#pragma once /** @defgroup Bitmap Bitmap * @{ * Functions for manipulating bitmaps */ typedef enum { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT } Alignment; typedef struct { unsigned short type; // specifies the file type unsigned int size; // specifies the size in bytes of the bitmap file unsigned int reserved; // reserved; must be 0 unsigned int offset; // specifies the offset in bytes from the bitmapfileheader to the bitmap bits } BitmapFileHeader; typedef struct { unsigned int size; // specifies the number of bytes required by the struct int width; // specifies width in pixels int height; // specifies height in pixels unsigned short planes; // specifies the number of color planes, must be 1 unsigned short bits; // specifies the number of bit per pixel unsigned int compression; // specifies the type of compression unsigned int imageSize; // size of image in bytes int xResolution; // number of pixels per meter in x axis int yResolution; // number of pixels per meter in y axis unsigned int nColors; // number of colors used by the bitmap unsigned int importantColors; // number of colors that are important } BitmapInfoHeader; /// Represents a Bitmap typedef struct { BitmapInfoHeader bitmapInfoHeader; unsigned char* bitmapData; } Bitmap; /** * @brief Loads a bmp image * * @param filename Path of the image to load * @return Non NULL pointer to the image buffer */ Bitmap* loadBitmap(const char* filename); /** * @brief Draws an unscaled, unrotated bitmap at the given position * * @param bitmap bitmap to be drawn * @param x destiny x coord * @param y destiny y coord * @param alignment image alignment */ void drawBitmap(Bitmap* bitmap, int x, int y, Alignment alignment); /** * @brief Destroys the given bitmap, freeing all resources used by it. * * @param bitmap bitmap to be destroyed */ void deleteBitmap(Bitmap* bmp); /**@}*/
Bitmap.c
#include "Bitmap.h" #include "stdio.h" #include "Graphics.h" #include "Utilities.h" Bitmap* loadBitmap(const char* filename) { // allocating necessary size Bitmap* bmp = (Bitmap*) malloc(sizeof(Bitmap)); // open filename in read binary mode FILE *filePtr; filePtr = fopen(filename, "rb"); if (filePtr == NULL) return NULL; // read the bitmap file header BitmapFileHeader bitmapFileHeader; fread(&bitmapFileHeader, 2, 1, filePtr); // verify that this is a bmp file by check bitmap id if (bitmapFileHeader.type != 0x4D42) { fclose(filePtr); return NULL; } int rd; do { if ((rd = fread(&bitmapFileHeader.size, 4, 1, filePtr)) != 1) break; if ((rd = fread(&bitmapFileHeader.reserved, 4, 1, filePtr)) != 1) break; if ((rd = fread(&bitmapFileHeader.offset, 4, 1, filePtr)) != 1) break; } while (0); if (rd = !1) { fprintf(stderr, "Error reading file\n"); exit(-1); } // read the bitmap info header BitmapInfoHeader bitmapInfoHeader; fread(&bitmapInfoHeader, sizeof(BitmapInfoHeader), 1, filePtr); // move file pointer to the begining of bitmap data fseek(filePtr, bitmapFileHeader.offset, SEEK_SET); // allocate enough memory for the bitmap image data unsigned char* bitmapImage = (unsigned char*) malloc( bitmapInfoHeader.imageSize); // verify memory allocation if (!bitmapImage) { free(bitmapImage); fclose(filePtr); return NULL; } // read in the bitmap image data fread(bitmapImage, bitmapInfoHeader.imageSize, 1, filePtr); // make sure bitmap image data was read if (bitmapImage == NULL) { fclose(filePtr); return NULL; } // close file and return bitmap image data fclose(filePtr); bmp->bitmapData = bitmapImage; bmp->bitmapInfoHeader = bitmapInfoHeader; return bmp; } void drawBitmap(Bitmap* bmp, int x, int y, Alignment alignment) { if (bmp == NULL) return; int width = bmp->bitmapInfoHeader.width; int drawWidth = width; int height = bmp->bitmapInfoHeader.height; if (alignment == ALIGN_CENTER) x -= width / 2; else if (alignment == ALIGN_RIGHT) x -= width; if (x + width < 0 || x > getHorResolution() || y + height < 0 || y > getVerResolution()) return; int xCorrection = 0; if (x < 0) { xCorrection = -x; drawWidth -= xCorrection; x = 0; if (drawWidth > getHorResolution()) drawWidth = getHorResolution(); } else if (x + drawWidth >= getHorResolution()) { drawWidth = getHorResolution() - x; } char* bufferStartPos; char* imgStartPos; int i; for (i = 0; i < height; i++) { int pos = y + height - 1 - i; if (pos < 0 || pos >= getVerResolution()) continue; bufferStartPos = getGraphicsBuffer(); bufferStartPos += x * 2 + pos * getHorResolution() * 2; imgStartPos = bmp->bitmapData + xCorrection * 2 + i * width * 2; memcpy(bufferStartPos, imgStartPos, drawWidth * 2); } } void deleteBitmap(Bitmap* bmp) { if (bmp == NULL) return; free(bmp->bitmapData); free(bmp); }
You should now compile the program to make sure there are no errors related to what we have just introduced to the project.
Here is a quick explanation about the Bitmap functions: loadBitmap, drawBitmap and deleteBitmap.
loadBitmap
This function receives a path to an image as a string and loads that image to memory, it then returns a pointer to the memory region where the image was loaded.drawBitmap
This function receives a bitmap pointer, a coordinate and an alignment.If the alignment is ALIGN_LEFT, the bitmap is drawn with its top left corner at the specified coordinates.
If the alignment is ALIGN_CENTER, the bitmap is drawn with the middle of the top border at the specified coordinates.
If the alignment is ALIGN_RIGHT, the bitmap is drawn with its top right corner at the specified coordinates.
deleteBitmap
This function receives a pointer to a bitmap and frees the memory used by it, destroying the bitmap.Preparing an image
Since we are using a 16-bit graphics mode, we can not load every image type. In fact, the load function in Bitmap.c is only able to load .bmp images which are saved in 5:6:5 mode.In order to use images in our project we will need to prepare them by opening them with an editor and save them in this specific format. I am going to use GIMP to edit the images.
Let's add an image background to our program. Since we are using a graphics mode with a resolution of 800x600, I am going to use this image.
Go ahead and open the image with GIMP. Now select File > Export and name it test.bmp. An export dialog will pop up; expand the Advanced Options and under 16 bits select R5 G6 B5 and finally press Export.
This image is now prepared to be correctly loaded by our program, so let's add it. Inside the res folder, create a new folder called images. Paste the exported image - test.bmp - inside the newly created images folder.
If you open test.bmp you will notice the quality has decreased. That has to due with the fact we exported it as a 16 bit image, since that is the only mode supported by our program. There's nothing to do about it.
Do not forget to repeat the same exporting process every time you want to add a new image to the project. You first need to export it as a 16-bit 5:6:5 .bmp image.
Loading the new background
Open FlappyNix.h and include Bitmap.h. Also, add a Bitmap* called test to the FlappyNix struct.Open FlappyNix.c and load the bitmap as shown in line 24.
After that, go to the draw method and instead of filling the screen - remove that line, draw the loaded bitmap - add line 87. Your draw method should now look as follows:
We are now ready to see the final result! But first, do not forget to compile and since we have edited the res folder, you will also need to run the install script again. Always remember, if the res folder is modified, rerun sh install.sh.
Now you are ready to run the program. It should look something like the image below. Isn't that awesome?
No comments:
Post a Comment