Tag Archive: graphics-programming


Graphics and handling user inputs- the combination that creates the symphony called game. A world (of game) where these two are out of phase ends in cacophony. In the last article I discussed about the various parameters that goes into creating a screen and loading bitmapped images on to the screen. That was pretty high-level as the work was done on structures that represent the actual screen and maps or sprites. But there are times when one has to get his/her hand dirty by working directly upon pixels. The creators of SDL had already anticipated this requirement and built the capacities to work at raw graphics level into the core itself. Thus developer is redeemed from understanding system, platform and architecture specific nitty-gritty about manipulating the pixels. The other aspect of gaming that gives sleepless nights to developers is handling the user input, as the handling of input devices changes from system to system. To remove this burden from the minds of the developers, SDL provides object-oriented approach in handling the events. In this article, I would be discussing these two aspects of SDL. In the first section, the discussion would focus on using pixel manipulation functions and their usages and the second section would focus on input handling. So now that the agenda for this article have been laid down lets get started.   Raw Graphics-Writing Directly onto the Display:  Though the SDL Graphics APIs provide pretty high level functionality abstracting off all the low-level details, yet, there are times when abstraction is not required. For this purpose also there are ways. These ways doesn’t exist as a library function but as separate functions that has to be embedded into your program. The functions are freely available. But for the completeness I am including them here. These functions are:

  1. getpixel():

This function is useful if pixel value have to be obtained from a given coordinates represented by X and Y values on the display. It works on a single pixel at a time. The first parameter is the surface from which the value has to be obtained. This is represented by a pointer to the SDL_Surface. The next two integer parameters represent the x and y coordinates from where the pixel value has to be obtained. The return value is an Uint32 representing the value of the pixel. Following is the code:   /*  * Return the pixel value at (x, y)  * NOTE: The surface must be locked before calling this!  */ Uint32 getpixel(SDL_Surface *surface, int x, int y) {     int bpp = surface->format->BytesPerPixel;     /* Here p is the address to the pixel we want to retrieve */     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;       switch(bpp) {     case 1:         return *p;       case 2:         return *(Uint16 *)p;       case 3:         if(SDL_BYTEORDER == SDL_BIG_ENDIAN)             return p[0] << 16 | p[1] << 8 | p[2];         else             return p[0] | p[1] << 8 | p[2] << 16;       case 4:         return *(Uint32 *)p;       default:         return 0;       /* shouldn’t happen, but avoids warnings */     } }   The first thing to be done is to obtain the depth represented by BytesPerPixel. It is done by the first statement: int bpp = surface->format->BytesPerPixel;   Next statement is self explanatory. To get the address of the pixel, the pitch of the of the passed surface is multiplied by the value of Y- coordinate, the depth is multiplied by the X- coordinate and the resulting values are added with pixel data of the surface represented by pixels member of SDL_Surface. This calculation provides the actual address of the pixel. The SDL_Surface could be thought of as multi dimensional array. Hence the value could be accessed as row-major and column-major format. That is done in the second statement: Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;  As the value returned by BytesPerPixels ranges from 1-4 according to the bytes needed to represent the pixel, it can be used for returning the values in the corresponding format i.e. 8, 16, 24 or 32. this is achieved by the switch-case block. That’s all about getpixel function.

  1. putpixel():

