I recently discovered that the ANSI standard for C does not use the // marks for comment lines, so I will be removing them from sample code. They will be replaced with /*, which is the start of a comment, and */, which indicates the end of a comment. I will also test my example code for ANSI compliance by using the command "gcc -ansi filename.c -o programname" in the prompt (where filename and programname indicate the name of a file and program).
Another part of the ANSI standard is declaring variables at the start of a function. Luckily, the existing examples follow this routine, so I will only be changing the comments.
Also, I encountered a site called http://tohtml.com/, which converts code text into syntax-highlighted text. I will most likely highlight all my examples this way to minimize any errors in highlighting.
Thursday, October 18, 2012
BMP images in SDL
The SDL_Surface structure is ideal for managing the data of images. One image format that SDL supports automatically is the BMP format, which most often contains properties of the image and its uncompressed color data. SDL allows us to easily import BMP images and use them for display.
Displaying an image:
To display an image, all we need is a function to load in the BMP file and another function to place it on the screen. SDL provides the function SDL_LoadBMP which requires a file path. This file path is a string of characters that tells SDL where to find the image file. Before we provide a file path, we need an image:
One option is to use the image above. This image is the one I will use in examples because it is 640 pixels by 480 pixels like the window and should show any color errors if the words and colors don't match. You are free to use any image you wish; just note that for examples which output the above image, it will be your image instead.
Once we have an image saved somewhere on the hard drive, we must give SDL_LoadBMP a string to find this image. This string can be an absolute path: starting at the root directory for Mac or Linux or a drive letter for windows, and ending with the name and extension of the image file. Another approach is to use relative file paths, which don't require a long string, are example-friendly, and are often portable.
I call the image above "testImage.bmp", which can also be its relative path. The relative path starts not at the root or drive, but the program itself. If we place the image in the same directory as either the executable program or the source code files, then we can call: SDL_LoadBMP("testImage.bmp");. However, we can also create a folder in the relative directory called "images" and place the image in this folder. Then, the command would be called as: SDL_LoadBMP("images/testImage.bmp");.
The function SDL_BlitSurface will place the image on the screen for this example. More detail about this function will be covered, but for now, here is testImage.bmp drawn on the screen:
Here, an image is placed on the screen and the window waits to be closed. The command SDL_Flip(screen); could be placed right after SDL_BlitSurface(image, NULL, screen, NULL); and we would have the same output. First, the function SDL_LoadBMP is called and given the input "testImage.bmp", which should correspond to a real file called testImage.bmp located next to either the .c file or the built program. If this function fails, the resulting SDL_Surface pointer will be NULL, so we can check if the image failed to load and act accordingly. For this example, the program will print an error message and exit.
Next, the example uses the data from the SDL_Surface struct to print the width and height of our image. Remember that the variable image is a pointer to an SDL_Surface, so we access width with image->w. Then, SDL_BlitSurface is called and given the image variable and the screen variable along with two NULL inputs. Finally, SDL_FreeSurface is called to remove the image from memory before exiting.
Surface Blitting:
The command SDL_BlitSurface requires a pointer to an SDL_Surface struct (the source surface) as a first parameter. The next parameter is a pointer to an SDL_Rect, which specifies the rectangular area of the source surface you want. This rectangle is similar to ones placed on the screen: the x and y fields determine the upper-left corner and the w and h fields determine the width and height in pixels.
The width and height fields do not scale the image, but determine the size of the rectangle which will sample from the image. For example, if we use a rectangle with height and width of 50 and x and y of 100, then we will sample a 50 pixels by 50 pixels rectangle with an upper left corner at position (100, 100) of the image. If we input NULL in place of an SDL_Rect pointer, it will simply sample all of the image.
The last two parameters are another pointer to an SDL_Surface (the desination surface) and another SDL_Rect pointer. The sample of the source surface will be placed on the destination surface (image onto screen for the example) and overwrite the destination's original pixels. This last SDL_Rect structure is only used for the x and y, which make up the position to place the sample from the source surface. For example, an x and y of 200 will place the rectangular source sample at the position (200, 200) on the destination surface. NULL will simply use the position (0, 0).
For documentation of SDL_BlitSurface, see the page http://sdl.beuc.net/sdl.wiki/SDL_BlitSurface. The following example makes use of surface blitting:
This example will follow the mouse and take a centered 200 by 150 pixel sample of the image. It will then apply this sample to the screen, again centered on the mouse. The result is an area around the mouse where we see the original image. The picture above uses screen updating, but not screen clearing in order to show the motion of the mouse. To enable screen clearing, un-comment the line 'SDLFillRect(screen, &screen_rect, 0x000000);'.
Had dst.x and dst.y been set to 0, a rectangle of the image would appear in the upper-left of the screen. The contents of this rectangle would change based on where the mouse is, but its position would not change. Also, had src.x and src.y been 0 and dst.x and dst.y been set to mouse_x and mouse_y, the rectangle would move with the mouse, but it would only be the upper-left corner of the image.
To wrap up this post, here is some documentation on SDL_LoadBMP: http://sdl.beuc.net/sdl.wiki/SDL_LoadBMP. Also, for other tutorials about images in SDL, see the pages http://lazyfoo.net/SDL_tutorials/lesson01/index2.php, http://content.gpwiki.org/index.php/SDL:Tutorials:Displaying_a_Bitmap, and http://interhacker.wordpress.com/2012/08/28/chapter-7-images-in-sdl/.
The next post will cover how to setup and use OpenGL with SDL and C. Like the C posts, I may come back to SDL occasionally, but newer posts will most likely be about OpenGL. Textures in OpenGL do require images, so I will mostly use BMP, but I plan to discuss other image formats (like PNG and JPG) soon in another SDL post.
Thanks again for reading.
P.S. After changing the comments and recompiling, I encountered an error: the program would close shortly after opening. This was because I forgot to include the image file "TestImage.bmp" alongside my program. I included a simple error-handler for when the image file does not load; The program will print "Image failed to load" and wait 3 seconds before closing.
Also, the example image above is of type JPG. I'm not sure if I can upload BMP images to the blog, so for anyone running the examples, be sure to use a valid BMP file or convert the above JPG into BMP.
Displaying an image:
To display an image, all we need is a function to load in the BMP file and another function to place it on the screen. SDL provides the function SDL_LoadBMP which requires a file path. This file path is a string of characters that tells SDL where to find the image file. Before we provide a file path, we need an image:
![]() |
640x480 test image |
One option is to use the image above. This image is the one I will use in examples because it is 640 pixels by 480 pixels like the window and should show any color errors if the words and colors don't match. You are free to use any image you wish; just note that for examples which output the above image, it will be your image instead.
Once we have an image saved somewhere on the hard drive, we must give SDL_LoadBMP a string to find this image. This string can be an absolute path: starting at the root directory for Mac or Linux or a drive letter for windows, and ending with the name and extension of the image file. Another approach is to use relative file paths, which don't require a long string, are example-friendly, and are often portable.
I call the image above "testImage.bmp", which can also be its relative path. The relative path starts not at the root or drive, but the program itself. If we place the image in the same directory as either the executable program or the source code files, then we can call: SDL_LoadBMP("testImage.bmp");. However, we can also create a folder in the relative directory called "images" and place the image in this folder. Then, the command would be called as: SDL_LoadBMP("images/testImage.bmp");.
The function SDL_BlitSurface will place the image on the screen for this example. More detail about this function will be covered, but for now, here is testImage.bmp drawn on the screen:
Here, an image is placed on the screen and the window waits to be closed. The command SDL_Flip(screen); could be placed right after SDL_BlitSurface(image, NULL, screen, NULL); and we would have the same output. First, the function SDL_LoadBMP is called and given the input "testImage.bmp", which should correspond to a real file called testImage.bmp located next to either the .c file or the built program. If this function fails, the resulting SDL_Surface pointer will be NULL, so we can check if the image failed to load and act accordingly. For this example, the program will print an error message and exit.
Next, the example uses the data from the SDL_Surface struct to print the width and height of our image. Remember that the variable image is a pointer to an SDL_Surface, so we access width with image->w. Then, SDL_BlitSurface is called and given the image variable and the screen variable along with two NULL inputs. Finally, SDL_FreeSurface is called to remove the image from memory before exiting.
Surface Blitting:
The command SDL_BlitSurface requires a pointer to an SDL_Surface struct (the source surface) as a first parameter. The next parameter is a pointer to an SDL_Rect, which specifies the rectangular area of the source surface you want. This rectangle is similar to ones placed on the screen: the x and y fields determine the upper-left corner and the w and h fields determine the width and height in pixels.
The width and height fields do not scale the image, but determine the size of the rectangle which will sample from the image. For example, if we use a rectangle with height and width of 50 and x and y of 100, then we will sample a 50 pixels by 50 pixels rectangle with an upper left corner at position (100, 100) of the image. If we input NULL in place of an SDL_Rect pointer, it will simply sample all of the image.
The last two parameters are another pointer to an SDL_Surface (the desination surface) and another SDL_Rect pointer. The sample of the source surface will be placed on the destination surface (image onto screen for the example) and overwrite the destination's original pixels. This last SDL_Rect structure is only used for the x and y, which make up the position to place the sample from the source surface. For example, an x and y of 200 will place the rectangular source sample at the position (200, 200) on the destination surface. NULL will simply use the position (0, 0).
For documentation of SDL_BlitSurface, see the page http://sdl.beuc.net/sdl.wiki/SDL_BlitSurface. The following example makes use of surface blitting:
/*thanks to tohtml.com for syntax highlighting*/ #include <SDL/SDL.h> #include <stdio.h> SDL_Surface *screen; /*visible surface*/ SDL_Surface *image; /*holds data about loaded image*/ SDL_Event input; /*holds input*/ /*rectangles for source and destination surfaces*/ SDL_Rect src, dst; /*integers to track mouse position and buttons*/ int mouse_x, mouse_y; unsigned char button_flags; int loop = 1; /*set to zero to exit*/ int main(int argc, char** argv) { /*screen-sized rectangle*/ SDL_Rect screen_rect; screen_rect.w = 640; screen_rect.h = 480; screen_rect.x = 0; screen_rect.y = 0; /*start up SDL and setup window*/ SDL_Init(SDL_INIT_EVERYTHING); screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); /*try to load image, exit if there are problems*/ image = SDL_LoadBMP("testImage.bmp"); if (image == NULL) { printf("Image failed to load\n"); SDL_Delay(3000); SDL_Quit(); /*shutdown SDL*/ fflush(stdout); /*update stdout*/ return 0; } /*print dimensions of loaded image*/ printf("image width = %d pixels\n", image->w); printf("image height = %d pixels\n", image->h); /*set dimensions of sample and print them*/ src.w = 200; src.h = 150; printf("sample size = %d x %d pixels", src.w, src.h); /*loop is needed to keep window open*/ while (loop != 0) { /*read all events per cycle*/ while (SDL_PollEvent(&input)) { /*check for close button clicks*/ if (input.type == SDL_QUIT) loop = 0; } button_flags = SDL_GetMouseState(&mouse_x, &mouse_y); src.x = mouse_x - src.w / 2; /*sample near cursor*/ src.y = mouse_y - src.h / 2; dst.x = src.x; /*place source sample near cursor*/ dst.y = src.y; /*clear screen*/ /*SDL_FillRect(screen, &screen_rect, 0x000000);*/ /*apply source sample to destination surface*/ SDL_BlitSurface(image, &src, screen, &dst); SDL_Delay(20); /*wait 20ms*/ SDL_Flip(screen); /*update screen*/ } /*perform final commands then exit*/ SDL_FreeSurface(image); /*free image data*/ SDL_Quit(); /*shutdown SDL*/ fflush(stdout); /*update stdout*/ return 0; } | ||
prompt/stdout: image width = 640 pixels image height = 480 pixels sample size = 200 x 150 pixels | ||
output inside window:
|
This example will follow the mouse and take a centered 200 by 150 pixel sample of the image. It will then apply this sample to the screen, again centered on the mouse. The result is an area around the mouse where we see the original image. The picture above uses screen updating, but not screen clearing in order to show the motion of the mouse. To enable screen clearing, un-comment the line 'SDLFillRect(screen, &screen_rect, 0x000000);'.
Had dst.x and dst.y been set to 0, a rectangle of the image would appear in the upper-left of the screen. The contents of this rectangle would change based on where the mouse is, but its position would not change. Also, had src.x and src.y been 0 and dst.x and dst.y been set to mouse_x and mouse_y, the rectangle would move with the mouse, but it would only be the upper-left corner of the image.
To wrap up this post, here is some documentation on SDL_LoadBMP: http://sdl.beuc.net/sdl.wiki/SDL_LoadBMP. Also, for other tutorials about images in SDL, see the pages http://lazyfoo.net/SDL_tutorials/lesson01/index2.php, http://content.gpwiki.org/index.php/SDL:Tutorials:Displaying_a_Bitmap, and http://interhacker.wordpress.com/2012/08/28/chapter-7-images-in-sdl/.
The next post will cover how to setup and use OpenGL with SDL and C. Like the C posts, I may come back to SDL occasionally, but newer posts will most likely be about OpenGL. Textures in OpenGL do require images, so I will mostly use BMP, but I plan to discuss other image formats (like PNG and JPG) soon in another SDL post.
Thanks again for reading.
P.S. After changing the comments and recompiling, I encountered an error: the program would close shortly after opening. This was because I forgot to include the image file "TestImage.bmp" alongside my program. I included a simple error-handler for when the image file does not load; The program will print "Image failed to load" and wait 3 seconds before closing.
Also, the example image above is of type JPG. I'm not sure if I can upload BMP images to the blog, so for anyone running the examples, be sure to use a valid BMP file or convert the above JPG into BMP.
Subscribe to:
Posts (Atom)