32. Thumbnails

The following MakeThumbnail extension method creates a thumbnail file for an image:

// Make a thumbnail for the file with maximum
// dimensions maxWidth x maxHeight.
private Bitmap MakeThumbnail(string filename, int maxWidth,
int maxHeight)
{
// Load the image.
Bitmap bm = LoadImageWithoutLocking(filename);

// Calculate the scale.
float xscale = maxWidth / (float)bm.Width;
float yscale = maxHeight / (float)bm.Height;
float scale = Math.Min(xscale, yscale);

// Make the thumbnail's bitmap.
int width = (int)Math.Round(bm.Width * scale);
int height = (int)Math.Round(bm.Height * scale);
Bitmap thumbnail = new Bitmap(width, height);
using (Graphics gr = Graphics.FromImage(thumbnail))
{
gr.InterpolationMode = InterpolationMode.High;

Rectangle srcRect = new Rectangle(0, 0, bm.Width, bm.Height);
Point[] destPoints =
{
new Point(0, 0),
new Point(width, 0),
new Point(0, height),
};
gr.DrawImage(bm, destPoints, srcRect, GraphicsUnit.Pixel);
}

return thumbnail;
}

This method first calls the LoadImageWithoutLocking method, which is described shortly, to load the image file without locking it. The code then calculates vertical and horizontal scales that it could use to resize the image so that it has the maximum allowed thumbnail width or height. It picks the smaller of the two scales to be the one that it will use so that the resulting thumbnail fits within the allowed bounds and is not stretched out of shape.

Next, the method calculates the image's scaled dimensions and creates a Bitmap of that size. It creates an associated Graphics object and sets that object's InterpolationMode property so that the image is resized smoothly.

The method defines a source rectangle that covers the entire original image. It then creates a destination array of Point structures that cover the area of the thumbnail bitmap. Finally, the code calls the Graphics object's DrawImage method to copy the original image onto the bitmap and returns the result.

Normally, if you load a Bitmap object from a file, the program keeps the file locked so that it can use it if necessary to redraw the image. This can be inconvenient if you want to edit or delete the image file while your program is displaying the image.

In this program, this may seem like a non-issue because the MakeThumbnail method does not need to redraw the original images. When the method ends, the Bitmap objects that it created leave the program so they are destroyed and they unlock their files.

Unfortunately, C# does not necessarily destroy Bitmap objects immediately. They may hang around, keeping their files locked, until the garbage collector runs and frees their resources.

One solution is to call the Bitmap objects' Dispose methods or to place them inside using blocks so that the Dispose method is called automatically. This example uses a different approach. It uses the following method to open the image files:

// Load an image file without locking it.
private Bitmap LoadImageWithoutLocking(string filename)
{
using (Bitmap bm = new Bitmap(filename))
{
return new Bitmap(bm);
}
}

This code opens the image file, placing the Bitmap in a using block. It then makes a new Bitmap from the first bitmap and returns it. The new Bitmap contains a copy of the original object's data, but doesn't need the original image file to draw itself.

When the using block ends, the original Bitmap is disposed, so its resources are freed and its image file is unlocked.

The next major piece in the example is the following ProcessFiles method:

// Process the files.
private void ProcessFiles()
{
// Get the input parameters.
string dirname = directoryTextBox.Text;
int thumbWidth = int.Parse(widthTextBox.Text);
int thumbHeight = int.Parse(heightTextBox.Text);

// Graphic file name patterns.
string[] patterns = { "*.png", "*.bmp", "*.jpg", "*.jpeg",
"*.gif" };

// Make a list of the directory's image files.
List<string> filenames = new List<string>();
foreach (string pattern in patterns)
filenames.AddRange(Directory.GetFiles(dirname, pattern));

// Compose the thumbnail directory's name.
string thumbdir = Path.Combine(dirname, "Thumbnails");

// Create an empty thumbnail directory.
EmptyDirectory(thumbdir);
Directory.CreateDirectory(thumbdir);

// Process the files.
foreach (string filename in filenames)
{
Bitmap bm = MakeThumbnail(filename, thumbWidth, thumbHeight);
string thumbname = Path.Combine(thumbdir,
Path.GetFileNameWithoutExtension(filename)) +
" thumb.bmp";
bm.Save(thumbname);
}
numCreatedLabel.Text = $"Created {filenames.Count} thumbnails";
}

This code gets the directory name, width, and height entered by the user. It then creates an array holding image file extensions and loops through that array.

For each extension, the method calls the Directory class's GetFiles method to get files that have the extension and adds the returned file names to the filenames list.

Next, the code uses Path.Combine to create the name of the thumbnail directory inside the image directory. The code calls the EmptyDirectory method, which is described shortly, to remove any files from that directory and then uses the Directory class's CreateDirectory method to create the directory if it doesn't already exist.

Now, the method loops through the image files, calls MakeThumbnail for each, and saves the resulting bitmaps in the thumbnail directory.

The final interesting piece of code in this example is the following EmptyDirectory method:

// Delete the files in this directory.
// Hide errors if the directory doesn't exist.
private void EmptyDirectory(string dirname)
{
DirectoryInfo dirinfo = new DirectoryInfo(dirname);
if (!dirinfo.Exists) return;

foreach (FileInfo fileinfo in dirinfo.GetFiles()) fileinfo.Delete();
}

This method creates a DirectoryInfo object for the directory. If the directory exists, the method then loops through the directory's files and deletes them.

Download the Thumbnails example solution to see additional details.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.253.223