This is same as getpixel(). Apart from the parameters accepted by getpixel() function, this accepts one parameter extra- the address where the value has to be put. Following is the code for putpixel():   /*  * Set the pixel at (x, y) to the given value  * NOTE: The surface must be locked before calling this!  */ void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel) {     int bpp = surface->format->BytesPerPixel;     /* Here p is the address to the pixel we want to set */     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;       switch(bpp) {     case 1:         *p = pixel;         break;       case 2:         *(Uint16 *)p = pixel;         break;       case 3:         if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {             p[0] = (pixel >> 16) & 0xff;             p[1] = (pixel >> 8) & 0xff;             p[2] = pixel & 0xff;         } else {             p[0] = pixel & 0xff;             p[1] = (pixel >> 8) & 0xff;             p[2] = (pixel >> 16) & 0xff;         }         break;       case 4:         *(Uint32 *)p = pixel;         break;     } }   The working of putpixel() is almost opposite to that of getpixel(). In case of later, the returned value is pixel value corresponding to the coordinates whereas the later places the pixel value according the coordinates. For this first the BytesPerPixel of the passed SDL_Suface is extracted just like before. Then pixel’s address (or pixel value) is calculated and according to the value returned by BytesPerPixels the value is placed. Since the calculated value is address of the pixel, hence the passed pixel value can be directly assigned and the display will be getting the new value.   Now that both the functions have been explained, lets see how to put one them to use i.e. putpixel(). For this I am defining a method called putyellowpixel() that places a yellow pixel at the center of the screen. It doesn’t accept any parameter nor does it returns any value.   void putyellowpixel() {     int x, y;     Uint32 yellow;       /* Map the color yellow to this display (R=0xff, G=0xFF, B=0×00)        Note:  If the display is palettized, you must set the palette first.     */     yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0×00);       x = screen->w / 2;     y = screen->h / 2;       /* Lock the screen for direct access to the pixels */     if ( SDL_MUSTLOCK(screen) ) {         if ( SDL_LockSurface(screen) < 0 ) {             fprintf(stderr, “Can’t lock screen: %s\n”, SDL_GetError());             return;         }     }       putpixel(screen, x, y, yellow);       if ( SDL_MUSTLOCK(screen) ) {         SDL_UnlockSurface(screen);     }     /* Update just the part of the display that we’ve changed */     SDL_UpdateRect(screen, x, y, 1, 1);       return;   }   To get the yellow color, the SDL_MapRGB() has to be used. The SDL_PixelFormat is the first parameter. It stores surface format information. Next three parameters correspond to the red, blue and green components of the color. The return value is the actual color corresponding to the passed color components in hexadecimal format as follows: yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0×00);   Once the color has been retrieved, the next step is to get the required x and y coordinates which is achieved by the following statement: x = screen->w / 2; y = screen->h / 2; then screen surface is locked. If this is not done, then corruption of the SDL_Surface structure could get corrupted causing instability of the game as putpixel works on the address of pixel directly. This is done by:   SDL_MUSTLOCK(screen);   The next step is to call the putpixel. Once putpixel has returned, then unlock the surface and update the surface. That completes placing a pixel directly on to the surface. Next section would focus on even handling with reference to keyboard.   Handling the Key Board- the SDL way:Whatever has been discussed till now completes only one aspect of providing interactivity. Even now the application doesn’t have the ability to handle user gestures provided through different input devices such as keyboard, joy stick etc. So now the focus would be on the input handling. Two of the most common input devices are mouse and keyboard. SDL has wrappers for each of these. In this section I would be discussing about keyboard handling. Before entering the world of keyboard events, it is better to understand the most recurring structures in keyboard handling jargon. They are:

  1. SDLKey:

It is an enumerated type that represents various keys. For example SDLK_a represents lowercase ‘a’, SDLK_DELETE is for ‘delete’ key and so on.

  1. SDLMod:

SDLKey enumeration represents only keys. To represent key modifiers such as Shift and Ctrl, SDLMod enumeration is provided by the SDL. The KMOD_CAPS is one of the enumeration that can be used to find out whether caps key is down or not. Other modifiers also have representations in SDLMod.

  1. SDL_keysym:

It is a structure that contains the information of a key-press. The members of this structure include scan code in hardware dependent format, SDLKey value of the pressed key in sym field, the value of modifier key in mod field and the Unicode representation of the key in Unicode field.

  1. SDL_KeyboardEvent:

