Lectures/Notes
|
Displaying an ImageAdapted from Stephen J. Chenney's TutorialModified by Yu-Chi Lai at 2005This tutorial will build on the previous tutorial, adding image loading, display, and modification to our FLTK window.
Step 1: TargaImage Class
To aid you with image loading and saving in this course we will provide
you with the TargaImage class.
This class provides methods to load and save targa (.tga) images along with
a few utility methods. First get the files for TargaImage
and a few targas to work with (courtesy of FreeFoto.com)
and from the links below and save them in the same directory as your VS03
project. |
Ok we're ready to build and run our program. Choose Build -> Build Solution to compile and link the program and Debug -> Start Without Debugging to run it. You should see a window just like this. Before we quit lets get a little practice manipulating the image data in our TargaImage. |
|
... virtual ~MyWindow(); virtual void draw(); void ModifyImage(); TargaImage* image; ...
... image = TargaImage::Load_Image("flower.tga"); if (image == NULL) std::cerr << "Failed to load image." << std::endl; else { size(image->width, image->height); ModifyImage(); } firstTime = false; } ...
//***************************************************************************************
// // * Modified the image //======================================================================================= void MyWindow::ModifyImage(char* second_image_name) //======================================================================================= { if (image == NULL) return; TargaImage* secondImage = TargaImage::Load_Image(second_image_name); if (secondImage == NULL){ std::cerr << "Failed to load second image." << std::endl; return; } int sourceCenterX = 200, sourceCenterY = 200, destinationCenterX= 200, destinationCenterY = 150, radius = 50; for (int yOffset = -radius; yOffset <= radius; ++yOffset){ int sourceY = sourceCenterY + yOffset; int destinationY = destinationCenterY + yOffset; if (sourceY >= 0 && sourceY < secondImage->height && destinationY >= 0 && destinationY < image->height){ for (int xOffset = -radius; xOffset <= radius; ++xOffset){ int sourceX = sourceCenterX + xOffset; int destinationX = destinationCenterX + xOffset; if (sourceX >= 0 && sourceX < secondImage->width && destinationX >= 0 && destinationX < image->width){ unsigned char* sourcePixel = secondImage->data + (sourceY * secondImage->width + sourceX) * 4; unsigned char* destinationPixel = image->data + (destinationY * image->width + destinationX) * 4; for (int i = 0; i < 4; ++i) destinationPixel[i] = sourcePixel[i]; } } } } delete secondImage; }
This looks like quit a bit of code so lets take our time and walk through what it does.
We begin by checking if our window already has an image loaded, if it doesn't we have no destination for our image copying so we quit. We then attempt to load a second image called tree.tga. We use the same function as before. We check to make sure our second image loaded, if it didn't we print and error message and quit.
Now we define a bunch of variables that are going to control the source, destination position, and size of the area to copy. The source position is the center of the area in the newly loaded second image that we wish to copy. The destination position is the center of the area that we're going to copy to in the destination image (the one we loaded in draw()). The radius is the distance we'll go in each of the four cardinal directions from the center positions to define the areas when copying. By changing these values you can experiment with copying different portions of the source image to different locations in the destination image.
Ok now that we've loaded our images and defined the areas to copy to and from lets look at the code which actually does the copying. We begin by iterating through each row in the area to copy from and to. If either the source row or destination row are outside of their respective images we skip the row. Otherwise, we walk over the columns in the area for that row. We calculate the source and destination column for each entry in the row and test to see if its within the respective image just like we did with the rows. If the source and destination columns are both acceptable we can copy the current source pixel to the current destination pixel.
The easiest way to do this is to get a pointer to source and destination pixels. The formula for the offset into the image data for a pixel at position (x, y) is:
index of pixel(x, y) = (y * imageWidth + x) * numComponents
Image data is stored in row major order starting with the upper left pixel of the image. This means the image data stores all of the pixels in the first row from left to right followed by all the pixels in the second row from left to right, etc through all the rows in the image. Hence to find the pixel location we multiply the row of the pixel we want by the width of the image and then add the column of the desired pixel. This will give us the index of the pixel in the image data array. There's one catch though, each pixel in the image has multiple components (red, blue, green, etc), hence each pixel is using multiple slots in the array so we need to multiply our pixel index by the number of components per pixel to get the actual index into the image data. All TargaImage images have four components (red, green, blue, alpha) so for any image data loaded with the TargaImage class we'll always be multiplying by four to get the actual index into the image array.
The above code uses this formula to calculate a pointer to both the source and destination pixels. It then iterates through each of the four components of the pixels, replacing the destination pixels's component with the corresponding component of the source pixel.
Step 6: Build and Run the Program Again
Ok we're ready to build and run the final version of our program.
Choose Build -> Build Solution to compile and link the program and Debug -> Start Without Debugging to run it. You should get the image to the right. Feel free to move the source and destination points in the code to verify that our code works even if part of the source or destination area is outside of the respective image.
Hope these tutorials have made you somewhat comfortable with VS03, FLTK, and working with image data cause its time to start on the first project for 559.
Source code for this tutorial.