From the name itself it is obvious that this structure describes a keyboard event. The first member, type, tells that the event is key release or key press event. The second member gives the same info as the first but uses different values. The last member is a structure itself- the SDL_keysym structure.   Now that the structures have been brought into the picture, the next step is to use these in handling the keyboard events. For this the logic is simple. The SDL_PollEvent is used to read the events. This is placed within the while loop. Then the value of type member of SDL_Event variable, passed as the parameter to SDL_PollEvent, is checked to find the type of event and then event processing can be done. In code it is thus:   SDL_Event event;   .   .   /* Poll for events. SDL_PollEvent() returns 0 when there are no  */   /* more events on the event queue, our while loop will exit when */   /* that occurs.                                                  */   while( SDL_PollEvent( &event ) ){     /* We are only worried about SDL_KEYDOWN and SDL_KEYUP events */     switch( event.type ){       case SDL_KEYDOWN:         printf( “Key press detected\n” );         break;         case SDL_KEYUP:         printf( “Key release detected\n” );         break;         default:         break;     }   }   .   .   If this is used this in the program developed in last article, the exit condition of the program can be controlled. The new version would exit only at key press.     void display_bmp(char *file_name) { SDL_Surface *image;   /* Load the BMP file into a surface */ image = SDL_LoadBMP(file_name); if (image == NULL) { fprintf(stderr, “Couldn’t load %s: %s\n”, file_name, SDL_GetError()); return; }   /* * Palettized screen modes will have a default palette (a standard * 8*8*4 colour cube), but if the image is palettized as well we can * use that palette for a nicer colour matching */ if (image->format->palette && screen->format->palette) { SDL_SetColors(screen, image->format->palette->colors, 0, image->format->palette->ncolors); }   /* Blit onto the screen surface */ if(SDL_BlitSurface(image, NULL, screen, NULL) < 0) fprintf(stderr, “BlitSurface error: %s\n”, SDL_GetError());   SDL_UpdateRect(screen, 0, 0, image->w, image->h);   /* Free the allocated BMP surface */ SDL_FreeSurface(image); }   int main(int argc,char* argv[]) { /*variable to hold the file name of the image to be loaded *In real world error  handling  code would precede this                                                                          */ char* filename=”Tux.bmp”; /*The following code does the initialization for Audio and Video*/ int i_error=SDL_Init(SDL_INIT_VIDEO); /*If initialization is unsuccessful, then quit */ if(i_error==-1) exit(1); atexit(SDL_Quit); /* * Initialize the display in a 640×480 8-bit palettized mode, * requesting a software surface */ screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE); if ( screen == NULL ) { fprintf(stderr, “Couldn’t set 640x480x8 video mode: %s\n”, SDL_GetError()); exit(1); }   /*Handle the keyboards events here. Catch the SDL_Quit event to exit*/ done = 0;              while (!done)             {                SDL_Event event;                 /* Check for events */         while (SDL_PollEvent (&event))              {             switch (event.type)             {                           case SDL_KEYDOWN:                 break;                           case SDL_QUIT:                             done = 1;                            break;             default:                           break;             }         } /* Now call the function to load the image and copy it to the screen surface*/ load_bmp(filename); }   If you run the above code the window wont be closed until the close button is pressed. Though, this code does nothing much in area of interactivity but it’s a beginning. So as you can see, it is really easy to handle keyboard events using SDL. It totally removes the dependence of developer on Operating System for event handling. Also working at raw graphics level is not that difficult.   This brings us to the end of the third part of SDL programming. The next part would cover using OpenGL with SDL. Also using timers would be covered. Till next time.

In the last part I gave you a bird’s eye view of what GDI+ is and how to setup application for using GDI+. However, without knowing about the facilitator i.e. GDI+, going forward would be counter-productive. Hence, in this part, along with different image manipulation techniques, the focus would be on the essentials of GDI+. In the first section, I would discuss about GDI+ in brief including the differences it has with its predecessor that is GDI. Second section would focus on the manipulation techniques such as zooming, creating thumbnails. In the third section, I would extend the application created in last part to include the techniques introduced in this discussion. That is the outline of this discussion.

More About GDI+:

GDI+ essentially refers to the library that helps developers to interact with various devices such as Monitors, Printers etc. having graphical capabilities without going into low-level details of these devices. The essence of GUI is that it can interact with peripherals such as Monitors and presents data in human readable form. However, from the point of view of a developer, if he or she had to directly interact with these devices, then such a task would have been monumental. This is where GDI+ comes into picture. It acts as a conduit and a translator for the data being passed between devices and applications. Even controlling the command line terminals also comes under GDI+. It does everything from printing a ‘Hello World’ program on the console to drawing lines, rectangles etc and printing a form. Pictorially it can be shown thus:

Fig 1- GDI2_1.jpg

The next question that arises is how does GDI+ works? To make it clear as a crystal, lets have a look at an example of drawing a line. A line, in essence, is a sequence of pixels from starting location (X­­0, Y0) to an ending location (Xn, Yn). To draw such a line the devices i.e. monitor in this case, need to know the device coordinate or physical coordinates. However, instead of directly telling the device, the call is made to the drawLine() method of GDI+, and GDI+ draws the line from point A to point B in the memory also known as video memory. GDI+ reads the point A and point B locations, converts them to a sequence of pixels, and tells the monitor to display the sequence of pixels. In short GDI+ converts device independent calls to device understandable form and vice-versa. So that’s an overview of how GDI+ works. Lets now move onto topic of manipulation of image.

Image Manipulation – Thumbnail, zooming and saving:

In the last part, I discussed simple manipulations such as flipping and rotating. Lets tackle something a bit more complex in concept if not in implementation. They are:

1. Creating thumbnails

2. Zooming a loaded image

3. Saving a manipulated image

Of these first two strictly comes under the category of image manipulation whereas the last is a generic image based operation in the lines of file operation.

1. Creating Thumbnails:

Thumbnails are reduced version of images. The dimensions of a thumbnail image are typically of 80 to 200 pixels in length. In GDI+, thumbnail of an image can be created by GetThumbnailImage() of Image class. It takes four parameters- the first parameter corresponds to the width and second parameter is for the height of the thumbnail to be generated, the third parameter is Image.GetThumbnailImageAbort which needs to be passed for compatibility though not being used in current version. The fourth parameter, likewise is not used, yet need to be passed for compatibility. The fourth parameter must be IntPtr.Zero. If the first two parameters that is width and height are 0, then GDI+ returns embedded thumbnail (some images contains thumbnails embedded into them. Otherwise the thumbnail is created using system defined dimensions. For example if img is an instance of Image class and the width, height to be used is system defined, the statement to create a thumbnail would be:

Image thumbNailImage = img.GetThumbnailImage(0, 0, tnCallBack, IntPtr.Zero);

Where thumbNailImage contains the returned thumbnail, the tnCallback is a function corresponding to Image.GetThumbnailImageAbort which is defined as:

// Must be called, but not used

public bool tnCallbackMethod()

{

return false;

}

2. Zooming a loaded image:

Zooming is the process of enlarging an image by multiplying it with a number called zoom-factor. By definition, “The zoom factor is the ratio of the current size of the image to the desired new size of the image”. That means by dividing the current size of the image with desired size of the image we get the zoom factor. For example, to zoom in on an image by 200%, the current size must be multiplied by 200% or by 2 (200%= 200/100=2). To zoom out an image by 25 percent, the size must be multiplied by 25 percent, or 0.25 (25/100 = 0.25 times). It is one of those areas of image manipulation where GDI+ doesn’t provide methods to achieve the result.

3. Saving a manipulated image:

Saving an image is one of the primary operations done on an image. While saving an image, the type into which the image has to be saved or in other words the extension of the image plays a major role. Each type corresponds to a particular format. In essence, while saving an image writing out the data according to the format is necessary. However, since the API that is being used is GDI+, a single call to Save() method of Image class abstracts out all the details about writing out according to the format. It takes two parameters – the name as which the image is to be saved and the format into which the image has to be saved. The format can be specified using the types provided by ImageFormat class. Following table specifies the various formats supported by GDI+:

Property

Description

Bmp Specifies BMP format.
Emf Specifies EMF (Enhanced Metafile Format).
Exif Specifies EXIF format.
Gif Specifies GIF format.
Guid Specifies a GUID structure that represents the ImageFormat object.
Icon Specifies Windows icon format.
Jpeg Specifies JPEG format.
MemoryBmp Specifies memory bitmap format.
Png Specifies PNG format.
Tiff Specifies TIFF format.
Wmf Specifies WMF (Windows Metafile Format

Of these Emf and Wmf are specific to windows. I will be discussing about these, in detail, in future.

For example to save the image with the name “checker.gif” in GIF format, the statement would be:

curImage.Save(“checker.gif”, ImageFormat.Gif);

where curImage is the instance of Image class.

That brings us to the end of this section. In the next section, I would be extending the application developed in the first part by embedding the operations discussed in this section.

Image Manipulation – In the Real World:

It is time to put the theory into practice. I would be doing it by enhancing the application developed in the last part by providing the following functionalities:

1. Save the image in the format specified by user

2. Zoom-in according to the percentage selected from the menu

3. Create thumbnail of a loaded image

The menus corresponding to the operations are:

mnuSave – the submenu of File menu to save the image

mnu200Zoom – zooms the image by 200%.

mnuThumbNail- creates a thumbnail of the image.

Here is the method that handles click event of mnuSave

private void mnuSave_Click(object sender,

System.EventArgs e)

{

// If image is created

if(curImage == null)

return;

// Call SaveFileDialog

SaveFileDialog saveDlg = new SaveFileDialog();

saveDlg.Title = “Save Image As”;

saveDlg.OverwritePrompt = true;

saveDlg.CheckPathExists = true;

saveDlg.Filter =

“Bitmap File(*.bmp)|*.bmp|” +

“Gif File(*.gif)|*.gif|” +

“JPEG File(*.jpg)|*.jpg|” +

“PNG File(*.png)|*.png” ;

saveDlg.ShowHelp = true;

// If selected, save

if(saveDlg.ShowDialog() == DialogResult.OK)

{

// Get the user-selected file name

string fileName = saveDlg.FileName;

// Get the extension

string strFilExtn =

fileName.Remove(0, fileName.Length – 3);

// Save file

switch(strFilExtn)

{

case “bmp”:

curImage.Save(fileName, ImageFormat.Bmp);

break;

case “jpg”:

curImage.Save(fileName, ImageFormat.Jpeg);

break;

case “gif”:

curImage.Save(fileName, ImageFormat.Gif);

break;

case “tif”:

curImage.Save(fileName, ImageFormat.Tiff);

break;

case “png”:

curImage.Save(fileName, ImageFormat.Png);

break;

default:

break;

}

}

}

First save dialog box is shown with acceptable extensions. Then from the file name returned by the dialog box the extension is retrieved and according to the extension Save() method is called with corresponding image format parameter.

Next comes the handler for mnu200Zoom. But before that certain things have to be done. First add the following line to the class at the application level:

private double curZoom = 1.0;

Then the mnuLoad (from previous part) has to be changed slightly. The added code is shown in bold:

private void mnuLoad_Click(object sender,

System.EventArgs e)

{

//Change the AutoScrollMinSize property

this.AutoScrollMinSize = new Size

((int)(curImage.Width * curZoom),

(int)(curImage.Height * curZoom));

// Create OpenFileDialog

OpenFileDialog opnDlg = new OpenFileDialog();

// Set a filter for images

opnDlg.Filter =

“All Image files|*.bmp;*.gif;*.jpg;*.ico;”+

“*.emf;,*.wmf|Bitmap Files(*.bmp;*.gif;*.jpg;”+

“*.ico)|*.bmp;*.gif;*.jpg;*.ico|”+

“Meta Files(*.emf;*.wmf;*.png)|*.emf;*.wmf;*.png”;

opnDlg.Title = “ImageViewer: Open Image File”;

opnDlg.ShowHelp = true;

// If OK, selected

if(opnDlg.ShowDialog() == DialogResult.OK)

{

// Read current selected file name

curFileName = opnDlg.FileName;

// Create the Image object using

// Image.FromFile

try

{

curImage = Image.FromFile(curFileName);

}

catch(Exception exp)

{

MessageBox.Show(exp.Message);

}

}

// Repaint the form, which forces the paint

// event handler

Invalidate();

}

What the added code does is that it multiples the image width and height with zoom factor to render an image with appropriate zoom setting. Next the paint event handler has to be changed. The DrawImage() method has to be changed into the following:

g.DrawImage(curImage, new Rectangle

(this.AutoScrollPosition.X,

this.AutoScrollPosition.Y,

(int)(curRect.Width * curZoom),

(int)(curRect.Height * curZoom)));

The image should have the height and width according to the zoom factor. For that the current width and height is multiplied with the current zoom factor represented by curZoom variable. Last step in zooming is the event handler for mnu200Zoom:

private void mnu200_Click(object sender,

System.EventArgs e)

{

if(curImage != null)

{

curZoom = (double)200/100;

Invalidate();

}

}

Lastly comes the event handler for the mnuThumbNail:

private void mnuThumbNail_Click(object sender,

System.EventArgs e)

{

if(curImage != null)

{

// Callback

Image.GetThumbnailImageAbort tnCallBack =

new Image.GetThumbnailImageAbort(tnCallbackMethod);

// Get the thumbnail image

Image thumbNailImage = curImage.GetThumbnailImage

(100, 100, tnCallBack, IntPtr.Zero);

// Create a Graphics object

Graphics tmpg = this.CreateGraphics();

tmpg.Clear(this.BackColor);

// Draw thumbnail image

tmpg.DrawImage(thumbNailImage, 40, 20);

// Dispose of Graphics object

tmpg.Dispose();

}

}

// Must be called, but not used

public bool tnCallbackMethod()

{

return false;

}

It first creates a variable of type GetThumbnailImageAbort and assign the tnCallbackMethod() to it by passing the method to the GetThumbnailImageAbort. Then it creates a new instance of Image class to hold the image returned by the GetThumbnailImage method, which is then used to draw the thumbnail onto the screen.

This brings us to the end of this discussion. In this part I discussed more advanced features of GDI+. I will continue along the same lines in next part. Till then…

Follow

Get every new post delivered to your Inbox.